OKLCH: The Future of CSS Colors

OKLCH is perceptually uniform, HDR-ready, and makes theming easier. Here's why you should switch from HSL/RGB.
I switched my entire portfolio to OKLCH colors. Here's why it matters.
What's Wrong with HSL/RGB?
RGB is for Computers
color: rgb(100, 150, 200);What does this look like? No idea. RGB describes light output, not perceptual color.
HSL is Better, But Flawed
color: hsl(210, 60%, 50%);Better — we can reason about hue and saturation. But lightness is inconsistent:
/* Same lightness value, different perceived brightness */
background: hsl(200, 80%, 50%); /* Looks darker */
background: hsl(80, 80%, 50%); /* Looks brighter */Yellow at 50% lightness appears much brighter than blue at 50%. This makes theming painful.
Enter OKLCH
OKLCH is designed for perceptual uniformity:
color: oklch(65% 0.15 250);- L = Lightness (0-100%)
- C = Chroma (color intensity)
- H = Hue (0-360)
The key insight: equal lightness = equal perceived brightness.
Real Example: Theming
HSL Version (Inconsistent)
:root {
--bg-primary: hsl(220, 20%, 10%);
--bg-secondary: hsl(220, 20%, 15%);
--bg-tertiary: hsl(220, 20%, 20%);
}Looks fine until you add accent colors:
/* These don't match visually */
.accent-yellow { background: hsl(60, 80%, 50%); }
.accent-blue { background: hsl(220, 80%, 50%); }OKLCH Version (Consistent)
:root {
--bg-primary: oklch(20% 0.02 250);
--bg-secondary: oklch(25% 0.02 250);
--bg-tertiary: oklch(30% 0.02 250);
/* These match perfectly */
.accent-yellow { background: oklch(80% 0.18 100); }
.accent-blue { background: oklch(80% 0.18 250); }
}Both accents at 80% lightness look equally bright.
Browser Support
OKLCH is supported in:
- ✅ Chrome 111+
- ✅ Safari 15.4+
- ✅ Firefox 113+
- ✅ Edge 111+
That's ~92% of users globally. For older browsers, provide a fallback:
.button {
background: hsl(220, 80%, 50%); /* Fallback */
background: oklch(65% 0.18 250); /* Modern */
}My Portfolio Color System
I use OKLCH for everything:
:root {
/* Neutrals */
--bg-dark: oklch(12% 0.01 250);
--bg-card: oklch(18% 0.01 250);
--text-primary: oklch(95% 0.01 250);
--text-secondary: oklch(70% 0.01 250);
/* Accent */
--accent: oklch(91.7% 0.201 117); /* #e2f658 green */
--accent-dim: oklch(70% 0.15 117);
/* Semantic */
--success: oklch(70% 0.18 145);
--warning: oklch(75% 0.18 85);
--error: oklch(65% 0.18 25);
}Light/dark mode? Just adjust L:
@media (prefers-color-scheme: dark) {
:root {
--bg-base: oklch(12% 0.01 250);
--text-base: oklch(95% 0.01 250);
}
}
@media (prefers-color-scheme: light) {
:root {
--bg-base: oklch(98% 0.01 250);
--text-base: oklch(20% 0.01 250);
}
}Converting Colors
Use oklch.com to visually convert colors, or use libraries like colorjs.io for programmatic conversion.
Why It Matters
- Accessible theming: Consistent contrast ratios
- HDR support: OKLCH can display wider gamut colors
- Future-proof: Designed for modern displays
Start Simple
Replace one color system at a time:
/* Before */
--primary: hsl(220, 80%, 50%);
/* After */
--primary: oklch(65% 0.18 250);Your future self (and your users) will thank you.
Switched to OKLCH in 2024 · Colors finally make sense