EmpireUI
Get Pro
← Blog8 min read#color contrast#accessibility#apca

Color Contrast Tools in 2026: APCA, Contrast Grid, Whocanuse

WCAG 2.x contrast ratios are showing their age. Here's how APCA, Contrast Grid, and Whocanuse actually work — and which tool to reach for when.

colorful paint swatches arranged by hue on white surface

Why the Old 4.5:1 Rule Isn't Enough Anymore

WCAG 2.1's contrast ratio algorithm — the one that gave us the famous 4.5:1 for normal text and 3:1 for large text — was written in 2008. That's 18 years ago. The model it uses, called WCAG 2.x relative luminance, treats all hue combinations as equal as long as the ratio number hits the threshold. Which means a dark blue on black can pass while being genuinely unreadable, and a saturated yellow on white can fail while looking perfectly fine. You've probably hit this wall before.

Honestly, the problem isn't that WCAG's authors were careless — it's that the formula was designed around CRT monitors, not the OLED panels and high-DPI displays almost everyone is using in 2026. The way human vision perceives contrast is context-dependent: font weight matters, font size matters, the spatial frequency of the letterforms matters. A 16px body paragraph in a thin 300-weight font needs far more contrast than a 48px bold headline, even though WCAG 2.x treats both identically.

This is the core motivation behind APCA — the Advanced Perceptual Contrast Algorithm — which is being developed as the backbone of WCAG 3.0. It's not a drop-in number swap. The whole conceptual frame shifts from a ratio to a "lightness contrast" value expressed in Lc (lightness contrast) units, typically ranging from Lc 15 (barely visible) to Lc 106 (maximum). Worth noting: the WCAG 3.0 spec still isn't finalized as of September 2026, so you're in a transition period where you need to satisfy 2.x for legal compliance while designing with APCA for actual quality.

The good news: you don't have to choose one tool and commit to it forever. The three tools covered here — APCA-based checkers, Contrast Grid, and Whocanuse — each answer a different question. Once you know which question you're asking, picking the right tool takes two seconds.

APCA: What the Algorithm Actually Does

APCA is developed by Andrew Somers under the Accessible Perceptual Contrast Algorithm project. The core idea: instead of a ratio between two luminance values, it calculates a *directional* lightness difference — polarity matters, so dark text on light is not treated the same as light text on dark. This alone fixes a massive class of false passes in WCAG 2.x.

The output is an Lc value with a sign. Positive Lc means dark text on light background. Negative Lc means light text on dark background. The magnitude tells you how usable the combination is. Rough guidance for 2026: Lc 75 is the target for 14px–16px body text at normal weight, Lc 60 works for 24px+ bold headings, and Lc 45 is the floor for large decorative text or disabled states. These aren't yet official WCAG 3.0 thresholds — think of them as well-tested community consensus.

// Quick APCA check using the apca-w3 npm package
import { APCAcontrast, sRGBtoY } from 'apca-w3';

// Convert hex to [r, g, b] 0-255 array first
const textColor  = [30, 30, 30];   // near-black
const bgColor    = [245, 245, 250]; // near-white

const Lc = APCAcontrast(
  sRGBtoY(bgColor),
  sRGBtoY(textColor)
);

console.log(Lc); // e.g. 97.3 — well above Lc 75, you're good

The best browser-based APCA tool right now is Colour Contrast Checker by Myndex at apcacontrast.com. You paste hex codes, it gives you the Lc value, tells you which font sizes and weights are appropriate for that combination, and shows you a live preview. That last part — the font-size/weight lookup table — is what makes APCA genuinely different from any WCAG 2.x checker. One more thing — the tool also flags combinations that technically clear Lc 60 but are flagged as risky for specific impairments like low vision or cataracts.

Quick aside: if you're designing a system that spans dozens of color pairs — like a full design token set — checking each pair individually gets tedious fast. That's where Contrast Grid comes in.

Contrast Grid: Checking Your Whole Palette at Once

Contrast Grid (contrast-grid.eightshapes.com) is made by EightShapes. You drop in a set of hex values — your entire color palette — and it renders a matrix showing every possible foreground/background combination with its WCAG 2.x contrast ratio and a pass/fail label. The whole thing is visual: green cells pass AA, yellow pass AA large-only, red fail everything.

In practice, this is the tool you pull up when you're reviewing a design system, not when you're checking a single component. If your color system has 30 tokens — neutrals, brand primaries, semantic colors — you can paste all 30 hex values and immediately see which combinations are safe, which are marginal, and which you should never let anyone use. That visual matrix catches problems that a per-pair checker would take an hour to find manually.

# Example input for Contrast Grid (paste as text in the tool)
# Format: HexValue, Label

#0F172A, neutral-950
#1E293B, neutral-900
#334155, neutral-700
#94A3B8, neutral-400
#F1F5F9, neutral-100
#FAFAFA, neutral-50
#6D28D9, purple-700
#DDD6FE, purple-200
#EF4444, red-500
#FEF2F2, red-50

The limitation is that Contrast Grid only does WCAG 2.x ratios — there's no APCA output. For a lot of teams doing compliance work against existing legal standards, that's completely fine. If you're auditing for ADA compliance right now, WCAG 2.x is still what the law references. Run APCA separately to catch the qualitative issues WCAG misses. That said, there's a community fork in the works that adds APCA columns — watch the GitHub repo if you want it baked in.

Pair this with your gradient generator workflow: whenever you create a new gradient token, drop both endpoint colors into Contrast Grid to confirm any text you overlay on that gradient has at least one safe foreground option at every point along the range.

Whocanuse: Simulating Real User Vision

Whocanuse (whocanuse.com) takes a completely different angle. Instead of a pass/fail number, it shows you a breakdown of *who* can read your color combination — across 8+ types of color blindness, low vision conditions, and situational impairments like bright sunlight or a dim screen. You enter a foreground and background hex, and it renders your text as each of those users would see it, with a count of how many people worldwide that population represents.

Look, this is the tool that actually changes how designers think about contrast. Seeing that 253 million people have low vision, and that your combination is borderline legible for them, lands differently than seeing "contrast ratio: 3.8, fails AA." It's emotional data, which makes it useful in stakeholder conversations where "the number is too low" doesn't move anyone.

It also covers two conditions most other tools ignore: situational low contrast (bright sunlight washing out your screen, typically modeled as about 25% reduction in perceived contrast) and aging vision (contrast sensitivity drops roughly 30% by age 70 compared to a 20-year-old baseline). If your product targets users over 50, those two columns in Whocanuse are often more relevant than any WCAG ratio.

// Programmatic approach: build a contrast audit into your design-token CI
// Uses the wcag-contrast package (WCAG 2.x) as a gate,
// then logs APCA as advisory

import { score } from 'wcag-contrast';
import { APCAcontrast, sRGBtoY } from 'apca-w3';
import tokens from './tokens.json';

const pairs = [
  { fg: tokens['text-primary'], bg: tokens['surface-default'] },
  { fg: tokens['text-muted'],   bg: tokens['surface-raised'] },
];

for (const { fg, bg } of pairs) {
  const wcag = score(fg, bg);
  const apca = APCAcontrast(sRGBtoY(hex2rgb(bg)), sRGBtoY(hex2rgb(fg)));
  if (wcag === 'Fail') {
    console.error(`FAIL ${fg} on ${bg}: WCAG ${wcag}, APCA Lc ${apca.toFixed(1)}`);
    process.exitCode = 1;
  } else {
    console.log(`OK   ${fg} on ${bg}: WCAG ${wcag}, APCA Lc ${apca.toFixed(1)}`);
  }
}

Whocanuse doesn't export data or offer an API — it's purely a visual, browser-based tool. For CI integration, you're still using wcag-contrast or apca-w3 npm packages directly. Use Whocanuse when you're making design decisions; use the packages when you're enforcing them automatically.

How to Use All Three Together (Without Losing Your Mind)

Here's a workflow that actually holds up across a real project. Early in a design sprint, when you're picking your color palette, open Contrast Grid with your brand tokens and flag any combination that fails AA. Cross those off your safe-to-use list. This takes 5 minutes and saves hours of hunting for contrast problems later in the process.

When you're assigning colors to specific UI roles — body text, placeholder text, icon colors, disabled states — switch to an APCA checker. Body text at 14px/400-weight needs Lc 75 minimum. Captions at 12px need Lc 90. That guidance is far more precise than WCAG's blanket 4.5:1 for everything under 18px. You'll find that some combinations you thought were fine (because they passed 2.x) need adjustment, and others you were unnecessarily boosting can actually relax a bit.

Before a major release — or any time you're presenting to stakeholders who care about inclusion — run your key text/background pairs through Whocanuse. Export a screenshot of the vision simulation grid and put it in your design review document. It makes the abstract concrete. If your glassmorphism components use text over blurred backgrounds, this step is especially important because the effective background color varies — run the check against the lightest and darkest possible background state, not just a midpoint.

In CI, gate on WCAG 2.x for now (it's the legal standard), log APCA values as advisory warnings, and fail the build only for outright WCAG failures. As WCAG 3.0 stabilizes, flip the gate to APCA. The npm packages make this straightforward — the code block in the previous section is a near-production-ready starting point.

One more thing — if you're building on top of Empire UI's component library, the style tokens in each theme (including the glassmorphism generator and box shadow generator) have already been validated against WCAG AA. You're not starting from zero.

Picking Token Colors That Pass Automatically

The cleanest approach to contrast compliance isn't checking after the fact — it's building a color scale where specific step pairings always pass. Most design systems that do this well (Radix UI, Adobe Spectrum, Tailwind's default palette) use a 12-step scale where steps 1–4 are backgrounds, steps 5–8 are UI elements and borders, and steps 9–12 are text. Text tokens always pull from the top of the scale, background tokens from the bottom, so the math just works.

In practice, you can generate a WCAG-safe scale by fixing your target contrast ratio and solving backwards from your brand hue. Pick your midpoint brand color, decide the background is neutral-50 (#FAFAFA approximately), and calculate which shade of your hue hits Lc 75 against that background. That shade becomes your text-brand token. Everything anchors to that calculation rather than to aesthetic taste.

/* Example token structure that enforces contrast by convention */
:root {
  /* Backgrounds — always paired with text-* from the bottom of the scale */
  --surface-default:  #FAFAFA; /* neutral-50  */
  --surface-raised:   #F1F5F9; /* neutral-100 */
  --surface-sunken:   #E2E8F0; /* neutral-200 */

  /* Text — always used on surface-* tokens above, pre-validated */
  --text-primary:     #0F172A; /* neutral-950 — Lc 108 on surface-default */
  --text-secondary:   #334155; /* neutral-700 — Lc  88 on surface-default */
  --text-muted:       #64748B; /* neutral-500 — Lc  60 on surface-default, captions only */

  /* Semantic — don't use these on arbitrary backgrounds */
  --text-brand:       #4F46E5; /* indigo-600  — Lc  78 on surface-default */
  --text-danger:      #B91C1C; /* red-700     — Lc  82 on surface-default */
}

This pattern essentially encodes your contrast decisions into naming conventions. A developer who sees var(--text-muted) on var(--surface-raised) knows that combination was pre-validated. A developer who tries to put --text-muted on --surface-sunken is breaking the contract and your linter can catch it. Check out the color system design guide for more on building scales this way.

The APCA Migration Playbook for Existing Projects

If you're on an existing codebase that passes WCAG 2.1 AA and you want to start layering in APCA awareness without breaking compliance, the safest path is additive: keep your WCAG 2.x checks, add APCA logging, and use the APCA output to identify false passes. A false pass is a combination where WCAG 2.x says "AA pass" but the Lc value is below 60 — those are your priority fixes.

Run a sweep with the apca-w3 package over your token pairs and sort the output by Lc ascending. Anything below Lc 45 is genuinely problematic and should be fixed regardless of WCAG status. Lc 45–60 is the gray zone — probably fine for large/bold text, problematic for body copy. Lc 60+ is where you want all body text to live. This sweep usually surfaces 3–6 real issues in a typical design system, not the dozens you might fear.

The hardest part isn't the technical change — it's the design review. Some combinations that fail APCA look perfectly readable to sighted developers on calibrated monitors in dim office lighting. Whocanuse is your best argument tool here: show the simulation for a user with moderate low vision or a cataract condition. That's who you're designing for when you set Lc 75 as your body text floor.

In 2026, tooling support is solid enough that there's no good reason to ignore APCA on new projects. The npm package is stable, Figma plugins exist (A11y Annotation Kit, Contrast, and others), and the browser devtools contrast checker in Chrome 125+ already shows both WCAG and APCA values side by side. Start now on new work, backfill on legacy as you touch components. That's a realistic migration that doesn't require a big-bang audit project nobody has time for.

FAQ

Is APCA replacing WCAG 2.x contrast ratios right now?

Not yet. WCAG 3.0 is still in draft as of September 2026, and legal compliance for ADA and EN 301 549 still references WCAG 2.1 AA. Use APCA to catch quality issues WCAG misses, but don't drop your WCAG 2.x checks until the standard is finalized and adopted by regulators.

What Lc value do I need for body text with APCA?

The community consensus target is Lc 75 for normal-weight body text at 14px–16px. For 12px captions you want Lc 90+, and for 24px+ bold headings Lc 60 is generally sufficient. These aren't yet official WCAG 3.0 thresholds but reflect the best available guidance from the APCA project.

Can I use Contrast Grid for APCA checks?

Not natively — Contrast Grid only outputs WCAG 2.x ratios. There's a community fork in progress, but for APCA you need apcacontrast.com or the apca-w3 npm package. Use Contrast Grid for WCAG compliance sweeps across a full palette and APCA tools for per-pair quality checks.

How is Whocanuse different from other contrast checkers?

Whocanuse shows you who can read your color combination across 8+ vision conditions — color blindness types, low vision, aging eyes, bright sunlight — with real population counts. It's a design empathy tool rather than a pass/fail checker, and it's most useful for stakeholder reviews and catching issues that a single contrast ratio can't surface.

Free components in 40 styles
React & Tailwind, copy-paste ready.
Browse →

Read next

WCAG 2025 Accessibility Guide for React DevelopersWCAG 2.2 Checklist: Every New Criterion Explained With CodeColor Contrast Checker: WCAG AA/AAA and APCA in 2026Neumorphism and Accessibility: The Contrast Problem and How to Fix It