examples/css/template.md
_{{example.js}}_
_{{style.css}}_
_{{style.module.css}}_
The config also registers a small plugin that gives CSS modules TypeScript
types. It reads each CSS module's name map (original class/id name ->
generated scoped name) from module.buildInfo.cssData.exports — the same data
webpack uses to build the JS exports, and the same data Lightning CSS returns as
exports from transform() — and writes a .d.ts next to the source file
so editors and tsc type import … from "./x.module.css". No bundler ships
this natively today; the map webpack already computes makes it a few lines.
_{{webpack.config.js}}_
_{{dist/output.js}}_
_{{dist/output.css}}_
_{{production:dist/output.css}}_
_{{dist/1.output.css}}_
The plugin writes this declaration next to the CSS source (not into dist),
so it is picked up automatically by your editor and by tsc. It uses the same
object shape as typed-css-modules / css-modules-typescript-loader — quoted
keys, so kebab-case names and reserved words like default need no special
handling — paired with parser: { namedExports: false } so the module's default
export is that object:
_{{style.module.css.d.ts}}_
With it in place, the import in example.js is fully typed — styles.main is a
string, styles.nope is a compile error, and the editor autocompletes the
class names:
import styles from "./style.module.css"; // styles.main: string
Wiring it into a real project: run the build (or webpack --watch) so the
.d.ts files stay in sync with the CSS, then either commit them or add
*.module.css.d.ts to .gitignore. Because the declaration lives beside the
source, no paths/ambient-module setup is needed.
webpack's native CSS localizes more identifiers than any classic loader. For a
css/module (or auto-detected *.module.css):
.foo) and id (#foo) selectors.true): @keyframes +
animation-name (animation), grid line/area names (grid),
@counter-style + list-style (customIdents), @container +
container-name (container), @function names + calls (function),
view-transition-name/-group/-class + ::view-transition-*() pseudo
arguments (customIdents).--foo dashed ident, via dashedIdents, default true): custom
properties and var(--foo) incl. cross-file var(--foo from "./x.css") and
from global; @property / @font-palette-values / @color-profile names;
anchor positioning (anchor-name, position-anchor, anchor(),
@position-try, anchor-scope); scroll-driven-animation names; and
@container style(--foo) queries. New dashed-ident CSS features are covered
automatically, with no feature-specific code.composes (same-file, from "./x.css",
from global), @value (incl. cross-file), ICSS :import / :export.Intentionally left global (they coordinate across documents or the whole
app, so scoping would break them): @layer and @page names,
@font-feature-values family names, @view-transition types, and
:global(...) selectors.
_{{stdout}}_
_{{production:stdout}}_