Web制作

CSS変数をトークンとして運用するときの決め方

CSS変数をトークンとして運用するときの決め方

デザインカンプが更新され、メインカラーの青を16か所、余白の数値を30か所修正することになった。検索して一つずつ書き換えたものの、1か所だけ修正が漏れていて、確認の段階で差し戻される。こうした経験は、Web制作の現場で珍しくありません。数値をCSSの中に直接書いていると、デザインの変更があるたびに、同じ作業を繰り返すことになります。

この問題に対して、CSS変数(カスタムプロパティ)を使えば解決する、と考える方も多いと思います。ただ、変数を導入するだけで保守の手間がなくなるわけではありません。カスタムプロパティは、名前の付け方のルールと、値を上書きしてよい範囲を先に決めたときに、修正箇所を減らしやすくなる道具です。

結論からお伝えすると、案件の中で運用が崩れにくいのは、:root に色・余白・文字サイズといったデザイントークンをまとめて定義し、各コンポーネントはそのトークンを参照するだけにする方法です。ページごとに --blue: #00f のような変数を増やしていくと、変数を使っているにもかかわらず、数値を直接書いているのとあまり変わらない状態になります。Lism CSS のように、トークンとユーティリティをあらかじめ用意しているCSSフレームワークもありますが、自前で運用する場合は、名前の付け方と層の分け方を文書にまとめておくことが大切です。

この記事では、フリーランスのWebディレクター兼エンジニアとしての実務をもとに、CSS変数の切り分け方と、運用の中で変数が増えすぎてしまうパターンを整理します。

記事の前半では、変数にしたほうがよい値と、変数にしなくてよい値の見分け方、そして後から追いやすい名前の付け方を解説します。後半では、値の上書きを安全に行う方法、実務での活用方法、運用の中でつまずきやすいポイントを紹介します。新しく案件を立ち上げる場合にも、既存のサイトに少しずつ変数を導入する場合にも、判断の基準として使っていただける内容を目指しています。想定している読者は、コーディングを担当するエンジニアの方に加え、デザインと実装の受け渡しを管理するディレクターやデザイナーの方です。

変数にしたほうがよいものと、しなくてよいもの

CSS変数は便利な仕組みですが、すべての数値を変数にすればよいわけではありません。まずは、変数にすると効果を発揮しやすいものを挙げます。

  • ブランドカラー、背景色、ボーダーの色、文字色
  • 余白や角丸の段階(4px / 8px / 16px など、デザインカンプで使われている尺度)
  • 基本となる文字サイズと行の高さ

これらに共通するのは、サイト全体で繰り返し使われ、デザインの変更によって一斉に変わる可能性があるという点です。こうした値を変数にまとめておけば、変更が必要になったときに、定義している1か所を書き換えるだけで済みます。

一方で、変数にしなくてよいもの、あるいは後回しにしてよいものもあります。

  • 一度しか使わない、特定のセクションだけの 1.27rem のような値
  • アニメーションの途中の値まで、すべてトークンの名前で管理すること
  • JavaScriptでも同じ値を管理していて、片方だけを更新してしまうような運用

一度しか使わない値まで変数にすると、変数の一覧が長くなり、本当に共通で使うべきトークンが見つけにくくなります。変数の数が増えるほど、「この値はどの変数を使えばよいのか」を判断する負担も大きくなります。

また、CSS変数は継承とフォールバックの仕組みにより、親要素から子要素へと値が受け継がれます。親要素で --gap の値を変えると、子要素の gap: var(--gap) の値も変わります。この仕組みは便利ですが、--gap のように意味の広い名前を付けていると、意図していない子孫の要素まで値が変わってしまうことがあります。変数の名前は、どこで使われるのかが分かるように付けることが大切です。

変数にするかどうか迷ったときは、「この値が変わるとき、ほかの箇所も一緒に変わるべきか」を考えてみると判断しやすくなります。一緒に変わるべき値であれば変数にまとめ、その場所だけの事情で決まっている値であれば、直接書いたままにしておきます。すべてを変数にすることを目標にするのではなく、変更が一斉に及ぶ値を見極めることが、変数を増やしすぎないためのコツです。判断に迷う値は、2か所目で使うことになった時点で変数にする、という基準を設けておく方法もあります。

追いにくい名前と、追いやすい名前

追いにくい名前

:root {
  --blue: #1e4b8e;
  --blue2: #163a6e;
  --space: 16px;
}

この書き方では、それぞれの色が本文、リンク、背景のどの役割で使われているのかが分かりません。さらに、次のデザイン変更でメインカラーが緑に変わった場合、--blue という名前のまま中身が緑になり、名前と値が食い違ってしまいます。--blue2 のような連番の名前も、どちらをどの場面で使うのかが判断しにくく、使う人によって選び方がばらつく原因になります。

追いやすい名前

:root {
  --color-text: #1a1a1a;
  --color-text-muted: #5c5c5c;
  --color-surface: #f5f5f3;
  --color-accent: #1e4b8e;
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 16px;
  --space-lg: 24px;
  --font-size-body: 1rem;
  --line-height-body: 1.7;
}

.c-button {
  padding: var(--space-sm) var(--space-md);
  color: var(--color-surface);
  background-color: var(--color-accent);
}

色は「何色か」ではなく「何の役割か」で名前を付け、余白は段階で名前を付けます。こうしておけば、デザインカンプのメインカラーが変わったときも、--color-accent の値を書き換えるだけで済みます。余白の段階を増やす場合も、変数を1行追加すれば対応できます。

もちろん、名前を整えたからといって、すべてのコンポーネントで変数が正しく参照されているとは限りません。数値が直接書かれたまま残っている箇所が見つかることもあります。それでも、名前が役割を表していれば、検索するときのキーワードがはっきりするため、修正漏れを見つけやすくなります。

名前の付け方のルールは、「カテゴリ - 役割 - 段階」の順に並べると整理しやすくなります。--color-text-muted であれば、色(color)の中の、文字(text)の、控えめな(muted)段階という意味になります。この順番をチームで統一しておくと、新しい変数を追加するときにも名前に迷いにくくなります。

値の上書きは、ページ全体ではなく範囲を絞って行う

特定のセクションだけ色や余白を変えたいときは、:root の値を書き換えるのではなく、そのセクションのクラスの中で変数を上書きします。

.p-pricing {
  --color-accent: #0b6b4f;
  --space-md: 20px;
}

この例では、料金表のセクションの中だけ、アクセントカラーと基準の余白を変えています。もしページごとのCSSで :root の値を上書きしてしまうと、ヘッダーやフッターなど、そのページにあるほかの部品の色まで変わってしまいます。これは実務の中でも起きやすい失敗です。変数の上書きは、対象となるクラスの中に閉じ込めるようにします。FLOCSSを採用している場合は Project のクラス、BEMを採用している場合はそのブロックのクラスが、上書きする場所の目安になります。

画面幅に応じて値を切り替える場合も、変数を使えば定義を1か所にまとめられます。

:root {
  --font-size-heading: 1.5rem;
}

@media (min-width: 768px) {
  :root {
    --font-size-heading: 2rem;
  }
}

コンポーネントの側では font-size: var(--font-size-heading) と書いておくだけで、画面幅に合わせて文字サイズが切り替わります。各コンポーネントにメディアクエリを書き足す必要がないため、ブレークポイントでの調整漏れを防ぎやすくなります。

なお、ブレークポイントの数値は、案件のデザインに合わせて決めます。デザインカンプで想定している画面幅が768pxと異なる場合は、コンポーネントを個別に調整する前に、トークンの定義から見直します。値の定義が1か所にまとまっていれば、こうした見直しも短い時間で済みます。

ダークモードへの対応も、同じ考え方で進められます。prefers-color-scheme のメディアクエリや、data-theme のような属性の中で色のトークンだけを上書きすれば、コンポーネントのCSSには手を加えずに配色を切り替えられます。ここでも、色の名前が「青」や「白」ではなく「文字」や「背景」といった役割で付けられていることが前提になります。役割で名前を付けておけば、配色が反転しても名前と中身が食い違うことはありません。上書きの場所が決まっていれば、配色の確認もその範囲だけで済みます。

実務での3つの活用方法

1. キックオフの段階でトークン表を1枚用意する

案件の初期に、色を5〜8種類、余白を4〜6段階、文字サイズを3〜5段階程度にまとめたトークン表を作っておきます。種類が多すぎると、デザイナーとエンジニアの間で名前の解釈がずれやすくなります。Figma の変数名と、CSSの -- から始まる変数名を揃えておくと、デザインと実装の対応が分かりやすくなります。名前を揃えられない場合は、対応表を1枚作っておきます。

2. 既存のマジックナンバーは、修正するときに少しずつ変数にする

既存のサイトにCSS変数を導入する場合、すべての数値を一度に変数へ置き換えると、変更の差分が大きくなり、レビューも難しくなります。修正を依頼されたコンポーネントから順に、色と余白を変数に置き換えていくほうが、影響範囲を確認しながら進められます。一括で置き換えることよりも、置き換えた変数が正しく参照されているかを確認することを優先します。

3. CSSフレームワークのトークンに合わせる

Lism CSS のように、余白や文字サイズのトークンをあらかじめ用意しているフレームワークを使う場合は、フレームワークのトークン名を優先して使います。案件独自の値が必要なときは、フレームワークのトークンと混ざらないよう、自前の変数として分けて定義します。フレームワークのトークンと --blue のような独自の変数が同じ場所に並んでいると、どちらを使うべきかが分かりにくくなります。

AIにCSSを書いてもらうときも、トークン表が役立ちます。次のように、使ってよい変数を明示して依頼すると、出力が揃いやすくなります。

## 依頼
料金表のカードのCSSを書いてください。

## 使用できる変数
- 色: --color-text / --color-surface / --color-accent
- 余白: --space-sm / --space-md / --space-lg

## 制約
- 色と余白は数値を直接書かず、上記の変数を使う

ただし、指示を整理しても、一覧にない変数や直接書かれた数値が含まれることはあるため、生成されたコードは人が確認します。「CSSを書いて」とだけ伝えた場合と比べると、確認する観点がはっきりするため、レビューの時間も短くなりやすくなります。トークン表は、デザイン・実装・AIへの指示のすべてで共通の基準として使えます。

現場でつまずきやすいポイント

CSS変数を運用していると、次のような場面でつまずくことがあります。

  • 定義されていない変数を参照してしまう: var(--color-accent) の変数が定義されていないと、そのプロパティは指定されていない状態になり、意図した色が表示されません。var(--color-accent, #1e4b8e) のように代わりの値(フォールバック)を書いておくか、:root に定義が必要な変数の一覧をレビューで確認します。
  • コンポーネント内の一時的な変数と、トークンに同じ名前を付ける: コンポーネントの中で --gap を定義すると、外側で定義されたトークンの --gap が上書きされて見えなくなります。--c-button-gap と --space-md のように、接頭辞で用途を分けておきます。
  • JavaScriptの setProperty とCSSファイルの両方で値を更新する: ダークモードの切り替えなど、動的にテーマを変える場合は、どちらで管理している値が基準なのかを一文で残しておきます。
  • @property による型の登録を、必要になる前にすべて書く: @property は、変数の値をアニメーションさせたい場合や、継承を制御したい場合に使う仕組みです。トークン表の代わりになるものではないため、必要な場面が出てきてから導入を検討します。

一次情報としては、MDNのカスタムプロパティのページが参考になります。トークンの設計について書籍で学ぶ場合は、チームで同じ本を参照するようにすると、名前の付け方の考え方を揃えやすくなります。

もう一つ注意したいのが、変数を削除するときの扱いです。使われなくなった変数をそのまま残しておくと、一覧が長くなるだけでなく、新しく参加した人が「この変数はまだ使ってよいのか」と迷う原因になります。変数を削除する際は、プロジェクト全体で参照されていないことを検索で確かめてから削除し、変更の理由をコミットのメッセージに残しておきます。定期的に変数の一覧を見直す時間を設けておくと、トークン表と実際のCSSのずれを小さく保ちやすくなります。見直しの際は、トークン表の側も同時に更新しておきます。

まとめ:変数は、名前のルールがあってこそ役に立つ

CSS変数は、数値を一時的に退避させておくための場所ではありません。役割を表す名前を先に決め、コンポーネントからはその変数を参照するだけにする。値を上書きするときは、対象のブロックの範囲内に限定する。この3つのルールが揃うと、デザインカンプで色が変わったときも、:root の数行を書き換えるだけで対応できるようになります。

もし色の変更が数行の修正で済まない場合は、どこかに数値が直接書かれたまま残っている可能性があります。その箇所を見つけて変数に置き換えていくことが、次の変更をより楽にするための作業になります。最初からすべてを完璧に整える必要はありません。トークン表を1枚用意し、修正のたびに少しずつ変数に置き換えていくことで、CSSは徐々に保守しやすい状態に近づいていきます。

変数を導入する目的は、変数の数を増やすことではなく、変更に強いCSSを作ることです。名前のルールと上書きの範囲をチームで共有し、誰が触っても同じ基準で修正できる状態を目指してみてください。

最後に、この記事の要点を整理します。

  • 変数にするのは、サイト全体で繰り返し使われ、一斉に変わる可能性がある値に絞る
  • 色は「何色か」ではなく「何の役割か」で名前を付け、余白は段階で名前を付ける
  • 値の上書きは :root ではなく、対象のブロックのクラスの中で行う
  • 既存のサイトでは、修正するコンポーネントから順に変数へ置き換える

CSS変数の運用は、最初に決めたルールをチームで守り続けることで効果が積み重なっていきます。トークン表をデザイナーとエンジニアの共通の資料として扱い、デザインの変更があったときにはまずトークン表から見直す。この流れが定着すると、修正のたびに数値を探し回る作業を減らしやすくなります。

まずは、今関わっている案件のCSSで、メインカラーのカラーコードを検索してみてください。同じ値が何か所に直接書かれているかを数えるだけでも、変数にまとめる効果がどれくらいありそうか、そしてどのコンポーネントから置き換えを始めるべきかが見えてくるはずです。

トークン表の作り方、既存のCSSから変数への移行手順、CSSフレームワークの変数との切り分け方についてのご相談は、相談・お問い合わせからお気軽にどうぞ。