flashman
← All posts

WCAG contrast from painted colors

Debug WCAG contrast misses by measuring the colors that were actually painted, compositing any alpha first, and only then applying the luminance ratio.

2026-10-14 · 6 min read · Rahul Chitturi

  • css
  • accessibility
  • color

A WCAG 2 contrast ratio is a relationship between two relative luminances: (lighter + 0.05) / (darker + 0.05). Relative luminance comes from the sRGB channels after they are linearized, weighted about 21% red, 72% green, and 7% blue. A token named gray-700 does not have a ratio until you know the color underneath it.

The usual miss is measuring the design token against white while the component paints that token at 80% opacity over a tinted surface. The ratio of the two tokens passes. The ratio of the pixels fails.

Composite, then convert to opaque sRGB

Flatten alpha onto the real background before you run the formula. A translucent gray over a saturated blue is not the same gray. Gradients, images, and filters mean the ratio changes across the control. Pick the worst region a reader must actually read.

WCAG 2.1 AA asks for 4.5:1 for normal text and 3:1 for large text and for meaningful non-text UI boundaries. AAA raises the text thresholds to 7:1 and 4.5:1. A 4.4 ratio is a fail for body text, not a near pass you can round up.

  • Sample the painted foreground and background from the browser.
  • Flatten opacity before converting to hex.
  • Record the threshold that applies to that text size.
  • Recheck hover, disabled, and dark-theme states separately.

Watch the linearization cutoff

The sRGB linearization step uses a cutoff. Older WCAG text used 0.03928. WCAG 2.2 uses 0.04045, which matches the sRGB transfer. The two formulas disagree only for channel values in a thin band, which is exactly where a 4.5:1 edge case lives.

Flashman's color tool linearizes with the 0.03928 cutoff and expects opaque hex. If another checker uses 0.04045, diff the two ratios before you change a brand color to satisfy one of them.

Store the painted pair with the result

Write the foreground hex, the background hex, the opacity, and the ratio into the issue. A later reader cannot reconstruct the ratio from a token name after the theme has moved.

Use the color tool on the opaque pair, the CSS formatter to find the opacity and the token, diff to compare the two checkers, and the JSON formatter when the design tokens themselves are the source you are auditing.

Try these tools