【WordPress】Sass の @import が非推奨になるから、「おじいちゃん平成くさーいwww」とキッズから馬鹿にされる前に遅ればせながら滑り込みで @use に書き換えてみたよ【editor-style.css】

【2022/09/09 追記】
タイトルを編集しました。

いつものように Twitter を見ていたら(仕事中じゃないですよ)こんな投稿が目に飛び込んできた。

あーそうなのよ、そうなんですよ…。そいつはわかっちゃいるんですがね…
てか僕の場合「昭和くさーい」と言われないか不安だわ(笑

Sass(SCSS)から離れられない身体になってしまいました

Web 関連の仕事をしている人であればいまさら説明は必要ないだろうが、Sass は CSS(カスケーディングスタイルシート)を拡張したメタ言語だ。Wikipedia では「スクリプト言語」ともある。
僕は SCSS 記法でしか Sass を利用していないので、この記事の中での Sass は SCSS 記法での Sass を意味する。
もうひとつ、Sass を「コンパイル」するのか「トランスパイル」なのかはまた別の話なので、ここでは「コンパイル」で統一して記述するのであしからず(笑

タスクを管理する環境の変化

僕が Web 制作の仕事を始めたのはもう10年ほど前になるが、ちょうどその頃 HTML5(いまはもう HTML Living Standard になってしまったが)が使われ始めた頃で、その数年後には(たまたま知る機会があったおかげで)もう Grunt (「タスクランナー」という呼び方も懐かしく感じてしまう)を利用して Sass をコンパイルするような環境に触り始めていた。
Grunt がようやく動かせるようになった頃には「これからは gulp だよ!」と言われ、gulpfile の記述に慣れた頃には「え?npm scripts で書いてないの?」って言われるようななかなかハードな状況ではあったけれども(笑

Web の進歩は日進月歩だとはいえ、いつまで経っても勉強が終わらない(笑
それでも、新しい環境になれば以前よりも便利に、速く、ラクになっていることは間違いないので、老体に鞭打って(笑)どうにか(半分はそれを楽しむよう心がけながら)やっている現状ではある。

僕は使ったことはないのだが、Sass での開発環境としては GUI でコンパイルできる Prepros なんていうのもあるし、最近はエディターである VSCode の拡張機能でもコンパイルできるものがあるようだ。
いま時点では僕は npm scripts を利用しているが、これだけ進歩のスピードが凄まじい Web のことなので来年にはもうすっかり開発環境が違ってしまっているかもしれない。それでも相変わらず Sass は使っているのではないだろうか。でも CSS もどんどん進化してるし、いずれは CSS だけで全部実現できるようになるのかもね…。

Sass を利用する大きなメリットのひとつ、入れ子での記述

Sass は高機能なので、すべてを説明しようとすると一冊の本になるくらいには豊富な機能があるのだが、Sass を使う人すべてが利用しているであろう一番わかりやすい機能と言ったら「入れ子で記述ができる」ということではないだろうか。

詳細度が高い要素に対して CSS を記述するためにはその要素に対応するクラスを毎回すべて列記しなくてはいけないが、コンパイルして CSS を出力することが前提の Sass の場合は、Sass で入れ子で記述したクラスが CSS では列記された状態で出力される。
また、この入れ子を記述するときにインデントを揃えることで、入れ子の構造・階層もひと目でわかりやすくできる。この記法に慣れてしまってからは、素の CSS を記述するのがなかなか苦痛になってしまった。

Sass を利用するもうひとつの大きなメリット、@import

もうひとつ、僕が思う大きなメリットがこの @import だと思っている。
@import 自体は CSS でも利用可能だが、CSS で @import を使うのは読み込み速度の問題であまり推奨されない、と言われ続けてきた。いま現在それがどうなのか、それがまだ正しい認識なのかはちょっと怪しいのだが、ざっと確認した限りでは「問題ない」としたものは見つからなかったので、やはりいまでも CSS で @import を使うのは推奨されないのだろう。

一方、Sass の場合にはインポートされたファイルの記述はコンパイルして出力される CSS の中に含まれて書き出されるので、結果として CSS の中に @import の記述が残らない。ここも僕が特に気に入って Sass を使っていた理由だ。

数年前から Web のデザインがコンポーネント(ページのデザインのうち、ひとつの機能だけを構成するパーツ群)単位で考えてデザインされるようになり、それにつれてCSS の記述もそのコンポーネントそれぞれでファイルを分割して管理する方が都合がよくなってきている。
(もちろん全てではないけれど)Web サイトのデザインが細かいコンポーネントの集合体として構成されることが多くなり、コードもそれに呼応して HTML と CSS の小さなセットがコンポーネントとしてたくさん集まってひとつの Web サイト(の構造とデザイン)を構成するという、特に僕が普段触っている WordPress のような CMS では非常に扱いやすくなってきていると感じる(そういう要求を満たすためにコンポーネント単位で作られるようになってきた、というのが正しいんだろう)。

これを効率よく実現できるのが Sass の @import だったのだ。

@import が非推奨になる…だと…?

実際の作業環境では、前述したようにタスク管理には npm scripts を使っている。
ただ、もともと gulp を使っていたものを npm scripts へ移行した経緯もあって、利用している記述内容や技術に古いものがあったりするのは否めない。
タスク管理が Gruntgulpnpm scripts と移行してきたのと同様に、Sass をコンパイルする環境も ruby-sassLibSassnode-sass)→ Dart Sasssass) と変化してきた。これまでもなんとか追従してきたのでもちろん今後も対応していくつもりだが、その環境の問題とは別の問題が起きた。

それが冒頭に引用したツイートにある『@import が非推奨になる』という問題だった。

WordPress の開発環境で使う Sass の @import

前述したように、僕が普段している作業は WordPress のテーマあるいはプラグインの開発だ。

WordPress の場合、編集画面が WYSIWYG(ひゃー懐かしい表現(笑)。What You See Is What You Get のことで、編集画面で見たままがサイトに表示されるということ)になっているので、フロント(ブラウザで表示されるウェブサイトそのもの)と WordPress の編集画面のなかで表示されるデザイン・レイアウトが一致することが求められる。
つまりフロントと編集画面それぞれにそのテーマ専用の(言い換えればそのサイトのデザイン専用の)スタイルシートが読み込まれる必要がある、ということだ。

以前のいわゆる「クラシックエディター」の時代には、基本的に同じスタイルシートをそれぞれで読み込ませればよかった(もちろんこれはそのスタイルの詳細度に左右されたりする)のだが、2018年12月にリリースされた WordPress 5.0 から利用される「ブロックエディター」では、全く同じスタイルシートを読み込ませればいいというわけには行かなくなってしまった。
これはサイトに出力される HTML の構造と編集画面に表示するための HTML の構造とが一部違っていることが理由で、編集画面に出力する場合はエディター全体を囲むラッパー要素のクラスを付与する必要があるのだ(それ以外にもいくつか編集画面専用のスタイルが必要になるものがある)。
この「ほとんど同じスタイルを充てるけれど一部だけちょっと変えて2種類出力したい」という目的に、Sass の @import はとても相性がいいのだ。

その @import が使えなくなる、だと…?

現在のファイル構造とその中身

いま僕が普段作っているオリジナルテーマやオリジナルプラグインのファイルの構造をうんと簡略化・単純化したものが以下になる。
これはあくまでも解説のための簡略化したものとして、参考程度にご覧いただきたい。

┓
┣ package.json
┃
┣ node_modules
┃
┣ css
┃   ┣ styles.css
┃   ┗ editor-style.css
┃
┗ scss
   ┣ styles.scss
   ┣ editor-style.scss
   ┃
   ┣ common
   ┃   ┣ _reset.scss
   ┃   ┣ _utility.scss
   ┃   ┣ _utility-front.scss
   ┃   ┗ _utility-editor.scss
   ┃
   ┣ layout
   ┃   ┣ _header.scss
   ┃   ┣ _footer.scss
   ┃   ┣ _entry-content.scss
   ┃   ┗ article
   ┃       ┣ _article.scss
   ┃       ┣ _group-front.scss
   ┃       ┗ _group-editor.scss
   ┃
   ┗ component
       ┣ _paragraph.scss
       ┣ _heading-front.scss
       ┣ _heading-editor.scss
       ┣ _image.scss
       ┗ _button.scss

これはいま開発中のオリジナルテーマのディレクトリの内部構造の一部、と思っていただければいい。
テーマ本体の直下にある sass ディレクトリの中の styles.scsseditor-style.scss が、それぞれフロントで出力される styles.css と編集画面に出力される editor-style.css として css ディレクトリへ書き出される、という仕様になっている。

同様に、それぞれの中身を見ていただくと、これが styles.scss

/**
 * styles.scss
 */

@import "common/utility";
@import "common/utility-front";

@import "layout/header";
@import "layout/footer";
@import "layout/entry-content";
@import "layout/article/article";
@import "layout/article/group-front";

@import "component/paragraph";
@import "component/heading-front";
@import "component/image";
@import "component/button";

で、こちらが editor-style.scss

/**
 * editor-style.scss
 */

@import “common/utility”;

.editor-styles-wrapper {

  @import "common/utility-editor";

  @import "layout/article/article";
  @import "layout/article/group-editor";
  
  @import "component/paragraph";
  @import "component/heading-editor";
  @import "component/image";
  @import "component/button";

}

ご覧のように、読み込ませている個別の SCSS ファイルはどちらもほぼ同じものになるが、前述したように

  • 一部「フロントだけに読み込ませたいスタイル(の SCSS ファイル)」「エディターだけに読み込ませたいスタイル(の SCSS ファイル)」がある
  • エディターに読み込ませるスタイルにはラッパー要素のクラス .editor-styles-wrapper を付与する

という違いがある、というのが(あくまでも僕の用意している環境においての)フロントとエディターとにそれぞれ読み込ませるスタイルシートの概要だ。
これを実現していたのがまさに Sass の @import だったので、@use への移行をこれまで面倒がって対応せずにいたという言い訳…

さて、これを書き換えないと…

そして冒頭のツイートですよ(しつこい

いよいよ Sass の @import が非推奨になってしまう。期日は今年10月となっている。
そもそもこの「@import が非推奨になる」ことについて一番最初に読んだのは僕が CSS の師と仰ぐ kojika17 さんのこの記事
この記事が公開されたのが2020年5月だから、そこから考えてもこれまで2年以上猶予はあったはずなのだが、まあ日々の忙しさにかまけてここまで来てしまった…。

いま既に運用段階にあるものについては、今後開発環境そのもののアップデートがなければ使って使い続けられないこともないが、少なくともこれから新たに用意する場合にはそうは言っていられない。重い腰を上げて(笑)今度こそ本腰入れて対応しないと…。

ここでの僕の理解の範疇でざっくり説明すると、

  • @import は非推奨(廃止)になる
  • 代わりに @use と @forword を使う
    • ただし単なる代替ではない
  • @use で変数や関数がカプセル化されるので、直接読み込んでいるファイル以外ではその変数や関数が使えなくなる
    • だから使いたい変数や関数があったら、後述する @forward を使う(という認識でいいはず…)
  • @use はその .scss ファイルの先頭に記述して読み込ませる必要がある
  • @forword は @use でスタイルシートが読み込まれたときに、そこに記述された Mix-in や関数、変数を利用できるようにする
    • @use で読み込んでいるファイルの中で別のファイルに記述した関数とかを呼び出したいときに、その関数を記述したファイルを @forword で呼び出せば使える
    • ライブラリを読み込んでいるファイルの親ファイルなどでも変数や関数などを利用したい場合に @forword を使う

といったものだ(これで認識があってるといいんだけど(笑))。
詳しい(正しい?)仕様については公式ドキュメントの @use のページ@forward のページをご覧いただくとしよう(笑

書き換えの実際

いざ書き換えてみると、もともと変数や関数をろくに使っておらず(変数はほぼ CSS 変数だけを使っている)、ファイル構成もそんなに複雑にしていなかったことが幸いしてか、先程の例でいうところの styles.scss ではそれほどの手間がかからずに書き換えが済んだ。とはいえただ @import を @use に書き換えただけ、というわけには行かず

  • scss ディレクトリ内のすべてのディレクトリの直下にそれぞれ _index.scss を作成し、各ディレクトリ内の .scss ファイルをまずその中で @use を使って読み込ませて、その _index.scssstyles.scss で読み込ませるように修正
  • フロントとエディターで別々に読み込ませたいスタイルがある場合は、それ専用のディレクトリを作ってそこのファイルに記述するように変更
  • 一部で管理上ファイル構成をわかりやすくしようと思ってディレクトリの構造をちょっと深くしていたところがあったので、そこにも _index.scss を用意して同様に処理

といった対応は必要になった。書き換え後の scss ディレクトリのファイル構成は

┗ scss
   ┣ styles.scss
   ┣ editor-style.scss
   ┃
   ┣ common
   ┃   ┣ _index.scss
   ┃   ┣ _reset.scss
   ┃   ┗ _utility-common.scss
   ┃
   ┣ common-front
   ┃   ┣ _index.scss
   ┃   ┗ _utility-front.scss
   ┃
   ┣ common-editor
   ┃   ┣ _index.scss
   ┃   ┗ _utility-editor.scss
   ┃
   ┣ layout-common
   ┃   ┗ article
   ┃       ┣ _index.scss
   ┃       ┗ _article.scss
   ┃
   ┣ layout-front
   ┃   ┣ _index.scss
   ┃   ┣ _header.scss
   ┃   ┣ _footer.scss
   ┃   ┣ _entry-content.scss
   ┃   ┗ article
   ┃       ┣ _index.scss
   ┃       ┗ _group-front.scss
   ┃
   ┣ layout-editor
   ┃   ┗ article
   ┃       ┣ _index.scss
   ┃       ┗ _group-editor.scss
   ┃
   ┣ component-common
   ┃   ┣ _index.scss
   ┃   ┣ _paragraph.scss
   ┃   ┣ _image.scss
   ┃   ┗ _button.scss
   ┃
   ┣ component-front
   ┃   ┣ _index.scss
   ┃   ┗ _heading-front.scss
   ┃
   ┗ component-editor
       ┣ _index.scss
       ┗ _heading-editor.scss

となった。こうやって書き出してみると _index.scss がだいぶ増えたし、ディレクトリも追加されてだいぶ変わったかもしれない(笑
このあたりももともとのディレクトリ構成をいじりながら「こうかな?こうかな??」と探りつつやってみた結果なので、もっと管理しやすい方法がありそうだ。
styles.scss は以下

/**
 * styles.scss
 */

@use 'common' as *;
@use 'common-front' as *;

@use 'layout-common' as *;
@use 'layout-front' as *;

@use 'component-common' as *;
@use 'component-front' as *;

の様になる。こちらはずいぶんスッキリした。
ちなみに新たに作成した _index.scss の一例として layout-front/_index.scss の中身は

/**
 * layout-front/index
 */

@use 'header';
@use 'footer';
@use 'entry-content';
@use 'article' as *;

のようになる。

ところが editor-style.scss の書き換えで詰まってしまった。
具体的には、先ほど解説した中の

  • エディターに読み込ませるスタイルにはラッパー要素のクラス .editor-styles-wrapper を付与する

の部分が、ただ @import を @use に置き換えるだけでは表示できなかった。
やってみて動かなかった editor-style.scss はこちら。

/**
 * editor-styles.scss
 */

@use 'common' as *;

.editor-styles-wrapper {

  @use 'common-editor' as *;

  @use 'layout-common' as *;
  @use 'layout-editor' as *;

  @use 'component-common' as *;
  @use 'component-editor' as *;

}

先ほど @use はその .scss ファイルの先頭に記述して読み込ませる必要がある と書いたが、これはまさにその「@use が使えない」条件に当てはまってしまった。
結果として、もともと editor-style.scss でやっていたような「.editor-styles-wrapper の中に @import で他の Sass ファイルを読み込ませる」が(@forword 含め)@use では実現できなくなってしまったのだ。

さて、どうする?

これを実現したいと思ってあれこれ調べてみたのだが、どうにもやり方がわからない。このググり力のなさよ…(笑
たまたま見つけたツイートでは「これが実現できないからまだ移行できない」なんていうものもあった。ほんそれ、気持ちはめっちゃ分かる(笑

とはいえこれまで出来たことが全く出来なくなってしまうことはないはず、と考えて公式サイトの中のそれっぽいところ(笑)をしらみ潰しに調べてみた。
そこでようやく見つけたのが sass:meta のページだ。

これを読むと

meta.load-css( $url, $with: null )

という Mix-in が用意されていて、

Loads the module at $url and includes its CSS as though it were written as the contents of this mixin.

パラメータ $url からモジュールをロードし、このミックスインがコンテンツ内部に記述されているかのように CSS をインクルードする。

sass:meta

とある。URL(パス)で読み込むファイルが指定できる、ということなのでそこは @use 同様に使うことができる。
そしてさらに

This can be used anywhere in a stylesheet. It can even be nested within style rules to create nested styles!

これはスタイルシートのどこでも使える。スタイルのルール内でネストして、ネストされたスタイルを作成することもできる!

sass:meta

とある。

ネストされたスタイルを作成することもできる!

こ れ だ !!!

早速書き換えてみる

どうにか解決策が見つかったぽいので、さっそくこの meta.load-css( $url, $with: null ) に書き換えてみることにする。
もうひとつ今回の新しい仕様で注意することは、この meta というモジュールを呼び出す際には

@use 'sass:meta';

という記述で明示的に呼び出す必要がある、ということ。
これで書き換えた editor-style.scss がこちら。

/**
 * editor-styles.scss
 */

@use 'sass:meta';
@use 'common' as *;

.editor-styles-wrapper {

  @include meta.load-css( 'common-editor' );

  @include meta.load-css( 'layout-common' );
  @include meta.load-css( 'layout-editor' );

  @include meta.load-css( 'component-common' );
  @include meta.load-css( 'component-editor' );

}

ようやくこれで動かすことができて、無事に editor-style.css が書き出された…!!
入れ子になったディレクトリのファイルもうまく出力できることがわかったので、最終的に整理して用意したディレクトリ構造と styles.scsseditor-style.css がこちら。

┗ scss
   ┣ styles.scss
   ┣ editor-style.scss
   ┃
   ┣ common
   ┃   ┣ _index.scss
   ┃   ┣ _reset.scss
   ┃   ┣ _utility-common.scss
   ┃   ┃
   ┃   ┣ front
   ┃   ┃   ┣ _index.scss
   ┃   ┃   ┗ _utility-front.scss
   ┃   ┃
   ┃   ┗ editor
   ┃       ┣ _index.scss
   ┃       ┗ _utility-editor.scss
   ┃
   ┣ layout
   ┃   ┣ _index.scss
   ┃   ┃
   ┃   ┣ article
   ┃   ┃   ┣ _index.scss
   ┃   ┃   ┗ _article.scss
   ┃   ┃
   ┃   ┣ front
   ┃   ┃   ┣ _index.scss
   ┃   ┃   ┣ _header.scss
   ┃   ┃   ┣ _footer.scss
   ┃   ┃   ┣ _entry-content.scss
   ┃   ┃   ┗ article
   ┃   ┃       ┣ _index.scss
   ┃   ┃       ┗ _group-front.scss
   ┃   ┃
   ┃   ┗ editor
   ┃       ┗ article
   ┃           ┣ _index.scss
   ┃           ┗ _group-editor.scss
   ┃
   ┗ component
       ┣ _index.scss
       ┣ _paragraph.scss
       ┣ _image.scss
       ┣ _button.scss
       ┃
       ┣ front
       ┃   ┣ _index.scss
       ┃   ┗ _heading-front.scss
       ┃
       ┗ editor
           ┣ _index.scss
           ┗ _heading-editor.scss
/**
 * styles.scss
 */

@use 'common' as *;
@use 'common/front' as *;

@use 'layout' as *;
@use 'layout/article' as *;
@use 'layout/front' as *;

@use 'component' as *;
@use 'component-front' as *;
/**
 * editor-styles.scss
 */

@use 'sass:meta';
@use 'common' as *;

.editor-styles-wrapper {

  @include meta.load-css( 'common/editor' );

  @include meta.load-css( 'layout' );
  @include meta.load-css( 'layout/article' );
  @include meta.load-css( 'layout/editor' );

  @include meta.load-css( 'component' );
  @include meta.load-css( 'component/editor' );

}

どうにか無事に書き換えができたようだ。
ディレクトリ構造はこれで scss 直下は commonlayoutcomponent だけに単純化できて、styles.scsseditor-style.scss の記述もすっきりわかりやすくできたと思う。

いやー、この情報について調べるには最初にどこをみればよかったんだろう。ちょっとそれっぽい単語を入れてググっても全然出てこなかった…。
こんなに調べても情報が出てこないってことは、まだみんな未対応なのかな??最初から英語に絞って検索してみればよかった?

今回の僕の例ではこの meta.load-css( $url, $with: null ) だけを使って解決できたが、もっと複雑な構造になっていたり、変数や関数をたくさん使っているような場合だと移行するのはなかなか大変だろうと簡単に想像がつく。しかも今回パラメータ $with 一度も使ってないやん…。
もうまもなく @import が使えなくなってしまうタイミングにしては、探して出てくる情報が少ないことも手が出しにくい要因のひとつであることは間違いなさそうだ。まずは公式ドキュメントの sass:meta のページをいろいろ見てみるのが良さそう。
致し方ないことではあるけれど、探して出てくる情報は概論の説明で終わってしまっていることが多い印象だった。移行する手間ももちろんだが、いま運用中のテーマ(あるいはプラグインでも同様だが)でこれを移行していくのは相当骨の折れる仕事になることは想像に難くない。

僕も今回ようやく重い腰を上げたのは「ちょうど手をつけ始めたテーマ」があったからで、そのほかの「いま運用中のテーマ(またはプラグイン)」については相当まとまった時間を作らない限り移行はできそうにもない…。

まとめ

この記事では Sass の @import を @use に置き換えていく工程の中で実際にハマったところを解決方法まで説明したが、これはあくまでも「僕の環境で」の話ではあるし、こんな単純な構造の Sass ばかりではないだろうからこれだけでは解決しないものも多いだろうと想像される。
おまけにこれらはまだ @use への移行のほんの一部分でしかないので、今後開発を進めていったらこれだけでは済まない可能性は充分あるだろう。

また、今回は特に WordPress の環境において「フロント側とエディター側で2種類の CSS を読み込ませたい」という大前提があっての話なので、WordPress のテーマやプラグインの開発環境のアップデートの場合くらいしか参考にならないかもしれないことをお断りしておく。
おまけに「結果的には」だけど @forword も全然使わなかった…。

とはいえ、ひとまずは無事に Sass の @import を使わない環境にアップデートすることができた。
これで無事に「おじいちゃん平成(昭和)くさーいwww」とキッズから馬鹿にされずに済むだろうか…?

どこか間違ってるところがあったらこっそり教えてください(笑

【2022/09/07 追記】
Facebook で鹿野さんからコメントいただいた。

え……延期…なの…?

鹿野さんからのコメントとリンク

どうせいずれは移行しなきゃいけないんだから、いまのうちに移行できたんだからいいよね!!

えぇ?べ、別に泣いてなんかいないっすよ…