svelte-preprocess
svelte-preprocess copied to clipboard
Global styles not supporting composed selectors like :is()
Problem
Using the global attribute on a style block will prevent any use of composed CSS selectors inside it. Markup as simple as the following will trigger the error.
<style global>
article :is(h1, h2) {
font-family: "EB Garamond";
}
</style>
Output
<redacted>/src/layouts/article.svelte:47:21 :global(...) must contain a single selector
45 | }
46 |
47 | :global(article) :global(:is(h1), :global(h2)) {
^
48 | font-family: "EB Garamond";
49 | }
I would expect :global(article h1) :global(article h2) to be a more correct preprocessed output.
svelte: 3.55.1 svelte-preprocess: 5.0.1
Having the same issue:
<style lang="scss" global>
.highlight {
background: yellow;
}
div:has(input.highlight) {
span {
color: red;
}
}
.test {
input {
@extend .highlight;
}
}
</style>
Output
/src/routes/+page.svelte:320:0 :global(...) must contain a single selector
/src/routes/+page.svelte:320:0
318 | }
319 |
320 | :global(div:has(input.highlight), :global(.test) :global(input)) :global(span) {
^
321 | color: red;
322 | }
A workaround would be to wrap the problematic part in :global(), but this is not feasible when the problematic part is in an external scss file that we are just extending inside the global style tag.
<style lang="scss" global>
@use 'node_modules/.../highlight.scss';
.test {
input {
@extend .highlight;
}
}
</style>
A potential fix would be to set the combinatorPattern to the following:
/(?<!\\)(?:\\\\)*([ >+~,]\s*)(?![^(]*\))(?![^[]+\]|\d)/g
It excludes the content of the parentheses.
This bit me hard when migrating from Tailwind v2 to v3. The symptom was that dark mode in my app stopped being dark, it was stuck in light mode. They changed how class-based dark mode works, and it took me way too long to realize that the breakage was being caused by svelte-preprocess.
Selectors that should look like this:
:is(.dark .dark\:bg-black)
are instead emitted like this:
:is(.dark) :global(.dark\:bg-black)
when one brings in tailwind like this:
<style global lang="postcss">
@tailwind base;
@tailwind components;
@tailwind utilities;
</style>
Quite the bummer. Now I'll have to find some workaround based on patching/forking svelte-preprocess or by doing string replacements on the resulting CSS bundle, and I'm not sure which approach I'm least looking forward to 😅
This bit me hard when migrating from Tailwind v2 to v3. The symptom was that dark mode in my app stopped being dark, it was stuck in light mode. They changed how class-based dark mode works, and it took me way too long to realize that the breakage was being caused by svelte-preprocess.
Selectors that should look like this:
:is(.dark .dark\:bg-black)are instead emitted like this:
:is(.dark) :global(.dark\:bg-black)when one brings in tailwind like this:
<style global lang="postcss"> @tailwind base; @tailwind components; @tailwind utilities; </style>Quite the bummer. Now I'll have to find some workaround based on patching/forking svelte-preprocess or by doing string replacements on the resulting CSS bundle, and I'm not sure which approach I'm least looking forward to 😅
I'm having this exact issue right now. Any temp fixes so far?
@alvissraghnall I'm sorry I'm some months late but it turns out I wasn't subscribed to this issue and only saw your message when checking if this issue was fixed. The "fix" I've found is to use patch-package to modify combinatorPattern. The patch looks like this:
diff --git a/node_modules/svelte-preprocess/dist/modules/globalifySelector.js b/node_modules/svelte-preprocess/dist/modules/globalifySelector.js
index fff3bd6..e1c72a9 100644
--- a/node_modules/svelte-preprocess/dist/modules/globalifySelector.js
+++ b/node_modules/svelte-preprocess/dist/modules/globalifySelector.js
@@ -9,7 +9,7 @@ exports.globalifySelector = void 0;
* escaped combinators like `\~`.
*/
// TODO: maybe replace this ugly pattern with an actual selector parser? (https://github.com/leaverou/parsel, 2kb)
-const combinatorPattern = /(?<!\\)(?:\\\\)*([ >+~,]\s*)(?![^[]+\]|\d)/g;
+const combinatorPattern = /(?<!\\)(?:\\\\)*([ >+~,]\s*)(?![^(]*\))(?![^[]+\]|\d)/g;
function globalifySelector(selector) {
const parts = selector.trim().split(combinatorPattern);
const newSelector = [];