Skip to main content

Color Contrast and WCAG: How to Pass AA and AAA

A practical guide to text and background color contrast: WCAG AA needs 4.5:1 for normal text and 3:1 for large text. Worked examples, AAA, and how to fix failing pairs.

Published By Li Lei
#accessibility #wcag #color contrast #web design #a11y

Color Contrast and WCAG: How to Pass AA and AAA

Most failed accessibility audits I have seen start with the same boring problem: gray text on a white card that looks fine on the designer's calibrated monitor and disappears on a cheap phone in daylight. Contrast is the cheapest accessibility win you can buy, and it is also the one teams most often get wrong because the rules sound simple but hide a few traps. This guide walks through the exact thresholds, what counts as "large" text, a worked example, and how to repair a pair that fails.

The two numbers that matter: 4.5:1 and 3:1

WCAG 2.2 expresses contrast as a ratio between the luminance of two colors, running from 1:1 (a color against itself, invisible) to 21:1 (pure black on pure white). The two thresholds you have to memorize live under Success Criterion 1.4.3 (Contrast Minimum), which is Level AA:

  • Normal text needs at least 4.5:1.
  • Large text needs at least 3:1.

That is the whole AA story for text. If your body copy clears 4.5:1 and your big headings clear 3:1, you have passed the criterion that the overwhelming majority of contracts, lawsuits, and audit reports actually cite. There is a third AA rule worth knowing, 1.4.11 (Non-text Contrast), which asks for 3:1 between meaningful UI parts — button borders, form-field outlines, focus rings — and whatever sits behind them. Text gets all the attention, but a 1px focus ring that nobody can see is a real failure too.

What actually counts as "large text"

The 3:1 bar is generous, so people abuse it. Large text has a precise definition: at least 18 point in a regular weight (roughly 24px in a typical browser), or at least 14 point when bold (roughly 18.66px at font-weight 700). Thicker strokes carry more ink, so the eye tolerates lower contrast on them. That is the entire justification for the relaxed threshold.

The trap is borrowing the large-text bar for text that is not large. A 15px caption sitting next to a giant headline is still normal text. It must hit 4.5:1, not 3:1. A muted gray like #94A3B8 on white reads about 2.8:1, which means it fails AA outright even when the H1 a few pixels above it passes comfortably. Do not let one big number on the page launder the small text around it.

AAA: when you want belt and braces

Level AAA tightens both bars. SC 1.4.6 (Contrast Enhanced) requires 7:1 for normal text and 4.5:1 for large text. You rarely need to certify a whole product at AAA, but it is a sensible target for long-form reading, legal disclosures, or anything a low-vision user will sit with for minutes. A handy mental model: AAA normal text (7:1) and AA large text (3:1) bracket the same scale, with AA normal (4.5:1) and AAA large (4.5:1) sharing the middle. Hit 7:1 everywhere and you have a comfortable buffer against cheap screens and bright rooms.

A worked example: where #767676 lands

Take a common "secondary text" gray, #767676, on a white #FFFFFF background. Run it through the algorithm:

  1. Convert each sRGB channel to linear light. For #767676 every channel is 118/255 ≈ 0.463, which maps to about 0.179 linear.
  2. Compute relative luminance: L = 0.2126·R + 0.7152·G + 0.0722·B ≈ 0.179 for the gray, and 1.0 for white.
  3. Ratio = (L_lighter + 0.05) / (L_darker + 0.05) = 1.05 / 0.229 ≈ 4.54:1.

So #767676 on white squeaks past AA for normal text — it is the darkest "pure gray" that still passes 4.5:1, which is exactly why you see it everywhere. Now nudge it one step lighter to #808080 and the ratio drops to about 3.95:1: it fails normal text but still clears the 3:1 large-text bar. That single hex step is the difference between a passing caption and an audit finding. You can verify any pair like this in seconds with the color contrast checker instead of doing the luminance math by hand.

Fixing a pair that fails

When a pair comes up short, resist the urge to redesign. The fastest repair keeps your hue and saturation and only walks the lightness. Pick the pole — black or white — on the far side of the background's luminance, and step the foreground toward it until the ratio crosses your target. Against a white background you darken the text; against a dark panel you lighten it. Because only lightness moves, your brand blue stays recognizably blue and your accent red stays red.

Here is how it plays out in practice. I had a brand teal focus ring, #14B8A6, on white form fields that read only 2.4:1 — it failed the 3:1 non-text rule that keyboard users depend on. Darkening it one notch to #0D9488 pushed it to about 3.4:1 and left the rest of the component untouched. Two minutes, one hex value, and the audit line cleared.

Contrast is necessary, not sufficient

Passing 4.5:1 on every pair removes one of the most common audit failures, but it does not make a design accessible on its own. If meaning rides on hue alone — red for error, green for success — a color-blind user still cannot read it, no matter how high the contrast. Pair color with an icon, a label, or a position cue (that is SC 1.4.1). To see how your palette holds up for the roughly one in twelve men with a color vision deficiency, run it through the color blindness simulator after you have won the contrast battle. And remember the other failure modes contrast cannot catch: 11px text that is technically 7:1 is still hard to read, and a 1px focus ring at 3:1 still gets missed.

Contrast is the part of accessibility you can verify objectively, fix mechanically, and ship the same afternoon. Get the 4.5:1 and 3:1 thresholds into muscle memory, stop downgrading body text to the large-text bar, and treat the checker as the last gate before any color leaves your design system.


Made by Toolora · Updated 2026-06-13