A PostCSS plugin for type safe CSS modules by tapping into the postcss-modules plugin.
This plugin takes CSS modules one step further by generating .js
and .d.ts
files for each CSS module file. This means that CSS class names imported from a CSS module file will be typechecked so that developers can't reference non-existent classes. Source maps are also generated so that you can navigate from a CSS module class name directly to the Sass file.
For example, if you have the following SCSS module file:
// header.module.scss
$page-width: 850px;
:export {
pageWidth: $page-width;
}
.header {
font-size: 1.5rem;
}
In your corresponding TypeScript file, the following would work:
import React from "react";
import * as css from "./header.module.scss";
export const Header: React.FC = () => {
const width = css.pageWidth; // "850px"
return <header className={css.header}>Hello</header>;
};
However, referencing a non-existent class will fail to compile:
import React from "react";
import * as css from "./header.module.scss";
export const Header: React.FC = () => {
// Will fail to compile
return <header className={css.nonExistent}>Hello</header>;
};
Note that if there is a naming conflict between an exported variable (via :export
) and a class name, this plugin will throw to prevent surprising behavior:
// header.module.scss
$header-width: 850px;
:export {
// Will cause an error
header: $header-width;
}
.header {
font-size: 1.5rem;
}
Add this plugin to your build tool, along with the SCSS parser.
npm install postcss postcss-scss postcss-typesafe-css-modules --save-dev
In the following example, we are compiling Sass via PostCSS as well:
// postcss.config.mjs
import scssPlugin from "@csstools/postcss-sass";
import scss from "postcss-scss";
import cssModulesPlugin from "postcss-typesafe-css-modules";
export default {
syntax: scss,
plugins: [scssPlugin(), cssModulesPlugin],
};
Alternatively, in CommonJS:
// postcss.config.js
module.exports = {
syntax: require("postcss-scss"),
plugins: [
require("@csstools/postcss-sass")(),
require("postcss-typesafe-css-modules"),
],
};
For each SCSS module file, the following files will get written to disk in the output directory (--dir
if using the postcss-cli):
# In this example, build/postcss is the output directory for PostCSS
src/
hello.module.scss
hello.tsx # Contains the import for header.module.scss
build/
postcss/
_hello.css # Compiled CSS that is imported by hello.module.scss.js
hello.module.scss.js # File actually imported by hello.tsx
hello.module.scss.js.map
hello.module.scss.d.ts
hello.module.scss.d.ts
Currently this plugin has one option, forwarded to postcss-modules. See Generating scoped names for how to configure this option:
import cssModulesPlugin from "postcss-typesafe-css-modules";
cssModulesPlugin({
generateScopedName: /* Your desired scoped name behavior */
})