CSS を書いていると、同じ値を何度も書いていることに気づきます。とりわけ色の値では顕著ではないでしょうか。プログラマーであれば同じ値を何度も使うなら定数として変数に値を格納して利用するのが当たり前です。
そのようなニーズに応え、W3C では「CSS Custom Properties for Cascading Variables Module Level 1」という仕様の中で CSS 変数 (CSS カスタムプロパティ) を新たに作り上げました。現在では Internet Explorer を除くすべてのブラウザーが CSS 変数をサポートしています。
今回は CSS 変数の仕様と使い方を詳しく解説します。
CSS 変数の必要性
まずは、なぜ CSS 変数が必要になったのかについて、簡単に触れておきましょう。次の HTML と CSS とレンダリング結果をご覧ください。
<h1>webfrontend.ninja</h1>
<style>
h1 {
color: #06c;
border-bottom: 1px solid #06c;
border-left: 1px solid #06c;
padding-left: 0.5em;
}
</style>
CSS には #06c
が 3 箇所に記述されています。この色がウェブサイトのテーマカラーで、他に様々な箇所で同じ色が使われていることを想像してください。
もし、このウェブサイトのテーマカラーを微調整したいとしましょう。すると、CSS の #06c
の部分をすべて書き換えなければいけません。ウェブサイト制作時には頻繁に発生するシチュエーションです。テキストエディタの置換で一括で変換することも可能でしょうが、思いもよらない箇所まで変換されてしまう可能性を考えると慎重にもなります。また、CSS ファイルが分割されていれば、変換漏れも出てくるでしょう。
こういった状況を解決するのが CSS 変数です。
変数の宣言と呼び出し
では、前述の h1
要素のスタイリングに CSS 変数を使うとどうなるのかをご覧ください。
<style>
:root {
--main-color: #06c;
}
h1 {
color: var(--main-color);
border-bottom: 1px solid var(--main-color);
border-left: 1px solid var(--main-color);
padding-left: 0.5em;
}
</style>
まず、:root
疑似クラスを使い、ドキュメントツリーのルート、つまり html
要素に対して --main-color
とう名前の変数名を定義します。この名前は自由に決められますが、ハイフン 2 つで始まらないといけません。また、大文字と小文字は区別されます。この独自の変数 --main-color
に #06c
をセットしています。CSS 変数の定義はこれだけです。
h1 セレクタでは、もともと #06c
と記述された箇所が var(--main-color)
に置き換えられています。ここが #06c
に置き換えられてスタイリングが処理されます。
もしこのテーマカラーを一括で変更したい場合は、:root
疑似クラス内の --main-color
の値を一か所書き換えるだけで済みます。
変数のスコープ
前述の例では :root
疑似クラスで CSS 変数を定義しました。これは、プログラム言語の変数に例えれば、グローバル変数みたいなものです。そのページのどのセレクタでも、その変数は利用可能になります。
<h1>webfrontend.ninja</h1>
<h2>はじめに</h2>
<style>
:root {
--main-color: #06c;
}
h1 {
color: var(--main-color);
border-bottom: 1px solid var(--main-color);
border-left: 1px solid var(--main-color);
padding-left: 0.5em;
}
h2 {
color: var(--main-color);
}
</style>
上記の例では h2
セレクター内でも var(--main-color)
が有効になっていることが分かります。
では、次の CSS をご覧ください。
<style>
h1 {
--main-color: #06c;
color: var(--main-color);
border-bottom: 1px solid var(--main-color);
border-left: 1px solid var(--main-color);
padding-left: 0.5em;
}
h2 {
color: var(--main-color);
}
</style>
この CSS では :root
疑似クラスを削除し、CSS 変数 --main-color
の定義を h1
セレクタ内に移動しました。このレンダリング結果は次の通りです。
h1
セレクタ内で CSS 変数を定義すれば、その変数はその h1
セレクタ内であれば利用できます。しかし、h2 セレクタの var(--main-color)
は機能していません。このように、CSS 変数は定義した場所に応じて、そのスコープ(適用範囲)が違ってきますので、注意してください。
セレクタごとに CSS 変数の値を変える
CSS 変数は :root
セレクタで定義しなければいけないわけではありません。CSS 変数は同じ名前だとしても、様々なセレクタ内で定義することができます。
次の例では、--main-color
という名前の CSS 変数を :root
、h1
、h2
セレクタ内で定義していますが、それぞれで定義した値が異なります。最後にワイルドカードのセレクタで color: var(--main-color);
を指定しています。
このレンダリング結果は、h1
要素は緑色、h2
要素は茶色、それ以外は灰色になります。
<h1>webfrontend.ninja</h1>
<h2>はじめに</h2>
<p>Webエンジニア向け HTML, CSS, JavaScript 情報を発信</p>
<style>
:root {
--main-color: gray;
}
h1 {
--main-color: green;
}
h2 {
--main-color: brown;
}
* {
color: var(--main-color);
}
</style>
CSS 変数のデフォルト値
var()
を使って CSS 変数の値を利用するわけですが、もし var()
の引数に指定した変数が定義されていない場合はどうなるでしょうか。
h2 {
color: var(--sub-color);
}
--sub-color
が定義されていればその値が適用されますが、定義されていなければ color
プロパティの指定は無効になります。
しかし、var()
には、CSS 変数が定義されていない場合に適用する値を第二引数に指定することができます。
h2 {
color: var(--sub-color, red);
}
この場合は、CSS 変数 --sub-color
が定義されていなければ red
が適用されます。
CSS 変数の値を CSS 変数で定義する
これまで CSS 変数の値は固定値ばかりでしたが、別の CSS 変数を使って定義することもできます。
<h1>webfrontend.ninja</h1>
<style>
:root {
--main-color: orange;
--gradient: linear-gradient(to top, var(--main-color), white);
}
h1 {
background: var(--gradient);
}
</style>
このように CSS 変数の値を CSS 変数で定義するのは便利な一方、気づかないところで大きなミスをすることもあります。次の CSS の何が問題かわかりますか?
:root {
--one: calc(var(--two) + 20px);
--two: calc(var(--one) - 20px);
}
この CSS の問題点は calc()
の引数に CSS 変数を使っているところではありません。問題は、二つのそれぞれの CSS 変数の値に、もう一方の CSS 変数の値を相互に使っており、循環参照になっている点です。これでは、それぞれの CSS 変数の値が確定されず無効になります。
では、次の CSS はどうでしょうか?
<hgroup>
<h1>webfrontend<span>.ninja</span></h1>
<h2>...</h2>
</hgroup>
<style>
hgroup {
--foo: 10px;
}
hgroup>h1 {
--bar: calc(var(--foo) + 10px);
font-size: var(--bar);
}
hgroup>h1>span {
--foo: calc(var(--bar) + 10px);
font-size: var(--foo);
}
</style>
このサンプルは前述の循環参照のサンプルと同じように見えます。しかし、循環参照にはなっていませんので、期待通りにレンダリングされます。
まとめ
CSS 変数は、ウェブサイトのスタイリングの開発効率だけでなく保守運用性も高めてくれます。使い方も簡単ですので、積極的に使っていきたいですね。
今回は以上で終わりです。最後まで読んでくださりありがとうございました。それでは次回の記事までごきげんよう。