soy Kseso y esto EsCSS

Las Custom Properties no son variables CSS. Pero molan

CSS Custom Properties for Cascading Variables Module Level 1 llamadas erróneamente en ocasiones como variables CSS. El porqué, un poco de su historia, cómo usarlas y diferencia con las variables de las herramientas para manejar CSS (procesadores).

Las Custom Properties no son variables CSS. Pero molan

·Por Kseso ✎ 13
Las Custom Properties no son variables CSS. Pero molan

Allá por inicios de 2012 un grupo de trabajo del W3c compuesto por Luke Macpherson y Tab Atkins Jr (de Google) y Daniel Glazman (por entonces co-chairman del grupo de trabajo de CSS en el W3c) publicaron el borrador titulado CSS Variables Module Level 1.

Como suele ocurrir, dicho borrador fue sufriendo notables cambios con las siguientes revisiones hasta llegar a la actual CR: CSS Custom Properties for Cascading Variables Module Level 1.

Primer cambio: el nombre

Una de las primeras cuestionadas fue el mismo nombre del documento y la denominación de la novedad (variables CSS) en él presentada.

En la segunda versión, Marzo de 2013, el nombre asignado al documento había cambiado por completo: CSS Custom Properties for Cascading Variables Module Level 1. Pero si bien es cierto que en el título ya se llaman custom properties en el cuerpo del mismo sigue usándose la expresión variable.

Pero anterior a ello uno de sus editores, Daniel Glazman, en su página ya se manifestaba el respecto en Agosto de 2012:

First, the proposed spec is NOT about variables and I seriously wonder if we should not change the title of the document. You may call the feature it introduces "variables" but at the deeper level, that's not about variables. That about Inherited User-Defined Properties. Don't take that as some political correctness or a blurb hiding the reality of the feature. The spec is really about letting you define your own properties and reaching their cascaded values.

Antes de seguir un pequeño inciso sobre gustos personales: Yo, personalmente, hubiese sido más partidario de llamarlas custom declarations en vez de custom properties. La razón: la parte 'de autor' se utiliza en ambos lados de la declaración CSS: en la propiedad y en el valor.</gusto_personal>

Así que nos encontramos con una mala denominación que pese a su pronta corrección triunfó y aún se mantiene en el imaginario colectivo. Y aún hoy (Febrero de 2016) es utilizada por actores importantes, como los chicos de Chrome, para anunciar que por fin las incluyen en su desarrollo en su próxima 49 versión.

Para comprender el porqué no son variables mejor hacerlo viendo su sintaxis.

Segundo cambio: su sintaxis

La sintaxis CSS de las custom properties (forma en que se declaran) también sufrió drásticos cambios. Desde la notación inicial var-custom: valor para declararla y propiedad: var(custom) para usarla en un selector a la actual y vigente que es:

/*declaración inicial */ :root { --color-primario: red; --color-secundario: black; } /*Uso y aplicación*/ .error { background: var(--color-primario); color: var(--color-secundario); } .ejemplo { background: var(--color-secundario); color: var(--color-primario); }

El Porqué de esta sintaxis --*

La respuesta al porqué de usar los dos guiones -- es por coherencia y para mantener uniformidad en el lenguaje de CSS. Cada vez que te encuentres algo precedido por los dos guiones significa que es un dato 'de autor'. Esto es, que le ha asignado nombre el autor del código. Tal como ocurre con el guión y el prefijo privativo (identifica un dato sólo soportado por dicho navegador) o por los atributos de 'autor' en el HTML que los identifica por data-

Ver conversación

Cómo funcionan

En primer lugar tú te inventas el nombre de una serie de propiedades precedidos de los dos guiones a los que que asignas unos valores válidos según las especificaciones.

En segundo lugar y allí donde quieras usarla en vez de tener que recordar el valor cada vez que quieras usarlo declaras el nombre de "tu propiedad".

¿Recuerdas el viejo ejemplo que dediqué a cómo Mantener relación de aspecto con Css? Pues a continuación lo mismo utilizando "custom properties":

See the Pen CSS Custom Properties & Aspect ratios by Kseso (@Kseso) on CodePen.

Pero recuerda
Allí donde uses una 'custom property' en la función CSS propiedad: var(--laMia) para el valor de una propiedad estándar tiene que ser un valor permitido para dicha propiedad. En caso contrario no computa.

O lo que es lo mismo, usando el ejemplo del código 1 previo, si declarase font-size: var(--color-primario) sería una declaración nula, pues 'red' no es un valor válido para el tamaño de letra.

Esto para un valor sencillo apenas quita trabajo. Pero si en vez de usar el del ejemplo ('red') fuese un valor en hexadecimal o un valor complejo (como un grupo de transformaciones, filtros CSS o sombras múltiples por ejemplo) la cosa cambia.

Lo mismo si va a ser usado en cienes de reglas CSS distribuidas en un gran archivo o varios de ellos. Eso en el momento de crearlos. A ello suma si necesitas cambiar posteriormente dicho valor todas y cada una de las veces que aparece.

Custom properties anidadas y recursivas

Este tipo de propiedades se pueden usar usando unas dentro de otras:

:root { --main-color: #c06; --fondo-a: linear-gradient(to top, var(--main-color), white); }

Este uso es válido. Cada vez que declaremos el valor de un fondo con la variable var(--fondo-a) el resultado será función de la equivalencia --main-color: XXX. Si se cambia ésta el gradiente mostrado también.

Sin embargo si se cae en recursividad, esto es, si una equivalencia es función de otra que a su vez depende de la primera computará en nulo. Lo siguiente es inválido:

:root { --one: calc(var(--two) + 20px); --two: calc(var(--one) - 20px); } /*MECK!!! ERROR!*/

El siguiente ejemplo tampoco será válido:

.foo { --gap: 20; margin-top: var(--gap)px; } /*MECK!!! ERROR!*/

La razón es que no devolverá un valor de longitud si no la cadena textual '20 px'. El texto 'px' no computa como unidad de medida sino como un texto. Esto se puede corregir declarando en su lugar:

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

Por qué no son variables

Fíjate en el proceso. Inicialmente lo que se hace es asignar a un valor cualquiera un nombre personalizado. Y a continuación usar dicho nombre como sustituto del valor.

Creamos una equivalencia para sustituir un valor concreto por una palabra clave 'de autor'.

Pero en ningún momento dicha equivalencia cambia si no es actuando en su declaración inicial. Tampoco su valor se modifica en función de ninguna otra condición que no sea esa declaración inicial en la que se fija la igualdad.

De hecho el documento se refiere a ellas como the --* family of properties y su finalidad:

This specification defines an open-ended set of properties called custom properties, which, among other things, are used to define the substitution value of var() functions.

...son usadas, entre otras cosas, como sustitutas del valor de la función css 'var()'

Por qué son "Cascading Variables"

O para una cascada variable

Fíjate en el siguiente código CSS:

.izq, .dch { animation: pendulem ...; /*misma animación para ambos*/ } .izq { --miGiro: rotate(25deg); } .dch { --miGiro: rotate(-25deg); } @keyframes pendulum { 0%, 50%, 100% {transform: rotate(0);} 25% {transform: var(--miGiro);} }

¿Hacia dónde giran los elementos .izq y .dch? Pese a que la animación es la misma para ambos pendulem... con el mismo keyframe 25% {transform: var(--miGiro);} el valor asociado a cada una de las dos custom properties es diferente.

En el artículo CSS custom properties acotadas y polivalentes tienes una explicación detallada.

Diferencias CSS custom properties y variables procesadores

No cabe duda que el nombre inicial y la costumbre de seguir usando la expresión variable es por influencia de las distintas herramientas para manejar CSS. Como Sass, Less, Stylus...

Recuerdo que hubo quien en un principio se preguntaba por la necesidad de esta característica CSS si ya la aportaban dichas herramientas de manejo de CSS.

En estas herramientas para manejar CSS sus variables son procesadas y convertidas a CSS estándar antes de enviar el archivo CSS al navegador. Así que para cualquier manipulación que se necesite hacer "en vivo" tras ser recibido por el navegador ya no existirán como tales y habría que buscar sus valores computados y discriminar los casos en que proceda de la variable de los que no.

OjO! Esto en sí ni es bueno ni malo, ni mejor o peor. Es símplemente cómo funcionan.

Sin embargo las "custom properties" de CSS siguen existiendo como tales en el CSS recibido por el navegador. Basta modificar el valor en la equivalencia inicial para que se propague a todas las declaraciones donde se utilice y sólo en ellas.

Como ejemplo de esta diferencia estos días de atrás se mencionó y puso de ejemplo el siguiente pen

See the Pen Update CSS Variables with JS by Wes Bos (@wesbos) on CodePen.

Si echas un vistazo con el inspector de código verás que el CSS se mantiene inalterado. Las declaraciones de autor se mantienen pero el efecto al cambiar la equivalencia afecta sólo a los elementos donde se usan como valor var(--*);

css custom properties en el inspector
css custom properties en el inspector

Otra diferencia es que mientras en CSS puedes cambiar las equivalencias --*: valor en las medias queries de regla a regla (o de elemento a elemento) en esas herramientas manipuladoras de CSS (Sass, Less,...) no (imposibilidad chivada por Jorge Aznar).

.foo { --gap: 2rem; } @media screen and (max-device-width: 600px) { .foo { --gap: .5rem; } } .miel { padding: var(--gap); }

La función var(--gap) se resuelve con un valor de .5rem en tamaños menores a 600px y de 2rem en los mayores.

Un ejemplo de cambio del valor de la equivalencia de elemento a elemento lo puedes ver en el artículo CSS custom properties acotadas y polivalentes donde aprovecho esta particularidad para lograr dos animaciones de signo contrario con una sóla regla @keyframes {}:

.izq, .dch { animation: pendulem ...; /*misma animación para ambos*/ } .izq { --miGiro: rotate(25deg); } .derecha { --miGiro: rotate(-25deg); } @keyframes pendulum { 0%, 50%, 100% {transform: rotate(0);} 25% {transform: var(--miGiro);} /*el valor computado está en función del declarado para cada --miGiro que hayamos establecido en cada regla*/ }

Ver el post explicativo y Demo

No desesperes, no todo está perdido

Pero si eres usuario de algunas de estas herramientas para manejar CSS no desesperes. Según he podido leer y me comentan por Twitter ya hay quien está trabajando para incluir las "custom properties" de CSS en ellas manteniendo todas sus ventajas.

En concreto se que tanto Jorge Aznar aka @jorgeATGU como Naiara Abaroa aka @nabaroa (por nombrarte 2 que están en mi TL en español) están trabajando en ello.

Con suerte los convencemos para que nos den alguna información detallada aunque sólo sea en un comentario ;-)

avatar del Editor del blog

Ramajero Argonauta, Enredique Amanuense de CSS.
#impoCSSible inside
Dicen que, en español, EsCss es el mejor blog de CSS. Posíblemente exageren.
@Kseso EsCss Don Kseso Kseso