El poder (y la diversión) del alcance con las propiedades personalizadas de CSS | Programar Plus

Probablemente ya esté al menos un poco familiarizado con las variables CSS. Si no, aquí hay una descripción general de dos segundos: en realidad se llaman propiedades personalizadas, las establece en bloques de declaración como --size: 1em y usarlos como valores como font-size: var(--size);, se diferencian de las variables del preprocesador (por ejemplo, en cascada), y aquí hay una guía con mucha más información.

Pero, ¿los estamos utilizando en todo su potencial? ¿Caemos en viejos hábitos y pasamos por alto oportunidades en las que las variables podrían reducir significativamente la cantidad de código que escribimos?

Este artículo fue motivado por un tweet reciente que hice sobre el uso de variables CSS para crear un comportamiento de animación dinámico.

Las variables CSS son increíbles, ¿verdad? Pero el poder del alcance a menudo se pasa por alto. Por ejemplo, tome esta demostración, 3 animaciones diferentes pero solo 1 animación definida 💪 Eso significa animaciones dinámicas 😎 https://t.co/VN02NlC4G8 vía @CodePen #CSS #animación #webdev #diseño web #codificación pic.twitter.com/ig8baxr7F3

— Jhey @ NodeConfEU 2019 📍🇮🇪⌨️ (@jh3yy) 5 de noviembre de 2019

Veamos un par de casos en los que las variables CSS se pueden usar para hacer algunas cosas geniales que quizás no hayamos considerado.

Victorias de alcance básico

El ejemplo más simple y probablemente más común sería el de los colores con alcance. ¿Y cuál es nuestro componente favorito para usar con el color? El botón. 😅

Considere la configuración estándar de botones primarios y secundarios. Comencemos con un marcado básico que usa una sintaxis BEM.

<button class="button button--primary">Primary</button>
<button class="button button--secondary">Secondary</button>

Tradicionalmente, podríamos hacer algo como esto para darles estilo:

.button {
  padding: 1rem 1.25rem;
  color: #fff;
  font-weight: bold;
  font-size: 1.25rem;
  margin: 4px;
  transition: background 0.1s ease;
}

.button--primary {
  background: hsl(233, 100%, 50%);
  outline-color: hsl(233, 100%, 80%);
}

.button--primary:hover {
  background: hsl(233, 100%, 40%);
}

.button--primary:active {
  background: hsl(233, 100%, 30%);
}

.button--secondary {
  background: hsl(200, 100%, 50%);
  outline-color: hsl(200, 100%, 80%);
}

.button--secondary:hover {
  background: hsl(200, 100%, 40%);
}

.button--secondary:active {
  background: hsl(200, 100%, 30%);
}

ver la pluma
Botones básicos de Jhey (@jh3y)
en CodePen.

Eso es una gran cantidad de código para algo que no es particularmente complejo. No hemos agregado muchos estilos y hemos agregado muchas reglas para adaptarse a los diferentes estados y colores del botón. Podríamos reducir significativamente el código con una variable con ámbito.

En nuestro ejemplo, el único valor diferente entre las dos variantes de botón es el tono. Refactoricemos un poco ese código entonces. No cambiaremos el marcado, pero limpiando un poco los estilos, obtenemos esto:

.button {
  padding: 1rem 1.25rem;
  color: #fff;
  font-weight: bold;
  font-size: 1.25rem;
  margin: 1rem;
  transition: background 0.1s ease;
  background: hsl(var(--hue), 100%, 50%);
  outline-color: hsl(var(--hue), 100%, 80%);

}
.button:hover {
  background: hsl(var(--hue), 100%, 40%);
}

.button:active {
  background: hsl(var(--hue), 100%, 30%);
}

.button--primary {
  --hue: 233;
}

.button--secondary {
  --hue: 200;
}

ver la pluma
Refactorización de estilos con una variable de alcance por Jhey (@jh3y)
en CodePen.

Esto no solo reduce el código, sino que facilita mucho el mantenimiento. ¡Cambie los estilos de los botones principales en un solo lugar y actualizará todas las variantes! 🙌

Probablemente lo dejaría ahí para que sea más fácil para los desarrolladores que quieran usar esos botones. Pero, podríamos llevarlo más lejos. Podríamos alinear la variable en el elemento real y eliminar las declaraciones de clase por completo. 😲

<button class="button" style="--hue: 233;">Primary</button>
<button class="button" style="--hue: 200;">Secondary</button>

Ahora no necesitamos estos. 👍

.button--primary {
  --hue: 233;
}

.button--secondary {
  --hue: 200;
}

ver la pluma
Alcance con variables CSS en línea por Jhey (@jh3y)
en CodePen.

Incluir esas variables puede no ser lo mejor para su próximo sistema de diseño o aplicación, pero abre oportunidades. Como, por ejemplo, si tuviéramos una instancia de botón donde necesitáramos anular el color.

button.button.button--primary(style=`--hue: 20;`) Overridden

ver la pluma
Anulado con alcance en línea por Jhey (@jh3y)
en CodePen.

Divertirse con las variables en línea

Otra oportunidad es divertirse un poco con él. Esta es una técnica que utilizo para muchos de los bolígrafos que creo en CodePen. 😉

Puede estar escribiendo HTML sencillo, pero en muchos casos, puede estar usando un marco, como React o un preprocesador como Pug, para escribir su marcado. Estas soluciones le permiten aprovechar JavaScript para crear variables aleatorias en línea. Para los siguientes ejemplos, usaré Pug. Pug es un motor de plantillas HTML basado en sangría. Si no estás familiarizado con Pug, ¡no temas! Trataré de mantener el marcado simple.

Comencemos por aleatorizar el tono de nuestros botones:

button.button(style=`--hue: ${Math.random() * 360}`) First

Con Pug, podemos usar literales de plantilla ES6 para incorporar variables CSS aleatorias. 💪

ver la pluma
Matices aleatorios de variables CSS en línea de Jhey (@jh3y)
en CodePen.

Alteraciones de animación

Entonces, ahora que tenemos la oportunidad de definir características aleatorias para un elemento, ¿qué más podemos hacer? Bueno, una oportunidad que se pasa por alto es la animación. Cierto, no podemos animar la variable en sí, así:

@keyframes grow {
  from { --scale: 1; }
  to   { --scale: 2; }
}

Pero podemos crear animaciones dinámicas basadas en variables de ámbito. ¡Podemos cambiar el comportamiento de la animación sobre la marcha! 🤩

Ejemplo 1: El botón excitado

Vamos a crear un botón que flote ocupándose de sus propios asuntos y luego se emocione cuando pasemos el mouse sobre él.

Comience con el marcado:

button.button(style=`--hue: ${Math.random() * 360}`) Show me attention

Una animación flotante simple puede verse así:

@keyframes flow {
  0%, 100% {
    transform: translate(0, 0);
  }
  50% {
    transform: translate(0, -25%);
  }
}

Esto nos dará algo como esto:

ver la pluma
La base del botón emocionado por Jhey (@jh3y)
en CodePen.

He añadido un poco de sombra como extra, pero no es vital. 👍

Hagamos que nuestro botón se emocione cuando pasemos el mouse sobre él. Ahora, simplemente podríamos cambiar la animación que se está utilizando a algo como esto:

.button:hover {
  animation: shake .1s infinite ease-in-out;
}

@keyframes shake {
  0%, 100% {
    transform: translate(0, 0) rotate(0deg);
  }
  25% {
    transform: translate(-1%, 3%) rotate(-2deg);
  }
  50% {
    transform: translate(1%, 2%) rotate(2deg);
  }
  75% {
    transform: translate(1%, -2%) rotate(-1deg);
  }
}

Y funciona:

ver la pluma
El botón emocionado obtiene otra definición de fotogramas clave de Jhey (@jh3y)
en CodePen.

Pero, necesitamos introducir otra definición de fotogramas clave. ¿Y si pudiéramos fusionar las dos animaciones en una sola? No están demasiado lejos el uno del otro en términos de estructura.

Podemos intentarlo:

@keyframes flow-and-shake {
  0%, 100% {
    transform: translate(0, 0) rotate(0deg);
  }
  25%, 75% {
    transform: translate(0, -12.5%) rotate(0deg);
  }
  50% {
    transform: translate(0, -25%) rotate(0deg);
  }
}

Aunque esto funciona, terminamos con una animación que no es tan fluida debido a los pasos de traducción. Entonces, ¿qué más podríamos hacer? Busquemos un compromiso eliminando los pasos al 25% y al 75%.

@keyframes flow-and-shake {
  0%, 100% {
    transform: translate(0, 0) rotate(0deg);
  }
  50% {
    transform: translate(0, -25%) rotate(0deg);
  }
}

Funciona bien, como esperábamos, pero aquí viene el truco: actualicemos nuestro botón con algunas variables.

.button {
  --y: -25;
  --x: 0;
  --rotation: 0;
  --speed: 2;
}

Ahora vamos a conectarlos a la definición de animación, junto con las propiedades de animación del botón.

.button {
  animation-name: flow-and-shake;
  animation-duration: calc(var(--speed) * 1s);
  animation-iteration-count: infinite;
  animation-timing-function: ease-in-out;
}

@keyframes flow-and-shake {
  0%, 100% {
    transform: translate(calc(var(--x) * -1%), calc(var(--y) * -1%))
      rotate(calc(var(--rotation) * -1deg));
  }
  50% {
    transform: translate(calc(var(--x) * 1%), calc(var(--y) * 1%))
      rotate(calc(var(--rotation) * 1deg));
  }
}

Todo está bien. 👍

Cambiemos esos valores cuando se desplace el botón:

.button:hover {
  --speed: .1;
  --x: 1;
  --y: -1;
  --rotation: -1;
}

ver la pluma
El botón emocionado con fotogramas clave refactorizados y variables de ámbito por Jhey (@jh3y)
en CodePen.

¡Bonito! Ahora nuestro botón tiene dos tipos diferentes de animaciones pero definidas a través de un conjunto de fotogramas clave. 🤯

Vamos a divertirnos un poco más con eso. Si lo llevamos un poco más allá, podemos hacer que el botón sea un poco más divertido y tal vez dejar de animar por completo cuando esté activo. 😅

ver la pluma
El botón emocionado con animación dinámica 🤓 por Jhey (@jh3y)
en CodePen.

Ejemplo 2: Burbujas

Ahora que hemos pasado por algunas técnicas diferentes para las cosas que podemos hacer con el poder del osciloscopio, pongámoslo todo junto. Vamos a crear una escena de burbuja generada aleatoriamente que aproveche en gran medida las variables CSS de ámbito.

Comencemos por crear una burbuja. Una burbuja estática.

.bubble {
  background: radial-gradient(100% 115% at 25% 25%, #fff, transparent 33%),
    radial-gradient(15% 15% at 75% 75%, #80dfff, transparent),
    radial-gradient(100% 100% at 50% 25%, transparent, #66d9ff 98%);
  border: 1px solid #b3ecff;
  border-radius: 100%;
  height: 50px;
  width: 50px;
}

Estamos usando background con múltiples valores y un border para hacer el efecto de burbuja, pero no es muy dinámico. sabemos el border-radius siempre será el mismo. Y conocemos la estructura de la border y background no cambiará. Pero los valores utilizados dentro de esas propiedades y los otros valores de propiedad podrían ser aleatorios.

Refactoricemos el CSS para hacer uso de variables:

.bubble {
  --size: 50;
  --hue: 195;
  --bubble-outline: hsl(var(--hue), 100%, 50%);
  --bubble-spot: hsl(var(--hue), 100%, 75%);
  --bubble-shade: hsl(var(--hue), 100%, 70%);
  background: radial-gradient(100% 115% at 25% 25%, #fff, transparent 33%),
    radial-gradient(15% 15% at 75% 75%, var(--bubble-spot), transparent),
    radial-gradient(100% 100% at 50% 25%, transparent, var(--bubble-shade) 98%);
  border: 1px solid var(--bubble-outline);
  border-radius: 100%;
  height: calc(var(--size) * 1px);
  width: calc(var(--size) * 1px);
}

Ese es un buen comienzo. 👍

ver la pluma
Fundación Bubbles de Jhey (@jh3y)
en CodePen.

Agreguemos algunas burbujas más y aprovechemos el alcance en línea para posicionarlas y dimensionarlas. Dado que vamos a comenzar a aleatorizar más de un valor, es útil tener una función para generar un número aleatorio en el rango de nuestro marcado.

- const randomInRange = (max, min) => Math.floor(Math.random() * (max - min + 1)) + min

Con Pug, podemos utilizar la iteración para crear un gran conjunto de burbujas:

- const baseHue = randomInRange(0, 360)
- const bubbleCount = 50
- let b = 0
while b < bubbleCount
  - const size = randomInRange(10, 50)
  - const x = randomInRange(0, 100)
  .bubble(style=`--x: ${x}; --size: ${size}; --hue: ${baseHue}`)
  - b++

Actualizando nuestro .bubble el estilo nos permite hacer uso de las nuevas variables en línea.

.bubble {
  left: calc(var(--x) * 1%);
  position: absolute;
  transform: translate(-50%, 0);
}

Dándonos un conjunto aleatorio de burbujas:

ver la pluma
Agregando burbujas por Jhey (@jh3y)
en CodePen.

Vayamos aún más lejos y animemos esas burbujas para que floten de arriba a abajo y desaparezcan.

.bubble {
  animation: float 5s infinite ease-in-out;
  top: 100%;
}

@keyframes float {
  from {
    opacity: 1;
    transform: translate(0, 0) scale(0);
  }
  to {
    opacity: 0;
    transform: translate(0, -100vh) scale(1);
  }
}

ver la pluma
Burbujas subiendo juntas por Jhey (@jh3y)
en CodePen.

Eso es bastante aburrido. Todos hacen lo mismo al mismo tiempo. Entonces, aleatoricemos la velocidad, el retraso, la escala final y la distancia que cada burbuja viajará.

- const randomInRange = (max, min) => Math.floor(Math.random() * (max - min + 1)) + min
- const baseHue = randomInRange(0, 360)
- const bubbleCount = 50
- let b = 0
while b < bubbleCount
  - const size = randomInRange(10, 50)
  - const delay = randomInRange(1, 10)
  - const speed = randomInRange(2, 20)
  - const distance = randomInRange(25, 150)
  - const scale = randomInRange(100, 150) / 100
  - const x = randomInRange(0, 100)
  .bubble(style=`--x: ${x}; --size: ${size}; --hue: ${baseHue}; --distance: ${distance}; --speed: ${speed}; --delay: ${delay}; --scale: ${scale}`)
  - b++

Y ahora, actualicemos nuestros estilos.

.bubble {
  animation-name: float;
  animation-duration: calc(var(--speed) * 1s);
  animation-delay: calc(var(--delay) * -1s);
  animation-iteration-count: infinite;
  animation-timing-function: ease-in-out;
}

@keyframes float {
  from {
    opacity: 1;
    transform: translate(-50%, 0) scale(0);
  }
  to {
    opacity: 0;
    transform: translate(-50%, calc(var(--distance) * -1vh)) scale(var(--scale));
  }
}

Y obtendremos esto:

ver la pluma
Escena de burbuja aleatoria con alcance variable 😎 por Jhey (@jh3y)
en CodePen.

¡Con alrededor de 50 líneas de código, puede crear una escena animada generada aleatoriamente perfeccionando el poder del alcance! 💪

¡Eso es!

Podemos crear algunas cosas geniales con muy poco código poniendo en uso las variables CSS y aprovechando algunos pequeños trucos.

Espero que este artículo haya creado cierta conciencia sobre el poder del alcance variable de CSS y espero que perfeccione el poder y lo transmita 😎

Todas las demostraciones de este artículo están disponibles en esta colección de CodePen.

(Visited 32 times, 1 visits today)