OKLCH y colores CSS modernos — ¿Por qué tu paleta parece incorrecta en algunos navegadores?
RGB y HSL fueron diseñados para monitores de tubo de rayos catódicos. Aquí se explica por qué OKLCH corrige la uniformidad perceptual, cómo color-mix() cambia el mezclado de gradientes y qué permite el sintaxis de colores relativa sin un preprocesador.
Elige un azul para tu botón principal. Tiene un aspecto adecuado en tu navegador. Abres la versión de pruebas en un nuevo MacBook Pro y parece… correcto, aunque un poco plano. Tu diseñador envía una captura de pantalla desde su iPhone y el mismo botón parece notablemente más vibrante. El mismo código hexadecimal. Diferente pantalla.
Eso es la brecha del espacio P3. Y de hecho, es la parte menos interesante de por qué el modelo de colores CSS que has estado usando te está fallando. El problema más grande está en la matemática detrás del modelo HSL.
El techo del espacio sRGB
Cada color en hex y rgb() valor que hayas escrito siempre reside en el espacio de color sRGB — un estándar implementado en 1996 para monitores de CRT. El espacio de color sRGB cubre aproximadamente 35% de los colores visibles para el ojo humano. El espacio de color P3, utilizado por todos los iPhone desde 2017 y por la mayoría de los MacBook desde 2016, cubre alrededor de 45%.
Cuando escribes #3b82f6, ese color está limitado al espacio sRGB. En una pantalla P3, el navegador podría teóricamente renderizar un azul más saturado, pero tu CSS no lo pide. Dejas así el potencial de la pantalla sin explotar por defecto.
Para acceder a colores P3 directamente, deberías usar color(display-p3 0.2 0.4 0.9). Eso funciona. Pero no es por qué importa OKLCH. El mayor fallo está en la suposición básica de HSL.
La luminosidad de HSL es una mentira
HSL fue una mejora real respecto a hex. Hue, Saturation, Lightness — conceptualmente sensato, editable por humanos. El problema: la "L" no significa lo que crees que significa.
Coloca amarillo en hsl(60, 100%, 50%) y azul en hsl(240, 100%, 50%) al lado. Ambos están exactamente en 50% de luminosidad.
.yellow { background: hsl(60, 100%, 50%); }
.blue { background: hsl(240, 100%, 50%); }
El amarillo parece casi blanco. El azul parece medio oscuro. No son casi iguales en brillo percibido. Esto es la no uniformidad perceptual de HSL — el eje L no se alinea con cómo procesa la visión humana el brillo.
Esto crea un problema real al construir escalas de color. Si generas una escala de 9 pasos incrementando los valores de L en HSL, tus amarillos y cianes se verán saturados mientras que tus azules y morados se verán borrosos. Terminas ajustando valores manualmente, lo que anula por completo el propósito de tener un enfoque sistemático.
OKLCH: diseñado para la percepción
OKLCH fue diseñado por Björn Ottosson en 2020. Se basa en CIELAB (un espacio de color perceptivo de los años 1970) pero corrige sus desplazamientos conocidos de tono, especialmente el problema del morado/azul, donde ajustar el valor de saturación causaría un cambio visible en el tono percibido.
Los tres canales:
- L (Luminosidad) — de 0 a 1, uniforme perceptivamente. L 0.5 en amarillo se ve igual de brillante que L 0.5 en azul. De verdad.
- C (Croma) — comienza en 0 (gris), aumenta hasta aproximadamente 0.3–0.4 para colores altamente saturados. No hay un máximo fijo — el límite superior varía según el tono y el espacio de gamut objetivo.
- H (Tono) — de 0 a 360 grados, aproximadamente comparable al tono de HSL.
Aquí está el azul Tailwind blue-500 en ambos sistemas:
/* sRGB hex */
background: #3b82f6;
/* OKLCH equivalent — same color, different notation */
background: oklch(0.623 0.214 259.1);
/* Push chroma past sRGB — more vivid blue on P3 displays, clips gracefully on sRGB */
background: oklch(0.623 0.27 259.1);
Ese último valor está fuera del espacio de gamut sRGB. Los navegadores en pantallas P3 renderizan la versión más vibrante; los navegadores antiguos clavan en el equivalente más cercano en sRGB. Mejora progresiva con esfuerzo cero.
color-mix() y por qué el espacio de color cambia el mezclado
color-mix() fue lanzado en Chrome 111, Firefox 113 y Safari 16.2. Mezcla dos colores en una proporción — sencillo. Pero el espacio de color que especificas cambia significativamente el resultado.
/* Mixes through sRGB — midpoint is a washed-out brown-grey */
background: color-mix(in srgb, oklch(0.7 0.2 30), oklch(0.7 0.2 270));
/* Mixes through the OKLCH color wheel — midpoint is a vivid purple */
background: color-mix(in oklch, oklch(0.7 0.2 30), oklch(0.7 0.2 270));
Cuando mezclas naranja y azul en sRGB, estás promediando valores brutos de canales. El punto medio pasa por un tono desaturado de marrón-gris. En OKLCH, estás interpolando a lo largo del reloj de colores — llegas a un morado vibrante. ¿Cuál es correcto depende del propósito, pero para transiciones de interfaz, gradientes y estados de hover, la interpolación en OKLCH es casi siempre lo que necesitas.
Un uso práctico: generar un estado desactivado sin tener que codificar un segundo valor en hex.
.button--disabled {
background: color-mix(in oklch, var(--color-primary) 40%, white);
cursor: not-allowed;
}
Sintaxis de color relativo
La sintaxis de color relativo (Chrome 119+, Firefox 128+, Safari 16.4+) te permite derivar un nuevo color modificando canales individuales de uno existente:
:root {
--brand: oklch(0.623 0.214 259.1);
}
.button:hover {
/* Lighten by 8% lightness units */
background: oklch(from var(--brand) calc(l + 0.08) c h);
}
.button:active {
/* Darken and reduce chroma slightly */
background: oklch(from var(--brand) calc(l - 0.08) calc(c - 0.02) h);
}
.button--muted {
/* Drop chroma to near-zero: perceptual grey at the same lightness */
background: oklch(from var(--brand) l 0.03 h);
}
Esto reemplaza a Sass’s lighten(), darken()y desaturate() con CSS nativo — sin preprocesador, sin paso de compilación, sin valores en hex que deban mantenerse sincronizados. Y como estás en OKLCH, "clarificar" realmente se ve más claro en todos los tonos de tu paleta, no solo en algunos.
Una limitación real: las restricciones complejas de canales (como clamar el croma para mantenerse dentro del espacio de gamut) requieren cadenas largas de calc() que se vuelven confusas rápidamente. Para cambios simples de luminosidad y croma, la sintaxis es limpia. Para cualquier cosa más sofisticada, calcular valores en tiempo de compilación y generar propiedades personalizadas de CSS es más mantenible.
Soporte en navegadores en 2026
| Característica | Cromo | Firefox | Safari |
|---|---|---|---|
oklch() | 111+ | 113+ | 15.4+ |
color-mix() | 111+ | 113+ | 16.2+ |
| Sintaxis de color relativo | 119+ | 128+ | 16.4+ |
color(display-p3) | 111+ | 113+ | 10+ |
Soporte global para oklch() es superior al 90% en medio de 2026. Los que se resisten son navegadores empresariales basados en Chromium y cualquier versión anterior de Firefox que aún esté en uso. Si esos navegadores forman parte de tu base de usuarios, un fallback de dos líneas es todo lo que necesitas:
.button {
background: #3b82f6; /* sRGB fallback */
}
@supports (background: oklch(0 0 0)) {
.button {
background: oklch(0.623 0.214 259.1);
}
}
En la práctica: si tus datos de análisis confirman Chrome 111+, Firefox 113+ y Safari 15.4+, puedes omitir el fallback por completo. El @supports wrapper es para tener tranquilidad en herramientas internas donde no controlas las versiones de navegador.
Conversión de tu paleta actual
El punto de fricción con la adopción de OKLCH es pasar de tus valores actuales en hex a coordenadas en OKLCH y entender qué significan los canales para cada color. El Convertidor de espacio de color unificado maneja la conversión en ambas direcciones — pega un valor en hex o en HSL, obtén valores en OKLCH y comienza a ajustar L y C desde un punto de referencia real. Soporta HSL, RGB, Lab, LCH, HSV y OKLCH en un solo lugar, lo cual es útil cuando trabajas con un archivo de Figma que te da valores en hex y necesitas llegar a valores en OKLCH que puedas razonar.
Cómo realmente migrar
No necesitas reescribir todo de golpe. Una secuencia práctica:
- Comienza con colores interactivos — tus tokens principales, secundarios y destructivos. Estos son los que ya estás generando variantes de hover, activo y desactivado. OKLCH + sintaxis de color relativo reemplaza inmediatamente esa lógica con CSS nativo.
- Deja los colores estáticos en hex por ahora — texto, fondos, bordes. No están siendo manipulados programáticamente, así que no hay beneficio aún en convertirlos.
- Crea el próximo sistema nuevo desde cero en OKLCH — ahí es donde las ventajas de uniformidad perceptiva se vuelven estructurales. Tu escala neutra en el paso 500 realmente estará en el punto visual medio, y tus tonos saturados serán consistentes en todos los tonos sin necesidad de ajustes manuales.
El objetivo no es usar OKLCH por sí solo. Es tener un sistema de colores en el que la matemática coincida con lo que ven tus ojos — así que dejas de compensar manualmente cada vez que necesitas un azul ligeramente más claro.
Instalar extensiones
Agregue herramientas IO a su navegador favorito para obtener acceso instantáneo y búsquedas más rápidas
恵 ¡El marcador ha llegado!
Marcador es una forma divertida de llevar un registro de tus juegos, todos los datos se almacenan en tu navegador. ¡Próximamente habrá más funciones!
Herramientas clave
Ver todo Los recién llegados
Ver todoActualizar: Nuestro última herramienta se agregó el 15 de junio de 2026
