Variables de CSS: ¿Por qué debería importarte?

Las variables de CSS, más conocidas como propiedades personalizadas de CSS, llegarán a Chrome 49. Pueden ser útiles para reducir la repetición en CSS y también para obtener efectos de tiempo de ejecución potentes, como el cambio de tema y, potencialmente, la extensión o el polyfill de funciones de CSS futuras.

Desorden de CSS

Cuando se diseña una aplicación, es una práctica común reservar un conjunto de colores de la marca que se reutilizarán para mantener la apariencia de la app coherente. Lamentablemente, repetir estos valores de color una y otra vez en tu CSS no solo es una tarea tediosa, sino que también es propensa a errores. Si, en algún momento, es necesario cambiar uno de los colores, puedes caer con precaución al viento y "buscar y reemplazar" todos los elementos, pero en un proyecto lo suficientemente grande, esto podría volverse peligroso.

En los últimos tiempos, muchos desarrolladores recurrieron a preprocesadores de CSS, como SASS o MENOS, que resuelven este problema mediante el uso de variables del preprocesador. Si bien estas herramientas aumentaron enormemente la productividad de los desarrolladores, las variables que usan tienen un gran inconveniente, que es que son estáticas y no se pueden cambiar durante el tiempo de ejecución. Agregar la capacidad de cambiar variables en el tiempo de ejecución no solo abre la puerta a aspectos como los temas dinámicos de las aplicaciones, sino que también tiene importantes consecuencias para el diseño responsivo y la posibilidad de polyfill en funciones de CSS futuras. Con el lanzamiento de Chrome 49, estas funciones ahora están disponibles en forma de propiedades personalizadas de CSS.

Resumen de las propiedades personalizadas

Las propiedades personalizadas agregan dos funciones nuevas a nuestra caja de herramientas de CSS:

  • Es la capacidad que tiene un autor de asignar valores arbitrarios a una propiedad con un nombre que elija.
  • La función var(), que permite que un autor use estos valores en otras propiedades

Aquí hay un ejemplo rápido para demostrar

:root {
    --main-color: #06c;
}

#foo h1 {
    color: var(--main-color);
}

--main-color es una propiedad personalizada definida por el autor con un valor de #06c. Ten en cuenta que todas las propiedades personalizadas comienzan con dos guiones.

La función var() se recupera y reemplaza por el valor de la propiedad personalizada, lo que da como resultado color: #06c;. Siempre que la propiedad personalizada esté definida en algún lugar de tu hoja de estilo, debería estar disponible para la función var.

Al principio, la sintaxis puede parecer un poco extraña. Muchos desarrolladores preguntan: "¿Por qué no solo usar $foo para los nombres de variables?". El enfoque se eligió específicamente para que sea lo más flexible posible y, en el futuro, permita macros $foo. Para conocer la historia de fondo, puedes leer esta publicación de uno de los autores de la especificación, Tab Atkins.

Sintaxis de propiedades personalizadas

La sintaxis de una propiedad personalizada es sencilla.

--header-color: #06c;

Ten en cuenta que las propiedades personalizadas distinguen mayúsculas de minúsculas, por lo que --header-color y --Header-Color son propiedades personalizadas diferentes. Si bien pueden parecer simples en el sentido literal, la sintaxis permitida para las propiedades personalizadas es bastante permisiva en realidad. Por ejemplo, la siguiente es una propiedad personalizada válida:

--foo: if(x > 5) this.width = 10;

Si bien no sería útil como variable, ya que no sería válido en cualquier propiedad normal, podría leerse y usarse para realizar acciones con JavaScript en el tiempo de ejecución. Esto significa que las propiedades personalizadas tienen el potencial de desbloquear todo tipo de técnicas interesantes que no son posibles actualmente con los preprocesadores de CSS actuales. Por lo tanto, si estás pensando “bostezo Tengo SASS, ¿a quién le importa…?”, tómate un segundo para revisarlo. Estas no son las variables con las que estás acostumbrado a trabajar.

La cascada

Las propiedades personalizadas siguen reglas de cascada estándar, por lo que puedes definir la misma propiedad en diferentes niveles de especificidad

:root { --color: blue; }
div { --color: green; }
#alert { --color: red; }
* { color: var(--color); }
<p>I inherited blue from the root element!</p>
<div>I got green set directly on me!</div>
<div id="alert">
    While I got red set directly on me!
    <p>I’m red too, because of inheritance!</p>
</div>

Esto significa que puedes aprovechar las propiedades personalizadas dentro de las consultas de medios para ayudar con el diseño responsivo. Un caso de uso podría ser expandir los márgenes alrededor de los elementos principales de sección a medida que aumenta el tamaño de la pantalla:

:root {
    --gutter: 4px;
}

section {
    margin: var(--gutter);
}

@media (min-width: 600px) {
    :root {
    --gutter: 16px;
    }
}

Es importante señalar que el fragmento de código anterior no se puede usar con los preprocesadores de CSS actuales, que no pueden definir variables dentro de las consultas de medios. Con esta habilidad se desbloquea un gran potencial.

También es posible tener propiedades personalizadas que deriven su valor de otras propiedades personalizadas. Esto puede ser muy útil para crear temas:

:root {
    --primary-color: red;
    --logo-text: var(--primary-color);
}

La función var()

Para recuperar y usar el valor de una propiedad personalizada, deberás usar la función var(). La sintaxis de la función var() se ve de la siguiente manera:

var(<custom-property-name> [, <declaration-value> ]? )

<custom-property-name> es el nombre de una propiedad personalizada definida por el autor, como --foo, y <declaration-value> es un valor de resguardo que se usará cuando la propiedad personalizada de referencia no sea válida. Los valores de resguardo pueden ser una lista separada por comas, que se combinará en un solo valor. Por ejemplo, var(--font-stack, "Roboto", "Helvetica"); define un resguardo de "Roboto", "Helvetica". Ten en cuenta que los valores de abreviatura, como los que se usan para el margen y el padding, no están separados por comas, por lo que un resguardo adecuado para el padding se vería de la siguiente manera.

p {
    padding: var(--pad, 10px 15px 20px);
}

Con estos valores de resguardo, el autor de un componente puede escribir estilos defensivos para su elemento:

/* In the component’s style: */
.component .header {
    color: var(--header-color, blue);
}
.component .text {
    color: var(--text-color, black);
}

/* In the larger application’s style: */
.component {
    --text-color: #080;
    /* header-color isn’t set,
        and so remains blue,
        the fallback value */
}

Esta técnica es especialmente útil para aplicar temas a los componentes web que usan el DOM de la sombra, ya que las propiedades personalizadas pueden atravesar los límites de las sombras. Los autores de componentes web pueden crear un diseño inicial utilizando valores de resguardo y exponer “hooks” de temas en forma de propiedades personalizadas.

<!-- In the web component's definition: -->
<x-foo>
    #shadow
    <style>
        p {
        background-color: var(--text-background, blue);
        }
    </style>
    <p>
        This text has a yellow background because the document styled me! Otherwise it
        would be blue.
    </p>
</x-foo>
/* In the larger application's style: */
x-foo {
    --text-background: yellow;
}

Cuando usas var(), debes tener en cuenta algunos problemas. Las variables no pueden ser nombres de propiedades. Por ejemplo:

.foo {
    --side: margin-top;
    var(--side): 20px;
}

Sin embargo, esto no equivale a configurar margin-top: 20px;. En cambio, la segunda declaración no es válida y se descarta como un error.

Del mismo modo, no puedes crear (de forma simple) un valor si parte de él lo proporciona una variable:

.foo {
    --gap: 20;
    margin-top: var(--gap)px;
}

Una vez más, esto no equivale a configurar margin-top: 20px;. Para crear un valor, necesitas algo más: la función calc().

Cómo crear valores con calc()

Si nunca antes trabajaste con ella, la función calc() es una herramienta útil que te permite realizar cálculos para determinar valores de CSS. Se admite en todos los navegadores modernos y se puede combinar con propiedades personalizadas para crear valores nuevos. Por ejemplo:

.foo {
    --gap: 20;
    margin-top: calc(var(--gap) * 1px); /* niiiiice */
}

Cómo trabajar con propiedades personalizadas en JavaScript

Para obtener el valor de una propiedad personalizada durante el tiempo de ejecución, usa el método getPropertyValue() del objeto CSSStyleDeclaration calculado.

/* CSS */
:root {
    --primary-color: red;
}

p {
    color: var(--primary-color);
}
<!-- HTML -->
<p>I’m a red paragraph!</p>
/* JS */
var styles = getComputedStyle(document.documentElement);
var value = String(styles.getPropertyValue('--primary-color')).trim();
// value = 'red'

De manera similar, para configurar el valor de la propiedad personalizada en el tiempo de ejecución, usa el método setProperty() del objeto CSSStyleDeclaration.

/* CSS */
:root {
    --primary-color: red;
}

p {
    color: var(--primary-color);
}
<!-- HTML -->
<p>Now I’m a green paragraph!</p>
/* JS */
document.documentElement.style.setProperty('--primary-color', 'green');

También puedes configurar el valor de la propiedad personalizada para hacer referencia a otra propiedad personalizada en el entorno de ejecución mediante la función var() en tu llamada a setProperty().

/* CSS */
:root {
    --primary-color: red;
    --secondary-color: blue;
}
<!-- HTML -->
<p>Sweet! I’m a blue paragraph!</p>
/* JS */
document.documentElement.style.setProperty('--primary-color', 'var(--secondary-color)');

Dado que las propiedades personalizadas pueden hacer referencia a otras propiedades personalizadas en tus hojas de estilo, puedes imaginar cómo esto podría generar todo tipo de efectos interesantes del tiempo de ejecución.

Navegadores compatibles

Actualmente, Chrome 49, Firefox 42, Safari 9.1 y Safari 9.3 de iOS admiten propiedades personalizadas.

Demostración

Prueba este ejemplo para conocer todas las técnicas interesantes que ahora puedes aprovechar gracias a las propiedades personalizadas.

Lecturas adicionales

Si quieres obtener más información sobre las propiedades personalizadas, Philip Walton, del equipo de Google Analytics, escribió una introducción sobre por qué le entusiasman las propiedades personalizadas y puedes consultar su progreso en otros navegadores en chromestatus.com.