Post on:2020年10月28日
sponsorsr
CSSの変数(カスタムプロパティ)の基礎知識、便利な使い方を詳しく解説します。Webページやスマホアプリのレイアウト、コンポーネントなど、実際の使用例がたくさんあるので、実用的なテクニックが満載です。
CSS Variables 101
by Ahmad Shadeed
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
CSSの変数(別名: カスタムプロパティ)がブラウザでサポートされるようになって4年が経ちました。プロジェクトや状況に応じて、使用されていると思います。CSSの変数は非常に便利で使いやすいのですが、フロントエンドのデベロッパーが間違った使い方をしたり、誤解していることがあります。
この記事では、CSSの変数についてすべての情報を得ることができるドキュメントがあればと思い、文章化しました。数多くの使用例を用いて、CSSの変数について知っておくべきことを学ぶことができます。
準備はいいですか?
それでは、始めましょう!
CSSの変数とは、CSSの値の再利用性と冗長性の削減を目的として、CSSドキュメント内で定義される値のことです。
CSSの変数の基本的な使用例を見てましょう。
セクションの実装
セクションのボーダー、タイトル、疑似要素はすべて同じカラーです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | .section{ border:2pxsolid#235ad1; } .section-title{ color:#235ad1; } .section-title::before{ content:""; display:inline-block; width:20px; height:20px; background-color:#235ad1; } |
このスタイルシートでは、#235ad1という同じ値が3回使用されています。
大規模なプロジェクトで複数のCSSファイルを使用し、カラーを変更するように指示されたと想像してみてください。
簡単に思い浮かぶのは、古き良き時代の「検索と置換」です。
CSSの変数を使用すると、これはさらに簡単になります。
まず、変数名にダブルハイフン(--)をつけ、とりあえず:rootまたは<html>要素で変数を定義します。変数を値に使用する時には、var()に変数名を定義します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | :root{ --color-primary:#235ad1; } .section{ border:2pxsolidvar(--color-primary); } .section-title{ color:var(--color-primary); } .section-title::before{ /* Other styles */ background-color:var(--color-primary); } |
前述のスタイルシートより、すっきりしましたね。
変数--color-primaryは:root要素で定義したので、グローバル変数です。すべての値でこのCSSの変数を使用できます。また、変数のスコープを設定し、特定の要素にだけ変数を適用することもできます。
プログラミング言語での変数の命名と同様に、CSSの変数の命名もそれほど違いはありません。CSSの変数名には、前にダブルハイフン(--)をつけ、英数字・アンダースコア・ハイフンを使用できます。また、大文字と小文字は区別されることに注意してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* 有効な変数名 */ :root{ --primary-color:#222; --_primary-color:#222; --12-primary-color:#222; --primay-color-12:#222; } /* 無効な変数名 */ :root{ --primarycolor:#222; /* 変数名にスペースは使用できません */ --primary$%#%$# } |
CSSの変数で便利な点は、スコープを設定できることです。このコンセプトは他のプログラミング言語と似ています。
例えば、JavaScriptを例に見てましょう。
1 2 3 4 5 6 | letelement="cool"; functioncool(){ letotherElement="Not cool"; console.log(element); } |
変数elementのスコープはグローバルなので、cool()関数内でアクセスできます。しかし、変数otherElementはcool()関数内でしかアクセスできません。
このコンセプトをCSSの変数に当てはめてみましょう。
1 2 3 4 5 6 7 8 | :root{ --primary-color:#235ad1; } .section-title{ --primary-color:d12374; color:var(--primary-color); } |
変数--primary-colorは:root要素で定義されているのでグローバルで、ドキュメント内のすべての要素でアクセスできます。さらに.section-titleの宣言ブロックのスコープ内で再定義することで、その新しい値は.section-title内でのみ機能します。
スコープのコンセプトを図にしてみました。
CSSの変数のスコープのコンセプト
セクションのタイトルのカラーに使用される--primary-colorが定義されています。Featured authorsとLatest articlesのカラーを変更したいので、--primary-colorの値を上書きしています。変数--unitにも同じことが当てはまります。
上記の図のCSSは、下記の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | /* グローバル変数 */ :root{ --primary-color:#235ad1; --unit:1rem; } /* セクションのタイトルのデフォルトのカラーとスペース */ .section-title{ color:var(--primary-color); margin-bottom:var(--unit); } /* タイトルのカラーの上書き */ .featured-authors.section-title{ --primary-color:#d16823; } /* カラーとスペースの上書き */ .latest-articles.section-title{ --primary-color:#d12374; --unit:2rem; } |
サポートされていないブラウザにフォールバックを提供するという意味ではありません。ここでのフォールバックは、CSSの変数で使用できる機能です。
下記の例をご覧ください。
1 2 3 | .section-title{ color:var(--primary-color,#222); } |
var()内に2つの値が定義されていることに注目してください。2番目の#222は何らかの理由で変数--primary-colorが定義されていない場合のみ機能します。
さらに、var()はネストすることもできます。
1 2 3 | .section-title{ color:var(--primary-color,var(--black,#222)); } |
この機能は、変数の値が特定のアクションに依存している場合に便利です。変数が値を持っていない場合、変数のフォールバックを用意しておくことが重要です。
デザインシステムでは、ボタンに複数のサイズがあるのが一般的です。通常はボタンに3つのサイズ(small, normal, large)があります。
これらのサイズをCSSの変数で実装すると、非常に簡単です。
ボタンのサイズをCSSの変数で定義
1 2 3 4 5 6 7 8 9 10 11 12 | .button{ --unit:1rem; padding:var(--unit); } .button--small{ --unit:0.5rem; } .button--large{ --unit:1.5rem; } |
ボタンコンポーネントのスコープ内で変数--unitを変更することで、ボタンのさまざまなバリエーションを作成できます。
HSLとは、色相、彩度、明度の略です。色相の値によってカラーが決まり、彩度と明度の値によってカラーの濃さや明るさを調整します。
HSLカラーをCSSの変数で定義
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | :root{ --primary-h:221; --primary-s:71; --primary-b:48; } .button{ background-color:hsl(var(--primary-h),var(--primary-s),var(--primary-b)); transition:background0.3sease-out; } /* 背景を暗くする */ .button:hover{ --primary-b:33; } |
変数--primary-bの値を減らして、ボタンを暗くしていることに注目してください。
CSSのカラーについてもっと知りたい場合は、CSSに関する詳細な記事をご覧ください。
Photoshop、Sketch、Figma、Adobe XDなどのデザインプログラムを使用したことがある場合は、Shiftキーを押しながら要素のサイズを変更して、要素が歪まないようにすることをお勧めします。
CSSで同じ事を直接行う方法はありませんが、CSSの変数を使用した簡単な回避策があります。
CSSの変数で比例を維持したままサイズ変更
アイコンがあり、その幅と高さが同じにしなければならないとします。変数--sizeを定義して、幅と高さの両方に使用します。
1 2 3 4 5 | .icon{ --size:22px; width:var(--size); height:var(--size); } |
これで完成です!
変数--sizeの値を変更するだけで、Shiftキーのリサイズを真似ることができます。この仕組みについては、こちらの記事をご覧ください。
CSSの変数は、CSS Gridにも非常に便利です。グリッドコンテナに、定義された優先幅に基づいて子アイテムを表示させたい場合を想像してみてください。各バリエーションごとにクラスを作成してCSSを複製するよりも、変数を使用した方がはるかに簡単です。
CSS GridをCSSの変数で定義
1 2 3 4 5 6 7 8 9 10 | .wrapper{ --item-width:300px; display:grid; grid-template-columns:repeat(auto-fill,minmax(var(--item-width),1fr)); grid-gap:1rem; } .wrapper-2{ --item-width:500px; } |
たったこれだけのCSSで、柔軟性があり、メンテナンスが容易で、他のプロジェクトでも使用できるフルグリッドシステムを作成できます。
同じコンセプトをgrid-gapプロパティにも適用できます。
1 2 3 4 5 6 7 8 9 10 | .wrapper{ --item-width:300px; --gap:0; display:grid; grid-template-columns:repeat(auto-fill,minmax(var(--item-width),1fr)); } .wrapper.gap-1{ --gap:16px; } |
CSS GridのgapをCSSの変数で定義
システム全体で使用されているグラデーションや背景がある場合、それを完全な値としてCSSの変数に定義することをお勧めします。
1 2 3 4 5 6 7 | :root{ --primary-gradient:linear-gradient(150deg,#235ad1, #23d1a8); } .element{ background-image:var(--primary-gradient); } |
あるいは、単一の値を格納することもできます。例えば、グラデーションの角度を例に挙げてみましょう。
1 2 3 4 5 6 7 8 | .element{ --angle:150deg; background-image:linear-gradient(var(--angle),#235ad1, #23d1a8); } .element.inverted{ --angle:-150deg; } |
グラデーションの角度をCSSの変数で定義
CSSの変数内に複数の値を含めることもできます。
これは特定のコンテキストに基づいて異なるポジションに配置する必要がある要素がある場合に便利です。
背景のポジションをCSSの変数で定義
1 2 3 4 5 6 7 | .table{ --size:50px; --pos:leftcenter; background:#ccc linear-gradient(#000, #000) no-repeat; background-size:var(--size)var(--size); background-position:var(--pos); } |
ダークモードとライトモードは、今まで以上にWebサイトに要求されるようになりました。CSSの変数を使用することで、それら2つのバージョンを保存し、ユーザーやシステムの設定に応じて切り替えることができます。
ダークモードとライトモードの切り替えをCSSの変数で定義
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | :root{ --text-color:#434343; --border-color:#d2d2d2; --main-bg-color:#fff; --action-bg-color:#f9f7f7; } /* <html>要素に追加されたクラス */ .dark-mode{ --text-color:#e9e9e9; --border-color:#434343; --main-bg-color:#434343; --action-bg-color:#363636; } |
ダークモードとライトモードの切り替え
場合によっては、JavaScriptでCSSの変数を設定する必要があります。例えば、拡張可能なコンポーネントのheightを取得する必要があるとしましょう。
このテクニックはMichael Scharnaglの記事から学びました。
変数--details-height-openは空で、特定のHTML要素に追加されます。これにはピクセル値が含まれます。JavaScriptが何らかの理由で失敗した場合、適切なデフォルト値またはフォールバック値を提供することが重要です。
1 2 3 | .section.is-active{ max-height:var(--details-height-open,auto); } |
値autoは、JavaScriptが失敗し、CSSの変数--details-height-openが定義されていない場合のフォールバック値です。
Webページのラッパーには複数のバリエーションがあります。あるページでは小さなラッパーが、別のページでは大きなラッパーが必要になるかもしれません。そのような場合、CSSの変数で実装すると簡単です。
ラッパーの幅をCSSの変数で定義
1 2 3 4 5 6 7 8 | .wrapper{ --size:1140px; max-width:var(--size); } .wrapper--small{ --size:800px; } |
CSSの変数をインラインスタイルで使用すると、気づいていない多くの新しい可能性が開かれます。以前、記事を書いたので、その中から興味深い使用例をいくつか紹介します。
これは本番のWebサイトにとって完璧なアプローチではないかもしれませんが、さまざまなアイデアのプロトタイプやテストには役立ちます。
style属性に変数--item-widthを加えます。このアプローチは、例えば、グリッドのプロトタイプに役立ちます。
1 2 3 4 5 | <divclass="wrapper"style="--item-width: 250px;"> <div></div> <div></div> <div></div> </div> |
1 2 3 4 5 | .wrapper{ display:grid; grid-template-columns:repeat(auto-fill,minmax(var(--item-width),1fr)); grid-gap:1rem; } |
ユーザーのアバター
もう1つ便利な使用例は、要素のサイズ調整です。アバター画像を4つの異なるサイズにして、そのサイズを1つの変数で制御できます。
1 2 3 4 | <imgsrc="user.jpg"alt=""class="c-avatar"style="--size: 1"/> <imgsrc="user.jpg"alt=""class="c-avatar"style="--size: 2"/> <imgsrc="user.jpg"alt=""class="c-avatar"style="--size: 3"/> <imgsrc="user.jpg"alt=""class="c-avatar"style="--size: 4"/> |
1 2 3 4 5 | .c-avatar{ display:inline-block; width:calc(var(--size,1)*30px); height:calc(var(--size,1)*30px); } |
上記のCSSを分析してみましょう。
CSSの変数とメディアクエリを組み合わせることで、Webサイト全体で使用される変数を微調整するのに役立ちます。私が考える最も簡単な例は、変数でスペースの値を変更することです。
1 2 3 4 5 6 7 8 9 | :root{ --gutter:8px; } @media(min-width:800px){ :root{ --gutter:16px; } } |
変数--gutterを使用する要素は、ビューポートのサイズに応じてスペースを変更します。
これってすごいことだと思いませんか?
CSSの変数は、継承されます。親要素に変数が定義されている場合、子孫要素は同じ変数を継承します。下記の例をご覧ください。
1 2 3 | <divclass="parent"> <pclass="child"></p> </div> |
1 2 3 4 5 6 7 | .parent{ --size:20px; } .child{ font-size:var(--size); } |
.child要素は、親から継承した結果として、変数--sizeにアクセスできます。どうすればこの恩恵を受けることができるのか疑問に思うかもしれません。では、その実際の例を見てみましょう。
親から変数を継承する
次の要件を持つアイテムのグループがあります。
1 2 3 4 5 | <divclass="actions"> <divclass="actions__item"></div> <divclass="actions__item"></div> <divclass="actions__item"></div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | .actions{ --size:50px; display:flex; gap:calc(var(--size)/5); } .actions--m{ --size:70px; } .actions__item{ width:var(--size); height:var(--size); } |
Flexboxのgapプロパティに変数--sizeをどのように使用したかに注目してください。つまり、スペースは動的にすることができ、変数--sizeに依存します。
もう1つ便利な使用例を紹介します。
CSSアニメーションをカスタマイズするためにCSSの変数の継承を使用することです。以下は、CSS Tricksの記事から学びました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | @keyframesbreath{ from{ transform:scale(var(--scaleStart)); } to{ transform:scale(var(--scaleEnd)); } } .walk{ --scaleStart:0.3; --scaleEnd:1.7; animation:breath2salternate; } .run{ --scaleStart:0.8; --scaleEnd:1.2; animation:breath0.5salternate; } |
このようにすれば、@keyframesを2回定義する必要はなく、.walkと.run要素用にカスタマイズされたCSSの変数を継承することができます。
var()関数内の変数が無効な場合、ブラウザは使用されているプロパティに基づいて初期値または継承された値に置き換えます。
1 2 3 4 5 6 7 | :root{ --main-color:16px; } .section-title{ color:var(--main-color); } |
上記ではcolorプロパティに、16pxを使用しています。これは無効な値なので、ブラウザはcolorプロパティの値を下記のようにします。
下記は、ブラウザの動作を説明するフローチャートです。
var()関数内の変数が無効な場合
上記は、技術的には計算値時に無効(invalid at computed-value time)と呼ばれます。これは、var()関数が変数を初期値で参照するか、CSSプロパティに無効な値を持つ有効な変数を使用している場合に発生します。
Lea Verouによる記事で見た例を紹介します。
1 2 3 4 | .section-title{ top:10px; top:clamp(5px,var(--offset),20px); } |
ブラウザがclamp()関数をサポートしていない場合、top: 10px;はフォールバックとして機能するでしょうか?
簡単な答えは、ノーです。その理由は、ブラウザが無効なプロパティ値を検出した時点で、他のカスケード値はすでに破棄されているからです。つまり、top: 10px;は無視されます。
CSSの仕様によると、
計算値時に無効のコンセプトは、他の構文エラーのように変数が「早期に失敗」できないために存在します。そのため、ユーザーエージェントがプロパティ値が無効であることに気づくまでに、他のカスケードされた値はすでに破棄されます。
そのため、広くサポートされていないCSSの機能を使用する必要があり、その中にCSSの変数がある場合は、CSSの@supportsを使用する必要があります。
下記はさきほどの記事でそのテクニックを使用した方法です。
1 2 3 4 5 | @supports(top:max(1em,1px)){ #toc { top:max(0em,11rem-var(--scrolltop)*1px); } } |
Webページ内のすべてのリソースを制御できない場合があり、それらの一部はオンラインでホストする必要があるとします。その場合、リンクのURL値をCSSの変数に格納できます。
1 2 3 4 5 6 7 | :root{ --main-bg:url("https://example.com/cool-image.jpg"); } .section{ background:var(--main-bg); } |
しかし、CSSの変数をurl()で補完することは可能なのかと疑問に思うかもしれません。次のように考えてみてください。
1 2 3 4 5 6 7 | :root{ --main-bg:"https://example.com/cool-image.jpg"; } .section{ background:url(var(--main-bg)); } |
var(--main-bg)はURL自体として扱われ、無効なので無理です。ブラウザが値を計算するまでに値は無効になり、期待通りには動作しません。
便利なのは、変数の値に関係なく複数の値を格納できることです。それらの値が有効であれば、動作するはずです。
変数に複数の値を保存
1 2 3 4 5 6 7 | :root{ --main-color:35,90,209; } .section-title{ color:rgba(var(--main-color),0.75); } |
rgba()関数に、RGB値をカンマ区切りでCSSの変数に格納しています。これにより、要素に応じてアルファ値を微調整したい場合に柔軟性を持たせることができます。
この場合の唯一の欠点は、デベロッパーツールのカラーピッカーでrgba値を調整できないことだけです。そのことが重要である場合は、rgbaの使用を再検討する必要があるかもしれません。
別の例として、backgroundプロパティで使用します。
1 2 3 4 5 6 7 8 9 10 11 | :root{ --bg:linear-gradient(#000, #000) center/50px; } .section{ background:var(--bg); } .section--unique{ background:var(--bg)no-repeat; } |
2つのセクションがあり、そのうちの1つは背景がx軸とy軸で繰り返されないようにする必要があります。
CSSの変数の仕様書を読んだことがあるなら、animation-taintedという用語を見たことがあるでしょう。@keyframesルール内でCSSの変数を使用する場合、アニメーション化できないという考え方です。
1 | <divclass="box"></div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | .box{ width:50px; height:50px; background:#222; --offset:0; transform:translateX(var(--offset)); animation:moveBox1sinfinitealternate; } @keyframesmoveBox{ 0%{ --offset:0; } 50%{ --offset:50px; } 100%{ --offset:100px; } } |
アニメーションがスムーズに機能しません。値(0, 50px, 100px)に対してのみボックスをアニメーションさせます。
CSSの仕様によると、
@keyframesルールで使用されているカスタムプロパティはアニメーションに汚染されてしまい、アニメーションプロパティのvar()関数で参照された場合の処理方法に影響します。
上記のアニメーションを機能させたい場合は、昔ながらの方法で行う必要があります。つまり、変数をアニメーション化する実際のプロパティに置き換える必要があります。
1 2 3 4 5 6 7 8 9 10 11 | @keyframesmoveBox{ 0%{ transform:translateX(0); } 50%{ transform:translateX(50px); } 100%{ transform:translateX(100px); } } |
See the Pen
CSS Variables - Keyframes - 1 by Ahmad Shadeed (@shadeed)
onCodePen.
Dannie Vintherの指摘によると、キーフレーム内のCSSの変数を@propertyで登録することで、キーフレーム内の変数をアニメーションさせることが可能だそうです。これは現在、Chromiumでサポートされています。
1 2 3 4 5 | @property--offset{ syntax:"<length-percentage>"; inherits:true; initial-value:0px; } |
See the Pen
CSS Variables - Keyframes - 1 by Dannie Vinther (@dannievinther)
onCodePen.
CSSの変数で計算ができることを知っていましたか?
前述した下記の例で見てみましょう。
1 2 3 4 5 | .c-avatar{ display:inline-block; width:calc(var(--size,1)*30px); height:calc(var(--size,1)*30px); } |
アバターにはバリエーションがあるかもしれません。デフォルト値を1に設定したので、デフォルトのサイズは30px * 30pxです。さまざまなクラスのバリエーションと--size値を変更すると、アバターのサイズが変わることに注目してください。
1 2 3 4 5 6 7 8 9 10 11 | .c-avatar--small{ --size:2; } .c-avatar--medium{ --size:3; } .c-avatar--large{ --size:4; } |
CSSの変数の操作を簡単にするためにデベロッパーツールで使用できる便利なテクニックがあります!
CSSの変数を使用しているときに、カラーや背景の値を視覚的に表示できると便利です。ChromeとEdgeでは視覚的に表示されます。
カラーの値を視覚的に表示
CSSの変数の計算された値を確認するには、ホバーまたはクリックします。
計算値を表示
Safariを除いて、すべての計算値はホバーで表示できます。Safariは「=」をクリックします。
大規模なプロジェクトで、変数名を全部覚えるのは大変です。
でも大丈夫、--を入力するだけで、変数のリストが表示されます。Chrome、Firefox、Edgeで動作します。
変数名のオートコンプリート
CSSの変数を使用しているすべての要素から無効にする必要がある場合は、その変数が定義されている要素のチェックを外すことで無効になります。
変数の無効化
CSSの変数についてばかりでしたね。やっと変数のページができて嬉しく思います。もしこれがお役に立てば幸いです。ここまで読んでくれて、ありがとうございます。
CSSのデバッグに関する電子書籍を書いていることをお知らせします。
興味がある場合は、debuggingcss.comにアクセスして、本に関する最新情報を購読してください。
コメントや提案があれば、@shadeed9までお願いします。
sponsors