MicroAd Developers Blog

マイクロアドのエンジニアブログです。インフラ、開発、分析について発信していきます。

保守運用を楽にするデザイントークン導入

マイクロアドでフロントエンドエンジニアをしている大石です。 フロントエンド領域で欠くことのできない"デザイン"に課題はありませんか? 仮実装フェーズから最終成果物まで、非デザイナーでもデザインを行うケースは多々あります。

非デザイナーのフロントエンドエンジニアでも、根拠を持ってUIを組み立てられる仕組みがあると開発が一気に楽になります。 今回は、Vue.jsを採用しているプロダクトでデザイントークンを導入し、デザインと開発のワークフローを改善したプロセスを共有します。

TL;DR

  • 課題: デザインプロセスに明確なルールがなく、スタイルの一貫性とアクセシビリティが崩れていた
  • 解決策: デザイントークンを導入し、トークン中心にデザインすることで迷いなく一貫性を持ったデザインをチーム全体で統一化
  • 結果: 意思決定コストが下がり、アクセシビリティとUIの一貫性が向上

1. プロダクトの状況(デザイントークン導入前)

前提として私たちのチームには専任のデザイナーがいません。 そのためエンジニア自身がUI・デザインを提案から実装まで行っています。

デザイントークン導入以前では、色管理は以下のようなTypeScriptファイルで行われていました。 コンポーネント内からは、Vueの v-bind 機能を利用してスタイルを適用する仕組みです。

// カラーパレットの定義例
const baseColorTable = {
  green: "#A4E000",
  blue: "#00C0E2",
  // ...他多数
};

export const lightTheme: ColorRoles = {
  commandColor: baseColorTable.green,
  backgroundAccentColor: baseColorTable.ivory,
  // ...
};
<style scoped lang="sass">
  .content
    background: v-bind('lightTheme.backgroundBaseColor')
</style>

定数定義はあるものの、一部でマジックナンバー(直接書かれた数値や色コード)が使用されており、全体として統一されていませんでした。

また、色以外の値についても定数管理されていませんでした。 style内で直接記述されたり、以下のようにVue/TSファイル内で個別に定義されたりと、非常に混沌とした状態でした。

/** パディング情報 */
const padding = computed(() => {
  switch (buttonConfigType.size) {
    case ButtonSize.TINY:
      return "4px 10px";
    case ButtonSize.SMALL:
      return "8px 16px";
    case ButtonSize.MEDIUM:
      return "12px 24px";
    case ButtonSize.LARGE:
    default:
      return "16px 48px";
  }
});

この手法が抱えていた問題点

規模が拡大するにつれて以下のような課題が顕在化しました。

  • カラーパレットの不足:
    • 定義されている色数が少なく、UIの多様な要素に対応しきれていませんでした。
    • また、追加するためのルールも曖昧で、マジックナンバーが発生したり、似たような色が乱立したりしていました。
  • アクセシビリティ(コントラスト比)の軽視:
    • 色の組み合わせが定義されていないため、背景色と文字色のコントラスト比がWCAG基準を満たしていない箇所が多く存在していました。
  • デザインツールとの同期コスト:
    • Figma上のコンポーネントと、実装されているコンポーネントのスタイルが一致していないため、要件定義から実装の間で微調整が必要でした。
  • 色以外の管理の欠如:
    • 余白、角丸、フォントサイズといった値は管理されておらず、コード内にマジックナンバーが点在していました。
    • そのため、実装者の「感覚」によるスタイリングに委ねられ、メリハリがなく統一感に欠けるUIが量産されていました。
  • Sass本来の機能が使えない:
    • v-bind は実行時にCSS変数を介して値を注入するため、Sassのビルド時に行われる darken()rgba() といった関数が利用できない状態でした。

2. デザイントークンとは

デザイントークンとは、色、タイポグラフィ、スペーシングといったデザインを構成する最小単位(原子)に、抽象的な名前を付けたものです。

単なる変数の定義と異なる最大の特徴は、「デザインの意図」をカプセル化している点にあります。 具体的には、単に #3B82F6$blue と呼ぶのではなく、$color-button-primary-background と命名することで、「これはメインボタンの背景色」という情報をコードとデザインの両方に持たせることができます。

具体的には、以下のような重要な役割を果たします。

  • プラットフォームを超えた共通言語:
    • Web, iOS, Android など、異なる技術スタック間でも同じ名前でスタイルを同期できる「信頼できる唯一の情報源(Single Source of Truth)」となる
  • デザイン変更への柔軟な対応:
    • トークンの値を一箇所変更するだけで、プロダクト全体のスタイルを一貫性を保ったまま更新が可能。ダークモードへの対応なども、トークンの参照先を切り替えるだけで実現可能
  • コミュニケーションの効率化:
    • デザイナーとエンジニアが具体的な数値(16px, #000000 など)ではなく、トークン名で会話できるようになり、認識の齟齬が減少する

3. 設計コンセプト

非デザイナーでも迷わない「シンプルさ」

専任デザイナーが不在のため、高機能さよりも「わかりやすく使いやすい」シンプルさを最優先しました。 後述するコンポーネントトークンはなるべく使わず、汎用的なセマンティックトークンを中心に設計しています。

既存デザインの尊重と「守りの改善」

大規模なリニューアルではないため、可能な限り既存のデザイン(色味)には変更を加えない方針としました。 一方で、コントラスト不足については、WCAG基準をクリアできるよう適宜調整し、アクセシビリティを向上させています。

ミニマムスタートと継続的調整

最初から完璧を目指さず、運用しながら調整していくアジャイルな手法をとっています。

4. 導入を支えるツールチェーン

「デザインが正(Source of Truth)」な状態を維持するため、以下のツールチェーンを構築しました。

  • Figma Tokens Studio (FTS):
    • Figma上でトークンを管理
  • Style Dictionary:
    • FTSが出力したJSONを、Sass変数として出力
    • 今回はWebアプリケーションのみだが、複数のフォーマットへ自由に変換することができる

Figmaでのプロトタイプ製作と、Web実装の両方で同じトークンを参照できるようにすることで、デザインとコードの同期を自動化しています。

5. デザイントークンのレイヤー構成と命名規則

レイヤー構成

初期段階は、以下の2レイヤー構成でスタートしました。

  • プリミティブトークン: 色やサイズの絶対値を定義。メインカラーから11段階のスケールをシステム的に生成しました。
  • セマンティックトークン: 「Primary」「Error」「Surface」「Content」といった役割に応じた定義。

他にもグローバルトークン、エイリアストークンなど、組織によって様々な呼び方が存在します

セマンティックトークンに加えて、コンポーネントごとのコンテキストに応じたコンポーネントトークンを設ける場合があります。 しかし、コンポーネントトークンは設計と運用の複雑性が増すため、初期段階では導入せず、代わりにセマンティックトークンを充実させることで対応しています。

また、セマンティックトークンの命名規則については以下のような4層構造で定義しています。

サイズ設計

padding, margin, gap など大きさを扱うスペーストークンは、まず8pxの倍数を採用しました。 主要なブラウザのデフォルトフォントサイズが16pxなことや、主要なディスプレイサイズが8の倍数なことから、相性がよく扱いやすい値だからです。

トークン間の数値の間隔は、2px・4px・8pxなど小さい値では細かく刻み、 128px, 160px, 192pxなど大きな値ではジャンプ率を大きく設定しています。 これは「8px→16pxでは変化が大きく感じるのに対し、160px→168pxではほぼ同じに見える」というウェーバー・フェヒナーの法則(視覚的な認知特性)に基づいています。

サイズトークンはセマンティックトークンを設けず、プリミティブトークンを直接使用しています。 利用がある程度進んだ段階で集計し、セマンティックトークン化へ再設計する予定です。

カラー設計

色に関しては、まず既存で使用されている色をざっくりと集計しました。 grepでコードから色コードを抽出し、GAS(Google Apps Script)でセルの色を変更して可視化を行いました。

ここから頻出している色を基準とし、11段階のカラーパレットを作成しました。

原則として既存のデザインは可能な限り変更しない方針のため、基準となる色はそのまま残し、基準と同系色の他の色を近しい色に統合する形をとりました。

#00C0E2: 基準色(そのまま使用)
#B7F9FA: カラーパレットから近しい色を使用

機械的に作成したパレットでは明度にばらつきがあり、そのまま使用するには課題が残っています(図参照)。

この課題を認識しつつ、まずは実用性を優先してセマンティックトークンを定義しました。 ユースケースを複雑にしすぎないよう、以下の4パターンに絞って作成しています。

  • 文字単体での使用
  • 明るい背景色のコンポーネント
  • 中間の背景色のコンポーネント
  • 暗い背景色のコンポーネント

各ユースケースにおいて、背景色・ボーダー・文字色、そしてそれぞれの状態(ホバー時など)について、コントラスト比を考慮しながらトークンを設計しました。

その他にも、フォントサイズ、z-index、シャドウ、といったCSSで使う値もトークン化しています。

6. デザイントークン化の実装

FTSから出力されたJSONをStyle DictionaryでSass変数に変換し、コンポーネントから参照しています。

<style scoped lang="sass">
  .button
    &--primary
      background-color: $color-primary-background
      border-color: $color-primary-border
      color: $color-primary-background-content

      &:hover
        background-color: $color-primary-background-hover
        border-color: $color-primary-border-hover
</style>

デザイントークン化の段階では既存コンポーネントの書き換えは行わず、機能追加や改修のタイミングで徐々にトークンへ置き換えるようにしています。

7. 導入によって得られたメリット

デザイントークン化により、開発効率とプロダクト品質の両面で大きな変化がありました。

  • 意思決定コストの削減:
    • 「背景は何色?」「余白は16px?それとも20px?」といった微細な迷いがなくなる
    • セマンティックトークン(例: $color-accent-background-surface)を選ぶだけで、意図に沿った実装が完結
  • アクセシビリティの自動担保:
    • トークン自体にWCAG準拠の調整を組み込んだため、トークンを使うだけで視認性の高いUIが作れる「ガードレール」が整った
  • 技術的な制約の解消:
    • JSオブジェクトによるランタイム管理から静的なSass変数へ移行
    • これによりSassの強力な関数群が再び利用可能になり、ビルドプロセスの健全性が高まった
  • 一貫性の維持:
    • デザイナー不在でも、一度決めた「8pxグリッド」や「11段階パレット」のルールから逸脱しにくくなり、UIの断片化が劇的に減少

8. 今後の課題

現在は以下の課題に取り組んでいます。

  • デザイントークンへの置き換え: 運用しながら徐々に既存コードをトークン化する
  • パレットのアクセシビリティ向上と再定義: 機械的生成によるコントラストの不一致を解消。将来的に色全体を刷新するフェーズで再調整予定
  • トークンの拡充: 運用上必要になったトークンを適宜定義し、不足を補完していく

まとめ

専任デザイナーがいないからこそ、デザイントークンという「共通言語」を持つメリットは絶大です。既存デザインを尊重しつつ、アクセシビリティ基準のクリアやパレットの体系化を行ったことで、エンジニアが「根拠を持って値を選べる」環境が整いました。