Designing Accessible Color Systems: Contrast, Color Blindness, and WCAG in Practice

Team 5 min read

#webdev

#a11y

#design

Introduction

Designing color systems that are usable by anyone starts with accessibility baked into the color palette. This post explores practical approaches to contrast, color-vision deficiency considerations, and applying WCAG guidelines in real-world designs. You’ll learn how to define a robust color token system, validate contrast across components, and ship themes that work in both light and dark contexts.

Understanding Contrast

Contrast is the perceptual difference between foreground and background colors. WCAG defines specific thresholds to ensure readable text and distinguishable UI states.

  • Normal text: minimum contrast of 4.5:1
  • Large text (18pt or 14pt bold): minimum contrast of 3:1
  • WCAG 2.1/2.2 also describes higher (AAA) targets, such as 7:1 for normal text, for scenarios requiring stronger legibility.

Practical tips:

  • Start with a strong base: choose a foreground color that meets 4.5:1 against its background for body text.
  • Separate concerns: maintain a semantic color system (text, surface, border, support) rather than hard-coding color values everywhere.
  • Test across surfaces: ensure text on surfaces (card, modal, navigation) maintains the contrast target, including borders and shadows that contribute to readability.

Color Blindness and Perception

Color alone is not a reliable signal. People with color-vision deficiencies may misinterpret hue differences that others perceive easily.

Key considerations:

  • Deuteranopia/Protanopia (red-green) and Tritanopia (blue-yellow) affect how colors appear. Don’t rely on hue alone to convey meaning.
  • Use multiple cues: text labels, icons, patterns, or text within badges to describe status (e.g., “Error” or “Success” in addition to color).
  • Favor color palettes with sufficient luminance separation and avoid near-similar hues in critical UI areas (forms, alerts, charts).

Practical techniques:

  • Design color tokens that pair with accessible text labels, not just colors.
  • Use patterns or icons in data visualizations (e.g., stripes on bars, different hatch patterns on pies) to differentiate data series.
  • Test with color-blind simulators or browser extensions to verify that information remains clear.

Designing the token system

A scalable color system should separate semantics from presentation so you can adapt to light/dark modes and design changes without breaking accessibility.

Guidelines:

  • Define semantic tokens: color-foreground, color-background, color-surface, color-border, color-primary, color-danger, color-success, etc.
  • Include on-token variants: color-on-foreground, color-on-surface, color-on-primary to indicate text/icon color on the given surface.
  • Build modes on tokens: provide artifacts for light, dark, and high-contrast modes if needed.

Example token strategy:

  • Base tokens: —bg, —surface, —text, —muted
  • Interaction tokens: —primary, —on-primary, —primary-weak
  • Status tokens: —success, —warning, —danger, each with corresponding —on-… variants

Practical Patterns and Implementation

A sound implementation uses CSS variables and a well-documented palette.

Example CSS pattern:

:root {
  --bg: #ffffff;
  --surface: #f8f9fb;
  --text: #1f2937;
  --muted: #6b7280;

  --primary: #2563eb;
  --on-primary: #ffffff;

  --secondary: #a855f7;
  --on-secondary: #ffffff;

  --surface-border: #e5e7eb;
}
[data-theme="dark"] {
  --bg: #0b1020;
  --surface: #141a2b;
  --text: #e5e7eb;
  --muted: #a3a3a3;

  --primary: #60a5fa;
  --on-primary: #0b1020;

  --secondary: #c084fc;
  --on-secondary: #0b1020;

  --surface-border: #1f2a44;
}

Usage examples:

body {
  background-color: var(--bg);
  color: var(--text);
}
.card {
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--surface-border);
}
.button {
  background-color: var(--primary);
  color: var(--on-primary);
  border: 1px solid color-mix(in oklab, var(--primary), black 10%);
}

Tips:

  • When selecting color pairs, test for contrast against both light and dark surfaces.
  • Ensure focus states have sufficient contrast and are visually distinct (outline, glow, or border).

Testing and validation

A robust workflow combines automated checks with human review.

Steps:

  • Automated contrast checks: verify 4.5:1 (normal text) and 3:1 (large text) for all foreground/background combinations. Tools: WCAG contrast checkers, browser extensions like Stark.
  • Color-blind testing: use simulators to view pages as different vision types (deuteranopia, protanopia, tritanopia). Adjust palettes if critical information changes with the simulation.
  • Real content review: validate that non-text cues (icons, labels, patterns) communicate meaning without relying on color alone.
  • Interactive components: ensure hover/focus/disabled states maintain legibility and sufficient contrast.

Accessibility validation should extend to charts and data visuals. When a color-coded legend is used, ensure the data series are distinguishable by shape, linestyle, or labels in addition to color.

Case study: dashboard color system

Consider a data dashboard used by analytics teams. It must convey status, trends, and precision values without relying solely on color.

Approach:

  • Establish a strong base palette with a high-contrast text-on-surface ratio.
  • Define tokens for data states: —state-critical, —state-warning, —state-ok with explicit —on-state variants.
  • Use patterned fills or hatching in charts for accessibility alongside color.
  • Implement light/dark themes with identical contrast targets to prevent drift.
  • Provide per-component text labels on charts (e.g., value badges) so color is not the sole signal.

Example usage:

  • Alerts: background is var(—state-critical) with text var(—on-state-critical) and a border variant using var(—surface-border).
  • Tables: zebra rows use different surfaces for readability; row headers use text color with sufficient contrast.

Conclusion

Designing accessible color systems is about more than choosing pretty hues. It requires a deliberate approach to contrast, consideration of color vision deficiencies, and a token-driven architecture that supports themes and scalable design. By pairing semantic tokens with concrete contrast targets and multiple cues beyond color, you can build interfaces that remain legible, usable, and inclusive across contexts and users.