CSS color-mix(): Mix Colors Dynamically in CSS
AI generated
CSS · color-mix() · oklch · Theming · Custom Properties
CSS color-mix(): Mix Colors Dynamically
in oklch, srgb and more

CSS color-mix() mixes colors natively in the browser: no Sass, no JavaScript, no preprocessors. Choosing the right color space produces perceptually uniform transitions, natural transparency variants, and dynamic themes that respond to user preferences.

16 min read color-mix() · oklch · srgb · hsl · Custom Properties · Theming Chrome 111+ · Firefox 113+ · Safari 16.2+

1. Why CSS color-mix() is a paradigm shift

Until now, any form of color mixing in CSS required either a preprocessor (Sass mix(), Less mix()) or JavaScript. That meant dependencies, build steps, and static color values calculated at build time instead of runtime. CSS color-mix() changes this fundamentally: color mixes are calculated natively in the browser, respond to CSS Custom Properties, and can react to runtime information such as user preferences or JavaScript-driven variables.

A concrete example: a design system defines a primary color as the Custom Property --color-brand. With CSS color-mix(), every variant of that color, light backgrounds, dark text, hover states, disabled states, can be derived directly from that single variable. If a user or a theming system changes the primary color, every variant updates automatically. That is not possible with Sass variables, because Sass compiles at build time and has no concept of runtime Custom Properties.

The second paradigm shift lies in color quality. CSS color-mix() supports modern color spaces such as oklch, display-p3 and oklab, which are perceptually more uniform than sRGB. That means mixed colors look consistent to the human eye, with no unexpected darkening or desaturation in the middle of a gradient, which can happen with sRGB mixes. The choice of color space is therefore not a technical detail but a design decision.

2. Syntax and parameters of color-mix()

CSS color-mix() has the following syntax: color-mix(in <color-space>, <color1> [percentage], <color2> [percentage]). The first parameter is always the color space specification, introduced by in. The two colors can be weighted with percentages; if none is given, 50% is assumed for each. The percentages do not have to add up to 100%: if both are under 100%, the output's alpha transparency is reduced accordingly.

The percentage indicates how much of the first color is present in the result. color-mix(in oklch, violet 80%, white) produces a color that is 80% violet and 20% white. color-mix(in oklch, violet 30%, white) produces a noticeably lighter variant with 30% violet and 70% white. The percentages are optional: without them, CSS color-mix() mixes in equal parts. color-mix(in oklch, violet, white) is identical to color-mix(in oklch, violet 50%, white 50%).

Colors in CSS color-mix() can be given in any CSS format: named colors, hex, rgb(), hsl(), oklch(), color(). The browser automatically converts them into the specified color space before mixing. That means you can mix two colors from different formats and get the result in the target color space. Particularly useful: CSS color-mix() accepts CSS Custom Properties as color arguments, which is the key to dynamic theming.

/* CSS color-mix() syntax and examples */

/* Basic 50/50 mix in oklch color space */
.example-1 {
  color: color-mix(in oklch, violet, white);
  /* -> 50% violet + 50% white, mixed in oklch */
}

/* Weighted mix: 80% brand color, 20% white (light tint) */
.example-2 {
  background: color-mix(in oklch, var(--color-brand) 80%, white);
}

/* Very light tint: 10% brand + 90% white */
.example-3 {
  background: color-mix(in oklch, var(--color-brand) 10%, white);
}

/* Dark shade: 80% brand + 20% black */
.example-4 {
  background: color-mix(in oklch, var(--color-brand) 80%, black);
}

/* Transparency from partial percentages (alpha effect) */
.example-5 {
  /* Both < 100% -> result is semi-transparent */
  background: color-mix(in srgb, blue 50%, transparent);
  /* Equivalent to blue with 50% opacity */
}

/* Mix two custom properties */
.example-6 {
  border-color: color-mix(in oklch, var(--color-primary), var(--color-secondary) 30%);
}

3. Color spaces: oklch, srgb, hsl and their differences

The color space parameter of CSS color-mix() determines how the mix is calculated, and the result can vary considerably depending on the color space. In sRGB, the RGB channels are interpolated linearly, which produces unexpected results for some color combinations: mixing yellow and blue in sRGB yields a muddy gray instead of a clean green, because sRGB is not perceptually uniform.

In hsl, hue, saturation and lightness are interpolated. That sounds intuitive, but it has a problem: lightness in HSL does not correlate with perceived brightness. An "equally light" HSL transition can look visually uneven. In oklch (Oklab Lightness Chroma Hue), lightness is modeled to be perceptually uniform, meaning a transition from one color to the next appears visually even, even when the raw values change unevenly. CSS color-mix() in oklch therefore produces the most natural color mixes.

Other color spaces supported by CSS color-mix(): oklab (like oklch but in cartesian coordinates), lab (CIE Lab), lch (CIE LCH), display-p3 (an extended color space for wide-gamut displays), a98-rgb, prophoto-rgb. For most web projects, oklch and srgb are the most relevant options: oklch for perceptually uniform mixes, srgb for full compatibility with legacy color calculations.

4. Why oklch is preferred for color-mix()

oklch has a decisive advantage over other color spaces in CSS color-mix(): even lightness perception. In practice, that means a tonal palette, from very light to very dark, produces visually evenly spaced brightness steps for the same mix ratios. That is not guaranteed with sRGB. An sRGB palette from light blue to white can visually "clump" in certain ranges, with steps that appear unequal in size.

Another advantage of oklch in CSS color-mix(): hue preservation across lightness changes. When you mix a color in oklch with white or black, the hue stays intact, only lightness and chroma change. In sRGB, hue drift can occur: a red mixed with white shifts slightly toward orange or pink, depending on the source RGB values. oklch models hue as a separate axis that is interpolated independently during mixing.

For design systems that need consistent brand colors across multiple lightness steps, CSS color-mix() in oklch is the best choice. The resulting tints and shades match what a professional color system (such as Material Design or Tailwind CSS with modern color tokens) optimizes manually for its palette design. The difference is visible in direct comparison, especially with saturated colors such as blue, green and violet.

/* oklch vs srgb: perceptual uniformity in color-mix() */

:root {
  --brand: oklch(55% 0.25 290); /* perceptual violet */
}

/* oklch mix: perceptually uniform, each step looks equally spaced */
.palette-oklch {
  --tint-90: color-mix(in oklch, var(--brand) 10%, white);
  --tint-70: color-mix(in oklch, var(--brand) 30%, white);
  --tint-50: color-mix(in oklch, var(--brand) 50%, white);
  --tint-30: color-mix(in oklch, var(--brand) 70%, white);
  --shade-70: color-mix(in oklch, var(--brand) 70%, black);
  --shade-50: color-mix(in oklch, var(--brand) 50%, black);
  --shade-30: color-mix(in oklch, var(--brand) 30%, black);
}

/* srgb mix: RGB channel interpolation, may drift visually */
.palette-srgb {
  --tint-50: color-mix(in srgb, var(--brand) 50%, white);
  /* May appear pinkish/bluish depending on the base hue */
}

/* oklab mix: similar to oklch but cartesian, good for smooth gradients */
.gradient-bg {
  background: linear-gradient(
    to right,
    color-mix(in oklab, var(--brand), black 50%),
    var(--brand),
    color-mix(in oklab, var(--brand), white 50%)
  );
}

5. Transparency and alpha mixing

An elegant application of CSS color-mix() is generating colors with transparency from opaque source values. Mixing a color with transparent produces a version of that color with a reduced alpha channel. color-mix(in srgb, blue 50%, transparent) is equivalent to rgb(0 0 255 / 0.5). This is especially valuable in combination with Custom Properties: if the base color is defined as a Custom Property, the transparent variant can be derived directly from it, without needing to know the alpha value manually.

Alpha mixing in CSS color-mix() works through the partial percentage technique: if the percentages of both colors together add up to less than 100%, the missing portion is interpreted as transparency. color-mix(in oklch, red 40%, transparent 40%) produces red at 80% opacity, because 40% plus 40% equals 80%; the missing portion (20%) makes the result 20% transparent. This behavior allows precise control over alpha values through percentage combinations.

A practical use case: overlay colors for modal backgrounds, skeleton loader animations and hover states. Instead of guessing or calculating rgba() values, you define the base color and derive the transparent variant through CSS color-mix(). If the base color changes via a Custom Property, for instance in a user-customizable theme, every transparent variant updates automatically as well.

6. Dynamic theming with Custom Properties

The most powerful application of CSS color-mix() is dynamic theming. The principle: you define a small number of core Custom Properties, the primary color, an accent color, perhaps a neutral color, and derive every other design value from them. Button hover states, backgrounds, borders, shadows: all of it as derivatives of the core Custom Properties, calculated by CSS color-mix() at runtime.

This approach has several advantages over static color palettes. First, switching a theme only requires changing the core Custom Properties; every derived color follows automatically. Second, users can supply their own colors (for example through a color picker), and the entire theme adapts harmoniously. Third, you do not need to record an explicit color value for every variant in a design document; the derivation formula is the documentation.

A complete theme architecture using CSS color-mix() typically defines 3 to 5 core colors and derives 20 to 40 variants from them. That is considerably fewer than a static design system with 40 to 80 explicit color values, and the derived variants are always harmoniously aligned, since they are all calculated from the same core colors.

/* Dynamic theming system with CSS color-mix() and Custom Properties */

:root {
  /* Core brand colors, the only values to change for a new theme */
  --brand-primary: oklch(55% 0.25 290);
  --brand-neutral: oklch(40% 0.02 290);

  /* Automatically derived palette, no manual hex values needed */
  --color-surface:   color-mix(in oklch, var(--brand-primary)  5%, white);
  --color-subtle:    color-mix(in oklch, var(--brand-primary) 12%, white);
  --color-muted:     color-mix(in oklch, var(--brand-primary) 20%, white);
  --color-border:    color-mix(in oklch, var(--brand-primary) 25%, white);
  --color-text-weak: color-mix(in oklch, var(--brand-neutral) 60%, white);
  --color-text:      var(--brand-neutral);
  --color-text-strong: color-mix(in oklch, var(--brand-neutral), black 30%);

  /* Interactive states derived from primary */
  --btn-bg:        var(--brand-primary);
  --btn-bg-hover:  color-mix(in oklch, var(--brand-primary), black 15%);
  --btn-bg-active: color-mix(in oklch, var(--brand-primary), black 30%);
  --btn-ring:      color-mix(in oklch, var(--brand-primary) 40%, transparent);

  /* Semantic colors built on the same base */
  --color-danger:  color-mix(in oklch, red, var(--brand-primary) 20%);
  --color-success: color-mix(in oklch, green, var(--brand-primary) 15%);
}

/* Changing the entire theme: one variable */
[data-theme="teal"] {
  --brand-primary: oklch(60% 0.20 195);
  /* All derived values auto-update */
}

7. Implementing dark mode with color-mix()

Dark mode is a classic application for CSS color-mix(). Instead of maintaining two separate color palettes for light and dark mode, you define one core palette and derive the dark mode variants by mixing with dark tones. This reduces inconsistencies between the modes and ensures brand colors keep the same hue in both modes, with only lightness and context changing.

An effective dark mode strategy with CSS color-mix(): in light mode, colors are set in the default :root definition. In dark mode (@media (prefers-color-scheme: dark) or [data-theme="dark"]), the Custom Properties are overridden, not with manually chosen hex values, but with CSS color-mix() derivatives of the brand colors. The background is generated by mixing the brand color with black, which ensures the dark mode background matches the brand instead of being a generic gray.

8. Color spaces compared for mixing

The choice of color space in CSS color-mix() significantly affects the visual result. Here is a direct comparison of the most important options.

Color space Strength Weakness Recommendation
oklch Perceptually uniform, hue stable Newer format, less familiar First choice for design systems
oklab Best perceptual uniformity, smooth gradients No intuitive hue angle Ideal for gradients
srgb Maximum compatibility, familiar Gray artifacts with complementary colors Fine for simple tints/shades
hsl Intuitive hue rotation Perceived brightness is uneven Useful mainly for hue rotation
display-p3 Wide gamut, more vivid on modern displays No benefit on sRGB displays For wide gamut projects

For most web projects using CSS color-mix(), oklch is the clear first choice: perceptually uniform results, stable hue preservation and good browser support since 2023. sRGB remains relevant when exact matching with previous calculations is needed, or when very simple tint/shade operations with the widest possible compatibility are required.

9. Practice: building a tonal palette with color-mix()

A tonal palette is a collection of lightness steps of a base color, from very light (near white) to very dark (near black). Design systems such as Material Design 3 and Tailwind CSS v4 use tonal palettes as the foundation for every semantic color assignment. With CSS color-mix(), a tonal palette can be calculated dynamically instead of maintaining 11 or more manually chosen hex values.

Building a tonal palette with CSS color-mix() in oklch follows a simple pattern: for tints (light variants), you mix the base color with white, with the base color's share ranging from 90% (nearly pure) to 5% (very light). For shades (dark variants), you mix with black. The result is a harmonious, perceptually uniform palette that looks more consistent under oklch mixing than manually chosen values. The key benefit for projects with multiple themes or user configurable colors: every tonal palette value follows the base color without manual intervention.

/* Complete tonal palette via CSS color-mix() in oklch */

:root {
  --base: oklch(55% 0.25 290); /* violet brand color */

  /* Tints, base mixed with white */
  --color-50:  color-mix(in oklch, var(--base)  5%, white);
  --color-100: color-mix(in oklch, var(--base) 12%, white);
  --color-200: color-mix(in oklch, var(--base) 22%, white);
  --color-300: color-mix(in oklch, var(--base) 35%, white);
  --color-400: color-mix(in oklch, var(--base) 50%, white);
  --color-500: var(--base); /* pure base */

  /* Shades, base mixed with black */
  --color-600: color-mix(in oklch, var(--base) 85%, black);
  --color-700: color-mix(in oklch, var(--base) 70%, black);
  --color-800: color-mix(in oklch, var(--base) 55%, black);
  --color-900: color-mix(in oklch, var(--base) 40%, black);
  --color-950: color-mix(in oklch, var(--base) 25%, black);
}

/* Usage in components */
.alert-info {
  background: var(--color-100);
  border: 1px solid var(--color-300);
  color: var(--color-800);
}

.btn-primary {
  background: var(--color-500);
  color: white;
}
.btn-primary:hover { background: var(--color-600); }
.btn-primary:active { background: var(--color-700); }

Mironsoft

Design systems, CSS architecture and modern color science

Want dynamic theming with CSS color-mix()?

We build complete color systems with CSS color-mix() and oklch, from tonal palettes through dark mode to user configurable themes for Magento and Hyva.

Tonal palette

A complete color palette in oklch built with color-mix(), consistent and maintainable

Theme system

Dynamic themes with Custom Properties and color-mix() for real time customization

Dark mode

Consistent dark mode without duplicate color palettes, generated automatically from the base color

10. Summary

CSS color-mix() is a native CSS function that calculates color mixes directly in the browser, without preprocessors or JavaScript. The choice of color space, especially oklch, determines the visual quality of the mix. oklch produces perceptually uniform results with stable hue preservation, which makes it the first choice for design systems and tonal palettes. Combined with CSS Custom Properties, it enables dynamic themes that react to user preferences and JavaScript variables.

The key use cases: deriving tonal palettes from a single base color. Generating transparent color variants for overlays and shadows. Calculating interactive states (hover, active, disabled) as derivatives of the primary color. Implementing dark mode without duplicate color palettes. And building user configurable themes where every design value is derived harmoniously from a small set of core colors. CSS color-mix() is not a toy feature, it is a serious tool for professional CSS architecture.

CSS color-mix(): the essentials at a glance

Syntax

color-mix(in oklch, color1 [%], color2 [%]): color space first, then two colors with optional percentages.

Prefer oklch

Perceptually uniform, hue stable. Produces more natural tints and shades than sRGB or HSL.

Custom Properties

color-mix() accepts Custom Properties as color arguments, the key to dynamic theming without a build step.

Transparency

Mix with transparent or use partial percentages under 100% to create alpha variants from opaque base colors.

11. FAQ: CSS color-mix(), mixing colors dynamically

1What does CSS color-mix() do?
Mixes two colors in the browser at runtime in a defined color space. No build step, no preprocessors, works with Custom Properties.
2Why oklch instead of srgb?
Perceptually uniform, hue stable. sRGB can produce gray artifacts with complementary colors and the hue drifts while mixing.
3color-mix() with Custom Properties?
Yes: color-mix(in oklch, var(--brand) 80%, white) works. If --brand changes, every derivative updates automatically, the core of dynamic theming.
4Transparency with color-mix()?
Mix with transparent: color-mix(in srgb, blue 50%, transparent) equals blue at 50% opacity. Or use partial percentages under 100% for an alpha effect.
5Browser support?
Chrome 111+, Firefox 113+, Safari 16.2+. About 90% global support. A fallback with fixed hex values or @supports is recommended.
6color-mix() vs. Sass mix()?
Sass compiles at build time and has no concept of Custom Properties. color-mix() runs in the browser, essential for dynamic theming.
7A full palette with color-mix()?
Tints: base color at 5% to 90% plus white. Shades: base color plus black. Every value follows the base color, so the whole palette updates from one variable.
8Dark mode with color-mix()?
Override the core variables in dark mode. Surfaces as color-mix(in oklch, var(--brand), black X%), keeping the hue on brand even in the dark theme.
9What if the percentages do not add up to 100%?
The missing portion is interpreted as transparency. 40% plus 40% equals 80% opacity, 20% transparent. Precise alpha control through the percentage sum.
10Best color space for gradients?
oklab or oklch. CSS gradients support this directly: linear-gradient(in oklch, red, blue), no color-mix() needed for gradients.