新しいSassの@use、@forwardを組み合わせて変数を別ファイルに分割する

# dart-sass ではグローバル変数が使えない

dart-sass というか、今後の sass についてです。
下記の記事が新しい`@use`記法について詳しく解説されており、大変勉強になりました。

[≫Sassを@importから@useに置き換えるための手引き](https://kojika17.com/2020/05/next-generation-sass-module-system.html)

これまでの sass との違いが解説されていますが、独自フレームワークを作成している自分にとって一番大きな変更点は**グローバル変数が使えない**という点です。

**結論から言うと、`@forward`と`@use`を併記すれば変数を別ファイルに分割できるという記事です。**

## 今までの書き方
今までは変数、スタイル定義、出力用親ファイルという構成でした。

```scss:_vars.scss
$color: #000;
```
```scss:_btn.scss
.btn{
  color: $color;
}
```
```scss:common.scss
@import "vars";
@import "btn";
```

## `@use`記法ではエラーになる
```scss:common.scss
@use "vars";
@use "btn"; // _btn.scss の中では $color は見えない

body{
  color: vars.$color; // こちらは問題ない
}
```
これはエラーになります。

`@import`記法では`$color`はグローバル変数ですが、`@use`記法でのスコープは`_vars.scss`にあります。そのため`_btn.scss`からはアクセスできません。

`common.scss`からは`vars.$color`という感じにネームスペースを付与してアクセスします。

# 対策1：変数とスタイル定義を同じファイルにする

グローバル変数が使えないならば変数とスタイルを同じファイルにまとめてみます。
**後から変更できるように**`!default`を付けておきます。
変数の上書きには`with`を使います。

```scss:_btn.scss
// !default を付けると上書き可能になる
$color: #000 !default;

.btn{
  color: $color;
}
```
```scss:common.scss
// with を使うと !default 指定された変数を上書きできる。
@use "btn" with (
  $color: #333
  );
```
```css:出力結果
.btn{
  color: #333;
}
```
この方法なら問題ありませんが、**変数が大量にあるモジュールでは見通しが悪くなります。**
やはり変数は別のファイルに分けたいです。

# `@forward`を使う

`@forward`は`@use`と同じく、ファイルの読み込みです。
違いをざっくり言うと**`@forward`はさらに他のファイルから読まれることを前提とした時に使う**という点です。

例えば下記はエラーになります。

```scss:_var1.scss
//変数定義ファイル
$color: #000 !default; // !default で変更可能にする
```

```scss:_import.scss
// 変数ファイルをまとめて読む
@use "var1";
@use "var2";
@use "var3";
```
```scss:common.scss
@use "import";

body{
  color: import.$color;
}
```

`_import.scss`で`@forward`を使えば問題ありません。

```scss:_import.scss
// @forward を使って変数ファイルをまとめて読む
@forward "var1";
@forward "var2";
@forward "var3";
```
```scss:common.scss
@use "import" with {
  $color: #ccc;
};

body{
  color: import.$color;
}
```
```css:出力結果
body{
  color: #ccc;
}
```

## `@forward`の読み込み元ファイルからはアクセスできない

`@forward`にはもうひとつ特徴があります。それは**読み込み元からはアクセスできない**という点です。

例えば下記はエラーになります。

```scss:_vars.scss
$color: #000 !default;
```
```scss:_btn.scss
@forward "vars";

.btn{
  color: vars.$color;
}
```

`@forward`はスコープを転送しているので`_btn.scss`からはアクセスできません。


# 対策2：`@forward`と`@use`を併記する

というわけで結論です。

`@forward` `@use` を併記することによって**変数ファイルを外に出すことができ**、さらに**親から上書きすることもできます。**


```scss:_vars.scss
$color: #000 !default;
```

```scss:_btn.scss
@forward "vars";
@use "vars";  // 同じファイルを @use でも読む

.btn{
  color: vars.$color;
}
```

```scss:common.scss
@use "btn" with (
  $color: #ccc
  );

body{
  color: btn.$color;
}
```


```css:出力結果
.btn{
  color: #ccc;
}
body{
  color: #ccc;
}
```

これがベストプラクティスとは言えないでしょうが、誰かがもっと良い方法を考えてくれるまではこの方法を使いたいと思います。

## `@forward`と`@use`を同時に使っていいの？

`@forward`と`@use`を同時に使っていいの？　という点に関しては[公式に書かれてました](https://sass-lang.com/documentation/at-rules/forward)。

>If you do write both a @forward and a @use for the same module in the same file, it’s always a good idea to write the @forward first. That way, if your users want to configure the forwarded module, that configuration will be applied to the @forward before your @use loads it without any configuration.

google翻訳さんによると

>同じファイルに同じモジュールの@forwardと@useの両方を書き込む場合は、常に@forwardを最初に書き込むことをお勧めします。 そうすれば、ユーザーが転送されたモジュールを構成したい場合、@ useが構成なしでモジュールをロードする前に、その構成が@forwardに適用されます。

ということで問題ないようです。


# グローバル変数が使いたければ css変数を使う

もっと気楽にグローバル変数が使いたいという時は css変数を使うほうがいいです。

例えばボタン class のバリエーションを作りたいというなら`--btn-color: #333;`みたいなのを定義して、それを上書きするほうがスマートでしょう。