Guide
Color contrast for accessibility: WCAG 2.1, APCA, and what to actually ship
WCAG 4.5:1 is the legal floor. It's also a flawed formula. Here's the modern picture.
WCAG 2.1 requires 4.5:1 contrast ratio for regular body text and 3:1for large text (18pt+ regular or 14pt+ bold). The formula is the de-facto legal standard in the US (ADA-relevant), EU (EAA), and most of the world. It also has well-documented flaws that have spawned a replacement (APCA) which didn’t make WCAG 2.2. This guide explains the practical picture today.
The WCAG 2.x contrast formula
Defined in WCAG 2.0 (2008) and unchanged through 2.2:
contrast = (L_light + 0.05) / (L_dark + 0.05)
where L = relative luminance (0-1) of each colour, computed in linear-light sRGBResults range from 1:1 (identical colours) to 21:1 (pure white on pure black).
WCAG thresholds (for AA compliance, the standard target):
- 4.5:1 — body text, icons, form controls.
- 3:1 — large text (≥ 18pt or ≥ 14pt bold), UI components (button borders, focus indicators).
- AAA targets are stricter: 7:1 body, 4.5:1 large. Required only in some specific contexts (legal text, government).
Where the formula is wrong
Three documented problems:
- Mid-tone insensitivity. The formula gives equal contrast scores to mid-tone pairs (e.g., grey on grey) that look very different in actual perception. A 4.5:1 pair of mid-greys can be visually weaker than a 3:1 pair of black on light grey.
- Hue-blind.Equal-luminance hues (red and green at the same brightness) score 1:1 contrast, but dichromat users may discriminate them poorly anyway. The formula can’t detect colour-blindness issues.
- Font weight ignored.Thin fonts need more contrast than bold fonts at the same size; WCAG gives no credit for weight beyond the binary “large text” threshold.
APCA: the replacement that didn’t make WCAG 2.2
APCA (Accessible Perceptual Contrast Algorithm) is the proposed-and-rejected next-generation contrast metric. Developed by Andrew Somers for WCAG 3.0 working draft. It accounts for the issues above and is dramatically more accurate against actual user-study data.
APCA scores range −108 to +106. Positive means dark text on light background; negative means the reverse. Thresholds:
- Lc 75 — body text (replaces WCAG’s 4.5:1).
- Lc 60 — large text or headlines (replaces 3:1).
- Lc 45 — non-content text (decorative, copyright lines).
APCA gives different thresholds per direction (light-on-dark vs dark-on-light) because human eyes process the two cases differently. WCAG’s symmetric formula misses this.
What to use in 2026
Two-track approach:
- Pass WCAG 2.1 AA at minimum.It’s the legal standard. Use the standard ratio formula, target 4.5:1 body / 3:1 large. Tools:
npm i wcag-contrastfor programmatic, browser DevTools (Chrome and Firefox both report contrast on hover) for ad-hoc. - Use APCA as a sanity check. When WCAG passes but the result looks subjectively weak, APCA usually flags it. APCA is available as
apca-w3(npm) and in dedicated tools (Atmos, Stark plugins).
Both metrics agree on the obvious cases (black text on white is fine; pale grey on white is not). They diverge on the mid-tone and large-text edge cases — exactly where modern design trends like to live.
Concrete recommendations
- Body text: targeting
#1a1a1a(dark grey) on white gives ~17:1 — pleasant, clearly legible. Pure black on white (21:1) can feel harsh; deliberate near-black is fine. - Disabled / placeholder text:WCAG exempts “inactive UI components” from the 4.5:1 rule, but users still need to read placeholders. Target 3:1 minimum.
- Buttons:button text vs button background must hit 4.5:1. Button border vs page background must hit 3:1 (for the “non-text contrast” rule).
- Focus indicators: the focus outline must hit 3:1 against the adjacent background. This is the rule that breaks the most when designers remove default browser focus outlines.
- Dark mode:the WCAG formula is symmetric; numbers translate. But APCA is asymmetric — dark mode tends to need slightly different design tokens than light mode for the same perceived quality. Don’t just invert colours.
What to skip
- Don’t use contrast as the sole signal for state. Error states need contrast andanother cue (an icon, text). Colour-blind users may not distinguish red errors from green successes even at high contrast.
- Don’t target AAA reflexively.WCAG’s own guidance says AAA is “not recommended as a general policy for entire sites” — it’s for specific contexts. AA is the design ceiling for most products.
- Don’t trust auto-generated colour ramps uncritically.An OKLCH ramp with even perceptual steps doesn’t automatically pass WCAG. Verify each adjacent pair.
The pragmatic workflow
At design time: use the WCAG contrast checker in your design tool (Figma, Sketch, Adobe XD all have plugins). Hit 4.5:1 / 3:1 minimums.
At code time: lint your design tokens against WCAG withwcag-contrast or similar. Fail CI on regressions.
At QA time: real screen-reader and keyboard-navigation testing. Contrast is a small slice of accessibility; the keyboard and screen-reader paths are where most a11y bugs actually live.
Sources: W3C WCAG 2.1 (2018) and WCAG 2.2 (2023); WCAG 3.0 Working Draft (2024); Andrew Somers, APCA-W3 documentation and supporting user studies.
Related
Published May 16, 2026