Cuando chocan Sass y las nuevas funciones CSS
Recientemente, CSS ha agregado muchas características nuevas e interesantes, como propiedades personalizadas y nuevas funciones. Si bien estas cosas pueden hacernos la vida mucho más fácil, también pueden terminar interactuando con preprocesadores, como Sass, de maneras divertidas.
Así que esta será una publicación sobre los problemas que he encontrado, cómo los soluciono y por qué todavía encuentro necesario Sass en estos días.
Los errores
Si ha jugado con las nuevas funciones min()
y max()
, es posible que se haya topado con un mensaje de error como este al trabajar con diferentes unidades: "Unidades incompatibles: vh
y em
".
Esto se debe a que Sass tiene su propia min()
función e ignora la min()
función CSS . Además, Sass no puede realizar ningún tipo de cálculo utilizando dos valores con unidades que no tengan una relación fija entre ellos.
Por ejemplo, cm
y in
las unidades tienen una relación fija entre ellas, por lo que Sass puede descubrir cuál es el resultado min(20in, 50cm)
y no arroja un error cuando intentamos usarlo en nuestro código.
Lo mismo ocurre con otras unidades. Las unidades angulares, por ejemplo, tienen todas una relación fija entre ellas: 1turn
, 1rad
o 1grad
siempre se calculan con los mismos deg
valores. Lo mismo ocurre con 1s
cuál es siempre 1000ms
, 1kHz
cuál es siempre 1000Hz
, 1dppx
cuál es siempre 96dpi
y 1in
cuál es siempre 96px
. Es por eso que Sass puede convertir entre ellos y mezclarlos en cálculos y funciones internas como su propia min()
función.
Pero las cosas se rompen cuando estas unidades no tienen una relación fija entre ellas (como en el caso anterior con las unidades em
y vh
).
Y no se trata sólo de unidades diferentes. Intentar usarlo calc()
en el interior min()
también genera un error. Si intento algo como calc(20em + 7px)
, el error que aparece es " calc(20em + 7px)
no es un número para min
".
Otro problema surge cuando queremos usar una variable CSS o el resultado de una función matemática CSS (como calc()
, min()
o max()
) en un filtro CSS como invert()
.
En este caso, nos dicen que “ $color: 'var(--p, 0.85)
no es un color para invert
”.
Lo mismo ocurre con grayscale()
: “ $color
: ' calc(.2 + var(--d, .3))
' no es un color para grayscale
.”
opacity()
causa el mismo problema: " $color
: ' var(--p, 0.8)
' no es un color para opacity
".
Sin embargo, otras filter
funciones, incluidas sepia()
, blur()
, drop-shadow()
, brightness()
y contrast()
, hue-rotate()
¡todas funcionan bien con variables CSS!
Resulta que lo que está sucediendo es similar al problema min()
y max()
. Sass no tiene funciones sepia()
, blur()
, drop-shadow()
, brightness()
, integradas contrast()
, hue-rotate()
pero tiene las suyas propias grayscale()
, invert()
y opacity()
funciones, y su primer argumento es un $color
valor. Como no encuentra ese argumento, arroja un error.
Por la misma razón, también tenemos problemas al intentar utilizar una variable CSS que enumera al menos dos valores hsl()
o hsla()
.
Por otro lado, color: hsl(9, var(--sl, 95%, 65%))
es CSS perfectamente válido y funciona bien sin Sass.
rgb()
Con las funciones y ocurre exactamente lo mismo rgba()
.
Además, si importamos Compass e intentamos usar una variable CSS dentro de a linear-gradient()
o dentro de a radial-gradient()
, obtenemos otro error, aunque usar variables internas conic-gradient()
funciona bien (es decir, si el navegador lo admite).
Esto se debe a que Compass viene con linear-gradient()
funciones radial-gradient()
, pero nunca ha agregado conic-gradient()
ninguna.
Los problemas en todos estos casos surgen porque Sass o Compass tienen funciones con nombres idénticos y suponen que esas son las que pretendíamos usar en nuestro código.
¡Maldita sea!
La solución
El truco aquí es recordar que Sass distingue entre mayúsculas y minúsculas, pero CSS no.
Eso significa que podemos escribir Min(20em, 50vh)
y Sass no lo reconocerá como su propia min()
función. No se generarán errores y seguirá siendo CSS válido que funciona según lo previsto. De manera similar, escribir HSL()
/ HSLA()
/ RGB()
/ RGBA()
o Invert()
nos permite evitar problemas que vimos anteriormente.
En cuanto a los degradados, normalmente prefiero linear-Gradient()
y radial-Gradient()
solo porque está más cerca de la versión SVG, pero usar al menos una letra mayúscula funciona bien.
¿Pero por qué?
Casi cada vez que twitteo algo relacionado con Sass, me sermonean sobre cómo no debería usarse ahora que tenemos variables CSS. Pensé en abordar eso y explicar por qué no estoy de acuerdo.
En primer lugar, si bien las variables CSS me parecen inmensamente útiles y las he usado para casi todo durante los últimos tres años, es bueno tener en cuenta que conllevan un costo de rendimiento y que rastrear dónde algo salió mal en un laberinto de calc()
cálculos puede ser una tarea complicada. dolor con nuestras DevTools actuales. Intento no abusar de ellos para evitar entrar en un territorio donde las desventajas de usarlos superan los beneficios.
En general, si actúa como una constante, no cambia de elemento a elemento o de estado a estado (en cuyo caso las propiedades personalizadas son definitivamente el camino a seguir) ni reduce la cantidad de CSS compilado (resolviendo el problema de repetición). creado por prefijos), entonces usaré una variable Sass.
En segundo lugar, las variables siempre han sido una parte bastante pequeña de por qué uso Sass. Cuando comencé a usar Sass a finales de 2012, era principalmente para bucles, una característica que todavía no tenemos en CSS. Si bien moví algunos de esos bucles a un preprocesador HTML (porque reduce el código generado y evita tener que modificar tanto el HTML como el CSS más adelante), sigo usando bucles Sass en muchos casos, como generar listas de valores, listas de parada dentro de funciones de gradiente, listas de puntos dentro de una función poligonal, listas de transformaciones, etc.
He aquí un ejemplo. Solía generar n
elementos HTML con un preprocesador. La elección del preprocesador importa menos, pero aquí usaré Pug.
- let n = 12;while n-- .item
Luego establecería la $n
variable en Sass (y tendría que ser igual a la del HTML) y la recorrería para generar las transformaciones que posicionarían cada elemento:
$n: 12;$ba: 360deg/$n;$d: 2em;.item { position: absolute; top: 50%; left: 50%; margin: -.5*$d; width: $d; height: $d; /* prettifying styles */ @for $i from 0 to $n { :nth-child(#{$i + 1}) { transform: rotate($i*$ba) translate(2*$d) rotate(-$i*$ba); ::before { content: '#{$i}' } } }}
Sin embargo, esto significaba que tendría que cambiar tanto el Pug como el Sass al cambiar la cantidad de elementos, lo que hacía que el código generado fuera muy repetitivo.
Desde entonces, pasé a hacer que Pug genere los índices como propiedades personalizadas y luego los use en la transform
declaración.
- let n = 12;body(style=`--n: ${n}`) - for(let i = 0; i n; i++) .item(style=`--i: ${i}`)
$d: 2em;.item { position: absolute; top: 50%; left: 50%; margin: -.5*$d; width: $d; height: $d; /* prettifying styles */ --az: calc(var(--i)*1turn/var(--n)); transform: rotate(var(--az)) translate(2*$d) rotate(calc(-1*var(--az))); counter-reset: i var(--i); ::before { content: counter(i) }}
Esto reduce significativamente el código generado.
Sin embargo, aún es necesario realizar un bucle en Sass si quiero generar algo como un arco iris.
@function get-rainbow($n: 12, $sat: 90%, $lum: 65%) { $unit: 360/$n; $s-list: (); @for $i from 0 through $n { $s-list: $s-list, hsl($i*$unit, $sat, $lum) } @return $s-list}html { background: linear-gradient(90deg, get-rainbow()) }
Claro, podría generarlo como una variable de lista desde Pug, pero hacerlo no aprovecha la naturaleza dinámica de las variables CSS y no reduce la cantidad de código que se entrega al navegador, por lo que no obtendré ningún beneficio. fuera de el.
Otra gran parte de mi uso de Sass (y Compass) está ligada a funciones matemáticas integradas (como funciones trigonométricas), que ahora forman parte de la especificación CSS, pero que aún no están implementadas en ningún navegador. Sass tampoco viene con estas funciones, pero Compass sí y es por eso que a menudo necesito usar Compass.
Y, claro, podría escribir mis propias funciones en Sass. Recurrí a esto al principio, antes de que Compass admitiera funciones trigonométricas inversas. Realmente los necesitaba, así que escribí el mío basado en la serie de Taylor. Pero Compass proporciona este tipo de funciones hoy en día y son mejores y más eficientes que las mías.
Las funciones matemáticas son extremadamente importantes para mí porque soy técnico, no artista. Los valores en mi CSS generalmente resultan de cálculos matemáticos. No son números mágicos ni algo usado puramente por estética. Un ejemplo es generar listas de puntos de trazados de recorte que crean polígonos regulares o cuasi regulares. Piense en el caso en el que queremos crear cosas como avatares o pegatinas no rectangulares.
Consideremos un polígono regular con vértices en un círculo con un radio 50%
del elemento cuadrado del que partimos. Arrastrar el control deslizante en la siguiente demostración nos permite ver dónde se colocan los puntos para diferentes números de vértices:
Poniéndolo en código Sass, tenemos:
@mixin reg-poly($n: 3) { $ba: 360deg/$n; // base angle $p: (); // point coords list, initially empty @for $i from 0 to $n { $ca: $i*$ba; // current angle $x: 50%*(1 + cos($ca)); // x coord of current point $y: 50%*(1 + sin($ca)); // y coord of current point $p: $p, $x $y // add current point coords to point coords list } clip-path: polygon($p) // set clip-path to list of points}
Tenga en cuenta que aquí también estamos haciendo uso de bucles y cosas como condicionales y módulos que son una verdadera molestia cuando se usa CSS sin Sass.
Una versión ligeramente más evolucionada de esto podría implicar rotar el polígono agregando el mismo ángulo de desplazamiento ( $oa
) al ángulo de cada vértice. Esto se puede ver en la siguiente demostración. Este ejemplo incluye un mixin de estrellas que funciona de manera similar, excepto que siempre tenemos un número par de vértices y cada vértice con índice impar está situado en un círculo de un radio más pequeño ( $f*50%
, donde $f
es subunitario):
También podemos tener estrellas gorditas como esta:
O pegatinas con border
patrones interesantes. En esta demostración en particular, cada pegatina se crea con un único elemento HTML y el border
patrón se crea con clip-path
bucles y matemáticas en Sass. De hecho, bastante.
Otro ejemplo son estos fondos de tarjetas donde el bucle, la operación de módulo y las funciones exponenciales trabajan juntas para generar las capas de fondo de píxeles difuminados:
Esta demostración también depende en gran medida de las variables CSS.
Luego está el uso de mixins para evitar escribir exactamente las mismas declaraciones una y otra vez al diseñar cosas como entradas de rango. Diferentes navegadores usan diferentes pseudoelementos para diseñar los componentes de dicho control, por lo que para cada componente, tenemos que configurar los estilos que controlan su apariencia en múltiples pseudos.
Lamentablemente, por más tentador que sea poner esto en nuestro CSS:
input::-webkit-slider-runnable-track, input::-moz-range-track, input::-ms-track { /* common styles */ }
…¡no podemos hacerlo porque no funciona! Todo el conjunto de reglas se descarta si no se reconoce siquiera uno de los selectores. Y como ningún navegador reconoce los tres anteriores, los estilos no se aplican en ningún navegador.
Necesitamos tener algo como esto si queremos que se apliquen nuestros estilos:
input::-webkit-slider-runnable-track { /* common styles */ }input::-moz-range-track { /* common styles */ }input::-ms-track { /* common styles */ }
Pero eso puede significar que muchos estilos idénticos se repitan tres veces. Y si queremos cambiar, digamos, el background
de la pista, necesitamos cambiarlo en los ::-webkit-slider-runnable-track
estilos, en los ::-moz-range-track
estilos y en los ::-ms-track
estilos.
La única solución sensata que tenemos es utilizar un mixin. Los estilos se repiten en el código compilado porque deben repetirse allí, pero ya no tenemos que escribir lo mismo tres veces.
@mixin track() { /* common styles */ }input { ::-webkit-slider-runnable-track { @include track } ::-moz-range-track { @include track } ::-ms-track { @include track }}
La conclusión es: sí, Sass sigue siendo muy necesario en 2020.
Deja un comentario