diff options
author | 2022-09-28 18:12:22 -0400 | |
---|---|---|
committer | 2022-09-28 18:12:22 -0400 | |
commit | 01c1aaa00397c7fdc7a3ef7fb0212eb43aad6238 (patch) | |
tree | 1a14d4e85149112859643127e5aa475ead22ce49 | |
parent | 55a1b5bb58a07cbf6d86818c28c199751bde03b5 (diff) | |
download | astro-01c1aaa00397c7fdc7a3ef7fb0212eb43aad6238.tar.gz astro-01c1aaa00397c7fdc7a3ef7fb0212eb43aad6238.tar.zst astro-01c1aaa00397c7fdc7a3ef7fb0212eb43aad6238.zip |
Fix CSS ordering between imported and Astro styles (#4907)
* Fix CSS ordering between imported and Astro styles
* Fix linting errors
* Add changeset and upgrade compiler version
* Update test to reflect shared styles placed before page styles
12 files changed, 218 insertions, 7 deletions
diff --git a/.changeset/small-bugs-prove.md b/.changeset/small-bugs-prove.md new file mode 100644 index 000000000..16a89271d --- /dev/null +++ b/.changeset/small-bugs-prove.md @@ -0,0 +1,34 @@ +--- +'astro': minor +--- + +Order Astro styles last, to override imported styles + +This fixes CSS ordering so that imported styles are placed *higher* than page/component level styles. This means that if you do: + +```astro +--- +import '../styles/global.css'; +--- +<style> + body { + background: limegreen; + } +</style> +``` + +The `<style>` defined in this component will be placed *below* the imported CSS. When compiled for production this will result in something like this: + +```css +/* /src/styles/global.css */ +body { + background: blue; +} + +/* /src/pages/index.astro */ +body:where(.astro-12345) { + background: limegreen; +} +``` + +Given Astro's 0-specificity hashing, this change effectively makes it so that Astro styles "win" when they have the same specificity as global styles. diff --git a/packages/astro/package.json b/packages/astro/package.json index 0a39c75e1..de6483e9d 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -95,7 +95,7 @@ "test:e2e:match": "playwright test -g" }, "dependencies": { - "@astrojs/compiler": "^0.24.0", + "@astrojs/compiler": "^0.25.0", "@astrojs/language-server": "^0.26.2", "@astrojs/markdown-remark": "^1.1.2", "@astrojs/telemetry": "^1.0.0", diff --git a/packages/astro/test/css-order-import.test.js b/packages/astro/test/css-order-import.test.js new file mode 100644 index 000000000..91cecadab --- /dev/null +++ b/packages/astro/test/css-order-import.test.js @@ -0,0 +1,114 @@ +import { expect } from 'chai'; +import * as cheerio from 'cheerio'; +import { loadFixture } from './test-utils.js'; + +describe('CSS ordering - import order', () => { + /** @type {import('./test-utils').Fixture} */ + let fixture; + before(async () => { + fixture = await loadFixture({ + root: './fixtures/css-order-import/', + }); + }); + + /** + * + * @param {string} html + * @returns {string[]} + */ + function getLinks(html) { + let $ = cheerio.load(html); + let out = []; + $('link[rel=stylesheet]').each((i, el) => { + out.push($(el).attr('href')); + }); + return out; + } + + function getStyles(html) { + let $ = cheerio.load(html); + let out = []; + $('style').each((i, el) => { + out.push($(el).text()); + }); + return out; + } + + /** + * + * @param {string} href + * @returns {Promise<{ href: string; css: string; }>} + */ + async function getLinkContent(href) { + const css = await fixture.readFile(href); + return { href, css }; + } + + describe('Development', () => { + /** @type {import('./test-utils').DevServer} */ + let devServer; + + before(async () => { + devServer = await fixture.startDevServer(); + }); + + after(async () => { + await devServer.stop(); + }); + + it('Page level CSS is defined lower in the page', async () => { + let res = await fixture.fetch('/'); + let html = await res.text(); + let [style1, style2] = getStyles(html); + + expect(style1).to.include('green'); + expect(style2).to.include('salmon'); + }); + + it('import order is depth-first', async () => { + let res = await fixture.fetch('/component/'); + let html = await res.text(); + let [style1, style2, style3] = getStyles(html); + + expect(style1).to.include('burlywood'); + expect(style2).to.include('aliceblue'); + expect(style3).to.include('whitesmoke'); + }); + }); + + describe('Production', () => { + before(async () => { + await fixture.build(); + }); + + it('Page level CSS is defined lower in the page', async () => { + let html = await fixture.readFile('/index.html'); + + const content = await Promise.all( + getLinks(html).map((href) => getLinkContent(href)) + ); + + const [{ css }] = content; + let idx1 = css.indexOf('salmon'); + let idx2 = css.indexOf('green'); + + expect(idx1).to.be.greaterThan(idx2, 'Page level CSS should be placed after imported CSS'); + }); + + it('import order is depth-first', async () => { + let html = await fixture.readFile('/component/index.html'); + + const content = await Promise.all( + getLinks(html).map((href) => getLinkContent(href)) + ); + + const [{ css }] = content; + let idx1 = css.indexOf('whitesmoke'); + let idx2 = css.indexOf('aliceblue'); + let idx3 = css.indexOf('burlywood'); + + expect(idx1).to.be.greaterThan(idx2); + expect(idx2).to.be.greaterThan(idx3); + }); + }); +}); diff --git a/packages/astro/test/css-order.test.js b/packages/astro/test/css-order.test.js index fbcd580dd..e3333b875 100644 --- a/packages/astro/test/css-order.test.js +++ b/packages/astro/test/css-order.test.js @@ -87,9 +87,10 @@ describe('CSS production ordering', () => { ); expect(content).to.have.a.lengthOf(3, 'there are 3 stylesheets'); - const [, found] = content; + const [, sharedStyles, pageStyles] = content; - expect(found.css).to.match(/#00f/); + expect(sharedStyles.css).to.match(/red/); + expect(pageStyles.css).to.match(/#00f/); }); it('CSS injected by injectScript comes first because of import order', async () => { diff --git a/packages/astro/test/fixtures/css-order-import/package.json b/packages/astro/test/fixtures/css-order-import/package.json new file mode 100644 index 000000000..2901a838f --- /dev/null +++ b/packages/astro/test/fixtures/css-order-import/package.json @@ -0,0 +1,6 @@ +{ + "name": "@test/css-order-import", + "dependencies": { + "astro": "workspace:*" + } +} diff --git a/packages/astro/test/fixtures/css-order-import/src/components/One.astro b/packages/astro/test/fixtures/css-order-import/src/components/One.astro new file mode 100644 index 000000000..54b1039b1 --- /dev/null +++ b/packages/astro/test/fixtures/css-order-import/src/components/One.astro @@ -0,0 +1,4 @@ +--- +import '../styles/One.css'; +--- +<link> diff --git a/packages/astro/test/fixtures/css-order-import/src/components/Two.astro b/packages/astro/test/fixtures/css-order-import/src/components/Two.astro new file mode 100644 index 000000000..1afb0ef87 --- /dev/null +++ b/packages/astro/test/fixtures/css-order-import/src/components/Two.astro @@ -0,0 +1,5 @@ +<style> + body { + background: aliceblue; + } +</style> diff --git a/packages/astro/test/fixtures/css-order-import/src/pages/component.astro b/packages/astro/test/fixtures/css-order-import/src/pages/component.astro new file mode 100644 index 000000000..018ab1866 --- /dev/null +++ b/packages/astro/test/fixtures/css-order-import/src/pages/component.astro @@ -0,0 +1,19 @@ +--- +import One from '../components/One.astro'; +import Two from '../components/Two.astro'; +--- +<html> +<head> + <title>Test</title> + <One /> + <Two /> + <style> + body { + background: whitesmoke; + } + </style> +</head> +<body> + <h1>Test</h1> +</body> +</html> diff --git a/packages/astro/test/fixtures/css-order-import/src/pages/index.astro b/packages/astro/test/fixtures/css-order-import/src/pages/index.astro new file mode 100644 index 000000000..0843250c0 --- /dev/null +++ b/packages/astro/test/fixtures/css-order-import/src/pages/index.astro @@ -0,0 +1,16 @@ +--- +import '../styles/base.css'; +--- +<html> +<head> + <title>Test</title> + <style> + body { + background: salmon; + } + </style> +</head> +<body> + <h1>Test</h1> +</body> +</html> diff --git a/packages/astro/test/fixtures/css-order-import/src/styles/One.css b/packages/astro/test/fixtures/css-order-import/src/styles/One.css new file mode 100644 index 000000000..66d2bb71b --- /dev/null +++ b/packages/astro/test/fixtures/css-order-import/src/styles/One.css @@ -0,0 +1,3 @@ +body { + background: burlywood; +} diff --git a/packages/astro/test/fixtures/css-order-import/src/styles/base.css b/packages/astro/test/fixtures/css-order-import/src/styles/base.css new file mode 100644 index 000000000..828bff206 --- /dev/null +++ b/packages/astro/test/fixtures/css-order-import/src/styles/base.css @@ -0,0 +1,3 @@ +body { + background: green; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aaeec592c..67b08ecc1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -349,7 +349,7 @@ importers: packages/astro: specifiers: - '@astrojs/compiler': ^0.24.0 + '@astrojs/compiler': ^0.25.0 '@astrojs/language-server': ^0.26.2 '@astrojs/markdown-remark': ^1.1.2 '@astrojs/telemetry': ^1.0.0 @@ -443,7 +443,7 @@ importers: yargs-parser: ^21.0.1 zod: ^3.17.3 dependencies: - '@astrojs/compiler': 0.24.0 + '@astrojs/compiler': 0.25.0 '@astrojs/language-server': 0.26.2 '@astrojs/markdown-remark': link:../markdown/remark '@astrojs/telemetry': link:../telemetry @@ -1584,6 +1584,12 @@ importers: dependencies: astro: link:../../.. + packages/astro/test/fixtures/css-order-import: + specifiers: + astro: workspace:* + dependencies: + astro: link:../../.. + packages/astro/test/fixtures/custom-404: specifiers: astro: workspace:* @@ -3639,8 +3645,8 @@ packages: resolution: {integrity: sha512-vBMPy9ok4iLapSyCCT1qsZ9dK7LkVFl9mObtLEmWiec9myGHS9h2kQY2xzPeFNJiWXUf9O6tSyQpQTy5As/p3g==} dev: false - /@astrojs/compiler/0.24.0: - resolution: {integrity: sha512-xZ81C/oMfExdF18I1Tyd2BKKzBqO+qYYctSy4iCwH4UWSo/4Y8A8MAzV1hG67uuE7hFRourSl6H5KUbhyChv/A==} + /@astrojs/compiler/0.25.0: + resolution: {integrity: sha512-thJdIFuKT7f6uzxUs5d7qjh3g/L4kmlSfddAcbC62QEMy4H9N9fig3+FBwg9exUvXYcqOjZ/nC2PsGsVIqmZYA==} dev: false /@astrojs/language-server/0.26.2: |