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