wandres.dev
COLOR II · OKLCH y los espacios perceptuales

Los tres ejes de OKLCH y qué controla cada uno

Qué es exactamente la lightness perceptual, por qué el chroma no es la saturación, cómo se reparte el círculo de tonos, y por qué el techo de croma cambia según dónde estés.

⏱ 20 min

oklch() acepta tres números y cada uno significa algo muy concreto que no coincide con lo que significaba su homónimo en HSL. Confundir chroma con saturación, o suponer que la claridad va de 0 a 100 igual que en HSL, produce paletas que se salen del gamut y rampas que no suben. Los tres ejes se entienden en diez minutos y ahorran meses de retoques a mano.

🎯 Al terminar esta lección sabrás
  • Interpretar la lightness de OKLCH y saber dónde cae el gris medio.
  • Distinguir chroma de saturación y explicar por qué el chroma no está acotado.
  • Situar los tonos principales en el círculo de hue de OKLCH.
  • Predecir cuándo una combinación de claridad y croma se sale del gamut de sRGB.

Lightness: la claridad que sí corresponde con lo que ves

L va de 0 a 1, o de 0% a 100%. Cero es negro absoluto, uno es blanco. Hasta ahí nada sorprendente. Lo interesante es dónde caen los valores intermedios.

El gris medio de sRGB, #808080, es oklch(59.99% 0 0). No 50%: 60%. La razón es que L modela claridad percibida y la percepción de claridad es aproximadamente la raíz cúbica de la luminancia, no la luminancia misma. Un gris que emite la mitad de luz que el blanco te parece bastante más de la mitad de claro. Ese cubo es el mismo que hay dentro de CIELAB y dentro de Oklab, y es la razón por la que el punto medio visual de una rampa está por encima del punto medio numérico de la luminancia.

De esa propiedad sale la única regla que necesitas para construir rampas: si quieres pasos regulares, reparte L regularmente. Y de ahí sale también el criterio para el modo oscuro, que no es “invertir la rampa” sino recolocarla: en un fondo oscuro los pasos altos de L hacen más trabajo y conviene comprimirlos.

L es además independiente del tono. Esa es la propiedad que HSL no tiene y la que hace que todo lo demás funcione. oklch(55% 0.09 29) y oklch(55% 0.09 264) son un rojo apagado y un azul apagado que pesan lo mismo en la página; puedes intercambiarlos en un diseño sin que la jerarquía visual se mueva.

Chroma no es saturación

Este es el punto donde más gente tropieza. C es croma: una medida absoluta de cuánto se aparta el color del gris de la misma claridad. La saturación es una medida relativa: croma dividido por claridad. No son el mismo concepto y se comportan de forma muy distinta.

La S de HSL está acotada entre 0% y 100% porque es relativa por construcción: mide la distancia al eje gris como fracción del máximo posible en ese punto. La C de OKLCH no tiene un máximo fijo. Es una distancia en el plano a-b y en teoría puede crecer indefinidamente; en la práctica queda limitada por el gamut de la pantalla, y ese límite depende del tono y de la claridad.

En CSS, el valor útil de C va de 0 a algo más de 0.37. Los primarios y secundarios de sRGB, que son los colores más saturados que una pantalla estándar puede producir, caen aquí:

Color sRGB OKLCH
#ff0000 oklch(62.80% 0.2577 29.23)
#00ff00 oklch(86.64% 0.2948 142.50)
#0000ff oklch(45.20% 0.3132 264.05)
#ffff00 oklch(96.80% 0.2110 109.77)
#00ffff oklch(90.54% 0.1546 194.77)
#ff00ff oklch(70.17% 0.3225 328.36)

Fíjate en el rango real de la última columna: el magenta llega a 0.32 y el cian se queda en 0.15. El cian de sRGB es la mitad de croma que el magenta, y no hay forma de conseguir un cian más intenso en una pantalla normal. Eso no es un defecto de OKLCH, es lo que hay: OKLCH simplemente te lo dice con un número en vez de con un 100% que miente.

Órdenes de magnitud para trabajar:

  • C entre 0 y 0.02: prácticamente gris. Aquí viven los grises tintados de un buen sistema de diseño.
  • C entre 0.03 y 0.08: colores apagados, superficies, fondos, bordes.
  • C entre 0.10 y 0.18: colores de interfaz, botones, estados.
  • C por encima de 0.20: solo alcanzable en algunos tonos y muy cerca del borde del gamut.

El círculo de hue

H es un ángulo en grados. Cero apunta en la dirección del eje +a de Oklab, que corresponde aproximadamente al rosa-rojo, y el ángulo crece pasando por amarillo, verde, cian, azul y magenta hasta cerrar. Los anclajes que conviene memorizar son los de la tabla anterior: rojo cerca de 29, amarillo cerca de 110, verde cerca de 142, cian cerca de 195, azul cerca de 264 y magenta cerca de 328.

Dos consecuencias directas de esos números. La primera es que el círculo no está repartido de forma regular entre las familias de color: del rojo al amarillo hay 80 grados y del amarillo al verde solo 32. Si generas seis tonos equiespaciados cada 60 grados, no obtienes las seis familias que esperas; obtienes dos verdes y ningún naranja decente. Para una paleta multitono conviene elegir los ángulos a mano.

La segunda es que el tono es angular y por tanto cíclico, lo cual importa muchísimo en cuanto interpolas. Ir de 350 a 10 son 20 grados por el camino corto o 340 por el largo, y esa elección la controlas explícitamente. En esta lección solo importa saber que existe; el control fino aparece cuando mezclas y cuando construyes degradados.

Cuando C es cero el color es un gris y H no significa nada: se dice que el componente es powerless. Por eso el gris medio de antes sale como oklch(59.99% 0 89.88) en algunas herramientas y como oklch(59.99% 0 0) en otras. Ambos son el mismo gris.

El techo del gamut cambia según dónde estés

Aquí está la parte que decide el diseño de tus paletas. Para un tono dado, el conjunto de pares L-C representables en sRGB tiene forma de triángulo curvo: sale del negro, sube hasta un vértice —el cusp, el punto de máximo croma de ese tono— y baja hasta el blanco. La posición de ese vértice cambia radicalmente de un tono a otro:

Tono Claridad del vértice Croma máximo
29.23 (rojo) 0.628 0.258
109.77 (amarillo) 0.968 0.211
142.50 (verde) 0.866 0.295
194.77 (cian) 0.905 0.155
264.05 (azul) 0.452 0.313
328.36 (magenta) 0.701 0.322

Compara esta tabla con la anterior y verás que son la misma: el vértice de un tono primario o secundario es exactamente ese primario o secundario. Tiene sentido, porque el punto de máximo croma de un gamut RGB es siempre una esquina del cubo, y las esquinas del cubo son justamente esos seis colores.

El amarillo solo alcanza croma alto cuando está casi blanco. El azul solo cuando está bastante oscuro. Ese es el mismo hecho físico del que hablábamos antes visto desde otro ángulo, y tiene una consecuencia operativa inmediata.

Si fijas una claridad y quieres el mismo croma en todos los tonos, el tono más restrictivo manda. A L igual a 0.6 el techo de croma es este:

Tono Croma máximo a L=0.6
29.23 0.246
109.77 0.131
142.50 0.204
194.77 0.102
264.05 0.217
328.36 0.276

El cian se queda en 0.102 y todos los demás pueden más del doble. Por eso una paleta multitono con L y C constantes en sRGB tiene que usar un croma alrededor de 0.09 o 0.10: no porque quede mejor, sino porque es lo máximo que aguanta el tono más estrecho.

ℹ️
Cómo lo compruebas sin fiarte de nadie

El panel de estilos de Chrome y de Safari trae un selector de OKLCH que dibuja la frontera del gamut de sRGB y la de P3 sobre el plano de claridad y croma del tono actual. Escribes oklch(60% 0.2 195) en una declaración, abres el selector, y ves el punto fuera de la región. Es la forma más rápida de calibrar la intuición sobre qué combinaciones existen.

Por qué toda paleta buena baja el croma en los extremos

Mira cualquier escala de color de un sistema de diseño que funcione y verás la misma curva: el croma sube desde casi cero en el paso más claro, alcanza su máximo en algún punto de la mitad oscura, y vuelve a bajar en el paso más oscuro. Durante años eso se explicaba como una decisión estética. No lo es: es geométricamente obligatorio. Cerca del blanco y cerca del negro el triángulo del gamut se estrecha hasta un punto, así que el croma disponible tiende a cero por los dos extremos, sea cual sea el tono. Un 50 con croma 0.15 no es que quede chillón, es que no existe en sRGB y el navegador te lo va a recortar a otra cosa. Lo que sí es una decisión es dónde pones el máximo de la curva y cómo de rápido baja, y eso sí distingue una paleta de otra. Ponerlo por encima del cusp da una escala que se ve lavada en la parte oscura; ponerlo justo por debajo da una escala con cuerpo. Y aquí está el detalle que casi nadie tiene en cuenta: como el cusp está a claridades distintas según el tono, el máximo de la curva de croma debería moverse con él. Una escala de azules quiere su croma máximo alrededor de L 0.5, y una de amarillos alrededor de L 0.9. Si aplicas la misma curva de croma a los doce tonos de tu sistema, los amarillos te van a salir turbios y los azules apagados. La solución barata es normalizar la curva por el cusp de cada tono, que es exactamente lo que hacen los generadores de paletas serios y lo que a mano casi nadie se molesta en hacer.

⚔️ Cartografía tu gamut
  1. Escribe oklch(60% C 195) y sube C de 0.05 en 0.01 hasta que el color deje de cambiar. Anota el valor.
  2. Repite con el tono 328 y compara.
  3. Encuentra a ojo el cusp del tono 264 probando claridades de 0.3 a 0.7.
  4. Convierte el color de marca de un proyecto tuyo a OKLCH y sitúalo respecto a su cusp.