CSS Modules
Scoped CSS with automatic class names
CSS Modules
CSS Modules give you locally scoped class names, eliminating naming collisions.
Packem handles them through @visulima/rollup-plugin-css
and can generate scoped names automatically for *.module.css files.
Overview
This example demonstrates:
- Enabling CSS Modules for
*.module.*files viaautoModules - Customizing scoped name generation
- Importing scoped class names into TypeScript
- Generating companion
.d.tsfiles for type-safe class names
Setup
CSS Modules build on PostCSS, so install postcss:
npm install --save-dev postcssDirectory Structure
my-package/
├── src/
│ ├── Button.ts
│ └── Button.module.css
├── package.json
└── packem.config.tsSource Files
src/Button.module.css
.button {
padding: 0.5rem 1rem;
border: none;
border-radius: 0.25rem;
cursor: pointer;
}
.primary {
background-color: #007bff;
color: white;
}
.secondary {
background-color: #6c757d;
color: white;
}src/Button.ts
import styles from './Button.module.css'
export function createButton(variant: 'primary' | 'secondary' = 'primary') {
const button = document.createElement('button')
button.className = `${styles.button} ${styles[variant]}`
button.textContent = 'Click me'
return button
}Configuration
Enable CSS Modules with the autoModules option. It accepts a boolean, a
regular expression, or a function. When true, it applies to files named
[name].module.[ext] (for example Button.module.css).
import { defineConfig } from '@visulima/packem/config'
import transformer from '@visulima/packem/transformer/esbuild'
import postcssLoader from '@visulima/packem/css/loader/postcss'
export default defineConfig({
transformer,
rollup: {
css: {
mode: 'extract',
loaders: [postcssLoader],
autoModules: true,
},
},
})To restrict CSS Modules to a custom pattern, pass a regular expression:
css: {
loaders: [postcssLoader],
autoModules: /\.module\./,
}Customizing Scoped Names
Module behavior is configured through postcss.modules. The
generateScopedName placeholder controls the emitted class names (default
"[name]_[local]_[hash:8]").
export default defineConfig({
transformer,
rollup: {
css: {
mode: 'extract',
loaders: [postcssLoader],
postcss: {
modules: {
generateScopedName: '[name]__[local]___[hash:base64:5]',
exportGlobals: true,
},
},
},
},
})Available modules options:
generateScopedName— placeholder string or function for scoped name generationmode— default scoping mode ("local", default)exportGlobals— export global class namesfailOnWrongOrder— fail on wrong composition orderinclude— files to treat as CSS Modules
You can also enable modules through postcss.modules directly (set it to true
or an options object) instead of using autoModules.
Type-Safe Class Names
Set the dts option to generate companion .d.ts files alongside your CSS
Modules, giving you IntelliSense and compile-time checks for class names.
css: {
mode: 'extract',
loaders: [postcssLoader],
autoModules: true,
dts: true,
}This emits a declaration file next to each module:
// src/Button.module.css.d.ts (auto-generated)
declare const button: string
declare const primary: string
declare const secondary: string
interface ModulesExports {
button: string
primary: string
secondary: string
}
declare const styles: ModulesExports
export default styles
export { button, primary, secondary }Named Exports
Enable namedExports to export each class as a named binding alongside the
default export:
css: {
loaders: [postcssLoader],
autoModules: true,
namedExports: true,
}Building
packem build