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

Color Contrast Checker: WCAG AA/AAA and APCA in 2026

Color contrast in 2026 means more than WCAG 4.5:1. Learn how APCA changes the math, when AA vs AAA matters, and how to check contrast fast without losing sleep.

Colorful gradient palette swatches illustrating color contrast and accessibility testing

Why Color Contrast Still Trips Up Good Developers

Color contrast isn't glamorous. Nobody writes tweets about it. But it's one of those things that, if you get it wrong, real people can't read your UI — and in some jurisdictions, you're also looking at legal exposure. So let's be blunt about where things stand in 2026.

WCAG 2.1 has been the law of the land since 2018, and for normal body text it demands a minimum contrast ratio of 4.5:1 against the background (AA level). Large text — 18px regular or 14px bold — gets a pass at 3:1. Most devs know this. What fewer devs know is that WCAG 2.2 shipped in 2023 and didn't change those numbers, and the draft WCAG 3.0 spec is replacing the entire ratio model with something called APCA.

Honestly, the transition has been messy. You've got teams who've never heard of APCA, teams who've panic-switched to it, and a middle ground of people confused about which standard actually applies right now. The answer: WCAG AA (4.5:1) is still the legally enforceable baseline in most countries. APCA is the future, but it's not law yet.

That said, understanding APCA now gives you a real edge — it produces more readable interfaces and catches false positives that WCAG's older math misses. We'll get into both. Properly.

WCAG AA vs AAA: What the Numbers Actually Mean

AA at 4.5:1 is the floor. AAA at 7:1 is the ceiling for normal text (and 4.5:1 for large text). In practice, hitting AAA everywhere is genuinely hard — especially if your brand palette leans dark on dark or involves saturated midtones. Most production apps aim for AA with AAA where it's achievable without torturing the design.

Here's a quick mental model: 4.5:1 is the point where most people with moderately reduced contrast sensitivity can still read comfortably. 7:1 covers people with more severe impairments and low-quality displays. Neither ratio accounts for font rendering, antialiasing, or the visual complexity of your background — which is exactly what APCA tries to fix.

Worth noting: the 4.5:1 rule applies to text rendered at roughly 16px with normal font weight. Drop to 12px or use a thin 300-weight font, and even a 5:1 ratio can feel unreadable. The spec doesn't say that loudly enough. You'll still fail users even while technically passing.

One more thing — UI components and icons have their own threshold: 3:1 against adjacent color. Buttons, form outlines, focus rings. A lot of audit failures come from here, not body copy.

APCA: What It Is and Why It Matters

APCA — Advanced Perceptual Contrast Algorithm — was developed by Andrew Somers starting around 2019 as the contrast engine for WCAG 3.0. Instead of a single ratio, it produces a signed "lightness contrast" value, usually written as Lc (e.g., Lc 75). The sign tells you which direction the contrast runs: positive means light text on dark, negative means dark text on light.

The core insight is that human vision is not symmetrical. A dark gray on white feels different to your eyes than white on dark gray at the same mathematical ratio. WCAG 2.x treats them as identical. APCA doesn't. It also models the spatial frequency of text — meaning font size and weight are first-class inputs to the algorithm, not bolted-on afterthoughts like in WCAG.

Here's what that looks like in practice. Take #767676 on white (#FFFFFF). WCAG ratio: exactly 4.54:1 — a hair over the AA threshold, technically passing. APCA Lc value: around Lc 54, which APCA considers insufficient for body text at 16px. WCAG says pass; APCA says no. That discrepancy isn't a bug. APCA is catching something real about how that particular color pairing actually reads.

Look, APCA isn't a drop-in replacement for WCAG yet. The W3C hasn't ratified WCAG 3.0, no government has legislated it, and the API for checking APCA compliance still changed in 2024. But using it as a second opinion alongside your WCAG check will catch real readability problems that the old math misses. Worth adding to your toolchain now, not later.

Quick aside: if you're working with glassmorphism components or frosted-glass effects, APCA becomes especially important. Frosted backgrounds are not a single flat color, so static contrast ratios are misleading anyway — but APCA's perceptual model at least gives you a more honest baseline than WCAG does for those UI patterns.

How to Check Contrast Without Losing Your Mind

The fastest in-browser approach is the DevTools accessibility pane. Chrome 120+ and Firefox 122+ both surface WCAG ratios inline when you inspect a text node. Flip to the "Accessibility" tab, hover a text element, and you'll see the computed contrast ratio right there. Not perfect — it doesn't catch APCA or dynamic backgrounds — but it's zero-setup and catches obvious failures immediately.

For automated pipeline checking, axe-core is the standard. It runs in Jest, Playwright, and Storybook. Here's the minimal setup in a Playwright test: ``ts import { checkA11y } from 'axe-playwright'; test('homepage passes contrast checks', async ({ page }) => { await page.goto('http://localhost:3000'); await checkA11y(page, undefined, { runOnly: ['color-contrast'], }); }); ` That will catch WCAG AA failures on every text node. It won't catch APCA — for that, you'll need @adobe/leonardo-contrast-colors or the apca-w3` npm package directly.

For one-off color decisions during design, the APCA contrast calculator at www.myndex.com/APCA/ is the reference implementation. Drop in your hex values and you get Lc, plus a lookup table showing which font sizes and weights are appropriate for that Lc score. That lookup table is where APCA gets genuinely useful: instead of a binary pass/fail, it tells you "use at least 18px regular or 14px bold at this contrast level."

In your CSS, if you're building a design system, commit your contrast decisions to CSS custom properties. Something like --color-text-body always on --color-bg-surface, checked once in your token layer. Don't scatter raw hex values across components and try to track contrast manually — you will miss something.

Building a Contrast-Safe Color System

The right time to think about contrast is when you're defining your palette, not when you're shipping. Most color systems fail at the mid-range: the 400–600 range in a typical 50–900 scale often clusters in the 3:1 to 4:1 danger zone, too dark to use on white, too light to use on dark. Choosing your palette with contrast in mind means deliberately leaving gaps there.

If you're working with a custom palette from our gradient generator, pull out any midtone stops and immediately check them against your primary background. A stop that sits at, say, #6C6C6C on white gives you 4.48:1 — which is a WCAG AA fail by a hair. Either darken it to #757575 (4.6:1) or decide not to use it as body text color. Make that call at token time.

Honestly, the cleanest system is one where every text token and every background token have a documented, tested contrast pair. You define --text-primary: #1A1A1A and --bg-default: #FFFFFF, and somewhere in your CI you have a test that asserts those two values together hit at least 4.5:1. When someone updates a token, the test fails before it reaches production. Simple, but almost nobody does it.

For component-level work, don't forget interactive states. The hover color on a button, the placeholder text in a form input, the disabled state label — all of them need to meet contrast requirements. Disabled text has an exemption in WCAG 2.x (it's explicitly excluded from the 1.4.3 criterion), but placeholder text does not. That one surprises a lot of people.

One more thing — dark mode doesn't automatically mean better contrast. A white-on-dark scheme with #FFFFFF on #121212 gives you 19.1:1, which is technically AAA, but subjectively that much contrast in a dark environment can cause halation — the light from text bleeds into adjacent pixels on OLED displays. In practice, capping body text at around #E0E0E0 on #121212 gives you 14.7:1 and reads more comfortably at night. APCA handles this nuance; WCAG doesn't.

Testing Gradient and Frosted Backgrounds

Static contrast ratios assume a solid background. Half the interesting UI you're building probably doesn't have one. Gradients, blurs, image overlays, the translucent cards you get from glassmorphism — all of these require a different approach.

The WCAG standard says you should measure against the "lightest or darkest" background color a user might encounter. For a horizontal gradient from white to #333333, that means testing against white. If your text passes at 4.5:1 on white, it probably passes everywhere along the gradient — but you should verify the midpoint isn't a problem zone. The W3C has a non-normative technique (G17) that covers this.

For frosted glass or backdrop-filter scenarios, there's no perfect answer. The background color seen through the blur depends on what's scrolled behind the panel. The practical approach is to measure against the lightest background content that could appear there, enforce a minimum opacity on your blur layer (something like background: rgba(255,255,255,0.7) as a floor), and add a visible border or shadow to help distinguish the panel edge without relying on contrast alone.

Automated tools won't catch these dynamic cases. You need manual review or screenshot-based visual regression testing with Playwright against known background states. It's imperfect, but it's honest — and it's still better than shipping a dark text node on a gradient and hoping nobody notices.

Quick Reference: Contrast Numbers to Know

Here's the cheat sheet you can paste into your team's Notion: `` WCAG 2.x Contrast Ratios ──────────────────────── AA normal text (< 18px regular / < 14px bold): 4.5:1 minimum AA large text (≥ 18px regular / ≥ 14px bold): 3.0:1 minimum AA UI components & graphical objects: 3.0:1 minimum AAA normal text: 7.0:1 AAA large text: 4.5:1 APCA Lc Thresholds (approximate, body text at 16px) ──────────────────────────────────────────────────── Lc 90+ Preferred for fluent reading (books, long-form) Lc 75 Minimum for body text at 16px / 400 weight Lc 60 Subheadings, 24px+ Lc 45 Large display text, 36px+ Lc 30 Non-text, decorative, disabled ``

A few things in that table that trip people up. The WCAG 3:1 for UI components applies to the component outline or boundary, not to text inside the component — that still needs 4.5:1. And the APCA Lc values above are for dark-on-light (positive Lc). Light-on-dark uses negative Lc values with similar but not identical thresholds.

If you're maintaining a component library — or evaluating one from Empire UI — running a quick accessibility audit on your Storybook stories with axe-playwright takes about 20 minutes to set up and catches the obvious failures automatically on every PR. That's the baseline. Layer in APCA tooling when you're refining a token palette, or when something passes WCAG but visually feels wrong — because sometimes it is.

Color contrast is one of those things that separates a component library that actually works for everyone from one that only works for people with perfect vision on a calibrated display. It's not hard to get right. It just requires being deliberate about it from the start.

FAQ

What's the difference between WCAG AA and WCAG AAA?

AA requires 4.5:1 contrast for normal text and 3:1 for large text — it's the legal baseline. AAA requires 7:1 for normal text and is more of a quality target than a hard requirement.

Is APCA replacing WCAG 2.x right now?

Not yet. APCA is the contrast algorithm planned for WCAG 3.0, which hasn't been ratified. Use WCAG 4.5:1 for compliance, APCA as a second sanity check.

Do placeholder text and disabled inputs need to meet contrast requirements?

Placeholder text does — it has no WCAG exemption and must hit 4.5:1. Disabled input text is explicitly exempted from WCAG 1.4.3, so it's technically fine to let it fail.

How do I check contrast on a gradient or frosted-glass background?

Test against the lightest background value the text could appear over. For frosted glass, enforce a minimum opacity floor on the blur layer and supplement with a visible border so the panel boundary doesn't rely on contrast alone.

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

Read next

Web Accessibility Audit Tools: axe, WAVE, Lighthouse, ManualCSS clip-path Generator: Polygon, Circle and Inset ShapesColor Contrast Tools in 2026: APCA, Contrast Grid, WhocanuseNeumorphism and Accessibility: The Contrast Problem and How to Fix It