Skip to content

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.

By Published Updated

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 sRGB

Results 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:

  1. 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.
  2. 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.
  3. 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:

  1. 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.
  2. 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.

Worked example: a brand colour that “passes”

Brand teal #3aa6a0 on white background. WCAG contrast: 2.99:1 — fails AA for body text (need 4.5:1) and fails the 3:1 threshold for large text by a hair. APCA score: Lc 52, which APCA rates as “use only for non-content text or above 24px.” Both metrics agree: this teal does not pass as body text on white.

Darkening the teal one step to #2f8682: WCAG 4.52:1 (passes AA body), APCA Lc 67 (passes APCA for large text, marginal for body). One hex-digit change moves the component from failing to passing — and the underlying perceived hue stays recognisably the same teal. Brand colours rarely survive contact with accessibility without a dark-mode variant; build the variant at the same time as the brand palette, not retroactively.

Common mistakes

  • Testing only against white. Body text on a coloured panel, button text on a coloured button, link text in a footer with a dark background — each pair needs its own check. A token system that mandates per-pair contrast verification catches what eyeballing misses.
  • Ignoring text over images. Hero headlines over photographs almost always fail somewhere on the image. Solutions: a semi-transparent dark overlay beneath the text (drop opacity to 0.4-0.6), a text shadow that creates contrast at the glyph edge, or repositioning the text onto a solid panel.
  • Relying on hover states for legibility. Hover styles often improve contrast (darker on hover) — but the resting state is what the user reads. If the resting state fails, the hover doesn’t rescue it.
  • Using rgba() with low alpha for text.Translucent text inherits whatever’s behind it. The computed contrast depends on the background; on a coloured background, alpha < 0.8 almost always fails. Use opaque hex values for text, reserve alpha for decorative chrome.
  • Treating colour-blind simulation as accessibility testing.Sim tools (Chrome DevTools, Stark) show what a colourblind user sees approximately. They don’t test whether contrast is adequate; they test whether colours are distinguishable. Both checks are needed.

For the underlying colour-space mechanics that the formula linearises, see our CSS color formats guide.

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; US DOJ ADA Title III web accessibility rule (2024); EU Accessibility Act (Directive 2019/882).

Frequently asked questions

What contrast ratio does WCAG 2.1 AA require for body text?
WCAG 2.1 AA requires a 4.5:1 contrast ratio for normal text (below 18 pt / 14 pt bold) and 3:1 for large text (18 pt+ or 14 pt+ bold).
How do I calculate the contrast ratio between two colours?
Contrast ratio = (L_lighter + 0.05) / (L_darker + 0.05), where L is relative luminance in linear sRGB. Convert hex to sRGB, linearise using the IEC 61966 gamma, then apply the luminance formula before dividing.
What is the difference between WCAG AA and AAA contrast levels?
AA requires 4.5:1 for text (3:1 for large text) and is the legal minimum in most jurisdictions. AAA requires 7:1 for text (4.5:1 for large) and is recommended for critical readability contexts like medical or legal interfaces.
Does the WCAG contrast formula work for all visual impairments?
No — the WCAG 2.x formula is based on luminance only and does not account for hue-based colour blindness or low-contrast within similar luminance values. The APCA algorithm (proposed for WCAG 3.0) better correlates with real-world readability.
Is there a CSS background colour that passes 4.5:1 against both black and white text?
No single colour achieves 4.5:1 against both simultaneously. Black (#000000) has a ratio of 21:1 against white and 1:1 against itself; you must pick one foreground per background and verify with a contrast checker tool.
When does the European Accessibility Act require WCAG compliance?
The European Accessibility Act (Directive 2019/882) required covered digital products and services to meet WCAG 2.1 AA by June 28, 2025, with enforcement applying to new contracts from that date.

Sources & references

Authoritative references cited by this piece. Verified by Buğra Sözeri on the dates shown and re-checked at every deploy.

Related

Published May 16, 2026 · Last reviewed May 31, 2026