We spent part of this week rebuilding the design system on this site, and the migration surfaced a bug that had been sitting in the codebase doing nothing at all — quietly, and for a while.
A table row carried this class:
<tr class="hover:bg-slate-50/50 dark:hover:bg-white/2">
It looks fine. It reads fine in review. It had never once rendered.
Why it never worked
Tailwind's colour opacity modifiers are not arbitrary numbers. Out of the box they come from the opacity scale in the theme, which runs 0, 5, 10, 15 … 100. Ask for a value on that scale and you get a class. Ask for one that is not — white/2, white/97 — and Tailwind does not warn, does not error, and does not generate the class. The utility simply does not exist in the output stylesheet.
There is no build failure. There is no console message. The element just renders without the style, and unless someone is specifically looking for that hover state on that row, nobody notices.
The version that bites harder
The dead hover state is cosmetic. The same mistake in a gradient is not.
While restyling our hero sections we wrote:
<div class="bg-gradient-to-br from-white/97 via-white/90 to-white/60">
That was meant to be a wash sitting over a background photograph, keeping it faint enough for text to stay readable. Because from-white/97 never generated, the gradient lost its starting stop — and the whole background-image resolved to none. The wash disappeared entirely and the photograph came through at full strength, directly underneath body copy.
The failure mode is worth noticing: one invalid stop did not degrade the gradient, it removed it. A contrast problem, introduced by a class that looks more precise than the one that works.
What we do about it now
Two things.
First, if you want a value off the scale, bracket it. bg-white/[0.02] is arbitrary-value syntax and Tailwind will generate it. The bracket is the signal that you know you are off the standard scale.
Second, we added a check to the design-system pass — a small script that walks the source for colour utilities with an opacity modifier and flags any that fall off the default scale:
const valid = new Set(
Array.from({ length: 21 }, (_, i) => String(i * 5))
);
const pattern =
/(?:bg|text|border|from|via|to|ring|divide|placeholder|fill|stroke)-[a-z0-9-]+\/(\d+)\b/g;
Anything the regex catches that is not in valid is either a typo or wants brackets. Run it once across a mature codebase and it is a fast way to find styles that have never done anything.
The general shape of it
The interesting part is not Tailwind. It is that this is a whole category of bug: tooling that fails by producing nothing rather than by complaining.
A missing class, an unmatched CSS selector, a media query that never applies, an environment variable read as undefined and defaulted away. None of them break the build. All of them look correct in the diff. They are only visible if you check the output rather than the input — the generated stylesheet, the computed style, the rendered pixel.
Reviewing the source tells you what someone intended. It does not tell you what the browser got.
What we shipped this week
- Rebuilt the site's token layer, component vocabulary and page structure on a new design language.
- Replaced a 4.5 MB logo asset that was 79% transparent padding with a cropped 147 KB version, plus a dark-mode variant that lifts only the neutral ink so the brand colour survives on dark grounds.
- Removed a set of CSS utilities that were shadowing Tailwind's own token-backed classes and pinning our brand colour to a fixed shade — which had also been defeating the dark-mode adjustment.
If you have a Tailwind codebase of any age, run the check. We would be surprised if it comes back empty.


