The Quiet Case for CSS Modules
In an era of utility-first frameworks and CSS-in-JS, there's still something right about a plain stylesheet that belongs to exactly one component.
Every few years, the front-end world decides it has finally solved CSS. We get a new framework, a new methodology, a new runtime. And each one solves some real problems while creating new ones that we don't fully understand until we're three years into a large codebase.
I've been through a few of these cycles. And lately I keep coming back to CSS Modules — not out of nostalgia, but because they have a property that I find increasingly valuable: they're boring.
What boring means
Boring in software means predictable. It means the tool does one thing, it does it well, and it doesn't ask you to restructure your thinking around it.
CSS Modules take your plain CSS and scope it to a component. That's the whole feature. The .title class in Card.module.css doesn't leak into Header.module.css. You don't need a runtime. You don't need to install eleven packages. You don't need to learn a new mental model for how styles are applied.
/* Card.module.css */
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 4px;
padding: 24px;
}
This is just CSS. Any developer who knows CSS can read this. Any designer who knows CSS can change it.
The cost of expressiveness
The problem with more expressive styling solutions isn't that they're bad. It's that expressiveness has a cost. When anything is possible, you have to make more decisions. When utilities are atomic, you push your design into your markup. When styles live in your JavaScript, your bundle grows and your server rendering gets complicated.
None of these are fatal. Experienced teams manage them. But they're costs that CSS Modules don't ask you to pay.
What CSS variables give you
The thing that makes CSS Modules actually pleasant in 2026 is CSS custom properties. You get your design tokens — colors, spacing, type scales — defined once in :root, and they're available everywhere without any JavaScript involvement.
:root {
--bg: #F2EDE4;
--accent: #C4603A;
}
Change --accent in one place, and every component that uses it updates. You get theming, you get consistency, and you get it without any build-time magic.
A small argument
I'm not saying CSS Modules are always the right choice. For very large teams with complex design systems, the tradeoffs shift. For projects that need heavy runtime theming, they may not be enough.
But for a personal site, for a small product, for anything where you want to actually understand your styles six months from now — they're worth serious consideration.
The boring choice is often the right one.