Colour systems
HSL's lightness axis lies. OKLCH fixes it.
HSL lies. Yellow at hsl(60, 100%, 50%) and blue at hsl(240, 100%, 50%) share the same L value, but yellow looks twice as bright. Add a second hue and you're hand-tuning the entire scale again.
OKLCH: perceptually uniform colour
- L (Lightness): 0 = black, 1 = white.
- C (Chroma): Vividness. 0 = grey. Maximum depends on hue.
- H (Hue): 0-360 degrees.
Building a palette with CSS variables
Chroma tapers at extremes because light and dark shades clip at high chroma. To add a second hue, change only the hue angle and chroma curve. Lightness stays the same.
Avoiding pure black and pure white
#000 on #FFF is 21:1 contrast, but harsh. Pure black on pure white creates a vibrating edge.
~18:1 contrast. Far above WCAG AA, far more comfortable.
Dark mode mapping
Dark mode is not a reversed light palette:
- Low background chroma. Saturated dark backgrounds create colour noise.
- High text lightness, not 1.0. Pure white glares.
- Surface sits close to background. Small lightness jumps.
- Borders need more contrast.
rgba(0,0,0,0.1)vanishes. Usergba(255,255,255,0.1).
Common mistakes
- HSL palettes with uneven mid-tones. Switch to OKLCH.
- Pure
#000on pure#FFF. Soften both ends. - Light grey body text.
#999on#fffis 2.85:1. Fails WCAG AA. - Reversing the palette for dark mode. Map dark mode independently.
- Brand colours as links without contrast checks. Many teals and oranges fail against white.