¿Odias los anuncios? Ir Sin publicidad Hoy

OKLCH y colores CSS modernos — ¿Por qué tu paleta parece incorrecta en algunos navegadores?

Actualizado en

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.

OKLCH y los colores modernos de CSS — Por qué tu paleta se ve mal en algunos navegadores 1
ANUNCIO · ¿ELIMINAR?

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ísticaCromoFirefoxSafari
oklch()111+113+15.4+
color-mix()111+113+16.2+
Sintaxis de color relativo119+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:

  1. 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.
  2. 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.
  3. 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.

¿Quieres eliminar publicidad? Adiós publicidad hoy

Instalar extensiones

Agregue herramientas IO a su navegador favorito para obtener acceso instantáneo y búsquedas más rápidas

añadir Extensión de Chrome añadir Extensión de borde añadir Extensión de Firefox añadir Extensión de Opera

¡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!

ANUNCIO · ¿ELIMINAR?
ANUNCIO · ¿ELIMINAR?
ANUNCIO · ¿ELIMINAR?

Noticias Aspectos técnicos clave

Involucrarse

Ayúdanos a seguir brindando valiosas herramientas gratuitas

Invítame a un café
ANUNCIO · ¿ELIMINAR?