Home / Articles / Color & Theming /

color

color-mix(): blending colors in CSS

Generate hover shades, semi-transparent overlays, and entire palettes from a single base color — no preprocessor needed.

Basic syntax

color-mix() takes a color space and two colors with optional percentages. The result is a new color blended in that space.

/* mix 50/50 in oklch */
background: color-mix(in oklch, oklch(65% 0.22 270), oklch(80% 0.15 145));

/* mix 70% of the first color, 30% of the second */
background: color-mix(in oklch, oklch(65% 0.22 270) 70%, oklch(80% 0.15 145));

Choosing a color space

The color space matters. Mixing in srgb can produce muddy mid-tones; oklch preserves perceptual lightness and chroma.

/* srgb — familiar but can desaturate */
color-mix(in srgb, red, blue)

/* oklch — perceptually uniform, best for UI work */
color-mix(in oklch, oklch(60% 0.25 30), oklch(60% 0.25 270))

/* hsl — hue interpolation is intuitive but lightness is uneven */
color-mix(in hsl, hsl(0 80% 50%), hsl(240 80% 50%))
For design tokens and UI colors, in oklch is almost always the right choice. It avoids the grey-zone artifacts common in srgb mixing.

Hover and active states

The most practical use case: derive darker and lighter variants from a single base color.

:root {
  --brand: oklch(62% 0.24 275);
}

.btn {
  background: var(--brand);
}

.btn:hover {
  /* 20% darker by mixing with black */
  background: color-mix(in oklch, var(--brand) 80%, black);
}

.btn:active {
  background: color-mix(in oklch, var(--brand) 65%, black);
}

Alpha blending with transparent

Mix any color with transparent to get a semi-transparent version. This is cleaner than manually slicing the alpha channel.

.overlay {
  /* 40% opacity version of brand */
  background: color-mix(in oklch, var(--brand) 40%, transparent);
}

.border-subtle {
  border: 1px solid color-mix(in oklch, var(--text) 15%, transparent);
}

Design token scales

Build an entire shade scale from one base color using color-mix() and custom properties.

:root {
  --blue: oklch(58% 0.22 265);
  --blue-100: color-mix(in oklch, var(--blue) 15%, white);
  --blue-200: color-mix(in oklch, var(--blue) 30%, white);
  --blue-300: color-mix(in oklch, var(--blue) 50%, white);
  --blue-500: var(--blue);
  --blue-700: color-mix(in oklch, var(--blue) 70%, black);
  --blue-900: color-mix(in oklch, var(--blue) 40%, black);
}
Change --blue and every shade updates automatically. This is the single-source-of-truth approach that design systems need.

Browser support

color-mix() is supported in all major browsers since mid-2023. For older browsers, provide a fallback color before the color-mix() declaration.

  • Always declare a plain color fallback on the line before.
  • Pair with @supports (color: color-mix(in oklch, red, blue)) for complex fallback logic.
  • Combine with @property to animate mixed colors via typed custom properties.