Why OKLCH
is the future of colors in CSS


HSL
OKLCH
Andrey Sitnik, Evil Martians

😓
😍
CSS evolved a lot
--big-font: 18px; // Variables
.title {
&.is-center { // Nesting
// Vertical centering
display: flex;
align-items: center;
justify-content: center;
}
}
@sitnikcode
Changes are coming
to CSS colors too
@sitnikcode
New color functions in CSS Color 4
color(display-p3)
color(rec2020)
color(xyz)
color(srgb-linear)
color(a98-rgb)
…
hwb()
lab()
lch()
oklab()
oklch()
@sitnikcode
Change 1
P3 colors

@sitnikcode
New screens
show more colors
Many new Apple devices
already support P3
Image by astramael.com
Change 2
.error {
background: hsl(from var(--accent) 10 s l);
}
@sitnikcode
Native color transform in CSS Color 5
Change 3
Section 2
Relative colors
/* H S L */
hsl(from hsl(5 6 7) 15 16 17) → hsl(15 16 17)
hsl(from hsl(5 6 7) h s 17) → hsl(5 6 17)
hsl(from hsl(5 6 7) h s calc(l + 10)) → hsl(5 16 7)
@sitnikcode
Topic 1: How relative colors will work
/* A little lighter */
hsl(from var(--accent) h s calc(l + 10%))
/* Error from accent */
hsl(from var(--accent) 10deg s l)
@sitnikcode
Better with Custom Properties
hsl(from var(--accent) h s calc(l - 10%))
vs
darken($accent, 10%)
@sitnikcode
Why not darken()
from Sass?
darken(#7aae73, 40%)
darken(#40b1b2, 40%)
@sitnikcode
Because darken()
is bad
Same
↓ darker
↑ lighter
@sitnikcode
darken()
is bad because it uses HSL


HSL
OKLCH
😓
😍
Section 3
Why HSL is bad
@sitnikcode
HSL is a cylindrical color model

Hue
Saturation
Every hue has the same saturation levels
@sitnikcode
Eye color sensitivity is complex
Image by Wikipedia
@sitnikcode
OKLab & Lab* are more accurate
Hue
Every hue has different saturation levels
Saturation

* — Lab/LCH color space is a little different
@sitnikcode
HSL deforms space


How human eye sees colors
HSL deforms it to rectangle
@sitnikcode
Transforms are not accurate


This is why darken()
works differently on different hues
Native color transforms (CSS Color 5)
are better than Sass’ darken()
because we can choose color model
/* Bad: unpredictable */
hsl(from var(--accent) h s calc(l + 10%))
/* Good */
oklch(from var(--accent) calc(l + 10%) c h)
Section 3
Choosing the best color functions
@sitnikcode
What color function do we need?
P3 support
CSS native support
Good for color transformation
@sitnikcode
Old hex
, rgb()
, hsl()
are bad
P3 support
Good for color transformation
CSS native support
@sitnikcode
New color spaces in CSS Color 4
color(display-p3)
color(rec2020)
color(xyz)
color(srgb-linear)
color(a98-rgb)
…
hwb()
lab()
lch()
oklab()
oklch()
@sitnikcode
P3 support
color(display-p3)
color(rec2020)
color(xyz)
color(srgb-linear)
color(a98-rgb)
…
hwb()
lab()
lch()
oklab()
oklch()
@sitnikcode
OKLab and Lab are future-proof
sRGB

P3
Rec2020
Far future


@sitnikcode
Good for color transformation
color(display-p3)
color(rec2020)
color(xyz)
color(srgb-linear)
color(a98-rgb)
…
hwb()
lab()
lch()
oklab()
oklch()
@sitnikcode
OKLCH vs. HSL


Deformed space
Closer to the human eye
HSL
OKLab, OKLCH, Lab*, LCH*
* — Lab/LCH color space is a little different
@sitnikcode
OKLCH vs. OKLab / LCH vs. Lab

Chroma
Hue

A
B
LCH
Lab
Same space. Polar coordinates vs. Cartesian coordinates
Abstract
Readable
@sitnikcode
OKLCH vs. LCH

Lab in blue has hue shift on chroma changes
OKLab was created to fix this problem
@sitnikcode
The winner: OKLCH
P3 support
Good for color transformation
CSS native support
Section 4
How to use
/* No comma */
rgb(1, 2, 3) → rgb(1 2 3)
/* No special function for alpha */
rgba(1, 2, 3, 0.5) → rgb(1 2 3 / 0.5)
/* % for alpha */
rgb(1 2 3 / 0.5) → rgb(1 2 3 / 50%)
/* The same for oklch() and all other color functions */
oklch(70% 0.1 330 / 50%)
@sitnikcode
New color syntax in CSS Color 4
@sitnikcode
OKLCH axis

L
C
H
Hue
Chroma
Lightness
oklch(L% C H / ALPHA)
0%
100%
0
0.4*
0
360
* — technically, chroma is unlimited, not sRGB, P3 or Rec2020 > 0.4
oklch( 60% 0.2 20) /* red */
oklch( 60% 0.2 270) /* blue, same C and L */
oklch(100% 0 0) /* white */
oklch( 0% 0 0) /* black */
@sitnikcode
OKLCH examples (colors now readable)
@sitnikcode
Problem 1: Browser support

Safari only
@sitnikcode
Solution: PostCSS Preset Env
{
"plugins": {
"@csstools/postcss-oklab-function": {}
}
}
@sitnikcode
Problem 2: Invisible colors

oklch(62% 0.2 20)
oklch(62% 0.2 270)
oklch(62% 0.2 100)
@sitnikcode
Solution: LCH color picker

Section 5
Benefits
@sitnikcode
Benefit 1: Readability
oklch(75% 0.2 320) /* light purple */
oklch(45% 0.2 320) /* dark purple */
oklch(75% 0.1 320) /* faded purple */
oklch(75% 0.2 140) /* complementary green */
@sitnikcode
@sitnikcode
Benefit 2: P3 ready, rich colors on Apple
@sitnikcode


P3 is new retina
@sitnikcode
Benefit 3: Editable colors in code
@sitnikcode
button {
background: oklch(75% 0.2 320);
&:hover {
background: oklch(70% 0.2 320);
/* 5% darker */
}
}
@sitnikcode
Benefit 4: Analyzable
@sitnikcode
if (l > 0.5) {
return 'color: black'
} else {
return 'color: white'
}

Black text is readable
White text is readable
@sitnikcode
Benefit 5: A11y palettes generation
@sitnikcode

Huetone uses Lab* to generate palette with predictable contrast
* — OKLCH works too
@sitnikcode
Benefit 6: Colors understanding
@sitnikcode



@sitnikcode
Benefit 7: Understanding designers
@sitnikcode

The end
During my talk (20 minutes)
EU paid Russia ≈€5 169 600
for fossil fuels (≈€4300 per second)
Bring nuclear back. All gas is bloody.
Source: CREA analysis
Why OKLCH is the future of colors in CSS
By Andrey Sitnik
Why OKLCH is the future of colors in CSS
- 641