The Complete Guide to WCAG Color Contrast (AA, AAA & APCA)

ScreenTools TeamJune 24, 202610 min read
wcag contrastcolor contrast checkeraccessibility colorcolor contrast ratiowcag aa aaa

Accessibility is the easiest thing to claim and the hardest thing to actually deliver, and color contrast is where the gap shows first. A brand chooses a primary color because it looks good in the comp; a designer uses it for a CTA button; an engineer ships it; a Lighthouse audit four months later says "fails WCAG AA" and now everyone has to revisit a decision that was already approved. This guide explains where the WCAG contrast numbers come from, what they actually require, which numbers to optimize for (the answer isn't always "the strictest one"), and the simplest workflow for fixing a failing color without rebuilding your design system. The Color Contrast Checker on ScreenTools implements the math.

What WCAG actually says

The Web Content Accessibility Guidelines define a single contrast metric — the ratio between the relative luminance of two colors — and then set four threshold pairs across two compliance levels.

The metric:

ratio = (L1 + 0.05) / (L2 + 0.05)

where L1 is the lighter of the two colors' relative luminances and L2 is the darker. Relative luminance itself is computed by linearizing each of the R, G, B channels (a piecewise function — small values divide by 12.92, larger ones run through a gamma of 2.4) and then taking a weighted sum (0.2126 R + 0.7152 G + 0.0722 B, the standard ITU-R BT.709 weights matching human eye sensitivity).

The four threshold pairs:

Compliance Normal text Large text UI components
WCAG 2.x AA (legal minimum) 4.5 : 1 3 : 1 3 : 1
WCAG 2.x AAA (enhanced) 7 : 1 4.5 : 1 3 : 1

"Large text" is at least 18 point regular weight or at least 14 point bold (about 24px regular / 18.5px bold at 96 DPI). The 3:1 UI threshold added in WCAG 2.1 (2018) covers button outlines, icon edges, focus rings, and other non-text components that need to be visible.

That's the entire spec. The math is fixed; the thresholds are fixed; only the question of "which colors to actually use" is up to you.

Why the 4.5:1 number

The 4.5:1 minimum isn't an opinion — it's calibrated to keep text readable for someone with 20/40 vision (the threshold where many countries require corrective lenses to drive), accounting for typical screen brightness and ambient light. The 7:1 AAA bumps it to 20/80 vision — readers who would qualify as legally blind in some jurisdictions. The 3:1 large-text and UI threshold acknowledges that bigger features and bigger text are easier to see at lower contrast.

Translation: AA is the threshold below which a significant fraction of your users literally cannot read your text. It's not a "nice to have." Hitting AA is the floor for not-actively-excluding-users, not a gold star.

What "fails AA" means in practice

Take the most-cited example: tailwind blue-500 (#3b82f6) on white. Pop it into a contrast checker; the answer is 3.68:1. That's below the 4.5 normal-text threshold by a meaningful margin. Use it for a body paragraph and Lighthouse will flag every paragraph; use it for an h1 (large text, 3:1 threshold) and it passes.

The systemic problem: design teams pick a brand color based on aesthetic + emotional fit + competitive differentiation, then engineers apply it everywhere — small text, large text, button labels, link hover states. The brand color was never tested against the accessibility math; the four downstream uses inherit the failure.

The same color used in the right places is fine. Use blue-500 for headlines and call-to-action buttons (large bold text, where 3:1 applies); use blue-600 or blue-700 for the in-line links and small labels where 4.5:1 is required. The brand integrity stays intact, the audit passes.

The accessibility-vs-brand tension

Designers push back on contrast requirements because the colors that meet AA on white tend toward the "darker" end of the palette. A vibrant teal might pass at 3.5:1 (large text) but fail at 4.5:1; a slightly darker teal passes both. The vibrant one looks better in isolation. The darker one is the one that's actually readable.

Three resolutions that don't require giving up the brand:

1. Pair light backgrounds with darker text variants. Brand color stays the brand color in marketing material; in the product UI, use the brand-color-100 or brand-color-700 variants for actual text-on-bg. Both Tailwind and Open Props color scales include these variants by design.

2. Use the brand color for surfaces, not text. A brand-color button with white text on it can hit AAA easily if the brand color is dark enough; let the brand show through the surface, not through the text.

3. Provide a "high contrast" theme toggle for the small percentage of users who need it. Many design systems ship a "max contrast" theme alongside the default; the default uses brand colors at their natural vibrancy, the alternate uses WCAG-AAA-compliant variants.

The wrong fix is reducing accessibility coverage to keep one specific color. The right fix is admitting one color can't carry every usage.

When to aim for AAA

AAA is not the default goal for most products. It's roughly twice as restrictive as AA and it tends to push designs toward muted, low-saturation palettes. Aim for AAA when:

  • Your product directly serves users with low vision (vision-impairment apps, screen readers, magnification tools)
  • Your product is the only way to access a service (government, medical, education) where "I can't read it" means "I can't use it"
  • Your brand explicitly commits to maximum-accessibility as a product value

For consumer apps, marketing sites, and most B2B SaaS, AA-everywhere is the right floor and AAA where it doesn't compromise the design.

How to find passing colors fast

The fastest workflow for fixing a failing pair:

  1. Identify the failure. Run the contrast checker on the current pair; note which thresholds fail.
  2. Hold one color constant. Usually the background is the "real" constraint (it's set by the page template). Vary the text color.
  3. Move along the lightness axis. Open the HEX Converter or any HSL editor and adjust just the L value. Darken the text until the contrast rises above the threshold.
  4. Verify in context. Eyeballing the swatch is useful; rendering actual text against actual background (the checker's preview block does this) is more useful.

The reason to vary along lightness specifically: hue and saturation barely affect WCAG contrast (luminance weights are 0.21 R + 0.72 G + 0.07 B regardless of saturation). Lightness is the only knob that meaningfully moves the ratio.

What contrast doesn't measure

WCAG contrast is one piece of a larger accessibility picture. It tells you nothing about:

  • Color blindness. Two colors with 7:1 contrast can still be indistinguishable for someone with protanopia or deuteranopia. Pair the checker with a color-blindness simulator (Coblis, Stark, or Chrome DevTools' rendering panel).
  • Text size and weight in absolute terms. WCAG's "large" threshold is 18pt regular; that's still small for many low-vision users. Real readability is a function of contrast × size × line-spacing × font weight × user's zoom level.
  • Information conveyed by color alone. Even with perfect contrast, conveying state purely through color (red error / green success) excludes color-blind users. Add an icon or text label.
  • Ambient lighting. A 4.5:1 contrast that works in a dark room can fail in direct sunlight on a phone. WCAG assumes a typical indoor environment.
  • Print, projection, or print-on-screen. WCAG is calibrated for screen rendering. Printed material has different reflectance, projection has wildly different ambient interaction.

WCAG contrast is a necessary minimum, not a sufficient guarantee.

APCA: the next-generation algorithm

You may have heard of APCA (Advanced Perceptual Contrast Algorithm), proposed for WCAG 3 to replace the current ratio formula. APCA is more perceptually accurate — it accounts for text weight, polarity (light-on-dark vs dark-on-light), and font size in a way the simple ratio doesn't. Under APCA, "passing" is a single number (between -108 and +106) interpreted against a context-dependent threshold.

APCA is the future, but it's not the present:

  • WCAG 3 is still in draft and not legally enforceable anywhere
  • Legal accessibility compliance (ADA, EAA, AODA, Section 508) all currently reference WCAG 2.x
  • Automated tools (Lighthouse, axe, Pa11y) use WCAG 2.x as the default

Use WCAG 2.x today for any audit, regulatory, or contractual purpose. Use APCA as a complementary "more accurate" check when designing — if a pair passes APCA but fails WCAG 2.x, you have a defensible argument that the perceptual reality is better than the math says, but it doesn't help in an audit.

Common surprises

Some colors that pass or fail in counterintuitive ways:

  • Tailwind gray-500 (#6b7280) on white passes AA normal — barely, at 4.83:1. Most designers assume gray-500 fails because it looks washed out; the math disagrees.
  • Light orange / yellow on white fails badly. Pure yellow on white is 1.07:1 — essentially invisible. Yellow is high-luminance regardless of saturation, so it almost never has enough darkness to contrast with white.
  • Tailwind red-500 (#ef4444) on white = 3.76:1 — fails AA normal. The red that looks "vibrant" in mockups is also the red that fails for body text.
  • Dark blue on white tends to pass everywhere. blue-700 / blue-800 / blue-900 all pass AA easily; this is why so many sites converge on dark blue for links.
  • Dark mode is not automatically more accessible. White text on a near-black background tends to pass everything, but white text on a dark-blue background can fail AAA at small sizes.

The single way to know is to check. The math doesn't care what the color looks like.

FAQ

Which thresholds do legal regulations actually require? US ADA Title II / III and federal Section 508 cite WCAG 2.x AA. The EU EAA, UK Equality Act 2010, and Canadian AODA all reference WCAG 2.x AA. AAA is rarely required by law; AA is the legal floor in essentially all major jurisdictions as of 2026.

Does dark mode count as a separate accessibility theme? No. WCAG requires both light and dark modes (if you ship both) to meet the threshold independently. A button that passes on the light theme but fails on the dark theme is still a failing button.

What about text on images or gradients? You need a guaranteed contrast at every point of the image / gradient. The standard approach is a semi-transparent dark overlay between image and text, sized to bring the worst-case contrast above the threshold.

Do icon-only buttons need a label? Per WCAG 4.1.2 Name, Role, Value: yes, they need an aria-label or other accessible name. The 3:1 contrast on the icon itself is necessary but not sufficient.

What about text-shadow and outlines on text? WCAG doesn't account for shadows / outlines in the contrast math. A text-shadow: 0 0 4px black on light-blue-on-white text might be more readable to a sighted user but doesn't change the WCAG ratio. Some checkers (including ours) measure the literal foreground / background colors.

Is the Color Picker or HEX Converter the right tool to fix a failing pair? Use the contrast checker to identify the failure; use the HEX Converter or HSL inputs to adjust lightness until the ratio rises above the threshold; bookmark the ?fg=&bg= URL to share with your team.

Related Articles