Latr marks a reminder that is about to fire with a small amber badge. It is the most urgent state the product has, so it is the one thing on the screen that most needs to be read.

In light mode it measured 1.9:1 against its own background. WCAG asks for 4.5:1 on body text and 3:1 on large text. 1.9:1 is not a near miss; it is the least readable thing in the product, marking the most important state in it.

How it happened

The design system uses semantic colour pairs - --warning, --destructive, --success, --info - and each is supposed to be two values: a fill, and a separately tuned foreground that is legible on that fill.

The badge used one. Text was set to hsl(var(--warning)) on a 10% tint of hsl(var(--warning)). Amber on amber. Nothing in the code was obviously wrong; it reads as “use the warning colour for the warning badge”, which is exactly what you would want a design system to let you write.

Why nothing caught it

The theme engine already derived custom themes through a contrast check. A user building their own palette in the theme editor got their values verified.

The built-in palette went through none of it. What stood behind it was a comment - a block at the top of the stylesheet stating the ratios each pair achieved, written by the person who had measured them, once, correctly.

Comments do not re-measure themselves when somebody nudges a lightness.

That gap is where both of this product’s worst colour bugs lived. The other was --border at 1.24:1 against the page in light mode, which is the whole explanation for a report that said, simply, “the borders aren’t properly visible”. Dark mode kept its edges throughout, which is why it survived every review - the person reviewing was in dark mode.

What replaced the comment

The test suite now parses the two :root blocks out of the stylesheet and measures every pair, in both themes, at the ratio that pair actually owes. Not a lint rule about which token names may appear together - an actual contrast computation against actual values.

Three things about it turned out to matter more than the check itself.

Every ground, not the convenient ones. The first version measured borders against --background and --card. It did not measure --popover, which is the lightest step of the dark ladder and therefore the hardest surface for an edge to hold against. Dark --border sat at 1.36:1 there. The row menu, the command palette and the picker panel all lost their outlines on a dark theme while the suite reported a pass. A pair is only as good as its worst ground.

A floor the palette does not already clear. The first thresholds were 1.4 and 1.8, set as “a bar, which is what was missing”. Every value in the product sat between 1.36 and 2.13

  • so the bar was cleared by hundredths and measured nothing. That band is what a user describes as the borders not being properly visible.

Two floors, because light and dark do not build depth the same way. Light mode has white cards on warm paper and no lit edge available - a bright edge on white is invisible - so the border and the shadow are the only things separating a surface from the page. Dark has a four-step surface ladder and a lit top edge doing that work. One number applied to both pushed dark to 32.5:1 and 41.5:1 and drew a grey wire around every card, field, menu and row in the product: technically passing, and a wireframe to look at.

It is 1.8 for a divider and 2.6 for a field rim in light, 1.6 and 2.25 in dark. The split is the point, not a concession.

The badge

6.4:1 now, on a separately tuned foreground that inverts between themes - dark ink on the amber tint in light mode, bright amber in dark. Every semantic colour is a token pair, and setting text to hsl(var(--warning)) is the mistake the pair exists to prevent.

The rule

If a number matters, measure it in the build. A comment stating a contrast ratio, a bundle size, a query count or a p99 is a claim about the past written by someone who is no longer in the room. It ages silently and it ages in one direction.

The version of this that generalises: anything you would write in a comment to reassure the next reader is a candidate for an assertion. The assertion is barely more work and it is the only one of the two that argues back.