Introduction
Web Content Accessibility Guidelines (WCAG) ensure that websites are usable by people with disabilities, including visual impairments. Color contrast is one of the most important accessibility requirements.
What is Contrast Ratio?
Contrast ratio measures the difference in luminance between text and its background. It’s expressed as a ratio:
Formula:
Contrast Ratio = (L1 + 0.05) / (L2 + 0.05)
Where L1 is the relative luminance of the lighter color and L2 is the darker.
Scale: 1:1 (no contrast) to 21:1 (maximum contrast, white on black)
WCAG Requirements
Level AA (Standard)
Normal Text (under 18pt or 14pt bold):
- Minimum: 4.5:1
Large Text (18pt+ or 14pt+ bold):
- Minimum: 3:1
Level AAA (Enhanced)
Normal Text:
- Minimum: 7:1
Large Text:
- Minimum: 4.5:1
Examples
✅ Passes WCAG AA
| Foreground | Background | Ratio | Status |
|---|---|---|---|
| Black (#000) | White (#fff) | 21:1 | ✅ AAA |
| Dark Gray (#333) | White (#fff) | 12.6:1 | ✅ AAA |
| Navy (#003366) | White (#fff) | 12.4:1 | ✅ AAA |
| Medium Gray (#666) | White (#fff) | 5.7:1 | ✅ AA |
| Blue (#0066cc) | White (#fff) | 4.5:1 | ✅ AA (exact) |
❌ Fails WCAG AA
| Foreground | Background | Ratio | Status |
|---|---|---|---|
| Light Gray (#ccc) | White (#fff) | 1.6:1 | ❌ Fails |
| Yellow (#ffff00) | White (#fff) | 1.1:1 | ❌ Fails |
| Light Blue (#66ccff) | White (#fff) | 2.3:1 | ❌ Fails |
| Orange (#ff9900) | White (#fff) | 2.9:1 | ❌ Fails |
Calculating Contrast
Relative Luminance
First, calculate relative luminance for each color:
function getLuminance(hex) {
const rgb = hexToRgb(hex);
const [r, g, b] = rgb.map((val) => {
val = val / 255;
return val <= 0.03928 ? val / 12.92 : Math.pow((val + 0.055) / 1.055, 2.4);
});
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
function getContrastRatio(color1, color2) {
const lum1 = getLuminance(color1);
const lum2 = getLuminance(color2);
const lighter = Math.max(lum1, lum2);
const darker = Math.min(lum1, lum2);
return (lighter + 0.05) / (darker + 0.05);
}
Common Issues
1. Light Gray Text on White
/* ❌ Poor contrast */
.text {
color: #cccccc;
background: #ffffff;
/* Ratio: 1.6:1 - Fails! */
}
/* ✅ Good contrast */
.text {
color: #666666;
background: #ffffff;
/* Ratio: 5.7:1 - Passes AA */
}
2. Colored Text on Similar Backgrounds
/* ❌ Poor contrast */
.button {
color: #ffcc00;
background: #ffffcc;
/* Ratio: 1.4:1 - Fails! */
}
/* ✅ Good contrast */
.button {
color: #996600;
background: #ffffcc;
/* Ratio: 7.2:1 - Passes AAA */
}
3. Transparent Overlays
/* ❌ Poor contrast with transparency */
.overlay {
background: rgba(255, 255, 255, 0.3);
color: white;
/* Actual contrast depends on background */
}
/* ✅ Better - darker overlay */
.overlay {
background: rgba(0, 0, 0, 0.5);
color: white;
/* Better contrast */
}
Best Practices
1. Default to High Contrast
Start with high-contrast combinations:
- Dark text on light backgrounds
- Light text on dark backgrounds
- Avoid gray-on-gray combinations
2. Test All States
Check contrast for:
- Normal state
- Hover state
- Focus state
- Disabled state
- Error states
.button {
color: #ffffff;
background: #0066cc;
/* Ratio: 4.5:1 - Passes */
}
.button:hover {
background: #0052a3;
/* Ratio: 4.5:1 - Still passes */
}
.button:disabled {
color: #cccccc;
background: #e0e0e0;
/* Ratio: 1.6:1 - Fails! */
}
3. Use Tools
Our tools:
- Contrast Checker - Test color combinations
- Color Converter - Convert between formats
Other tools:
- WebAIM Contrast Checker
- Chrome DevTools Lighthouse
- axe DevTools
4. Consider Dark Mode
Dark mode needs separate contrast testing:
:root {
--text: #333333;
--bg: #ffffff;
}
@media (prefers-color-scheme: dark) {
:root {
--text: #e0e0e0;
--bg: #1a1a1a;
}
}
Fixing Low Contrast
Method 1: Darken Text
/* Before */
.text {
color: #cccccc;
} /* 1.6:1 */
/* After */
.text {
color: #666666;
} /* 5.7:1 */
Method 2: Lighten Background
/* Before */
.text {
color: #333333;
background: #f0f0f0;
} /* 10:1 */
/* Already good, but can improve */
.text {
color: #333333;
background: #ffffff;
} /* 12.6:1 */
Method 3: Add Outline/Shadow
/* Low contrast text */
.text {
color: #ffcc00;
background: #ffffcc;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
/* Shadow improves readability */
}
Method 4: Use Different Colors
/* Before - fails */
.link {
color: #66ccff;
} /* 2.3:1 */
/* After - passes */
.link {
color: #0066cc;
} /* 4.5:1 */
Real-World Examples
Accessible Button
.button-primary {
background: #0066cc;
color: #ffffff;
/* Ratio: 4.5:1 - Passes AA */
padding: 12px 24px;
border-radius: 8px;
}
.button-primary:hover {
background: #0052a3;
/* Ratio: 4.5:1 - Maintains contrast */
}
.button-primary:focus {
outline: 2px solid #0066cc;
outline-offset: 2px;
}
Accessible Form Input
.input {
border: 2px solid #666666;
/* Ratio: 5.7:1 - Passes AA */
color: #333333;
background: #ffffff;
padding: 8px 12px;
}
.input:focus {
border-color: #0066cc;
outline: 2px solid #0066cc;
}
.input-error {
border-color: #cc0000;
/* Error states need good contrast too */
}
Accessible Card
.card {
background: #ffffff;
border: 1px solid #e0e0e0;
}
.card-title {
color: #333333;
/* Ratio: 12.6:1 - Excellent */
}
.card-text {
color: #666666;
/* Ratio: 5.7:1 - Passes AA */
}
Testing Tools
Browser DevTools
Chrome DevTools:
- Inspect element
- Check Computed styles
- Use Lighthouse for accessibility audit
Automated Testing
// Using axe-core
import axe from "axe-core";
axe.run(document, (err, results) => {
if (results.violations) {
results.violations.forEach((violation) => {
if (violation.id === "color-contrast") {
console.error("Contrast violation:", violation);
}
});
}
});
Legal Requirements
Many countries require WCAG compliance:
- USA: Section 508, ADA
- EU: EN 301 549, Web Accessibility Directive
- Canada: ACA (Accessible Canada Act)
- Australia: DDA (Disability Discrimination Act)
Benefits:
- Legal compliance
- Larger audience (15% of population has disabilities)
- Better SEO
- Improved UX for all users
Conclusion
Color contrast is essential for web accessibility:
Remember:
- Minimum 4.5:1 for normal text (WCAG AA)
- Minimum 3:1 for large text (WCAG AA)
- Test all interactive states
- Use tools to verify
- Consider dark mode separately
Benefits:
- Legal compliance
- Better user experience
- Improved SEO
- Larger audience reach
Next Steps
- Test colors with Contrast Checker
- Learn about Color Formats
- Explore more Accessibility Best Practices