diff --git a/.changeset/silent-goats-invent.md b/.changeset/silent-goats-invent.md new file mode 100644 index 000000000000..1ebb37772264 --- /dev/null +++ b/.changeset/silent-goats-invent.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixes CSS modules ordering by rendering styles before links diff --git a/packages/astro/src/runtime/server/render/head.ts b/packages/astro/src/runtime/server/render/head.ts index bad8140ec9ab..20e3c714345f 100644 --- a/packages/astro/src/runtime/server/render/head.ts +++ b/packages/astro/src/runtime/server/render/head.ts @@ -34,7 +34,13 @@ export function renderAllHeadContent(result: SSRResult) { .filter(uniqueElements) .map((link) => renderElement('link', link, false)); - let content = links.join('\n') + styles.join('\n') + scripts.join('\n'); + // Order styles -> links -> scripts similar to src/content/runtime.ts + // The order is usually fine as the ordering between these groups are mutually exclusive, + // except for CSS styles and CSS stylesheet links. However CSS stylesheet links usually + // consist of CSS modules which should naturally take precedence over CSS styles, so the + // order will still work. In prod, all CSS are stylesheet links. + // In the future, it may be better to have only an array of head elements to avoid these assumptions. + let content = styles.join('\n') + links.join('\n') + scripts.join('\n'); if (result._metadata.extraHead.length > 0) { for (const part of result._metadata.extraHead) { diff --git a/packages/astro/test/0-css.test.js b/packages/astro/test/0-css.test.js index 9371673d4942..2120452eb53a 100644 --- a/packages/astro/test/0-css.test.js +++ b/packages/astro/test/0-css.test.js @@ -170,6 +170,15 @@ describe('CSS', function () { // 2. check CSS expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy`)); }); + + it('.module.css ordering', () => { + const globalStyleClassIndex = bundledCSS.indexOf('.module-ordering'); + const moduleStyleClassIndex = bundledCSS.indexOf('._module_ordering'); + // css module has higher priority than global style + expect(globalStyleClassIndex).to.be.greaterThan(-1); + expect(moduleStyleClassIndex).to.be.greaterThan(-1); + expect(moduleStyleClassIndex).to.be.greaterThan(globalStyleClassIndex); + }); }); describe('Vue', () => { @@ -412,6 +421,17 @@ describe('CSS', function () { ); }); + it('.module.css ordering', () => { + const globalStyleTag = $('style[data-vite-dev-id$="default.css"]'); + const moduleStyleTag = $('link[href$="ModuleOrdering.module.css"]'); + const globalStyleClassIndex = globalStyleTag.index(); + const moduleStyleClassIndex = moduleStyleTag.index(); + // css module has higher priority than global style + expect(globalStyleClassIndex).to.be.greaterThan(-1); + expect(moduleStyleClassIndex).to.be.greaterThan(-1); + expect(moduleStyleClassIndex).to.be.greaterThan(globalStyleClassIndex); + }); + it('.css?raw return a string', () => { const el = $('#css-raw'); expect(el.text()).to.equal('.foo {color: red;}'); diff --git a/packages/astro/test/fixtures/0-css/src/components/ModuleOrdering.jsx b/packages/astro/test/fixtures/0-css/src/components/ModuleOrdering.jsx new file mode 100644 index 000000000000..2983d2bb2bc5 --- /dev/null +++ b/packages/astro/test/fixtures/0-css/src/components/ModuleOrdering.jsx @@ -0,0 +1,7 @@ +import { module_ordering } from './ModuleOrdering.module.css'; + +export default function Counter() { + return ( +
This should be green
+ ) +} diff --git a/packages/astro/test/fixtures/0-css/src/components/ModuleOrdering.module.css b/packages/astro/test/fixtures/0-css/src/components/ModuleOrdering.module.css new file mode 100644 index 000000000000..dfd292ea2701 --- /dev/null +++ b/packages/astro/test/fixtures/0-css/src/components/ModuleOrdering.module.css @@ -0,0 +1,3 @@ +.module_ordering { + color: green; +} diff --git a/packages/astro/test/fixtures/0-css/src/pages/index.astro b/packages/astro/test/fixtures/0-css/src/pages/index.astro index e0f693feb109..c11fe1166e37 100644 --- a/packages/astro/test/fixtures/0-css/src/pages/index.astro +++ b/packages/astro/test/fixtures/0-css/src/pages/index.astro @@ -1,4 +1,6 @@ --- +import '../styles/default.css' + import AstroComponent from '../components/Astro.astro'; import AstroComponentNone from '../components/AstroNone.astro'; import AstroSass from '../components/AstroSass.astro'; @@ -19,6 +21,7 @@ import VueScoped from '../components/VueScoped.vue'; import VueScss from '../components/VueScss.vue'; import ReactDynamic from '../components/ReactDynamic.jsx'; import SvelteDynamic from '../components/SvelteDynamic.svelte'; +import ModuleOrdering from '../components/ModuleOrdering.jsx'; import '../styles/imported-url.css'; import '../styles/imported.sass'; @@ -76,6 +79,7 @@ import raw from '../styles/raw.css?raw'{raw}+