本セッションの登壇者
セッション動画
それでは始めたいと思います。
自己紹介ですが、久保知己と言います。株式会社まぼろしでCSSを書いています。今回のスライドですが、けっこう作りすぎてしまったので一部は省略していきます。そのかわり、共有する資料にはちゃんと説明が書いてあるので、詳細はそちらをご確認ください。
それでは進めていきたいと思います。”Custom properties”だと名前が長いので、以後「CSS変数」と言っていきます。
CSS変数とは
CSS変数というのは、CSSだけで変数の機能を実行できます。CSSにもうそのまま「-- (ハイフンハイフン)」で何か名前を付けて、var関数でその変数を取り出すこともできますし、JavaScriptからのアクセスもできます。
CSS変数の特性ですが、たとえば:root
というところにCSS変数を定義すると、HTML全体にCSS変数が使用できるようになります。
またCSS変数というのはHTMLの子孫に継承されていくので、特定の場所に設定するとそれ以降の子孫のHTMLでCSS変数を効かせられます。
CSS変数の使い方
次に、CSS変数の使い方ですが、よく使われる方法がトークンとして使われる方法ですね。
トークン以外にもJavaScriptと組み合わせて使うこともできます。こちらは左ナビゲーションをマウスで動かしたりというようなナビゲーションですね。こちらも従来の方法だとインラインスタイルにてCSSの宣言が必要だったんですが、CSS変数を使うことによってスマートに書くこともできます。
またTailwindとの相性も良く、こういうこともできますね。
細かい説明は資料に記載しているので、ここではちょっと省略します。
Component思考のCSS変数 - CSSの変数化にChatGPTを使う
ここからが本題の「Component思考のCSS変数」です。コンポーネントっていろいろあるのですが、わかりやすいようにボタンから進めていきたいと思います。
ボタン、いろいろあります。一番上の白い点線で囲っているところにベースのボタンを用意しました。その下にバリエーションのボタンを用意しています。まずベースのボタンから作成してみましょう。
従来どおりのSCSSだとこういう感じですね。何の変哲もないものです。is関数セレクタを使ったりとか、あとはaria-pressed=true
というのを使って、ボタンが押された状態とか、disabledとか、いつもどおりの感じでボタンを作ります。
次にバリエーションの部分です。2段目は塗りの状態のボタンで、ホバーもしますし押した状態、そしてdisabled、3段目はアウトライン、ゴーストボタン的なやつですね。今日はこれをラインと呼びます。
こういう感じでバリエーションを作成しました。これも従来どおりの作り方です。
これをCSS変数化してみます。CSS変数化するにあたって、ChatGPTを使うと非常に楽です。
先ほどのベースで作ったボタンに対して、ChatGPTでテンプレート構文が使えるので、「こういう感じでやってよ」というと良い感じにChatGPTが吐き出してくれます。ただ、ちゃんとうまくすべて吐き出してくれるわけじゃないので、ちょっとだけ手直しをしました。たとえば、ChatGPTの場合はCSS変数をすべて:root
に対して吐き出します。
僕のほうは:root
の変数をそのままコンポーネントの中に入れてしまいました。こうするとコンポーネントで使っているCSS変数が何なのか、非常にわかりやすくなります。
次にバリエーションのCSSです。先ほどいろいろ書いていたバリエーションのCSSなんですが、塗りの部分に関しては3行で終わるようになってしまいました。非常に短くなっていますね。
従来のSCSSの記述とCSS変数化した記述の両方を見てみると一目瞭然です。しかもCSS変数はCSSネイティブで、そのまま生の状態で使うことができるので、非常にありがたい機能ですね。これで何が起こっているのかと言うと、セレクタを省略しています。なので行数がかなり短くなったと言えます。
次にこの「セレクタを縮める」に関連してCSSアニメーションの例も見てみましょう。キーフレーム(@keyframes
)をこのように1つだけ用意してあります。
このキーフレームを利用して、ベースのボタンに対してアニメーションを付けたいというクラスを用意したらアニメーションが走るようにしています。
そうすると、ベースのボタンに対して設定したものが、バリエーションも含めたすべてにアニメーションが効くようになります。こちらなんですけれども、本来であれば白に対しての黒、緑色背景に対しての黒背景といった具合にキーフレームをそれぞれ用意しなければいけなかったものが、1つのキーフレームでこのようにアニメーションができるようになりました。セレクタが非常にコンパクトになります。
詳細度の分離
つづいて詳細度の分離について説明していきたいと思います。通常であればプロパティと値は同じ詳細度になるはずです。
CSS変数を使うことによってこの状態でもまったく問題ありません。期待どおりの描写が行えます。
また、プロパティと値が反対になったとしても描写してくれます。
こちらはあまり良い例ではないんですが、ボタンのコンポーネントに対して「#legacy」というIDを付与しました。この状態でボタンの通常時だけの色を変えたいとき、宣言を設定するとすべての色が変わってしまいます。こちらはIDの詳細度が強すぎるためにこうなってしまった例です。
同じように「#css-variables」というIDを設定しています。ただ、CSS変数を使うことによって、ちゃんと通常時の色だけを変えることができます。また、押した状態やdisabledの状態ではちゃんと元の色をある程度保持してくれます。最後の行の--button-disabled-border
で上書きしてる部分はあるのですが、ある程度保持してくれます。
また、架空の話なんですけども、大きな大きな企業のLP案件で、リセットCSSのa
タグに!important
が付与されていたとします。しかもLPページにしか編集権限がないので、このリセットCSSを編集することができません。そうなるとどうなるかというと「!important祭り」が発生します。
ただ、これもCSS変数を使えば解決できます。リセットCSSは変更できないので、ここで:where
を使って、.lp-demo
みたいなLPの起点となるようなクラス名を設定してあげて、a
タグに対して!important
で上書きしたいものにCSS宣言を設定してあげます。そうすると以後、このCSS変数を再定義してあげるだけで!important
を使用せずにちゃんとスタイルが反映されます。
まとめ
では、最後のまとめにいきたいと思います。まとめはもうタイトルの通りなんですけれども、セレクタは省略して値にアクセスできる方法があります。もうひとつは、プロパティと値の詳細度は分離ができます。
以上です。ありがとうございました。