HeoLab
ToolsBlogAboutContact
HeoLab

Free developer tools with AI enhancement. Built for developers who ship.

Tools

  • JSON Formatter
  • JWT Decoder
  • Base64 Encoder
  • Timestamp Converter
  • Regex Tester
  • All Tools →

Resources

  • Blog
  • What is JSON?
  • JWT Deep Dive
  • Base64 Explained

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 HeoLab. All rights reserved.

Tools work in your browser. Zero data retention.

HomeBlogModern CSS Color Functions: oklch(), color-mix(), and Dynamic Theming
Table of Contents▾
  • The Limits of Traditional Color Spaces
  • oklch() — The Modern Color Space
  • color-mix() — Semantic Theming
  • Building a Light/Dark Color System
  • Relative Color Syntax (CSS Level 5)
  • Conclusion
guides#css#colors#theming

Modern CSS Color Functions: oklch(), color-mix(), and Dynamic Theming

CSS color handling has evolved dramatically. Learn oklch() for perceptually uniform colors, color-mix() for semantic theming, and how to build light/dark mode systems with CSS custom properties.

Trong Ngo
March 3, 2026
4 min read

The Limits of Traditional Color Spaces

CSS has supported rgb() and hsl() for decades, but both have a critical flaw: they are not perceptually uniform. In HSL, increasing lightness from 50% to 60% looks very different depending on the hue. Blue appears much darker than yellow at the same HSL lightness value.

This makes generating programmatic color scales (like Tailwind's 50-900 range) inconsistent — the steps look uneven to the human eye.

oklch() — The Modern Color Space

oklch() is based on the Oklab perceptual model, designed so that equal numeric steps produce equal perceived differences in lightness and chroma.

/* oklch(lightness chroma hue) */
/* Lightness: 0-1 */
/* Chroma: 0-0.4+ (saturation) */
/* Hue: 0-360 */

.primary {
  color: oklch(0.6 0.2 250);     /* Medium-light, saturated blue */
}

.subtle {
  color: oklch(0.8 0.05 250);    /* Light, desaturated blue */
}

/* Generating a consistent color scale */
.swatch-100 { background: oklch(0.95 0.03 250); }
.swatch-200 { background: oklch(0.88 0.06 250); }
.swatch-300 { background: oklch(0.78 0.10 250); }
.swatch-400 { background: oklch(0.65 0.15 250); }
.swatch-500 { background: oklch(0.52 0.20 250); }
.swatch-600 { background: oklch(0.42 0.18 250); }
.swatch-700 { background: oklch(0.32 0.14 250); }
.swatch-800 { background: oklch(0.22 0.10 250); }
.swatch-900 { background: oklch(0.15 0.06 250); }

Each step in this scale looks visually equidistant — something impossible to achieve with HSL.

Browser support: Chrome 111+, Firefox 113+, Safari 15.4+. Use @supports for fallbacks.

color-mix() — Semantic Theming

color-mix() blends two colors by a percentage — powerful for generating hover states, disabled states, and transparency without JavaScript.

:root {
  --brand: #2563eb;
}

.button {
  background: var(--brand);
}

.button:hover {
  /* 10% darker on hover */
  background: color-mix(in oklch, var(--brand) 90%, black);
}

.button:disabled {
  /* 40% transparent */
  background: color-mix(in oklch, var(--brand) 60%, transparent);
}

/* Tinted background */
.alert-info {
  background: color-mix(in srgb, #2563eb 10%, white);
  border-color: color-mix(in srgb, #2563eb 40%, white);
}

This is exactly how Tailwind CSS v4's color-mix utilities work under the hood.

Building a Light/Dark Color System

The best approach: define semantic tokens that change meaning in dark mode, rather than having components specify both light and dark values.

:root {
  /* Base palette */
  --blue-500: oklch(0.52 0.20 250);
  --blue-400: oklch(0.65 0.15 250);

  /* Semantic tokens — light mode */
  --bg: oklch(0.98 0 0);              /* near-white */
  --surface: oklch(1 0 0);            /* white */
  --text-primary: oklch(0.15 0 0);    /* near-black */
  --text-muted: oklch(0.45 0 0);      /* gray */
  --brand: var(--blue-500);
  --brand-hover: color-mix(in oklch, var(--brand) 90%, black);
}

.dark {
  /* Semantic tokens — dark mode (same names, different values) */
  --bg: oklch(0.12 0 0);              /* near-black */
  --surface: oklch(0.17 0 0);         /* dark gray */
  --text-primary: oklch(0.93 0 0);    /* near-white */
  --text-muted: oklch(0.65 0 0);      /* light gray */
  --brand: var(--blue-400);           /* lighter blue for dark bg */
  --brand-hover: color-mix(in oklch, var(--brand) 90%, white);
}

With this system, components never reference light/dark directly:

.card {
  background: var(--surface);
  color: var(--text-primary);
  border-color: color-mix(in oklch, var(--text-muted) 20%, transparent);
}

Relative Color Syntax (CSS Level 5)

Coming to all browsers in 2025: create color variations from a base color without color-mix():

.element {
  --base: oklch(0.52 0.20 250);

  /* 20% lighter version */
  color: oklch(from var(--base) calc(l + 0.2) c h);

  /* Desaturated */
  border-color: oklch(from var(--base) l calc(c * 0.3) h);

  /* Complementary (opposite hue) */
  accent-color: oklch(from var(--base) l c calc(h + 180));
}

Explore color relationships interactively with HeoLab's Color Palette Generator — generate harmonious palettes from any base color.

Conclusion

oklch() gives you perceptually consistent color scales that HSL cannot. color-mix() eliminates the need for separate hover/disabled/transparent color variables. Combined with CSS custom properties for semantic tokens, you can build a complete design system that correctly adapts to light/dark mode without any JavaScript color manipulation.

Try These Tools

Color Palette Generator

Generate Tailwind-style shades (50–950) and color harmonies from any hex color.

Color Converter

Convert colors between HEX, RGB, HSL, HSV, and CMYK formats with a visual picker.

Related Articles

Placeholder Images in Web Development: From Lorem Picsum to SVG Data URIs

3 min read

CSS Animations: From Transitions to Keyframes

4 min read

CSS Grid Complete Guide: From Basics to Subgrid

3 min read

Back to Blog

Table of Contents

  • The Limits of Traditional Color Spaces
  • oklch() — The Modern Color Space
  • color-mix() — Semantic Theming
  • Building a Light/Dark Color System
  • Relative Color Syntax (CSS Level 5)
  • Conclusion

Related Articles

Placeholder Images in Web Development: From Lorem Picsum to SVG Data URIs

3 min read

CSS Animations: From Transitions to Keyframes

4 min read

CSS Grid Complete Guide: From Basics to Subgrid

3 min read