summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/twenty-numbers-complain.md5
-rw-r--r--packages/astro/src/compiler/transform/postcss-scoped-styles/index.ts2
-rw-r--r--packages/astro/test/astro-scoped-styles.test.js2
-rw-r--r--packages/astro/test/astro-styles-ssr.test.js5
-rw-r--r--packages/astro/test/fixtures/astro-styles-ssr/src/components/Astro.astro5
5 files changed, 17 insertions, 2 deletions
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(/(?<!\\):/);
if (pseudoIndex > 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;
<div id="class">I’m just used to get the Scoped class</div>
<div id="dynamic-class" class={blue ? 'blue' : 'notblue'}>I change colors</div>
{visible && <div id="dynamic-vis" class="visible">I disappear</div>}
+<div id="colon-class" class="color:blue">I am blue</div>