Color Contrast Beyond 4.5:1 — APCA and the Future of Readability
Why WCAG contrast ratios are flawed, how APCA works, and practical dark mode design that meets perceptual requirements.
The Problem with WCAG Contrast Ratios
WCAG 2.x uses the luminance contrast ratio formula defined in 2008. It works by comparing relative luminance values of foreground and background colors. The formula: (L1 + 0.05) / (L2 + 0.05) where L1 is the lighter color.
The problem? This formula doesn't account for how humans actually perceive contrast. A white-on-dark-blue text at 4.5:1 feels MORE readable than dark-orange-on-white at the same ratio. The perceptual model is wrong.
Additionally, WCAG ratios treat all text sizes the same below 18pt (or 14pt bold). But an 11px footnote needs much more contrast than a 16px body paragraph to be equally readable.
APCA: Perceptual Contrast Done Right
APCA (Accessible Perceptual Contrast Algorithm) is the next-generation contrast model developed by Andrew Somers for WCAG 3.0. Unlike WCAG 2.x ratios, APCA considers: polarity (dark-on-light vs light-on-dark behave differently), font size and weight, spatial frequency of text, and human visual perception models.
APCA produces a Lightness Contrast value (Lc) ranging from 0 to ±108. The key insight: APCA assigns different minimum Lc values based on font size and weight. Large bold headings need less contrast than small body text. This matches how we actually read.
APCA in practice
Body text (16px, 400 weight) needs Lc 75+. Large headings (32px, 700 weight) only need Lc 60+. Placeholder text (non-essential) can be Lc 45+. This flexibility enables better design.
Designing Dark Mode That Actually Works
Most dark modes fail accessibility because they simply invert colors. White (#fff) on black (#000) has a 21:1 contrast ratio — technically 'passing' but actually harder to read due to halation (bright text on dark backgrounds appears to bleed/glow).
Best practices for dark mode: Use off-white text (#ededf0) on dark backgrounds — not pure white. Keep body text at Lc 80-90 (not maximum). Use slightly warmer dark backgrounds (#0c0c10) instead of pure black. Increase font weight by one step in dark mode (400→500 for body).
- Use off-white (#ededf0) instead of pure white (#fff) for body text
- Dark background should be warm-tinted (#0c0c10), not pure black
- Increase body font weight by one step in dark mode
- Large text (24px+) can use lighter colors — Lc 60 is sufficient
- Secondary text should be Lc 60+ (not the Lc 40 many designs use)
- Test with f.lux/Night Shift on — many users use these tools
/* Dark mode done right — RegLayer's approach */
.dark {
--background: #0c0c10; /* Warm navy-black */
--foreground: #ededf0; /* Off-white, not #fff */
--muted: #8b8b99; /* Lc 60+ for secondary text */
--border: #1e1e2a; /* Visible but subtle */
--accent: #6366f1; /* Indigo — vibrant without glare */
}
/* Anti-halation: slightly lighter font weight in dark mode */
@media (prefers-color-scheme: dark) {
body { font-weight: 450; }
h1, h2, h3 { font-weight: 650; }
}Testing Contrast: Tools and Workflow
RegLayer tests both WCAG 2.x ratios (for current compliance) and APCA Lc values (for future-proofing). Here's a practical testing workflow:
Step 1: Run automated scan — catches obvious failures (sub-3:1 contrast, invisible text). Step 2: Check dark mode separately — many sites only test light mode. Step 3: Test with browser zoom at 200% — text may become unreadable at different sizes. Step 4: Check non-text contrast — borders, icons, and focus indicators need 3:1 minimum.
Check your contrast ratios
RegLayer tests both WCAG 2.x ratios and APCA perceptual contrast. Get the full picture.
Run Free Scan