WCAG Contrast Ratio Formula Explained: The Math, the Flaws, and Why APCA Is Next
Published on May 25, 2026 by The Kestrel Tools Team • 7 min read
You’ve probably seen the advice: “aim for a 4.5:1 contrast ratio.” Maybe you’ve even plugged colors into a checker and moved on when the green checkmark appeared. But what does that number actually measure? The WCAG contrast ratio formula explained in full reveals a surprisingly simple equation — and some surprising blind spots that affect real-world readability.
This article walks through the actual math behind WCAG 2’s contrast algorithm, shows where it misjudges contrast in common UI scenarios, and covers what APCA (the likely successor in WCAG 3) does differently.
What the WCAG Contrast Ratio Formula Actually Computes
The formula itself is deceptively short:
Contrast Ratio = (L1 + 0.05) / (L2 + 0.05)
Where L1 is the relative luminance of the lighter color and L2 is the relative luminance of the darker color. The result ranges from 1:1 (identical colors) to 21:1 (pure black against pure white).
WCAG 2.1 Success Criterion 1.4.3 requires:
- 4.5:1 for normal text (under 18pt or 14pt bold)
- 3:1 for large text (18pt+ or 14pt+ bold)
- 3:1 for UI components and graphical objects
Simple enough. But the interesting part is how L gets calculated.
Relative Luminance: The Core Calculation
Relative luminance represents how bright a color appears to the human eye, normalized to a 0–1 scale where 0 is the darkest black and 1 is the brightest white.
For an sRGB color with 8-bit channels (0–255), the calculation involves three steps:
Step 1: Linearize each channel.
Convert each sRGB value from its gamma-compressed form to linear light:
function linearize(channel) {
const srgb = channel / 255;
if (srgb <= 0.04045) {
return srgb / 12.92;
}
return Math.pow((srgb + 0.055) / 1.055, 2.4);
}
This reverses the sRGB transfer function (often called “gamma correction”). The exponent 2.4 approximates the sRGB curve — not exactly gamma 2.2, which is a common misconception.
Step 2: Weight the channels.
const L = 0.2126 * linearR + 0.7152 * linearG + 0.0722 * linearB;
These coefficients reflect human sensitivity to each primary: green dominates because our eyes have the most green-sensitive cones. The weights come from the ITU-R BT.709 standard (the same color space used for HD video).
Step 3: Compute the ratio.
Plug both luminance values into the contrast formula. The + 0.05 offset on both sides prevents division by zero and represents the light reflected off a typical screen even when displaying “black.”
Where the Formula Breaks Down
The WCAG 2 formula works reasonably well for text on white or near-white backgrounds — the scenario it was originally designed for. But it produces misleading results in several common cases:
Problem 1: It Ignores Polarity
The formula treats light-on-dark and dark-on-light identically. In practice, white text on a dark blue background reads differently than dark blue text on a white background — even at the same contrast ratio. Human perception is asymmetric: we’re more sensitive to luminance differences in lighter regions.
A 4.5:1 ratio with dark text on white feels more readable than 4.5:1 with light text on dark. The formula can’t express this.
Problem 2: The +0.05 Offset Distorts Mid-Range Colors
That + 0.05 constant was a practical hack to handle screen black levels circa 2008. But it compresses the ratio scale at the dark end. Two very dark colors — say #1a1a2e and #16213e — can technically “pass” WCAG AA even though they’re nearly impossible to distinguish on most displays.
Conversely, some readable mid-range pairings get flagged as failures because the offset amplifies differences in the lighter range.
Problem 3: It Doesn’t Account for Font Size or Weight Properly
WCAG 2 has only two thresholds: normal text (4.5:1) and large text (3:1). The cutoff at 18pt is arbitrary. A 16px font at 700 weight is more readable than an 18px font at 300 weight, but the formula treats the latter more leniently.
A Real-World Example
Consider these two pairings:
| Foreground | Background | WCAG Ratio | Actual readability |
|---|---|---|---|
#767676 (gray) | #ffffff (white) | 4.54:1 ✅ | Acceptable |
#7c5cff (purple) | #ffffff (white) | 4.27:1 ❌ | Clearly readable |
The purple fails WCAG AA despite being perceptually easier to read than the gray that passes. This happens because the formula’s luminance weighting undervalues the blue channel contribution to perceived contrast.
How APCA Does It Differently
The Advanced Perceptual Contrast Algorithm (APCA) is the contrast method proposed for WCAG 3.0. Developed by Andrew Somers, it addresses the WCAG 2 formula’s limitations through a fundamentally different approach.
Key Differences
Polarity-aware. APCA produces different scores for light-on-dark versus dark-on-light. The same two colors yield different contrast values depending on which is foreground and which is background — matching how human vision actually works.
Perceptual lightness, not luminance. APCA uses a perceptual lightness model (related to CIELAB L*) instead of raw relative luminance. This better predicts what humans actually see, especially in the mid-tones where WCAG 2 struggles.
Continuous scale, not binary pass/fail. Instead of a single threshold, APCA returns a value from 0 to roughly 106 (Lc, lightness contrast). Different use cases map to different minimum Lc values:
| Use case | Minimum Lc |
|---|---|
| Body text (16px, 400 weight) | Lc 75 |
| Large text (24px+) | Lc 60 |
| Non-text UI elements | Lc 45 |
| Placeholder/disabled text | Lc 30 |
Font-size and weight aware. APCA’s lookup tables factor in font size and weight together, eliminating the crude “large text” binary of WCAG 2.
The APCA Calculation (Simplified)
The core idea:
- Convert sRGB to Y (luminance), similar to WCAG 2 but with refined linearization
- Apply a perceptual lightness curve to both foreground and background Y values
- Subtract foreground lightness from background lightness (polarity matters here)
- Apply scaling and clamping to produce the final Lc value
// Simplified APCA concept (not production code)
const Ytxt = linearize(textColor);
const Ybg = linearize(bgColor);
// Perceptual lightness (power curve, not the +0.05 hack)
const txtL = Math.pow(Ytxt, 0.56);
const bgL = Math.pow(Ybg, 0.65);
// Polarity-aware subtraction
const rawContrast = (Ybg > Ytxt)
? (bgL - txtL) * 1.14 // dark text on light bg
: (bgL - txtL) * 1.14; // light text on dark bg (different exponents in full algo)
The actual APCA algorithm uses different exponents for text-on-bg versus bg-on-text, plus soft clamping near zero — but the key insight is the asymmetric treatment.
The Current State: CSS color-contrast() and WCAG 3
CSS has a proposed color-contrast() function that would let browsers pick accessible foreground colors automatically:
/* Pick the color with highest contrast against the background */
.text {
color: color-contrast(var(--bg) vs white, black, navy);
}
As of mid-2026, this is still at Editor’s Draft stage in CSS Color Level 5. No browser ships it unflagged. The specification doesn’t yet commit to WCAG 2 or APCA as the contrast algorithm — that decision is coupled to WCAG 3’s timeline.
WCAG 3.0 itself remains a Working Draft. The current plan uses APCA as the contrast method, but the conformance model (replacing A/AA/AAA with a scoring system) is still in flux. Practical adoption is likely 2027+ for normative requirements.
What This Means for Your Work Today
You still need to pass WCAG 2.1 AA for legal compliance and most accessibility audits. The 4.5:1 threshold isn’t going away soon. But understanding the formula’s limitations helps you make better decisions:
- Don’t trust the ratio blindly for dark themes. Test readability visually, especially for light text on dark backgrounds.
- Be skeptical of near-threshold passes. A 4.51:1 ratio on purple text might technically pass but still cause readability issues.
- Check APCA values as a second opinion. Tools like the Kestrel Tools Color Converter let you evaluate colors across multiple contrast models.
- Document your decisions. When you override a WCAG 2 failure because APCA confirms readability, note why.
The formula that’s governed web accessibility for 15+ years is a product of its time — a reasonable approximation that served its purpose. APCA represents the next generation: perceptually grounded, polarity-aware, and fine-grained enough for modern UI design. Until WCAG 3 lands, the smart approach is to satisfy WCAG 2 requirements while using APCA as your design compass.
Want to test your color pairings against both models? Try the Color Converter — it handles HEX, RGB, HSL, OKLCH, and shows contrast ratios in real time.