🎨

デザイントークンの仕様を覗いてみる

に公開
1

はじめまして、株式会社バニッシュ・スタンダードでフロントエンドエンジニアをやっているいっきゅうです。よろしくお願いします。

今回は、「デザイントークン」をテーマに記事を書いてみようと思います。

はじめに

「デザイントークン」という言葉を聞くけれど、実際に何なのか、どう使うのかがよくわからない方も多いのではないでしょうか。
この記事では、2025年4月18日に公開されたW3C Design Tokens Community Groupliving-draftを参考に、デザイントークンの要点から実際の作成方法まで、初心者の方にもわかりやすく解説します。

前提条件

この記事はこんな方を想定しています。

  • JSON形式の基本的な読み書きができる
  • Web開発またはデザインシステムの基本用語を知っている
  • CSSの基本がわかる
  • npm/Node.jsの基本操作を知っている

デザイントークンとは

デザイントークンは、一言で言うと「色や余白、文字サイズなどのデザインの最小単位」です。

仕様書では以下のように説明されています

Design tokens are indivisible pieces of a design system such as colors, spacing, typography scale.

Design tokens were created by the Salesforce design system team, and the name comes from them (Jon & Jina).

また、技術やプラットフォームに依存しない表現方法です。これにより、デザイナー・エンジニア・プロダクトマネージャーなど、異なる職種や部署の人たちが同じデザイン情報を共有できます。

ファイル形式

デザイントークンはJSON形式で記述されます。JSONは、多くのプログラミング言語がサポートしており、広く普及しているため採用されました。

ファイルの拡張子は、以下が推奨されています

  • .tokens
  • .tokens.json

基本構造

デザイントークンは、少なくとも トークン名 をキーとし、$value に値を持つ構成が必要です。

{
  "トークン名": {
    "$value": "値"
  }
}

また、プロパティは、いくつか追加できます。以下、表でまとめました。

プロパティ名 必須 説明
$value トークンの値を指定します
$description トークンの説明を文字列で指定します
$type トークンのタイプを指定します(ex. color)
$extensions ツール固有の拡張データを追加できます
$deprecated 非推奨としてマークします

特徴は以下の通りです。

  • トークン名は大文字と小文字が区別される
  • プロパティ名の接頭辞として$が付く

タイプ

以下のタイプが定義されています。

タイプ 用途
color
dimension サイズ
fontFamily フォント名
fontWeight フォントの太さ
duration アニメーションの時間
cubicBezier 3次ベジェ曲線(cssのcubic-bezier()関数と同じ)
number 数値

文書化されてないタイプもあります。詳細はAdditional typesをご覧ください。

詳細

紹介されている例から、よく利用するタイプを抜粋し、どんな値を指定しているのか見ていきます。

color

{
  "Hot pink": {
    "$type": "color",
    "$value": {
      "colorSpace": "srgb",
      "components": [1, 0, 1],
      "alpha": 1,
      "hex": "#ff00ff"
    }
  }
}
プロパティ名 必須 説明
colorSpace color space または color model を指定します
components 色を表現する値を配列で指定します。要素は、数値 or none を指定します
alpha 色のアルファ値を指定します
hex 色のフォールバックを指定します(6桁の16新数カラーで指定する必要がある)

colorSpaceはいくつか指定できますが、基本は srgb で問題ないでしょう。

dimension

{
  "spacing-stack-0": {
    "$value": {
      "value": 0,
      "unit": "px"
    },
    "$type": "dimension"
  },
}
プロパティ名 必須 説明
value 数値で指定します。(小数点入力も可能)
unit 単位を指定します。指定できる値は、px or remのみ

エイリアス/リファレンス

デザイントークンは値の代わりに、他のトークンの値を参照できます。
よって、他のデザイントークンのエイリアスを定義できます。

{
  "group name": {
    "token name": {
      "$value": 1234,
      "$type": "number"
    }
  },
  "alias name": {
    "$value": "{group name.token name}"
  }
}

複合型(Composite types)

上記で説明したトークンは、1つの値のみを持つ単純な構造です。
一方、複合型は複数の関連する値を組み合わせて、1つのトークンとして定義されます。

{
  // shadow
  "shadow-token": {
    "$type": "shadow",
    "$value": {
      "color": { // サブ値
        "$type": "color",
        "$value": {
          "colorSpace": "srgb",
          "components": [0, 0, 0],
          "alpha": 0.5,
          "hex": "#000000"
        }
      },
      "offsetX": { "value": 0.5, "unit": "rem" }, // サブ値
      "offsetY": { "value": 0.5, "unit": "rem" }, // サブ値
      "blur": { "value": 1.5, "unit": "rem" }, // サブ値
      "spread": { "value": 0, "unit": "rem" } // サブ値
    }
  },
  // typography
  "heading-level-1": {
    "$type": "typography",
    "$value": {
      "fontFamily": "Roboto", // サブ値
      "fontSize": { // サブ値
        "value": 42,
        "unit": "px"
      },
      "fontWeight": 700, // サブ値
      "letterSpacing": { // サブ値
        "value": 0.1,
        "unit": "px"
      },
      "lineHeight": 1.2 // サブ値
    }
  },
}

複合型の特徴

  • 複数のサブ値から構成される
  • 事前定義された構造に従う値の組み合わせ

グループ(Groups)

関連するトークンはグループにまとめて階層化できます。
グループは、任意で$descriptionプロパティを含めることができ、説明が追加できます。

{
  "Group of tokens": {
    "$description": "This is an example of a group containing a nested group",
    "Subgroup of tokens": {
      "Token 1 name": {
        "$type": "color",
        "$value": {
          "colorSpace": "srgb",
          "components": [0.667, 0.733, 0.8]
        }
      },
      "Token 2 name": {
        "$type": "color",
        "$value": {
          "colorSpace": "srgb",
          "components": [0.867, 0.933, 1]
        }
      }
    }
  }
}

デザイントークンを定義してみよう

実際に紹介したデータタイプを使って、シンプルなデザイントークンファイルを作成してみましょう。

定義したデザイントークン
{
  "primitive": {
    "$description": "他のトークンの基礎となる最小単位のトークンです。",
    "color": {
      "white": {
        "$type": "color",
        "$value": {
          "colorSpace": "srgb",
          "components": [1, 1, 1],
          "alpha": 1,
          "hex": "#ffffff"
        }
      },
      "blue-500": {
        "$type": "color",
        "$value": {
          "colorSpace": "srgb",
          "components": [0.376, 0.008, 0.933],
          "alpha": 1,
          "hex": "#6002ee"
        }
      },
      "gray-800": {
        "$type": "color", 
        "$value": {
          "colorSpace": "srgb",
          "components": [0.278, 0.278, 0.278],
          "hex": "#474747"
        }
      },
      "gray-900": {
        "$type": "color",
        "$value": {
          "colorSpace": "srgb",
          "components": [0.133, 0.133, 0.133],
          "hex": "#222222"
        }
      }
    },
    "spacing": {
      "xs": {
        "$type": "dimension",
        "$value": { "value": 4, "unit": "px" }
      },
      "sm": {
        "$type": "dimension",
        "$value": { "value": 8, "unit": "px" }
      },
      "md": {
        "$type": "dimension", 
        "$value": { "value": 16, "unit": "px" }
      }
    },
    "typography": {
      "fontFamily": {
        "inter": {
          "$type": "fontFamily",
          "$value": ["Inter", "-apple-system", "sans-serif"]
        }
      },
      "fontSize": {
        "md": {
          "$type": "dimension",
          "$value": { "value": 16, "unit": "px" }
        }
      },
      "lineHeight": {
        "md": {
          "$type": "dimension",
          "$value": { "value": 24, "unit": "px" }
        }
      },
      "fontWeight": {
        "regular": {
          "$type": "fontWeight",
          "$value": 400
        },
        "semiBold": {
          "$type": "fontWeight",
          "$value": 600
        }
      }
    },
    "shadow": {
      "elevation1": {
        "$type": "shadow",
        "$value": {
          "color": "{primitive.color.gray-800}",
          "offsetX": { "value": 0, "unit": "px" },
          "offsetY": { "value": 2, "unit": "px" },
          "blur": { "value": 4, "unit": "px" },
          "spread": { "value": 0, "unit": "px" }
        }
      }
    }
  },
  "semantic": {
    "$description": "デザイン上の意味や役割を持つトークンです。",
    "color": {
      "brand-primary": {
        "$type": "color",
        "$value": "{primitive.color.blue-500}",
        "$description": "ブランドを象徴する主要なカラーです。"
      },
      "text-primary": {
        "$type": "color",
        "$value": "{primitive.color.gray-900}",
        "$description": "主に本文などの基本テキストに使用する色です。"
      }
    },
    "typography": {
      "body": {
        "$type": "typography",
        "$value": {
          "fontFamily": "{primitive.typography.fontFamily.inter}",
          "fontSize": "{primitive.typography.fontSize.md}",
          "lineHeight": "{primitive.typography.lineHeight.md}",
          "fontWeight": "{primitive.typography.fontWeight.regular}"
        }
      },
      "button": {
        "$type": "typography",
        "$value": {
          "fontFamily": "{primitive.typography.fontFamily.inter}",
          "fontSize": "{primitive.typography.fontSize.md}",
          "lineHeight": "{primitive.typography.lineHeight.md}",
          "fontWeight": "{primitive.typography.fontWeight.semiBold}"
        }
      }
    },
    "shadow": {
      "button": {
        "$type": "shadow",
        "$value": "{primitive.shadow.elevation1}"
      }
    }
  },
  "component": {
    "$description": "特定のUIコンポーネント専用のトークンです。",
    "button": {
      "primary": {
        "typography": {
          "$type": "typography",
          "$value": "{semantic.typography.button}"
        },
        "shadow": {
          "$type": "shadow",
          "$value": "{semantic.shadow.button}"
        },
        "backgroundColor": {
          "$type": "color",
          "$value": "{semantic.color.brand-primary}"
        },
        "textColor": {
          "$type": "color",
          "$value": "{primitive.color.white}"
        }
      }
    }
  }
}

Style Dictionaryを使った検証方法

デザイントークンの妥当性を検証するために、Style Dictionaryを使用した検証環境を構築してみましょう。
検証にはNode.jsを使用します。

検証用プロジェクト: design-tokens-example

1. インストール

npm init -y
npm install --save-dev style-dictionary

2. 設定ファイルを作成

touch config.json
config.json
{
  "source": ["tokens/**/*.json"],
  "platforms": {
    "ts": {
      "transformGroup": "js",
      "buildPath": "build/ts/",
      "files": [
        {
          "format": "javascript/es6",
          "destination": "tokens.js"
        },
        {
          "format": "typescript/es6-declarations",
          "destination": "tokens.d.ts"
        }
      ]
    }
  }
}

3. スクリプト設定

package.json
{
  ...,
  "scripts": {
    "build": "style-dictionary build",
  }
}

4. tokensファイル作成

mkdir tokens
touch tokens/tokens.json

作成したデザイントークンをコピーします。

5. build

npm run build

ts
✔︎ build/ts/tokens.js
✔︎ build/ts/tokens.d.ts

6. 出力されたファイルを確認する

出力されたファイルが意図しているものになっているのか確認します。

出力されたファイル
tokens.js
/**
 * Do not edit directly, this file was auto-generated.
 */

export const PrimitiveColorWhite = {
  colorSpace: "srgb",
  components: [1, 1, 1],
  alpha: 1,
  hex: "#ffffff",
};
export const PrimitiveColorBlue500 = {
  colorSpace: "srgb",
  components: [0.376, 0.008, 0.933],
  alpha: 1,
  hex: "#6002ee",
};
export const PrimitiveColorGray800 = {
  colorSpace: "srgb",
  components: [0.278, 0.278, 0.278],
  hex: "#474747",
};
export const PrimitiveColorGray900 = {
  colorSpace: "srgb",
  components: [0.133, 0.133, 0.133],
  hex: "#222222",
};
export const PrimitiveSpacingXs = { value: 4, unit: "px" };
export const PrimitiveSpacingSm = { value: 8, unit: "px" };
export const PrimitiveSpacingMd = { value: 16, unit: "px" };
export const PrimitiveTypographyFontFamilyInter = [
  "Inter",
  "-apple-system",
  "sans-serif",
];
export const PrimitiveTypographyFontSizeMd = { value: 16, unit: "px" };
export const PrimitiveTypographyLineHeightMd = { value: 24, unit: "px" };
export const PrimitiveTypographyFontWeightRegular = 400;
export const PrimitiveTypographyFontWeightSemiBold = 600;
export const PrimitiveShadowElevation1 = {
  color: {
    colorSpace: "srgb",
    components: [0.278, 0.278, 0.278],
    hex: "#474747",
  },
  offsetX: { value: 0, unit: "px" },
  offsetY: { value: 2, unit: "px" },
  blur: { value: 4, unit: "px" },
  spread: { value: 0, unit: "px" },
};
export const SemanticColorBrandPrimary = {
  colorSpace: "srgb",
  components: [0.376, 0.008, 0.933],
  alpha: 1,
  hex: "#6002ee",
}; // ブランドを象徴する主要なカラーです。
export const SemanticColorTextPrimary = {
  colorSpace: "srgb",
  components: [0.133, 0.133, 0.133],
  hex: "#222222",
}; // 主に本文などの基本テキストに使用する色です。
export const SemanticTypographyBody = {
  fontFamily: ["Inter", "-apple-system", "sans-serif"],
  fontSize: { value: 16, unit: "px" },
  lineHeight: { value: 24, unit: "px" },
  fontWeight: 400,
};
export const SemanticTypographyButton = {
  fontFamily: ["Inter", "-apple-system", "sans-serif"],
  fontSize: { value: 16, unit: "px" },
  lineHeight: { value: 24, unit: "px" },
  fontWeight: 600,
};
export const SemanticShadowButton = {
  color: {
    colorSpace: "srgb",
    components: [0.278, 0.278, 0.278],
    hex: "#474747",
  },
  offsetX: { value: 0, unit: "px" },
  offsetY: { value: 2, unit: "px" },
  blur: { value: 4, unit: "px" },
  spread: { value: 0, unit: "px" },
};
export const ComponentButtonPrimaryTypography = {
  fontFamily: ["Inter", "-apple-system", "sans-serif"],
  fontSize: { value: 16, unit: "px" },
  lineHeight: { value: 24, unit: "px" },
  fontWeight: 600,
};
export const ComponentButtonPrimaryShadow = {
  color: {
    colorSpace: "srgb",
    components: [0.278, 0.278, 0.278],
    hex: "#474747",
  },
  offsetX: { value: 0, unit: "px" },
  offsetY: { value: 2, unit: "px" },
  blur: { value: 4, unit: "px" },
  spread: { value: 0, unit: "px" },
};
export const ComponentButtonPrimaryBackgroundColor = {
  colorSpace: "srgb",
  components: [0.376, 0.008, 0.933],
  alpha: 1,
  hex: "#6002ee",
};
export const ComponentButtonPrimaryTextColor = {
  colorSpace: "srgb",
  components: [1, 1, 1],
  alpha: 1,
  hex: "#ffffff",
};

終わりに

この記事を通じて、デザイントークンの可能性や標準化の価値を感じていただき、デザインシステム界隈がより盛り上がってくれれば嬉しく思います。
まだliving-draft段階で変更の可能性はありますが、今後の動向をwatchし続けたいと考えています。
この記事が皆さんのデザイントークン導入の参考になれば幸いです。

株式会社バニッシュ・スタンダード

Discussion

いっきゅういっきゅう

[追記] 2025.8.26
デザイントークンを定義してみようのcolor tokenの定義方法が間違っておりましたので修正しました🙇‍♂️