
Updated 2026-08-24.[1]
Executive Summary
WCAG 2.x's contrast formula was specified in 1999 for display calibration, not legibility. The math is achromatic and symmetric — properties that produce documented false passes for saturated chromatic text and erase the polarity distinctions designers treat as meaningful. Hot pink on near-black scores 6.6:1, a comfortable AA pass that isn't readable.
OKCA closes that gap at the algorithm layer. The interface stays intact (same 1–21 scale, same 4.5 and 7.0 thresholds), but the math underneath swaps in OKLCH L as the luminance input, applies a chroma-weighted penalty to the lighter element, and scales the final ratio asymmetrically by polarity. Hot pink on near-black drops to 3.6; the same-luminance neutral grey still passes.
OKCA is constrained by an FP = 0 invariant: it never approves a pair WCAG would reject. Since v2.0.0 that holds by construction across sRGB — an exact identity plus two interval-verified lemmas, certified with zero uncertified boxes and enforced in CI. A 1,249-pair probe across light-on-dark, dark-on-light, and three production design systems (Tailwind, GOV.UK, USWDS) returns zero false passes and 97 intentional disagreements, all sitting just above WCAG's 4.5 line. OKCA is on npm as @pawn002/okca, MIT-licensed.
The Gap
#ff69b4 on #1a1a1a scores 6.6:1 under WCAG 2.x, a comfortable AA pass. Anyone who has run a real audit will tell you it isn't readable for users with moderate contrast sensitivity loss. The math passes; the experience doesn't.
The gap isn't a bug. The formula is doing exactly what it was specified to do. The problem is that it was never designed to predict legibility. It was designed for display calibration in 1999, and twenty-five years later it underwrites nearly every accessibility ratio reported on the web.
I built OKCA to close the gap.
I've spent over a decade building accessibility tooling inside a special corner of the federal government. Contrast Chaser, a browser tool I built, picked up grassroots adoption there. OKCA comes from the same problem space: designers and accessibility engineers running into pairs that pass WCAG but don't work in production. The difference is that OKCA attacks it at the algorithm layer rather than the tooling layer.
What WCAG Is Computing
WCAG's contrast formula is a luminance ratio:
The luminance values come from linearised sRGB per IEC 61966-2-1, the 1999 display-calibration spec. Two consequences fall out, and both are well documented.
The formula is achromatic. Any two foreground colors at the same produce identical ratios regardless of hue or saturation. The neutral grey that matches hot pink's luminance is #9f9f9f (); on #1a1a1a it scores the same 6.6:1. Practitioners don't treat the two pairs the same way. The formula can't tell them apart.
It's also symmetric. ratio(A on B) = ratio(B on A), so light-on-dark and dark-on-light score identically. But designers treat polarity as a meaningful input. Light mode and dark mode are different decisions, not interchangeable, and the formula discards that distinction.
One failure produces false passes for chromatic text. The other hides asymmetry that practitioners end up encoding by hand.
What OKCA Does Instead
OKCA changes the math underneath and leaves WCAG's interface intact: same scale ([1, 21]), same AA threshold (4.5), same AAA threshold (7.0). Designers don't have to relearn the numbers.
OKLCH L as the luminance input. OKCA reads the L channel from OKLCH, the perceptually uniform lightness from the Oklab color space. For neutral greys, holds to floating-point precision; the cube root inverts the perceptual transform back to linear luminance. So on the achromatic axis, OKCA and WCAG are reading the same numbers, just in a representation a designer can see directly in any modern color picker.
Chroma compression on the lighter element. This is where the false-pass problem gets fixed. The lighter element's lightness is penalised by a chroma-weighted power exponent: more saturation, larger penalty.[2] The exponent runs from 1.0 at fully achromatic up to 1.65 at fully saturated. Hot pink has Oklab chroma around 0.197, saturated enough to receive the full penalty. Its effective luminance proxy drops, the raw ratio falls with it, and OKCA scores hot pink on near-black at 3.6. Below AA. The same-luminance grey scores 5.8 and passes. The math now distinguishes what practitioners always could.
Polarity-aware scaling. OKCA's final step applies a power curve that differs by polarity: light-on-dark caps at 20.9, dark-on-light at 20. Same shape, different ceiling. The light-on-dark cap sits just below WCAG's 21 deliberately — that gap is what holds every OKCA score strictly under WCAG's, and it is the load-bearing piece of the FP = 0 proof below. White on black scores 20.9; black on white scores 20.0. White on #767676, the canonical WCAG AA boundary grey, scores 3.9 light-on-dark and 3.7 dark-on-light. Both fail AA. The case for putting the line there is in the probe section below.
The FP = 0 Invariant
A contrast algorithm intended for accessibility decisions has one hard constraint: it must never approve a pair that WCAG would reject. The reason is asymmetry. A false pass ships inaccessible text to production. A false failure just nudges a designer toward a safer choice.
OKCA holds FP = 0: it never scores a pair above its WCAG ratio. Since v2.0.0 this is proven by construction across sRGB rather than sampled — an exact identity plus two interval-verified single-color lemmas, with the verifier (npm run fp0) certifying the full sRGB cube with zero uncertified boxes. It runs in CI on every push, so a change that broke the invariant could not ship. The proof is in docs/FP0_PROOF.md.
In practice: across the sRGB gamut, a designer who trusts the OKCA number won't accidentally ship text that WCAG would reject.
What the Probe Data Shows
The FP = 0 property rules out unsafe outputs. It doesn't show OKCA's numbers are calibrated — an algorithm that scored every pair at 1.0 would also satisfy FP = 0. The probe work tests calibration: does OKCA agree with WCAG where WCAG is right, and disagree only where practitioners would?
Three test batteries, 1,249 pairs total:
| Battery | Pairs | False passes | WCAG disagreements |
|---|---|---|---|
| Light-on-dark | 53 | 0 | — |
| Dark-on-light | 54 | 0 | — |
| Design systems | 1,142 | 0 | 97 |
| Total | 1,249 | 0 | 97 |
The design systems battery pulls documented foreground/background pairs from Tailwind CSS v3.4 (34 disagreements), the GOV.UK Design System (13), and USWDS v3.x (50). These are production palettes maintained by teams that take accessibility seriously.
All 97 disagreements sit in the marginal zone, where OKCA scores below 4.5 and WCAG scores at or above it. They're intentional. WCAG's 4.5 boundary is widely treated as too permissive in production, but the argument I'd actually defend is about variance rather than taste. A threshold experiment on white/#767676 put the same observer's judgment of shippable body text at 5.2 under one display brightness and 2.8 under another — same room, same session. OKCA scores that pair 3.9, between the two. A pair sitting on the AA line carries no margin against the conditions it will actually be read in, and no fixed number resolves a judgment that moves that far. What a floor above the line buys is margin. Full enumeration with hex values is in WCAG_DISAGREEMENTS.md in the repo.
Zero false passes across 1,249 pairs. The 97 disagreements come with the math.
What OKCA Does Not Do
OKCA is just a contrast algorithm. It deliberately doesn't:
- Model font size or weight. OKCA outputs a single ratio per pair. Size-dependent thresholds (WCAG AA's 3 for large text, 4.5 for normal) are the caller's responsibility.
- Patch WCAG's green-channel weighting. Chroma compression is rotationally symmetric in a/b space. Hue-varying outcomes come from different hues reaching different chroma values at similar lightness, not from OKCA targeting specific hues.
- Replace perceptual judgment. A pair that scores 4.5 clears the threshold but may still be harsh, too low-contrast in context, or wrong for the brand. The number is a floor, not a recommendation.
What's Next
OKCA is on npm as @pawn002/okca, MIT-licensed. The repo is pawn002/okca. The design document is at docs/OKCA_DESIGN.md; the FP = 0 proof is at docs/FP0_PROOF.md.
Try it in one line. No install required — npx klar-cli contrast "#ff69b4" "#1a1a1a" prints Contrast (OKCA): 3.6, the same hot-pink-on-near-black pair from the opening. klar is an MIT CLI that uses OKCA for its contrast engine.
OKCA is one independent attempt to close WCAG 2.x's contrast gaps. It is clean-room work, derived from public specifications, and accepts hex codes and CSS oklch()/oklab() natively.
If you maintain a design system, run your palette through it. If you find an sRGB pair OKCA scores higher than WCAG, that's a false pass — and since v2.0.0 the proof says it cannot happen, so a counterexample would be a real finding. Open an issue.
2026-06-09 — the original framing of the FP = 0 guarantee ("enforced mathematically… unconditional for every input") overstated it, and the claim was narrowed. 2026-08-24 — OKCA v2.0.0 made FP = 0 provable by construction across sRGB, so the narrowed claim now understates it; the same recalibration moved every anchor figure in this post. v2.0.3 separately retracted the argument that the marginal pairs here are ones practitioners reject on sight, after a threshold experiment found that judgment inverts with display brightness — the probe section now argues from variance. The zero-false-pass result itself has never changed. Earlier wording available on request. ↩︎
Throughout this post I use "saturation" in the everyday sense, meaning how strong or colorful a hue appears to a viewer. OKCA's math operates on Oklab chroma, the absolute distance from neutral in a/b space. The two terms are closely related but not formally identical. ↩︎