CSSカスタムプロパティの戦略ガイド
公開: 2022-03-10CSSカスタムプロパティ(「CSS変数」と呼ばれることもあります)は、現在すべての最新のブラウザーでサポートされており、人々はそれらを本番環境で使用し始めています。 これは素晴らしいことですが、プリプロセッサの変数とは異なり、どのような利点があるかを考慮せずにそれらを使用している人々の例をすでにたくさん見ています。
カスタムプロパティは、CSSの記述方法と構造を変更する可能性が非常に高く、JavaScriptを使用してUIコンポーネントを操作する方法を変更する可能性があります。 構文とその動作に焦点を当てるつもりはありません(そのために、「カスタムプロパティの使用を開始するときが来ました」を読むことをお勧めします)。 代わりに、CSSカスタムプロパティを最大限に活用するための戦略を詳しく見ていきたいと思います。
それらはプリプロセッサの変数とどのように似ていますか?
カスタムプロパティはプリプロセッサの変数に少し似ていますが、いくつかの重要な違いがあります。 最初の最も明白な違いは構文です。
SCSS
では、変数を表すためにドル記号を使用します。
$smashing-red: #d33a2c;
Lessでは、 @
記号を使用します。
@smashing-red: #d33a2c;
カスタムプロパティは同様の規則に従い、 --
プレフィックスを使用します。
:root { --smashing-red: #d33a2c; } .smashing-text { color: var(--smashing-red); }
プリプロセッサのカスタムプロパティと変数の重要な違いの1つは、カスタムプロパティには、値の割り当てとその値の取得の構文が異なることです。 カスタムプロパティの値を取得するときは、 var()
関数を使用します。
次に明らかな違いは名前にあります。 それらは実際にはCSSプロパティであるため、「カスタムプロパティ」と呼ばれます。 プリプロセッサでは、外部の宣言ブロック、メディアルール、またはセレクタの一部を含め、ほとんどどこでも変数を宣言して使用できます。
$breakpoint: 800px; $smashing-red: #d33a2c; $smashing-things: ".smashing-text, .cats"; @media screen and (min-width: $breakpoint) { #{$smashing-things} { color: $smashing-red; } }
上記の例のほとんどは、カスタムプロパティを使用すると無効になります。
カスタムプロパティには、通常のCSSプロパティとして使用できる場所に関する同じルールがあります。 それらを変数よりも動的プロパティと考える方がはるかに優れています。 つまり、宣言ブロック内でのみ使用できます。つまり、カスタムプロパティはセレクターに関連付けられています。 これは、 :root
セレクター、またはその他の有効なセレクターにすることができます。
:root { --smashing-red: #d33a2c; } @media screen and (min-width: 800px) { .smashing-text, .cats { --margin-left: 1em; } }
カスタムプロパティの値は、プロパティ宣言で値を使用する場合はどこでも取得できます。 これは、それらを単一の値として、省略形のステートメントの一部として、またはcalc()
方程式の内部でさえ使用できることを意味します。
.smashing-text, .cats { color: var(--smashing-red); margin: 0 var(--margin-horizontal); padding: calc(var(--margin-horizontal) / 2) }
ただし、メディアクエリ、または:nth-child()
を含むセレクターでは使用できません。
フォールバック値の使用方法や他の変数に変数を割り当てる方法(はい)など、構文やカスタムプロパティのしくみについて知りたいことはおそらくもっとたくさんありますが、この基本的な紹介で、残りの部分を理解するのに十分なはずです。この記事の概念。 カスタムプロパティの動作の詳細については、SergHospodaretsが作成した「カスタムプロパティの使用を開始するときが来ました」を参照してください。
動的vs静的
外観上の違いはさておき、プリプロセッサの変数とカスタムプロパティの最も重要な違いは、スコープの方法です。 変数は、静的または動的スコープのいずれかとして参照できます。 プリプロセッサの変数は静的ですが、カスタムプロパティは動的です。
CSSに関して言えば、静的とは、コンパイルプロセスのさまざまな時点で変数の値を更新できることを意味しますが、これにより、その前にあるコードの値を変更することはできません。
$background: blue; .blue { background: $background; } $background: red; .red { background: $background; }
結果:
.blue { background: blue; } .red { background: red; }
これがCSSにレンダリングされると、変数はなくなります。 これは、HTML、ブラウザ、またはその他の入力について何も知らなくても、 .scss
ファイルを読み取ってその出力を決定できる可能性があることを意味します。 これは、カスタムプロパティには当てはまりません。
プリプロセッサには一種の「ブロックスコープ」があり、セレクタ、関数、またはミックスイン内で変数を一時的に変更できます。 これにより、ブロック内の変数の値が変更されますが、それでも静的です。 これは、セレクターではなく、ブロックに関連付けられています。 以下の例では、変数$background
が.example
ブロック内で変更されています。 同じセレクターを使用しても、ブロック外の初期値に戻ります。
$background: red; .example { $background: blue; background: $background; } .example { background: $background; }
これにより、次のようになります。
.example { background: blue; } .example { background: red; }
カスタムプロパティの動作は異なります。 カスタムプロパティが関係する場合、動的スコープとは、それらが継承とカスケードの対象となることを意味します。 プロパティはセレクターに関連付けられており、値が変更されると、他のCSSプロパティと同様に、一致するすべてのDOM要素に影響します。
これは、メディアクエリ内で、ホバーなどの疑似セレクターを使用して、またはJavaScriptを使用して、カスタムプロパティの値を変更できるため優れています。
a { --link-color: black; } a:hover, a:focus { --link-color: tomato; } @media screen and (min-width: 600px) { a { --link-color: blue; } } a { color: var(--link-color); }
カスタムプロパティが使用される場所を変更する必要はありません。CSSを使用してカスタムプロパティの値を変更します。 これは、同じカスタムプロパティを使用することを意味し、同じページのさまざまな場所またはコンテキストでさまざまな値を持つことができます。
グローバル対ローカル
静的または動的であることに加えて、変数はグローバルまたはローカルのいずれかにすることもできます。 JavaScriptを作成する場合は、これに精通しているはずです。 変数は、アプリケーション内のすべてに適用することも、スコープを特定の関数またはコードのブロックに制限することもできます。
CSSも同様です。 グローバルに適用されるものと、よりローカルなものがあります。 ブランドの色、垂直方向の間隔、タイポグラフィはすべて、Webサイトまたはアプリケーション全体にグローバルかつ一貫して適用したいものの例です。 地元のものもあります。 たとえば、ボタンコンポーネントには、小さなバリエーションと大きなバリエーションがあります。 これらのボタンのサイズが、ページ上のすべての入力要素またはすべての要素に適用されることは望ましくありません。
これは私たちがCSSでよく知っていることです。 ローカルコンポーネントとグローバルデザイン要素の分離に役立つデザインシステム、命名規則、JavaScriptライブラリを開発しました。 カスタムプロパティは、この古い問題に対処するための新しいオプションを提供します。
CSSカスタムプロパティは、デフォルトで、それらを適用する特定のセレクターにローカルスコープされます。 つまり、それらはローカル変数のようなものです。 ただし、カスタムプロパティも継承されるため、多くの場合、特に:root
セレクターに適用すると、グローバル変数のように動作します。 これは、それらの使用方法について慎重に検討する必要があることを意味します。
非常に多くの例で、カスタムプロパティが:root
要素に適用されていることが示されています。これはデモでは問題ありませんが、グローバルスコープが乱雑になり、継承に関する意図しない問題が発生する可能性があります。 幸いなことに、私たちはすでにこれらのレッスンを学びました。
グローバル変数は静的になる傾向があります
いくつかの小さな例外がありますが、一般的に言えば、CSSのほとんどのグローバルなものも静的です。
ブランドの色、タイポグラフィ、間隔などのグローバル変数は、コンポーネントごとにあまり変化しない傾向があります。 それらが変更された場合、これはグローバルなブランド変更または成熟した製品ではめったに発生しないその他の重要な変更になる傾向があります。 これらのものが変数であることは依然として理にかなっており、多くの場所で使用されており、変数は一貫性を保つのに役立ちます。 しかし、それらが動的であることは意味がありません。 これらの変数の値は動的に変化しません。
このため、グローバル(静的)変数にはプリプロセッサを使用することを強くお勧めします。 これにより、それらが常に静的であることが保証されるだけでなく、コード内でそれらが視覚的に示されます。 これにより、CSSが非常に読みやすくなり、保守が容易になります。
ローカル静的変数はOKです(時々)
グローバル変数が静的であるという強いスタンスを考えると、リフレクションによって、すべてのローカル変数は動的である必要があるかもしれないと思うかもしれません。 ローカル変数は動的である傾向があることは事実ですが、これはグローバル変数が静的である傾向ほど強力ではありません。
ローカル静的変数は、多くの状況で完全に問題ありません。 私は主に開発者の便宜のために、コンポーネントファイルでプリプロセッサ変数を使用しています。
複数のサイズバリエーションを持つボタンコンポーネントの典型的な例を考えてみましょう。
私のscss
は次のようになります。
$button-sml: 1em; $button-med: 1.5em; $button-lrg: 2em; .btn { // Visual styles } .btn-sml { font-size: $button-sml; } .btn-med { font-size: $button-med; } .btn-lrg { font-size: $button-lrg; }
明らかに、この例は、変数を複数回使用する場合、またはサイズ変数からマージンとパディング値を導出する場合に、より意味があります。 ただし、さまざまなサイズのプロトタイプをすばやく作成できることは、十分な理由である可能性があります。
ほとんどの静的変数はグローバルであるため、コンポーネント内でのみ使用される静的変数を区別するのが好きです。 これを行うには、これらの変数にコンポーネント名のプレフィックスを付けるか、コンポーネントの場合はc-variable-name
-name、ローカルの場合はl-variable-name
などの別のプレフィックスを使用できます。 必要なプレフィックスを使用することも、グローバル変数にプレフィックスを付けることもできます。 どちらを選択しても、特に既存のコードベースをカスタムプロパティを使用するように変換する場合は、区別することが役立ちます。
カスタムプロパティを使用する場合
コンポーネント内で静的変数を使用しても問題がない場合、カスタムプロパティをいつ使用する必要がありますか? 通常、既存のプリプロセッサ変数をカスタムプロパティに変換してもほとんど意味がありません。 結局のところ、カスタムプロパティの理由は完全に異なります。 カスタムプロパティは、DOMの条件(特に:focus
、 :hover
、メディアクエリ、JavaScriptなどの動的条件)に関連して変化するCSSプロパティがある場合に意味があります。
カスタムプロパティはロジックとコードを整理するための新しい方法を提供するため、将来的には必要性が少なくなる可能性がありますが、常に何らかの形式の静的変数を使用すると思います。 それまでは、ほとんどの場合、プリプロセッサ変数とカスタムプロパティの組み合わせで作業することになると思います。
カスタムプロパティに静的変数を割り当てることができることを知っておくと役に立ちます。 それらがグローバルであろうとローカルであろうと、多くの状況で静的変数をローカルで動的なカスタムプロパティに変換することは理にかなっています。
注: $var
がカスタムプロパティの有効な値であることをご存知ですか? 最近のバージョンのSassはこれを認識しているため、次のようにカスタムプロパティに割り当てられた変数を補間する必要があります: #{$var}
。 これは、スタイルシートの$var
だけでなく、変数の値を出力するようにSassに指示します。 これは、変数名も有効なCSSになる可能性があるカスタムプロパティなどの状況でのみ必要です。
上記のボタンの例を使用して、HTMLで適用されるクラスに関係なく、すべてのボタンでモバイルデバイスの小さなバリエーションを使用する必要があると判断した場合、これはより動的な状況になります。 このために、カスタムプロパティを使用する必要があります。
$button-sml: 1em; $button-med: 1.5em; $button-lrg: 2em; .btn { --button-size: #{$button-sml}; } @media screen and (min-width: 600px) { .btn-med { --button-size: #{$button-med}; } .btn-lrg { --button-size: #{$button-lrg}; } } .btn { font-size: var(--button-size); }
ここでは、単一のカスタムプロパティ--button-size
を作成します。 このカスタムプロパティは、最初はbtn
クラスを使用してすべてのボタン要素にスコープされます。 次に、クラスbtn-med
およびbtn-lrg
の--button-size
の値を600pxより上に変更します。 最後に、このカスタムプロパティをすべてのボタン要素に1か所で適用します。
賢くなりすぎないでください
カスタムプロパティの動的な性質により、巧妙で複雑なコンポーネントを作成できます。
プリプロセッサの導入により、私たちの多くは、ミックスインとカスタム関数を使用して巧妙な抽象化を備えたライブラリを作成しました。 限られたケースでは、このような例は今日でも役立ちますが、ほとんどの場合、プリプロセッサを使用する時間が長くなるほど、使用する機能は少なくなります。 今日、私は静的変数専用のプリプロセッサを使用しています。
カスタムプロパティは、このタイプの実験の影響を受けない(そしてそうすべきではない)ので、多くの巧妙な例を見るのを楽しみにしています。 しかし、長期的には、読み取り可能で保守可能なコードは、常に巧妙な抽象化に勝ちます(少なくとも本番環境では)。
最近、Free Code CampMediumでこのトピックに関する優れた記事を読みました。 これはBillSourourによって書かれ、「実行時に実行しないでください」と呼ばれます。 設計時に実行してください。」 彼の議論を言い換えるのではなく、私はあなたにそれを読ませます。
プリプロセッサ変数とカスタムプロパティの主な違いの1つは、カスタムプロパティが実行時に機能することです。 これは、プリプロセッサでは複雑さの点で境界線で受け入れられた可能性があるものは、カスタムプロパティでは適切ではない可能性があることを意味します。
最近私にこれを説明した1つの例はこれでした:
:root { --font-scale: 1.2; --font-size-1: calc(var(--font-scale) * var(--font-size-2)); --font-size-2: calc(var(--font-scale) * var(--font-size-3)); --font-size-3: calc(var(--font-scale) * var(--font-size-4)); --font-size-4: 1rem; }
これにより、モジュラースケールが生成されます。 モジュラースケールは、比率を使用して相互に関連する一連の数値です。 これらは、フォントサイズや間隔を設定するために、Webデザインや開発でよく使用されます。
この例では、各カスタムプロパティは、 calc()
を使用して、前のカスタムプロパティの値を取得し、これに比率を掛けることによって決定されます。 これを行うと、スケールの次の数値を取得できます。
つまり、比率は実行時に計算され、 --font-scale
プロパティの値のみを更新することで比率を変更できます。 例えば:
@media screen and (min-width: 800px) { :root { --font-scale: 1.33; } }
これは、スケールを変更したい場合にすべての値を再度計算するよりも賢く、簡潔で、はるかに高速です。 これは、本番コードでは実行しないことでもあります。
上記の例はプロトタイピングには役立ちますが、本番環境では、次のようなものを見たいと思います。
:root { --font-size-1: 1.728rem; --font-size-2: 1.44rem; --font-size-3: 1.2em; --font-size-4: 1em; } @media screen and (min-width: 800px) { :root { --font-size-1: 2.369rem; --font-size-2: 1.777rem; --font-size-3: 1.333rem; --font-size-4: 1rem; } }
ビルの記事の例と同様に、実際の値を確認すると便利です。 私たちはコードを書くよりも何度もコードを読み、フォントスケールなどのグローバルな値は本番環境ではめったに変更されません。
上記の例はまだ完全ではありません。 グローバル値は静的である必要があるという以前のルールに違反しています。 私はプリプロセッサ変数を使用し、前に示した手法を使用してそれらをローカルで動的なカスタムプロパティに変換することを強く望んでいます。
また、あるカスタムプロパティの使用から別のカスタムプロパティに移行する状況を回避することも重要です。 これは、このようなプロパティに名前を付けるときに発生する可能性があります。
変数ではなく値を変更する
変数ではなく値を変更することは、カスタムプロパティを効果的に使用するための最も重要な戦略の1つです。
原則として、単一の目的で使用されるカスタムプロパティを変更しないでください。 これはプリプロセッサで行う方法とまったく同じであるため、簡単に実行できますが、カスタムプロパティではほとんど意味がありません。
この例では、サンプルコンポーネントで使用される2つのカスタムプロパティがあります。 画面サイズに応じて、 --font-size-large
--font-size-small
の値から--font-size-largeの値に切り替えます。
:root { --font-size-small: 1.2em; --font-size-large: 2em; } .example { font-size: var(--font-size-small); } @media screen and (min-width: 800px) { .example { font-size: var(--font-size-large); } }
これを行うためのより良い方法は、コンポーネントにスコープされた単一のカスタムプロパティを定義することです。 次に、メディアクエリまたはその他のセレクターを使用して、その値を変更します。
.example { --example-font-size: 1.2em; } @media screen and (min-width: 800px) { .example { --example-font-size: 2em; } }
最後に、1つの場所で、このカスタムプロパティの値を使用します。
.example { font-size: var(--example-font-size); }
この例とそれ以前の例では、メディアクエリはカスタムプロパティの値を変更するためにのみ使用されていました。 また、 var()
ステートメントが使用されている場所が1つだけであり、通常のCSSプロパティが更新されていることに気付くかもしれません。
変数宣言とプロパティ宣言の間のこの分離は意図的なものです。 これには多くの理由がありますが、レスポンシブデザインを考えると、その利点は最も明白です。
カスタムプロパティを使用したレスポンシブデザイン
メディアクエリに大きく依存するレスポンシブデザインの問題の1つは、CSSをどのように編成しても、特定のコンポーネントに関連するスタイルがスタイルシート全体で断片化されることです。
どのCSSプロパティが変更されるかを知ることは非常に難しい場合があります。 それでも、CSSカスタムプロパティは、レスポンシブデザインに関連するロジックの一部を整理し、メディアクエリの操作を非常に簡単にするのに役立ちます。
それが変わるなら、それは変数です
メディアクエリを使用して変更されるプロパティは本質的に動的であり、カスタムプロパティはCSSで動的な値を表現する手段を提供します。 つまり、メディアクエリを使用してCSSプロパティを変更する場合は、この値をカスタムプロパティに配置する必要があります。
次に、これを、すべてのメディアルール、ホバー状態、または値の変更方法を定義する動的セレクターとともに、ドキュメントの先頭に移動できます。
ロジックを設計から分離する
正しく実行された場合、ロジックとデザインの分離は、メディアクエリがカスタムプロパティの値を変更するためにのみ使用されることを意味します。 これは、レスポンシブデザインに関連するすべてのロジックがドキュメントの先頭にある必要があることを意味し、CSSでvar()
ステートメントが表示される場合は常に、このプロパティが変更されることがすぐにわかります。 CSSを書く従来の方法では、これを一目で知る方法はありませんでした。
私たちの多くは、さまざまな状況でどのプロパティが変更されたかを頭の中で追跡しながら、CSSを一目で読んで解釈するのが非常に上手になりました。 私はこれにうんざりしていて、もうこれをやりたくない! カスタムプロパティは、ロジックとその実装の間のリンクを提供するようになったため、これを追跡する必要はありません。これは非常に便利です。
ロジックフォールド
ドキュメントまたは関数の上部で変数を宣言するという考えは、新しい考えではありません。 これはほとんどの言語で実行できることであり、CSSでも実行できるようになりました。 このようにCSSを作成すると、ドキュメントの上部と下部にあるCSSが視覚的に明確に区別されます。 これらのセクションについて話すときは、これらのセクションを区別する方法が必要です。「ロジックフォールド」のアイデアは、私が使い始めた比喩です。
フォールドの上には、すべてのプリプロセッサ変数とカスタムプロパティが含まれています。 これには、カスタムプロパティが持つことができるすべての異なる値が含まれます。 カスタムプロパティがどのように変化するかを簡単に追跡できるはずです。
フォールドの下のCSSは単純で、非常に宣言的で、読みやすいです。 メディアクエリやその他の必要な現代のCSSの複雑さの前のCSSのように感じます。
6列のフレックスボックスグリッドシステムの非常に単純な例を見てみましょう。
.row { --row-display: block; } @media screen and (min-width: 600px) { .row { --row-display: flex; } }
--row-display
カスタムプロパティは、最初はblock
に設定されています。 600pxを超えると、表示モードがフレックスに設定されます。
折り目の下は次のようになります。
.row { display: var(--row-display); flex-direction: row; flex-wrap: nowrap; } .col-1, .col-2, .col-3, .col-4, .col-5, .col-6 { flex-grow: 0; flex-shrink: 0; } .col-1 { flex-basis: 16.66%; } .col-2 { flex-basis: 33.33%; } .col-3 { flex-basis: 50%; } .col-4 { flex-basis: 66.66%; } .col-5 { flex-basis: 83.33%; } .col-6 { flex-basis: 100%; }
--row-display
は変化する値であることがすぐにわかります。 最初はblock
になるため、フレックス値は無視されます。
この例はかなり単純ですが、残りのスペースを埋める柔軟な幅の列を含めるように拡張した場合、 flex-grow
、 flex-shrink
、およびflex-basis
値をカスタムプロパティに変換する必要がある可能性があります。 これを試すか、ここでより詳細な例を見ることができます。
テーマのカスタムプロパティ
私は主にグローバル動的変数にカスタムプロパティを使用することに反対し、カスタムプロパティを:root
セレクターにアタッチすることは多くの場合有害であると考えられていることを示唆しています。 ただし、すべてのルールには例外があり、カスタムプロパティの場合はテーマになります。
グローバルカスタムプロパティの使用を制限すると、テーマ設定が非常に簡単になります。
テーマ設定とは、通常、ユーザーが何らかの方法でUIをカスタマイズできるようにすることを指します。 これは、プロファイルページの色を変更するようなものである可能性があります。 または、よりローカライズされたものである可能性があります。 たとえば、GoogleKeepアプリケーションでメモの色を選択できます。
テーマ設定には通常、個別のスタイルシートをコンパイルしてデフォルト値をユーザー設定でオーバーライドするか、ユーザーごとに異なるスタイルシートをコンパイルすることが含まれます。 これらは両方とも困難であり、パフォーマンスに影響を与える可能性があります。
カスタムプロパティを使用すると、別のスタイルシートをコンパイルする必要はありません。 ユーザーの好みに応じてプロパティの値を更新するだけで済みます。 これらは継承された値であるため、ルート要素でこれを行うと、アプリケーションのどこでも使用できます。
グローバルダイナミックプロパティを活用する
カスタムプロパティでは大文字と小文字が区別されます。ほとんどのカスタムプロパティはローカルであるため、グローバル動的プロパティを使用している場合は、それらを大文字にするのが理にかなっています。
:root { --THEME-COLOR: var(--user-theme-color, #d33a2c); }
変数の大文字化は、多くの場合、グローバル定数を意味します。 私たちにとって、これは、プロパティがアプリケーションの他の場所に設定されており、おそらくローカルで変更してはならないことを意味します。
グローバル動的プロパティを直接設定しないでください
カスタムプロパティはフォールバック値を受け入れます。 グローバルカスタムプロパティの値を直接上書きすることを避け、ユーザー値を分離しておくと便利な場合があります。 フォールバック値を使用してこれを行うことができます。
上記の例では、 --THEME-COLOR
の値が存在する場合は--user-theme-color
の値に設定されます。 --user-theme-color
が設定されていない場合、 #d33a2c
の値が使用されます。 このように、 --THEME-COLOR
を使用するたびにフォールバックを提供する必要はありません。
以下の例では、背景がgreen
に設定されることを期待できます。 ただし、ルート要素に--user-theme-color
の値が設定されていないため、 --THEME-COLOR
の値は変更されていません。
:root { --THEME-COLOR: var(--user-theme-color, #d33a2c); } body { --user-theme-color: green; background: var(--THEME-COLOR); }
このようにグローバル動的プロパティを間接的に設定すると、ローカルで上書きされないように保護され、ユーザー設定が常にルート要素から継承されるようになります。 これは、テーマの値を保護し、意図しない継承を回避するための便利な規則です。
特定のプロパティを継承に公開したい場合は、 :root
セレクターを*
セレクターに置き換えることができます。
* { --THEME-COLOR: var(--user-theme-color, #d33a2c); } body { --user-theme-color: green; background: var(--THEME-COLOR); }
これで、 --THEME-COLOR
の値がすべての要素に対して再計算されるため、-user- --user-theme-color
のローカル値を使用できます。 つまり、この例の背景色はgreen
になります。
このパターンのより詳細な例は、カスタムプロパティを使用した色の操作のセクションで確認できます。
JavaScriptを使用したカスタムプロパティの更新
JavaScriptを使用してカスタムプロパティを設定する場合は、かなり単純なAPIがあり、次のようになります。
const elm = document.documentElement; elm.style.setProperty('--USER-THEME-COLOR', 'tomato');
ここでは、ドキュメント要素に--USER-THEME-COLOR
の値を設定しています。つまり、すべての要素に継承される:root
要素です。
これは新しいAPIではありません。 これは、要素のスタイルを更新するための同じJavaScriptメソッドです。 これらはインラインスタイルであるため、通常のCSSよりも高い特異性があります。
これは、ローカルのカスタマイズを簡単に適用できることを意味します。
.note { --note-color: #eaeaea; } .note { background: var(--note-color); }
ここでは、 --note-color
のデフォルト値を設定し、これを.note
コンポーネントにスコープします。 この単純な例でも、変数宣言をプロパティ宣言とは別にしています。
const elm = document.querySelector('#note-uid'); elm.style.setProperty('--note-color', 'yellow');
次に、 .note
要素の特定のインスタンスをターゲットにして、その要素のみの--note-color
カスタムプロパティの値を変更します。 これにより、デフォルト値よりも高い特異性が得られます。
Reactを使用して、この例でこれがどのように機能するかを確認できます。 これらのユーザー設定は、ローカルストレージに保存することも、大規模なアプリケーションの場合はデータベースに保存することもできます。
カスタムプロパティを使用した色の操作
16進値と名前付きの色に加えて、CSSにはrgb()
やhsl()
)などの色関数があります。 これらにより、色相や明度などの色の個々のコンポーネントを指定できます。 カスタムプロパティは、カラー関数と組み合わせて使用できます。
:root { --hue: 25; } body { background: hsl(var(--hue), 80%, 50%); }
これは便利ですが、プリプロセッサで最も広く使用されている機能のいくつかは、明るく、暗く、彩度を下げるなどの関数を使用して色を操作できる高度な色関数です。
darken($base-color, 10%); lighten($base-color, 10%); desaturate($base-color, 20%);
これらの機能のいくつかをブラウザに含めると便利です。 それらは来ていますが、CSSにネイティブの色変更関数ができるまでは、カスタムプロパティがそのギャップの一部を埋めることができます。
カスタムプロパティは、 rgb()
やhsl()
)などの既存のカラー関数内で使用できますが、 calc()
でも使用できます。 これは、実数を乗算することでパーセンテージに変換できることを意味します。たとえば、 calc(50 * 1%)
= 50%
です。
:root { --lightness: 50; } body { background: hsl(25, 80%, calc(var(--lightness) * 1%)); }
明度の値を実数として保存する理由は、パーセンテージに変換する前にcalc
で操作できるようにするためです。 たとえば、色を20%
暗くしたい場合は、その明度に0.8
を掛けることができます。 明度の計算をローカルスコープのカスタムプロパティに分割することで、これを少し読みやすくすることができます。
:root { --lightness: 50; } body { --lightness: calc(var(--lightness * 0.8)); background: hsl(25, 80%, calc(var(--lightness) * 1%)); }
さらに多くの計算を抽象化し、カスタムプロパティを使用してCSSで色変更関数のようなものを作成することもできます。 この例は、ほとんどの実用的なテーマの場合には複雑すぎる可能性がありますが、動的なカスタムプロパティの全機能を示しています。
テーマを簡素化する
カスタムプロパティを使用する利点の1つは、テーマを単純化できることです。 アプリケーションは、カスタムプロパティがどのように使用されるかを認識する必要はありません。 代わりに、JavaScriptまたはサーバー側のコードを使用して、カスタムプロパティの値を設定します。 これらの値がどのように使用されるかは、スタイルシートによって決定されます。
これは、ロジックを設計から分離できることをもう一度意味します。 技術設計チームがいる場合、作成者はスタイルシートを更新し、JavaScriptやバックエンドコードを1行も変更せずにカスタムプロパティを適用する方法を決定できます。
カスタムプロパティを使用すると、テーマの複雑さの一部をCSSに移動することもできます。この複雑さは、CSSの保守性に悪影響を与える可能性があるため、可能な限り単純にすることを忘れないでください。
今日のカスタムプロパティの使用
IE10および11をサポートしている場合でも、今日からカスタムプロパティの使用を開始できます。 この記事の例のほとんどは、CSSの記述と構造化の方法に関係しています。 保守性の点では利点は重要ですが、ほとんどの例では、より複雑なコードで実行できることを減らすだけです。
postcss-css-variablesというツールを使用して、カスタムプロパティのほとんどの機能を同じコードの静的表現に変換します。 他の同様のツールは、メディアクエリまたは複雑なセレクター内のカスタムプロパティを無視し、カスタムプロパティをプリプロセッサ変数のように扱います。
これらのツールで実行できないのは、カスタムプロパティの実行時機能をエミュレートすることです。 これは、JavaScriptを使用したテーマ設定やプロパティの変更などの動的な機能がないことを意味します。 これは多くの状況で問題ないかもしれません。 状況によっては、UIのカスタマイズはプログレッシブエンハンスメントと見なされる場合があり、デフォルトのテーマは古いブラウザーで完全に受け入れられる可能性があります。
正しいスタイルシートのロード
postCSSを使用する方法はたくさんあります。 私はgulp
プロセスを使用して、新しいブラウザーと古いブラウザー用に別々のスタイルシートをコンパイルします。 私のgulp
タスクの簡略版は次のようになります。
import gulp from "gulp"; import sass from "gulp-sass"; import postcss from "gulp-postcss"; import rename from "gulp-rename"; import cssvariables from "postcss-css-variables"; import autoprefixer from "autoprefixer"; import cssnano from "cssnano"; gulp.task("css-no-vars", () => gulp .src("./src/css/*.scss") .pipe(sass().on("error", sass.logError)) .pipe(postcss([cssvariables(), cssnano()])) .pipe(rename({ extname: ".no-vars.css" })) .pipe(gulp.dest("./dist/css")) ); gulp.task("css", () => gulp .src("./src/css/*.scss") .pipe(sass().on("error", sass.logError)) .pipe(postcss([cssnano()])) .pipe(rename({ extname: ".css" })) .pipe(gulp.dest("./dist/css")) );
これにより、2つのCSSファイルが作成されます。1つはカスタムプロパティを持つ通常のファイル( styles.css
)で、もう1つは古いブラウザー用です( styles.no-vars.css
)。 IE10と11をstyles.no-vars.css
やその他のブラウザーで提供して、通常のCSSファイルを取得したいと思います。
通常、私は機能クエリの使用を推奨しますが、IE11は機能クエリをサポートしておらず、カスタムプロパティを広範囲に使用しているため、この場合は別のスタイルシートを提供するのが理にかなっています。
別のスタイルシートをインテリジェントに提供し、スタイルのないコンテンツのフラッシュを回避することは簡単な作業ではありません。 カスタムプロパティの動的機能が必要ない場合は、すべてのブラウザstyles.no-vars.css
を提供し、カスタムプロパティを単に開発ツールとして使用することを検討できます。
カスタムプロパティのすべての動的機能を最大限に活用したい場合は、重要なCSS手法を使用することをお勧めします。 これらの手法に従って、重要なCSSがインラインでレンダリングされている間、メインのスタイルシートが非同期にロードされます。 ページヘッダーは次のようになります。
<head> <style> /* inlined critical CSS */ </style> <script> loadCSS('non-critical.css'); </script> </head>
これを拡張して、ブラウザがカスタムプロパティをサポートしているかどうかに応じて、 styles.css
またはstyles.no-vars.css
いずれかをロードできます。 次のようなサポートを検出できます。
if ( window.CSS && CSS.supports('color', 'var(--test)') ) { loadCSS('styles.css'); } else { loadCSS('styles.no-vars.css'); }
結論
CSSを効率的に整理するのに苦労している場合、レスポンシブコンポーネントに問題がある場合、クライアント側のテーマを実装したい場合、またはカスタムプロパティをすぐに使い始めたい場合は、このガイドで知っておく必要のあるすべてのことを説明します。
CSSの動的変数と静的変数の違いと、いくつかの簡単なルールを理解することが重要です。
- ロジックを設計から分離します。
- CSSプロパティが変更された場合は、カスタムプロパティの使用を検討してください。
- 使用されているカスタムプロパティではなく、カスタムプロパティの値を変更します。
- グローバル変数は通常静的です。
これらの規則に従うと、カスタムプロパティの操作が思ったよりもはるかに簡単であることがわかります。 これにより、CSS全般へのアプローチ方法が変わる可能性もあります。
参考文献
- “It's Time To Start Using Custom Properties,” Serg Hospodarets
A general introduction to the syntax and the features of custom properties. - “Pragmatic, Practical, And Progressive Theming With Custom Properties,” Harry Roberts
More useful information on theming. - Custom Properties Collection, Mike Riethmuller on CodePen
A number of different examples you can experiment with.