learn.colinkim.dev

Modern color with oklch() and color-mix()

Learn perceptual color spaces, oklch(), oklab(), color-mix(), relative colors, theme tokens, and support strategy.

Older CSS color often used hex, RGB, or HSL. Those still work, but modern CSS adds color spaces that better match human perception.

OKLCH

oklch() describes color with:

  • lightness
  • chroma
  • hue
:root {
  --accent: oklch(58% 0.18 255);
  --accent-strong: oklch(48% 0.2 255);
}

Lightness is easier to adjust predictably than in HSL. Increasing or decreasing lightness tends to feel more even.

OKLab

oklab() is a rectangular form of the same color space:

.swatch {
  background: oklab(62% -0.05 -0.16);
}

You will often use oklch() directly and oklab as a mixing space.

Mixing colors

color-mix() creates a color from other colors:

.button:hover {
  background: color-mix(in oklab, var(--accent), black 12%);
}

.button:focus-visible {
  outline-color: color-mix(in oklab, var(--accent), white 35%);
}

This keeps state colors tied to the base token.

Theme tokens

Use semantic tokens:

:root {
  --page: oklch(99% 0.01 250);
  --text: oklch(22% 0.02 250);
  --accent: oklch(56% 0.18 255);
}

@media (prefers-color-scheme: dark) {
  :root {
    --page: oklch(18% 0.02 250);
    --text: oklch(94% 0.01 250);
    --accent: oklch(72% 0.16 255);
  }
}

Components use meaning, not raw palette names:

body {
  background: var(--page);
  color: var(--text);
}

a {
  color: var(--accent);
}

Relative colors, briefly

Relative color syntax can derive one color from another:

.surface {
  background: oklch(from var(--accent) 96% 0.03 h);
}

This is powerful, but newer than basic color values. Use it when it makes a system clearer, and check target support.

Support and fallback

oklch() and color-mix() are broadly usable in modern browsers. Still, use fallbacks when the color is essential:

.button {
  background: rgb(40 90 200);
  background: oklch(56% 0.18 255);
}

Older browsers use the first value and ignore the unsupported second value.

For feature-specific enhancement:

@supports (background: color-mix(in oklab, red, white)) {
  .button:hover {
    background: color-mix(in oklab, var(--accent), black 12%);
  }
}

Preview

Theme preview

Contrast must work before color choices can communicate.

Ratio
Normal text
Large text
Generated CSS

What to carry forward

  • oklch() gives more perceptual control than HSL for many palettes
  • color-mix() creates state colors from tokens
  • semantic tokens are better than raw color names in components
  • relative colors are powerful but should be introduced deliberately
  • modern color needs support checks and contrast checks

The next lesson starts motion with transitions and transforms.

Progress

Quick checks

No quick checks in this lesson.

Mark lesson manually or answer quick checks to track progress.