CSS 変数 - メリット

Chrome 49 では、CSS 変数(より正確には CSS カスタム プロパティ)が導入されます。これらは CSS の繰り返しを減らすためだけでなく、テーマの切り替えや、将来の CSS 機能の拡張やポリフィルなどの強力なランタイム エフェクトにも役立ちます。

CSS が雑然としている

アプリケーションを設計する際は、アプリの外観の一貫性を維持するために再利用されるブランドカラーのセットを確保しておくのが一般的です。残念ながら、CSS でこれらの色の値を何度も繰り返すのは面倒なだけでなく、エラーが発生しやすくなります。ある時点でいずれかの色を変更する必要がある場合、風に注意を払い、すべてのものを「見つけて交換」することができますが、大規模なプロジェクトでは、これは簡単に危険です。

近年、多くのデベロッパーが SASS や LESS などの CSS プリプロセッサに目を向け、プリプロセッサ変数を使用することでこの問題を解決しています。これらのツールはデベロッパーの生産性を大幅に向上させましたが、使用される変数には大きな欠点があります。それは、静的であり、ランタイム時に変更できないということです。実行時に変数を変更する機能を追加すると、アプリケーションの動的テーマ設定などが可能になるだけでなく、レスポンシブ デザインに大きな影響を及ぼし、将来の CSS 機能にポリフィルできるようになります。Chrome 49 のリリースでは、こうした機能を CSS カスタム プロパティの形式で利用できるようになりました。

カスタム プロパティの概要

カスタム プロパティに、CSS ツールボックスに次の 2 つの新機能が追加されました。

  • 作成者は、作成者が選択した名前のプロパティに任意の値を割り当てることができる。
  • var() 関数。作成者はこれらの値を他のプロパティで使用できます。

簡単な例で説明しましょう

:root {
    --main-color: #06c;
}

#foo h1 {
    color: var(--main-color);
}

--main-color は、作成者が定義したカスタム プロパティであり、値は #06c です。すべてのカスタム プロパティは 2 つのダッシュで始まります。

var() 関数は、自身を取得してカスタム プロパティ値に置き換えます。その結果、color: #06c; になります。ただし、スタイルシートのどこかでそのカスタム プロパティを定義していれば、var 関数で使用できます。

最初は構文が少し奇妙に思われるかもしれません。多くのデベロッパーから、「変数名に $foo だけを使用すべきではないのはなぜですか?」という質問が寄せられます。特に、可能な限り柔軟性を高め、将来的に $foo マクロを使用できるようにするために選択しました。背景については、仕様作成者の Tab Atkins によるこちらの投稿をご覧ください。

カスタム プロパティの構文

カスタム プロパティの構文は単純です。

--header-color: #06c;

カスタム プロパティでは大文字と小文字が区別されるため、--header-color--Header-Color は異なるカスタム プロパティです。表面上は単純に見えるかもしれませんが、カスタム プロパティに使用できる構文は、実際にはかなり制限が緩やかです。たとえば、有効なカスタム プロパティは次のとおりです。

--foo: if(x > 5) this.width = 10;

通常のプロパティでは無効なため、変数としては有用ではありませんが、実行時に JavaScript で読み取られ、操作される可能性があります。つまり、カスタム プロパティを使用すると、現在の CSS プリプロセッサでは実現できないあらゆる興味深い手法を活用できる可能性があります。「あくびは SASS をしているから、どうやって気にしているのだろう...」と思った方は、もう一度ご覧いただき、これらの変数は、使い慣れた変数ではありません。

カスケード

カスタム プロパティは標準のカスケード ルールに従うため、同じプロパティを異なる詳細度で定義できます。

:root { --color: blue; }
div { --color: green; }
#alert { --color: red; }
* { color: var(--color); }
<p>I inherited blue from the root element!</p>
<div>I got green set directly on me!</div>
<div id="alert">
    While I got red set directly on me!
    <p>I’m red too, because of inheritance!</p>
</div>

つまり、メディアクエリ内のカスタム プロパティをレスポンシブ デザインに役立てることができます。ユースケースの 1 つは、画面サイズが大きくなるにつれて、主要なセクション要素の周囲の余白を拡大することです。

:root {
    --gutter: 4px;
}

section {
    margin: var(--gutter);
}

@media (min-width: 600px) {
    :root {
    --gutter: 16px;
    }
}

重要な点として、上記のコード スニペットは、メディアクエリ内の変数を定義できない現在の CSS プリプロセッサでは不可能です。アビリティ アビリティがあれば、大きな可能性が開けます。

また、他のカスタム プロパティから値を取得するカスタム プロパティを使用することもできます。これはテーマ設定に非常に役立ちます。

:root {
    --primary-color: red;
    --logo-text: var(--primary-color);
}

var() 関数

カスタム プロパティの値を取得して使用するには、var() 関数を使用する必要があります。var() 関数の構文は次のようになります。

var(<custom-property-name> [, <declaration-value> ]? )

ここで、<custom-property-name> は作成者が定義したカスタム プロパティの名前(--foo など)、<declaration-value> は、参照先のカスタム プロパティが無効な場合に使用されるフォールバック値です。代替値はカンマ区切りのリストで 1 つの値に結合されますたとえば、var(--font-stack, "Roboto", "Helvetica");"Roboto", "Helvetica" のフォールバックを定義します。マージンやパディングに使用されるような省略形の値はカンマで区切られていないため、パディングの適切な代替例は次のようになります。

p {
    padding: var(--pad, 10px 15px 20px);
}

コンポーネント作成者は、これらのフォールバック値を使用して、要素の防御スタイルを作成できます。

/* In the component’s style: */
.component .header {
    color: var(--header-color, blue);
}
.component .text {
    color: var(--text-color, black);
}

/* In the larger application’s style: */
.component {
    --text-color: #080;
    /* header-color isn’t set,
        and so remains blue,
        the fallback value */
}

この手法は、カスタム プロパティが Shadow 境界を越える可能性があるため、Shadow DOM を使用するウェブ コンポーネントのテーマ設定に特に役立ちます。ウェブ コンポーネントの作成者は、フォールバック値を使用して初期デザインを作成し、カスタム プロパティの形式でテーマ設定「フック」を公開できます。

<!-- In the web component's definition: -->
<x-foo>
    #shadow
    <style>
        p {
        background-color: var(--text-background, blue);
        }
    </style>
    <p>
        This text has a yellow background because the document styled me! Otherwise it
        would be blue.
    </p>
</x-foo>
/* In the larger application's style: */
x-foo {
    --text-background: yellow;
}

var() を使用する際に注意すべき問題がいくつかあります。変数をプロパティ名にすることはできません。次の例をご覧ください。

.foo {
    --side: margin-top;
    var(--side): 20px;
}

ただし、これは margin-top: 20px; の設定とは異なります。2 番目の宣言は無効であり、エラーとしてスローされます。

同様に、その一部が変数で指定される値を(単純に)構築することはできません。

.foo {
    --gap: 20;
    margin-top: var(--gap)px;
}

これは、margin-top: 20px; の設定とは異なります。値を作成するには、calc() 関数という別の関数が必要です。

calc() を使用して値を作成する

calc() 関数は、CSS 値の計算を行うための小さなツールです。最新のすべてのブラウザでサポートされており、カスタム プロパティと組み合わせて新しい値を作成できます。次に例を示します。

.foo {
    --gap: 20;
    margin-top: calc(var(--gap) * 1px); /* niiiiice */
}

JavaScript でのカスタム プロパティの操作

実行時にカスタム プロパティの値を取得するには、計算済みの CSSStyleDeclaration オブジェクトの getPropertyValue() メソッドを使用します。

/* CSS */
:root {
    --primary-color: red;
}

p {
    color: var(--primary-color);
}
<!-- HTML -->
<p>I’m a red paragraph!</p>
/* JS */
var styles = getComputedStyle(document.documentElement);
var value = String(styles.getPropertyValue('--primary-color')).trim();
// value = 'red'

同様に、実行時にカスタム プロパティの値を設定するには、CSSStyleDeclaration オブジェクトの setProperty() メソッドを使用します。

/* CSS */
:root {
    --primary-color: red;
}

p {
    color: var(--primary-color);
}
<!-- HTML -->
<p>Now I’m a green paragraph!</p>
/* JS */
document.documentElement.style.setProperty('--primary-color', 'green');

setProperty() の呼び出しで var() 関数を使用して、実行時に別のカスタム プロパティを参照するようにカスタム プロパティの値を設定することもできます。

/* CSS */
:root {
    --primary-color: red;
    --secondary-color: blue;
}
<!-- HTML -->
<p>Sweet! I’m a blue paragraph!</p>
/* JS */
document.documentElement.style.setProperty('--primary-color', 'var(--secondary-color)');

カスタム プロパティはスタイルシート内の他のカスタム プロパティを参照できるため、ランタイムにさまざまな効果をもたらすことが想像できます。

ブラウザ サポート

現在、カスタム プロパティをサポートしているのは、Chrome 49、Firefox 42、Safari 9.1、iOS Safari 9.3 です。

デモ

サンプルで、カスタム プロパティのおかげで活用できる興味深い手法の一部をご紹介していますので、ぜひお試しください。

関連情報

カスタム プロパティについて詳しくは、Google アナリティクス チームの Philip Walton がカスタム プロパティに期待している理由について解説しています。他のブラウザでの進捗状況については chromestatus.com をご覧ください。