Hacer que las propiedades personalizadas (variables CSS) sean más dinámicas | Programar Plus

Las propiedades personalizadas de CSS (quizás más fáciles de entender como variables de CSS) nos brindan formas de hacer que el código sea más conciso, así como también presentan nuevas formas de trabajar con CSS que antes no eran posibles. Pueden hacer lo que pueden hacer las variables del preprocesador… pero también mucho más. Ya sea que haya sido un fanático de la naturaleza declarativa de CSS o prefiera manejar la mayor parte de su lógica de estilo en JavaScript, las Propiedades personalizadas traen algo a la mesa para todos.

La mayor parte del poder proviene de dos habilidades únicas de Propiedades personalizadas:

  • la cascada
  • La capacidad de modificar valores con JavaScript

Se expone aún más poder al combinar propiedades personalizadas con otros conceptos de CSS preexistentes, como calc() .

Los basicos

Puede usar Propiedades personalizadas para hacer de manera efectiva lo que proporcionan las variables en los preprocesadores como Sass: establecer un valor de variable global o de alcance y luego usarlo más adelante en su código. Pero gracias a la cascada, puedes dar nuevos valores de propiedad dentro de una regla más específica.

Esta cascada puede conducir a varios enfoques interesantes, como lo muestra Violet Peña con una descripción general de los beneficios clave de las variables y Chris con un resumen de las opciones de tematización del sitio.

La gente ha estado discutiendo estos beneficios de la cascada durante algunos años, pero a menudo se pierde en la conversación a pesar de ser una funcionalidad clave que la diferencia de los preprocesadores. Amelia Bellamy-Royds lo discutió en el contexto de SVG y use en 2014, y Philip Walton notó muchos de estos beneficios generales en cascada en 2015, y el año pasado Gregor Adams mostró cómo se pueden usar en un marco de cuadrícula mínimo. Aprovechar la cascada es probablemente la forma más fácil de comenzar a trabajar con propiedades personalizadas con la mejora progresiva en mente.

Bueno. Ahora que sabemos que las propiedades personalizadas pueden brindarnos de forma nativa algunas funciones que tienen los preprocesadores y algunos usos nuevos gracias a la cascada, ¿nos brindan algo que simplemente nunca pudimos hacer antes?

¡Apuesta!

Individualización de propiedades

Todas las propiedades que tienen varias partes ahora se pueden usar de manera diferente. Múltiple backgrounds se pueden separar y múltiples transition-durations se pueden desglosar individualmente. En lugar de tomar una regla como transform: translateX(10vmin) rotate(90deg) scale(.8) translateY(5vmin) puede establecer una regla con varias propiedades personalizadas y cambiar los valores de forma independiente a partir de entonces.

.view { 
  transform: 
    translateX(var(--tx, 0))
    rotate(var(--deg, 0))
    scale(var(--scale, 1))
    translateY(var(--ty, 0));
}
.view.activated {
  --tx: 10vmin;
  --deg: 90deg;
}
.view.minimize {
  --scale: .8;
}
.view.priority {
  --ty: 10vmin;
}

Se necesita un poco para inicializar, pero luego ese pequeño esfuerzo adicional lo configura para modificar cada función de transformación de forma independiente según las necesidades de la regla de clase/selector. Su marcado puede incluir cualquiera o todas las clases definidas en cada .view elemento y el transform se actualizará apropiadamente.

Mientras que las propiedades de transformación independientes están llegando (y en ese momento translate, scale, y rotate serán ciudadanos de primer nivel), actualmente solo están en Chrome detrás de una bandera. Con Propiedades personalizadas, puede obtener esta funcionalidad hoy con más soporte (y la capacidad adicional de definir su propio orden de funciones, ya que rotate(90deg) translateX(10vmin) es diferente a translateX(10vmin) rotate(90deg), por ejemplo).

Si está de acuerdo con que compartan las mismas opciones de tiempo, incluso pueden animar sin problemas cuando se usan transition al cambiar cualquiera de las variables. Es un poco mágico.

Consulte Variables CSS de Pen + Transform = Propiedades individuales (con entradas) de Dan Wilson (@danwilson) en CodePen.

Pasar de ninguna unidad a todas las unidades

Puede basarse en estos conceptos al combinarlos con calc(). En lugar de establecer siempre las variables como arriba con unidades (--card-width: 10vmin o --rotation-amount: 1turn) puede soltar las unidades y usarlas en más lugares con una relación entre sí. Ahora los valores en nuestras propiedades personalizadas pueden ser más dinámicos de lo que ya han sido.

Mientras calc() ha existido durante algunos años, podría decirse que ha sido más útil cuando se intenta obtener un resultado al sumar valores con diferentes unidades. Por ejemplo, usted tiene un líquido width en unidades de porcentaje que debe acortarse en 50 px (width: calc(100% - 50px) ). Sin embargo, calc() es capaz de más.

Otras operaciones como la multiplicación están permitidas dentro calc para ajustar un valor. Lo siguiente es válido y nos da la sensación de que las transformaciones y los filtros están relacionados entre sí, ya que todos usan el número 10.

.colorful {
  transform: 
    translateX(calc(10 * 1vw))
    translateY(calc(10 * 1vh));
  filter: hue-rotate(calc(10 * 4.5deg));
}

Es probable que este no sea un caso de uso tan común porque es un cálculo que no necesita que el navegador realice. 10 * 1vw siempre será 10vw por lo que el calc no nos da nada. Puede ser útil cuando se usa un preprocesador con bucles, pero ese es un caso de uso más pequeño y, por lo general, se puede hacer sin necesidad de CSS. calc() .

Pero, ¿y si reemplazamos eso repetido? 10 con una variable? Puede basar valores a partir de un solo valor en varios lugares, incluso con diferentes unidades, así como abrirlo para cambiar valores en el futuro. Lo siguiente es válido gracias a las variables sin unidad y calc:

.colorful {
  --translation: 10;
  transform: 
    translateX(calc(var(--translation) * 1vw))
    translateY(calc(var(--translation) * 1vh));
  filter: hue-rotate(calc(var(--translation) * 4.5deg));

  will-change: transform, filter;
  transition: transform 5000ms ease-in-out, filter 5000ms linear;
}

.colorful.go {
  --translation: 80;
}

Consulte la propiedad personalizada Pen Single, Multiple Calcs de Dan Wilson (@danwilson) en CodePen.

Se puede tomar el valor único (inicialmente 10, o cambiar posteriormente a 80… o cualquier otro número) y aplicarlo por separado a vw unidades o vh unidades para una traducción. Puedes convertirlo a deg para una rotación o un filter: hue-rotate().

No es necesario que suelte las unidades en la variable, pero mientras las tenga en su calc puede, y abre la opción de usarlo de más maneras en otros lugares. La coreografía de animación para compensar la duración y los retrasos se puede lograr modificando el valor base en diferentes reglas. En este ejemplo siempre queremos ms como nuestra unidad final, pero el resultado clave que queremos es para nuestro delay para ser siempre la mitad de la animación duration . Entonces podemos hacer esto modificando solo nuestro --duration-base.

Vea Pen Delay basado en Duration por Dan Wilson (@danwilson) en CodePen.

Incluso los beziers cúbicos se pueden modificar en Propiedades personalizadas. En el siguiente ejemplo, hay varias cajas apiladas. Cada uno tiene una escala un poco más pequeña, y cada uno tiene un multiplicador de bezier cúbico. Este multiplicador se aplicará individualmente a las cuatro partes de un bózier cúbico de referencia. Esto permite que cada caja tenga un bezier cúbico diferente pero relacionado entre sí. Intente eliminar o agregar cajas para ver cómo interactúan entre sí. Presione en cualquier lugar para trasladar los cuadros a ese punto.

Vea el Pen Spiral Trail… Un poco por Dan Wilson (@danwilson) en CodePen.

JavaScript se utiliza para aleatorizar la línea de base en cada pulsación, así como para configurar el multiplicador de cada cuadro. Sin embargo, la parte clave del CSS es:

.x {
  transform: translateX(calc(var(--x) * 1px));
  /* baseline value, updated via JS on press */
  transition-timing-function: 
    cubic-bezier(
      var(--cubic1-1),
      var(--cubic1-2),
      var(--cubic1-3),
      var(--cubic1-4));
}
.advanced-calc .x {
  transition-timing-function: 
    cubic-bezier(
      calc(var(--cubic1-1) * var(--cubic1-change)),
      calc(var(--cubic1-2) * var(--cubic1-change)),
      calc(var(--cubic1-3) * var(--cubic1-change)),
      calc(var(--cubic1-4) * var(--cubic1-change)));
}

Si está viendo esto en ciertos navegadores (o se pregunta por qué este ejemplo tiene un .advanced-calc class) es posible que ya sospeche que hay un problema con este enfoque. De hecho, hay una advertencia importante… calc La magia no siempre funciona como se esperaba en todos los navegadores. Ana Tudor ha discutido durante mucho tiempo las diferencias en el soporte del navegador para calc , y tengo una prueba adicional para algunos otros simplificados calc casos de uso.

La buena noticia: todos los navegadores que admiten propiedades personalizadas también funcionan en gran medida con calc al convertir a unidades como px, vmin, rem, y otras unidades de distancia lineal dentro de propiedades tales como width y transform: translate().

Las noticias no tan buenas: Firefox y Edge a menudo tienen problemas con otros tipos de unidades, como deg, ms , e incluso % en algunos contextos. Así que el anterior filter: hue-rotate() y --rotation las propiedades serían ignoradas. Incluso tienen problemas para entender calc(1 * 1) en ciertos casos, por lo que incluso permanecer sin unidad (como dentro rgb()) puede ser un problema.

Si bien todos los navegadores que admiten Propiedades personalizadas permitirán variables dentro de nuestro cubic-bezier , no todos permiten calc en cualquier nivel. siento estos calc los problemas son los principales factores limitantes de las propiedades personalizadas en la actualidad… y ni siquiera forman parte de las propiedades personalizadas.

Hay errores rastreados en los navegadores para estos problemas, y puede solucionarlos con mejoras progresivas. Las demostraciones anteriores solo hacen lo cubic-bezier modificaciones si sabe que puede manejarlas; de lo contrario, obtiene los valores de referencia. Pasarán por error un CSS @supports check, por lo que se necesita una verificación de estilo JS Modernizr:

function isAdvancedCalcSupported() {
  document.body.style.transitionTimingFunction = 'cubic-bezier(calc(1 * 1),1,1,1)';
  return getComputedStyle(document.body).transitionTimingFunction != 'ease';
  //if the browser does not understand it, the computed value will be the default value (in this case "ease")
}

Interactuando a través de JavaScript

Las propiedades personalizadas son excelentes por lo que proporcionan en CSS, pero se desbloquea más poder cuando se comunica a través de JavaScript. Como se muestra en el cubic-bezier demo, podemos escribir un nuevo valor de propiedad en JavaScript:

var element = document.documentElement;
element.style.setProperty('--name', value);

Esto establecerá un nuevo valor para una propiedad definida globalmente (en CSS definido en el :root regla). O puede ir más directo y establecer un nuevo valor para un elemento específico (y así darle la mayor especificidad para ese elemento, y dejar la variable sin cambios para otros elementos que la usan). Esto es útil cuando está administrando el estado y necesita modificar un estilo en función de los valores dados.

David Khourshid ha discutido formas poderosas de interactuar con propiedades personalizadas a través de JS en el contexto de Observables y realmente encajan muy bien. Ya sea que desee utilizar Observables, cambios de estado de React, detectores de eventos probados y verdaderos o alguna otra forma de derivar cambios de valor, ahora se abre una amplia puerta para la comunicación entre los dos.

Esta comunicación es especialmente importante para las propiedades CSS que toman múltiples valores. Durante mucho tiempo hemos tenido la style object para modificar estilos desde JavaScript, pero eso puede complicarse tan pronto como necesitemos modificar solo una parte de un valor largo. Si necesitamos cambiar un fondo de los diez que están definidos en un background regla, tenemos que saber cuál estamos modificando y luego asegurarnos de dejar los otros nueve solos. Esto se vuelve aún más complicado para transform reglas cuando se trata de modificar sólo un rotate() y mantener la corriente scale() sin alterar. Con las propiedades personalizadas, puede usar JavaScript para modificar cada una individualmente, lo que simplifica la administración del estado de la totalidad. transform propiedad.

Vea Pen Dance of the Hexagons and Variables de Dan Wilson (@danwilson) en CodePen.

El enfoque sin unidades también funciona bien aquí. Tu setProperty() las llamadas pueden pasar números sin procesar a CSS en lugar de tener que agregar unidades, lo que puede simplificar su JavaScript en algunos casos.

¿Es hora de usar esto?

Dado que las propiedades personalizadas ahora se encuentran en los últimos navegadores de Mozilla, Google, Opera, Apple y Microsoft, definitivamente es un buen momento para explorar y experimentar. Mucho de lo que se discute aquí se puede usar ahora con respaldos sensatos en su lugar. El calc las actualizaciones necesarias en algunos de los navegadores están más avanzadas, pero todavía hay ocasiones en las que puede usarlas razonablemente. Por ejemplo, si trabaja en aplicaciones móviles híbridas que se limitan a las versiones más recientes de iOS, Android o Windows, tendrá más espacio para jugar.

Las propiedades personalizadas presentan una gran adición a CSS, y puede tomar algún tiempo entender cómo funciona todo. Sumerja los dedos de los pies y luego sumérjase si le conviene.