PostCSS plugin to use CSS Modules everywhere
npx @tessl/cli install tessl/npm-postcss-modules@6.0.0PostCSS Modules is a PostCSS plugin that enables CSS Modules functionality everywhere, not just client-side applications. It transforms CSS classes into scoped, locally unique identifiers while preserving global styles where needed, providing automatic collision prevention and maintainable class name management.
npm install postcss-modulesconst postcssModules = require("postcss-modules");For TypeScript or ES modules:
import postcssModules = require("postcss-modules");
// or with newer ES module support:
import postcssModules from "postcss-modules";const postcss = require("postcss");
const postcssModules = require("postcss-modules");
postcss([
postcssModules({
generateScopedName: "[name]__[local]___[hash:base64:5]",
getJSON: (cssFilename, json) => {
console.log("Generated mappings:", json);
}
})
])
.process(css, { from: cssFile })
.then(result => {
console.log(result.css); // Transformed CSS with scoped class names
});PostCSS Modules is built around several key components:
:import() and :export)Main plugin factory that creates a PostCSS plugin instance with the specified options.
/**
* Creates a PostCSS plugin instance for CSS Modules processing
* @param options - Configuration options for the plugin
* @returns PostCSS plugin instance
*/
declare function postcssModules(options?: Options): Plugin;
/** PostCSS plugin identifier */
postcssModules.postcss: true;
interface Options {
/** Callback function to handle generated class name mappings */
getJSON?: (cssFilename: string, json: { [name: string]: string }, outputFilename?: string) => void;
/** Class name transformation convention */
localsConvention?: "camelCase" | "camelCaseOnly" | "dashes" | "dashesOnly" | LocalsConventionFunction;
/** Default scoping behavior for class names */
scopeBehaviour?: "global" | "local";
/** Array of regexes to match global module paths */
globalModulePaths?: RegExp[];
/** Custom scoped name generator */
generateScopedName?: string | GenerateScopedNameFunction;
/** Prefix for generated hash values */
hashPrefix?: string;
/** Whether to export global class names */
exportGlobals?: boolean;
/** Root directory for resolving imports */
root?: string;
/** Custom loader class for handling CSS module imports */
Loader?: typeof Loader;
/** Custom file resolver function */
resolve?: (file: string, importer: string) => string | null | Promise<string | null>;
}Usage Examples:
// Basic usage with string template for scoped names
postcssModules({
generateScopedName: "[name]__[local]___[hash:base64:5]"
})
// Custom scoped name generation function
postcssModules({
generateScopedName: (name, filename, css) => {
return `_${name}_${require('string-hash')(css).toString(36).substr(0, 5)}`;
}
})
// Handle generated mappings
postcssModules({
getJSON: (cssFilename, json, outputFilename) => {
fs.writeFileSync(cssFilename + '.json', JSON.stringify(json));
}
})
// Global module paths configuration
postcssModules({
globalModulePaths: [/node_modules/, /global\.css$/],
scopeBehaviour: "local"
})Controls how CSS class names are transformed into unique, scoped identifiers.
/**
* Function type for custom scoped name generation
* @param name - Original CSS class name
* @param filename - CSS file path
* @param css - Complete CSS content
* @returns Generated scoped class name
*/
type GenerateScopedNameFunction = (name: string, filename: string, css: string) => string;String Template Format:
[name] - Original class name[local] - Alias for [name][hash] - Hash of CSS content[hash:base64:5] - Base64 hash truncated to 5 charactersTransforms exported class names according to naming conventions for JavaScript consumption.
/**
* Function type for custom locals convention transformation
* @param originalClassName - Original CSS class name
* @param generatedClassName - Generated scoped class name
* @param inputFile - CSS file path
* @returns Transformed class name for export
*/
type LocalsConventionFunction = (
originalClassName: string,
generatedClassName: string,
inputFile: string
) => string;Built-in Conventions:
"camelCase" - Exports both original and camelCase versions: { "btn-primary": "...", "btnPrimary": "..." }"camelCaseOnly" - Exports only camelCase version: { "btnPrimary": "..." }"dashes" - Exports both original and dash-converted versions"dashesOnly" - Exports only dash-converted versionsDefault loader implementation for handling CSS module imports and dependency resolution.
/**
* Default file system loader for CSS modules
*/
declare class Loader {
/**
* Creates a new loader instance
* @param root - Root directory for resolving imports
* @param plugins - Array of PostCSS plugins to apply
* @param resolve - Optional custom file resolver function
*/
constructor(root: string, plugins: Plugin[], resolve?: (file: string, importer: string) => string | null | Promise<string | null>);
/**
* Fetches and processes a CSS module file
* @param file - File path to fetch
* @param relativeTo - Path to resolve relative imports from
* @param depTrace - Dependency trace for circular import detection
* @returns Promise resolving to class name mappings
*/
fetch(file: string, relativeTo: string, depTrace: string): Promise<{ [key: string]: string }>;
/** Combined final CSS source after processing all imports */
finalSource?: string;
}Internal core processor class used by FileSystemLoader for handling CSS module transformations.
/**
* Core CSS module processor that applies PostCSS transformations
*/
declare class Core {
/**
* Creates a new Core processor instance
* @param plugins - Array of PostCSS plugins to apply during processing
*/
constructor(plugins?: Plugin[]);
/**
* Loads and processes a CSS module file
* @param sourceString - CSS source code to process
* @param sourcePath - File path for the source (used for error reporting)
* @param trace - Dependency trace identifier for circular import detection
* @param pathFetcher - Function to fetch imported files
* @returns Promise resolving to processed CSS and export tokens
*/
load(
sourceString: string,
sourcePath: string,
trace: string,
pathFetcher: (file: string, relativeTo: string, depTrace: string) => Promise<{ [key: string]: string }>
): Promise<{
injectableSource: string;
exportTokens: { [key: string]: string };
}>;
/** Default plugins used when no plugins are specified */
static defaultPlugins?: Plugin[];
}Controls the default scoping behavior for CSS class names.
/** Available scoping behaviors */
const behaviours = {
LOCAL: "local",
GLOBAL: "global"
} as const;
/**
* Gets default plugins for specified scoping behavior
* @param config - Scoping configuration
* @returns Array of PostCSS plugins
*/
function getDefaultPlugins(config: {
behaviour: "local" | "global";
generateScopedName: GenerateScopedNameFunction;
exportGlobals: boolean;
}): Plugin[];
/**
* Validates and returns scoping behavior, defaults to LOCAL
* @param scopeBehaviour - Desired scoping behavior
* @returns Valid scoping behavior
*/
function getDefaultScopeBehaviour(scopeBehaviour?: string): "local" | "global";
/**
* Creates or returns a scoped name generator function
* @param generateScopedName - String template or custom function for generating scoped names
* @param hashPrefix - Optional prefix for hash generation
* @returns Function that generates scoped class names
*/
function getScopedNameGenerator(
generateScopedName?: string | GenerateScopedNameFunction,
hashPrefix?: string
): GenerateScopedNameFunction;/** PostCSS Plugin interface */
interface Plugin {
postcssPlugin: string;
OnceExit?: (root: any, helpers: any) => void | Promise<void>;
}PostCSS Modules processes special CSS syntax for imports and exports:
Import Syntax:
:import("./other-module.css") {
imported-class: exported-class;
}Export Syntax:
:export {
localClass: scoped-class-name;
}Global/Local Selectors:
:global(.global-class) {
/* This class remains global */
}
:local(.local-class) {
/* This class gets scoped (default behavior) */
}The plugin may throw errors in the following cases: