HEX, RGB, HSL y los espacios de color más nuevos
Un color en CSS son tres o cuatro números más una regla para leerlos: el espacio de color. Las paletas que se ven desiguales en pantalla suelen estar escalonadas en un espacio que no encaja con el funcionamiento de la visión.
HEX y RGB son los mismos números
#3B82F6 son tres bytes en base 16: 3B es 59, 82 es 130 y F6 es 246, así que rgb(59, 130, 246) es el mismo color en base 10, y un conversor de colores pasa de uno a otro sin pérdida.
La forma abreviada de tres dígitos duplica cada dígito en vez de rellenarlo, así que #F80 se expande a #FF8800, no a #F08000; solo 4.096 colores tienen forma corta. #RGBA añade un dígito de alfa de la misma manera y #RRGGBBAA escribe los cuatro bytes completos, con ese byte de alfa yendo de 0 a 255 (1A es el 10%, 80 es el 50%, CC es el 80%), mientras que el resto de alfas de CSS van de 0 a 1. La sintaxis moderna también admite componentes separados por espacios con el alfa después de una barra: rgb(59 130 246 / 50%).
Estos números no son cantidades de luz. sRGB codifica cada canal a través de una curva de transferencia de aproximadamente 2,2, así que 128 emite alrededor del 21,6% de la luz que emite 255, no la mitad, y toda mezcla calculada sobre los valores en bruto hereda esa curva.
HSL, HSV y la luminosidad que no es luminosidad
HSL reorganiza el mismo cubo RGB en un cilindro: el tono es un ángulo (0 rojo, 120 verde, 240 azul), la saturación es la distancia al eje gris central y la luminosidad va de 0, pasando por el 50%, que es el tono puro, hasta el 100%, que es blanco. HSV cambia la luminosidad por el valor, cuya cima es el tono puro en lugar del blanco, así que los tintes salen de bajar la saturación. Ninguno de los dos sabe nada de percepción.
| Color | HSL | Luminancia relativa | Luminosidad en OKLCH |
|---|---|---|---|
#FFFF00 | hsl(60 100% 50%) | 0,928 | 0,97 |
#0000FF | hsl(240 100% 50%) | 0,072 | 0,45 |
Los dos declaran un 50% de luminosidad; uno emite unas trece veces más luz que el otro. Una fila de tokens con todos los tonos fijados al 50% se lee como un revoltijo de pesos.
Escalonar la luminosidad de HSL con el tono y la saturación fijos falla por el mismo motivo, más uno adicional: la saturación se mide contra el espacio disponible en esa luminosidad, así que el paso del 50% es el más colorido y todo lo que queda a los lados se lava. El ángulo de tono también se mantiene mientras el croma percibido se hunde, que es lo que hace que los pasos intermedios se vean turbios. No son el color base oscurecido, son el color base agrisado en una cantidad que no eligió nadie.
El amarillo enseña lo peor. Bajar la luminosidad de HSL lleva un amarillo directo al oliva, porque un amarillo oscuro en ese ángulo de tono es sencillamente oliva, y mantenerlo amarillo obliga a girar el tono hacia el naranja mientras baja. El azul tiene el problema contrario: está tan bajo de luminancia que toda su variación aprovechable vive por encima del 50%.
LCH y OKLCH
LCH es la forma polar de CIE Lab, construida a partir de experimentos de igualación de color: luminosidad de 0 a 100, croma (colorido, sin máximo fijo) y un ángulo de tono. OKLCH tiene la misma forma, pero sobre Oklab, que corrige los desplazamientos de tono por los que Lab es conocido, sobre todo los azules que se van hacia el morado al aclararse.
.a { color: oklch(0.62 0.21 260); } /* L 0 to 1, C about 0 to 0.37, H in degrees */
.b { color: oklch(62% 0.21 260 / 50%); } /* percentages and alpha both allowed */
.c { color: lch(52% 82 285); } /* L runs 0 to 100 here */
Uniformidad perceptual significa que una distancia numérica igual se acerca a una distancia percibida igual. Una rampa en pasos de 0,08 de luminosidad se ve como pasos regulares, una fila de tonos fijados a una misma luminosidad se lee como una sola fila, y cambiar la luminosidad deja el tono donde lo pusiste. La interpolación también puede usar ese espacio, como en linear-gradient(in oklch, #0000FF, #FFFF00). Una advertencia: la luminosidad de OKLCH está mucho más cerca del brillo aparente que la de HSL, pero no es la fórmula de luminancia de las WCAG, así que los pares de texto siguen pasando por un comprobador de contraste.
Gamut y recorte
Un gamut es el conjunto de colores que un espacio puede representar. sRGB es el valor por defecto de toda la vida; Display P3 cubre aproximadamente una cuarta parte más del color visible, y casi toda esa ganancia está en los rojos y verdes profundos. El croma en OKLCH no tiene límite, así que escribir un color que ninguna pantalla puede mostrar es fácil.
@supports (color: color(display-p3 1 1 1)) {
.vivid { background: color(display-p3 0.9 0.1 0.14); }
}
El recorte canal a canal devuelve un color fuera de gamut fijando cada canal en su límite de forma independiente, lo que desplaza el tono y aplasta tonalidades vecinas sobre un mismo valor, de modo que los degradados hacen bandas y el detalle desaparece. CSS Color 4 hace en su lugar un mapeado: mantiene la luminosidad y el tono y reduce el croma en OKLCH hasta que el color cabe.
Por eso una captura de pantalla de una foto vistosa puede verse plana. La foto lleva un perfil P3 y la pantalla la muestra entera; una exportación convertida a sRGB aprieta esos píxeles saturados dentro de un volumen más pequeño, y los rojos y los verdes son los que más camino tienen que recorrer.
Composición con alfa
Un color semitransparente sobre un fondo produce un tercer color, opaco, canal a canal:
result = alpha * source + (1 - alpha) * backdrop
Negro al 50% sobre blanco da (127.5, 127.5, 127.5), es decir, #808080. De esa coincidencia viene la idea de que «el negro al 50% de opacidad es gris», y se cumple sobre blanco y en ningún otro sitio: ese mismo negro sobre #3B82F6 da alrededor de #1E417B, un azul oscuro, y sobre una fotografía da un color distinto en cada píxel. El negro semitransparente es un operador de oscurecimiento, no un gris.
La composición trabaja sobre los valores codificados, así que ese #808080 tiene una luminancia relativa de alrededor de 0,216 y no de 0,5. Mide el hexadecimal compuesto, nunca el color declarado. El alfa tampoco se acumula como se espera: dos capas al 50% cubren un 75%, porque la segunda solo actúa sobre lo que dejó pasar la primera.
Tintes, sombras, tonos y armonías
| Término | Receta tradicional | En OKLCH |
|---|---|---|
| Tinte | base más blanco | subir L y bajar C un poco |
| Sombra | base más negro | bajar L |
| Tono | base más gris | bajar C con L fija |
Las armonías son desplazamientos de tono. Sirven para elegir un acento; no construyen un sistema.
| Armonía | Desplazamientos | Buena para |
|---|---|---|
| Complementaria | +180° | un acento frente a un tono dominante, aunque vibra cuando los dos están saturados |
| Análoga | ±30° | agrupaciones tranquilas, fondos, estados dentro de una misma familia |
| Triádica | +120°, +240° | conjuntos categóricos como las series de un gráfico |
Los desplazamientos sobre la rueda RGB no son perceptualmente regulares, porque esa rueda dedica un arco desproporcionado al verde, así que trata una armonía calculada como un primer borrador y ajústala a ojo. Un generador de paletas que construye tintes, sombras y armonías a partir de una sola base pone ese borrador en pantalla enseguida.
La rampa de neutros necesita una decisión propia. Un gris puro, con rojo, verde y azul iguales, queda junto a una interfaz con color con aspecto muerto y ligeramente sucio. Dale a los neutros un croma pequeño, de 0,005 a 0,02 aproximadamente en OKLCH, cerca del tono de la marca: los grises cálidos en torno a 60° o 90° se leen como papel, y los grises fríos en torno a 250° se leen como técnicos. Reduce ese croma progresivamente hacia los pasos más claros para que los fondos no parezcan teñidos.
Daltonismo
Alrededor de 1 de cada 12 hombres y 1 de cada 200 mujeres tienen una deficiencia en la visión del color. Los tipos rojo-verde suponen casi todos los casos, y la deuteranomalía por amplio margen; los tipos tritán, azul-amarillo, afectan a menos de 1 de cada 10.000.
Las confusiones que importan en las interfaces son el rojo frente al verde con luminosidad parecida, el verde frente al marrón y al naranja, el morado frente al azul y el rosa frente al gris. La visión protán además oscurece los rojos, así que un rojo elegido para transmitir urgencia puede acabar siendo lo más discreto de la pantalla.
Por eso el color nunca debe ser el único portador de significado. Acompaña cada estado con una etiqueta, un icono, una forma o una posición, y haz que los estados emparejados se diferencien en luminosidad además de en tono, porque un rojo y un verde con la misma luminosidad OKLCH son la peor combinación posible. Convertir una captura de pantalla a escala de grises detecta esas colisiones en segundos.