本文へ

開発記録

デザイントークンは色の一覧だけではない:色ごとの役割を決める

このサイトの背景、カード、文字、アクション用の色を例に、色の役割とコンポーネントライブラリへの対応付け、操作時の状態を整理します。配色変更をカラーコードの一括置換で済ませないための考え方です。

林予晴
公開
  • デザイントークン
  • Tailwind CSS
  • 配色
  • コンポーネント設計

カラーコードをいくつかの変数にまとめても、それぞれの色をどこに使うかは決まりません。ページの背景、カード、ホバー時の表示、主要なボタンがすべて同じ名前を参照していると、次に色を調整するときも、一つの値を変えてどこまで影響するのか分からないままです。

私が重視しているのは、色の役割です。コンテンツの面を区別する色なのか、文字を読みやすくする色なのか、次の操作を見つけてもらう色なのか。こうした分担があって初めて、トークンは保守に役立ちます。役割が曖昧なままでは、カラーコードの置き場所を変えただけです。

このノートは、2026-10-24 時点のこのサイトの共通カラーとコンポーネントの使い方をもとにまとめています。その時点でのサイトの設計上の選択であり、あらゆるプロダクトに適した標準配色ではありません。

名前で見た目だけでなく用途を伝えます

このサイトでは、ページ全体の背景と、通常のコンテンツを載せる背景面を分けています。siteBg はページの背景、siteSurface はコンテンツカードに使います。文字、強調用の暗い背景面、その上に置く前景色にも、それぞれ別の役割を持たせています。

役割の一部を表にすると、次のようになります。

役割 記録時点のカラーコード 主な用途
siteBg #F1F1F0 ページの背景
siteSurface #FCFBF9 通常のコンテンツの背景面
siteText #25272C 明るい背景面の主要な文字
siteInk #25272C 強調用の暗い背景面やコントロール
siteOnInk #FBFAF7 暗い背景面に載せる明るい前景色
siteAction #C13F1B 主要な操作用コントロールの塗り色

siteText と siteInk は同じカラーコードですが、役割は異なります。一方は文字、もう一方は背景面です。将来、別々に調整する必要が生じても、呼び出し側で一つひとつの黒がどの用途なのかを推測せずに済みます。

だからといって、CSS のすべてのプロパティにトークンが必要なわけではありません。用途や組み合わせ、将来どこまで一緒に変更するかが異なる場合に、名前を分ける意味があります。同じ役割を似た名前に細分化しすぎると、かえって選びにくくなります。

文字の強調色とボタンの塗り色は分けて考えます

このサイトの primary はより濃い #A93618 で、主要な操作用コントロールには siteAction の #C13F1B を使います。Button の既定の variant では、前景色に siteOnInk を明示的に組み合わせ、ホバー時には siteActionHover を使っています。

オレンジを一色余分に残したいからではありません。明るい背景上で文字として読める色と、文字を載せるコントロールの背景色を、独立して調整するためです。「ブランドのオレンジ」というトークン一つにまとめると、この二つの課題まで結び付けてしまいがちです。

色は組み合わせで見る必要もあります。W3C の文字コントラストの解説が確認対象にしているのは、文字とその背景の関係であり、カラーコード単体を「読みやすい」と呼べるかどうかではありません。[4] 同じオレンジでも、オフホワイト、チャコール、半透明の画像の上では、同じ判断をそのまま当てはめることはできません。

そのため、トークンを意味に基づいて命名しても、コントラスト基準を自動的に満たすわけではありません。実際の前景色、背景色、文字サイズ、状態を確認する必要があります。無効状態、ホバー、フォーカスも、一枚の静止したスクリーンショットだけでは確認できません。

ライブラリのセマンティックトークンを既存の役割に対応付けます

サイトとコンポーネントライブラリで、同じ命名体系を使う必要はありません。このサイトでは siteSurface や siteText といった役割を残し、コンポーネントが必要とする card、popover、foreground などの名前を、そこへ対応付けています。

対応付けの一部を抜き出すと、次のようになります。

@theme inline {
  --color-background: var(--color-siteBg);
  --color-foreground: var(--color-siteText);
  --color-card: var(--color-siteSurface);
  --color-card-foreground: var(--color-siteText);
  --color-popover: var(--color-siteSurface);
}

ここでは、ほかのセマンティックな色を省略しています。スタイル全体を置き換えられる完全な設定ではありません。大切なのは、カードという同じ役割には、サイト側の同じ定義を使うという点です。コンポーネントライブラリを追加しても、別の場所で「どの白を使うか」を決め直すことはしません。

Tailwind CSS は、利用できるユーティリティを @theme 変数で定義します。テーマ変数から別の変数を参照する場合の inline の扱いも、公式ドキュメントで説明されています。[3] この例では、既存の役割をユーティリティにつなぐ実装上の接点であり、サイトに別のテーマを導入するものではありません。

ただし、対応付けをしたからといって、コンポーネント自身の variants を確認しなくてよいわけではありません。このサイトの Button の既定の背景色は、siteAction を直接参照しています。ライブラリに primary という名前があるだけで、主要なボタンがすべて同じカラーコードを使うと推測してはいけません。

ホバーで操作できることを示しても、カード全体の色を変える必要はありません

このサイトの通常のノートカードは、オフホワイトの背景面を保ちます。ホバー時や focus-within の状態では、わずかな浮き上がり、影、タイトルや矢印の部分的な強調で状態を示します。ポインターを重ねるたびに、通常のカードを強調用の暗いカードへ切り替えることはしません。

暗いカードには、別途 siteOnInk、siteOnInkMuted、siteOnInkAccent が用意されています。これにより、「もともと強調しているコンテンツ」と「今、操作できることを示している状態」に、同じ大きな色の変化を使わずに済みます。

これは、このサイトの視覚的な階層の付け方です。ボタンや選択項目を含め、あらゆるコントロールの背景を透明にすべきだという意味ではありません。コントロールでは、操作可能、選択中、無効の状態を見分けられる必要があります。機能に応じて判断し、カードのホバールールをインターフェース全体へ当てはめないようにします。

キーボードフォーカスにも、専用の明確な表示が必要です。マウスのホバー表示だけに頼ることはできません。一部の色を共有していても、表示の条件や識別に求められることは異なります。

配色は役割をたどって変更し、やみくもな一括置換は避けます

たとえば主要な操作色をもっと目立たせるなら、私はまず siteAction の利用箇所を探します。そのうえで、ボタンの前景色、ホバー、文字選択、ほかの参照箇所を確認します。プロジェクト内の同じ 16 進数のカラーコードを、すべて一括で置き換える方法は取りません。

反対に、通常のカードで文章を読む背景面を変えたいなら、siteSurface と対応する文字の役割に戻ります。画面に似た白がいくつかあるからといって、すべて一緒に変えるべきだとは判断できません。

このサイトでは、プロダクトの UI を例示するための色も、独立した名前で管理しています。例示用の配色は別の表現のためのものであり、正式なプロダクトの配色全体を示すものではありません。サイトのトークンを少ない色数に見せるためだけに、これらまでサイトのアクション用の色に置き換えるべきではありません。

実際に確認するときは、まず影響を受けるコンポーネントと状態を特定し、出力された CSS とブラウザー上の実際の組み合わせを見ます。ローカル UI コンポーネントの導入と保守には、別の責任もあります。トークンを集約しても、props、HTML のセマンティクス、開閉動作まで自動的に維持されるわけではありません。

このノートには、サイト全体のあらゆる状態のコントラスト測定や、見た目の受け入れ確認、保守コストのデータは含まれていません。示しているのは、配色を変えるときにたどれる役割分担です。まず変更したい役割を把握し、影響を受ける組み合わせを確かめます。一つの新しいカラーコードだけで、デザイン全体を決めないようにするためです。

参考資料

[3] https://tailwindcss.com/docs/theme — Tailwind CSS — Theme variables [4] https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html — W3C — Understanding SC 1.4.3 Contrast (Minimum)