css-colores-relativos-color-mix

Colores relativos en CSS con color-mix y sintaxis moderna

  • 7 min

Un color relativo en CSS es un color calculado a partir de otro color existente.

Hasta ahora hemos escrito colores como valores cerrados: #2563eb, rgb(37 99 235) o hsl(217 91% 60%). Eso funciona muy bien, pero en una interfaz real casi nunca usamos un único color aislado. Necesitamos variantes más claras, más oscuras, más transparentes o ligeramente menos saturadas.

Durante mucho tiempo, esas variantes se calculaban a mano o con herramientas externas. Elegíamos un azul principal, abríamos una paleta, copiábamos cinco tonos parecidos y los pegábamos en CSS como si estuviéramos haciendo inventario de pinturas.

CSS moderno nos permite hacer algo bastante más cómodo: partir de un color base y generar otros colores desde el propio CSS.

El problema de las paletas duplicadas

Supongamos que tenemos un color principal para nuestra marca.

:root {
    --color-primario: #2563eb;
}
Copied!

Ahora queremos usar ese color en un botón, en un borde suave, en un fondo claro y en un estado hover.

La forma clásica sería crear todas las variantes a mano.

:root {
    --color-primario: #2563eb;
    --color-primario-hover: #1d4ed8;
    --color-primario-fondo: #eff6ff;
    --color-primario-borde: #bfdbfe;
}
Copied!

Esto no está mal. De hecho, muchas veces es perfectamente razonable. El problema aparece cuando cambiáis el color principal y todas esas variantes dejan de encajar.

Para esos casos existen funciones como color-mix() y la sintaxis relativa de color.

Mezclar colores con color-mix()

La función :key[color-mix()] crea un color nuevo mezclando dos colores en un espacio de color concreto.

.boton {
    background: color-mix(in srgb, var(--color-primario) 85%, black);
}
Copied!

La sintaxis tiene tres partes importantes.

color-mix(in srgb, color-a 80%, color-b)
Copied!
  • in srgb: Indica el espacio de color donde se hace la mezcla.
  • color-a 80%: Primer color y cuánto pesa en la mezcla.
  • color-b: Segundo color. Si no indicamos porcentaje, CSS calcula el resto.

Por ejemplo, para aclarar un color podemos mezclarlo con blanco.

:root {
    --color-primario: #2563eb;
    --color-primario-suave: color-mix(in srgb, var(--color-primario) 15%, white);
}
Copied!

Y para oscurecerlo, lo mezclamos con negro.

:root {
    --color-primario-hover: color-mix(in srgb, var(--color-primario) 85%, black);
}
Copied!

El resultado es muy práctico para estados visuales.

.boton {
    background: var(--color-primario);
}

.boton:hover {
    background: color-mix(in srgb, var(--color-primario) 85%, black);
}
Copied!

Para empezar, srgb es una opción cómoda y fácil de entender. Más adelante podéis investigar espacios como oklch u oklab, que suelen producir mezclas visualmente más uniformes.

Colores relativos

La otra gran herramienta son los colores relativos. En lugar de mezclar dos colores, podemos tomar un color existente, descomponerlo en canales y construir otro color a partir de ellos.

Los canales disponibles cambian según la función. En rgb() tenéis r, g y b. En hsl() tenéis h, s y l. En oklch() tendréis l, c y h. Cada modelo mira el color con unas gafas distintas.

Eso crea un color RGB usando como origen --color-primario. Dentro de la función aparecen nombres de canales disponibles: r, g, b y alpha.

rgb(from var(--color-primario) r g b)
Copied!

Podemos mantener los mismos canales y cambiar solo la transparencia.

.velo {
    background: rgb(from var(--color-primario) r g b / 20%);
}
Copied!

Esto significa: “usa el mismo rojo, verde y azul del color base, pero con un 20% de opacidad”.

También podemos hacer operaciones con calc().

.boton:hover {
    background: rgb(
        from var(--color-primario)
        calc(r - 20)
        calc(g - 20)
        calc(b - 20)
    );
}
Copied!

Aunque esto funciona, modificar canales RGB a mano no siempre produce colores bonitos. Bajar r, g y b puede oscurecer, sí, pero no siempre mantiene la sensación visual que esperáis.

Trabajar en HSL

Para ajustar tono, saturación y luminosidad, muchas veces resulta más cómodo usar hsl(from ...).

hsl(from var(--color-primario) h s l)
Copied!

En este caso, CSS nos da acceso a los canales h, s, l y alpha. Podemos crear una versión más clara aumentando la luminosidad.

.panel {
    background: hsl(
        from var(--color-primario)
        h
        s
        calc(l + 35)
    );
}
Copied!

Esto se lee bastante bien: mantenemos el mismo tono, tocamos la saturación o la luminosidad, y conseguimos una variante relacionada con el color original.

En los colores relativos, los canales se exponen como números. Por eso escribimos calc(l + 35) y no calc(l + 35%). Parece una tontería, pero ese % de más puede invalidar el color entero.

Cuándo usar cada uno

color-mix() es la opción más cómoda cuando queréis aclarar, oscurecer o aproximar un color hacia otro.

background: color-mix(in srgb, var(--color) 20%, white);
Copied!

La sintaxis relativa es mejor cuando queréis mantener unos canales y modificar otros con precisión.

background: hsl(from var(--color) h calc(s - 20) calc(l + 15));
Copied!

Y si solo queréis añadir transparencia manteniendo el mismo color, rgb(from ...) resulta muy claro.

box-shadow: 0 12px 30px rgb(from var(--color) r g b / 25%);
Copied!