Back to Blog
CSSColorsFunctionsDesign

CSS Color Functions Guide

Master CSS color functions including color-mix, relative colors, and modern color spaces.

B
Bootspring Team
Engineering
December 26, 2018
6 min read

Modern CSS offers powerful color functions for dynamic color manipulation. Here's how to use them.

color-mix()#

1/* Mix two colors */ 2.mixed { 3 /* 50% of each color by default */ 4 background: color-mix(in srgb, red, blue); 5 6 /* Custom percentages */ 7 background: color-mix(in srgb, red 30%, blue 70%); 8 9 /* Only specify one percentage */ 10 background: color-mix(in srgb, red 25%, blue); 11 /* blue gets remaining 75% */ 12} 13 14/* Different color spaces */ 15.color-spaces { 16 /* sRGB - standard */ 17 color: color-mix(in srgb, #ff0000, #0000ff); 18 19 /* HSL - hue-based */ 20 color: color-mix(in hsl, red, blue); 21 22 /* LCH - perceptually uniform */ 23 color: color-mix(in lch, red, blue); 24 25 /* OKLCH - improved perceptual */ 26 color: color-mix(in oklch, red, blue); 27}

Tints and Shades#

1:root { 2 --brand: #3b82f6; 3 4 /* Tints (lighter) - mix with white */ 5 --brand-100: color-mix(in srgb, var(--brand) 10%, white); 6 --brand-200: color-mix(in srgb, var(--brand) 20%, white); 7 --brand-300: color-mix(in srgb, var(--brand) 40%, white); 8 --brand-400: color-mix(in srgb, var(--brand) 60%, white); 9 --brand-500: var(--brand); 10 11 /* Shades (darker) - mix with black */ 12 --brand-600: color-mix(in srgb, var(--brand) 80%, black); 13 --brand-700: color-mix(in srgb, var(--brand) 60%, black); 14 --brand-800: color-mix(in srgb, var(--brand) 40%, black); 15 --brand-900: color-mix(in srgb, var(--brand) 20%, black); 16}

Relative Color Syntax#

1/* Modify existing colors */ 2.relative-colors { 3 --base: #3b82f6; 4 5 /* Lighten by increasing lightness */ 6 color: hsl(from var(--base) h s calc(l + 20%)); 7 8 /* Darken by decreasing lightness */ 9 color: hsl(from var(--base) h s calc(l - 20%)); 10 11 /* Desaturate */ 12 color: hsl(from var(--base) h calc(s - 30%) l); 13 14 /* Shift hue */ 15 color: hsl(from var(--base) calc(h + 30) s l); 16 17 /* Add transparency */ 18 color: hsl(from var(--base) h s l / 50%); 19}

OKLCH Color Space#

1/* OKLCH: lightness, chroma, hue */ 2.oklch-colors { 3 /* Pure colors */ 4 --red: oklch(63% 0.26 29); 5 --green: oklch(87% 0.23 142); 6 --blue: oklch(45% 0.31 264); 7 8 /* Adjust lightness */ 9 --light-blue: oklch(from var(--blue) calc(l + 0.2) c h); 10 --dark-blue: oklch(from var(--blue) calc(l - 0.2) c h); 11 12 /* Adjust chroma (saturation) */ 13 --muted-blue: oklch(from var(--blue) l calc(c - 0.1) h); 14 --vivid-blue: oklch(from var(--blue) l calc(c + 0.1) h); 15}

Color Palette Generation#

1:root { 2 --hue: 220; /* Base hue */ 3 4 /* Generate palette from single hue */ 5 --primary: oklch(55% 0.2 var(--hue)); 6 --primary-light: oklch(75% 0.15 var(--hue)); 7 --primary-dark: oklch(35% 0.2 var(--hue)); 8 9 /* Complementary (opposite hue) */ 10 --complementary: oklch(55% 0.2 calc(var(--hue) + 180)); 11 12 /* Analogous (adjacent hues) */ 13 --analogous-1: oklch(55% 0.2 calc(var(--hue) - 30)); 14 --analogous-2: oklch(55% 0.2 calc(var(--hue) + 30)); 15 16 /* Triadic (120° apart) */ 17 --triadic-1: oklch(55% 0.2 calc(var(--hue) + 120)); 18 --triadic-2: oklch(55% 0.2 calc(var(--hue) + 240)); 19}

Semantic Colors#

1:root { 2 --success-base: oklch(65% 0.2 145); 3 --warning-base: oklch(75% 0.18 85); 4 --error-base: oklch(55% 0.22 25); 5 --info-base: oklch(60% 0.18 250); 6 7 /* Generate variants */ 8 --success: var(--success-base); 9 --success-light: oklch(from var(--success-base) calc(l + 0.25) calc(c - 0.05) h); 10 --success-dark: oklch(from var(--success-base) calc(l - 0.15) c h); 11 12 --error: var(--error-base); 13 --error-light: oklch(from var(--error-base) calc(l + 0.25) calc(c - 0.05) h); 14 --error-dark: oklch(from var(--error-base) calc(l - 0.15) c h); 15}

Dark Mode Colors#

1:root { 2 --bg: oklch(98% 0.01 250); 3 --text: oklch(20% 0.02 250); 4 --primary: oklch(50% 0.2 250); 5} 6 7@media (prefers-color-scheme: dark) { 8 :root { 9 /* Invert lightness while keeping hue/chroma */ 10 --bg: oklch(from var(--bg) calc(1 - l) c h); 11 --text: oklch(from var(--text) calc(1 - l) c h); 12 /* Adjust primary for dark backgrounds */ 13 --primary: oklch(from var(--primary) calc(l + 0.2) c h); 14 } 15}

Transparency Variations#

1:root { 2 --brand: #3b82f6; 3 4 /* Create transparent versions */ 5 --brand-10: color-mix(in srgb, var(--brand) 10%, transparent); 6 --brand-20: color-mix(in srgb, var(--brand) 20%, transparent); 7 --brand-50: color-mix(in srgb, var(--brand) 50%, transparent); 8 --brand-80: color-mix(in srgb, var(--brand) 80%, transparent); 9} 10 11/* Or with relative color syntax */ 12.transparent-variants { 13 --brand: #3b82f6; 14 --brand-alpha-50: rgb(from var(--brand) r g b / 50%); 15 --brand-alpha-20: rgb(from var(--brand) r g b / 20%); 16}

Interactive States#

1.button { 2 --btn-color: #3b82f6; 3 4 background: var(--btn-color); 5 transition: background 0.2s; 6} 7 8.button:hover { 9 /* Lighten on hover */ 10 background: color-mix(in oklch, var(--btn-color), white 20%); 11} 12 13.button:active { 14 /* Darken on press */ 15 background: color-mix(in oklch, var(--btn-color), black 20%); 16} 17 18.button:disabled { 19 /* Desaturate when disabled */ 20 background: oklch(from var(--btn-color) l calc(c * 0.3) h / 50%); 21}

Gradient Enhancement#

1.gradient { 2 --start: #3b82f6; 3 --end: #8b5cf6; 4 5 /* Add middle color for smoother transition */ 6 background: linear-gradient( 7 to right, 8 var(--start), 9 color-mix(in oklch, var(--start) 50%, var(--end)), 10 var(--end) 11 ); 12} 13 14/* Prevent muddy midpoints */ 15.smooth-gradient { 16 /* Using oklch prevents gray midpoints */ 17 background: linear-gradient( 18 to right in oklch, 19 oklch(60% 0.2 250), 20 oklch(60% 0.2 330) 21 ); 22}

Contrast Colors#

1/* Generate text color based on background */ 2.auto-contrast { 3 --bg: #3b82f6; 4 5 background: var(--bg); 6 7 /* Light text for dark backgrounds */ 8 color: oklch(from var(--bg) calc(l < 0.6 ? 0.95 : 0.1) 0.01 h); 9}

Color Tokens System#

1:root { 2 /* Base colors */ 3 --blue-500: oklch(55% 0.2 250); 4 --green-500: oklch(65% 0.2 145); 5 --red-500: oklch(55% 0.22 25); 6 --gray-500: oklch(55% 0.01 250); 7 8 /* Generate full scale for each */ 9 --blue-50: oklch(from var(--blue-500) 0.97 0.01 h); 10 --blue-100: oklch(from var(--blue-500) 0.93 0.04 h); 11 --blue-200: oklch(from var(--blue-500) 0.86 0.08 h); 12 --blue-300: oklch(from var(--blue-500) 0.76 0.12 h); 13 --blue-400: oklch(from var(--blue-500) 0.66 0.16 h); 14 /* 500 is base */ 15 --blue-600: oklch(from var(--blue-500) 0.46 0.2 h); 16 --blue-700: oklch(from var(--blue-500) 0.38 0.18 h); 17 --blue-800: oklch(from var(--blue-500) 0.30 0.14 h); 18 --blue-900: oklch(from var(--blue-500) 0.22 0.1 h); 19}

Browser Support Check#

1/* Feature detection */ 2@supports (color: color-mix(in lch, red, blue)) { 3 .supported { 4 background: color-mix(in lch, var(--primary), white 20%); 5 } 6} 7 8/* Fallback */ 9.button { 10 /* Fallback */ 11 background: #5b9af7; 12 /* Modern */ 13 background: color-mix(in srgb, #3b82f6, white 20%); 14}

Best Practices#

Color Spaces: ✓ oklch for perceptual uniformity ✓ srgb for compatibility ✓ hsl for intuitive adjustments ✓ lch for wide gamut Patterns: ✓ Generate scales from base ✓ Use relative syntax for variants ✓ Mix with transparent for alpha ✓ Test in both light/dark modes Performance: ✓ Define in :root when possible ✓ Use CSS variables ✓ Minimize runtime calculations ✓ Provide fallbacks Avoid: ✗ Hardcoding many color values ✗ Ignoring contrast ratios ✗ Complex nested calculations ✗ Forgetting browser support

Conclusion#

CSS color functions like color-mix() and relative color syntax enable dynamic color manipulation without JavaScript. Use OKLCH for perceptually uniform colors, generate entire palettes from single base colors, and create automatic dark mode variants. Always provide fallbacks for older browsers and test contrast ratios for accessibility.

Share this article

Help spread the word about Bootspring