Accessibility

WCAG Contrast Checker Guide: AA/AAA Requirements and How to Fix Failures

Learn WCAG 2.1 contrast requirements, how to calculate contrast ratios, common failures, and practical fixes for text, UI components, and entire color palettes.

8 min read

Accessibility symbol on keyboard

Color contrast is the most common accessibility failure on the web. According to WebAIM's annual accessibility analysis, low contrast affects over 80% of home pages. Yet it's also one of the easiest issues to fix — if you know the rules and have the right tools. This guide explains WCAG contrast requirements, how contrast ratios work, and how to fix failures without redesigning your entire palette.

What is color contrast and why does it matter?

Color contrast is the difference in luminance (perceived brightness) between text and its background. High contrast makes text readable for everyone — including users with low vision, color blindness, or viewing screens in bright sunlight.

The Web Content Accessibility Guidelines (WCAG) 2.1 define minimum contrast ratios to ensure readability. Meeting these standards isn't just good practice — it's legally required under the Americans with Disabilities Act (ADA), Section 508, and similar laws worldwide.

WCAG contrast requirements

WCAG defines two conformance levels for contrast:

Level AA (minimum standard)

Normal text (under 18pt or under 14pt bold):

  • Minimum contrast ratio: 4.5:1

Large text (18pt+ or 14pt+ bold):

  • Minimum contrast ratio: 3:1

UI components and graphical objects:

  • Minimum contrast ratio: 3:1 (WCAG 2.1 addition)

Level AAA (enhanced standard)

Normal text:

  • Minimum contrast ratio: 7:1

Large text:

  • Minimum contrast ratio: 4.5:1

Most organizations target Level AA as the baseline. Level AAA is recommended for text-heavy applications (documentation, legal sites, educational platforms) and products serving users with visual impairments.

How contrast ratios are calculated

Contrast ratio is the relative luminance of the lighter color divided by the relative luminance of the darker color, expressed as a ratio from 1:1 (no contrast) to 21:1 (black on white).

The formula (from WCAG 2.1):

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

Where:

  • L1 = relative luminance of the lighter color
  • L2 = relative luminance of the darker color
  • Luminance is calculated from sRGB values using a complex formula accounting for human perception

You don't need to calculate this by hand. Use our WCAG Contrast Checker to test multiple color pairs instantly.

Examples

Foreground Background Ratio AA Normal AA Large AAA Normal
#000000 #FFFFFF 21:1 ✅ Pass ✅ Pass ✅ Pass
#767676 #FFFFFF 4.54:1 ✅ Pass ✅ Pass ❌ Fail
#959595 #FFFFFF 3.01:1 ❌ Fail ✅ Pass ❌ Fail
#3B82F6 #FFFFFF 3.15:1 ❌ Fail ✅ Pass ❌ Fail
#1E40AF #FFFFFF 7.04:1 ✅ Pass ✅ Pass ✅ Pass

Notice that many popular brand colors (like #3B82F6, Tailwind's blue-500) fail AA for normal text on white backgrounds.

Common contrast failures and how to fix them

1. Light gray text on white backgrounds

Failure:

color: #999999;           /* Gray-400 */
background: #FFFFFF;      /* White */
/* Ratio: 2.85:1 — Fails AA */

Fix: Darken the text to at least #767676 (4.54:1) for AA or #595959 (7.0:1) for AAA.

color: #767676;           /* AA compliant */
background: #FFFFFF;

2. Brand colors on white

Many vibrant brand colors fail contrast when used for text.

Failure:

color: #3B82F6;           /* Tailwind blue-500 */
background: #FFFFFF;
/* Ratio: 3.15:1 — Fails AA normal text */

Fixes:

Option A: Use the brand color for large text only (headings, buttons).

Option B: Darken the brand color for body text:

color: #1E40AF;           /* Tailwind blue-700 */
background: #FFFFFF;
/* Ratio: 7.04:1 — Passes AAA */

Option C: Use the brand color as a background with white text:

color: #FFFFFF;
background: #3B82F6;
/* Ratio: 3.15:1 — Passes AA large text */

3. Placeholder text

Placeholder text in form inputs often uses very light gray (#A0A0A0 or lighter) and fails contrast.

Failure:

::placeholder {
  color: #A0A0A0;         /* Ratio: 2.32:1 — Fails */
}

Fix: WCAG does not require placeholders to meet contrast minimums (they're considered "incidental" text), but best practice is to meet AA anyway:

::placeholder {
  color: #767676;         /* Ratio: 4.54:1 — Passes AA */
}

Links must meet contrast requirements against their background AND be distinguishable from surrounding text without relying on color alone (underline, bold, icon, etc.).

Failure:

a {
  color: #60A5FA;         /* Light blue */
  text-decoration: none;
}
/* Ratio vs white: 2.13:1 — Fails AA */

Fix:

a {
  color: #2563EB;         /* Darker blue, 5.14:1 */
  text-decoration: underline;
}

5. Disabled buttons

Disabled UI components are exempt from WCAG contrast requirements (they're "inactive"), but making them too light creates confusion.

Recommendation: Aim for at least 3:1 even for disabled states.

button:disabled {
  color: #9CA3AF;         /* Gray-400 */
  background: #F3F4F6;    /* Gray-100 */
  /* Ratio: 2.1:1 — Technically exempt, but consider 3:1+ */
}

Testing entire color palettes

If you're building a design system or choosing a palette, test all combinations at once with our Batch WCAG Contrast Checker.

Example workflow:

  1. Paste your palette (foreground colors in one column, background colors in another)
  2. Get a pass/fail matrix showing every combination
  3. Identify which pairs are safe for normal text, large text, or fail entirely
  4. Adjust lightness values until you have enough compliant pairs

Building a contrast-safe palette

Start with a base hue and generate shades at different lightness levels. Test each shade against your backgrounds.

Example: Blue palette for white background

hsl(220, 90%, 90%)  →  1.35:1  ❌ Decorative only
hsl(220, 90%, 70%)  →  2.44:1  ❌ Fails
hsl(220, 90%, 50%)  →  3.52:1  ⚠️  Large text only
hsl(220, 90%, 40%)  →  5.14:1  ✅ AA normal text
hsl(220, 90%, 30%)  →  7.89:1  ✅ AAA normal text
hsl(220, 90%, 20%)  → 11.6:1   ✅ AAA+ (very high)

For a white background, you need L ≤ 40% for AA normal text and L ≤ 30% for AAA.

Dark mode contrast

Dark mode inverts the challenge: light text on dark backgrounds.

Common failure:

color: #D1D5DB;           /* Gray-300 */
background: #1F2937;      /* Gray-800 */
/* Ratio: 9.73:1 — Passes AAA */

This passes! But if you lighten the background too much:

color: #D1D5DB;
background: #374151;      /* Gray-700 */
/* Ratio: 5.85:1 — Passes AA, fails AAA */

Rule of thumb for dark mode:

  • Background: L ≤ 20% (very dark)
  • Body text: L ≥ 80% (very light)
  • Secondary text: L ≥ 60% (medium-light)

UI components and graphical objects (WCAG 2.1)

WCAG 2.1 added contrast requirements for non-text elements:

Minimum 3:1 contrast for:

  • Form input borders
  • Focus indicators
  • Icon buttons (the icon itself)
  • Chart/graph elements that convey information
  • Active/inactive states (if color is the only differentiator)

Example failure:

input {
  border: 1px solid #E5E7EB;  /* Gray-200 on white */
  /* Ratio: 1.19:1 — Fails */
}

Fix:

input {
  border: 1px solid #9CA3AF;  /* Gray-400 */
  /* Ratio: 2.85:1 — Still fails! Need darker */
}

input {
  border: 1px solid #6B7280;  /* Gray-500 */
  /* Ratio: 4.76:1 — Passes */
}

Tools and workflow

1. Design phase

2. Development phase

  • Browser DevTools: Chrome/Edge DevTools show contrast ratios in the color picker (inspect element → Styles → click color swatch)
  • Axe DevTools extension: Automated accessibility testing including contrast

3. QA phase

  • Manual spot-checks with contrast checker tools
  • Automated testing with Lighthouse, axe-core, or Pa11y
  • Test on actual devices in bright sunlight (outdoor readability)

Exceptions and edge cases

What doesn't need to meet contrast requirements?

  • Logos and brand names (considered "essential")
  • Decorative text (not conveying information)
  • Inactive UI components (disabled buttons, grayed-out menu items)
  • Incidental text (text in photographs, screenshots of other interfaces)

What about gradients?

If text sits on a gradient background, the contrast ratio is measured against the least contrasting part of the gradient. If any part fails, the entire combination fails.

Solution: Add a semi-transparent overlay to ensure minimum contrast across the entire gradient:

.hero {
  background: linear-gradient(to right, #3B82F6, #8B5CF6);
  position: relative;
}

.hero::before {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);  /* Darkens the gradient */
}

.hero h1 {
  color: white;
  position: relative;
  z-index: 1;
}

Quick reference

Text Size AA Minimum AAA Minimum
Normal text (< 18pt / < 14pt bold) 4.5:1 7:1
Large text (≥ 18pt / ≥ 14pt bold) 3:1 4.5:1
UI components / graphics 3:1

Safe color pairs (white background):

  • Black #000000 — 21:1 ✅ AAA
  • Dark gray #595959 — 7.0:1 ✅ AAA
  • Medium gray #767676 — 4.54:1 ✅ AA
  • Light gray #959595 — 3.01:1 ⚠️ Large text only
  • Very light gray #B8B8B8 — 2.0:1 ❌ Fails

Conclusion

WCAG contrast requirements exist because low contrast excludes millions of users. The good news: contrast failures are mechanical and fixable. You don't need to redesign your brand — just darken text colors by 10-20% lightness, test with a contrast checker, and adjust until you hit 4.5:1.

Use our WCAG Contrast Batch Checker to test your entire palette in seconds. Paste your colors, get a pass/fail matrix, and ship accessible designs with confidence.

Further reading: