Color Contrast
Color contrast ratios, visual accessibility, and inclusive design for users with low vision or color blindness
You are an expert in color contrast and visual accessibility for building accessible web applications. ## Key Points - **Deuteranopia / Deuteranomaly**: Reduced green sensitivity (most common, ~6% of males). - **Protanopia / Protanomaly**: Reduced red sensitivity. - **Tritanopia / Tritanomaly**: Reduced blue sensitivity (rare). - **Achromatopsia**: Complete color blindness (very rare). - **WebAIM Contrast Checker** (webaim.org/resources/contrastchecker): Enter foreground and background colors to check the ratio. - **Chrome DevTools**: Inspect an element, click the color swatch in the Styles pane to see the contrast ratio and AA/AAA pass/fail. - **Firefox Accessibility Inspector**: Highlights contrast issues on the page. - **Simulate color blindness**: Chrome DevTools > Rendering > Emulate vision deficiencies. - Manually check that every use of color to convey meaning also has a non-color indicator (text, pattern, icon, or shape). - Start with a contrast-safe palette during design, not as a remediation step. - Test both light and dark modes against WCAG contrast thresholds. - Support `prefers-contrast: more` for users who need extra contrast beyond WCAG minimums.
skilldb get accessibility-skills/Color ContrastFull skill: 154 linesColor Contrast — Web Accessibility
You are an expert in color contrast and visual accessibility for building accessible web applications.
Core Philosophy
Overview
Color contrast ensures that text and meaningful UI elements are distinguishable from their backgrounds. WCAG 2.2 defines minimum contrast ratios to support users with low vision, aging-related vision changes, and situational impairments like screen glare. Beyond contrast, accessible visual design avoids relying on color alone to convey information.
Core Concepts
Contrast ratio thresholds (WCAG 2.2)
| Element | Level AA | Level AAA |
|---|---|---|
| Normal text (< 18pt / < 14pt bold) | 4.5:1 | 7:1 |
| Large text (>= 18pt / >= 14pt bold) | 3:1 | 4.5:1 |
| UI components and graphical objects | 3:1 | — |
| Focus indicators | 3:1 | — |
How contrast ratio works
Contrast ratio is calculated as (L1 + 0.05) / (L2 + 0.05) where L1 is the relative luminance of the lighter color and L2 is the relative luminance of the darker color. The result ranges from 1:1 (no contrast) to 21:1 (maximum, black on white).
Types of color vision deficiency
- Deuteranopia / Deuteranomaly: Reduced green sensitivity (most common, ~6% of males).
- Protanopia / Protanomaly: Reduced red sensitivity.
- Tritanopia / Tritanomaly: Reduced blue sensitivity (rare).
- Achromatopsia: Complete color blindness (very rare).
Implementation Patterns
CSS custom properties for a contrast-safe palette
:root {
/* Text on white (#fff) backgrounds */
--color-text-primary: #1a1a1a; /* 16.6:1 on white */
--color-text-secondary: #545454; /* 7.4:1 on white */
--color-text-muted: #717171; /* 4.6:1 on white — meets AA normal */
/* Interactive colors */
--color-link: #0055cc; /* 7.4:1 on white */
--color-link-hover: #003d99; /* 10.5:1 on white */
/* Status colors (not relying on color alone) */
--color-error: #c62828; /* 6.5:1 on white */
--color-success: #2e7d32; /* 5.0:1 on white */
}
Never rely on color alone
<!-- Bad: color is the only indicator -->
<span style="color: red;">Required</span>
<!-- Good: color + icon + text -->
<label for="email">
Email <span aria-hidden="true" style="color: #c62828;">*</span>
<span class="sr-only">(required)</span>
</label>
<input id="email" type="email" required aria-required="true">
Accessible data visualization
<!-- Use patterns, labels, and shapes in addition to color -->
<svg role="img" aria-label="Sales by region: North 45%, South 30%, West 25%">
<rect x="0" y="0" width="45" height="20" fill="#0055cc" />
<rect x="0" y="25" width="30" height="20" fill="#c62828" stroke="#c62828"
stroke-dasharray="4 2" />
<rect x="0" y="50" width="25" height="20" fill="#2e7d32"
stroke="#2e7d32" stroke-width="2" />
<!-- Each bar also has a text label -->
<text x="47" y="15">North: 45%</text>
<text x="32" y="40">South: 30%</text>
<text x="27" y="65">West: 25%</text>
</svg>
High contrast mode support
@media (forced-colors: active) {
.button {
border: 2px solid ButtonText;
}
.icon {
forced-color-adjust: auto;
}
/* Ensure custom focus indicators survive forced colors */
:focus-visible {
outline: 2px solid Highlight;
outline-offset: 2px;
}
}
Dark mode with maintained contrast
@media (prefers-color-scheme: dark) {
:root {
--color-bg: #121212;
--color-surface: #1e1e1e;
--color-text-primary: #e0e0e0; /* 13.3:1 on #121212 */
--color-text-secondary: #a0a0a0; /* 6.8:1 on #121212 */
--color-link: #6fa3f7; /* 6.2:1 on #121212 */
}
}
Testing & Validation
- WebAIM Contrast Checker (webaim.org/resources/contrastchecker): Enter foreground and background colors to check the ratio.
- Chrome DevTools: Inspect an element, click the color swatch in the Styles pane to see the contrast ratio and AA/AAA pass/fail.
- Firefox Accessibility Inspector: Highlights contrast issues on the page.
- Simulate color blindness: Chrome DevTools > Rendering > Emulate vision deficiencies.
- Manually check that every use of color to convey meaning also has a non-color indicator (text, pattern, icon, or shape).
Best Practices
- Start with a contrast-safe palette during design, not as a remediation step.
- Test both light and dark modes against WCAG contrast thresholds.
- Support
prefers-contrast: morefor users who need extra contrast beyond WCAG minimums.
Common Pitfalls
- Using placeholder text as the only label, which typically has insufficient contrast (e.g., light gray on white).
- Assuming that dark mode automatically has good contrast—dark text on dark backgrounds can fail just as easily as light-on-light.
Anti-Patterns
Over-engineering for hypothetical scale. Building for millions of users when you have hundreds adds complexity without value. Solve today's problems first.
Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide well wastes time and introduces unnecessary risk.
Premature abstraction. Creating elaborate frameworks and utilities before you have enough concrete cases to know what the abstraction should look like produces the wrong abstraction.
Neglecting error handling at boundaries. Internal code can trust its inputs, but system boundaries (user input, APIs, file I/O) require defensive validation.
Skipping documentation for obvious code. What is obvious to you today will not be obvious to your colleague next month or to you next year.
Install this skill directly: skilldb add accessibility-skills
Related Skills
Accessible Forms
Accessible form design patterns including labels, validation, error handling, and multi-step forms
Aria Patterns
ARIA roles, states, and properties for building accessible custom widgets and UI components
Axe Testing
Automated accessibility testing with axe-core, including CI integration, custom rules, and result analysis
Focus Management
Focus management strategies for single-page applications, modals, route changes, and dynamic content
Keyboard Navigation
Keyboard navigation patterns, focus order, and shortcut design for fully keyboard-accessible interfaces
Screen Reader Compat
Building web content that works correctly with screen readers like NVDA, JAWS, and VoiceOver