Older CSS color often used hex, RGB, or HSL. Those still work, but modern CSS adds color spaces that better match human perception.
OKLCH
oklch() describes color with:
- lightness
- chroma
- hue
:root {
--accent: oklch(58% 0.18 255);
--accent-strong: oklch(48% 0.2 255);
}
Lightness is easier to adjust predictably than in HSL. Increasing or decreasing lightness tends to feel more even.
OKLab
oklab() is a rectangular form of the same color space:
.swatch {
background: oklab(62% -0.05 -0.16);
}
You will often use oklch() directly and oklab as a mixing space.
Mixing colors
color-mix() creates a color from other colors:
.button:hover {
background: color-mix(in oklab, var(--accent), black 12%);
}
.button:focus-visible {
outline-color: color-mix(in oklab, var(--accent), white 35%);
}
This keeps state colors tied to the base token.
Theme tokens
Use semantic tokens:
:root {
--page: oklch(99% 0.01 250);
--text: oklch(22% 0.02 250);
--accent: oklch(56% 0.18 255);
}
@media (prefers-color-scheme: dark) {
:root {
--page: oklch(18% 0.02 250);
--text: oklch(94% 0.01 250);
--accent: oklch(72% 0.16 255);
}
}
Components use meaning, not raw palette names:
body {
background: var(--page);
color: var(--text);
}
a {
color: var(--accent);
}
Relative colors, briefly
Relative color syntax can derive one color from another:
.surface {
background: oklch(from var(--accent) 96% 0.03 h);
}
This is powerful, but newer than basic color values. Use it when it makes a system clearer, and check target support.
Support and fallback
oklch() and color-mix() are broadly usable in modern browsers. Still, use fallbacks when the color is essential:
.button {
background: rgb(40 90 200);
background: oklch(56% 0.18 255);
}
Older browsers use the first value and ignore the unsupported second value.
For feature-specific enhancement:
@supports (background: color-mix(in oklab, red, white)) {
.button:hover {
background: color-mix(in oklab, var(--accent), black 12%);
}
}
Preview
Theme preview
Contrast must work before color choices can communicate.
Generated CSS
What to carry forward
oklch()gives more perceptual control than HSL for many palettescolor-mix()creates state colors from tokens- semantic tokens are better than raw color names in components
- relative colors are powerful but should be introduced deliberately
- modern color needs support checks and contrast checks
The next lesson starts motion with transitions and transforms.