# WCAG Contrast Ratios: What 4.5:1 Actually Means

"Your text needs a contrast ratio of at least 4.5:1." You've seen this in an audit, a Lighthouse report, or a design review. But what *is* that ratio measuring, where do the magic numbers come from, and why does the threshold change for big text? Here's the whole model.

## Contrast is a ratio of luminance, not color

The WCAG contrast ratio compares the **relative luminance** of two colors — the foreground (text) and the background. Luminance is roughly "how much light the color emits," computed from its RGB channels with a perceptual weighting (green counts more than red, red more than blue, because that's how human vision works).

The ratio is defined as:

```
(L_lighter + 0.05) / (L_darker + 0.05)
```

where `L` is relative luminance from 0 (black) to 1 (white). That `+ 0.05` accounts for ambient screen glare. The result ranges from **1:1** (identical colors, invisible) to **21:1** (pure black on pure white).

A crucial consequence: contrast depends on **lightness, not hue**. Two vivid, very different colors — say a saturated red and a saturated green — can have *terrible* contrast if they're similarly light. This is why "it looks colorful and distinct to me" is not a substitute for measuring.

## The thresholds

WCAG defines two conformance levels, and the bar depends on text size:

| | Normal text | Large text |
|---|---|---|
| **AA** (the common legal target) | 4.5:1 | 3:1 |
| **AAA** (enhanced) | 7:1 | 4.5:1 |

"**Large text**" means **18pt (≈24px) or larger**, *or* **14pt (≈18.66px) bold or larger**. The reasoning: bigger, heavier letterforms have more pixels and thicker strokes, so they stay legible at lower contrast. That's the entire basis for the relaxed 3:1 threshold.

Non-text elements — icons, input borders, focus indicators, the meaningful parts of a chart — have their own requirement: **3:1** against adjacent colors (WCAG 1.4.11). A button people can't find because its border is too faint fails accessibility just like unreadable text does.

## Common ways people fail it

- **Gray-on-gray placeholder text.** `#999` on white is only ~2.8:1 — it fails AA for normal text. Light gray "secondary" text is the single most common violation.
- **Brand colors used as text.** A mid-tone brand blue or green often clears 3:1 (fine for a large heading or an icon) but misses 4.5:1 for body copy.
- **Text over images or gradients.** Contrast varies pixel by pixel; the worst spot is what counts. Add a scrim or text shadow.
- **Disabled controls** are exempt from the contrast requirement — but don't use "disabled" styling for text that's meant to be read.

## Hitting the target

You usually fix contrast by adjusting **lightness**, not by abandoning your hue. Darken the text (or lighten the background) in small steps and re-measure — often a 10–15% lightness shift takes you from failing to passing without changing the color's character. Working in HSL makes this easy because you're moving one axis.

The [color & contrast tool](/tools/design/color-tool) computes the exact ratio for any foreground/background pair and tells you which levels (AA, AAA, large-text) you pass, so you can nudge a value until it clears the bar. If you're adjusting lightness by hand, a [HEX ↔ HSL converter](/convert/hex-to-hsl) lets you change the `L` channel directly and convert back.

## Takeaways

- Contrast is a **luminance** ratio from 1:1 to 21:1 — driven by lightness, not hue.
- **AA = 4.5:1** for normal text, **3:1** for large text; **AAA = 7:1 / 4.5:1**.
- "Large" = 24px regular or ~18.66px bold.
- UI components and focus indicators need **3:1**.
- Fix failures by adjusting lightness and re-measuring — never eyeball it.

If you remember one number, remember **4.5:1** for body text. Everything else is a variation on it.
