0Support

CSS color-mix() playground

Explore color-mix() by adjusting the color space, two or more colors, and their percentages. In polar spaces such as hsl, hwb, lch, and oklch you can also change hue interpolation and see the browser-resolved color in the generated code.

Read the guide

Ask AI

CSS color-mix() playground

Response generated with openai/gpt-5.4-nano. AI can make mistakes. Always review the result.
color-mix()
  1. 1
  2. 2
HTML
<div class="color-mix"></div>
Tailwind CSS
Some classes with arbitrary values can be replaced by native classes, depending on the value and your Tailwind CSS configuration.
<div class="inline-[250px] block-[250px] bg-[color-mix(in_srgb,_#ff7a8a_50%,_#6fd6ff_50%)]"></div>
CSS
.color-mix {
  inline-size: 250px;
  block-size: 250px;
  background-color: color-mix(in srgb, #ff7a8a 50%, #6fd6ff 50%);
}

Educational guide to color-mix()

Mix colors with intent

color-mix() computes a new color from other colors. The interpolation space, weights, and alpha determine the result; the background it is painted on determines its final appearance.

Back to the playground

Mental model

First the color is computed; then it is composited over the background.

  1. Colors and weights

    Two colors at 25% and 75% contribute different amounts. Their alpha also participates.

  2. Space and path

    Colors are converted to the chosen space. In a polar space, hue follows an angular path.

  3. Color and background

    The result may be transparent. When painted, it combines with the actual background.

From arguments to painted colorStatic diagram of a two-color mix. The swatches in the following sections use native CSS; this diagram explains the process.

A value, not a layer effect

color-mix() returns a color. mix-blend-mode blends an element with its backdrop using blend modes; the operations are not interchangeable.

Check contrast at the end

Perceptual mixing does not guarantee readable text. Check the composited color against its background in every theme and state.

Use the result wherever CSS accepts a color: backgrounds, text, borders, or shadows. It does not create a gradient or blend pixels from two elements. Inputs can be hex, rgb(), hsl(), oklch(), currentColor, or variables; their notation does not set the interpolation space.

Interpolation spaces

The same colors and weights can produce different results.

sRGB interpolates encoded channels; srgb-linear works in linear light. Oklab aims for a more perceptually uniform distribution. Oklch separates lightness, chroma, and hue: the angular path may preserve more chroma, but can also produce colors outside the display gamut.

Two sources, four spaces, the same 50/50

srgb · 50% / 50%

This expression is unsupported: its computed color is not shown.

srgb-linear · 50% / 50%

This expression is unsupported: its computed color is not shown.

oklab · 50% / 50%

This expression is unsupported: its computed color is not shown.

oklch · 50% / 50%

This expression is unsupported: its computed color is not shown.

The sources are #ff7a8a and #6fd6ff in every swatch. Only the labeled space changes; none of the swatches promises sufficient contrast.
Two sources, four spaces, the same 50/50
<div class="mix-guide-spaces">
  <div class="srgb">
    <p>srgb · 50% / 50%</p>
    <div class="stage" aria-hidden="true"><span class="paint"></span></div>
    <p class="unsupported">This expression is unsupported: its computed color is not shown.</p>
  </div>
  <div class="srgb-linear">
    <p>srgb-linear · 50% / 50%</p>
    <div class="stage" aria-hidden="true"><span class="paint"></span></div>
    <p class="unsupported">This expression is unsupported: its computed color is not shown.</p>
  </div>
  <div class="oklab">
    <p>oklab · 50% / 50%</p>
    <div class="stage" aria-hidden="true"><span class="paint"></span></div>
    <p class="unsupported">This expression is unsupported: its computed color is not shown.</p>
  </div>
  <div class="oklch">
    <p>oklch · 50% / 50%</p>
    <div class="stage" aria-hidden="true"><span class="paint"></span></div>
    <p class="unsupported">This expression is unsupported: its computed color is not shown.</p>
  </div>
</div>
:root {
  color-scheme: light dark;
}

.mix-guide-spaces {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;

  > div {
    flex: 1 1 12rem;
    min-inline-size: 0;
    display: grid;
    align-content: start;
    gap: 0.5rem;
  }

  p {
    margin: 0;
    color: light-dark(oklch(0.22 0.01 95), oklch(0.95 0.01 95));
    font: 1rem/1.6 system-ui, sans-serif;
    overflow-wrap: anywhere;
  }

  .stage {
    min-block-size: 5rem;
    display: grid;
    border: 1px solid light-dark(oklch(0.84 0.01 95), oklch(0.36 0.024 260));
    background: repeating-conic-gradient(#fff 0 25%, #172554 0 50%) 0 / 1.5rem 1.5rem;
  }

  .paint {
    display: none;
  }

  @supports (background-color: color-mix(in srgb, #ff7a8a 50%, #6fd6ff 50%)) {
    .srgb .paint {
      display: block;
      background-color: color-mix(in srgb, #ff7a8a 50%, #6fd6ff 50%);
    }

    .srgb .unsupported {
      display: none;
    }
  }

  @supports (background-color: color-mix(in srgb-linear, #ff7a8a 50%, #6fd6ff 50%)) {
    .srgb-linear .paint {
      display: block;
      background-color: color-mix(in srgb-linear, #ff7a8a 50%, #6fd6ff 50%);
    }

    .srgb-linear .unsupported {
      display: none;
    }
  }

  @supports (background-color: color-mix(in oklab, #ff7a8a 50%, #6fd6ff 50%)) {
    .oklab .paint {
      display: block;
      background-color: color-mix(in oklab, #ff7a8a 50%, #6fd6ff 50%);
    }

    .oklab .unsupported {
      display: none;
    }
  }

  @supports (background-color: color-mix(in oklch shorter hue, #ff7a8a 50%, #6fd6ff 50%)) {
    .oklch .paint {
      display: block;
      background-color: color-mix(in oklch shorter hue, #ff7a8a 50%, #6fd6ff 50%);
    }

    .oklch .unsupported {
      display: none;
    }
  }

  @media (forced-colors: active) {
    .stage {
      border: 1px solid CanvasText;
      background: Canvas;
    }
  }
}

Choose by intent: srgb to reproduce an existing interpolation, srgb-linear for light, oklab for perceptual steps, and oklch to control hue. Lab/LCH and XYZ are also available in the lab. Write in <space> explicitly for readability and compatibility; omitting it is a newer variant.

Percentages and normalization

A color’s weight is not that color’s alpha.

With two colors, omitting both percentages gives 50/50; omitting one completes the total to 100%. When both are supplied and total more than 100%, they are normalized: 80% + 80% equals 50% + 50%. Below 100%, the weights are normalized and the total also reduces the final alpha.

Equal proportions, different opacity

50% + 50%

This expression is unsupported: its computed color is not shown.

80% + 80%

This expression is unsupported: its computed color is not shown.

20% + 20%

This expression is unsupported: its computed color is not shown.

25% + 75%

This expression is unsupported: its computed color is not shown.

50/50 and 80/80 give the same opaque color. 20/20 keeps the 50/50 proportion with alpha 0.4; 25/75 changes the proportion. The pattern reveals transparency.
Equal proportions, different opacity
<div class="mix-guide-weights">
  <div class="equal">
    <p>50% + 50%</p>
    <div class="stage" aria-hidden="true"><span class="paint"></span></div>
    <p class="unsupported">This expression is unsupported: its computed color is not shown.</p>
  </div>
  <div class="above">
    <p>80% + 80%</p>
    <div class="stage" aria-hidden="true"><span class="paint"></span></div>
    <p class="unsupported">This expression is unsupported: its computed color is not shown.</p>
  </div>
  <div class="below">
    <p>20% + 20%</p>
    <div class="stage" aria-hidden="true"><span class="paint"></span></div>
    <p class="unsupported">This expression is unsupported: its computed color is not shown.</p>
  </div>
  <div class="weighted">
    <p>25% + 75%</p>
    <div class="stage" aria-hidden="true"><span class="paint"></span></div>
    <p class="unsupported">This expression is unsupported: its computed color is not shown.</p>
  </div>
</div>
:root {
  color-scheme: light dark;
}

.mix-guide-weights {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;

  > div {
    flex: 1 1 12rem;
    min-inline-size: 0;
    display: grid;
    align-content: start;
    gap: 0.5rem;
  }

  p {
    margin: 0;
    color: light-dark(oklch(0.22 0.01 95), oklch(0.95 0.01 95));
    font: 1rem/1.6 system-ui, sans-serif;
    overflow-wrap: anywhere;
  }

  .stage {
    min-block-size: 5rem;
    display: grid;
    border: 1px solid light-dark(oklch(0.84 0.01 95), oklch(0.36 0.024 260));
    background: repeating-conic-gradient(#fff 0 25%, #172554 0 50%) 0 / 1.5rem 1.5rem;
  }

  .paint {
    display: none;
  }

  @supports (background-color: color-mix(in oklab, #ff7a8a 50%, #6fd6ff 50%)) {
    .equal .paint {
      display: block;
      background-color: color-mix(in oklab, #ff7a8a 50%, #6fd6ff 50%);
    }

    .equal .unsupported {
      display: none;
    }
  }

  @supports (background-color: color-mix(in oklab, #ff7a8a 80%, #6fd6ff 80%)) {
    .above .paint {
      display: block;
      background-color: color-mix(in oklab, #ff7a8a 80%, #6fd6ff 80%);
    }

    .above .unsupported {
      display: none;
    }
  }

  @supports (background-color: color-mix(in oklab, #ff7a8a 20%, #6fd6ff 20%)) {
    .below .paint {
      display: block;
      background-color: color-mix(in oklab, #ff7a8a 20%, #6fd6ff 20%);
    }

    .below .unsupported {
      display: none;
    }
  }

  @supports (background-color: color-mix(in oklab, #ff7a8a 25%, #6fd6ff 75%)) {
    .weighted .paint {
      display: block;
      background-color: color-mix(in oklab, #ff7a8a 25%, #6fd6ff 75%);
    }

    .weighted .unsupported {
      display: none;
    }
  }

  @media (forced-colors: active) {
    .stage {
      border: 1px solid CanvasText;
      background: Canvas;
    }
  }
}

Both omitted → 50% / 50%

color-mix(in oklab, #ff7a8a, #6fd6ff)

One omitted → 25% / 75%

color-mix(in oklab, #ff7a8a 25%, #6fd6ff)

For two opaque colors and nonzero total S: normalized weight = percentage / S; final alpha = min(S / 100, 1). With semitransparent inputs, this factor multiplies the interpolated alpha. Use values from 0% to 100% and a positive total: the all-zero case differs across documentation, the proposal, and engines.

Transparency and compositing

Mixing with white, mixing with transparent, and using opacity solve different problems.

White lightens the color and can keep it opaque. transparent reduces alpha: an opaque source at 50% produces alpha 0.5 and reveals the background. opacity: 0.5 affects the whole element, including its text and descendants; a semitransparent background-color does not reduce text alpha.

White versus transparent over two backgrounds

Source 50% + white 50% · alpha 1

This expression is unsupported: its computed color is not shown.

Source 50% + transparent 50% · alpha 0.5

This expression is unsupported: its computed color is not shown.

Source with alpha 0.4 × 50% · alpha 0.2

This expression is unsupported: its computed color is not shown.

The mix with white is opaque. The mix with transparent and the source with alpha 0.4 reveal both background halves; mixing the latter at 50% with transparent gives alpha 0.2. Labels stay outside the swatches.
White versus transparent over two backgrounds
<div class="mix-guide-transparency">
  <div class="white">
    <p>Source 50% + white 50% · alpha 1</p>
    <div class="stage" aria-hidden="true"><span class="paint"></span></div>
    <p class="unsupported">This expression is unsupported: its computed color is not shown.</p>
  </div>
  <div class="transparent">
    <p>Source 50% + transparent 50% · alpha 0.5</p>
    <div class="stage" aria-hidden="true"><span class="paint"></span></div>
    <p class="unsupported">This expression is unsupported: its computed color is not shown.</p>
  </div>
  <div class="existing-alpha">
    <p>Source with alpha 0.4 × 50% · alpha 0.2</p>
    <div class="stage" aria-hidden="true"><span class="paint"></span></div>
    <p class="unsupported">This expression is unsupported: its computed color is not shown.</p>
  </div>
</div>
:root {
  color-scheme: light dark;
}

.mix-guide-transparency {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;

  > div {
    flex: 1 1 12rem;
    min-inline-size: 0;
    display: grid;
    align-content: start;
    gap: 0.5rem;
  }

  p {
    margin: 0;
    color: light-dark(oklch(0.22 0.01 95), oklch(0.95 0.01 95));
    font: 1rem/1.6 system-ui, sans-serif;
    overflow-wrap: anywhere;
  }

  .stage {
    min-block-size: 5rem;
    display: grid;
    border: 1px solid light-dark(oklch(0.84 0.01 95), oklch(0.36 0.024 260));
    background: linear-gradient(90deg, #fff 50%, #172554 0);
  }

  .paint {
    display: none;
  }

  @supports (background-color: color-mix(in oklab, #ff7a8a 50%, white 50%)) {
    .white .paint {
      display: block;
      background-color: color-mix(in oklab, #ff7a8a 50%, white 50%);
    }

    .white .unsupported {
      display: none;
    }
  }

  @supports (background-color: color-mix(in oklab, #ff7a8a 50%, transparent 50%)) {
    .transparent .paint {
      display: block;
      background-color: color-mix(in oklab, #ff7a8a 50%, transparent 50%);
    }

    .transparent .unsupported {
      display: none;
    }
  }

  @supports (background-color: color-mix(in oklab, rgb(255 122 138 / 0.4) 50%, transparent 50%)) {
    .existing-alpha .paint {
      display: block;
      background-color: color-mix(in oklab, rgb(255 122 138 / 0.4) 50%, transparent 50%);
    }

    .existing-alpha .unsupported {
      display: none;
    }
  }

  @media (forced-colors: active) {
    .stage {
      border: 1px solid CanvasText;
      background: Canvas;
    }
  }
}

CSS interpolates with premultiplied alpha: transparent does not simply add black to the channels. Mixing a source with itself or with transparent does not restore its lost opacity. To set or change channels and alpha, consider relative colors or alpha() with their specific support.

Hue path

In polar spaces, hue is an angle rather than a linear channel.

hsl, hwb, lch, and oklch accept shorter hue, longer hue, increasing hue, and decreasing hue. shorter is the default and takes the shorter arc. longer takes the longer arc; increasing and decreasing follow rising or falling angles from the first color to the second. A rectangular space such as oklab does not accept this modifier.

From 30° to 300° with equal lightness and chroma

shorter hue · 345°

This expression is unsupported: its computed color is not shown.

longer hue · 165°

This expression is unsupported: its computed color is not shown.

increasing hue · 165°

This expression is unsupported: its computed color is not shown.

decreasing hue · 345°

This expression is unsupported: its computed color is not shown.

shorter/decreasing pass through 345°; longer/increasing pass through 165°. L = 65%, C = 0.15, and weights 50/50 stay fixed. Text identifies the path as well as color.
From 30° to 300° with equal lightness and chroma
<div class="mix-guide-hue">
  <div class="shorter">
    <p>shorter hue · 345°</p>
    <div class="stage" aria-hidden="true"><span class="paint"></span></div>
    <p class="unsupported">This expression is unsupported: its computed color is not shown.</p>
  </div>
  <div class="longer">
    <p>longer hue · 165°</p>
    <div class="stage" aria-hidden="true"><span class="paint"></span></div>
    <p class="unsupported">This expression is unsupported: its computed color is not shown.</p>
  </div>
  <div class="increasing">
    <p>increasing hue · 165°</p>
    <div class="stage" aria-hidden="true"><span class="paint"></span></div>
    <p class="unsupported">This expression is unsupported: its computed color is not shown.</p>
  </div>
  <div class="decreasing">
    <p>decreasing hue · 345°</p>
    <div class="stage" aria-hidden="true"><span class="paint"></span></div>
    <p class="unsupported">This expression is unsupported: its computed color is not shown.</p>
  </div>
</div>
:root {
  color-scheme: light dark;
}

.mix-guide-hue {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;

  > div {
    flex: 1 1 12rem;
    min-inline-size: 0;
    display: grid;
    align-content: start;
    gap: 0.5rem;
  }

  p {
    margin: 0;
    color: light-dark(oklch(0.22 0.01 95), oklch(0.95 0.01 95));
    font: 1rem/1.6 system-ui, sans-serif;
    overflow-wrap: anywhere;
  }

  .stage {
    min-block-size: 5rem;
    display: grid;
    border: 1px solid light-dark(oklch(0.84 0.01 95), oklch(0.36 0.024 260));
    background: repeating-conic-gradient(#fff 0 25%, #172554 0 50%) 0 / 1.5rem 1.5rem;
  }

  .paint {
    display: none;
  }

  @supports (background-color: color-mix(in oklch shorter hue, oklch(65% 0.15 30) 50%, oklch(65% 0.15 300) 50%)) {
    .shorter .paint {
      display: block;
      background-color: color-mix(in oklch shorter hue, oklch(65% 0.15 30) 50%, oklch(65% 0.15 300) 50%);
    }

    .shorter .unsupported {
      display: none;
    }
  }

  @supports (background-color: color-mix(in oklch longer hue, oklch(65% 0.15 30) 50%, oklch(65% 0.15 300) 50%)) {
    .longer .paint {
      display: block;
      background-color: color-mix(in oklch longer hue, oklch(65% 0.15 30) 50%, oklch(65% 0.15 300) 50%);
    }

    .longer .unsupported {
      display: none;
    }
  }

  @supports (background-color: color-mix(in oklch increasing hue, oklch(65% 0.15 30) 50%, oklch(65% 0.15 300) 50%)) {
    .increasing .paint {
      display: block;
      background-color: color-mix(in oklch increasing hue, oklch(65% 0.15 30) 50%, oklch(65% 0.15 300) 50%);
    }

    .increasing .unsupported {
      display: none;
    }
  }

  @supports (background-color: color-mix(in oklch decreasing hue, oklch(65% 0.15 30) 50%, oklch(65% 0.15 300) 50%)) {
    .decreasing .paint {
      display: block;
      background-color: color-mix(in oklch decreasing hue, oklch(65% 0.15 30) 50%, oklch(65% 0.15 300) 50%);
    }

    .decreasing .unsupported {
      display: none;
    }
  }

  @media (forced-colors: active) {
    .stage {
      border: 1px solid CanvasText;
      background: Canvas;
    }
  }
}

These matches are specific to the angles in this example. Colors with missing hue or no chroma have special rules; do not infer a useful path by mixing white or gray. The choice of space remains independent of the notation used for the sources.

Reusable variants of a color

Derive a tint and a shade without duplicating the base color.

A custom property holds the source and mixes derive variants. This example uses Oklab, white for the background, and black for the border. Text has its own explicit color; it is not inferred automatically from the brand.

A card with a base and progressive enhancement

One source, two variants, and independent text.

The background is an opaque 15% tint and the border a 70% shade. Without support for the expression, explicit base colors remain. The card does not use opacity and keeps its text opaque.
A card with a base and progressive enhancement
<div class="mix-guide-card">
  <p>One source, two variants, and independent text.</p>
</div>
.mix-guide-card {
  --base: #ff7a8a;
  padding: 1rem;
  border: 2px solid #7c3945;
  border-radius: 16px;
  corner-shape: squircle;
  color: #172554;
  background-color: #fff0f2;
  font: 1rem/1.6 system-ui, sans-serif;
  overflow-wrap: anywhere;

  p {
    margin: 0;
  }

  @supports (background-color: color-mix(in oklab, red, white)) {
    background-color: color-mix(in oklab, var(--base) 15%, white 85%);
    border-color: color-mix(in oklab, var(--base) 70%, black 30%);
  }

  @media (forced-colors: active) {
    color: CanvasText;
    background-color: Canvas;
    border-color: CanvasText;
  }
}

Declare the fallback outside variables containing modern functions; @supports enables the full enhancement. A variable can accept text even when using it as a color fails at computed-value time. For theme variants, combine this idea with light-dark(); for a translucent layer, mix with transparent and check the actual background again.

Color lists in the current lab

Add color uses the variant with three or more colors, which has independent support. In rectangular spaces, list mixing does not depend on order; in polar spaces, the accumulated path may depend on it. Do not replace a list with arbitrary nested mixes: weights and results may change.

Common mistakes

Diagnose the expression and its context before adjusting colors at random.

The mix looks washed out over a pattern

Expected result
An opaque mix between the two sources.
Cause
The explicit percentages total less than 100% and reduce alpha.
Solution
Use a total of 100%, or accept transparency and test over the actual background.

The intermediate hue is unexpected

Expected result
A hue between the sources along the chosen path.
Cause
The space changed or the default shorter arc is being used.
Solution
Set the space and, if polar, the hue method. Compare identical sources and weights.

Text becomes transparent too

Expected result
A translucent background with opaque text.
Cause
opacity was applied to the entire container.
Solution
Reduce background color alpha with transparent and keep the text color.

The base color disappears when using a variable

Expected result
A fallback color when modern syntax is unavailable.
Cause
The variable exists, but its value is invalid as a color at computed-value time.
Solution
Declare the base and assign the mix inside @supports using the exact expression.

A soft tint becomes unreadable

Expected result
Text and states with sufficient contrast.
Cause
A perceptual space or fixed percentage was assumed to guarantee contrast.
Solution
Measure the composited result over each background and choose text and states separately.

Support and compatibility

The classic function and its extensions do not necessarily share availability.

Explicit two-color mixing is Baseline Widely Available. Color lists and omitting the interpolation space are extensions with their own data: the base function’s card does not certify them. Also check support for each color function used as an input.

The swatches test specific expressions with @supports and show a notice if they cannot be painted; they do not simulate the result with JavaScript. CSS.supports checks syntax acceptance in the current engine without certifying gamut, visual fidelity, contrast, or other browsers’ behavior.

For applications that must support older engines, keep an explicit color and enhance it with @supports. Labels and explanations remain available without JavaScript and in forced colors; swatches may adopt system colors in that mode.