Go Back

Mar 08, 2026

preview

OKLCH in CSS: perché abbandonare RGB e HSL

Guida pratica a OKLCH in CSS: come creare palette bilanciate, sostituire HSL e RGB e migliorare il tuo design system con esempi di codice.


Il problema che probabilmente non sai di avere

Hai mai costruito una palette di colori in HSL, scelto valori di lightness identici per ogni tonalità, e poi notato che il giallo sembrava molto più chiaro del blu — anche se tecnicamente erano “uguali”?

Non è un bug del browser. È un limite strutturale di come HSL e RGB rappresentano il colore.

HSL è stato un grande passo avanti rispetto a RGB: finalmente potevamo ragionare in termini di tonalità, saturazione e luminosità invece di mischiare canali rossi, verdi e blu. Ma HSL ha un difetto fondamentale — la sua L non corrisponde a come il nostro occhio percepisce la luminosità. Due colori con L: 50% possono sembrare completamente diversi in termini di brillantezza percepita.

Ecco perchè esiste OKLCH: uno spazio colore progettato attorno alla percezione umana, non attorno alla fisica dei monitor.


Cosa sono RGB, HSL e OKLCH: un confronto rapido

Prima di entrare nei dettagli di OKLCH, vale la pena capire dove si posiziona rispetto agli spazi colore che già conosci.

RGB

RGB descrive un colore come combinazione di rosso, verde e blu — i tre canali dei pixel dei monitor. È il linguaggio nativo degli schermi, ma è del tutto anti-intuitivo per chi progetta:

color: rgb(41, 182, 246); /* Che colore è? Impossibile dirlo ad'occhio */

Non c’è modo di ricavare visivamente tonalità o luminosità da questi tre numeri. RGB è ottimo per le macchine, pessimo per gli esseri umani.

HSL

HSL nasce proprio per risolvere questo: esprime il colore come Hue (tonalità, 0–360°), Saturation (saturazione, 0–100%) e Lightness (luminosità, 0–100%).

color: hsl(199, 89%, 56%); /* Molto meglio: azzurro chiaro */

È molto più leggibile, ma ha il problema della luminosità non uniforme. Guarda questo esempio:

/* Stesso valore L: 60% — ma percettivamente NON uguali */
.giallo  { color: hsl(60,  90%, 60%); }
.blu     { color: hsl(220, 90%, 60%); }
.verde   { color: hsl(120, 90%, 60%); }

Il giallo sembrerà molto più luminoso del blu. La L di HSL è una bugia (ben intenzionata).

OKLCH

OKLCH usa anch’esso tre assi, ma costruiti su modelli percettivi della visione umana:

color: oklch(0.72 0.18 199); /* Azzurro chiaro, lightness davvero uniforme */

La differenza fondamentale: se due colori OKLCH hanno lo stesso valore L, appaiono davvero alla stessa luminosità percepita. Questo cambia tutto quando si tratta di costruire palette.

CaratteristicaRGBHSLOKLCH
Leggibilità umana
Lightness percettivamente uniforme
Accesso all’intera gamma P3
Adatto per design system⚠️

La struttura di oklch() in dettaglio

color: oklch(L C H);
/* oppure con alpha */
color: oklch(L C H / alpha);

Lightness (L)

Va da 0 (nero) a 1 (bianco). A differenza di HSL, questo valore è percettivamente lineare: 0.5 è davvero a metà strada tra il nero e il bianco per il nostro occhio.

oklch(0.2 0.15 250)  /* Blu molto scuro */
oklch(0.5 0.15 250)  /* Blu medio */
oklch(0.8 0.15 250)  /* Blu chiaro */

Chroma (C)

Rappresenta l’intensità o saturazione del colore. 0 è un grigio neutro, valori più alti producono colori più vividi. Il massimo dipende dalla tonalità e dallo spazio colore del dispositivo — su display P3 si può arrivare a circa 0.37.

oklch(0.6 0.0  250)  /* Grigio neutro */
oklch(0.6 0.1  250)  /* Blu desaturato */
oklch(0.6 0.25 250)  /* Blu vivido */

Hue (H)

La tonalità in gradi, come in HSL. Ma attenzione: la disposizione non è identica. In OKLCH, approssimativamente:

oklch(0.65 0.2 30)   /* Arancione */
oklch(0.65 0.2 145)  /* Verde */
oklch(0.65 0.2 250)  /* Blu */
oklch(0.65 0.2 320)  /* Rosa/magenta */

Il vantaggio principale: costruire palette coerenti

Questo è il motivo per cui OKLCH sta conquistando il mondo del CSS moderno. Costruire una palette armonica è banale.

Palette monocromatica

Tieni fissi C e H, varia solo L:

:root {
  --brand-900: oklch(0.20 0.18 250);
  --brand-700: oklch(0.35 0.18 250);
  --brand-500: oklch(0.50 0.18 250);
  --brand-300: oklch(0.70 0.18 250);
  --brand-100: oklch(0.90 0.18 250);
}

Risultato: cinque varianti dello stesso blu, percettivamente bilanciate. In HSL avresti dovuto aggiustare manualmente ogni valore perché la luminosità reale non è uniforme.

Palette complementare

Stesso L e C, ruota H di 180°:

:root {
  --primary:     oklch(0.55 0.22 250); /* Blu */
  --complementary: oklch(0.55 0.22 70);  /* Arancione/giallo */
}

Entrambi i colori avranno la stessa luminosità percepita e la stessa intensità visiva — qualcosa di molto difficile da ottenere con HSL.

Palette analogica

Sposta H di piccoli incrementi:

:root {
  --color-1: oklch(0.60 0.20 220); /* Ciano */
  --color-2: oklch(0.60 0.20 250); /* Blu */
  --color-3: oklch(0.60 0.20 280); /* Indaco */
}

Palette in CSS con custom properties: esempio completo

:root {
  /* Colore primario */
  --hue-primary: 250;
  --chroma-primary: 0.20;

  --primary-100: oklch(0.95 calc(var(--chroma-primary) * 0.3) var(--hue-primary));
  --primary-300: oklch(0.80 calc(var(--chroma-primary) * 0.6) var(--hue-primary));
  --primary-500: oklch(0.60 var(--chroma-primary) var(--hue-primary));
  --primary-700: oklch(0.40 var(--chroma-primary) var(--hue-primary));
  --primary-900: oklch(0.25 calc(var(--chroma-primary) * 0.8) var(--hue-primary));

  /* Basta cambiare --hue-primary per ottenere un nuovo tema */
}

Cambiando un solo valore — --hue-primary — l’intera palette si rigenera in modo percettivamente bilanciato. Prova a fare lo stesso con HSL.


OKLCH per l’accessibilità e il contrasto

La lightness percettivamente uniforme di OKLCH ha un impatto diretto sull’accessibilità. Quando costruisci combinazioni testo/sfondo, puoi usare la differenza di L come stima affidabile del contrasto percepito.

Una regola pratica:

/* Contrasto elevato: differenza L ≥ 0.5 */
.testo-su-sfondo-scuro {
  background: oklch(0.20 0.05 250); /* L = 0.20 */
  color:      oklch(0.95 0.02 250); /* L = 0.95 — differenza: 0.75 ✅ */
}

/* Contrasto insufficiente: differenza L < 0.3 */
.attenzione {
  background: oklch(0.50 0.15 250); /* L = 0.50 */
  color:      oklch(0.65 0.15 250); /* L = 0.65 — differenza: 0.15 ⚠️ */
}

Questo non sostituisce una verifica WCAG formale, ma offre un’intuizione molto più affidabile di HSL per stimare il contrasto “a occhio” mentre si progetta.


Supporto browser attuale

OKLCH è supportato nativamente in tutti i browser moderni senza prefissi:

La copertura globale attuale supera il 93% degli utenti. Per i browser legacy ancora in circolazione, la strategia consigliata è un fallback esplicito:

.elemento {
  /* Fallback per browser legacy */
  color: hsl(220, 70%, 55%);

  /* OKLCH per browser moderni */
  color: oklch(0.55 0.18 250);
}

Oppure con @supports per logiche più complesse:

@supports (color: oklch(0 0 0)) {
  :root {
    --primary: oklch(0.55 0.18 250);
  }
}

@supports not (color: oklch(0 0 0)) {
  :root {
    --primary: hsl(220, 70%, 55%);
  }
}

Se usi PostCSS, il plugin postcss-oklab-function converte automaticamente oklch() in valori compatibili in fase di build — zero compromessi nel codice sorgente, massima compatibilità in produzione.


Tool e risorse

Editor e picker

Librerie e integrazioni

Letture approfondite


Conclusione

OKLCH non è solo una sintassi più moderna per scrivere colori in CSS. È un cambio di paradigma nel modo di pensare al colore quando si progetta un’interfaccia.

Con RGB ragionavi in canali fisici. Con HSL ragionavi in tonalità e luminosità — ma quella luminosità era inaffidabile. Con OKLCH ragioni finalmente in termini di come il colore viene percepito: palette coerenti senza aggiustamenti manuali, contrasti prevedibili, design system che si scalano cambiando un singolo valore.

Il supporto browser è ormai maturo, i tool sono eccellenti e l’integrazione con l’ecosistema CSS moderno (variabili, calc(), Tailwind) è nativa.

Se stai costruendo qualcosa oggi, non c’è motivo reale per non iniziare con OKLCH.