Contrast ratio is a ratio of luminances
The WCAG contrast ratio is (L1 + 0.05) / (L2 + 0.05) where L1 is the lighter colour's relative luminance and L2 the darker's — nothing more. Relative luminance is where the maths lives: each RGB channel is linearized (divide by 255, then the sRGB curve) and weighted 0.2126 red, 0.7152 green, 0.0722 blue. Green dominates because the eye is most sensitive there — which is why two colours that 'look' equally bright on your monitor can fail contrast while a surprising pair passes.
function channel(c: number) { // 0..255 -> linear
const s = c / 255;
return s <= 0.04045 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
}
function luminance(r: number, g: number, b: number) {
return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);
}
function ratio(a: number, b: number) {
const [hi, lo] = a > b ? [a, b] : [b, a];
return (hi + 0.05) / (lo + 0.05); // >= 4.5 passes AA text
}Three facts that settle arguments
- White on near-black is ~15.8:1 — the ceiling you are always aiming under, and the reason pure black text on pure white (21:1) is not 'more accessible' than a well-chosen dark grey; it is just harsher.
- Grey text on white fails long before it looks faint: #999 on white is 2.8:1 (fails AA for everything); #767676 is 4.54:1 (passes). The 'muted' text in most products is failing silently — the design review argument is really a luminance argument.
- Brand colours almost never pass for text: most logo blues and reds land between 3:1 and 4.4:1 on white — fine for large type and UI components (3:1 AA), failing for body copy. The professional move is a text-ink variant of the brand colour, tuned darker, used for words.
The napkin workflow
When a palette arrives, compute three ratios before anything else: body text on background (needs 4.5), large text and UI icons on background (needs 3), and the brand accent as text on background (needs 4.5 or a darker variant). Failures get fixed in the token layer — a --text-muted token, a --brand-ink token — so the design system encodes the maths instead of re-arguing it per screen.