CSS 変数の宣言と使い方
CSS 変数、正式には カスタムプロパティ は、名前が -- で始まるプロパティです。一度だけ宣言し (ページ全体に継承させるため、ふつうは :root に書きます)、どこからでも var() で読み取ります。
--brand を #16a34a に書き換えると、カードの枠線とボタンが一緒に変わります。これが要点です。値は 1 つ、変更する場所も 1 か所です。ボタンの左右の padding のように、変数は calc() とも組み合わせられます。
書き方
:root {
--name: value; /* declare */
}
.element {
color: var(--name); /* use */
color: var(--name, #111827); /* use, with a fallback */
padding: calc(var(--space) * 2); /* compute with it */
}
| ルール | 詳細 |
|---|---|
| 名前 | -- で始まり、英字、数字、-、_ が続きます。大文字と小文字を区別します: --Brand は --brand ではありません。 |
| 値 | 色、長さ、リスト、文字列など、ほぼ何でも入ります。使われるまでチェックされません。 |
| 宣言する場所 | どのルールでも可。グローバルなら :root、ローカルな値ならコンポーネントのセレクタ。 |
| 継承 | カスタムプロパティは継承されるので、子要素は祖先の値を参照できます。 |
var() が使える場所 | プロパティの値の中だけです。セレクタ、プロパティ名、@media の条件では使えません。 |
:root は <html> 要素に一致します。html の代わりに使われるのは、詳細度がクラスと同じであることと、慣習として「グローバル設定」を表すからです。
スコープ: コンポーネントごとに変数を上書きする
変数は継承されるので、ある要素で再宣言すると、その要素と内側のすべての要素で値が変わります。変数を使うルールそのものは変わりません。
バリエーションは変数だけを設定し、枠線そのものには触れていません。4 つ目のバリエーションを追加するのも 1 行で済みます。
フォールバック値
var(--name, fallback) は、要素にもどの祖先にも変数が定義されていないときにフォールバックを使います。最初のカンマ以降はすべてフォールバックなので、カンマを含めることができ (var(--font, Georgia, serif))、別の var() を書くこともできます。
JavaScript で変数を変更する: テーマの切り替え
JavaScript は style.setProperty() で任意の要素に変数を設定できます。document.documentElement (<html> 要素) に設定すれば、ページ全体が追従します。よく使われるのは、変数を再定義するクラスを切り替える方法です:
変数の値は getComputedStyle(element).getPropertyValue('--text') で読み取れます。setProperty は <html> にインラインスタイルを書き込み、それが .dark クラスに勝つことに注意してください。2 つ目のボタンを押した後は、root.style.removeProperty('--text') でインラインの値を削除するまで、どちらのテーマでも文字色は変わりません。
HTML から値を渡す
style 属性に変数を書くのは、要素ごとのデータをスタイルシートに渡すいちばんすっきりした方法です。デザインは CSS に残り、HTML は数値を渡すだけです。
calc(var(--value) * 1%) はただの数値をパーセントに変換します。変数はメディアクエリの中でも変更できます。@media (min-width: 900px) { :root { --space: 20px; } } と書けば、--space を読むすべての余白が広い画面で大きくなります。
無効な値の落とし穴
ブラウザは、変数の値がそれを使うプロパティにとって有効かどうかを事前に知ることができません。有効でない場合、プロパティはルール内の前の宣言には戻りません。計算値の時点で無効 になり、unset を指定したのと同じ動作をします。継承されるプロパティは親の値を、それ以外は初期値を取ります。
文字は <body> から 16px を継承し、border-width は初期値の medium に戻ります。ブラウザはこれを 3px で描画します。変数を使わない場合、無効な宣言は捨てられ、その前の宣言が残ります。変数を使う場合は、その判断をするには遅すぎるのです。変数が定義されていて値が間違っているときは、フォールバックも役に立ちません。フォールバックは未定義の変数のためだけのものです。
CSS 変数と Sass 変数の比較
| CSS 変数 | Sass 変数 | |
|---|---|---|
| 書き方 | --brand: #2563eb; と var(--brand) | $brand: #2563eb; |
| 解決されるタイミング | ブラウザ内でリアルタイムに | ビルド時。出力には残らない |
| 要素ごとの変更 | 可能 (継承による) | 不可 |
| クラスやメディアクエリでの変更 | 可能 | 不可 |
| JavaScript からの変更 | 可能 | 不可 |
| セレクタやメディア条件での使用 | 不可 | 可能 |
2 つは組み合わせて使えます。多くのプロジェクトでは、ビルド時のロジックに Sass を使い、テーマで変えたいものは CSS 変数として出力しています。
よくある間違い
- 2 つのハイフンを忘れる。
var(brand)や、-brandとして宣言した--brandは動きません。 - メディアクエリで変数を使う。
@media (min-width: var(--bp))は無効です。メディア条件にはリテラル値が必要です。 - 無効な変数でも前の宣言が残ると思う。 残りません。上の落とし穴を参照してください。
- 単位の組み合わせを間違える。
--gap: 10;としてmargin: var(--gap)pxと書いても10pxにはなりません。calc(var(--gap) * 1px)を使うか、10pxを格納しましょう。 - 間違った要素で宣言する。
.cardで宣言した変数は、兄弟要素や親要素からは見えません。共有する値は:rootで宣言しましょう。
よくある質問
CSS 変数はどう宣言しますか?
ルールの中に、名前が 2 つのハイフンで始まるプロパティを書きます。グローバルな値なら通常は :root に書きます: :root { --brand: #2563eb; }。どこからでも var(--brand) で読み取れます。例: color: var(--brand);。
CSS の :root とは何ですか?
:root は文書のルート要素に一致する疑似クラスで、HTML ページでは <html> です。ここで宣言した変数はすべての要素に継承されます。詳細度はクラスと同じなので、単なる html セレクタに勝ちます。
CSS 変数に初期値 (デフォルト値) を設定するには?
var() に 2 つ目の引数を渡します: color: var(--accent, #2563eb);。要素にも祖先にも --accent が定義されていないときにフォールバックが使われます。変数が定義されていて、その値がプロパティにとって無効な場合には使われません。
JavaScript で CSS 変数を変更するには?
要素の style に対して setProperty を呼びます: document.documentElement.style.setProperty('--brand', '#16a34a')。var(--brand) を使うすべてのルールが一度に更新されます。現在の値は getComputedStyle(el).getPropertyValue('--brand') で読み取れます。
CSS 変数と Sass 変数の違いは何ですか?
Sass 変数はスタイルシートのコンパイル時に値に置き換えられるため、ブラウザ上では変更できません。CSS 変数はブラウザの中で生きています。継承され、要素ごとに値を変えられ、メディアクエリやクラスに反応し、JavaScript から変更できます。
CSS 変数の名前は大文字と小文字を区別しますか?
区別します。--Brand と --brand は別の変数です。大文字と小文字を区別しない color などの通常のプロパティ名とは異なります。