-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
Svelte 5: add $style and CSS modules support (like Vue) to fully obfuscate/isolate class names #9999
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
You can already achieve this behaviour by using svelte-preprocess-cssmodules. Not sure if this needs to be added in svelte directly |
Thank you! I don't know this. Here is one little problem: so I get errors:
I used --force to install
I'm not sure either... But css scoping is not perfect at the moment. svelte-preprocess-cssmodules
|
hmm that's odd. You might want to file an issue on the svelte-preprocess-cssmodules repo. I do have some trouble getting it to work in a test project myself... Anyways, I believe this issue is a duplicate of #6972 and should be closed. |
This would be great considering svelte-preprocess-cssmodules has not been updated in a while |
I've been able to achieve a similar functionality (well, styling children by passing classnames defined by parent) by using a very minimal (small enough to copy-paste 😅) preprocessor. import MagicString from 'magic-string';
import { parse } from 'svelte/compiler';
/**
* @returns {import('svelte/compiler').PreprocessorGroup}
*/
export function svelteLocalSelector() {
return {
name: 'svelte-local-selector',
markup: markupPreprocessor(),
};
}
function markupPreprocessor() {
/** @type {import('svelte/compiler').Preprocessor} */
return ({ content, filename }) => {
const localClassNames = {};
const ast = parse(content);
const css = ast.css;
const js = ast.instance;
const style = stylePreprocessor(localClassNames);
const script = scriptPreprocessor(localClassNames);
const s = new MagicString(content);
if (css) {
const updatedStyles = style(css.content.styles);
if (Object.keys(localClassNames).length > 0) {
s.update(css.content.start, css.content.end, updatedStyles);
const { start, end } = js.content;
s.update(start, end, script(content.slice(start, end)));
}
}
const map = s.generateMap({ file: filename });
return {
code: s.toString(),
map,
};
};
}
const localPseudoClassSelectorRegex = /:local\(\.([\w-]+)\)/g;
function stylePreprocessor(localClassNames) {
/** @type {(content: string) => string} */
return (content) => {
const id = hash(content);
return content.replaceAll(localPseudoClassSelectorRegex, (_, className) => {
const nextValue = `${className}-${id}`;
localClassNames[className] = nextValue;
return `:global(.${nextValue})`;
});
};
}
function scriptPreprocessor(localClassNames) {
/** @type {(content: string) => string} */
return (content) => {
if (Object.keys(localClassNames).length > 0) {
return content + functionTemplate(localClassNames);
}
return content;
};
}
function functionTemplate(localClassNames = {}) {
return `
function __styles() {
return ${JSON.stringify(localClassNames)};
}
`;
}
/**
* @param {string} str
* @returns {string}
*/
function hash(str) {
str = str.replace(/\r/g, '');
let hash = 5381;
let i = str.length;
while (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i);
return (hash >>> 0).toString(36);
} Usage example: <script>
import Nav from './Nav.svelte';
const { nav } = __styles();
</script>
<style>
:local(.nav) {
margin-inline: auto;
}
</style>
<Nav className={nav} /> |
Describe the problem
First... Vue has this functionality - I miss in Svelte
https://vuejs.org/api/sfc-css-features.html#css-modules
vitejs/vite#7447 (comment)
Code like this in Svelte:
will generate such CSS:
.block.svelte-3zrlxr{display:block}
.block is my own custom class name
.svelte-3zrlxr is auto-generated class name for whole component
But this is not enough!
This was a "cheap" solution, that have side effects!
Today... custom class names (like block) can collide with GLOBAL class names.
This means, that custom used classes are simple not isolated!
Describe the proposed solution
Svelte like solution. Example code:
(module was added to not break compatibility)
or convert (auto obfuscate)
Both should be possible
Alternatives considered
CSS-modules work today. They obfuscate class names.
Vite support CSS modules: https://vitejs.dev/guide/features#css-modules
If i write code like this, it works:
It generate this class name for "block":
_block_1xra7_1
But now, I need to split component in separate files.
I search a way to use <style> block in Svelte file and not an external module.css
Importance
would make my life easier
The text was updated successfully, but these errors were encountered: