Las variables CSS, oficialmente llamadas propiedades CSS personalizadas, ofrecen un amplio abanico de posibilidades imposibles de conseguir en CSS con anterioridad, a no ser que se utilizase algún preprocesador, como pueden ser SASS o LESS.

No obstante, variables y preprocesadores no son sustitutivos el uno del otro, sino complementarios. Por ejemplo, las variables CSS introducen la capacidad reactiva a los stylesheets. Si el valor de una variable CSS cambia, se actualizará automáticamente el estilo de todos los elementos afectados. Los preprocesadores, al ser precisamente pre, no pueden hacer esto.

El uso básico de variables CSS es muy sencillo. Se identifican con -- antes del nombre de la propiedad personalizada y se accede a ella con la función var(). La declaración de la variable debe ser siempre dentro de un selector CSS y es accesible en todos los selectores descendientes. Es muy habitual declarar las variables CSS en el pseudoelemento :root para que estén disponibles de forma global:

:root {
    --color-dark: #333;
}
.dark-text {
    color: var(--color-dark);
}

¿Qué son exactamente las variables CSS?

Una variable se puede entender como un contenedor que almacena un dato o valor. Si tenemos la expresión x = 5, x es el nombre de la variable y 5 es su valor. Si posteriormente tenemos la expresión x + 10, esta será evaluada como 15. Si cambiamos el valor de x, por ejemplo, x = 10, la expresión x + 10 sería evaluada como 20.

Es decir, al nombrar una variable y otorgarle un valor, podemos acceder a ese valor posteriormente en el programa, algo esencial para no tener que repetir constantemente expresiones y cálculos ya realizados, lo que permite simplificar enormemente el desarrollo.

Tan solo un detalle, las llamadas variables CSS no son técnicamente variables, pues CSS es un lenguaje declarativo. Las variables CSS son en realidad propiedades personalizadas a cuyo valor se puede acceder con la función var(). Para que se entienda, la propiedad border-radius es una propiedad CSS nativa, mientras que --border-radius-small sería una propiedad personalizada.

Cuidado con confundir la función var() de CSS con el keyword var de JavaScript. En CSS, var() se utiliza para obtener el valor de una variable; en JS, var se utiliza para declarar una variable y, opcionalmente, otorgarle un valor.

¿Cómo se utilizan las variables CSS?

Si tenemos claro que las variables CSS son en realidad propiedades personalizadas, será muy fácil recordar que se utilizan como cualquier otra propiedad, tan solo que el nombre de la propiedad lo eliges tú. El único requisito es que el nombre elegido vaya precedido por -- para que el navegador sepa que ese nombre “inventado” no es un propiedad inválida, sino una propiedad personalizada.

Por ejemplo, en el siguiente snippet se declara la variable --block-margin:

.post-body {
    --block-margin: 2rem 0;
}

Luego se obtiene el valor de esa variable con la función var():

.post-body blockquote {
    margin: var(--block-margin);
}

A la hora de poner un nombre y luego recuperarlo, te en cuenta que los nombres de propiedades diferencia mayúsculas y minúsculas: por ejemplo, --myFontSize y --myfontsize serían consideradas propiedades diferentes.

:root {
    --myvar: 2;
}
p {
    /* Variable no definida */
    font-size: var(--myVar);
}

Un detalle importante es que no se puede combinar var() con otros elementos para generar un valor, por ejemplo margin: var(--my-margin)px; sería inválido, al igual que la siguiente expresión:

:root {
    --base-font-size: 2;
}
p {
    font-size: var(--base-font-size)rem;
}

Pero se puede utilizar var() dentro de la función calc():

:root {
    --base-font-size: 2;
}
p {
    font-size: cal(var(--base-font-size) * 1rem);
}

Herencia en cascada

CSS son las siglas de Cascade Style Sheet, y es que sus propiedades se heredan en cascada, desde un elemento superior, o elemento padre, a todos los elementos descendientes, o elementos hijos. Y esta herencia en cascada afecta a todas las propiedades, también a las propiedades personalizadas.

Por ejemplo, si tenemos este CSS:

.parent {
    --margin: 5px;
}
.child {
 --margin: 5em;
 --test: 2;
}

Y este HTML:

<div class="parent">
    <div class="child"></div>
</div>

El resultado será:

  • var(--margin) igual a 5px en el elemento class="parent
  • var(--margin) igual a 5em en el elemento class="child"
  • var(--test) igual a 2 el en elemento class="child"
  • var(--test) igual a “valor no válido” el en elemento class="parent"

En el último caso, no se puede acceder al valor de la variable --test en el elemento class="parent" porque ha sido declarada en un ámbito inferior y, como se heredan en cascada descendente, la variable no es visible en dicho elemento.

Para disponer de variables en cualquier elemento o selector CSS, es decir, tener variables CSS globales, es muy habitual que las variables CSS se declaren en el pseudoelemento :root, aunque no es obligatorio y se pueden declarar variables en cualquier elemento y tener variables CSS en scopes más específicos.

Valores no válidos

Además de los “valores no válidos” debidos a la herencia en cascada que hemos visto antes, las variables CSS pueden generar valores no válidos en tiempo de ejecución. Generalmente, los valores no válidos en CSS se identifican cuando el navegador lee el valor de una propiedad, pero en el caso de las propiedades personalizadas, el navegador no sabe donde se utilizarán al momento de leer su valor, por lo que en principio acepta cualquier valor como válido. Pero si luego la variable se utiliza en un contexto no adecuado, dará lugar a un error de tipo “valor no válido”:

:root {
    /* En principio, el valor 5px es válido */
    --my-var: 5px;
}
p {
    /* 5px en la propiedad "color" producirá un "valor no válido" */
    color: var(--my-var);
}

Valores predeterminados

Con la función var() es posible definir un valor predeterminado o fallback que será utilizado si la variable no se resuelve correctamente. Por ejemplo, si no está definida, no es accesible o tiene un valor no válido.

Para establecer un valor predeterminado, escríbelo en la función var() separado con una coma después del nombre de la variable:

:root {
    --mycolor: blue;
    --my-font-size: 2rem;
}
p {
    /* myColor no está definido, tomará el valor blue */
    color: var(--myColor, blue);
    font-size:  var(--my-font-size, 2rem);
}

Trabajando con variables CSS en JavaScript

Como las variables CSS son en realidad propiedades CSS, se puede trabajar con ellas de igual forma que con cualquier otra propiedad: utilizamos getComputedStyle() para obtener todas las reglas de estilo calculadas para un determinado elemento, y luego getPropertyValue() para obtener el valor de una propiedad o variable CSS específica:

var documentStyles = window.getComputedStyle(document.documentElement);
var propertyValue = documentStyles.getPropertyValue("--myColor");

El ejemplo anterior se aplica al todo el documento, pero se puede hacer para cualquier elemento:

var elementStyles = window.getComputedStyle(document.getElementById('myelement'));
var propertyValue = elementStyles.getPropertyValue("--myColor");

Aunque no es obligatorio, es frecuente que se recomiende pasar el resultado por la función trim() para eliminar posibles espacios en blanco antes y después del valor, que son más frecuentes de lo que pueda parecer:

var elementStyles = window.getComputedStyle(document.getElementById('myelement'));
var propertyValue = elementStyles.getPropertyValue("--myColor");
propertyValue = String(propertyValue).trim();

Del mismo modo, se puede utilizar la función setProperty() para establecer el valor de una variable CSS:

var element = document.getElementById('myelement');
element.style.setProperty("--myColor", "blue");

Cuando se utiliza setProperty() para cambiar el valor de una variable CSS, todos los elementos afectados serán “repintados” en el navegador, lo que lleva la capacidad reactiva a CSS. Ejemplo:

<style>
    #parent {
        --myColor: blue;
    }
    .child {
        color: var(--myColor);
    }
</style>
<div id="parent">
    <div class="child">Some text</div>
</div>
<script>
    var element = document.getElementById('parent');
    /* El texto en el div .child cambiará a color amarillo */
    element.setProperty('--myColor',"yellow");
</script>

Y, por último, las variables CSS también se puede eliminar:

var element = document.getElementById('myelement');
element.style.removeProperty("--myColor");