Sassに新しいモジュールシステムが導入されます。具体的には@use@forwardという2つのルールが登場し、@importルールは将来的に廃止される予定です。現在、Sassのおもな実装としてDart SassとLibSassがありますが、まずDart Sassへの実装が進んでいて、今年10月に安定版がリリースされる予定です。この新しいモジュールシステムを簡単に紹介したいと思います。

なお、ここで言うSassの「モジュール」とは、メンバー(変数、関数、ミックスイン)のコレクションを指し、具体的にはSassのソースファイルのことです。

背景

新しいモジュールシステムが開発された背景として、@importルールがかかえる多くの問題があります。ファイルスコープがないため依存モジュールの把握が困難になったり、名前空間を持たないためにライブラリの作者と利用者が命名を工夫しないといけなかったり、といった具合です。

Sassの新しいモジュールシステムはこれらの問題に取り組むとともに、Sassをほかのモダンな言語に近づけることを目指しています。

@use

@use@importを置き換えるルールで、Sassファイル読み込むための新しい方法です。その最大の特徴はファイルスコープを持つ点です。

たとえば、プロジェクト全体で利用する変数をまとめた_theme.scssというファイルがあったとします。このファイルをブロックの外側で@importすると、変数はグローバルになり、どのファイルからもアクセスできます。

main.scss:

@import "theme";
@import "base";

_theme.scss:

$font-stack-sans: "Helvetica Neue", sans-serif;
$font-stack-serif: "Georgia", serif;

_base.scss:

body {
  font-family: $font-stack-serif;
}

一方@useを使って読み込むとこれらの変数はグローバルにはならず、読み込んだファイル内でしかアクセスできません。またデフォルトでファイル名に応じた名前空間を持ち、以下のようにnamespace.$variableという形式で参照することになります。これは関数とミックスインも同様です。

main.scss:

@use "base";

_base.scss:

@use "theme";

body {
  font-family: theme.$font-stack-serif;
}

@importによる読み込みでは、あるSassファイルで使われているメンバーがどこに宣言されているかを把握するのがかなり大変でした。新しいモジュールシステムではファイルごとに参照するモジュールを宣言するため、そのSassファイルがどのモジュールに依存しているのか簡単に把握できるようになります。

名前空間のコントロール

@useを宣言するときにas節を使うと独自の名前空間を持たせることもできます。

@use "theme" as t;

body {
  font-family: t.$font-stack-serif;
}

このときas *とすると、そのモジュールはトップレベルの名前空間に展開されます。ただしモジュールを読み込むファイル内でメンバーの名前が重複するとエラーになるので注意が必要です。

@use "theme" as *;

body {
  font-family: $font-stack-serif;
}

ライブラリの設定

!defaultフラグを持った変数を上書きするとき、従来の@importルールでは変数をグローバルに定義する必要がありました。@useではファイルの読み込みと同時にwith節を用いることで変数を上書きでき、グローバルな名前空間を汚染しません。

_typography.scss:

$base-font-size: 1rem !default;
$base-line-height: 1.5 !default;

@mixin large-text () {
  font-size: $base-font-size * 2;
  line-height: $base-line-height * 0.75;
}

_base.scss:

@use "typography" with (
  $base-font-size: 1.125rem,
  $base-line-height: 1.6,
);

h1 {
  @include typography.large-text();
}

プライベートメンバー

名前が-または_ではじまるメンバーはプライベートになります。これらのメンバーは宣言されたファイル内でしかアクセスできません。

_theme.scss:

$-count: 0;

_base.scss:

@use "theme";

.layer {
  z-index: theme.$-count; // Error!
}

@forward

@forwardルールはほかのモジュールのメンバーを読み込みつつ、それらを現在のモジュールのAPIとしてエクスポートします。@useとは違って、@forwardを宣言しているファイル内ではそのモジュールのメンバーを参照できません。たとえば大きなライブラリを作るときなどに便利そうです。

bootstrap/_index.scss:

@forward "functions";
@forward "variables";
@forward "mixins";

_base.scss:

@use "bootstrap";

p {
  margin-top: 0;
  margin-bottom: bootstrap.$paragraph-margin-bottom;
}

show節でどのメンバーをエクスポートするかを選べます。それ以外のメンバーはエクスポートされません。

@forward "functions" show color-yiq;

逆にプライベートにしたいメンバーをhide節で除外することもできます。

@forward "functions" hide assert-ascending;

実際に試してみる

新しいモジュールシステムの実装されたSass 1.23.0は、すでにベータ版が提供されていて、パッケージマネージャーSassのリポジトリからインストールできます。また実際に試せるリポジトリも用意してみました。ぜひ触ってみてください。

タイムライン

今年10月にはDart Sassで安定版がリリースされる予定です。LibSassへの実装はまだこれからで、スケジュールも未定だそうです。

@importルールは、モジュールシステムがDart SassとLibSassの両方でリリースされてから1年後、またはDart Sassのリリースから2年後(遅くとも2021年10月)には非推奨になります。そしてそのさらに1年後(つまり遅くとも2022年10月)にサポートが終了する予定です。

おわりに

実際のプロジェクトで使ってみないことには見えてこないこともあるでしょうし、普及するまでにはそれなりの混乱もあるものと思われます。とは言えSassがより使いやすい言語になるのは間違いないのではないしょうか。楽しみですね。

参考資料