Accessibility ·
WCAG contrast ratios in the browser
WCAG 2.x contrast is (L1 + 0.05) / (L2 + 0.05) where L is relative luminance of sRGB. A browser contrast checker converts HEX to linear RGB, applies the coefficients, and reports AA and AAA. No design file is uploaded; the math runs on two color values in the DOM.
How is relative luminance calculated?
Take each 8-bit sRGB channel 0–255, divide by 255, then apply the piecewise gamma: if c <= 0.04045 use c/12.92, else ((c+0.055)/1.055)^2.4. L = 0.2126 R + 0.7152 G + 0.0722 B. The contrast ratio is the lighter L plus 0.05 over the darker L plus 0.05.
function lin(c) {
const s = c / 255;
return s <= 0.04045 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
}
function luminance(r, g, b) {
return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
}How does a client-side checker map to AA and AAA?
After the ratio, compare to 4.5 and 7 for normal text, 3 and 4.5 for large text, and 3:1 for non-text UI components (WCAG 2.1 1.4.11). The page can sample computed styles from a preview swatch; still no screenshot upload.
Where do designers get the formula wrong?
Using Rec. 709 without the gamma step, mixing Display P3 with sRGB coefficients, or checking hex against a CSS color that was filtered through opacity. Semi-transparent text must be composited on the actual background before luminance.
How should you test real UI, not just two hex codes?
Pick the foreground as rendered — including disabled gray and placeholder opacity. Check charts, not only body copy. Pair the contrast checker with a color converter when you need HSL tweaks that preserve hue while lifting L.
Does this belong in CI?
Yes, as a unit test of tokens. The same functions run in Node or in the browser. The public tool is for humans iterating; the formula should be one module, not a screenshot of a competitor’s widget.
Frequently asked questions
What contrast ratio does WCAG AA require for body text?
4.5:1 for normal text and 3:1 for large text (18pt or 14pt bold). AAA is 7:1 and 4.5:1 respectively.
Is the CSS color-contrast() function the same math?
It targets similar outcomes but engines and WCAG 2 relative luminance are specified separately. For audits, implement the WCAG 2 formula explicitly.
Does APCA replace WCAG 2 contrast?
APCA is the candidate for WCAG 3. Until it is the legal bar you are testing against, ship WCAG 2 ratios and treat APCA as extra.
Why compute contrast in the browser?
Brand palettes are not secret, but design systems are faster when the checker is a local page. You also avoid sending unreleased hex values to a third-party screenshot API.