Color Contrast Ratio WCAG Failure Fix
In this tutorial, you'll learn about Color Contrast Ratio WCAG Failure Fix. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
The Problem
Low color contrast between text and its background makes content unreadable for users with low vision, color blindness, or viewing in bright environments. WCAG requires a minimum contrast ratio of 4.5:1 for normal text and 3:1 for large text.
Quick Fix
Step 1: Check contrast ratios
// Wrong — guessing if contrast is sufficient
// Right — calculate contrast ratio
function getLuminance(hex) {
const rgb = hex.match(/[A-Fa-f0-9]{2}/g).map(v => {
const c = parseInt(v, 16) / 255;
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
});
return 0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2];
}
function getContrastRatio(hex1, hex2) {
const l1 = getLuminance(hex1);
const l2 = getLuminance(hex2);
const lighter = Math.max(l1, l2);
const darker = Math.min(l1, l2);
return (lighter + 0.05) / (darker + 0.05);
}
const ratio = getContrastRatio('#333333', '#FFFFFF');
console.log(ratio >= 4.5 ? 'Pass' : 'Fail'); // Pass (12.6:1)
Step 2: Use sufficient contrast values
/* Wrong — insufficient contrast for body text */
body {
color: #999999; /* Light gray on white — 2.8:1 */
background: #ffffff;
}
/* Right — sufficient contrast */
body {
color: #333333; /* Dark gray on white — 12.6:1 */
background: #ffffff;
}
/* For small text (<18px): 4.5:1 minimum */
/* For large text (>18px bold or >24px): 3:1 minimum */
Step 3: Handle link colors
/* Wrong — link color does not meet 4.5:1 against background */
a {
color: #6699cc; /* On white: 3.2:1 — fails */
text-decoration: underline;
}
/* Right — use darker link color */
a {
color: #1a5a99; /* On white: 6.2:1 — passes */
text-decoration: underline;
}
/* For links without underline: need 3:1 contrast with body text */
a {
color: #1a5a99;
text-decoration: none; /* Only if 3:1 with surrounding text */
}
Step 4: Use contrast-safe design tokens
/* Wrong — no systematic contrast checking */
:root {
--text-primary: #666666;
--text-secondary: #999999;
--bg-primary: #ffffff;
}
/* Right — design tokens with contrast built in */
:root {
--text-primary: #1a1a1a; /* 15.3:1 on white */
--text-secondary: #595959; /* 7.0:1 on white — WCAG AA+ */
--text-disabled: #a0a0a0; /* 2.8:1 — not used for info */
--bg-primary: #ffffff;
--bg-secondary: #f5f5f5;
}
/* Always test combinations */
/* --text-primary on --bg-primary: 15.3:1 ✓ */
/* --text-secondary on --bg-primary: 7.0:1 ✓ */
Step 5: Test with contrast tools
# Use axe DevTools browser extension
# Use Chrome DevTools: Elements > Styles > Color picker shows ratio
# Use WebAIM Contrast Checker: https://webaim.org/resources/contrastchecker/
# Programmatic testing (Node.js with axe-core)
npx axe https://example.com --include contrast
Prevention
- Design with color contrast in mind from the start
- Use a contrast ratio of 7:1 or higher for body text
- Never rely on color alone to convey information
- Test all color combinations with contrast-checking tools
- Maintain a contrast-safe design token system
Common Mistakes with color contrast
- Forgetting
deriving (Show, Eq)on custom data types needed for debugging - Placing the wildcard pattern first in case expressions, making all subsequent patterns unreachable
- Using
headandtailinstead of pattern matching, causing runtime errors on empty lists
These mistakes appear frequently in real-world A11Y code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro