Every new project starts the same way: someone opens a blank repo and immediately runs npm install tailwind — or worse, pulls in a component library before a single screen exists.
Boring is a feature
Plain CSS has gotten good. Custom properties, @layer, container queries, and :has() cover most layout problems without a build step. The cost of a framework is not the install — it’s the mental model you inherit.
“Simplicity is prerequisite for reliability.” — Edsger Dijkstra
That quote applies to styling too. A stylesheet you can read top-to-bottom beats a toolchain you need a cheat sheet to debug.
A starter that scales
Before you reach for utilities, try a token file and three layout primitives:
:root {
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-4: 1rem;
--space-8: 2rem;
--radius: 0.5rem;
--foreground: oklch(0.2 0 0);
--background: oklch(0.98 0 0);
}
.stack {
display: flex;
flex-direction: column;
gap: var(--space-4);
}
.cluster {
display: flex;
flex-wrap: wrap;
gap: var(--space-2);
align-items: center;
}
.sidebar {
display: grid;
grid-template-columns: 16rem 1fr;
gap: var(--space-8);
}Nothing exotic — just variables and composition. Most landing pages never need more.
When to add a framework
Add one when you have evidence, not enthusiasm:
- You are repeating the same utility patterns across dozens of files
- Same flex/grid snippets copied into five components
- Spacing values drifting (
12pxhere,0.75remthere) - New hires asking “where do I put styles?”
- Your team cannot agree on naming conventions
- BEM vs utilities vs CSS modules vs “whatever works”
- PRs blocked on style nits instead of behavior
- Design tokens need to propagate across multiple apps
- Web + email + native share a palette
- Brand updates require a single source of truth
Until then, a single global.css with a handful of variables will carry you further than you expect.
The payoff
Boring CSS is debuggable in DevTools, portable between projects, and never breaks on a major version bump. Sometimes the best abstraction is no abstraction at all.
# No build step required
open index.html
# Edit globals.css, refresh — doneIf your entire styling story fits in one file you can grep, you’ve won.