Las Custom Properties no son variables CSS. Pero molan 24.5.16
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
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-
@Kseso We wanted a consistent notation between all the custom things, and settled on -- prefix.
— ⓉⒶⒷ (ノ◕ヮ◕)ノ*:・゚✧ (@tabatkins) julio 23, 2014
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(--*);
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 ;-)
Kseso
the obCSServer ᛯ 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 Kseso
Bueno con postCSS podemos utilizarlas a través de este plugin(https://github.com/postcss/postcss-custom-properties) pero hay que tener en cuenta que lo utilizamos antes de compilar, lo que hace el plugin al final es que lo puedas utilizar en el INPUT del CSS pero al final en el OUTPOUT lo transforma en CSS normal. Si que es posible pasarle la opción para que las deje tal cúal(preserve: true) pero claro esto a día de hoy es una locura ya que nadie las soporta.
ResponderEliminarGracias Jorge por la información.
EliminarPongo tu enlace en forma funcional.
Un saludo
En la portada de la web de postCSS (http://postcss.org) se destaca el plugin CSSNext (http://cssnext.io/) para trabajar hoy con la sintaxis de mañana.
EliminarEl plugin de PostCSS solo simula la sintaxis, pero no la funcionalidad. El porqué lo ha explicado Jorge Aznar en su comentario (los valores se computan al "compilar").
ResponderEliminarEsto implica que usar esa herramienta lo único que aporta son variables como las que ya teníamos en los preprocesadores, solo que con la sintaxis de las "custom properties" de la especificación.
Considero que, debido a lo expuesto, no es recomendable usar dichas herramientas para simular las "custom properties", pues el hecho que de no funcionen igual que en la especificación puede dar lugar a confusión cuando se decida hacer el "switch" y eliminar el plugin (cuando los navegadores los soporten), además de la confusión que podría generar a desarrolladores que no conozcan la especificación.
Primero, es responsabilidad del desarrollador conocer la especificación y saber por qué utiliza una herramienta u otra. Si le crea confusión es porque no sabe lo que está haciendo. Eso se soluciona leyendo la documentación. Utilizar la sintaxis de la especificación precozmente, hará que el desarrollador se acostumbre a utilizarlo antes, sin más. Cuando no necesite el plugin que le aporta el soporte se eliminará y fin. No veo ningún problema en su uso, a no ser que todavía el plugin no esté estable y dé problemas, pero eso es otro tema.
Eliminarno es recomendable usar dichas herramientas...
EliminarIván dixit
Y me gusta, suscribo y aún más, extiendo a un "no deberías usar ninguna herramienta... mientras no domines un mínimo la materia sobre la que se aplica".
Ni editores que autocompleten las declaraciones.
Con lo que también resulta que estoy de acuerdo con Nabaroa
Ya sabéis que yo sólo soy un enredique de CSS. Pero en mi ignorancia cada día me sorprende más a menudo que evangelizadores de los procesadores resulta que encuentran dificultad en construir en puro CSS un selector determinado o aquél valor que cubre sus necesidades.
Pero este es otro tema para otra noche.
Sólo espero que con el empujón que pueda suponer el soporte anunciado por Chrome comencemos a ver "equivalencias" --*: value imaginativas que faciliten la vida de quienes estáis en la producción.
Un saludo y gracias por vuestra aportación.
Tampoco veo problema alguno en utilizar algo para acostumbrarte en el futuro, es más es por lo que las voy a empezar a utilizar. Me parece positivo el poder tener la oportunidad de utilizarlas sin esperar a la lenta implementación de algunos navegadores.
EliminarLo de no deberías usar ninguna herramienta que no conoces es de sentido común. Pero bueno ya sabemos lo que hace la gente con los manuales de instrucciones :)
Buenos días Kseso, lo primero agradecer tus buenas aportaciones al mundo hispano en la que creo que es tu especialidad, te seguimos y hablamos bien de ti muchos. Lo segundo es que me tomé la libertad de analizar tu blog con la herramienta de metricspot y observé resultados fantásticos en su valoración, pero comentan casi 300 errores ( aunque sean unos pocos en verdad, repetidos a lo largo del código ). Esto es lo mas común que hay en internet, comprobar que la mayoría de webs tienen infinidad de errores. Ya que todas las webs funcionan y los navegadores son muy permisivos, podrías dedicar un artículo a explicar los errores más comunes y si merece la pena perder tiempo en resolverlos? Muchas gracias
ResponderEliminarHola Daniel
EliminarEl tema de los analizadores y/o "validadores" es interesante pero complejo. Y con el tiempo la importancia que se le ha ido otorgando creo que ha menguado.
Creo que hoy se toman sus resultados más como otra herramienta de análisis que como dogma y obligado cumplimiento. Entre otras cosas por la fiabilidad de sus resultados.
Respecto al blog.
Es un tema pendiente. Se que debe marcar como varios cientos de errores. Unos que no lo son (prefijos, nuevas declaraciones que marcan como fallos o advertencias...) y seguro que otros serán realmente errores.
Pero entre unas cosas y otras y como no he notado malfuncionamientos cuyo origen pueda estar ahí... pues ahí está pendiente su análisis y actuación.
Quizás en un futuro me aventure en tu sugerencia.
Un saludo
Y yo con las ganas de usar ya, pero ya, las variables, supongo que me espero hasta el otro año a ver como nos va. De resto, te comparto este articulo en donde pueda y las veces que sea.
ResponderEliminarGracias por el esfuerzo!
Vuelvo yo a meter mi cucharada, justo después de un problema propio como cosa rara, existirán propiedades css que no podrán ser "customizadas"?? lo digo porque lo intente con una imagen (obviamente en juego):
Eliminar:root{--img-logo: patterns/sar-logo-bkg-white.png;} .thing{background-image: url('var(--img-logo)');} & :root{--img-logo: url('patterns/sar-logo-bkg-white.png');} .thing{background-image: var(--img-logo);} y no me funciono, por lo que quizá o no admiten cualquier valor o propiedad o no es "compatible" hoy con background-imagen chrome o yo estoy algo mal, sea como sea, me entra la duda y ya me leí la documentación de la w3c (bueno admito, que fue una escaneada rápida combinado con un ctrl + f) y no vi una tabla con propiedades que si admite o no. :(
La forma correcta, g3kdigital es la segunda.
EliminarSi te tomas un momento para analizar la primera verás el porqué no funcionará nunca:
[code]
:root{--img-logo: patterns/sar-logo-bkg-white.png;}
[/code]
devolverá el token patterns/sar-logo-bkg-white.png
[code]
.thing{background-image: url('var(--img-logo)');}
[/code]
var(--img-logo) así usado ya no es un 'custom valor'. Es una cadena textual interpretada como la DIR de la imagen. Y por supuesto en ese destino (que no existe) no se encuentra ninguna imagen.
En la segunda
[code]
:root{--img-logo: url('patterns/sar-logo-bkg-white.png');}
.thing{background-image: var(--img-logo);}
[/code]
algo ajeno al CSS es la causa de no mostrarse la imagen.
var(--img-logo) devuelve un valor válido para la propiedad background-image: una DIR bien conformada.
Otra cosa es que en ella se encuentre o no un recurso de imagen, o que falle el servidor al procesar la petición, o que...:
[pen]data-height="500" data-theme-id="299" data-slug-hash="GqZVZa" data-default-tab="css,result" data-user="Kseso" data-embed-version="2" class="codepen"[/pen]
La cuestión es que solo lo estaba probando en local y solo me sirvio si colocaba la url absoluta y no relativa. Gracias por tan detallada respuesta. +1
Eliminar