DevToys Web Pro

free web developer tools

Blog
Rate us:
Try browser extension:
← Back to Blog

Color Contrast for WCAG Compliance: AA and AAA Standards Guide

16 min read

You're designing a website or app, but you're not sure if your color choices are accessible to users with visual impairments. Your designer picked colors that look great but fail accessibility audits. This guide explains WCAG color contrast requirements, how to test contrast ratios, and how to fix common failures while maintaining visual appeal.

Understanding WCAG Contrast Requirements

WCAG (Web Content Accessibility Guidelines) defines minimum contrast ratios between text and background colors to ensure readability for users with:

  • Low vision: Reduced visual acuity
  • Color vision deficiencies: Color blindness (8% of men, 0.5% of women)
  • Age-related vision loss: Contrast sensitivity decreases with age
  • Environmental conditions: Glare, bright sunlight, low-quality displays

WCAG Levels: AA vs AAA

LevelNormal TextLarge TextTarget
AA4.5:13:1Legal compliance, most sites
AAA7:14.5:1Enhanced accessibility, government, healthcare

Large text is defined as:

  • 18pt+ (24px+) for regular weight
  • 14pt+ (18.66px+) for bold weight (700+)

What is a Contrast Ratio?

A contrast ratio measures the difference in luminance (perceived brightness) between two colors:

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

Where:
- L1 = relative luminance of lighter color
- L2 = relative luminance of darker color
- Range: 1:1 (no contrast) to 21:1 (maximum contrast)

Examples:
Black on white: 21:1 (maximum contrast)
#767676 on white: 4.54:1 (AA pass)
#595959 on white: 7.0:1 (AAA pass)
White on white: 1:1 (fail)

Contrast Ratio Visual Reference

RatioExample (on white)ReadabilityWCAG Status
21:1#000000 (black)ExcellentAAA pass
12:1#424242 (dark gray)ExcellentAAA pass
7:1#595959 (medium-dark gray)Very goodAAA pass
4.5:1#767676 (medium gray)GoodAA pass
3:1#959595 (light-medium gray)Fair (large text only)AA large text
2:1#BABABA (light gray)PoorFail

Testing Color Contrast

Manual Testing with Contrast Checker

Use Color Contrast Checker to test your color pairs:

Testing workflow:

1. Enter foreground color (text color)
2. Enter background color
3. View contrast ratio
4. Check WCAG AA/AAA compliance
5. Adjust colors if needed

Example test:
Foreground: #3B82F6 (blue)
Background: #FFFFFF (white)
Ratio: 3.13:1
Result: ❌ Fails AA (needs 4.5:1)

Fix: Darken blue to #1D4ED8
New ratio: 7.2:1
Result: ✓ Passes AAA

Browser DevTools Testing

Chrome DevTools:

  1. Open DevTools (F12)
  2. Select element with text
  3. In Styles panel, click color picker next to color value
  4. View contrast ratio under color picker
  5. See AA/AAA pass/fail indicators

Firefox DevTools:

  1. Open DevTools (F12)
  2. Go to Accessibility tab
  3. Select "Check for issues" → "All Issues"
  4. View contrast warnings with specific ratios

Automated Testing

Use browser extensions for page-wide scanning:

  • axe DevTools: Comprehensive accessibility testing
  • WAVE: Visual feedback of accessibility issues
  • Lighthouse: Built into Chrome DevTools (Audits panel)
Running Lighthouse audit:

1. Open Chrome DevTools → Lighthouse tab
2. Select "Accessibility" category
3. Click "Analyze page load"
4. Review contrast failures in report

Example output:
✓ 15 contrast checks passed
✗ 3 elements with insufficient contrast:
  - Button: 2.8:1 (needs 4.5:1)
  - Link: 3.1:1 (needs 4.5:1)
  - Label: 2.5:1 (needs 4.5:1)

Common Contrast Failures and Fixes

Failure 1: Light Gray Text on White

Problem: Secondary text, placeholders, and disabled states often use light gray that fails contrast requirements.

❌ Failing examples:
#CCCCCC on #FFFFFF → 1.6:1 (fail)
#999999 on #FFFFFF → 2.8:1 (fail)
#AAAAAA on #FFFFFF → 2.3:1 (fail)

✓ Accessible alternatives:
#757575 on #FFFFFF → 4.5:1 (AA pass)
#595959 on #FFFFFF → 7.0:1 (AAA pass)
#616161 on #FFFFFF → 6.5:1 (AAA pass)

Fix strategy: Darken text by 30-40% while maintaining visual hierarchy.

Failure 2: Brand Colors with Insufficient Contrast

Problem: Brand colors (especially blues, light greens, oranges) often fail on white backgrounds.

Brand color examples:

Primary Blue: #3B82F6
- On white: 3.13:1 ❌
- Fix: #1D4ED8 → 7.2:1 ✓

Success Green: #10B981
- On white: 1.93:1 ❌
- Fix: #047857 → 4.93:1 ✓

Warning Orange: #F59E0B
- On white: 1.93:1 ❌
- Fix: #D97706 → 3.1:1 (large text only)
- Better fix: #B45309 → 4.7:1 ✓

Fix strategy:

  • Option 1: Create darker text variant of brand color (e.g., primary-600 instead of primary-500)
  • Option 2: Use white text on brand color background (check that contrast too)
  • Option 3: Add text outline or shadow (not recommended, harder to read)

Failure 3: Colored Links on Colored Backgrounds

Problem: Blue links on light blue backgrounds, or any link color that blends with background.

Example scenario:

Background: #EFF6FF (light blue)
Link: #3B82F6 (blue)
Contrast: 2.1:1 ❌

Solutions:

1. Darken link:
   Link: #1D4ED8 → 4.2:1 ✓

2. Change background:
   Background: #FFFFFF (white)
   Link: #3B82F6 → 3.13:1 ❌ (still fails)
   Link needs darkening anyway

3. Add underline:
   Link with underline + 3:1 contrast = acceptable
   (WCAG allows 3:1 if additional indicator present)

Failure 4: Placeholder Text

Problem: Default browser placeholder text is often too light (usually #999999 or similar).

❌ Default placeholder (most browsers):
color: #999999;
Contrast on white: 2.8:1 (fail)

✓ Accessible placeholder:
input::placeholder {
  color: #6B7280; /* 4.6:1 on white */
}

Note: WCAG 2.1 does NOT require placeholder text
to meet contrast minimums (only required for
labels and actual input text). However, better
contrast improves usability for everyone.

Failure 5: Disabled Form Controls

Problem: Disabled buttons and inputs often use very light colors that fail contrast.

WCAG Exception: Disabled controls are NOT required to meet contrast minimums under WCAG 2.1. However, users may struggle to see them.

Approach 1: Skip contrast (WCAG compliant)
button:disabled {
  color: #CCCCCC; /* 1.6:1 - acceptable per WCAG */
  background: #F3F4F6;
  cursor: not-allowed;
}

Approach 2: Better usability (recommended)
button:disabled {
  color: #9CA3AF; /* 3.2:1 - still visible */
  background: #E5E7EB;
  opacity: 0.6; /* Visual indicator of disabled state */
  cursor: not-allowed;
}

Designing Accessible Color Palettes

Creating a WCAG-Compliant Scale

Build your color system with contrast in mind from the start:

Example: Gray scale for text

On white background (#FFFFFF):
--gray-900: #111827 (16.8:1) → Body text (AAA)
--gray-800: #1F2937 (13.4:1) → Headings (AAA)
--gray-700: #374151 (10.2:1) → Subheadings (AAA)
--gray-600: #4B5563 (7.6:1) → Secondary text (AAA)
--gray-500: #6B7280 (4.6:1) → Tertiary text (AA)
--gray-400: #9CA3AF (2.8:1) → Disabled (fail - use sparingly)
--gray-300: #D1D5DB (1.7:1) → Borders only (not for text)
--gray-200: #E5E7EB (1.3:1) → Backgrounds only
--gray-100: #F3F4F6 (1.1:1) → Light backgrounds only

Rule: Use gray-500 or darker for all text

Multi-Color System Strategy

For each brand color, create variants for different use cases:

Primary Blue system:

--primary-900: #1E3A8A (11.2:1) → Headings on white
--primary-800: #1E40AF (9.3:1) → Links on white (AAA)
--primary-700: #1D4ED8 (7.2:1) → Links on white (AAA)
--primary-600: #2563EB (5.1:1) → Links on white (AA)
--primary-500: #3B82F6 (3.1:1) → Large text, backgrounds
--primary-400: #60A5FA (2.1:1) → Backgrounds only
--primary-300: #93C5FD (1.5:1) → Light backgrounds

Usage rules:
- Text on white: Use 600+ (AA) or 700+ (AAA)
- Backgrounds with white text: Use 600+
- Buttons: Background 500+, text white or 900
- Badges: Background 100-300, text 900

Dark Mode Considerations

Dark mode requires different contrast approaches:

Dark mode challenges:

1. White text on pure black (#000000) can cause
   "halation" (glowing effect) for some users

2. Very high contrast (21:1) can be uncomfortable
   in low-light environments

3. Many colors that work on white fail on dark

Recommended dark mode base:
Background: #0F172A (not pure black)
Body text: #E2E8F0 (15.8:1 on #0F172A)
Secondary: #94A3B8 (8.3:1 on #0F172A)

Adjusted brand colors for dark mode:
--primary-dark: #60A5FA (lighter than light mode)
--success-dark: #34D399 (lighter)
--warning-dark: #FBBF24 (lighter)

Special Cases and Exceptions

When Contrast Requirements Don't Apply

WCAG 2.1 exempts:

  • Logos and brand names: No contrast requirement
  • Incidental text: Text in photographs, pure decoration
  • Inactive UI components: Disabled buttons, grayed-out menu items
  • Text part of inactive user interface: Items user cannot interact with

Large Text Exception

Large text (18pt+/24px+ or 14pt+/18.66px+ bold) only needs 3:1 contrast for AA compliance:

Example: Hero headings

Heading: 48px, font-weight: 700
Color: #93C5FD (light blue)
Background: #FFFFFF (white)
Contrast: 1.5:1

❌ Fails AA large text (needs 3:1)
✓ Darkening to #3B82F6 → 3.1:1 (AA pass for large text)
Note: Still fails AA for normal text (needs 4.5:1)

UI Components (WCAG 2.1 Level AA 1.4.11)

Non-text contrast: UI components and graphical objects need 3:1 contrast against adjacent colors:

  • Form inputs: Border must be 3:1 against background
  • Buttons: Button background must be 3:1 against page background
  • Focus indicators: Must be 3:1 against background
  • Icons: Must be 3:1 (if meaningful, not decorative)
Example: Input border

Background: #FFFFFF (white)
Input border: #E5E7EB (light gray)
Contrast: 1.3:1 ❌

Fix options:
1. Darken border: #D1D5DB → 1.7:1 ❌ (still fails)
2. Darken more: #9CA3AF → 2.8:1 ❌ (still fails)
3. Darken enough: #6B7280 → 4.6:1 ✓

Alternative approach:
Use subtle background instead:
Input background: #F9FAFB
Input border: #D1D5DB
Border vs background: 1.3:1 (but acceptable if
  input has 3:1 contrast against page background)

Real-World Design Patterns

Pattern 1: Blue Link on White

/* ❌ Common failure */
a {
  color: #3B82F6; /* Tailwind blue-500 */
}
/* Contrast: 3.13:1 - fails AA */

/* ✓ AA compliant */
a {
  color: #1D4ED8; /* Tailwind blue-700 */
}
/* Contrast: 7.2:1 - passes AAA */

/* ✓ Alternative: underline + lower contrast */
a {
  color: #3B82F6; /* 3.1:1 */
  text-decoration: underline;
}
/* WCAG allows 3:1 with additional indicator */

Pattern 2: Status Badges

Status badge system:

/* ❌ Failing design */
.badge-success {
  background: #ECFDF5; /* Very light green */
  color: #10B981; /* Medium green */
}
/* Contrast: 1.4:1 - fails */

/* ✓ AA compliant */
.badge-success {
  background: #D1FAE5; /* Light green */
  color: #065F46; /* Dark green */
}
/* Contrast: 9.2:1 - passes AAA */

/* ✓ Alternative: stronger background */
.badge-success {
  background: #10B981; /* Medium green */
  color: #FFFFFF; /* White */
}
/* Contrast: 3.4:1 - passes AA for large text */
/* Use larger font size (14px+) and bold */

Pattern 3: Card Hover States

Hover indication with accessible contrast:

/* ❌ Subtle hover (may fail) */
.card {
  background: #FFFFFF;
  border: 1px solid #E5E7EB;
}
.card:hover {
  background: #F9FAFB; /* Very subtle */
  border-color: #D1D5DB;
}
/* Background change: 1.2:1 - hard to see */

/* ✓ Clear hover indication */
.card {
  background: #FFFFFF;
  border: 1px solid #E5E7EB;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.card:hover {
  border-color: #3B82F6; /* Blue */
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
/* Border change is visible, shadow reinforces */

Pattern 4: Form Validation

Error state with accessible contrast:

/* ❌ Light error state */
.input-error {
  border-color: #FCA5A5; /* Light red */
  color: #EF4444; /* Medium red */
}
/* Border contrast: 1.8:1 - fails */

/* ✓ Clear error state */
.input-error {
  border-color: #DC2626; /* Dark red, 5.3:1 */
  color: #991B1B; /* Very dark red, 9.4:1 */
  background: #FEF2F2; /* Very light red */
}
/* All contrasts pass AA */

Error message:
.error-message {
  color: #991B1B; /* 9.4:1 on white - AAA pass */
  font-size: 14px;
  margin-top: 4px;
}

Testing Workflow for Designers and Developers

Design Phase (Before Development)

  1. Create color palette: Use Color Contrast Checker to test each text color against backgrounds
  2. Document passing combinations: Create a style guide showing which colors work together
  3. Test in design tool: Use Figma plugins (Contrast, A11y - Color Contrast Checker)
  4. Consider color blindness: Use Color Blindness Simulator to verify designs work for all users

Development Phase (Implementation)

  1. Use pre-tested colors: Reference style guide from design phase
  2. Test components: Check contrast in browser DevTools color picker
  3. Run automated audit: Use Lighthouse or axe DevTools
  4. Fix failures: Iterate on colors until passing

QA Phase (Before Launch)

  1. Full-page audit: Run WAVE or axe DevTools on all pages
  2. Manual testing: Check hover states, focus indicators, modals
  3. Dark mode: Verify all colors work in dark mode too
  4. Document exceptions: Note any decorative elements exempt from WCAG

Quick Reference: Common Accessible Colors

On White Background (#FFFFFF)

ColorHexRatioWCAG
Black#00000021:1AAA
Dark Gray#4B55637.6:1AAA
Medium Gray#6B72804.6:1AA
Dark Blue#1D4ED87.2:1AAA
Medium Blue#2563EB5.1:1AA
Dark Green#0478574.9:1AA
Dark Red#991B1B9.4:1AAA
Medium Red#DC26265.3:1AA

On Dark Background (#0F172A)

ColorHexRatioWCAG
White#FFFFFF17.9:1AAA
Light Gray#E2E8F015.8:1AAA
Medium Gray#94A3B88.3:1AAA
Light Blue#60A5FA6.2:1AAA
Light Green#34D3995.8:1AA
Light Red#F871715.1:1AA

Summary

WCAG requires minimum contrast ratios: 4.5:1 for normal text (AA), 7:1 for AAA compliance, and 3:1 for large text and UI components. Test contrast using browser DevTools or dedicated contrast checkers. Common failures include light gray text, brand colors, and placeholder text. Build accessible color palettes from the start by testing each color variant. Use darker shades for text on light backgrounds and lighter shades for text on dark backgrounds.

For testing color contrast ratios and ensuring WCAG compliance, use Color Contrast Checker. To verify your designs work for users with color blindness, use Color Blindness Simulator to see your interface through different types of color vision deficiency.