diff --git a/.changeset/twenty-numbers-complain.md b/.changeset/twenty-numbers-complain.md new file mode 100644 index 000000000..5dbb576a3 --- /dev/null +++ b/.changeset/twenty-numbers-complain.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fix scoped CSS selector when class contains a colon diff --git a/packages/astro/src/compiler/transform/postcss-scoped-styles/index.ts b/packages/astro/src/compiler/transform/postcss-scoped-styles/index.ts index 3f00cd92b..dd885ffb2 100644 --- a/packages/astro/src/compiler/transform/postcss-scoped-styles/index.ts +++ b/packages/astro/src/compiler/transform/postcss-scoped-styles/index.ts @@ -82,7 +82,7 @@ export function scopeRule(selector: string, className: string) { // scope everything else let newSelector = value; - const pseudoIndex = newSelector.indexOf(':'); + const pseudoIndex = newSelector.search(/(? 0) { // if there’s a pseudoclass (:focus or ::before) ss = head + newSelector.substring(0, pseudoIndex) + c + newSelector.substr(pseudoIndex) + tail; diff --git a/packages/astro/test/astro-scoped-styles.test.js b/packages/astro/test/astro-scoped-styles.test.js index 90ea5b785..548386168 100644 --- a/packages/astro/test/astro-scoped-styles.test.js +++ b/packages/astro/test/astro-scoped-styles.test.js @@ -27,6 +27,8 @@ ScopedStyles('Scopes rules correctly', () => { from: 'from', // ignore keyframe keywords (below) to: 'to', '55%': '55%', + '.class\\:class': `.class\\:class.${className}`, // classes can contain special characters if escaped + '.class\\:class:focus': `.class\\:class.${className}:focus`, }; for (const [given, expected] of Object.entries(tests)) { diff --git a/packages/astro/test/astro-styles-ssr.test.js b/packages/astro/test/astro-styles-ssr.test.js index 1e0f69b31..8a3fd7bdc 100644 --- a/packages/astro/test/astro-styles-ssr.test.js +++ b/packages/astro/test/astro-styles-ssr.test.js @@ -9,7 +9,7 @@ const StylesSSR = suite('Styles SSR'); function cssMinify(css) { return css .trim() // remove whitespace - .replace(/\n\s*/g, '') // collapse lines + .replace(/\r?\n\s*/g, '') // collapse lines .replace(/\s*\{/g, '{') // collapse selectors .replace(/:\s*/g, ':') // collapse attributes .replace(/;}/g, '}'); // collapse block @@ -121,6 +121,9 @@ StylesSSR('Astro scoped styles', async ({ runtime }) => { assert.match(el1.attr('class'), `blue ${scopedClass}`); assert.match(el2.attr('class'), `visible ${scopedClass}`); + + const { contents: css } = await runtime.load('/_astro/src/components/Astro.astro.css'); + assert.match(cssMinify(css.toString()), `.blue.${scopedClass}{color:powderblue}.color\\:blue.${scopedClass}{color:powderblue}.visible.${scopedClass}{display:block}`); }); StylesSSR.run(); diff --git a/packages/astro/test/fixtures/astro-styles-ssr/src/components/Astro.astro b/packages/astro/test/fixtures/astro-styles-ssr/src/components/Astro.astro index 42967c445..103b00f4c 100644 --- a/packages/astro/test/fixtures/astro-styles-ssr/src/components/Astro.astro +++ b/packages/astro/test/fixtures/astro-styles-ssr/src/components/Astro.astro @@ -8,6 +8,10 @@ let visible = true; color: powderblue; } +.color\:blue { + color: powderblue; +} + .visible { display: block; } @@ -16,3 +20,4 @@ let visible = true;