CSS color-mix()
color-mix() is a CSS color function that takes two colors and mixes them in a specified color space. It lets you derive new colors directly in your stylesheets without preprocessors, JavaScript, or design tools.
The function accepts two color values and a percentage that determines how much of each color goes into the mix. The result is a computed color value that inherits all the benefits of the chosen color space — wider gamut, perceptual uniformity, or backwards compatibility.
| 1 | /* Basic syntax */ |
| 2 | color-mix(in srgb, #3B82F6, #ffffff) |
| 3 | |
| 4 | /* With percentage — 30% blue, 70% white */ |
| 5 | color-mix(in srgb, #3B82F6 30%, #ffffff) |
| 6 | |
| 7 | /* Different color spaces */ |
| 8 | color-mix(in oklch, #3B82F6 50%, #ef4444) |
| 9 | color-mix(in lab, #3B82F6 70%, transparent) |
| 10 | color-mix(in hsl, #3B82F6 80%, black) |
The function signature is straightforward: specify the target color space, then list the two colors with optional percentages. If no percentage is given for a color, it defaults to 50%.
| 1 | /* Full syntax */ |
| 2 | color-mix(in <colorspace>, <color1> <pct1>, <color2> <pct2>) |
| 3 | |
| 4 | /* Percentages must sum to 100% */ |
| 5 | color-mix(in srgb, #3B82F6 40%, #ef4444 60%) /* ✓ */ |
| 6 | color-mix(in srgb, #3B82F6 30%, #ef4444 70%) /* ✓ */ |
| 7 | |
| 8 | /* Implicit 50/50 when both percentages omitted */ |
| 9 | color-mix(in srgb, #3B82F6, #ef4444) |
| 10 | |
| 11 | /* One percentage given — other is computed */ |
| 12 | color-mix(in srgb, #3B82F6 25%, #ef4444) /* 25% blue, 75% red */ |
| 13 | |
| 14 | /* Can use currentColor as an input */ |
| 15 | color-mix(in srgb, currentColor 60%, transparent) |
note
The color space you choose for mixing has a significant impact on the result. Each space interpolates colors differently — some produce more perceptually uniform mixes, others are better for wide-gamut displays, and some offer better browser support.
| Color Space | Characteristics | Best For |
|---|---|---|
| srgb | Standard RGB, well-understood | General use, broad support |
| oklch | Perceptually uniform, wide gamut | Design systems, gradients |
| lab | CIE Lab, perceptual | Scientific accuracy |
| hsl | Hue, Saturation, Lightness | Hue shifts, playful palettes |
| 1 | /* srgb — linear interpolation, can produce muddy midpoints */ |
| 2 | color-mix(in srgb, #3B82F6, #ef4444) |
| 3 | |
| 4 | /* oklch — perceptually uniform, vibrant midpoints */ |
| 5 | color-mix(in oklch, #3B82F6, #ef4444) |
| 6 | |
| 7 | /* lab — similar to oklch but different math */ |
| 8 | color-mix(in lab, #3B82F6, #ef4444) |
| 9 | |
| 10 | /* hsl — rotates through hue wheel, can go through unexpected colors */ |
| 11 | color-mix(in hsl, #3B82F6, #ef4444) |
info
One of the most common uses for color-mix() is generating hover and active states from a base color. Instead of hardcoding separate color values, you derive them dynamically.
| 1 | /* Base color with CSS custom properties */ |
| 2 | :root { |
| 3 | --color-primary: #3B82F6; |
| 4 | } |
| 5 | |
| 6 | .button { |
| 7 | background: var(--color-primary); |
| 8 | color: white; |
| 9 | border: none; |
| 10 | padding: 12px 24px; |
| 11 | border-radius: 8px; |
| 12 | transition: background 0.2s; |
| 13 | } |
| 14 | |
| 15 | /* Hover — mix with white for a lighter variant */ |
| 16 | .button:hover { |
| 17 | background: color-mix(in srgb, var(--color-primary) 85%, white); |
| 18 | } |
| 19 | |
| 20 | /* Active — mix with black for a darker variant */ |
| 21 | .button:active { |
| 22 | background: color-mix(in srgb, var(--color-primary) 80%, black); |
| 23 | } |
| 24 | |
| 25 | /* Focus ring using color-mix */ |
| 26 | .button:focus-visible { |
| 27 | outline: 2px solid color-mix(in srgb, var(--color-primary) 50%, transparent); |
| 28 | outline-offset: 2px; |
| 29 | } |
color-mix() is superior to the older rgba() approach for creating opacity variants because it preserves the original color's hue and saturation while reducing perceived transparency. Mixing with transparent in a specific color space gives you control over how the fade looks.
| 1 | /* Create opacity variants of a brand color */ |
| 2 | :root { |
| 3 | --blue-100: color-mix(in srgb, #3B82F6 10%, transparent); |
| 4 | --blue-200: color-mix(in srgb, #3B82F6 20%, transparent); |
| 5 | --blue-300: color-mix(in srgb, #3B82F6 30%, transparent); |
| 6 | --blue-400: color-mix(in srgb, #3B82F6 50%, transparent); |
| 7 | --blue-500: color-mix(in srgb, #3B82F6 70%, transparent); |
| 8 | --blue-600: color-mix(in srgb, #3B82F6 90%, transparent); |
| 9 | --blue-700: #3B82F6; |
| 10 | } |
| 11 | |
| 12 | /* oklch produces better transparency transitions */ |
| 13 | :root { |
| 14 | --surface-50: color-mix(in oklch, #3B82F6 5%, transparent); |
| 15 | --surface-100: color-mix(in oklch, #3B82F6 10%, transparent); |
| 16 | --surface-200: color-mix(in oklch, #3B82F6 20%, transparent); |
| 17 | } |
| 18 | |
| 19 | /* Use in background and border contexts */ |
| 20 | .card { |
| 21 | background: var(--surface-50); |
| 22 | border: 1px solid var(--surface-200); |
| 23 | } |
color-mix() makes it trivial to build a complete color scale from a single base color. By mixing with white and black at different percentages, you generate an entire palette that stays harmonious.
| 1 | :root { |
| 2 | /* Base color */ |
| 3 | --primary: #3B82F6; |
| 4 | |
| 5 | /* Lighter shades — mixed with white */ |
| 6 | --primary-50: color-mix(in oklch, var(--primary) 10%, white); |
| 7 | --primary-100: color-mix(in oklch, var(--primary) 20%, white); |
| 8 | --primary-200: color-mix(in oklch, var(--primary) 35%, white); |
| 9 | --primary-300: color-mix(in oklch, var(--primary) 50%, white); |
| 10 | --primary-400: color-mix(in oklch, var(--primary) 70%, white); |
| 11 | |
| 12 | /* The base color itself */ |
| 13 | --primary-500: var(--primary); |
| 14 | |
| 15 | /* Darker shades — mixed with black */ |
| 16 | --primary-600: color-mix(in oklch, var(--primary) 80%, black); |
| 17 | --primary-700: color-mix(in oklch, var(--primary) 65%, black); |
| 18 | --primary-800: color-mix(in oklch, var(--primary) 50%, black); |
| 19 | --primary-900: color-mix(in oklch, var(--primary) 35%, black); |
| 20 | --primary-950: color-mix(in oklch, var(--primary) 20%, black); |
| 21 | } |
info
| 1 | /* Dark mode — derive dark variants automatically */ |
| 2 | .dark { |
| 3 | --bg-primary: color-mix(in oklch, var(--primary-500) 8%, #0D0D0D); |
| 4 | --bg-secondary: color-mix(in oklch, var(--primary-500) 4%, #0D0D0D); |
| 5 | --bg-elevated: color-mix(in oklch, var(--primary-500) 12%, #1A1A2E); |
| 6 | --text-primary: color-mix(in oklch, var(--primary-500) 95%, white); |
| 7 | --text-secondary: color-mix(in oklch, var(--primary-500) 60%, #808080); |
| 8 | --border: color-mix(in oklch, var(--primary-500) 15%, #222); |
| 9 | } |
Beyond hover states and palettes, color-mix() solves many day-to-day CSS problems. Here are several patterns that replace JavaScript color manipulation libraries.
| 1 | /* Dynamic underline that fades from transparent to the text color */ |
| 2 | .link { |
| 3 | text-decoration: underline; |
| 4 | text-decoration-color: transparent; |
| 5 | transition: text-decoration-color 0.2s; |
| 6 | } |
| 7 | |
| 8 | .link:hover { |
| 9 | text-decoration-color: currentColor; |
| 10 | } |
| 11 | |
| 12 | /* Badge colors from a base semantic color */ |
| 13 | .badge-success { |
| 14 | background: color-mix(in srgb, #22c55e 15%, transparent); |
| 15 | color: #22c55e; |
| 16 | border: 1px solid color-mix(in srgb, #22c55e 30%, transparent); |
| 17 | } |
| 18 | |
| 19 | .badge-warning { |
| 20 | background: color-mix(in srgb, #f59e0b 15%, transparent); |
| 21 | color: #f59e0b; |
| 22 | border: 1px solid color-mix(in srgb, #f59e0b 30%, transparent); |
| 23 | } |
| 24 | |
| 25 | .badge-error { |
| 26 | background: color-mix(in srgb, #ef4444 15%, transparent); |
| 27 | color: #ef4444; |
| 28 | border: 1px solid color-mix(in srgb, #ef4444 30%, transparent); |
| 29 | } |
| 30 | |
| 31 | /* Overlay that sits between background and content */ |
| 32 | .overlay { |
| 33 | background: color-mix(in srgb, black 60%, transparent); |
| 34 | backdrop-filter: blur(4px); |
| 35 | } |
| Browser | Support | Version |
|---|---|---|
| Chrome | ✓ | 111+ |
| Edge | ✓ | 111+ |
| Firefox | ✓ | 113+ |
| Safari | ✓ | 16.2+ |