Entendiendo @use y @forward en Dart Sass

Tabla de contenido

En los últimos años los preprocesadores de CSS se han hecho muy populares porque permiten escribir CSS de forma programática, lo cuál ha supuesto un importante salto adelante en flexibilidad y potencia a la hora de declarar estilos, siendo Sass el más conocido y usado de entre éstos, y aunque actualmente existen muchas otras formas de estilar nuestras aplicaciones web, como CSS-in-JS en alguno de sus múltiples sabores, PostCSS o CSS modules, el uso de SASS sigue estando muy extendido, como revela el último The State of CSS 2021, y por tanto, sigue siendo una herramienta con largo recorrido.

Module System

En 2019 Sass anunció Module System, ****un cambio notable en core de su herramienta que dejaría atrás el uso de @import y algunas funciones globales, y que la implementación llamada Dart Sass (basada en JavaScript y en la que se trabajaba desde 2016) ya soportaba el nuevo sistema de módulos, por lo que desde la organización, empezaron a fomentar su uso por delante de LibSass y RubySass.

A pesar de existir retrocompatibilidad y de que podían cohexisitir ambos enfoques, tras un período de transición -para dar tiempo a los autores a realizar este cambio-, se abandonaría definitivamente @import y los viejos módulos internos de SASS, a más tardar el 1 de octubre de 2022.

Por entonces, LibSass estaba muy extendida (como dependencia de node-sass) pero no soportaba totalmente el nuevo sistema de módulos, y además, acabaría deprecada, por lo que el cambio acabaría siendo forzado.

¿Y cómo me afecta esto?

Pues depende. Significa que vamos a tener un montón de legacy code que tendríamos que migrar y que puede hacerse siguiendo esta guía de migración, pero, si algo está funcionando, ¿por qué cambiarlo?. Pero también significa que cuando uses SASS en tus nuevos desarrollos, tendrás que utilizar el nuevo sistema de módulos, y por tanto tendrás que conocer las nuevas reglas.

@use y @forward

Lo más destacable de esta versión son la aparición de estas dos nuevas reglas, que vienen para reemplazar a @import y que van a eliminar el ámbito global en el que vivían las utilidades.

Hasta ahora, incluíamos en un única hoja de estilos (en un índice), el resto de ficheros parciales que estilaban otros componentes o partes de la aplicación, así como ciertas utilidades; variables, mixins, placeholders y funciones, a las que todos esos parciales tenían acceso y podían utilizar, pero con el nuevo sistema de módulos, cuando un parcial quiera acceder a alguna utilidad, tendrá que importarla explícitamente, de manera que tendremos más control, y de un simple vistazo sabremos qué código que no pertenece a ese parcial se está utilizando y de dónde viene, algo muy parecido a los import de JavaScript.

Pero no sólo eso, sino que existen otras muchas ventajas.

Beneficios

@use

@use 'theme';

.text {
	color: theme.$primary-color;
}

Se declara al principio del archivo en el que se quiere tener acceso a esos miembros (variables, mixins y funciones) para poder utilizarlos.

Por defecto, están encapsulados con el nombre del fichero, de modo que si queremos acceder a la variable $primary-color de theme.scss tendremos que hacerlo como theme.$primay-color. Esto nos permite usar en el mismo fichero dos o más $primary-color ****si vienen de archivos diferentes.

Pero además, podemos renombrar los namespaces usando as

@use 'theme' as defaultTheme;
@use '../3rd-party-library/theme' as externalTheme;

.box {
	color: defaultTheme.$primary-color;
	background-color: externalTheme.$primary-color;
}

También podemos eliminar el namespace usando *, aunque en este caso, si hubiese coincidencias entre variables, de diferentes importaciones, SASS lanzaría un error

@use 'theme' as *;
@use '../3rd-party-library/theme' as *;

.box {
	color: $primary-color; // desde theme
	background-color: $primary-color; // desde 3rd-party-library/theme
}

Además, en la declaración de los miembros, podemos harcerlos privados, es decir, sólo para uso interno y que no se tenga acceso a éste desde fuera, prefijándolo con - o _

// _palette.scss

$primary-color: '#282828' // variable accesible
	@mixin _reset-list {
	// mixin no accesible desde el exterior
	margin: 0;
	padding: 0;
	list-style: none;
}

@forward

Sirve para traer a un fichero el contenido de otro, por ejemplo, el índice en el que importamos parciales a través de @import.

Como @use, tenemos protección ante duplicidad de código si tratamos de llamarlo dos veces, pero a diferencia de éste, @forward no añade namespaces.

// Ajustes generales
@forward 'base/reset';
@forward 'base/reset'; // no duplicamos código
@forward 'base/common';

// Componentes comunes
@forward '../components/';

Otra similitud con @use es la posibilidad de manejar la privacidad de los miembros, pero en este caso se indica en el momento de utilizar @forward utilizando claúsulas para declarar qué queremos importar o cuáles queremos excluir, usando show o hide

@forward 'hero' show $hero-color; // importando SÓLO la variable $hero-color

@forward 'utils' hide triangle; // importando TODO menos el mixin triangle()

Módulos internos (built-in)

Algunos de los módulos internos y utilidades de SASS han cambiado, porque son nuevos, han sido renombrado o eliminados, así no dejes de consultarlos en la documentación oficial, pero lo más importante es que ahora tendrás que acceder a los módulos de forma explícita con @use y que podrás renombrarlos

@use 'sass:math' as builtInSassMathModule;
@use 'sass:color';

@use 'theme';

body {
	&::before {
		content: builtInSassMathModule.random();
		color: color.alpha(theme.$color);
	}
}

Configuración de librerías de terceros

Con @use podemos configurar librerías de terceros sin tener que sobreescribir sus valores, usando with en el momento de la importación

$custom-breakpoints: (
	mobile: 375px,
	tablet-small: 640px,
	tablet: 780px,
	tablet-large: 920px,
	desktop: 1180px,
	wide: 1400px,
);

$custom-show-breakpoints: (mobile, tablet, desktop, wide);

@forward '../node_modules/sass-mq/mq' with (
	$breakpoints: $custom-breakpoints,
	$show-breakpoints: $custom-show-breakpoints
);

Conclusiones

A pesar de que el sistema de módulos traía muchas ventajas, no se produjo una transición masiva y sigue habiendo mucho código que usa @import, probablemente porque se juntaron varios factores al mismo tiempo, como por ejemplo:

Así que, debido al amplio abanico de posibilidades que se iba abriendo y que LibSass seguía disponible y no suponía ningún cambio, siguió siendo muy popular (como muestra npm trends), pero con el anuncio de que LibSass desaparecerá y el cambio será obligatorio para los nuevos desarrollos, si queremos seguir usando SASS hay que empezar a acoger el cambio y no aplazar más lo inevitable, por eso os dejamos un enalce a un repositorio con una estructura básica scratcheada y con algunos comentarios para que podáis probarlo :)

Aquí el enlace al repositorio para cacharrear un poquito con los diferentes tipos de importaciones

Nota al pie

En un post anterior sobre las funciones min, max y clamp, explicábamos que para usar usar estas funciones en Sass teníamos que entrecomillarlas y utilizar unquote() porque Sass las confundía con las suyas propias, ya que compartían nombres.

.element-c {
	width: unquote("min(500px, 98%)");
}

.element-d {
	font-size: unquote("max(min(10%, ((1.25vw + 10px) * 0.5), 5vw), 1rem)");
}

Pero si ya has leído el artículo mencionado al principio de esta entrada te podrás imaginar por qué ya no supone un problema y por qué con dart-sass podemos utilizarlas de forma normal.

.element-c {
	width: min(500px, 98%);
}

.element-d {
	font-size: max(min(10%, ((1.25vw + 10px) * 0.5), 5vw), 1rem);
}

Estas funciones pertenecen al módulo ‘math’, y como por defecto al importar un módulo con @use sus miembros están encapsulados, ya no hay colisión entre la función min nativa de CSS y la propia de Sass.


@use 'sass:math';

.element-c {
	width: min(500px, 98%); /* nativa CSS */
}

.element-e {
	width: math.min(500px, 300px); /* propia de Sass */
}
comments powered by Disqus

Si te ha parecido interesante

Tanto si tienes alguna duda o te apetece charlar sobre este tema, así como si el contenido te parece interesante o crees que pdemos hacer algo juntos, no dudes en ponerte en contacto con nosotros a través del email hola@mamutlove.com