What Are Modern CSS Color Models?
Modern CSS color models, such as lch(),
oklch(), and color(display-p3), allow web
developers to access wider color gamuts, generate perceptually uniform
palettes, and achieve richer color fidelity on contemporary screens.
Unlike legacy formats like rgb(), hex, and
hsl(), these newer functional notations separate perceived
lightness from color intensity, making programmatic color manipulation,
dynamic theme creation, and accessible contrast calculations
significantly more reliable across modern browsers.
Legacy Color Formats vs Modern Spaces
For decades, the web relied predominantly on standard sRGB color
formats: Hexadecimal codes, rgb(), and hsl().
While simple to implement, sRGB formats carry inherent limitations:
- Gamut Constraints: sRGB represents a relatively small subset of the colors human eyes can perceive and that modern displays (such as OLED screens and Retina displays) can produce.
- Perceptual Non-Uniformity in HSL: In
hsl(), colors with identical lightness values often have drastically different perceived brightness. For example, pure yellow (hsl(60, 100%, 50%)) appears substantially brighter to human vision than pure blue (hsl(240, 100%, 50%)), making predictable color palette generation and accessible contrast pairing difficult.
Modern color spaces resolve these issues by aligning mathematical representations of color with actual human visual perception.
Understanding LCH
The lch() functional notation represents color using
three parameters:
- Lightness (L): A percentage from 0% (black) to 100% (white) indicating perceived brightness.
- Chroma (C): A number representing color saturation or purity, theoretically unbounded, but practical limits range from 0 (gray) to around 150+.
- Hue (H): An angle from 0 to 360 degrees on the color wheel.
.badge-alert {
background-color: lch(65% 85 45);
}Because lightness is independent of hue and chroma, adjusting the hue angle keeps the perceived brightness constant. However, CIELCH exhibits a known mathematical anomaly: shifting chroma in certain blue hues can cause an unintended shift toward purple.
The Advantages of OKLCH
Developed by Björn Ottosson, oklch() improves directly
upon lch(). It uses the same fundamental
parameters—Lightness, Chroma, and Hue—while fixing the hue-shift
distortion found in standard CIELCH.
- Lightness (L): Specified as a percentage (0% to 100%) or decimal (0 to 1).
- Chroma (C): A decimal typically ranging from 0 to approximately 0.4.
- Hue (H): An angle from 0 to 360 degrees or with
units (
deg,rad,turn).
:root {
--primary-color: oklch(0.65 0.22 250);
--primary-hover: oklch(0.75 0.22 250);
}oklch() is widely regarded as the premier choice for UI
design systems and dynamic theming because increasing or decreasing
lightness reliably changes brightness without altering the underlying
perceived hue.
Wide Gamut with Display P3
display-p3 is a wide-gamut color space developed for
modern digital displays. It covers roughly 50% more visible colors than
standard sRGB, excelling particularly in vibrant greens and intense
reds.
In CSS, Display P3 is accessed via the generic color()
function:
.vibrant-banner {
background-color: color(display-p3 1 0.2 0.3);
}The parameters specify red, green, and blue coordinates as decimals from 0 to 1 (or percentages), mapped specifically to the P3 gamut.
Feature Comparison
| Color Model | Gamut Support | Perceptually Uniform | Primary Strength |
|---|---|---|---|
hsl() |
sRGB | No | Legacy familiarity |
lch() |
Unlimited (CIE LAB) | Yes | Direct lightness isolation |
oklch() |
Unlimited (CIE LAB) | Yes (Refined) | Consistent palettes without hue shifts |
display-p3 |
Wide P3 | No | Highly vibrant monitor output |
Practical Implementation and Fallbacks
Modern browsers across desktop and mobile support
oklch(), lch(), and
color(display-p3). When building stylesheets for
progressive enhancement, you can provide fallback declarations or use
CSS conditional rules:
.button {
background-color: rgb(0, 102, 204);
background-color: oklch(0.55 0.2 245);
}
@supports (color: color(display-p3 0 1 0)) {
.accent-element {
color: color(display-p3 0.1 0.9 0.2);
}
}Adopting these modern color models allows developers to craft consistent, accessible, and vibrant visual experiences natively in CSS.