A powerful TypeScript definition generator for PostCSS-powered CSS Modules, ensuring type safety and providing IDE intellisense for your CSS class names.
When working with CSS Modules in TypeScript projects, especially in large design systems or component libraries, you often face these challenges:
- Type Safety Gaps: CSS class names are essentially strings, making them prone to typos and runtime errors
- Poor IDE Support: No autocomplete for CSS class names means developers need to constantly reference CSS files
- Refactoring Difficulties: Renaming CSS classes becomes risky without TypeScript's refactoring support
- Design System Maintenance: Ensuring consistent class usage across a large codebase is challenging
pcssmdts bridges these gaps by:
- Type Safety: Automatically generates TypeScript definitions for your CSS Modules
- IDE Integration: Provides full IntelliSense support for class names
- Refactoring Support: Makes class names refactorable through TypeScript's tooling
- PostCSS Integration: Works seamlessly with your existing PostCSS setup
While IDE plugins like typescript-plugin-css-modules can provide type support during development, pcssmdts offers several advantages:
-
CI/CD Integration:
- Generate type definitions as part of your build process
- Catch type errors in CI before they reach production
- No reliance on developer IDE configuration
-
Team Consistency:
- Ensures all team members have the same type definitions
- Works regardless of IDE or editor preferences
- No need to maintain plugin configurations across the team
-
Build-time Validation:
- Validates CSS Modules during build time
- Integrates with your existing TypeScript compilation
- Prevents deployment of mismatched class names
-
Source Control:
- Generated types can be committed (optional)
- Enables type checking in environments without PostCSS setup
- Perfect for consuming libraries in other projects
// With IDE plugin only:
// - Types only available during development
// - Requires IDE configuration
// - May vary between team members
// With pcssmdts:
// - Types available everywhere
// - Part of your build process
// - Consistent across team and CI
import styles from './Button.module.css';
// TypeScript knows exactly what's available
const className = styles.button; // ✅ Type-safeOne of the key advantages of pcssmdts is its seamless integration with PostCSS, allowing you to:
-
Use Your Existing Setup:
// postcss.config.js module.exports = { plugins: [ require('postcss-nested'), require('tailwindcss'), require('autoprefixer'), require('postcss-modules')({ // your custom options generateScopedName: '[name]__[local]___[hash:base64:5]', }), ], };
-
Support Modern CSS Features:
/* Your CSS Module with modern syntax */ .button { /* Nesting (via postcss-nested) */ &:hover { background: theme('colors.blue.600'); } /* Custom Media Queries */ @media (--dark-mode) { background: theme('colors.gray.800'); } /* CSS Custom Properties */ --button-padding: 1rem; padding: var(--button-padding); }
-
Framework Integration:
-
Works with Tailwind CSS for utility-first styling
-
Compatible with CSS preprocessors (SASS/LESS) through PostCSS plugins
-
Integrates with popular frameworks:
// Next.js import styles from './Button.module.css'; // React import styles from './Button.module.scss'; // With SASS // Vue with TypeScript import styles from './Button.module.css';
-
Note: CSS Modules are different from CSS-in-JS solutions. While CSS-in-JS libraries like styled-components or emotion write styles in JavaScript, CSS Modules let you write traditional CSS files with local scope and static analysis. pcssmdts is specifically designed for CSS Modules, providing type safety without runtime overhead.
- Custom Naming Conventions:
// postcss.config.js with custom class naming module.exports = { plugins: [ require('postcss-modules')({ generateScopedName: (name, filename, css) => { // Your custom naming logic return `myApp_${name}_${hashString(css)}`; }, }), ], };
This flexibility means you can:
- Use modern CSS features while maintaining type safety
- Integrate with your existing build pipeline
- Customize class name generation to match your needs
- Support complex CSS transformations while keeping type definitions accurate
// Before: No type safety or autocomplete
const Button = ({ variant }) => (
<button className={`btn btn--${variant}`}>Click me</button>
);
// After: Full type safety and autocomplete
import styles from './Button.module.css';
type ButtonVariant = 'primary' | 'secondary';
const Button = ({ variant }: { variant: ButtonVariant }) => (
<button
className={`${styles.btn} ${
variant === 'primary' ? styles.btnPrimary : styles.btnSecondary
}`}
>
Click me
</button>
);When migrating a large application to use CSS Modules:
// Before: Global CSS with potential naming conflicts
const Header = () => (
<header className="app-header">
<nav className="navigation">
<a className="nav-link active">Home</a>
</nav>
</header>
);
// After: Scoped CSS Modules with type safety
import styles from './Header.module.css';
const Header = () => (
<header className={styles.header}>
<nav className={styles.navigation}>
<a className={`${styles.link} ${styles.linkActive}`}>Home</a>
</nav>
</header>
);When building a shared component library:
// components/Card/Card.module.css
.card {
/* styles */
}
.cardHeader {
/* styles */
}
.cardContent {
/* styles */
}
.cardFooter {
/* styles */
}
// components/Card/Card.tsx
import styles from './Card.module.css';
export const Card = ({
header,
children,
footer
}: CardProps) => (
<div className={styles.card}>
{header && <div className={styles.cardHeader}>{header}</div>}
<div className={styles.cardContent}>{children}</div>
{footer && <div className={styles.cardFooter}>{footer}</div>}
</div>
);-
Development Speed:
- Immediate feedback on invalid class names
- Autocomplete reduces need to reference CSS files
- Faster onboarding for new team members
-
Code Quality:
- Catch CSS class typos at compile time
- Ensure consistent class usage across components
- Make refactoring CSS class names safe and easy
-
Maintenance:
- Track CSS class usage across the codebase
- Safely remove unused classes
- Easier code reviews with type checking
- 🚀 Fast and efficient d.ts generation
- 🔍 Full PostCSS support with custom plugins and configurations
- 🎯 Automatic camelCase class name conversion
- 💡 IDE intellisense support
- ✅ TypeScript compilation-time type checking
- 🛠 Configurable output formats
- 📦 Zero runtime overhead
- Node.js:
>=16.19.1 <21 - PostCSS configuration file (optional, will use defaults if not provided)
The fastest way to use this utility is through npx without installation:
npx pcssmdts "src/**/*.module.css"Add it as a development dependency to your project:
# npm
npm install pcssmdts --save-dev
# yarn
yarn add pcssmdts -D
# pnpm
pnpm add -D pcssmdtsAdd to your package.json scripts:
{
"scripts": {
"generate:style-defs": "pcssmdts \"src/**/*.module.css\"",
"generate:style-defs:watch": "pcssmdts \"src/**/*.module.css\" --watch"
}
}pcssmdts operates in three main steps:
- CSS Module Detection: Scans your project for CSS module files based on the provided glob pattern
- PostCSS Processing: Processes your CSS files using your PostCSS configuration
- TypeScript Definition Generation: Creates corresponding .d.ts files with type definitions
src/
├── components/
│ └── Button/
│ ├── styles.module.css
│ ├── styles.module.css.d.ts (generated)
│ └── Button.tsx
/* styles.module.css */
.button {
display: flex;
&--primary {
background: blue;
}
&--secondary {
background: gray;
}
}
.icon-wrapper {
margin-right: 8px;
}// styles.module.css.d.ts
declare const styles: {
readonly button: string;
readonly buttonPrimary: string;
readonly buttonSecondary: string;
readonly iconWrapper: string;
};
export = styles;import styles from './styles.module.css';
export const Button: React.FC<{
variant?: 'primary' | 'secondary';
}> = ({ variant = 'primary' }) => {
return (
<button
className={`${styles.button} ${
variant === 'primary' ? styles.buttonPrimary : styles.buttonSecondary
}`}
>
<span className={styles.iconWrapper}>{/* Your icon here */}</span>
Click me
</button>
);
};The CLI is powered by typed-css-modules under the hood, providing robust TypeScript definition generation.
pcssmdts <source> [options]
Arguments:
source Source pattern for CSS module files (e.g., "src/**/*.module.css")
Basic options:
-v, --verbose Enable verbose logging [boolean]
-c, --config Custom PostCSS config path [string]
-k, --keep Keep compiled CSS files [boolean]
-w, --watch Watch mode for file changes [boolean]
typed-css-modules options:
-n, --namedExports Use named exports in .d.ts files [boolean] [default: false]
--camelCase Convert CSS class names to camelCase [choices: "true", "false", "dashes"]
--outDir Output directory for generated d.ts files. When specified, all generated .d.ts files will be placed in this directory, regardless of their original location. [string]
--eol End of line character [string]
General:
--help Show help information [boolean]
--version Show version number [boolean]
-
Basic Usage By default, .d.ts files are generated next to their corresponding CSS modules:
pcssmdts "src/**/*.module.css"Result:
src/ ├── components/ │ └── Button/ │ ├── styles.module.css │ └── styles.module.css.d.ts (generated here) -
Using outDir Place all generated .d.ts files in a specific directory:
pcssmdts "src/**/*.module.css" --outDir typesResult:
src/ ├── components/ │ └── Button/ │ └── styles.module.css └── types/ └── styles.module.css.d.ts (generated here)Note: When using
outDir, all generated .d.ts files will be placed flat in the specified directory, regardless of their original location in the source tree. -
Keep Compiled Files for Debugging Preserve PostCSS-processed files for debugging or inspection:
pcssmdts "src/**/*.module.css" -kResult:
src/ ├── components/ │ └── Button/ │ ├── styles.module.css (original) │ ├── _compiled.styles.module.css (processed CSS) │ └── styles.module.css.d.ts (generated types)The
_compiled.*.cssfiles contain the processed CSS after all PostCSS transformations. This is useful for:- Debugging CSS transformations
- Verifying PostCSS plugin output
- Inspecting final class names after scoping
-
Watch Mode
pcssmdts "src/**/*.module.css" -w -
Custom PostCSS Config
pcssmdts "src/**/*.module.css" -c ./config/postcss.config.js -
Named Exports with Custom Output Directory
pcssmdts "src/**/*.module.css" -n --outDir types -
CamelCase with Dashes
pcssmdts "src/**/*.module.css" --camelCase dashes -
Keep Compiled Files with Custom EOL
pcssmdts "src/**/*.module.css" -k --eol "\n"
Watch mode (-w or --watch) automatically regenerates TypeScript definitions when your CSS modules change. Here's how to verify it's working:
-
Start Watch Mode
pcssmdts "src/**/*.module.css" -w -
Verify Initial Generation
- Check that
.d.tsfiles are generated for all your CSS modules - You should see console output indicating the initial generation
- Check that
-
Test File Changes
# 1. Open your CSS module in an editor # 2. Add a new class .new-class { color: red; } # 3. Save the file
The watcher should automatically:
- Detect the file change
- Regenerate the
.d.tsfile - Show console output about the regeneration
-
Verify Type Updates
// Your component file import styles from './styles.module.css'; // The new class should be available with TypeScript intellisense <div className={styles.newClass} />; // If using camelCase
-
Common Watch Mode Issues
- If changes aren't detected, ensure you're watching the correct directory
- Some IDEs may need to refresh the TypeScript server to pick up new definitions
- Use
-vflag for verbose logging to debug watch mode issues:pcssmdts "src/**/*.module.css" -w -v
pcssmdts uses postcss-load-config to load your PostCSS configuration. It supports all standard PostCSS config files:
postcss.config.js.postcssrcpackage.jsonwithpostcssfield
Example PostCSS config:
// postcss.config.js
module.exports = {
plugins: [
require('postcss-nested'),
require('postcss-modules')({
// your options here
}),
// other plugins...
],
};-
Git Ignore Add generated files to your .gitignore:
**/*.module.css.d.ts **/_compiled.*.css -
CI Integration Run type generation before TypeScript compilation:
{ "scripts": { "build": "pcssmdts \"src/**/*.module.css\" && tsc" } } -
Development Workflow Use watch mode during development:
{ "scripts": { "dev": "concurrently \"pcssmdts 'src/**/*.module.css' -w\" \"your-dev-server\"" } }
Contributions are welcome! Please read our Contributing Guide for details on our code of conduct and the process for submitting pull requests.
This project is licensed under the MIT License - see the LICENSE file for details.