Color System Design — Build a Scalable Color System

How to design a color system that scales from one page to hundreds of screens without visual drift.

Why Color Systems Matter

A color system is the foundation of any design system. It defines the palette, the semantic meaning of each color, the state variations and the rules for usage. Without a system, colors proliferate — different designers choose slightly different blues, borders become inconsistent, and the product gradually loses visual coherence.

With a system, color decisions are made once and applied consistently everywhere. New components inherit the correct colors automatically. Rebranding becomes a single change rather than hundreds.

The Layers of a Color System

Layer 1: Primitives

The raw color values — the actual HEX/RGB codes. These are usually organized in scales:

  • Blue 50, 100, 200, ..., 900
  • Red 50, 100, 200, ..., 900
  • Neutral 50, 100, 200, ..., 900

Primitives are the "atoms" — they have no semantic meaning. They're just colors.

Layer 2: Semantic Tokens

Tokens map primitives to functional roles:

  • primary = blue-500
  • primary-hover = blue-600
  • background = neutral-50
  • surface = neutral-100
  • text = neutral-900
  • error = red-500

Semantic tokens are the "molecules" — they have meaning but can be remapped.

Layer 3: Component Tokens

Optional layer: component-specific tokens that reference semantic tokens:

  • button-primary-bg = primary
  • button-primary-text = white
  • card-bg = surface
  • card-border = border

This layer allows per-component customization without breaking the semantic system.

Building the Scale

How Many Colors?

Most design systems use:

  • 1 primary brand color (with 10 shades)
  • 1-2 secondary colors
  • 1 success (green)
  • 1 warning (amber)
  • 1 error (red)
  • 1 info (blue)
  • 1 neutral scale (10 shades)

That's 7-8 color families × 10 shades = 70-80 base colors — enough for nearly any UI.

Scale Spacing

The 50-900 naming comes from the observation that most UIs need about 10 lightness steps. Some design systems use more (Radix uses 12 steps), some fewer. Ten is a good default.

Within each scale, lightness should decrease approximately evenly from step 50 (very light) to step 900 (very dark).

Saturation Variation

Professional scales vary saturation alongside lightness:

  • Light steps (50-200) are less saturated — they're close to white
  • Mid steps (400-600) have maximum saturation
  • Dark steps (700-900) are slightly desaturated — they're close to black

This produces scales that look natural rather than garish.

Semantic Token Design

Name by Purpose, Not Color

Good token names:

  • color-primary
  • color-background
  • color-text
  • color-error

Bad token names:

  • color-blue (what if primary becomes green?)
  • color-light-gray (what if the theme changes?)

Include State Variants

Every interactive color needs states:

  • color-primary (default)
  • color-primary-hover
  • color-primary-active
  • color-primary-disabled

Theming

Semantic tokens enable theming without touching component code. To create a dark theme:

  • Remap background → neutral-900 instead of neutral-50
  • Remap text → neutral-50 instead of neutral-900
  • Remap primary → primary-400 instead of primary-500 (lighter for dark backgrounds)

Components don't change — they just read the tokens, which now resolve to different values.

Common Pitfalls

Pitfall 1: Too Many Colors

Design systems that start with 15 brand colors and 5 shades each quickly become unmanageable. Start with fewer colors and add only when necessary.

Pitfall 2: Inconsistent Scales

If blue-500 is #155EEF but red-500 has completely different lightness, mixing them produces inconsistent hierarchy. All scales should have similar lightness at each step.

Pitfall 3: No Accessibility Testing

Colors that look fine in isolation can fail WCAG contrast when combined. Test every pairing.

Pitfall 4: Ignoring Dark Mode

Colors designed for light mode often fail in dark mode. Design both simultaneously.

Implementing in CSS

:root {
  /* Primitives */
  --blue-500: #155EEF;
  --blue-600: #0F4BD1;
  --neutral-50: #F8FAFF;
  --neutral-900: #15243B;

  /* Semantic tokens */
  --color-primary: var(--blue-500);
  --color-primary-hover: var(--blue-600);
  --color-background: var(--neutral-50);
  --color-text: var(--neutral-900);
}

[data-theme="dark"] {
  --color-background: var(--neutral-900);
  --color-text: var(--neutral-50);
}

Tools for Building Color Systems

Frequently Asked Questions

Related Articles