デザイントークンの仕様を覗いてみる
はじめまして、株式会社バニッシュ・スタンダードでフロントエンドエンジニアをやっているいっきゅうです。よろしくお願いします。
今回は、「デザイントークン」をテーマに記事を書いてみようと思います。
はじめに
「デザイントークン」という言葉を聞くけれど、実際に何なのか、どう使うのかがよくわからない方も多いのではないでしょうか。
この記事では、2025年4月18日に公開されたW3C Design Tokens Community Groupのliving-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
{
"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. スクリプト設定
{
...,
"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. 出力されたファイルを確認する
出力されたファイルが意図しているものになっているのか確認します。
出力されたファイル
/**
* 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の定義方法が間違っておりましたので修正しました🙇♂️