Skip to main content
WCAG Patterns

WCAG 1.4.3 · Level AA · WCAG 2.0

Contrast (Minimum)

Text must have a contrast ratio of at least 4.5:1 against its background. Large text (≥18pt or 14pt bold) may go as low as 3:1.

Principle
Perceivable
Guideline
Distinguishable
Level
AA
Added in
WCAG 2.0

What it really means

Text must have a contrast ratio of at least 4.5:1 against its background. Large text — which WCAG defines as at least 18pt (≈24px) or 14pt bold (≈19px bold) — may go as low as 3:1. Logos and pure decoration are exempt.

This is the single most-cited WCAG violation on the web. WebAIM's annual Million study finds low-contrast text on ~80% of the top one million home pages. Design teams pick greys that look elegant on a pristine 5K screen; users on a cheap laptop in a sunlit café can't read them.

Who it helps

  • Low-vision users (cataracts, macular degeneration, astigmatism).
  • Everyone in bright ambient light — sunlit café, train, outdoor kiosk.
  • Everyone with a cheap or old screen.
  • Everyone on "night mode", which crushes certain palettes.

How to test

  1. Use a contrast checker: WebAIM's Contrast Checker, Colour Contrast Analyser, or Chrome DevTools' built-in picker (click a colour swatch in Styles → Contrast ratio).
  2. Run axe DevTools — the color-contrast rule catches most cases. It can't inspect text-over-image, gradients, or user-generated colours; review those by hand.
  3. Test in the browser's forced-colors mode (Windows High Contrast or Firefox Contrast). Colours defined with CSS system colors survive; hard-coded hex values may invert poorly.

What counts as "large text"

  • At least 18pt (24 CSS pixels) at any weight.
  • Or at least 14pt (18.66 CSS pixels) if the font weight is 700+ (bold).

Everything else is "normal text" and must hit 4.5:1.

A failing pattern

/* Light grey on white — 3.1:1. Fails at any size. */
.muted-text {
  color: #999;
  background: #fff;
}
 
/* Brand blue on brand white — 3.9:1 at 16px. Fails body text. */
.cta {
  color: #2563eb; /* blue-600 */
  background: #ffffff;
}
 
/* Dark mode overlay where muted turned illegible. */
.dark .hint { color: #475569; background: #0f172a; } /* 2.8:1 — fails */

A passing pattern

/* Slate-700 on white — 8.6:1. AAA at any size. */
.body-text {
  color: #334155;
  background: #ffffff;
}
 
/* Brand cyan-800 — 8.0:1 on #fafaf7. AAA normal text. */
.brand {
  color: #155e75;
  background: #fafaf7;
}
 
/* Paired with an icon, not just colour — belt and braces. */
.error {
  color: #991b1b; /* 8.1:1 on white — AAA */
  background: #ffffff;
}

The strictest shade-picking strategy: pick your body colour first by contrast target, then design the palette around it. Tailwind's 700–900 range on white (slate-700, zinc-800, neutral-900) all clear AAA.

Notes and edge cases

  • Placeholder text is text. ::placeholder { color: #d1d5db } fails at 4.5:1 — bump it to #6b7280 or darker.
  • Disabled controls are commonly exempt from contrast requirements per the WCAG "inactive UI component" exception, but users still need to read them. Shoot for at least 3:1 even when disabled.
  • Text over images must meet contrast against the effective background. Use a scrim or gradient overlay if the image is busy.