Text reading Creating color palettes with the CSS color-mix() function. A vibrant gradient behind the artwork of CSS in the top right corner and a graphic of a website with a color palette in the bottom left corner.

Creating color palettes with the CSS color-mix() function

Author avatarMichelle Barker8 minute read

Colors can sometimes get out of hand in a project. We often start with a few well-chosen brand colors, but over time, we may find ourselves adding variations as our project grows. Perhaps we realize that we need to adjust the lightness of a button color for accessibility reasons, or that we need a slightly different variant of a component. How do we ensure that the colors we choose fit within the design system for our project?

I've been exploring using the relatively new CSS color-mix() function for this purpose. It's been fun to see the different palette variations I could generate! Let's dive into how color-mix() can be a game-changer for your design process.

Getting familiar with the color-mix() function

The color-mix() function allows us to specify the two colors we want to mix and then outputs the result. We can control the amount of each color in the mix, as well as the color interpolation space, which determines how the colors blend together.

A graphic showing the components of a color-mix() function, namely an interpolation method and two colors along with their percentages

The color interpolation method is a required parameter. We'll cover it in a later section. For now, we'll use srgb to walk through the examples.

We specify the amount of each color as percentages. If we omit the percentages of both colors, color-mix() will use 50% for each by default. As shown below, mixing red and blue in equal parts gives us a purple hue as expected.

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

If we specify the percentage for only one color, the percentage for the other color is automatically adjusted so that the total adds up to 100%. For example, whether we specify 90% for blue or 10% for red, the result is the same — a color that's mostly blue with a hint of red.

css
/* Both these will produce the same resultant color */
color-mix(in srgb, blue 90%, red)
color-mix(in srgb, blue, red 10%)

If the sum of the percentages for the two colors is less than 100%, the color-mix() behavior is slightly different: the sum is saved as an alpha multiplier, and the two colors are scaled using this multiplier so that their total reaches 100%. (See the Percentage Normalization section in the specification for a few examples).

Although both the color-mix() functions below mix the same amount of each color, the second function, where the sum of the percentages is 40%, produces the same color but with an alpha value of 0.4:

css
/* Result: rgb(128 0 128) */
color-mix(in srgb, blue, red)

/* Result: rgb(128 0 128 / 0.4) */
color-mix(in srgb, blue 20%, red 20%)

Creating light and dark variations with color-mix()

As a typical use case, we often need to create lighter or darker variations of a brand color. To achieve this, we can mix white or black into our base color in varying amounts with color-mix().

The example below demonstrates how different amounts of white and black are mixed with the base color blue to create its lighter and darker variations, showcasing the use of color-mix() in adjusting base color intensity.

css
/* Initial base color */
.bg-blue {
  background-color: blue;
}

/* 50% blue, 50% white */
.bg-blue-light {
  background-color: color-mix(in srgb, blue, white);
}

/* 25% blue, 75% white */
.bg-blue-lighter {
  background-color: color-mix(in srgb, blue, white 75%);
}

/* 50% blue, 50% black */
.bg-blue-dark {
  background-color: color-mix(in srgb, blue, black);
}

/* 25% blue, 75% black */
.bg-blue-darker {
  background-color: color-mix(in srgb, blue, black 75%);
}

Using custom properties to reuse the color variations

By storing the color-mix() values as custom properties, we can reuse them throughout our code. This approach can be useful when we want to create lighter or darker variants of a brand's primary color.

As an example, the code below illustrates how to create brand color variations using the --brand custom property.

css
:root {
  --brand: rgb(0 0 255);

  --brand-light: color-mix(in srgb, var(--brand), white);
  --brand-lighter: color-mix(in srgb, var(--brand), white 75%);
  --brand-dark: color-mix(in srgb, var(--brand), black);
  --brand-darker: color-mix(in srgb, var(--brand), black 75%);
}

We can also create variants of different opacities by mixing in transparent:

css
:root {
  --brand: rgb(0 0 255);
  --brand-alpha-50: color-mix(in srgb, blue, transparent);
  --brand-alpha-75: color-mix(in srgb, blue 75%, transparent);
}

The article Using color-mix() to create opacity variants by Una Kravets explains further.

Example: Styling button variants using color-mix() custom properties

Let's apply the color-mix() custom properties to a practical case: styling a simple button. First, we define the custom properties for our main base and secondary colors. As a bonus, we use color-mix() for our secondary color to mix the base color with pink.

css
:root {
  --brand: rgb(0 0 255);
  --brand-light: color-mix(in srgb, blue, white);

  --secondary: color-mix(in srgb, var(--brand), pink);
  --secondary-light: color-mix(in srgb, var(--secondary), white);
}

Next, we apply these colors to the primary and secondary button variants, using the lighter color variants for hover states.

css
button {
  background-color: var(--brand);
  color: white;
}

button:where(:hover, :focus) {
  background-color: var(--brand-light);
}

button.secondary {
  background-color: var(--secondary);
}

button.secondary:where(:hover, :focus) {
  background-color: var(--secondary-light);
}

We're not limited to defining custom properties at the root level only. For instance, we could set a custom property for a component's base color and create variations of this base color within the component's styling using color-mix(). For a secondary component variant, we could simply apply a different base color. This is illustrated below.

css
.card {
  --color: blue;
  background: color-mix(in srgb, var(--color), white 80%);
  border-top: 5px solid var(--color);
  padding: 1rem;
}

.secondary {
  --color: deeppink;
}

Here's a demo of this concept applied to a variety of UI components.

Creating warm and cool variations with color-mix()

While creating lighter or darker variations of an existing color is a common use case for color-mix(), beyond that, we could also create warm and cool variations by mixing warmer or cooler colors into our original palette.

Here we're defining an initial color palette (colors grabbed from Coolors), along with the colors we want to mix to create the warm and cool variants using custom properties.

css
:root {
  --yellow: rgb(221 215 141);
  --peach: rgb(220 191 133);
  --chocolate: rgb(139 99 92);
  --khaki: rgb(96 89 77);
  --grey: rgb(147 162 155);

  --mix-warm: red;
  --mix-cool: blue;
}

.palette > div {
  --color: var(--yellow);

  &:nth-child(2) {
    --color: var(--peach);
  }

  &:nth-child(3) {
    --color: var(--chocolate);
  }

  &:nth-child(4) {
    --color: var(--khaki);
  }

  &:nth-child(5) {
    --color: var(--grey);
  }
}

Then we're using custom properties to apply the second color mixed into the original base color, as well as specifying the amount. We're also specifying default values, so that if no value is given for --mix, the original base color will be used.

css
.palette > div {
  background: color-mix(
    in srgb,
    var(--color),
    var(--mix, var(--color)) var(--amount, 10%)
  );
}

This way, we're able to mix different colors and apply them to the entire palette.

css
.cool {
  --mix: var(--mix-cool);
}

.cool--20 {
  --amount: 20%;
}

.warm {
  --mix: var(--mix-warm);
}

.warm--20 {
  --amount: 20%;
}

Specifying the interpolation color space in color-mix()

In the previous sections, we used srgb (standard red green blue) as the color interpolation method. We can drastically change the outcome by modifying the color space we use for interpolation. Color spaces are a complex topic and well beyond the scope of this article, but it's worth noting some advantages and disadvantages of a few color spaces when deciding which one to use in color-mix().

Exploring the color space options

Color interpolation determines how one color transitions to another. A good way to visualize this is with gradients, as done in the Color interpolation section by Adam Argyle, which goes into great depth about color spaces and gamuts.

Classic RGB interpolation can result in muddy colors in the central interpolation zone (the middle area of a gradient), whereas the colors remain vibrant when using lch or oklch. As illustrated in the image below, the results are distinctly different when applied to the warm and cool color palettes in the previous example.

The same color mixes produce different results when we compare sRGB (left) and OKLCH (right) color spaces

Unlike srgb and hsl, both oklch and oklab are perceptually uniform. In the words of Lea Verou, this means:

the same numerical change in coordinates produces the same perceptual color difference

Therefore, I tend to prefer oklch and oklab color spaces when it comes to color interpolation with color-mix() — but the choice is yours!

Understanding the shorter and longer interpolation paths

In polar (or circular) color spaces, such as oklch, oklab, and hsl, we can also choose the direction in which the color is interpolated. When we mix two colors equally, the resulting hue angle will be halfway between the angles of the two colors. This will differ depending on whether the interpolation follows the shorter or the longer path around the color circle.

css
color-mix(in hsl, rgb(255 88 88), rgb(86 86 255));
color-mix(in hsl longer hue, rgb(255 88 88), rgb(86 86 255));

Comparing longer and shorter interpolation direction in the HSL color space. The shorter hue produces a pink-ish purple (left) and the longer hue produces a bright green (right).

In the frame below, experiment by mixing colors in different color spaces to observe the outcomes.

Browser support for color-mix()

The color-mix() function has been supported in all modern browsers since mid-2023. As always, remember that not all users will have the latest browsers. One way to ensure everyone gets a usable experience is by setting an initial color value. Browsers that do not support color-mix() will ignore the second declaration.

css
div {
  /* First declaration is fallback for browsers that do not support color-mix() */
  background: rgb(150 0 255);
  background: color-mix(in srgb, blue, red);
}

Alternatively, we can use a feature query to detect whether the browser supports color-mix() and provide styles accordingly:

css
.card {
  background: lightblue;
}

@supports (color-mix(in srgb, blue, white)) {
  .card {
    --color: blue;
    background: color-mix(in srgb, var(--color), white 80%);
    border-top: 5px solid var(--color);
  }
}

If you use PostCSS, there is a PostCSS plugin that comes bundled with postcss-preset-env. This plugin allows you to write color-mix() functions without worrying about browser support because your code is automatically converted during build time into CSS that's cross-browser compatible. For example:

css
.some-element {
  background-color: color-mix(in srgb, red, blue);
}

becomes:

css
.some-element {
  background-color: rgb(128 0 128);
}

It's worth noting, this will only work with static values and not custom properties or dynamic values set in JavaScript.

Color interpolation resources

If you're interested in diving deeper into color spaces and color interpolation on the web, here are a few articles to help you understand them:

Summary

We've seen how to use color-mix() to create variations of colors, and how the function can be used in combination with custom properties for use in projects and design systems. With browser support already pretty great, we can look forward to a new era of working with color on the web!

Stay Informed with MDN

Get the MDN newsletter and never miss an update on the latest web development trends, tips, and best practices.