Skip to content
intermediatePhase 39 · Accessibility

Color Contrast

Ensure sufficient contrast ratios for text and interactive elements.

30m
0 problems
Topic Progress0%

WCAG Requirements

WCAG Requirements

Color contrast ratios for readability.

Contrast Ratios

Text Type AA Ratio AAA Ratio
Normal text (< 18pt) 4.5:1 7:1
Large text (≥ 18pt or 14pt bold) 3:1 4.5:1
UI components 3:1 3:1

Calculating Contrast

// Contrast ratio formula
function getContrastRatio(rgb1, rgb2) {
  const l1 = getRelativeLuminance(rgb1);
  const l2 = getRelativeLuminance(rgb2);
  const lighter = Math.max(l1, l2);
  const darker = Math.min(l1, l2);
  return (lighter + 0.05) / (darker + 0.05);
}

function getRelativeLuminance([r, g, b]) {
  const [rs, gs, bs] = [r, g, b].map(c => {
    c = c / 255;
    return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
  });
  return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
}

Color Combinations

/* ✅ Good contrast (7:1) */
.text-high-contrast {
  color: #000000;
  background: #FFFFFF;
}

/* ✅ Good contrast (4.5:1) */
.text-normal {
  color: #595959;
  background: #FFFFFF;
}

/* ❌ Poor contrast (2.1:1) */
.text-poor {
  color: #999999;
  background: #FFFFFF;
}

Contrast Tools

Contrast Tools

Browser Extensions

  • axe DevTools: Free, comprehensive
  • WAVE: Visual contrast checker
  • Colour Contrast Analyser: Desktop app

Online Tools

  • WebAIM Contrast Checker: webaim.org/resources/contrastchecker
  • Coolors Contrast Checker: coolors.co
  • TPGi Contrast Checker: tpgi.com

Code Integration

// Check contrast in tests
import { getContrastRatio } from './accessibility-utils';

describe('Color Contrast', () => {
  it('text should have sufficient contrast', () => {
    const ratio = getContrastRatio([0, 0, 0], [255, 255, 255]);
    expect(ratio).toBeGreaterThanOrEqual(4.5);
  });
});

CSS Custom Properties

:root {
  /* High contrast theme */
  --text-primary: #1a1a1a;
  --text-secondary: #595959;
  --background: #ffffff;
  --border: #767676;
  --focus-ring: #005fcc;
}

/* Dark mode */
@media (prefers-color-scheme: dark) {
  :root {
    --text-primary: #ffffff;
    --text-secondary: #cccccc;
    --background: #1a1a1a;
    --border: #999999;
    --focus-ring: #4da6ff;
  }
}

Non-Color Indicators

Non-Color Indicators

Don't rely on color alone to convey information.

Status Indicators

// ❌ Bad: Color only
<span style={{ color: 'red' }}>Error</span>
<span style={{ color: 'green' }}>Success</span>

// ✅ Good: Color + icon/text
<span style={{ color: 'red' }}>
  <Icon name="error" /> Error
</span>
<span style={{ color: 'green' }}>
  <Icon name="check" /> Success
</span>

// ✅ Good: Icon with sr-only text
<span className="status-error">
  <Icon name="error" aria-hidden="true" />
  <span className="sr-only">Error:</span> {errorMessage}
</span>

Form Validation

function FormField({ label, error, value }) {
  return (
    <div>
      <label>{label}</label>
      <input
        value={value}
        aria-invalid={!!error}
        aria-describedby={error ? `${label}-error` : undefined}
        style={{ borderColor: error ? 'red' : 'gray' }}
      />
      {error && (
        <span id={`${label}-error`} role="alert">
          <Icon name="error" /> {error}
        </span>
      )}
    </div>
  );
}

Links and Buttons

// ❌ Bad: Color only distinguishes links
<a href="/" style={{ color: 'blue' }}>Link</a>
<span style={{ color: 'gray' }}>Not a link</span>

// ✅ Good: Underline for links
<a href="/" style={{ textDecoration: 'underline' }}>Link</a>
<span>Not a link</span>

// ✅ Good: Explicit button
<button style={{ color: 'red', textDecoration: 'underline' }}>
  Delete
</button>

Charts and Graphs

// Use patterns + colors for charts
function BarChart({ data }) {
  return (
    <div>
      {data.map((item, i) => (
        <div key={i} className="bar-container">
          <div
            className={`bar pattern-${i % 3}`}
            style={{ backgroundColor: item.color }}
          />
          <span>{item.label}: {item.value}</span>
        </div>
      ))}
    </div>
  );
}

// CSS patterns
.bar.pattern-0 { background-image: repeating-linear-gradient(45deg, transparent, transparent 5px, rgba(255,255,255,0.3) 5px, rgba(255,255,255,0.3) 10px); }
.bar.pattern-1 { background-image: repeating-linear-gradient(-45deg, transparent, transparent 5px, rgba(255,255,255,0.3) 5px, rgba(255,255,255,0.3) 10px); }
.bar.pattern-2 { background-image: repeating-linear-gradient(0deg, transparent, transparent 5px, rgba(255,255,255,0.3) 5px, rgba(255,255,255,0.3) 10px); }

Practice Problems

0/3solved
Build Color Contrast Component

Create a reusable React component implementing Color Contrast. Include proper state management and accessibility.

Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where needed
Color Contrast Testing

Write unit and integration tests for Color Contrast using React Testing Library.

Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility tests
Color Contrast Performance

Optimize Color Contrast for performance. Consider memoization, code splitting, and bundle size.

Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysis

Quiz

1. What is the minimum contrast ratio for normal text?

Question 1 options

2. Why not use color alone to convey information?

Question 2 options

3. What is the primary purpose of Color Contrast?

Question 3 options

4. What is a common mistake when implementing Color Contrast?

Question 4 options

Flashcards

Question

What is the minimum contrast ratio for normal text?

Answer

4.5:1 for WCAG AA compliance.

Question

What is non-color indication?

Answer

Using icons, text, or patterns in addition to color to convey information.

Question

Why add underlines to links?

Answer

To distinguish links from regular text for color blind users.

Question

What is WCAG AA?

Answer

The standard level of accessibility compliance requiring 4.5:1 contrast for text.

Question

What is Color Contrast?

Answer

Color Contrast is a key concept in frontend development.

Revision Notes

Key Takeaways

  • 1.Normal text needs 4.5:1 contrast ratio
  • 2.Large text needs 3:1 contrast ratio
  • 3.Never rely on color alone to convey information
  • 4.Use icons, text, and patterns with color
  • 5.Test with contrast checking tools

Interview Tips

  • Explain WCAG contrast requirements
  • Discuss how to indicate status without color
  • Know common contrast checking tools

Cheat Sheet

Color Contrast Cheat Sheet

WCAG Ratios

  • Normal text: 4.5:1 (AA), 7:1 (AAA)
  • Large text: 3:1 (AA), 4.5:1 (AAA)
  • UI components: 3:1

Tools

  • axe DevTools
  • WebAIM Contrast Checker
  • Colour Contrast Analyser

Non-Color Indicators

  • Icons + color
  • Text + color
  • Patterns + color
  • Underlines for links