Modern CSS provides powerful color manipulation functions. Here's how to use them.
color-mix()#
1/* Mix two colors */
2.mixed {
3 /* 50% of each color */
4 background: color-mix(in srgb, red, blue);
5 /* Result: purple */
6}
7
8/* Custom percentages */
9.custom-mix {
10 /* 25% red, 75% blue */
11 background: color-mix(in srgb, red 25%, blue);
12
13 /* Explicit percentages */
14 background: color-mix(in srgb, red 30%, blue 70%);
15}
16
17/* Mix with currentColor */
18.tinted {
19 color: blue;
20 background: color-mix(in srgb, currentColor 20%, white);
21}
22
23/* Mix with transparent */
24.faded {
25 background: color-mix(in srgb, blue 50%, transparent);
26 /* Same as: rgba(0, 0, 255, 0.5) */
27}
28
29/* Different color spaces */
30.oklch-mix {
31 /* Perceptually uniform mixing */
32 background: color-mix(in oklch, red, blue);
33}
34
35.hsl-mix {
36 background: color-mix(in hsl, red, blue);
37}
38
39.lab-mix {
40 background: color-mix(in lab, red, blue);
41}Relative Color Syntax#
1/* Modify existing colors */
2.lighter {
3 --base: blue;
4 /* Increase lightness by 20% */
5 background: oklch(from var(--base) calc(l + 0.2) c h);
6}
7
8.darker {
9 --base: blue;
10 /* Decrease lightness */
11 background: oklch(from var(--base) calc(l - 0.2) c h);
12}
13
14.saturated {
15 --base: blue;
16 /* Increase chroma */
17 background: oklch(from var(--base) l calc(c + 0.1) h);
18}
19
20.desaturated {
21 --base: blue;
22 /* Decrease chroma */
23 background: oklch(from var(--base) l calc(c * 0.5) h);
24}
25
26/* Shift hue */
27.complementary {
28 --base: blue;
29 /* Rotate hue 180 degrees */
30 background: oklch(from var(--base) l c calc(h + 180));
31}
32
33.triadic {
34 --base: blue;
35 background: oklch(from var(--base) l c calc(h + 120));
36}
37
38/* With transparency */
39.semi-transparent {
40 --base: blue;
41 background: oklch(from var(--base) l c h / 0.5);
42}
43
44/* Using HSL */
45.hsl-lighter {
46 --base: hsl(220 80% 50%);
47 background: hsl(from var(--base) h s calc(l + 20%));
48}OKLCH Color Space#
1/* OKLCH: Lightness, Chroma, Hue */
2.oklch-color {
3 /* l: 0-1, c: 0-0.4, h: 0-360 */
4 background: oklch(0.7 0.15 250);
5}
6
7/* More readable with percentages */
8.oklch-percent {
9 background: oklch(70% 0.15 250);
10}
11
12/* With alpha */
13.oklch-alpha {
14 background: oklch(70% 0.15 250 / 0.8);
15}
16
17/* Creating a color palette */
18:root {
19 --hue: 250; /* Blue */
20 --chroma: 0.15;
21
22 --color-50: oklch(0.97 calc(var(--chroma) * 0.3) var(--hue));
23 --color-100: oklch(0.93 calc(var(--chroma) * 0.5) var(--hue));
24 --color-200: oklch(0.87 calc(var(--chroma) * 0.7) var(--hue));
25 --color-300: oklch(0.78 calc(var(--chroma) * 0.85) var(--hue));
26 --color-400: oklch(0.68 var(--chroma) var(--hue));
27 --color-500: oklch(0.58 var(--chroma) var(--hue));
28 --color-600: oklch(0.48 var(--chroma) var(--hue));
29 --color-700: oklch(0.38 var(--chroma) var(--hue));
30 --color-800: oklch(0.28 calc(var(--chroma) * 0.9) var(--hue));
31 --color-900: oklch(0.20 calc(var(--chroma) * 0.8) var(--hue));
32}color() Function#
1/* Access wider color gamuts */
2.display-p3 {
3 /* Display P3 color space - more vibrant */
4 background: color(display-p3 1 0 0);
5 /* Fallback for browsers without P3 */
6 background: color(display-p3 1 0 0) or red;
7}
8
9.srgb {
10 background: color(srgb 1 0.5 0);
11}
12
13.rec2020 {
14 /* Even wider gamut */
15 background: color(rec2020 0.3 0.7 0.2);
16}
17
18/* With @supports */
19@supports (color: color(display-p3 1 0 0)) {
20 .vibrant-red {
21 background: color(display-p3 1 0.1 0.1);
22 }
23}LAB and LCH#
1/* LAB: Lightness, a-axis (green-red), b-axis (blue-yellow) */
2.lab-color {
3 background: lab(60% -30 50);
4}
5
6/* LCH: Lightness, Chroma, Hue */
7.lch-color {
8 background: lch(60% 40 250);
9}
10
11/* LAB is good for perceptual uniformity */
12.gradient-lab {
13 background: linear-gradient(
14 in lab,
15 blue,
16 yellow
17 );
18 /* No muddy middle colors */
19}
20
21/* Compare with sRGB */
22.gradient-srgb {
23 background: linear-gradient(
24 in srgb,
25 blue,
26 yellow
27 );
28 /* May have greenish middle */
29}HWB Colors#
1/* HWB: Hue, Whiteness, Blackness */
2.hwb-color {
3 /* Hue: 0-360, Whiteness: 0-100%, Blackness: 0-100% */
4 background: hwb(200 10% 20%);
5}
6
7/* Pure hue */
8.pure-blue {
9 background: hwb(240 0% 0%);
10}
11
12/* Tinted (add white) */
13.light-blue {
14 background: hwb(240 40% 0%);
15}
16
17/* Shaded (add black) */
18.dark-blue {
19 background: hwb(240 0% 40%);
20}
21
22/* With transparency */
23.hwb-alpha {
24 background: hwb(240 10% 10% / 0.5);
25}Creating Color Scales#
1/* Programmatic color scale with OKLCH */
2:root {
3 --primary-hue: 220;
4
5 /* Light mode */
6 --primary-light: oklch(0.95 0.02 var(--primary-hue));
7 --primary-base: oklch(0.55 0.18 var(--primary-hue));
8 --primary-dark: oklch(0.25 0.12 var(--primary-hue));
9
10 /* Hover/active states */
11 --primary-hover: oklch(from var(--primary-base) calc(l - 0.05) c h);
12 --primary-active: oklch(from var(--primary-base) calc(l - 0.1) c h);
13}
14
15/* Dark mode adjustments */
16@media (prefers-color-scheme: dark) {
17 :root {
18 --primary-light: oklch(0.25 0.08 var(--primary-hue));
19 --primary-base: oklch(0.65 0.16 var(--primary-hue));
20 --primary-dark: oklch(0.90 0.04 var(--primary-hue));
21 }
22}
23
24/* Semantic colors */
25:root {
26 --success-hue: 145;
27 --warning-hue: 45;
28 --error-hue: 15;
29
30 --success: oklch(0.55 0.18 var(--success-hue));
31 --warning: oklch(0.70 0.18 var(--warning-hue));
32 --error: oklch(0.55 0.20 var(--error-hue));
33}Color Contrast#
1/* Ensuring contrast with color-mix */
2.auto-contrast {
3 --bg: blue;
4 --light-text: color-mix(in oklch, white 90%, var(--bg));
5 --dark-text: color-mix(in oklch, black 85%, var(--bg));
6
7 background: var(--bg);
8 /* Choose text color based on background lightness */
9}
10
11/* Using OKLCH for guaranteed contrast */
12.high-contrast {
13 --base-hue: 220;
14 --bg: oklch(0.2 0.1 var(--base-hue));
15 --text: oklch(0.9 0.05 var(--base-hue));
16
17 background: var(--bg);
18 color: var(--text);
19}
20
21/* Accessible focus rings */
22.focus-visible {
23 --focus-color: oklch(0.7 0.2 250);
24 outline: 3px solid var(--focus-color);
25 outline-offset: 2px;
26}Gradients with Color Spaces#
1/* Smooth gradients in OKLCH */
2.smooth-gradient {
3 background: linear-gradient(
4 in oklch,
5 oklch(0.7 0.2 30), /* Red */
6 oklch(0.7 0.2 150), /* Green */
7 oklch(0.7 0.2 270) /* Blue */
8 );
9}
10
11/* Hue interpolation */
12.rainbow-short {
13 background: linear-gradient(
14 in oklch shorter hue,
15 oklch(0.7 0.2 0),
16 oklch(0.7 0.2 270)
17 );
18}
19
20.rainbow-long {
21 background: linear-gradient(
22 in oklch longer hue,
23 oklch(0.7 0.2 0),
24 oklch(0.7 0.2 270)
25 );
26}
27
28/* Avoiding muddy midpoints */
29.clean-gradient {
30 background: linear-gradient(
31 in oklch,
32 hsl(0 80% 50%), /* Red */
33 hsl(60 80% 50%) /* Yellow */
34 );
35 /* No orange-brown in middle */
36}Dynamic Theming#
1/* Theme with CSS variables and color functions */
2:root {
3 --theme-hue: 220;
4 --theme-saturation: 0.15;
5
6 /* Generate full palette from one hue */
7 --surface: oklch(0.98 calc(var(--theme-saturation) * 0.2) var(--theme-hue));
8 --surface-hover: oklch(0.95 calc(var(--theme-saturation) * 0.3) var(--theme-hue));
9 --border: oklch(0.85 calc(var(--theme-saturation) * 0.4) var(--theme-hue));
10 --text-muted: oklch(0.55 calc(var(--theme-saturation) * 0.5) var(--theme-hue));
11 --text: oklch(0.25 calc(var(--theme-saturation) * 0.3) var(--theme-hue));
12 --primary: oklch(0.55 var(--theme-saturation) var(--theme-hue));
13 --primary-hover: oklch(0.50 var(--theme-saturation) var(--theme-hue));
14}
15
16/* Change entire theme by changing hue */
17.theme-purple {
18 --theme-hue: 280;
19}
20
21.theme-green {
22 --theme-hue: 145;
23}
24
25.theme-orange {
26 --theme-hue: 30;
27}Button Variations#
1/* Generate button states from base color */
2.button {
3 --btn-color: oklch(0.55 0.18 220);
4
5 background: var(--btn-color);
6 color: oklch(from var(--btn-color) 0.98 0.02 h);
7}
8
9.button:hover {
10 background: oklch(from var(--btn-color) calc(l - 0.05) c h);
11}
12
13.button:active {
14 background: oklch(from var(--btn-color) calc(l - 0.1) c h);
15}
16
17.button:disabled {
18 background: oklch(from var(--btn-color) l calc(c * 0.3) h / 0.5);
19}
20
21/* Outline variant */
22.button--outline {
23 background: transparent;
24 color: var(--btn-color);
25 border: 2px solid var(--btn-color);
26}
27
28.button--outline:hover {
29 background: oklch(from var(--btn-color) 0.95 calc(c * 0.3) h);
30}Best Practices#
Color Spaces:
✓ Use OKLCH for perceptual uniformity
✓ Use color-mix for simple blending
✓ Use relative colors for modifications
✓ Test in different color spaces
Accessibility:
✓ Maintain WCAG contrast ratios
✓ Test with color blindness simulators
✓ Don't rely on color alone
✓ Use sufficient lightness differences
Performance:
✓ Prefer CSS over JavaScript
✓ Use CSS custom properties
✓ Minimize calculations
✓ Test browser support
Fallbacks:
✓ Provide sRGB fallbacks
✓ Use @supports queries
✓ Test in older browsers
✓ Consider progressive enhancement
Conclusion#
Modern CSS color functions enable powerful color manipulation directly in stylesheets. Use OKLCH for perceptual uniformity, color-mix for blending, and relative color syntax for modifications. Always provide fallbacks for older browsers.