Heading
My lorem ipsum text
Svelte preprocessor to generate CSS Modules classname on Svelte components
npm install svelte-preprocess-cssmodulesGenerate CSS Modules classnames on Svelte components
``bash`
npm install --save-dev svelte-preprocess-cssmodules
for svelte 4 and below, use version 2 of the preprocessor.
- Usage
- Approach
- Class objects and arrays
- Class directive
- Local selector
- CSS binding
- Scoped class on child components
- Import styles from an external stylesheet
- Destructuring import
- kebab-case situation
- Unnamed import
- Directive and dynamic class
- Preprocessor Modes
- Native
- Mixed
- Scoped
- Comparative
- Why CSS Modules over Svelte scoping?
- Configuration
- Rollup
- Webpack
- SvelteKit
- Svelte Preprocess
- Vite
- Options
- Code example
Add the module attribute to
My red text
The component will be compiled to
`html
My red text
`$3
The default svelte scoping appends every css selectors with a unique class to only affect the elements of the component.
CSS Modules scopes each class name with a unique id/name in order to affect the elements of the component. As the other selectors are not scoped, it is recommended to write each selector with a class.
`html
lorem ipsum tut moue
lorem ipsum tut moue
`
`html
lorem ipsum tut moue
lorem ipsum tut moue
`_transformed to_
`html
lorem ipsum tut moue
lorem ipsum tut moue
``html
lorem ipsum tut moue
lorem ipsum tut moue
`$3
#### Object with thruthy values
`html
...
`generating
`html
...
...
`#### Array with thruthy values
`html
...
`generating
`html
...
...
`$3
Toggle a class on an element.
`html
`_generating_
`html
`#### Use of shorthand
`html
`_generating_
`html
`$3
Force a selector to be scoped within its component to prevent style inheritance on child components.
:local() is doing the opposite of :global() and can only be used with the native and mixed modes (see preprocessor modes). The svelte scoping is applied to the selector inside :local().`html
My main lorem ipsum tuye
`
`html
My secondary lorem ipsum tuye
`generating
`html
My main lorem ipsum tuye
`
`html
My secondary lorem ipsum tuye
`When used with a class,
:local() cssModules is replaced by the svelte scoping system. This could be useful when targetting global classnames.`html
`generating
`html
`$3
Link the value of a CSS property to a dynamic variable by using
bind().`html
My lorem ipsum text
`A scoped css variable, binding the declared statement, will be created on the component root elements which the css property will inherit from.
`html
My lorem ipsum text
`An object property can also be targetted and must be wrapped with quotes.
`html
Heading
My lorem ipsum text
`_generating_
`html
Heading
My lorem ipsum text
`$3
CSS Modules allows you to pass a scoped classname to a child component giving the possibility to style it from its parent. (Only with the
native and mixed modes – See preprocessor modes).`html
``html
Welcome
Lorem ipsum tut ewou tu po
`_generating_
`html
Welcome
Lorem ipsum tut ewou tu po
`
Import styles from an external stylesheet
Alternatively, styles can be created into an external file and imported onto a svelte component. The name referring to the import can then be used on the markup to target any existing classname of the stylesheet.
- The option
parseExternalStylesheet need to be enabled.
- The css file must follow the convention [FILENAME].module.css in order to be processed.Note: That import is only meant for stylesheets relative to the component. You will have to set your own bundler in order to import node_modules css files.
`css
/ style.module.css /
.red { color: red; }
.blue { color: blue; }
`
`html
My red text
My blue text
`generating
`html
My red text
My blue text
`$3
`css
/ style.module.css /
section { padding: 10px; }
.red { color: red; }
.blue { color: blue; }
.bold { font-weight: bold; }
`
`html
My red text
My blue text
`generating
`html
My red text
My blue text
`$3
The kebab-case class names are being transformed to a camelCase version to facilitate their use on Markup and Javascript.
`css
/ style.module.css /
.success { color: green; }
.error-message {
color: red;
text-decoration: line-through;
}
`
`html
My success text
My error message
My success message
My error message
`generating
`html
My success messge
`$3
If a css file is being imported without a name, CSS Modules will still apply to the classes of the stylesheet.
`css
/ style.module.css /
p { font-size: 18px; }
.success { color: green; }
`
`html
My success message
My another message
`generating
`html
My success messge
My error message
`$3
Use the Svelte's builtin
class: directive or javascript template to display a class dynamically.
Note: the shorthand directive is NOT working with imported CSS Module identifiers.`html
Success
class:success={isSuccess}
class:error={!isSuccess}>NoticeNotice
Notice
`Preprocessor Modes
The mode can be set globally from the config or locally to override the global setting.
$3
Scopes classes with CSS Modules, anything else is unscoped.
Pros:
- uses default CSS Modules approach
- creates unique ID to avoid classname conflicts and unexpected inheritances
- passes scoped class name to child components
Cons:
- does not scope non class selectors.
- forces to write selectors with classes.
- needs to consider third party plugins with
useAsDefaultScoping on – Read more.$3
Scopes non-class selectors with svelte scoping in addition to
native (same as preprocessor v1)`html
My red text
`_generating_
`html
My red text
`Pros:
- creates class names with unique ID to avoid conflicts and unexpected inheritances
- uses svelte scoping on non class selectors
- passes scoped class name to child components
Cons:
- adds more weight to tag selectors than class selectors (because of the svelte scoping)
`html
- Home
- About
- Home
- About
`$3
Scopes classes with svelte scoping in addition to
mixed.`html
My red text
`_generating_
`html
My red text
`Pros:
- creates class names with unique ID to avoid conflicts and unexpected inheritances
- scopes every selectors at equal weight
Cons:
- does not pass scoped classname to child components
$3
| | Svelte scoping | Preprocessor Native | Preprocessor Mixed | Preprocessor Scoped |
| -------------| ------------- | ------------- | ------------- | ------------- |
| Scopes classes | O | O | O | O |
| Scopes non class selectors | O | X | O | O |
| Creates unique class ID | X | O | O | O |
| Has equal selector weight | O | O | X | O |
| Passes scoped classname to a child component | X | O | O | X |
Why CSS Modules over Svelte scoping?
- On a full svelte application: it is just a question of taste as the default svelte scoping is largely enough. Component styles will never inherit from other styling.
- On a hybrid project (like using svelte to enhance a web page): the default scoping may actually inherits from a class of the same name belonging to the style of the page. In that case using CSS Modules to create a unique ID and to avoid class inheritance might be advantageous.
Configuration
$3
rollup-plugin-svelte.`js
import svelte from 'rollup-plugin-svelte';
import { cssModules } from 'svelte-preprocess-cssmodules';export default {
...
plugins: [
svelte({
preprocess: [
cssModules(),
]
}),
]
...
}
`$3
svelte-loader.`js
const { cssModules } = require('svelte-preprocess-cssmodules');module.exports = {
...
module: {
rules: [
{
test: /\.svelte$/,
exclude: /node_modules/,
use: [
{
loader: 'svelte-loader',
options: {
preprocess: [
cssModules(),
]
}
}
]
}
]
}
...
}
`$3
As the module distribution is targetting
esnext, Node.js 14 or above is required
in order to work. `js
// svelte.config.jsimport { cssModules } from 'svelte-preprocess-cssmodules';
const config = {
...
preprocess: [
cssModules(),
]
};
export default config;
`$3
The CSS Modules preprocessor requires the compoment to be a standard svelte component (using vanilla js and vanilla css). if any other code, such as Typescript or Sass, is encountered, an error will be thrown. Therefore CSS Modules needs to be run at the very end.
`js
import { typescript, scss } from 'svelte-preprocess';
import { cssModules } from 'svelte-preprocess-cssmodules';...
// svelte config:
preprocess: [
typescript(),
scss(),
cssModules(), // run last
],
...
`$3
Set the
svelte.config.js accordingly.`js
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
import { cssModules } from 'svelte-preprocess-cssmodules';export default {
preprocess: [
vitePreprocess(),
cssModules()
]
};
`
$3
Pass an object of the following properties| Name | Type | Default | Description |
| ------------- | ------------- | ------------- | ------------- |
|
cssVariableHash | {String} | [hash:base64:6] | The hash type (see locatonIdentName) |
| getLocalIdent | Function | undefined | Generate the classname by specifying a function instead of using the built-in interpolation |
| hashSeeder | {Array} | ['style', 'filepath', 'classname'] | An array of keys to base the hash on |
| includeAttributes | {Array} | [] | An array of attributes to parse along with class |
| includePaths | {Array} | [] (Any) | An array of paths to be processed |
| localIdentName | {String} | "[local]-[hash:base64:6]" | A rule using any available token |
| mode | native\|mixed\|scoped | native | The preprocess mode to use
| parseExternalStylesheet | {Boolean} | false | Enable parsing on imported external stylesheet |
| parseStyleTag | {Boolean} | true | Enable parsing on style tag |
| useAsDefaultScoping | {Boolean} | false | Replace svelte scoping globally |####
getLocalIdentCustomize the creation of the classname instead of relying on the built-in function.
`ts
function getLocalIdent(
context: {
context: string, // the context path
resourcePath: string, // path + filename
},
localIdentName: {
template: string, // the template rule
interpolatedName: string, // the built-in generated classname
},
className: string, // the classname string
content: {
markup: string, // the markup content
style: string, // the style content
}
): string {
return your_generated_classname;
}
`
Example of use
`bash
Directory
SvelteApp
└─ src
├─ App.svelte
└─ components
└─ Button.svelte
`
`html
``js
// Preprocess config
...
preprocess: [
cssModules({
localIdentName: '[path][name]__[local]',
getLocalIdent: (context, { interpolatedName }) => {
return interpolatedName.toLowerCase().replace('src_', '');
// svelteapp_components_button__red;
}
})
],
...
`####
hashSeederSet the source of the hash (when using
[hash] / [contenthash]).The list of available keys are:
-
style the content of the style tag (or the imported stylesheet)
- filepath the path of the component
- classname the local classnameExample of use: creating a common hash per component
`js
// Preprocess config
...
preprocess: [
cssModules({
hashSeeder: ['filepath', 'style'],
})
],
...
`
`html
`_generating_
`html
`####
includeAttributesAdd other attributes than
class to be parsed by the preprocesser`js
// Preprocess config
...
preprocess: [
cssModules({
includeAttributes: ['data-color', 'classname'],
})
],
...
`
`html
`_generating_
`html
`####
localIdentNameInspired by webpack interpolateName, here is the list of tokens:
-
[local] the targeted classname
- [ext] the extension of the resource
- [name] the basename of the resource
- [path] the path of the resource
- [folder] the folder the resource is in
- [contenthash] or [hash] (they are the same) the hash of the resource content (by default it's the hex digest of the md5 hash)
- [ optionally one can configure
- other hashTypes, i. e. sha1, md5, sha256, sha512
- other digestTypes, i. e. hex, base26, base32, base36, base49, base52, base58, base62, base64
- and length the length in chars####
useAsDefaultScopingGlobally replace the default svelte scoping by the CSS Modules scoping. As a result, the
module attribute to _generating_
`html
Welcome
`Potential issue with third party plugins
The preprocessor requires you to add the
module attribute to Lorem ipsum dolor sit, amet consectetur.
`
Final html code generated by svelte
`html
Lorem ipsum dolor sit, amet consectetur.
``