vite-css-modules

repository·develop·Indexed 41 days ago

https://github.com/privatenumber/vite-css-modules

A Vite plugin designed to fix broken behaviors in Vite's native CSS Modules implementation, including PostCSS plugin skipping, style duplication, silent failures on missing classes, and broken HMR. It provides features for generating TypeScript declarations (.d.ts) for autocomplete and "Go to Definition" navigation, a CLI for generating types without a full build, and support for ES2022 named exports to handle special characters in class names.

Tokens
3.6K
Snippets
13
Records
17
Agent score
97%

What's inside vite-css-modules

  1. How vite-css-modules works

    develop

    vite-css-modules integrates CSS Modules directly into Vite's build pipeline by treating them as JavaScript modules. This solves several issues inherent in the default postcss-modules delegation used by Vite:

    • Full Build Integration: CSS Modules are transformed into JS files that load the CSS. This allows Vite plugins (like SCSS, PostCSS, or LightningCSS) to hook into the build process and process internal dependencies.
    • Module Graph Integration: By becoming JS modules, CSS Modules join Vite's module graph, enabling proper dependency resolution, bundling, and de-duplication of shared dependencies.
    • Improved Error Handling: The plugin detects and throws descriptive errors for circular composes dependencies (e.g., Circular CSS Module dependency: "a.module.css" -> "b.module.css" -> "a.module.css") and prevents silent failures on unresolved dependencies.

    This approach is conceptually similar to Webpack's css-loader.

  2. Setup vite-css-modules in Vite

    develop

    To patch Vite's CSS Modules behavior, add the patchCssModules() plugin to your vite.config.js (or .ts). You should also ensure your build target is set to at least es2022 for optimal results. Configuration for CSS Modules itself remains handled via standard Vite css.modules or lightningcss settings.

    // vite.config.js
    import { patchCssModules } from 'vite-css-modules'
    
    export default {
        plugins: [
            patchCssModules() // ← This is all you need to add!
    
            // Other plugins...
        ],
        css: {
            // Your existing CSS Modules configuration
            modules: {
                // ...
            },
            // Or if using LightningCSS
            lightningcss: {
                cssModules: {
                    // ...
                }
            }
        },
        build: {
            // Recommended minimum target
            target: 'es2022'
        }
    }
  3. Enable Type Checking and Navigation for CSS Modules

    develop

    You can generate .d.ts files for your CSS Modules to enable autocomplete and type checking in TypeScript. To enable "Go to Definition" (jumping from a TS class name to the CSS source), you must enable both generateSourceTypes and declarationMap.

    patchCssModules({
        generateSourceTypes: true,
        declarationMap: true
    })
  4. Importing CSS Module class names with special characters

    develop

    By default, CSS class names containing special characters (like hyphens in .foo-bar) cannot be used as named exports in older JavaScript environments because they are invalid variable names.

    You have two primary ways to handle this:

    1. Use camelCase convention

    Set the localsConvention to camelCase in your Vite configuration. This automatically converts kebab-case class names into camelCase named exports.

    2. Use ES2022 named exports

    If you set your build target to es2022, you can use arbitrary strings as named exports by using quotes in your import statement. This allows you to import .foo-bar directly without changing the naming convention.

    // vite.config.js
    export default {
        build: {
            target: 'es2022'
        }
    }
    
    // Usage in your component
    import { 'foo-bar' as fooBar } from './styles.module.css'
    
    console.log(fooBar) // Outputs the class name
  5. Configure patchCssModules options

    develop

    The patchCssModules(options) function accepts the following configuration options:

    • exportMode: Determines how class names are exported. Defaults to 'both'.
      • 'both': Exports class names as both named and default exports.
      • 'named': Exports class names as named exports only.
      • 'default': Exports class names as a default export only (an object where keys are class names).
    • generateSourceTypes: A boolean that, when true, generates a .d.ts file next to each CSS Module. Defaults to false.
    • declarationMap: A boolean that generates inline declaration source maps in .d.ts files to enable "Go to Definition". Defaults to auto-detection from tsconfig.json's compilerOptions.declarationMap. Requires generateSourceTypes: true.
  6. Configure the vite-css-modules plugin via PatchConfig

    develop

    The cssModules factory function accepts an optional patchConfig object to customize how CSS Modules are exported and how type definitions are generated. This is primarily used when the plugin is being invoked internally by Vite.

    Available options:

    • exportMode: Determines the export strategy for CSS Modules.
      • 'both' (default): Exports both the default export and named exports.
      • 'default': Exports only the default export.
      • 'named': Exports only named exports.
    • generateSourceTypes: A boolean that, when true, generates TypeScript declaration (.d.ts) files next to your CSS modules (e.g., style.module.css.d.ts).
    • declarationMap: Enables inline declaration source maps in the generated .d.ts files. This allows "Go to Definition" to navigate from TypeScript directly to the CSS source. If undefined, it auto-detects the setting from your tsconfig.json's compilerOptions.declarationMap. Requires generateSourceTypes to be true.
    // Example PatchConfig shape
    const patchConfig: PatchConfig = {
      exportMode: 'named',
      generateSourceTypes: true,
      declarationMap: true
    };
  7. Configure CSS Modules class name conventions

    develop

    You can control how CSS module class names are transformed (e.g., converting kebab-case to camelCase) using the localsConvention option in your CSS Modules configuration. This option is compatible with both CSSModulesOptions (Vite) and CSSModulesConfig (LightningCSS).

    Supported values for localsConvention:

    • Function: A custom LocalsConventionFunction to define your own transformation logic.
    • 'camelCase' or 'camelCaseOnly': Transforms class names to camelCase (e.g., my-class becomes myClass).
    • 'dashes' or 'dashesOnly': Transforms class names to use dashes (e.g., myClass becomes my-class).

    Note: If localsConvention is not provided or is not one of the specific string/function types mentioned, the plugin may not keep the original export depending on the internal shouldKeepOriginalExport check.

    // Example configuration using 'camelCaseOnly'
    // This works within your Vite or LightningCSS CSS modules config
    const config = {
      localsConvention: 'camelCaseOnly'
    };
  8. Reference: vite-css-modules CLI flags

    develop

    The following flags are available for the vite-css-modules CLI:

    | Flag | Description |
    | :--- | :--- |
    | `--config <path>` | Use a specific `vite.config.*` file |
    | `--mode <mode>` | Set the Vite mode used when loading config |
    | `--silent`, `-s` | Suppress success output (errors still print to stderr) |
  9. Use the vite-css-modules CLI

    develop

    The CLI allows you to generate TypeScript declarations for CSS Modules without running a full build. It requires patchCssModules() to be present in your Vite config.

    Usage: npx vite-css-modules [globs...] [--config path] [--mode mode]

    • If no globs are provided, it defaults to **/*.module.{css,scss,sass} under the Vite root.
    • If globs are provided, they are resolved from the current working directory.
    • The CLI uses your existing vite.config.* file.
    # Generate types for CSS Modules under the Vite root
    npx vite-css-modules
    
    # Use explicit globs from the current working directory
    npx vite-css-modules 'src/**/*.module.css'
    
    # Use a specific Vite config
    npx vite-css-modules 'src/**/*.module.css' --config apps/web/vite.config.ts
    
    # Load config with production mode
    npx vite-css-modules 'src/**/*.module.css' --mode production
  10. Use the cssModules plugin factory

    develop

    The cssModules function is the primary entry point for creating the Vite plugin. It is designed to be used by Vite internally, taking the ResolvedConfig and an optional PatchConfig to return a Vite Plugin instance.

    Note: This function is intended for internal Vite usage rather than direct end-user plugin registration in a standard vite.config.ts.

    import { cssModules } from 'vite-css-modules';
    
    // Internal usage pattern
    const plugin = cssModules(resolvedConfig, patchConfig);