Skip to content

Measure before you believe the audit

A contrast script reported failures on components that pass at ten to one, because it painted the wrong thing.

View as Markdown

Colours in this project are oklch() and oklab() with alpha, so contrast cannot be checked by parsing strings. The obvious approach is to paint them: fill a one-pixel canvas with the element’s background, then the text colour, and read the pixel back.

Done that way it reports badges failing at 3:1 that actually pass at 10:1.

Why

A tinted badge’s own background is something like an 8% wash. Fill only that onto a transparent canvas and you get an 8% wash over nothing, which reads back as near-black. Every light-on-tint pairing then looks broken.

The element’s real background is the whole ancestor stack — the page colour, then each non-transparent ancestor from outermost inward, then the element’s own tint. Composite all of it, and the numbers become real.

What it caught once it was honest

The corrected audit found one thing the eye never would: every code comment on the site at 3.92:1 in dark mode, and eleven light-mode tokens under 4.5, straight from the default syntax theme. Swapping to the high-contrast pair put the worst token at 4.91 light and 8.90 dark across 130 measured tokens.

Two rules that came with it

Skip anything under an aria-hidden ancestor, not just the leaf — a decorative wordmark at 9% opacity is not a contrast failure, and it will otherwise dominate the report.

Measure in a separate step from the click that changed the theme. Computed styles are stale within the same task, so a same-step measurement reports the previous theme’s colours with total confidence.

The general point

An audit that reports failures is not evidence of failures. It is evidence about the audit. The first thing to check when a tool says everything is broken is whether the tool is measuring what you think it is — and the cheapest way to find out is to run it against a case whose answer you already know.