CSSカスタムプロパティを使用してアプリケーションの配色を構成する方法

公開: 2022-03-10
簡単な要約↬この記事では、Artur Basakが、アプリケーションの色に対応するCSSカスタムプロパティを設定する方法に関する最新のアプローチを紹介します。 色を3つのレベルに分割するというアイデアは、パレット(またはスキーム)、機能色(またはテーマ)、およびコンポーネントの色(ローカルスコープ)という非常に便利です。

変数は、プロジェクトの色を整理するのに役立つ基本的なツールです。 長い間、フロントエンドエンジニアは、プリプロセッサ変数を使用してプロジェクトの色を構成していました。 しかし現在、多くの開発者は、色変数を整理するための最新のネイティブメカニズムであるCSSカスタムプロパティを好みます。 プリプロセッサ変数に対する最も重要な利点は、プロジェクトのコンパイル段階ではなくリアルタイムで機能し、値の継承と再定義をその場で使用できるカスケードモデルをサポートしていることです。

アプリケーションの配色を整理しようとしているときは、いつでもルートセクションに色に関連するすべてのカスタムプロパティを配置し、それらに名前を付けて、必要なすべての場所で使用できます。

Artur Basakによるペン[色のカスタムプロパティ](https://codepen.io/smashingmag/pen/RwaNqxW)を参照してください。

ArturBasakによる色のペンカスタムプロパティを参照してください。

これはオプションですが、アプリケーションテーマ、ホワイトラベリング、ブランドの更新、またはライトモードまたはダークモードの整理の問題を解決するのに役立ちますか? コントラストを上げるために配色を調整する必要がある場合はどうなりますか? 現在のアプローチでは、変数の各値を更新する必要があります。

この記事では、カスタムプロパティを使用して色変数を分割する方法について、より柔軟で抵抗力のあるアプローチを提案します。これにより、これらの問題の多くを解決できます。

ジャンプした後もっと! 以下を読み続けてください↓

カラーパレットの設定

ウェブサイトのカラーリングは、配色の設定から始まります。 このようなスキームは、カラーホイールに基づいています。 通常、パレットの基礎となる原色はごくわずかで、残りは派生色(トーンとミッドトーン)です。 ほとんどの場合、パレットは静的であり、Webアプリケーションの実行中は変更されません。

色彩理論によると、配色にはいくつかのオプションしかありません。

  • 単色スキーム(1原色)
  • 補完スキーム(2原色)
  • トライアドスキーム(3原色)
  • テトラディックスキーム(4原色)
  • 隣接するパターン(2色または3原色)

私の例では、Palettonサービスを使用してトライアドカラースキームを生成します。

確立されたトライアドスキームを備えたカラーホイール:緑、青、赤のバリエーション。
パレットサービス:トライアドカラースキーム。 (大プレビュー)

私は今3つの主要な色を持っています。 これらに基づいて、トーンとミッドトーンを計算します( calc関数と組み合わせたHSLフォーマットはこれに非常に便利なツールです)。 明度の値を変更することで、パレットにいくつかの追加の色を生成できます。

Artur Basakによるペン[HSLパレット](https://codepen.io/smashingmag/pen/OJNPaQW)を参照してください。

ArturBasakによるペンHSLパレットを参照してください。

これで、パレットが変更された場合、原色の値のみを変更する必要があります。 残りは自動的に再計算されます。

HEXまたはRGB形式を好む場合は、問題ではありません。 プロジェクトをコンパイルする段階で、プリプロセッサの対応する機能(SCSSやcolor-adjust機能など)を使用してパレットを作成できます。 前に述べたように、このレイヤーはほとんど静的です。 実行中のアプリケーションでパレットが変更されることは非常にまれです。 そのため、プリプロセッサを使用して計算できます。

色ごとにHEXリテラルとRGBの両方を生成することもお勧めします。 これにより、将来的にアルファチャネルで再生できるようになります。

Artur Basakによるペン[SCSSパレット](https://codepen.io/smashingmag/pen/oNxgQqv)を参照してください。

ArturBasakによるペンSCSSパレットを参照してください。

パレットレベルは、色が変数名に直接エンコードされる唯一のレベルです。つまり、名前を読み取ることで色を一意に識別できます。

テーマまたは機能色を定義する

パレットが完成したら、次のステップは機能的な色のレベルです。 このレベルでは、色の値は、その目的、実行する機能、正確に色付けするものほど重要ではありません。 たとえば、プライマリまたはアプリのブランドの色、境界線の色、暗い背景のテキストの色、明るい背景のテキストの色、ボタンの背景色、リンクの色、ホバーリンクの色、ヒントテキストの色などです。 。

これらは、ほとんどすべてのWebサイトまたはアプリケーションで非常に一般的なものです。 そのような色は、アプリケーションの特定の色のテーマに関与していると言えます。 また、そのような変数の値は、パレットから厳密に取得されます。 したがって、さまざまなカラーパレットを操作するだけで、アプリケーションのテーマを簡単に変更できます。

以下に、ボタン、リンク、入力フィールドの3つの典型的なUIコントロールを作成しました。 これらは、前に上で生成したパレットの値を含む関数変数を使用して色付けされています。 アプリケーションテーマ(条件付きブランド)を担当する主な機能変数は、原色変数です。

上部にある3つのボタンを使用して、テーマを切り替えることができます(コントロールのブランドカラーを変更します)。 変更は、適切なCSSOM API(setProperty)を使用して行われます。

ArturBasakによるペン[FunctionalColors](https://codepen.io/smashingmag/pen/poyvQLL)を参照してください。

ArturBasakによるペン機能色を参照してください。

このアプローチは、テーマ設定だけでなく、個々のWebページの構成にも便利です。 たとえば、zubry.by Webサイトでは、共通のスタイルシートと機能変数--page-colorを使用して、すべてのページのロゴ、見出し、コントロール、およびテキストの選択に色を付けました。 そして、各ページの独自のスタイルで、この変数を再定義して、ページを個々の原色に設定しました。

ZUBRY.BYウェブサイトの3つのウェブページ:切手ページ、はがきページ、カードページ。
ZUBRY.BYのWebサイトでは、各ページに個別の原色があります。 (大プレビュー)

コンポーネントの色を使用する

大規模なWebプロジェクトには、常に分解が含まれています。 すべてを小さなコンポーネントに分割し、多くの場所で再利用します。 通常、各コンポーネントには独自のスタイルがあります。つまり、BEMまたはCSSモジュール、あるいは別のアプローチを分解するために何を使用したかは関係ありません。 このような各コードをローカルスコープと呼んで再利用できることが重要です。

一般に、2つのケースでコンポーネントレベルで色変数を使用することに意味があります。

1つ目は、アプリケーションスタイルガイドに従って、さまざまな設定で繰り返されるコンポーネントです。たとえば、プライマリ(ブランド)ボタン、セカンダリボタン、ターシャリなど、さまざまなニーズに対応するボタンです。

Tisprアプリケーションのさまざまなボタンスタイル。
Tisprアプリケーションスタイルガイド。 ボタン。 (大プレビュー)

2つ目は、ボタンのホバー、アクティブ状態、フォーカス状態など、色の異なる複数の状態を持つコンポーネントの場合です。 入力フィールドまたは選択フィールドなどの正常および無効な状態。

コンポーネント変数が役立つ可能性があるよりまれなケースは、「ホワイトラベル」の機能です。 「ホワイトラベル」は、ユーザーがユーザーインターフェイスの一部をカスタマイズまたはブランド化して、クライアントとのやり取りのエクスペリエンスを向上させることができるサービス機能です。 たとえば、ユーザーがサービスや電子メールテンプレートを介して顧客と共有する電子ドキュメント。 この場合、コンポーネントレベルの変数は、アプリケーションのカラーテーマの残りの部分とは別に特定のコンポーネントを構成するのに役立ちます。

以下の例では、プライマリ(ブランド)ボタンの色をカスタマイズするためのコントロールを追加しました。 コンポーネントレベルの色変数を使用して、UIコントロールを互いに個別に構成できます。

Artur Basakによるペン[コンポーネントの色](https://codepen.io/smashingmag/pen/LYNEXdw)を参照してください。

ArturBasakによるペンコンポーネントの色を参照してください。

変数のレベルを判断する方法は?

ルートに何を入れることができるか(テーマまたは機能レベル)、およびコンポーネントのレベルで何を残すかを理解する方法についての質問に出くわしました。 これは、あなたが取り組んでいる状況を見ずに答えるのが難しい優れた質問です。

残念ながら、プログラミングと同じアプローチは色やスタイルでは機能しません。3つの同一のコードが表示された場合は、それをリファクタリングする必要があります。

色はコンポーネントごとに繰り返すことができますが、これはそれがルールであることを意味するものではありません。 そのようなコンポーネント間には関係がありません。 たとえば、入力フィールドの境界線とプライマリボタンの背景。 はい、上記の私の例ではそうですが、次の例を確認しましょう。

ArturBasakによるペン[ColorSplit:Only Palette](https://codepen.io/smashingmag/pen/YzqPRLX)を参照してください。

Artur Basakによるペンカラースプリット:唯一のパレットを参照してください。

濃い灰色が繰り返されます—これは、入力フィールドの境界線、閉じるアイコンの塗りつぶし色、および2番目のボタンの背景です。 しかし、これらのコンポーネントは決して相互に接続されていません。 入力フィールドの境界線の色が変更された場合、セカンダリボタンの背景は変更されません。 このような場合、ここではパレットの変数のみを保持する必要があります。

UIコントロール:ボタン、リンク、ヘッドおよび通常のテキスト、入力フィールド
アプリケーションスタイルガイドの例。 (大プレビュー)

緑はどうですか? プライマリカラーまたはブランドカラーとして明確に定義できます。メインボタンの色が変わると、最初のレベルのリンクとヘッダーの色も変わる可能性があります。

赤はどうですか? 入力フィールドの無効な状態、エラーメッセージ、および破壊的なボタンは、アプリケーションレベル全体で同じ色になります。 これはパターンです。 これで、ルートセクションでいくつかの一般的な機能変数を定義できます。

Artur Basakによるペン[色分割:機能レベル](https://codepen.io/smashingmag/pen/MWyYzGX)を参照してください。

Artur Basakによるペンカラースプリット:機能レベルを参照してください。

コンポーネントの色のレベルについては、カスタムプロパティを使用してカスタマイズできるコンポーネントを簡単に識別できます。

ボタンはさまざまな設定で繰り返され、さまざまなユースケース(プライマリ、セカンダリ、ターシャリ、破壊的、またはネガティブなケース)の背景色とテキストが変更されます。

入力フィールドには、背景色と境界線の色が異なる、正しくない状態と正常な状態の2つの状態があります。 したがって、これらの設定を、対応するコンポーネントのレベルでカラー変数に入れましょう。

残りのコンポーネントについては、ローカルカラー変数を定義する必要はありません。これは冗長になります。

ArturBasakによるペン[ColorSplit:Component Level](https://codepen.io/smashingmag/pen/BaKyGVR)を参照してください。

Artur Basakによるペンカラースプリット:コンポーネントレベルを参照してください。

プロジェクトのパターン言語に飛び込む必要があります。これはおそらく、設計チームとUXによって開発されています。 エンジニアは視覚言語の概念全体を完全に理解する必要があります。そうしないと、何が一般的で機能レベルで生きるべきか、そして何がローカルの可視性の範囲に留まるべきかを判断できます。

しかし、すべてがそれほど複雑ではなく、明らかなことがあります。 ページの一般的な背景、メインテキストの背景、色。ほとんどの場合、これがアプリケーションのテーマを設定します。 特定のモード(ダークモードやライトモードなど)の構成に関与するものを収集すると非常に便利です。

すべてをルートセクションに入れてみませんか?

そんな経験をしました。 Litionプロジェクトでは、チームと私は、WebアプリケーションではIE11をサポートする必要があるが、Webサイトとランディングではサポートする必要がないという事実に直面しました。 プロジェクト間で共通のUIキットが使用され、すべての変数をルートに配置することにしました。これにより、任意のレベルでそれらを再定義できるようになります。

また、WebアプリケーションとIE11の場合のこのアプローチでは、次のポストプロセッサープラグインを介してコードを渡し、プロジェクト内のすべてのUIコンポーネントのリテラルにこれらの変数を変換しました。 このトリックは、ポストプロセッサがカスケードモデルの詳細を理解できないため、すべての変数がルートセクションで定義されている場合にのみ可能です。

ブラウザ開発ツールを開いたLitionWebサイトのメインページ
LitionSSRのWebサイト。 ルートセクションのすべての変数。 (大プレビュー)

今、私はこれが正しい方法ではなかったことを理解しています。 まず、コンポーネントの色をルートセクションに配置すると、関心の分離の原則が破られます。 その結果、スタイルシートに冗長なCSSが含まれる可能性があります。 たとえば、各コンポーネントが独自のスタイルを持つコンポーネントのフォルダがあります。 また、ルートセクションで色変数を説明する一般的なスタイルシートもあります。 ボタンコンポーネントを削除することにしました。 この場合、ボタンに関連付けられている変数も共通スタイルファイルから削除することを忘れないでください。

第二に、これはパフォーマンスの点で最善の解決策ではありません。 はい、色の変更はリペイントのプロセスのみを引き起こし、リフロー/レイアウトは引き起こしません。これ自体はそれほどコストがかかりませんが、最高レベルでいくつかの変更を行うと、これらの場合よりも多くのリソースを使用してツリー全体をチェックします。変更は小さなローカルエリアにあります。 詳細については、LisiLinhartのCSS変数のパフォーマンスベンチマークを読むことをお勧めします。

私の現在のプロジェクトTisprでは、チームと私は分割を使用し、ルートにすべてをダンプするのではなく、高レベルではパレットと機能的な色のみをダンプします。 また、この問題は対応するポリフィルによって解決されるため、IE11を恐れることはありません。 npmモジュールie11-custom-propertiesをインストールし、ライブラリをアプリケーションJSバンドルにインポートするだけです。

 // Use ES6 syntax import "ie11-custom-properties"; // or CommonJS require('ie11-custom-properties');

または、スクリプトタグでモジュールを追加します。

 <script async src="./node_modules/ie11-custom-properties/ie11CustomProperties.js">

また、CDNを介してnpmなしでライブラリを追加できます。 このポリフィルの作業は、IE11がカスタムプロパティを最小限にサポートしているという事実に基づいています。カスタムプロパティでは、カスケードに基づいてプロパティを定義および読み取ることができます。 これは、二重ダッシュで始まるプロパティでは不可能ですが、単一ダッシュで始まる可能性があります(ベンダープレフィックスと同様のメカニズム)。 これについては、リポジトリのドキュメントで詳しく読むことができます。また、いくつかの制限についても知ることができます。 他のブラウザはこのポリフィルを無視します。

以下は、Tispr Webアプリケーションのパレットと、電子ドキュメント(ユーザー契約、請求書、提案など)の「ホワイトラベル」機能のコントロールです。

次の列を持つグリッド:色、色名、色HEX、色RGB。
Tisprスタイルガイド:カラーパレット。 (大プレビュー)
カスタムカラーピッカーUIコンポーネント
Tisprスタイルガイド:ホワイトラベル機能のブランドピッカー。 (大プレビュー)

JavaScript側に色変数を保存してみませんか?

もう1つの合理的な質問:パレットと関数の変数をJavaScriptコードに格納してみませんか? これは動的に変更することもでき、後でインラインスタイルを使用して色を再定義することもできます。 これはオプションかもしれませんが、特定の要素にアクセスしてそれらの色のプロパティを変更する必要があるため、このアプローチは最適ではない可能性があります。 CSS変数を使用すると、単一のプロパティ、つまり変数値のみを変更できます。

JavaScriptには、色を操作するためのネイティブ関数やAPIはありません。 CSSカラーモジュール5では、派生色を作成したり、何らかの方法でそれらを計算したりする多くの機会があります。 将来の観点から、CSSカスタムプロパティはJS変数よりも豊富で柔軟性があります。 また、JS変数を使用すると、カスケードで継承を使用する可能性がなくなり、それが主な欠点になります。

結論

色を3つのレベル(パレット、機能、およびコンポーネント)に分割すると、プロジェクトでの作業中に変更や新しい要件にさらに適応できるようになります。 CSSカスタムプロパティは、色分割を整理するための適切なツールであると思います。スタイリングに何を使用するかは関係ありません。純粋なCSS、プリプロセッサ、またはCSS-in-JSアプローチです。

私は自分の経験からこのアプローチにたどり着きましたが、私は一人ではありません。 Sara Soueidanは、彼女の記事で、変数をグローバルレベルとコンポーネントレベルに分割する同様のアプローチについて説明しました。

また、Lea Verouの記事を読んで、CSS変数を適用する可能性のあるケースについて説明していることをお勧めします(色だけでなく)。