2026-09-10 · 2 min read

Three bugs this site shipped

A media query Chrome forgave, a focus ring that outranked everything, and a contrast failure the automated gate can't see. Guess before you scroll.

Every one of these actually happened here, on this site. None of them were exotic. All three shared the same shape: the code looked right, the tooling said it was fine, and the failure was somewhere nobody was looking.

They also share a lesson I keep relearning — the thing that catches a bug is almost never the thing you added to catch bugs. Two of these three slipped past a full lint, type-check, unit and end-to-end suite, and were found by looking at the page in a browser I don’t usually open.

Have a go before reading the answers. Each one explains itself either way.

Bug 01

A control is meant to appear only on devices with a precise pointer. It shows up in Chrome. In Safari and Firefox it never appears at all. The class is written by hand as a Tailwind arbitrary variant, and this is the CSS that reached the browser:

@media(hover:hover)and (pointer:fine) {
  .the-button { display: flex }
}
Bug 02

Every input in the app carries Tailwind's `outline-none`. Focus one and a grey ring appears anyway. The utility is definitely in the stylesheet and definitely applies to the element. Why does the ring still win?

Bug 03

This site's muted text token is `#737373`. In the light theme it sits on two different surfaces: the page canvas `#fdfbf7`, and the recessed fill `#f4f1eb` used for hover states and tracks. One of those pairings fails WCAG AA for body text. Which, and why is it so easy to miss?

What they have in common

The first one is a tooling monoculture problem. CI runs Chromium only, because five browser projects on every push is slow and mostly redundant. That is a reasonable trade until the bug you have is precisely a parser difference, at which point the suite reports success with total confidence. The local command runs all five; the pipeline runs one. The gap between those two numbers is exactly the set of bugs that reach production.

The second is a cascade problem, and it is worth internalising because cascade layers invert an instinct most of us built over a decade. Specificity used to be the whole story. Now a single-class selector outside a layer beats an ID selector inside one, and Tailwind puts everything in a layer. When a utility mysteriously loses, stop counting specificity and start asking which layer each declaration is in.

The third is the most uncomfortable, because it is a gap in the safety net rather than a gap in the code. The scan is real, it runs on every push, it fails the build. It also cannot see hover states, so a contrast failure that only exists while the pointer is down stays invisible to it forever. An automated gate tells you about the class of problems it was built to find, and says nothing at all about the rest — which is easy to hear as silence meaning safety.

The fix for that last one is not a better scanner. It is knowing which pairings are close to the line, and this site now carries that knowledge in two places: the contrast maths is a shared module with the real figures pinned in its tests, and hovering any element with inspect mode on reports the live ratio against whatever is actually painted behind it. The margin on muted text is about a tenth of a point. That is not a number worth trusting to memory.