Color on the web used to mean hex codes and sRGB. Modern CSS supports several color systems with meaningfully different ergonomics and capabilities. This guide explains what each system is, when to use it, and how to mix them safely.
The Systems at a Glance
| System | Syntax | Gamut | Best for |
|---|---|---|---|
| Hex | #ff6633 | sRGB | Concise; ubiquitous |
| RGB | rgb(255 102 51) | sRGB | Direct channel control |
| HSL | hsl(15 100% 60%) | sRGB | Hue rotation, simple palettes |
| LCH | lch(60% 80 30) | Wide | Perceptually uniform |
| OKLCH | oklch(0.6 0.2 30) | Wide | Modern design systems |
| Display P3 | color(display-p3 1 0.4 0.2) | P3 | Vivid colors on modern displays |
Perceptual Uniformity Matters
The reason designers are moving from HSL to OKLCH: HSL's lightness is mathematically simple but perceptually broken. hsl(60 100% 50%) (yellow) and hsl(240 100% 50%) (blue) have the same L value but look completely different in brightness. OKLCH fixes this — equal L produces equal perceived brightness across all hues. This makes scales, gradients, and contrast much more reliable.
Wide-Gamut Color
Most screens since ~2017 support Display P3, a wider gamut than sRGB. You can author colors outside sRGB using oklch(), color(display-p3 ...), or lab(). Browsers clip to the display gamut gracefully. The practical effect: more saturated reds and greens than sRGB can produce.
A Modern Color Workflow
- Author in OKLCH for clarity and perceptual uniformity.
- Provide hex fallbacks for older browsers if you support them.
- Define color tokens (--brand-500, etc.) using OKLCH; use them everywhere downstream.
- Build scales by varying L while holding C and H — produces uniform brightness steps.
- Test contrast pairs early and lock them into your token system.
Try It Yourself
Extract palettes from images and explore color relationships with DesignKit tools.
Color Palette Extractor →