Skip to main content
Technology & EngineeringAccessibility154 lines

Color Contrast

Color contrast ratios, visual accessibility, and inclusive design for users with low vision or color blindness

Quick Summary18 lines
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 lines
Paste into your CLAUDE.md or agent config

Color 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)

ElementLevel AALevel AAA
Normal text (< 18pt / < 14pt bold)4.5:17:1
Large text (>= 18pt / >= 14pt bold)3:14.5:1
UI components and graphical objects3:1
Focus indicators3: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: more for 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

Get CLI access →