Color contrast is one of the easiest accessibility wins to get right and one of the most common things audits flag. Here's the short, practical version I keep coming back to.
The numbers you actually need
Contrast is expressed as a ratio between text color and background, from 1:1 (identical) to 21:1 (black on white). The WCAG thresholds:
- AA — normal text: at least 4.5:1
- AA — large text (≈24px, or 19px bold): at least 3:1
- AAA — normal text: at least 7:1
- AAA — large text: at least 4.5:1
AA is what most teams target and what automated tools (Lighthouse, axe) check by default.
How the ratio is computed
It's based on relative luminance, not a naive RGB difference. For each channel you normalize to 0–1, linearize:
c_lin = c <= 0.03928 ? c/12.92 : ((c + 0.055)/1.055) ** 2.4
then L = 0.2126*R + 0.7152*G + 0.0722*B. The ratio is (Llight + 0.05) / (Ldark + 0.05). That 0.05 is the flare term so pure black on pure white tops out at 21, not infinity.
Practical tips
- Don't trust your eyes — a gray that "looks fine" on your calibrated monitor often fails. Check the number.
- Large text gets a lower bar (3:1), so headings can use lighter colors than body copy.
- Placeholder text and disabled states are the usual offenders — they're often way under 4.5:1.
- Brand colors rarely pass on white. Darken the text shade for body copy and keep the bright brand color for large headings or non-text accents.
Quick check
I put up a tiny client-side checker that shows the ratio and AA/AAA pass/fail for normal and large text, with a live preview: https://tsetsobg.github.io/tools/color-contrast-checker — paste two hex values and you'll see instantly whether a pairing works.
What contrast threshold does your team enforce in CI — AA, or do you push for AAA on body text?
Top comments (0)