Contrast is the most common accessibility failure on the web, and the rules fit on an index card. Here they are, with the exact maths, the edge cases auditors actually flag, and free ways to check.
| What | Level AA | Level AAA | Success criterion |
|---|---|---|---|
| Normal text | 4.5:1 | 7:1 | 1.4.3 / 1.4.6 |
| Large text | 3:1 | 4.5:1 | 1.4.3 / 1.4.6 |
| UI components and graphics | 3:1 | — | 1.4.11 |
Almost every law and procurement rule that mentions accessibility — Section 508 in the US, EN 301 549 behind the European Accessibility Act, the ADA settlements — points at level AA. AAA is a target, not usually a requirement.
Ratios run from 1:1 (the same colour twice) to 21:1 (black on white). Text that is purely decorative, part of a logo, or on a disabled control is exempt.
WCAG defines large text as at least 18 point, or at least 14 point bold. In CSS pixels that is 24px normal weight, or about 18.66px at bold (weight 700 or more). A 20px heading at weight 400 is not large, so it still needs 4.5:1.
Success criterion 1.4.11, Non-text Contrast, asks for 3:1 between a component's visual boundary and whatever is next to it — the border of a text field against the page, a checkbox outline, an icon that carries meaning, the line in a chart. A pale grey input border on white is the classic failure: #d1d5db on white is about 1.5:1.
Each colour is converted to relative luminance, then:
ratio = (L_lighter + 0.05) / (L_darker + 0.05)
Luminance comes from the sRGB channels, each divided by 255 and linearised:
c = channel / 255
c = c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ^ 2.4
L = 0.2126 * R + 0.7152 * G + 0.0722 * B
Green dominates, which is why pure red #ff0000 on white only reaches 4.00:1 and fails for body text, while pure blue #0000ff reaches 8.59:1.
| Text on white | Ratio | Normal text AA |
|---|---|---|
#595959 | 7.00:1 | Pass (and AAA) |
#6b7280 | 4.83:1 | Pass |
#767676 | 4.54:1 | Pass — the lightest grey that does |
#777777 | 4.48:1 | Fail |
#9ca3af | 2.54:1 | Fail — a very common placeholder grey |
#f97316 (orange) | 2.80:1 | Fail, even for large text |
4.48:1 is a fail. There is no rounding in WCAG; tools that display one decimal place can hide it.
Semi-transparent text or overlays change the colour a person actually sees. rgba(0,0,0,0.5) text on white renders as mid-grey, not black. You have to composite every translucent layer before measuring.
Text over a gradient must pass at its worst point, not its average. Text over a photo can't be verified from CSS at all — it needs a human look, or a solid backing behind the text.
Placeholder text that carries information (a format hint, a required field) is text and needs 4.5:1. Only truly disabled controls are exempt.
A button that passes at rest and fails on hover still fails. Red error text is a frequent offender: #ef4444 on white is about 3.8:1.
For one pair of colours: the free contrast checker on this site gives all four verdicts at once and suggests the nearest colour that passes. It runs in your browser.
For a whole page: Chrome DevTools shows the ratio for one element when you inspect it, and Lighthouse lists failures, but neither handles translucent layers or gradient worst-cases well, and neither gives you something to hand a client.
ContrastForge is our extension for this. It scans every visible text element on the page, composites translucent layers properly, tests gradients at their worst colour stop, reports text over images as unchecked instead of guessing, and exports the failures — with the WCAG criterion and a passing colour for each — as a report. Checking any single pair is free, and so are five full-page scans a month.
See ContrastForgeMore on this topic: WCAG 2.2 AA checklist: all 55 success criteria in plain language · How to do an accessibility audit (WCAG 2.2 AA): a practical checklist · Accessibility Conformance Report (ACR) and VPAT: what they are and how to write one · Color blindness simulator