WCAG Contrast Explained
A WCAG contrast check comes down to one number — a ratio between 1:1 and 21:1 that measures how far apart two colors are in lightness, not in hue. Grey text on white either clears 4.5:1 or it doesn't, and someone with low vision can read it or can't. This guide builds that number from first principles: what the ratio actually compares, how each color's relative luminance is computed by linearizing its sRGB channels and weighting them for the human eye, what the AA and AAA thresholds are and where the "large text" exception comes from, and — worked out by hand — why the common web grey #767676 on white lands at exactly the edge of passing.
What the contrast ratio measures
Contrast ratio answers one narrow question: how different are these two colors in brightness? It deliberately ignores hue. A vivid red and a vivid green can look wildly different yet score close to 1:1, because they reflect a similar amount of light. That is the whole point — the ratio predicts whether a person can tell the shape of a letter from its background, which depends on luminance difference far more than on which colors you chose.
The formula itself is short. Give each color a relative luminance — a single value from 0 (black) to 1 (white) — call the brighter one L1 and the darker one L2, and the ratio is:
Two identical colors give (L + 0.05) / (L + 0.05) = 1, i.e. 1:1 — invisible text. The maximum is pure white on pure black: (1.0 + 0.05) / (0.0 + 0.05) = 1.05 / 0.05 = 21, i.e. 21:1. That +0.05 added to both terms is not arbitrary: it models a small amount of ambient light reflecting off the screen glass, so a "black" pixel is never truly zero. Without it the darker term could reach 0 and the ratio would blow up to infinity; with it the scale politely stops at 21.
How relative luminance is computed
The interesting work is turning a color like #767676 into that single luminance value. It happens in three conceptual steps.
1. Scale each channel to 0–1. An sRGB color is three 8-bit channels, 0–255. Divide each by 255 so red, green and blue are fractions between 0 and 1.
2. Linearize (undo gamma). sRGB values are not stored in proportion to physical light — they're gamma-encoded so that the limited 0–255 steps are spent where the eye notices them, bunched toward the dark end. To reason about actual light you must undo that encoding. Each channel v is expanded: if v ≤ 0.03928 it is nearly linear already and you just divide by 12.92; otherwise you apply ((v + 0.055) / 1.055)2.4. The result is linear-light — doubling it really means twice the photons.
3. Weight for the eye and sum. The three linear channels are combined into one luminance with fixed weights:
Those coefficients are not guesses — they reflect that human vision is dramatically more sensitive to green than to blue. Green carries roughly 72% of perceived brightness, red about 21%, and blue barely 7%. This is why a saturated blue looks "dark" and a saturated yellow (red + green) looks "bright" even at the same RGB magnitude, and why swapping a heading from blue to green can quietly change whether it passes.
The thresholds: AA, AAA, and what "large" means
WCAG 2 fixes five numbers. Two conformance levels (AA and AAA) times two text sizes give four text thresholds, plus one for non-text interface parts:
| Requirement | Applies to | Minimum ratio |
|---|---|---|
| AA | Normal text | 4.5:1 |
| AA | Large text | 3:1 |
| AA · 1.4.11 | UI components, graphics | 3:1 |
| AAA | Normal text | 7:1 |
| AAA | Large text | 4.5:1 |
AA is the level most laws and contracts reference — the EU's EN 301 549, US ADA/Section 508 guidance and similar rules all point at WCAG AA. AAA is a stricter target you meet where practical rather than site-wide; the spec itself notes AAA is not required as a blanket policy.
Large text earns the relaxed 3:1 bar because thicker strokes stay legible with less contrast. WCAG defines it precisely: at least 18pt (about 24px at the default 96dpi) for regular weight, or at least 14pt (about 18.66px) when bold. The catch is that this is real rendered size — zoom, responsive units and a reader's own font-size settings all shift what actually counts, so a borderline heading is safest held to the 4.5:1 normal-text bar anyway. The separate 1.4.11 Non-text Contrast rule extends 3:1 to things that aren't text at all: an input's border, an unchecked checkbox, a focus ring, the slices of a chart. A pale #e0e0e0 field outline on white sits near 1.3:1 and is effectively invisible to low-vision users, yet passes no automated text check because there's no text — which is exactly why 1.4.11 exists.
Worked example: #767676 on white
Let's run the machinery on one concrete pair — the workhorse grey #767676 as text on a white #FFFFFF background — and see which thresholds it clears.
So #767676 on white scores about 4.54:1. Compare that against the table: it clears AA for normal text (needs 4.5:1) by the thinnest of margins, comfortably passes AA large and AAA large (both need less), but fails AAA for normal text (needs 7:1). This is why #767676 is often cited as the lightest pure grey you can safely use for body text on white — nudge it a shade lighter to #787878 and it slips below 4.5:1 and fails AA. One number decides it.
Why this matters beyond aesthetics
It is tempting to treat contrast as a taste question — "this grey looks a bit light" — but the ratio is standing in for real people. Low-vision users, readers with cataracts or aging eyes, anyone on a dim phone screen in bright sun: for them a 3:1 body text isn't merely "subtle," it can be genuinely unreadable, and the content is lost. The thresholds are the accessibility community's calibrated line between "most people can read this" and "a meaningful group cannot." Meeting them is also increasingly a legal floor rather than a nicety.
Two honest limits keep the number in perspective. First, contrast is luminance only — it says nothing about hue, so a red/green pairing can pass the ratio yet be indistinguishable to someone with red-green color blindness; never carry meaning by color alone. Second, the WCAG 2 formula is a known simplification: the newer APCA model being explored for WCAG 3 weights light and dark text differently and handles thin fonts more realistically. A green badge confirms the maths, not the whole experience — pair it with sensible font sizes and real testing.
Drop in a foreground and background and watch the exact ratio with live AA/AAA pass/fail for normal text, large text and UI components — plus a preview in both themes. Runs entirely in your browser; nothing is uploaded.
Open the WCAG contrast checker → to test your own colors live, or browse all Polyatic tools.