De toekomst van SCSS: Migreren naar @use en @forward
Al vanaf het moment dat SCSS bestaat wordt alles in SCSS files in de globale namespace gezet door `@import` te gebruiken. Dit betekent dat variabelen, mixins en functies overal beschikbaar zijn. Dat lijkt heel erg fijn en gemakkelijk, maar kan ook leiden tot onverwachte fouten en verminderde herbruikbaarheid. Om dit probleem op te lossen, hebben de makers van SCSS `@use` en `@forward` geïntroduceerd, waarmee je een betere structuur kunt aanbrengen en niet alles zomaar in de globale namespace wordt gezet. Bovendien zal `@import` binnen enkele jaren worden behandeld als een standaard CSS-`@import`, zonder SCSS-functionaliteiten, waardoor migratie naar `@use` en `@forward` op den duur essentieel wordt.
Wat is het verschil tussen @import, @use en @forward?
Met `@import` kun je je SCSS files opsplitsen in meerdere bestanden. Door deze files met `@import` te laden is alle (S)CSS binnen deze files beschikbaar. In feite bouw je met `@import` één groot file waarin alles onder elkaar gezet wordt. Hierdoor is alles globaal beschikbaar; behalve als je een variabele bijvoorbeeld binnen een selector zet zoals:
.button { $color: green; color: $green; }
Op die manier is de `$green` variabele alleen beschikbaar binnen `.button`. De volgorde van het inladen is dus wel belangrijk. Een variabele, mixin of functie is pas beschikbaar nadat deze is ingeladen.
Hierdoor hebben veel ontwikkelaars de gewoonte ontwikkeld om vanuit 1 hoofdbestand te werken waar als eerste de variabelen, mixins en functies worden ingeladen. Op deze manier zijn al deze tools beschikbaar in de globale namespace en kun je die door heel je project heen gebruiken.
Met `@use` en `@forward` wordt niet alles meer automatisch in de globale namespace geladen. Hierdoor kun je variabelen en mixins beter structureren en voorkomen dat namen elkaar overlappen. Dit is vooral handig in grotere projecten of frameworks.
Voorbeeld met `@import` (oude aanpak):
style.scss
@import "tools/variables"; @import "molecules/button";
tools/_variables.scss
$button-color: green !default;
molecules/_button.scss
.button { color: $button-color; }
Voorbeeld met `@use` en `@forward` (nieuwe aanpak):
style.scss
@forward "tools/variables"; @forward "molecules/button";
tools/_variables.scss
$button-color: green !default;
molecules/_button.scss
@use "../tools"; .button { color: tools.$button-color; }
Zoals je ziet is dit vooral extra code. We voegen een @use toe en passen de variabele namen aan zodat de juiste namespace gebruikt wordt. Maar in grote bestaande projecten; waar je als ontwikkelaar al rekening hebt gehouden met unieke namen voor variabelen, mixins of functies; of waar je zelfs actief gebruik maakt van het overschrijven hiervan op bepaalde plekken binnen je project, kan het erg vervelend zijn om dit allemaal aan te passen. Gelukkig kun je met @use er ook voor zorgen dat alles in de globale namespace komt door het volgende te doen:
@use "../tools" as *; .button { color: $button-color; }
Wat doet `@use`?
`@use` laadt een SCSS-bestand en maakt de inhoud beschikbaar binnen de lokale namespace (standaard de naam van het file, tenzij anders aangegeven). Zo voorkom je conflicten en kun je met dezelfde variabele in verschillende bestanden werken.
Best practice: gebruik een index-bestand
In grotere projecten is het handig om een `_index.scss`-bestand te maken dat alle benodigde bestanden laadt:
tools/_index.scss
@forward "variables"; @forward "mixins"; @forward "functions";
Hierdoor kun je alles in één keer importeren met:
@use "tools";
Wat doet `@forward`?
`@forward` stuurt inhoud door naar bestanden die dit laden. Dit is handig voor het centraliseren van SCSS-inhoud.
Voorbeeld:
tools/_variables.scss
$button-color: green !default;
tools/_index.scss
@forward "variables";
style.scss
Doordat in tools.scss de variables geforward worden; is alles binnen deze forward beschikbaar in de tools namespace.
@use "tools"; .button { color: tools.$button-color; }
Migreren van @import naar @use en @forward
Het migreren is helaas niet zo eenvoudig als alle @import vervangen met @forward of @use. Zo eenvoudig zou het wel kunnen zijn als je helemaal geen gebruik maakt van variabelen, mixins of functies; maar als dat het geval is dan heb je überhaupt geen SCSS nodig ;-)
Ik ga even uit van de volgende structuur
SCSS map
sass (hoofd folder) molecules _button.scss _card.scss _icon.scss tools functions _base.scss mixins _base.scss variables _base.scss _color.scss _tools.scss style.scss
Voorbeeld situatie bij gebruik van @import
style.scss
// Tools @import "tools"; // Molecules @import "molecules/button"; @import "molecules/card"; @import "molecules/icon";
**tools/tools.scss**
```
@import "variables/base";
@import "variables/functions";
@import "variables/mixins";
```
Als je deze opzet zou willen omzetten dan kun je het volgende doen:
**style.scss**
```
// Molecules
@forward "molecules/button";
@forward "molecules/card";
@forward "molecules/icon";
```
**tools/tool.scss**
```
@forward "variables/base";
@forward "variables/functions";
@forward "variables/mixins";
```
**molecules/\_button.scss | molecules/\_card.scss | molecules/\_icon.scss**
```
@use "../tools" as *;
```
Zoals je ziet hebben we dus altijd minstens 1 `@use` nodig voor het gebruik van variabelen, mixins of functies. Door deze allemaal samen in 1 tools file te zetten kunnen we bestaande projecten redelijk eenvoudig omzetten naar @use en @forward. Je haalt de tools (variables, mixins en functies) uit het hoofdbestand en voegt deze met @use toe aan alle individuele bestanden die gebruik maken van een van deze tools.
In bovenstaand project is dat vrij eenvoudig omdat het hier om een klein project gaat. Als je grotere projecten hebt met ingewikkeldere structuren dan kan het zijn dat de compiler niet wil compilen omdat bestanden meerdere keren worden ingeladen.
Mijn advies is om zoveel mogelijk code proberen te isoleren. Zet het in de globale tools als het globaal beschikbaar moet zijn. Is het niet globaal nodig? Zet het dan in het file waar je het zelf nodig hebt.
Als je de codebase 'plat kan slaan' zou ik dat ook zoveel mogelijk doen. De compiler waarschuwt je als je een bestand meerdere keren probeert te laden; maar de oplossing zoeken kan soms vrij complex zijn.
### **Migratie-uitdaging: Illusion**
Binnen onze projecten maken wij veelvuldig gebruik van het Illusion framework. Illusion geeft ons een aantal krachtige tools waarmee we sneller en efficienter kunnen werken; maar waar ook de gecompileerde CSS beter van wordt. Het voegt ook een aantal basis CSS regels toe waardoor we die als ontwikkelaars niet kunnen vergeten en de gebruikerservaring voor iedereen beter wordt.
Bij het gebruik van Illusion; in ieder geval op de manier waarop wij het inzetten; kwam wel een uitdaging om de hoek kijken. Wanneer je Illusion gebruikt dan doet dat in principe nog helemaal niets. Je hebt een aantal variabelen, mixins en functies tot je beschikking. Vooral die mixins en functies zijn erg krachtige tools. Maar door bepaalde variabelen te overschrijven kun je ervoor zorgen dat Illusion wel CSS compiled. Zo zitten er bepaalde styling regels in voor standaard elementen die voortborduren op normalize CSS genoemd extendalize. Als je deze variable op 'true' zet dan zullen er een aantal regels worden gecompileerd.
De uitdaging zat hem vooral in het feit dat we naast het main CSS file ook diverse 'thema' files compilen. Dus naast een hoofdbestand style.css hebben we bijvoorbeeld ook thema1.css of componentX.scss. Deze files kunnen we dan alleen inladen op de pagina's waar die files nodig zijn. Binnen deze files gebruiken we ook graag Illusion. Ook is het superhandig om alle overschreven Illusion variabelen hiervoor te gebruiken. Ook de aangemaakte custom mixins en functies zijn handig. Kortom; we willen de tools van het hoofdproject inladen. Maar als we dat zomaar doen dan worden binnen deze thema files ook o.a. alle extendalize CSS gecompileerd met als resultaat dubbele CSS code die nergens voor nodig is.
Onze oplossing met @import was altijd het opslitsen van de Illusion variabelen overrides in twee files namelijk `illusion-settings.scss` en `illusion-includes.scss` . Het includes file zag er dan bijvoorbeeld zo uit:
```
$illusion-extendalize: true !default;
$illusion-form: true !default;
$illusion-custom-select: true !default;
$illusion-multiple-choice: true !default;
```
En style.scss bevatte dan bijvoorbeeld:
```
@import "tools/variables/illusion-includes";
@import "tools/tools";
```
Terwijl tools.css het volgende had:
```
@import "tools/variables/illusion-variables;
// Hier wordt Illusion daadwerkelijk geladen
@import "../../../../../../node_modules/illusion/scss/illusion";
```
Binnen een thema konden we dan gemakkelijk het volgende doen
themas/thema1/tools/illusion-includes.scss
```
$illusion-extendalize: false !default;
$illusion-form: false !default;
$illusion-custom-select: false !default;
$illusion-multiple-choice: false !default;
```
En themas/thema1/thema1.scss bevatte dan bijvoorbeeld
```
@import "tools/variables/illusion-includes";
// Hier laden we de tools van het "hoofdbestand"
@import "../../tools/tools";
```
Op deze manier gebruikte we dezelfde tools maar werden er andere variables mee gestuurd.
Helaas is dit niet meer mogelijk bij het gebruik van @use. Illusion twee maal gebruiken binnen 1 folder structuur met andere overschreven variabelen geeft een compileerfout. Gelukkig is ook daarvoor een oplossing. Je kunt een `with ()` meegeven aan een `@forward` waardoor je alsnog andere variabelen kunt meesturen.
#### Oplossing:
**tools/\_index.scss**
```scss
// Settings
$use-extendalize: true !default;
$use-custom-select: true !default;
$use-multiple-choice: true !default;
$use-form: true !default;
// Illusion
@forward "illusion" with (
$extendalize: $use-extendalize,
$custom-select: $use-custom-select,
$multiple-choice: $use-multiple-choice,
$form: $use-form
);
```
**In alle bestanden waar je Illusion nodig hebt; bijvoorbeeld molecules/button.scss**
```scss
@use "../tools" as *;
```
Binnen je thema's kun je in themas/thema1/tools/index.scss het volgende zetten:
```scss
// Hiermee laden we de tools uit het main file; maar dan met overschreven waarden
@forward "../../../tools" with (
$use-extendalize: false,
$use-custom-select: false,
$use-multiple-choice: false,
$use-form: false
);
```
Vervolgens kun je in alle files binnen een thema ook gewoon het volgende doen:
```scss
@use "../tools" as *;
```
---
### **Conclusie**
Met `@use` en `@forward` maak je SCSS-projecten schaalbaarder, beter gestructureerd en toekomstbestendig. Of je nu begint met een klein project of een groot project omzet: deze nieuwe aanpak biedt tal van voordelen. Succes met je migratie!
6DecCraft CMS & Next.js website for Backbase.com
Just dropped 🔥 Check out the new Backbase website! With headless Craft CMS & Next.js frontend.
Enjoy the game! New website for anwbgolf.nl
ANWB Golf, the largest digital golf club with over 55,000 members, asked us to build their new online clubhouse.
An interactive online conference that looks like a retro game?!
Last year we made an interactive online Christmas card and that inspired one of our clients to have an idea. "Couldn't we offer our range of training courses in a fun, online and interactive way this year?"
Is this page for me?
This blog is intended as inspiration for people who want to reach their target audience(s) through their website.
20MayCraft Verified Partner!
We have been working with Craft CMS for a couple of years now so it was time to become a Craft Partner and get verified.
14AprA Christmas Carol - The story of Ebenezer Scroll
Sometimes you just want to make something beautiful and fun! We like to create extra bright spots, especially during these dark days. This is how the idea for our alternative digital Christmas card was born.
22DecCustomer portal Parcls.com
Parcls is the sustainable and personal neighborhood parcel service in Amsterdam where consumers can store, collect, return and have their parcels delivered at home with zero emission. In this new version of the website, but especially the improved dashboard and customer portal, we have used the Craft CMS.
Craft CMS website of "NPO Luister"
The Dutch Public Broadcasting (NPO) launches the new audioplatform "NPO Luister" for all fans of podcasts and radio. The audience can now listen to all podcasts and radiobroadcasts of the NPO for free with one APP. For the promotion of this APP we build the supporting website.
We keep building!
Our wonderful office is quiet for some months now but happily we can keep building from our office homes!
11May