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, #4a90e2
→ rgb(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
- MDN
color-mix()
- CSS Color Module Level 5
- CSS Color Module Level 4
- How To Handle CSS Colors Like A Senior DEV by Web Dev Simplified
- A Deep Dive Into CSS
color-mix()
by Kevin Powell - Create mathematically generated CSS Color Schemes with OKLCH by Supergeekery
- OKLCH in CSS: why we moved from RGB and HSL
- Gradients in linear space aren’t better by Aras Pranckevičius