Color · 4 min read
Colour Contrast and WCAG: The Ratio, the Thresholds, What They Miss
The contrast formula is thirty years old and has a documented blind spot around dark colours. It is still the law in most places, so it is worth knowing precisely.
WCAG contrast is a ratio between the relative luminance of two colours, and it runs from 1:1 (identical) to 21:1 (pure black on pure white). The thresholds you need are 4.5:1 and 3:1.
How the ratio is computed
Three steps, and the middle one is where the interesting behaviour lives.
Step 1: normalise. Divide each 8-bit channel by 255 to get a value from 0 to 1.
Step 2: linearise. Screen values are gamma-encoded, so they must be converted back to linear light:
otherwise: clin = ((c + 0.055) ÷ 1.055)2.4
Step 3: weight and combine. The eye is far more sensitive to green than to blue:
Then the ratio itself, with L1 the lighter of the two:
The 0.05 represents ambient light reflecting off the screen. It is what stops the ratio going to infinity against pure black, and it is also the source of the formula’s main weakness, see below.
The thresholds
| Content | AA | AAA |
|---|---|---|
| Body text | 4.5:1 | 7:1 |
| Large text (24px, or 18.66px bold) | 3:1 | 4.5:1 |
| UI components and graphics | 3:1 | n/a |
| Incidental / decorative / logos | exempt | exempt |
AA is the level referenced by most legislation: the European Accessibility Act, Section 508 in the US, and the accessibility regulations in the UK and Canada. AAA is generally treated as aspirational; WCAG itself notes it is not achievable for all content.
Why grey-on-white keeps failing
Light grey text on white is the most common contrast failure on the web, and designers reach for it constantly because it looks calm and expensive.
| Grey on #FFFFFF | Ratio | AA body text |
|---|---|---|
| #999999 | 2.85:1 | Fail |
| #8E8E8E | 3.22:1 | Fail |
| #767676 | 4.54:1 | Pass (only just) |
| #666666 | 5.74:1 | Pass |
| #595959 | 7.00:1 | Pass AAA |
#767676 is the lightest grey that passes AA on white. It is worth memorising. Anything lighter fails for body text, however good it looks on your calibrated monitor in a dark room.
And that caveat matters. The people most affected are not using your setup. They are on a phone in sunlight, on an uncalibrated budget laptop, or have age-related contrast sensitivity loss, which is extremely common past 50 and is exactly what these thresholds exist to accommodate.
The usual offenders
- Placeholder text in form fields. Browser defaults are often around #757575 or lighter, and it is real content. People read placeholders to understand what a field wants.
- Disabled controls. Technically exempt, but if a user cannot read why something is unavailable, the exemption has not helped them.
- Focus indicators. WCAG 2.2 added specific requirements here. A focus ring needs 3:1 against the adjacent background, and the default browser outline frequently does not survive a custom theme.
- Text over images. Contrast varies across the image. Test the worst region, or use a scrim.
- Brand colours. Many corporate palettes were chosen for print. A brand yellow at 1.8:1 on white is not usable for text no matter what the guidelines say.
Contrast is not the only rule
WCAG 1.4.1 says colour must not be the only means of conveying information. Around 8% of men and 0.5% of women of Northern European descent have some form of colour vision deficiency: most commonly red-green.
So a chart distinguishing series by colour alone fails, even at perfect contrast. Add shape, pattern, direct labels or line style. A form field marked invalid by turning red fails; add an icon and a text message. A link identified only by colour needs either an underline or a 3:1 contrast difference against surrounding text plus a non-colour cue on hover and focus.
The colour blindness simulator is the quickest way to check a palette: if two series become indistinguishable under deuteranopia, they need another differentiator.
The formula’s known weakness, and APCA
The WCAG 2.x ratio has a documented problem with dark colours, caused by that +0.05 ambient term. It compresses differences at the dark end, which produces two visible errors:
- Some pairs that pass comfortably are genuinely hard to read. Dark grey on black often clears 4.5:1 while being unpleasant in practice.
- Some pairs that fail are perfectly legible, particularly light text on mid-dark backgrounds.
The formula is also symmetric: it does not care which colour is the text and which is the background. Perceptually this is wrong: dark-on-light and light-on-dark of the same measured ratio do not read the same way, an effect related to how stroke weight appears to change against different grounds.
APCA (Accessible Perceptual Contrast Algorithm) is the proposed replacement being developed for WCAG 3. It is polarity-aware, accounts for font weight and size, and outputs an Lc value from roughly −108 to 106 rather than a ratio. It models perception considerably better.
It is also not yet normative, and WCAG 3 remains a working draft years from ratification. Build to WCAG 2.2 AA today: that is what regulations reference and what an audit will test against. Use APCA as a tiebreaker when the 2.x ratio gives an answer your eyes disagree with, particularly in dark mode.
The contrast checker computes the WCAG 2.x ratio and shows pass/fail for each threshold. If a pair sits between 4.4 and 4.6, treat it as failing. Rounding at the boundary is not a defence anyone will accept.
Common questions
Does the contrast rule apply to logos?
No. WCAG 1.4.3 explicitly exempts logotypes, as well as text that is purely decorative, invisible, or part of a picture containing significant other visual content. Inactive UI controls are exempt too. Everything a user is expected to read is in scope.
What counts as large text?
18 point (24px) or larger, or 14 point (18.66px) bold or larger. Large text needs only 3:1 for AA rather than 4.5:1, because stroke width contributes to legibility independently of colour. Note the thresholds are in points, which is why the pixel equivalents look arbitrary.
Do I need to check contrast against a gradient?
Yes, and against the worst point of it. The requirement applies to the actual rendered background behind the text. For gradients, background images or video, test the lightest and darkest regions the text can sit on, or add a solid scrim behind the text so there is one background to test.