The old pain, honestly described
Our stylesheets used to grow like sediment: a class for the card, a modifier for the card on the homepage, an override for the card in the modal, and a comment apologizing for all three. Renaming anything felt dangerous. Deleting anything felt fatal. Every project carried dead CSS like luggage.
Component-scoped CSS helped, but the naming problem just moved house. We were still inventing class names for every slight variation of spacing and color.
What changed on the first project
Three things, in order. First, speed: building a new section meant composing utilities in markup instead of context-switching to a stylesheet. Second, constraints: the spacing and color scales in the config file became the design system — deviations required a conscious edit, visible in review.
Utility-first didn’t remove CSS decisions. It moved them somewhere visible, reviewable, and hard to get subtly wrong.
The honest trade-offs
- Markup gets verbose — long class lists are real, and components are the answer, not shorter utilities
- The learning curve is a week of grumbling, then it clicks
- Truly novel visual effects still belong in real CSS, and that’s fine
- Without config discipline, you can recreate every mess utilities were meant to fix
How we keep it clean
Our rulebook fits on an index card: tokens live in config, repeated patterns become components, one-off magic numbers get a comment, and the stylesheet for custom CSS stays small enough to read in one sitting. New engineers learn the system in days because there’s almost nothing bespoke to learn.
Would we go back?
No — but we’d skip the purist phase. Extract components early, allow custom CSS where it earns its place, and judge the approach by shipping speed and maintainability, not by ideological purity. Used pragmatically, utility-first CSS is simply the fastest way we know to turn designs into interfaces that stay consistent.