Skip to content
Accessibility

Color Accessibility Beyond Contrast

Go beyond contrast ratios — color blindness, information encoding, and inclusive palettes.

stellae.design

8 min read

Introduction

Most developers know about contrast ratios — 4.5:1 for normal text, 3:1 for large text. But color accessibility goes much further. About 8% of men and 0.5% of women have some form of color vision deficiency. If your UI relies on color alone to convey information, these users miss critical context.

Beyond color blindness, consider low vision, different displays, and environments (bright sunlight). Accessible color design means every piece of color-encoded information has a non-color alternative — text labels, icons, patterns, or position.

This guide covers color blindness types, designing for color independence, testing tools, and building inclusive color palettes.


Key Concepts

Types of Color Vision Deficiency

  • Deuteranopia (green-blind) — Most common. Red and green look similar. ~5% of men.
  • Protanopia (red-blind) — Red appears dark. Red/green confusion. ~1% of men.
  • Tritanopia (blue-blind) — Blue and yellow confusion. Rare (~0.003%).
  • Achromatopsia — Complete color blindness (sees only grayscale). Very rare.

The Color Independence Principle

html
// ❌ Bad: Color is the only indicator
<span style={{ color: error ? 'red' : 'green' }}>{status}</span>

// ✅ Good: Color + text + icon
<span style={{ color: error ? 'red' : 'green' }}>
  {error ? '❌ Error' : '✅ Success'}: {status}
</span>

// ❌ Bad: Form error indicated only by red border
<input style={{ borderColor: 'red' }} />

// ✅ Good: Red border + error icon + error message
<div>
  <input aria-invalid="true" aria-describedby="email-error" style={{ borderColor: 'red' }} />
  <p id="email-error" className="text-red-500">⚠️ Please enter a valid email</p>
</div>

Practical Examples

1. Accessible Status Badges

typescript
function StatusBadge({ status }: { status: 'active' | 'warning' | 'error' | 'inactive' }) {
  const config = {
    active:   { color: 'bg-green-100 text-green-800', icon: '●', label: 'Active' },
    warning:  { color: 'bg-yellow-100 text-yellow-800', icon: '▲', label: 'Warning' },
    error:    { color: 'bg-red-100 text-red-800', icon: '✕', label: 'Error' },
    inactive: { color: 'bg-gray-100 text-gray-800', icon: '○', label: 'Inactive' },
  };
  const { color, icon, label } = config[status];
  return (
    <span className={`px-2 py-1 rounded-full text-sm font-medium ${color}`}>
      {icon} {label}
    </span>
  );
}

2. Accessible Data Visualization

typescript
// Charts: Don't rely on color alone
// ✅ Use patterns, labels, and shapes alongside colors

// For line charts: different line styles (solid, dashed, dotted)
const datasets = [
  { label: 'Revenue', color: '#3B82F6', dash: 'solid', marker: 'circle' },
  { label: 'Expenses', color: '#EF4444', dash: 'dashed', marker: 'square' },
  { label: 'Profit', color: '#10B981', dash: 'dotted', marker: 'triangle' },
];

// For bar charts: add patterns or direct labels
// For pie charts: add labels on or next to each slice

3. Testing Color Independence

plain
// Chrome DevTools: Rendering → Emulate vision deficiencies
// Options: Deuteranopia, Protanopia, Tritanopia, Achromatopsia

// Quick test: Convert screenshot to grayscale
// If you can still understand the UI, it passes the color independence test

// CSS grayscale test:
html { filter: grayscale(100%); }

// Tools:
// - Chrome DevTools vision emulation
// - Stark (Figma plugin)
// - Color Oracle (desktop app)
// - sim-daltonism (macOS)

Best Practices

  • ✅ Always provide a non-color indicator: text, icons, patterns, or position
  • ✅ Test with color blindness simulators in Chrome DevTools
  • ✅ Use the grayscale test — if UI is understandable in grayscale, it's color-independent
  • ✅ Choose palettes that are distinguishable for common deficiencies (avoid pure red/green pairs)
  • ✅ Use distinct shapes in charts alongside colors
  • ❌ Don't use red/green as the only differentiator (success/error)
  • ❌ Don't use color alone for required field indicators — add an asterisk or text

Common Pitfalls

  • Green checkmarks and red X marks as the only indicators — add text labels
  • Heat maps with no alternative data representation — provide a data table option
  • Link text that's only distinguished from body text by color — add underline
  • Assuming passing WCAG contrast means it's color-accessible — contrast and color independence are separate

Accessibilityaccessibility

Was this article helpful?