CSS becomes expensive when every small visual disagreement gets its own rule.
A margin for one page. A shadow for one card. A button variant created because one screen was almost, but not quite, aligned with the rest of the site. Each decision is cheap while it is being made. The cost appears later, when a new page has to understand all of them before it can make a safe change.
The stylesheet is not suffering from a lack of creativity. It is suffering from too many local decisions.
Make the common case boring
Defaults do more work than variants.
Text should already know how to wrap. Links should already have a readable state. Code should already look like code. Focus should be visible without each component remembering to invent it. A page that needs fewer corrective rules is easier to extend and easier to review.
h1,
h2,
h3 {
text-wrap: balance;
}
p,
li {
text-wrap: pretty;
}
code,
kbd,
samp,
pre,
.type-mono {
font-family: var(--font-mono);
font-variant-ligatures: none;
}
None of these rules is a design moment. That is the point. They remove repeated decisions from the page layer.
Exceptions should announce themselves
There are legitimate reasons to break the system. A homepage may need a stronger treatment. A product launch may need a temporary visual language. A dense tool may need controls that do not belong in a reading layout.
The danger is the ordinary component with a private vocabulary. If a blog card gets its own radius, shadow, spacing scale, and heading size, the exception should be visible enough to prompt a question: is this a real component distinction, or did the local value merely feel close?
One useful test is whether the rule will be wanted again. If it will, promote the decision into the system. If it will not, keep the exception close to the component and make it easy to find.
Name roles, not moods
Custom properties should describe what a value does.
--surface-wash gives the next person a role. --profile-orbit-blue gives them a history lesson about one component. --cool-blue records a preference without explaining where the value belongs.
A useful token should help answer:
- what role does this value play?
- where is that role allowed?
- what changes if the value moves?
If those answers are unclear, adding the token may only spread the ambiguity.
Keep the layout vocabulary small
Most layout drift starts with a nearly-right wrapper.
One page uses flex and gap. Another uses grid and local margins. A third adds a new container because a hero needs to overlap its neighbor. Soon, adding a page means learning a set of exceptions instead of arranging content inside a known structure.
A small layout vocabulary is usually enough:
- page container
- prose container
- dense toolbar
- repeated-item grid
- full-width band
- split layout when the content genuinely calls for it
The value is not visual uniformity for its own sake. It is the ability to predict how a new piece of content will fit.
Theme changes reveal hidden coupling
Dark mode exposes scattered decisions quickly.
When colors describe roles, changing the theme means updating a manageable set of values. When colors live in page-specific selectors, the same change becomes a search through unrelated components. Borders, shadows, and muted text expose the problem too.
The same principle applies beyond themes. A stylesheet with fewer independent opinions has fewer places for a refactor to surprise you.
I trust CSS that has a small set of global decisions, a narrow set of component exceptions, and enough restraint that the next page can use the system without negotiating with it.
Related posts

About Jeremy London
Engineering leader and builder in Denver. I write about AI platforms, agents, security, reliability, homelab infrastructure, and the parts of engineering work that have to survive production.