composes, :global & CSS Modules vs. Other Approaches
Style Reuse with composes
.base {
border-radius: 4px;
}
.primary {
composes: base;
background: blue;
}composes lets a class inherit another class's styles without duplicating declarations — a form of composition, similar in spirit to extending a base class. :global(...) is the escape hatch for the opposite case — marking a selector as intentionally global, useful for overriding a third-party component's internal classes that need to bypass local scoping.
CSS Modules vs. CSS-in-JS
Both solve the same naming-collision problem via different mechanisms: CSS Modules process separate .css files at build time; CSS-in-JS libraries (styled-components, Emotion) embed styles in JavaScript, often with runtime prop-driven dynamism. CSS Modules stay closer to plain CSS syntax — existing CSS tooling (linters, autocomplete) works out of the box — but genuinely dynamic, runtime-computed styling needs a fallback (inline styles, CSS variables, conditional classes) rather than being a first-class feature.
Build-Time-Only, No Runtime Overhead
Class transformation happens once during the build, producing static already-scoped CSS output — no ongoing JS overhead for style resolution the way some runtime CSS-in-JS injection can incur. It also composes with preprocessors: a project can author .module.scss files, letting Sass compile first and CSS Modules' scoping apply after.
Co-Location, Component Libraries & Incremental Adoption
A component's .module.css file typically lives right next to its component file, making per-component style ownership easy to find and maintain. For a component library specifically, automatic scoping means internal class names won't clash with whatever global CSS the consuming app already has. And CSS Modules adopt incrementally — new components use .module.css while existing global CSS files keep working untouched, no disruptive rewrite required.
Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free