4

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

 6 months ago
source link: https://developer.mozilla.org/en-US/blog/color-palettes-css-color-mix/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Get real-time assistance with your coding queries. Try AI Help now!

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

March 8, 20248 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.

 
   border-box


 
   100vh
   grid
   center


 
   flex
   1rem
   100% 40rem


 
   2px solid


 
   1 0 auto
   to right red 50% blue 0


 
   4 / 3
   0 0 20%

.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.


  
    color-mix(in srgb, blue 90%, red)
    
      
      
    
  
  
    color-mix(in srgb, blue, red 10%)
    
      
      
    
  

 
   border-box


 
   1rem


 
   block
   0.75rem
   1.1rem
   600


 
   100% 40rem
   2rem auto
   grid
   2rem


 
   flex
   1rem
   100%


 
   1 0 auto
   to right blue 90% red 0
   2px solid


 
   0 0 20%
   in srgb blue 90% red
   2px solid
   4 / 3


 
   in srgb blue red 10%

/* 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:


  
    color-mix(in srgb, blue, red)
    
      
      
    
  
  
    color-mix(in srgb, blue 20%, red 20%)
    
      
      
    
  

 
   border-box


 
   1rem


 
   block
   0.75rem
   1.1rem
   600


 
   100% 40rem
   2rem auto
   grid
   2rem


 
   flex
   1rem
   100%


 
   1 0 auto
   to right blue 50% red 0
   2px solid


 
   0 0 20%
   in srgb blue red
   2px solid
   4 / 3


 
   
    to right
    blue 20%
    red 0
    red 40%
    transparent 0
  


 
   in srgb blue 20% red 20%

/* 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.

 
   border-box


 
   flex


 
   1rem


 
   1
   25% 5rem

/* 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.

: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:

: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.

: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.

Primary button
Secondary button
 
   border-box


 
   0 0 255
   in srgb blue white

   in srgb --brand pink
   in srgb --secondary white


 
   none
   none
   1rem 1.5rem
   0.65rem

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.


  Card title
  Hello



  Card title
  Hello

 
   border-box


 
    sans-serif
   grid
   1rem
   auto-fill 300px 100% 1fr

.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.


  Primary button
  Secondary button

  
    I am a simple quote
    Ms A. Person
  

  
    I am a simple quote
    Ms A. Person
  

  
    
      
      
        Card heading
        Find out more
      
    
    
      
      
        Card heading
        Find out more
      
    
  

 
   border-box


 
   orchid
   in srgb --base lightblue 50%


 
   0
    sans-serif
   1rem


 
   0


 
   block
   100%


 
   inherit


 
   0


 
   40rem
   0 auto


 
   grid
   1rem
   auto-fit 300px 100% 1fr


 
   --base
   none
   none
   1rem 1.5rem
   0.5rem
   --color


 
   in srgb --color white 20%


 
   --secondary


 
   --base
   in srgb --color white 75%
   1rem
   0
   5px solid --color

   
     1.5em
  


 
   block
   1rem
   normal


 
   --secondary


 
   --base
   in srgb --color white 75%
   0.5rem
   hidden


 
   --secondary


 
   1rem

   
     block
     1rem
     in srgb --color black 40%
  

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.

: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.

.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.

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

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

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

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

  
    Original color palette
    
      
      
      
      
      
    
  
  
    red 10%
    
      
      
      
      
      
    
  
  
    red 20%
    
      
      
      
      
      
    
  
  
    blue 10%
    
      
      
      
      
      
    
  
  
    blue 20%
    
      
      
      
      
      
    
  
  
    white 40%
    
      
      
      
      
      
    
  

 
   border-box


 
   221 215 141
   220 191 133
   139 99 92
   96 89 77
   147 162 155

   red
   blue


 
   0
   1rem
    sans-serif


 
   none
   0
   900px
   0 auto


 
   grid
   10rem 1fr
   1rem


 
   grid
   5 1fr

   
     --yellow
     1
     --color

     
       --peach
    

     
       --chocolate
    

     
       --khaki
    

     
       --grey
    
  


 
   
     
      in srgb
      --color
      --mix --color --amount 10%
    
  


 
   --mix-cool


 
   20%


 
   --mix-warm


 
   20%


 
   #fff

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.

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.

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));

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


  Amount
  

  Color 1
  

  Color 2
  

  
    Interpolation method
    
    Shorter
    
    Longer
  

  
    Color space
    
    srgb

    
    oklch

    
    hsl
  


 
   border-box


 
   red
   blue
   50%
   50%
   
    in --space srgb
    --c1 --amount1
    --c2 --amount2
  
   grid
   auto 1fr
   1rem
   100vh
   0
    sans-serif


 
   20rem
   1rem
   white
   grid
   auto 1fr
   1rem
   center
   100vh
   auto


 
   1 / span 2
   grid
   subgrid
   0.5rem
   0.75rem

   
     span 2
     0
  


 
   center
   255 255 255 / 0.7
   1rem
   1rem
   0.5rem
   center
   1.4

 amountInput  document
 text  document
 colorInputs  document
 colorSpaceInputs  document
 methodInputs  document

     
  documentbodystyle 
  documentbodystyle 


     
   space  colorSpaceInputs  elcheckedvalue

   space    space

   method  methodInputs  elcheckedvalue

   


     
   c1  documentbody
   c2  documentbody

  texthidden  

  textinnerHTML  


amountInput   
    value   etarget
  value
  value


colorInputs  
  el   
      id value   etarget
    documentbodystyle value

    amountInputvalue
  


     
  documentbodystyle 


colorSpaceInputs  
  el   
      value   etarget
    value
    amountInputvalue
  


methodInputs  
  el   
     space  colorSpaceInputs  elcheckedvalue
    
    amountInputvalue
  


amountInputvalue
amountInputvalue

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.

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:

.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:

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

becomes:

.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.

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!

Previous Post Modernizing conventional test automation with TestGrid

Stay Informed with MDN

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

Your email address:
I’m okay with Mozilla handling my info as explained in this Privacy Notice

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK