CSS `color-mix()` Function

The CSS color-mix() function is like an artist’s palette for mixing colours. If you’ve been to a sip-and-paint event, a palette is that little flat plate you’re given to mix your paints in. That’s what the color-mix() function does, but unlike a real-life palette that allows you to merge any amount of different paints, the function is restricted to only two colors in a selected color space.

    .color-1 {
      background-color: red;
    }

    .color-2 {
      background-color: blue;
    }

    .result {
      background-color: color-mix(in srgb, red 50%, blue 50%)
    }

The color-mix() function is defined in the CSS Color Module Level 5 specification.

Syntax

The syntax for the color-mix() function is defined below:

    color-mix() = color-mix( <color-interpolation-method> , [ <color> && <percentage [0,100]>? ]#)

In use, this is what it looks like:

    element {
      color: color-mix(in lch, red 50%, blue 50%);
    }

Arguments

The color-mix() function takes 3 arguments: the <color-interpolation-method>, the 2 colors, and their respective percentages, which can be optional. In cases where the percentage of any of the two colors or both is not specified, the browser subtracts the percentage value of the specified color from 100 and uses it as the default percentage value for the second color. If both percentage values are not specified, it uses 50% for each, because the sum of the respective colors’ percentage values must be 100.

So, what happens if a user specifies 50% for color 1, and 70% for color 2? The function sums the respective percentage value, then divide each color value by the sum and multiply the result by 100 to get a new percentage value.

    color-1 = 50%
    color-2 = 70%

    color-1% + color-2% = 120

    color-1 new % value = 50/120 * 100 = approx. 41.67%
    color-2 new % value = 70/120 * 100 = approx. 58.33%

If you use these new % values for each color, you’ll get the same color result.

The <color-interpolation-method> decides the color space to be adopted in mixing the two colors, and is the color space in which the result is returned. It specifies whether the color interpolation should use a rectangular color space, a polar color space with an optional <hue-interpolation-method>, or a custom color space. The rectangular color space includes: srgb, srgb-linear, display-p3, a98-rgb, prophoto-rgb, rec2020, lab, oklab, xyz, xyz-d50, or xyz-d65, while the polar color space includes: hsl, hwb, lch, or oklch.

    <color-interpolation-method> = in [ <rectangular-color-space> | <polar-color-space> <hue-interpolation-method>? | <custom-color-space> ]

    <hue-interpolation-method> = [ shorter | longer | increasing | decreasing ] hue

    <rectangular-color-space> = srgb | srgb-linear | display-p3 | a98-rgb | prophoto-rgb | rec2020 | lab | oklab | xyz | xyz-d50 | xyz-d65

    <polar-color-space> = hsl | hwb | lch | oklch

    <custom-color-space> = <dashed-ident>

The second and third arguments are the colors and their respective percentages. The colors can be defined in any valid CSS color notation–it could even be a color-mix() function–while their percentages span 0 to 100%.

    <color> = <color-base> | currentColor | <system-color> | 
          <contrast-color()> | <device-cmyk()>  | <light-dark()>

    <color-base> = <hex-color> | <color-function> | <named-color> | <color-mix()> | transparent
    <color-function> = <rgb()> | <rgba()> |
                  <hsl()> | <hsla()> | <hwb()> |
                  <lab()> | <lch()> | <oklab()> | <oklch()> |
                  <color()>

Examples

    /* color-mix(in <polar-color-space>, <color>, <color> <percentage>) */
    element {
      color: color-mix(in hsl, hsl(200 50 80), coral 80%);
    }

    /* color-mix(in <polar-color-space> <hue-interpolation-method>, <color>, <color>) */
    element {
      background-color: color-mix(in lch longer hue, hsl(200deg 50% 80%), coral);
    }

    /* color-mix(in <rectangular-color-space>, <color>, <color>) */
    element {
      color: color-mix(in srgb, plum, #f00);
    }

    /* color-mix(in <rectangular-color-space>, <color> <percentage>, <color> <percentage> */
    element {
      background: color-mix(in lab, plum 60%, #f00 50%);
    }

    /* color-mix(in <custom-color-space>, <color>, <color>) */
    @color-profile --swop5c {
      src: url("https://example.org/SWOP2006_Coated5v2.icc");
    }

    element {
      color: color-mix(in --swop5c, red, blue);
    }

How it works

The color-mix() function converts the hexcode to RGB format, #4a90e2rgb(74, 144, 226), and does the calculation thus:

For --lighter: 70% of rgb(74, 144, 226) (your base) and 30% of rgb(255, 255, 255) (white);
For --darker: 70% of rgb(74, 144, 226) (your base) and 30% of rgb(0, 0, 0) (black);

    R = base.R * 0.7 + white.R/black.R * 0.3
    G = base.G * 0.7 + white.G/black.G * 0.3
    B = base.B * 0.7 + white.B/black.B * 0.3

    then, for --lighter:
    R = 74  * 0.7 + 255 * 0.3 = 51.8 + 76.5  = 128.3
    G = 144 * 0.7 + 255 * 0.3 = 100.8 + 76.5 = 177.3
    B = 226 * 0.7 + 255 * 0.3 = 158.2 + 76.5 = 234.7

    and, for --darker:
    R = 74  * 0.7 + 0 * 0.3 = 51.8 + 0  = 51.8
    G = 144 * 0.7 + 0 * 0.3 = 100.8 + 0 = 100.8
    B = 226 * 0.7 + 0 * 0.3 = 158.2 + 0 = 158.2

    results:
    --lighter: rgb(128, 177, 235); --darker: rgb(51.8, 100.8, 158.2)

If you replace any of the RGB values with the results in the color and background-color, you will see no change in the color variation, because they are the same color expressed in the same color space.

Basic usage

One of the simplest features you can achieve with the color-mix() function is a dynamic theme for your website. You can easily create light and dark variants or accent shades of your base color by blending with white or black.

    :root {
      --base: #4a90e2;
      --lighter: color-mix(in srgb, var(--base) 70%, white);
      --darker: color-mix(in srgb, var(--base) 70%, black);
    }

It doesn’t end there. You can add a little bit of alpha to your --base-color by mixing it with the color transparent or white value.

    element {
      color: color-mix(in srgb, var(--base) 70%, transparent);
    }

This is equivalent to the rgba() equivalent of --base with an alpha of 70%

    element {
      color: rgba(74, 144, 226, 70%)
    }

The first div is the white variant, the second div is the dark variant, the third uses rgba(), and the last is the transparent variant. As you can see, the first, third, and fourth all look the same.

Color scheme with color-mix()

OKLCH and OKLAB are two interesting color spaces because they offer colors that feel natural to the human eye. They were developed to be perceptually uniform, i.e., to match how the human eye perceive the colors in their environment. OKLCH and OKLAB behave similarly, but their differences lie in their respective orientations. OKLAB lies within the rectangular (or Cartesian) color space, and its colors are defined using x, y, and z coordinates. OKLCH, on the other hand, exists in the polar or cylindrical color space, and its colors are plotted on a cylinder. It is the polar transformation of OKLAB, just as hsl is to rgb. While they have more colors than their hsl and sRGB predecessors, each shine in its respective ways.

One of the several ways OKLCH particularly stands out is when you need to generate a color scheme with adjustable saturation. In generating a color scheme, you need variants of the same color and other colors that work together, and that is where color-mix() comes in.

    :root {
      /* OKLCH Scheme */
      --base: oklch(0.7 0.2 250);

      --primary: var(--base);
      --primary-light: color-mix(in oklch, var(--base) 60%, white);
      --primary-dark: color-mix(in oklch, var(--base) 70%, black);

      --accent: color-mix(in oklch, var(--base) 60%, oklch(0.7 0.3 90));
      --muted: color-mix(in oklch, var(--base) 60%, oklch(0.6 0.1 250));
      --subtle-bg: color-mix(in oklch, white 85%, var(--base));
      --border: color-mix(in oklch, black 10%, var(--base));
      --card-bg: color-mix(in oklch, white 60%, var(--base));
    }

Here, we have a base color mixed with white and black of different percentages to generate a light and dark variant of the base, and other color mixtures for the --accent and --muted custom variables.

If it looks ugly, you’ll have to pardon me. I’m not very good at designs. 😔

Generating gradients with color-mix()

I mentioned one of the strengths of OKLCH in the previous section, but what about OKLAB? OKLAB performs well when you need precise color maths for interpolation and when designing gradients.

Interpolation + gradient = color-mix(in oklab, oklab(L A B), oklab(L A B))

    :root {
      --base: oklch(0.7 0.2 250);
       --primary: var(--base);
      ...
      /* OKLAB Gradient */
      --button-hover-gradient: linear-gradient(
        to right,
        color-mix(in oklab, oklab(0.75 -0.1 -0.2) 100%, oklab(0.7 0.25 0.15) 0%),
        color-mix(in oklab, oklab(0.75 -0.1 -0.2) 75%, oklab(0.7 0.25 0.15) 25%),
        color-mix(in oklab, oklab(0.75 -0.1 -0.2) 50%, oklab(0.7 0.25 0.15) 50%),
        color-mix(in oklab, oklab(0.75 -0.1 -0.2) 25%, oklab(0.7 0.25 0.15) 75%),
        color-mix(in oklab, oklab(0.75 -0.1 -0.2) 0%, oklab(0.7 0.25 0.15) 100%)
      );
    }

    .cta-button {
      font-size: 1rem;
      padding: 0.75rem 2rem;
      border: none;
      border-radius: 8px;
      background: var(--primary);
      color: white;
      cursor: pointer;
    }

    .cta-button:hover {
      background: var(--button-hover-gradient);
    }

It’s the “Get Started” button

Browser support

The CSS color-mix() function is supported on all browsers.

References

Leave a Reply