MENU

デザインシステムのトークン管理:CSS変数実装

目次

この記事でわかること:トークン管理の全体像と要点

デザインシステムを構築・運用する現場では、デザインとコードの乖離が慢性的な課題になりがちです。デザイナーがFigmaで更新した色やスペーシングが、エンジニア側のコードに反映されるまでにタイムラグが生じたり、手作業による転記ミスが混入したりするケースは珍しくありません。この問題を解決する鍵が、デザイントークンとCSS変数(カスタムプロパティ)の組み合わせです。

デザイントークンとCSS変数を組み合わせる意義と効果

デザイントークンとは、色・タイポグラフィ・スペーシングといったUIの視覚的な値を、特定のプラットフォームやツールに依存しない形で定義したものです。これをCSS変数として実装することで、デザインの意図をそのままコードに落とし込めます。たとえば --color-primary: #0057FF; のように定義しておけば、デザイナーが色を変更した際にこの1箇所を書き換えるだけで、システム全体へ変更が波及します。結果として、コンポーネントの一貫性が保たれ、テーマ変更やブランドアップデートのコストを大幅に削減できます。

Figmaからコードへの自動連携フローの概要

現代のワークフローでは、Figma上で定義したトークンをJSONファイルとしてエクスポートし、変換ツール(Style Dictionaryなど)を通じてCSS変数として出力するパイプラインが主流になりつつあります。この仕組みが整えば、デザイナーがFigmaでトークンを更新するだけで、GitHubへの自動コミットを経てコードベースへ反映される自動化フローが実現できます。

マルチテーマ対応を見据えた設計の考え方

ライトモード・ダークモードの切り替えや、ブランドテーマの複数運用を見据えると、設計段階からトークンの階層構造を意識することが不可欠です。具体的な値(Primitiveトークン)と意味的な役割(Semanticトークン)を分離することで、テーマ切り替えの際にSemanticトークンが参照先を変えるだけで済む、柔軟なアーキテクチャを構築できます。

CSS Variablesによるトークン管理の基礎

CSS変数(正式名称:CSSカスタムプロパティ)は、現代のデザインシステム実装において事実上の標準的な手法となっています。Sass変数やJSのテーマオブジェクトと異なり、ビルドステップを経ずにブラウザのランタイムで値を動的に変更できる点が、デザインシステムとの相性を高めています。

CSS変数の宣言と参照:基本的な書き方

:root セレクタを使うと、ドキュメント全体からアクセス可能なグローバルトークンを定義できます。下記のように記述するのが基本形です。

:root {
  --color-primary: #0057FF;
  --color-secondary: #6B7280;
  --color-neutral-900: #111827;
  --spacing-xs: 4px;
  --spacing-sm: 8px;
  --spacing-md: 16px;
  --spacing-lg: 24px;
  --font-size-base: 16px;
  --font-weight-regular: 400;
  --font-weight-bold: 700;
}

定義したトークンは var(--color-primary) で参照できます。第2引数にフォールバック値を指定することも可能で、var(--color-primary, #0057FF) のように書くと、変数が未定義の場合に代替値が適用されます。フォールバック値は、外部ライブラリのコンポーネントを取り込む際や、段階的なトークン移行フェーズで特に有効です。

CSS変数がデザインシステムに適している理由

  • ビルド不要でランタイムに値を切り替えられる:Sass変数やPostCSSはビルド時に値が固定されますが、CSS変数はブラウザ上で動的に変更可能です。これにより、ユーザーの操作に応じてテーマをリアルタイムに切り替えられます。
  • JavaScriptからのアクセス:getComputedStyle() や setCSSVariable() を使ってJavaScriptからトークン値を読み書きできるため、動的なテーマ切り替えやアニメーション制御が容易になります。
  • カスケードを活かしたコンポーネントスコープの上書き:グローバルの :root で定義したトークンを、特定のコンポーネント内でローカルに上書きできます。例えば、ボタンの特定の状態だけ色を変えたい場合、.btn--disabled { --color-primary: #D1D5DB; } と書くだけで済みます。

デザイントークンの定義と分類:階層設計のベストプラクティス

トークンを効果的に管理するには、Primitive・Semantic・Componentの3層構造で整理することが推奨されます。

Primitive トークン:基盤となる生の値

プロジェクト全体で使用される色やサイズの基本パレットです。変更頻度は低く、ブランドの基本要素に当たります。

{
  "color": {
    "blue": {
      "50": "#EFF6FF",
      "500": "#3B82F6",
      "900": "#1E3A8A"
    },
    "gray": {
      "900": "#111827",
      "500": "#6B7280"
    }
  },
  "spacing": {
    "4": "4px",
    "8": "8px",
    "16": "16px"
  }
}

Semantic トークン:意味を持つ抽象化レイヤー

Primitiveトークンを参照しながら、用途に応じた命名を施します。テーマ切り替え時に価値を発揮します。

{
  "color": {
    "primary": "{color.blue.500}",
    "text-primary": "{color.gray.900}",
    "bg-light": "{color.gray.50}"
  },
  "spacing": {
    "component-padding": "{spacing.16}"
  }
}

Component トークン:コンポーネント固有の値

ボタンやカードなど、特定のコンポーネント向けの詳細なトークンです。他のコンポーネントとの共有はありません。

マルチテーマ対応の実装パターン

ライトモード・ダークモードを切り替える仕組みを実装する場合、Semanticトークンの参照先を変えるだけで済む設計が重要です。

ダークテーマの定義と切り替え実装

/* ライトモード(デフォルト) */
:root {
  --color-text: #111827;
  --color-bg: #FFFFFF;
  --color-primary: #0057FF;
}

/* ダークモード */
[data-theme="dark"] {
  --color-text: #F3F4F6;
  --color-bg: #1F2937;
  --color-primary: #60A5FA;
}

/* 実装例 */
body {
  color: var(--color-text);
  background-color: var(--color-bg);
}

button {
  background-color: var(--color-primary);
}

テーマ切り替えはJavaScriptで行います。

function setTheme(themeName) {
  document.documentElement.setAttribute("data-theme", themeName);
  localStorage.setItem("theme", themeName);
}

// ページ読み込み時に保存されたテーマを復元
const savedTheme = localStorage.getItem("theme") || "light";
setTheme(savedTheme);

トークン定義ファイルの自動生成ツール:Style Dictionaryの活用

JSONで定義したデザイントークンを、CSS変数・SCSS変数・JavaScriptモジュールなど複数フォーマットに自動変換するツールとして、Style Dictionary(Amazon製)が広く使われています。一度設定を行えば、トークンの値を変更するだけで全プラットフォーム向けのファイルを一括再生成できるため、手作業による転記ミスを防げます。

Style Dictionaryのセットアップ

npm install --save-dev style-dictionary

プロジェクトルートにconfig.jsonを作成します。

{
  "source": ["tokens/**/*.json"],
  "platforms": {
    "css": {
      "transformGroup": "css",
      "buildPath": "dist/css/",
      "files": [{ "destination": "variables.css", "format": "css/variables" }]
    },
    "js": {
      "transformGroup": "js",
      "buildPath": "dist/js/",
      "files": [{ "destination": "tokens.mjs", "format": "javascript/es6" }]
    }
  }
}

npx style-dictionary buildを実行するとビルドが走り、dist/以下に各フォーマットのファイルが生成されます。

トークン更新時のワークフロー:Git連携とCI/CD

Figmaから自動的にトークンJSONをエクスポートし、GitHubへの自動プッシュまでを実現するワークフローが、現代的な運用スタイルです。

Figma ✓ JSONエクスポート ✓ Git自動コミット ✓ CI/CDパイプラインの流れ

Tokens StudioやFigma のAPIを利用して、定期的にトークンをJSONとしてエクスポートする仕組みを構築できます。GitHubのWorkflowでこれを自動化すれば、デザイナーの更新がコード側へシームレスに反映されます。

# .github/workflows/sync-tokens.yml
name: Sync Tokens
on:
  schedule:
    - cron: "0 9 * * *"  # 毎日9時に実行
jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Export tokens from Figma
        run: npx figma-tokens-cli export
      - name: Build with Style Dictionary
        run: npx style-dictionary build
      - name: Commit and push
        run: |
          git config user.name "Token Bot"
          git add tokens/ dist/
          git commit -m "chore: update design tokens"
          git push

このパイプラインが整えば、デザイナーがFigmaで色を変更すると、翌朝には本番コードへ反映される自動化が実現できます。

実装時の注意点と落とし穴

CSS変数のパフォーマンス問題

CSS変数は計算コストが小さいとはいえ、数千個の変数を使用したり、複雑なカスケード構造を作ったりすると、ブラウザの計算時間が増加します。特にアニメーション中のリペイントが頻繁に発生する場合は注意が必要です。実測では、10,000個以上の変数を持つページでは計測可能な遅延が生じる報告があります。対策としては、グローバルスコープに必要最小限のトークンに留め、コンポーネント固有の値はローカルスコープで定義することが推奨されます。

命名規則の統一

チームの規模が大きくなるにつれ、トークンの命名が一貫性を欠きやすくなります。前もって命名規則をドキュメント化し、自動チェックツール(ESLintプラグインなど)を導入することで、ヒューマンエラーを防げます。

Figmaとコードの乖離防止

Figma Variablesはデザイナー向けの機能が充実していますが、コードとの連携に完全な自動化があるわけではありません。定期的に両者の整合性を確認するレビューフローを整備することが重要です。

まとめ

デザイントークンとCSS変数を組み合わせることで、デザインとコードの乖離を防ぎ、UIの一貫性を保ちながら変更コストを削減できます。Primitive・Semantic・Componentの3層構造でトークンを設計し、Style Dictionaryで自動生成する仕組みが、現在の実務スタンダードです。

マルチテーマ対応は data-theme 属性とCSS変数を組み合わせることで実装でき、ライトモード・ダークモードの切り替えもJavaScriptで動的に制御できます。Figmaとコードを自動連携させるパイプラインを構築すれば、デザイナーの更新がそのままコードへ反映される効率的なワークフローが実現できます。

運用フェーズでは、命名規則の統一、パフォーマンスチューニング、定期的な整合性確認が、長期的な品質維持につながります。小規模なスコープから始めて、段階的に自動化フローを拡張していくアプローチが、チームへの導入負荷を抑えつつ着実に成功させる現実的な進め方です。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次