Co jsou Sass moduly a jak @use a @forward zlepší váš kód
Sass moduly mění způsob práce se soubory. Nejsou novinkou, představeny byly před více než rokem. Nově je však Dart Sass jedinou oficiální implementací Sassu. Nucená migrace je příležitostí s moduly začít. Přinášíme podrobného průvodce migrací, zapojením Sass modulů a řešením nejčastějších chytáků.
Sass moduly nejsou úplná novinka, přesto dosud stály trochu stranou pozornosti. Představeny byly před více než rokem, přesně 2. října 2019, spolu s implementací v oficiální, ale nepříliš rozšířené knihovně Dart Sass. Jiné knihovny pro kompilaci Sassu podporu modulů nikdy nepřinesly, včetně dodnes nejpopulárnější knihovny LibSass. Uplynul rok a vše je jinak: 26. října letošního roku vyšlo oznámení, že Dart Sass zůstává jedinou oficiálně podporovanou knihovnou pro kompilaci Sass do CSS. Bootstrap 5 na Dart Sass přešel již 3 dny po tomto oznámení – a ani my bychom neměli otálet.
Dart Sass je od 26. října 2020 jedinou oficiálně podporovanou knihovnou pro kompilaci Sass do CSS.
Původní implementace Sassu byla napsána v Ruby. Později byla tato knihovna přeportována do C/C++ a vznikla kolem ní řada wrapperů, které umožnily kompilovat Sass do CSS v nejrůznějších programovacích jazycích: mj. Go, Java, Lua, Perl nebo PHP. Ve světě Node.js se oním wrapperem stal projekt node-sass
. V době vydání článku má node-sass
podle npmjs.com stále přes 5 milionů stažení týdně.
Od loňského roku ji Dart Sass, respektive sass
(sass
je distribuce Dart Sass zkompilovaného do čistého JavaScriptu), pomalu, ale jistě dotahuje: z milionu stažení týdně v loňském listopadu vyrostla popularita na více než 2,6 milionu stažení. Jak víme, předvídání epidemiologických a podobných křivek je ošemetné. Lze se nicméně domnívat, že po nedávném oznámení se křivky počtu stažení během pár měsíců protnou a Dart Sass se stane nejrozšířenější implementací Sassu.
Problémy s @import
Vedle proměnných, které jsme v CSS dlouhé roky postrádali, je jedním z největších přínosů preprocesorů oproti čistému CSS možnost přehledně rozdělit zdrojový kód do množství menších souborů. Ty pak s pomocí @import
a knihovny kompilujeme do finálního CSS souboru, který servírujeme prohlížečům. Díky tomu je náš kód řádově přehlednější. @import
má však své nevýhody:
@import
je zároveň funkcionalita nativního CSS, což může být matoucí.- Vícenásobné importování stejného souboru může vést ke zpomalení kompilace, konfliktům a nežádoucím duplicitám.
- Vše je importováno do globálního namespace, což z něj dělá doutnající sklad střelného prachu.
- Problém implicitního importu: při volání mixinu nebo proměnné nemusí být vůbec jasné, kde se vzaly. Soubor volaný s
@import
může být k dispozici i v souborech stylopisu, které ho přímo nevolají, což je matoucí a může vést k chybám.
Jistě, můžeme se pokusit proměnné, funkce a mixiny alespoň prefixovat a udržovat v jejich pojmenování podobný hierarchický systém, jakým je BEM pro CSS třídy. Nástroj je to však umělý a nedokonalý. S nástupem Sass modulů proto @import
nahrazují nová zavináčová pravidla (at rules) @use
a @forward
.
Import souborů pomocí @use
@use
je de facto nástupcem Sass příkazu @import
. Přináší ovšem podstatné rozdíly:
@use
importuje Sass modul pouze jednou, a to při prvním volání, bez ohledu na to, kolikrát volaný modul ve svém kódu použijete. Jinými slovy, první volání@use
v kódu je vlastním importem modulu a každé další@use
je už jen jeho použitím.@use
veškerý importovaný kód (proměnné, funkce, mixiny) zpřístupní v namespace odpovídajícím názvu volaného modulu.@use 'settings/grid'
tak zpřístupní vše v souborusettings/_grid.scss
v namespacegrid
, takže proměnné modulu budeme volatgrid.$gap
atd.- Kód importovaný pomocí
@use
zůstává dostupný pouze v aktuálním souboru. Soubory, které aktuální soubor importují nemají tento kód k dispozici. - Podobně je omezena působnost
@extends
– použije se na selektory v importovaných souborech, ale už ne v souborech, které aktuální soubor importují. - Proměnné, funkce a mixiny, které začínají podtržítkem
_
nebo spojovníkem-
(lidově a nesprávně pomlčkou) jsou brány jako privátní a nejsou importovány.
Typické použití Sass modulů může vypadat takto:
// settings/_container.scss
$max-width: 60rem;
$padding: 1.5rem;
// tools/_container.scss
@use '../settings/container';
@mixin create() {
max-width: container.$max-width;
padding-right: container.$padding;
padding-left: container.$padding;
}
// objects/_container.scss
@use '../tools/container';
.container {
@include container.create();
}
Sass moduly tak přináší do kódu tyto benefity:
- Původ závislostí je známý, protože v každém souboru musí být explicitně uvedeny.
- Místo prefixování globálních proměnných, funkcí a mixinů stylem
$container-max-width
je kód čitelnější díky strukturovanému zápisucontainer.$max-width
. - Díky konceptu privátního obsahu (jehož název začíná na
_
nebo-
) můžeme kód uvnitř modulů dále zpřehlednit rozdělením do menších, skutečně privátních proměnných, funkcí a mixinů, které nejsou exportovány a nelze je použít jinde. Díky tomu nezanáší globální kontext a jejich názvy nemusí být globálně jedinečné.
Přejmenování importů
Pokud jste předchozí ukázku pročetli pozorně, možná vás napadá: ale co mám dělat, když budu v jednom souboru potřebovat jak modul settings/container
, tak stejnojmenný tools/container
? V takovém případě můžete importované moduly přejmenovat:
@use '../settings/container' as settings;
@use '../tools/container' as tools;
.container {
@include tools.create();
padding-top: settings.$padding;
}
V případě, že byste se namespace potřebovali z nějakého důvodu zbavit úplně, je to možné, i když za cenu nižší expresivity kódu:
@use '../tools/container' as *;
.container {
@include create();
}
Konfigurovatelné importy
Zatímco s @import
je třeba knihovnám předávat konfiguraci přes proměnnou v divočině globálního kontextu, kde hrozí konflikt definic nebo nechtěné přepsání hodnoty, s @use
je možné tu samou konfiguraci přesně a cíleně nasměrovat přímo do daného modulu:
// _button.scss
$button-color: forestGreen !default;
.button {
background-color: $button-color;
}
// styles.scss – dříve
$button-color: deepSkyBlue;
@import 'button';
// styles.scss – nově
@use 'button' with (
$button-color: deepSkyBlue;
);
To je velice šikovné především pro knihovny. Podmínkou je, že jde o první volání @use
daného modulu (další @use
lze použít, už ale ne konfigurovat) a že je daná proměnná v modulu definovaná s příznakem !default
, čímž říká, že je otevřena přetížení. Je dobré vědět, že tento zápis lze využít i pro závislosti, které funkcionalitu Sass modulů dosud nevyužívají a počítají s konfigurací v globálním kontextu.
Rozšíření předávaného obsahu s @forward
Sass moduly neznamenají pouze výměnu @import
za @use
. Pravidlo @forward
umožňuje modulu navenek zpřístupnit i takové proměnné, funkce a mixiny, které v aktuálním souboru nemají být dostupné. Díky tomu je možné do vnějšího kontextu přeposlat vybrané pomocné funkce, aniž bychom přišli o granularitu zdrojových souborů:
@forward 'functions';
@forward 'variables';
@forward 'mixins';
Klíčová slova show
a hide
umožňují přesnější kontrolu nad přeposílaným obsahem:
@forward 'functions' hide color-contrast;
@forward 'variables' show $theme, $theme-light, $theme-dark;
@forward 'mixins';
Narozdíl od @use
neobaluje @forward
přeposílaný obsah do namespace. Můžeme jej však před přeposláním prefixovat:
// _theme.scss
$primary: blue;
// library.scss
@forward 'theme' as 'theme-*';
// project.scss
@use 'library' with ($theme-primary: green);
Vestavěné moduly
Velmi zajímavá je nabídka vestavěných Sass modulů: sass:math
, sass:string
, sass:list
, sass:map
, sass:selector
a sass:meta
. A to hned ze dvou důvodů. Zaprvé, vestavěné moduly zásadně rozšiřují možnosti práce s proměnnými, funkcemi a mixiny, ale nejen s nimi. Zadruhé, vyčleněním Sass funkcí do namespaců nedochází ke konfliktům s nativními funkcemi CSS, což je dobré teď a pro obě strany i do budoucna.
Řada funkcí v Sass modulech má z důvodu kompatibility své aliasy v podobě názvů původních vestavěných Sass funkcí, např. color.adjust()
má alias adjust-color()
. Zásadním rozdílem mezi starými a novými Sass funkcemi ale je, že ty nové jsou k dispozici pouze v rámci svých modulů, můžeme je tedy použít až po jejich importování:
// Dříve
$new-color: adjust-color($color, $amount);
// Nově
@use 'sass:color';
$new-color: color.adjust($color, $hue: $amount);
Pozor na to, že některé dosavadní Sass funkce nemají z koncepčních důvodů ve vestavěných modulech úplně přesné ekvivalenty. Příkladem budiž třeba hojně využívaná funkce transparentize()
/ fade-out()
se svým protějškem opacify()
/ fade-in()
, ale i lighten()
, darken()
a další. Přesné rozdíly v chování najdete v dokumentaci.
Migrace z node-sass
na sass
Migrace je ve většině případů bezbolestná, Sass moduly jsou plně zpětně kompatibilní. Stačí odinstalovat původní knihovnu, nainstalovat sass
a zapojit do automatizace – nic víc by nemělo být třeba.
$ npm uninstall node-sass && npm install --save-dev sass
Je dobré zmínit, že sass
přináší oproti node-sass
nemalou úsporu v závislostech. Ve spojení s dalšími aktualizacemi nemusí být patrná:
Zatímco u čistých migrací je vidět dobře:
Stylelint
Pokud používáte Stylelint (což byste měli), je možné, že budete potřebovat rozšířit povolená zavináčová pravidla v jeho konfiguraci o @use
a @forward
. K zádrhelu nemusí dojít, využíváte-li některou z komunitních konfigurací jako třeba stylelint-config-recommended-scss
, která povoluje jakákoli zavináčová pravidla bez omezení. Jenže pozor, to v důsledku činí jejich kontrolu bezzubou. Spolu s novými @use
a @forward
vám totiž projde úplně @cokoli
– a to nechcete.
Webpack
Pracujete-li na javascriptové aplikaci, pravděpodobně ve vašem stacku nebude chybět Webpack. A pokud píšete styly v SCSS, téměř jistě do něj máte zapojený sass-loader
. Ten sám o sobě podporuje jak node-sass
, tak Dart Sass, takže možná konfiguraci Webpacku vůbec nebudete muset upravovat.
Jediný chyták nastává s načítáním externích modulů z node_modules
. Zatímco s @import
jste mohli do node_modules
odkázat vlnovkou na začátku importu, tedy např. @import '~normalize.css/normalize.css'
, s @use
to není možné. Stačí ale node_modules
doplnit do prohledávaných cest v konfiguraci sass-loader
u, tildu vynechat a máte vyhráno:
// styles.scss
@use 'normalize.css/normalize.css';
// webpack.config.js
{
loader: 'sass-loader',
options: {
sassOptions: {
includePaths: ['node_modules'],
},
},
},
CLI a npm skripty
Pozor na to, že CLI API sass
se od node-sass
trochu liší. Je však šance, že to, co jste s původní knihovnou nastavovali pomocí CLI argumentů budete moci vynechat, protože se sass
půjde o výchozí nastavení.
Příklad npm skriptu v package.json
v původní podobě s node-sass
:
{
"scripts": {
"css:compile": "node-sass --output-style expanded --source-map true src/main.scss built/main.css"
}
}
Nový npm skript po migraci na sass
:
{
"scripts": {
"css:compile": "sass src/main.scss built/main.css"
}
}
Volání knihovny sass
je zde podstatně jednodušší, protože expanded styl zápisu a generování source map jsou ve výchozím stavu zapnuté. Dohledání externích závislostí v node_modules
lze opět vyřešit buď odmazáním vlnovky:
@use 'normalize.css/normalize.css';
… a doplněním cesty do konfigurační volby --load-path
:
{
"scripts": {
"css:compile": "sass --load-path=node_modules src/main.scss built/main.css"
}
}
… anebo explicitním vypsáním úplné relativní cesty:
@use '../../node_modules/normalize.css/normalize.css';
Záleží, co lépe vyhovuje vašemu projektu.
Samozřejmě jsou i další automatizační nástroje jako Parcel, Gulp aj., svět nepoužívá pouze Webpack nebo npm skripty. Princip migrace by se ale ani v jejich případě neměl nijak zásadně lišit.
Automatická migrace na Sass moduly
Pokud vám nestačí jen vyměnit kompilující knihovnu a chcete začít naplno využívat Sass moduly, jejichž podporu sass
přináší, můžete vyzkoušet automatický nástroj pro migraci přímo od autorů Sassu. Stačí jej nainstalovat:
$ npm install --global sass-migrator
A spustit (na zkoušku raději s --dry-run
):
$ sass-migrator module --dry-run --migrate-deps styles.scss
V případě migrace knihovny se může hodit zachovat dostupnost všech proměnných, funkcí a mixinů, a to přepínačem --forward=all
:
$ sass-migrator module --dry-run --migrate-deps --forward=all library.scss
Mějte na paměti, že soubory, které už jsou volané s pomocí @use
bude sass-migrator
považovat za již zmigrované. Více podrobností a další způsoby instalace najdete v dokumentaci.
Shrnutí
Co bude dál s @import
a se starými Sass funkcemi? Zatím zůstávají k dispozici, aby vývojáři měli dostatek času přemigrovat své projekty na nový modulární systém. Obě funkcionality budou časem zapovězeny a nakonec úplně odstraněny ve prospěch Sass modulů. A kdy k tomu dojde? Podle oficiálních plánů budou globální funkce Sassu spolu s @import
označeny za deprecated nejpozději 1. října 2021. Rok nato, 1. října 2022, pak bude jejich podpora definitivně odstraněna. Dosavadního plánu se zatím vývojářský tým Sassu držel, víme tedy přesně, kolik času máme na to se připravit.
Ponechání knihovny Dart Sass jedinou oficiální implementací a ukončení vývoje těch ostatních (oficiálních) je pro vývojáře impulzem ke změně. Je jen na nás, zda situaci pojmeme čistě jako výměnu jednoho kolečka ve frontendovém soukolí, anebo využijeme příležitost k tomu stroj rozebrat, promazat a znovu sestavit. Výhody konceptu Sass modulů, včetně těch vestavěných, jsou zásadní, a já věřím, že není nač čekat.
Související odkazy
- The Module System is Launched (Natalie Weizenbaum, Sass, 2019)
- Introducing Sass Modules (Miriam Suzanne, CSS Tricks, 2019)
- Understanding Dart Sass modules and namespaced variables (Kevin Hougasian, RIMdev, 2019)
Úvodní ilustraci způsobil autor článku. Nepoužívejte ji prosím bez jeho souhlasu nebo citace.
Další články od autora:
Sdílejte
Líbí se vám článek? Podpořte jej sdílením!
Komentujte
Chcete k článku něco doplnit? Našli jste chybu? Napište e-mail.