mcpField
T
tokens

tokens

github.com/dawitlabsデザイン・UI744 回インストール24 週間前に更新
リポジトリ

tokens について

tokens は、ハードコードされたデザイン値を監査し、階層型のデザイントークン体系を導入するコーディングエージェント向けスキルです。トークンの対応付け、置換、見た目の確認、結果報告を順に進めます。

主な機能

  1. 01色、余白、文字のハードコード値を監査
  2. 02プリミティブ、セマンティック、コンポーネントのトークンを整理
  3. 03コード内の値をトークンに置換
  4. 04変更前後のスクリーンショットを比較
  5. 05トークン一覧と未解決値を報告

活用シーン

  1. 01コードベース内の分散したスタイル値を統一
  2. 02重複するハードコード値を名前付きトークンに置換
  3. 03デスクトップとモバイルの表示変化を確認
  4. 04トークン化の進捗と残存値を記録

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dawitlabs/ui-skills --skill tokens

インストール後、Claude Code で/tokensを入力して読み込まれたか確認します。

同梱ファイルSYSTEMS.md

詳細

/tokens — デザイントークンシステム

コードベース内のハードコードされたデザイン値をすべて監査し、3層のトークンシステム(プリミティブ → セマンティック → コンポーネント)を構築または適用して、全体のハードコード値をトークンに置き換えます。

トークンのアーキテクチャと命名規則:SYSTEMS.md

プロジェクトのルートに DESIGN-BRIEF.md がある場合は、作業を始める前に読みます。ブリーフの Color System と Typography の項目には、プリミティブトークンの正確な開始点が記載されています。ゼロから導き出すのではなく、そこにあるカラーコードとフォント名をそのまま使います。


フェーズ 0 — 環境のセットアップ

0a. ソースルートを見つける
find . -type d -name "src" \
  -not -path "*/node_modules/*" \
  -not -path "*/.next/*" \
  -not -path "*/dist/*" \
  -not -path "*/.turbo/*" \
  | head -1

src/ がない場合:find . -type d -name "app" -not -path "*/node_modules/*" -not -path "*/.next/*" | head -1

結果を以降 <SRC> として使います。

0b. CSS-in-JS を検出する
grep -r "styled-components\|@emotion\|@stitches\|vanilla-extract\|linaria" package.json packages/*/package.json apps/*/package.json 2>/dev/null | head -5

見つかった場合、フェーズ 1 のカラー/スペーシング検索でも .ts ファイルとテンプレートリテラルをスキャンする必要があります。必要な箇所ではフェーズ 1 のコマンドにこれらが含まれています。使用しているライブラリを記録します。

0c. 既存のトークンシステムを検出する

以下をすべて実行し、結果が返るものを記録します。

# CSS custom properties (most common)
find . -name "globals.css" -o -name "variables.css" -o -name "tokens.css" | grep -v node_modules

# Tailwind config
find . -name "tailwind.config.*" -not -path "*/node_modules/*"

# Style Dictionary / tokens.json
find . -name "tokens.json" -o -name "design-tokens.json" | grep -v node_modules
find . -type d -name "tokens" -not -path "*/node_modules/*"

# shadcn/ui pattern (CSS vars in globals.css under :root)
grep -rn ":root" . --include="*.css" -l | grep -v node_modules | head -5

# Existing token usage (CSS vars already in use)
grep -rn "var(--" "$SRC" --include="*.tsx" --include="*.css" | head -10

結果を次のいずれかのトークン配置先に対応付けます。すべての修正はここに適用します。

  • CSS カスタムプロパティ → globals.css の :root ブロック
  • Tailwind theme.extend → tailwind.config.ts
  • Style Dictionary → tokens/ ディレクトリ内の JSON ファイル
  • なし → globals.css を :root ブロック付きで作成します(SYSTEMS.md を参照)
0d. 現在のトークンセットを記録する

0b で見つかったトークン配置先のファイルを読みます。既存のトークン名と値をすべて一覧にします。これを基準とし、新しいトークンが既存のものと競合または重複しないようにします。

0e. 開発サーバーを確認/起動する(表示確認用)
curl -s --max-time 2 -o /dev/null -w "%{http_code}" http://localhost:3000

200 でない場合は、開発コマンドを実行して待機します。

until curl -s --max-time 2 http://localhost:3000 > /dev/null 2>&1; do sleep 1; done && echo "ready"
0f. スクリーンショット用スクリプトを作成する

プロジェクトルートに作成します(node_modules が必要です)。フェーズ 4 で削除します。

_tokens_screenshot.mjs:

import { chromium } from '@playwright/test';
import { mkdirSync } from 'fs';

const get = (f) => { const i = process.argv.indexOf(f); return i !== -1 ? process.argv[i+1] : null; };
const url    = get('--url');
const output = get('--output') ?? '/tmp/tokens-screenshot.png';
const width  = parseInt(get('--width')  ?? '1280');
const height = parseInt(get('--height') ?? '900');
const auth   = get('--auth');

if (!url) { console.error('--url required'); process.exit(1); }
mkdirSync('/tmp/tokens', { recursive: true });

const browser = await chromium.launch();
const context = await browser.newContext({
  viewport: { width, height },
  ...(auth ? { storageState: auth } : {}),
});
const page = await context.newPage();
try {
  await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });
} catch {
  await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
}
await page.screenshot({ path: output, fullPage: true });
await browser.close();
console.log('saved:', output);

スクリーンショットを撮るたびに、画像に対して Read ツールを使います。

0g. 認証で保護された画面に対応する
ls /tmp/uiux-auth-state.json /tmp/animate-auth-state.json /tmp/copy-auth-state.json /tmp/a11y-auth-state.json 2>/dev/null | head -1

該当する画面が見つかった場合、保護されたルートへのすべてのスクリーンショット呼び出しに --auth <path> を渡します。


フェーズ 1 — トークン監査

1a. ハードコードされたカラーをすべて見つける
SRC=<your source root>

# Hex colors in TSX/JSX (inline styles, arbitrary Tailwind values)
echo "=== Hex colors in TSX ===" && \
grep -rn "#[0-9a-fA-F]\{3,8\}" "$SRC" --include="*.tsx" --include="*.jsx" \
  | grep -v "//.*#[0-9a-fA-F]" | grep -v "\.md"

# Hex colors in CSS
echo "=== Hex colors in CSS ===" && \
grep -rn "#[0-9a-fA-F]\{3,8\}" "$SRC" --include="*.css" --include="*.scss" \
  | grep -v "^.*:.*var(--"

# rgb/hsl inline values
echo "=== RGB/HSL values ===" && \
grep -rn "rgba\?\|hsla\?" "$SRC" --include="*.tsx" --include="*.css" | grep -v "var(--"

# Arbitrary Tailwind color values (hex)
echo "=== Arbitrary Tailwind colors (hex) ===" && \
grep -rn "\(bg\|text\|border\|ring\|shadow\|fill\|stroke\)-\[#" "$SRC" --include="*.tsx"

# Named Tailwind palette colors that should be semantic tokens
# e.g. bg-blue-500 should become bg-primary if blue-500 is the primary color
echo "=== Named Tailwind palette colors ===" && \
grep -rn "\(bg\|text\|border\|ring\|fill\|stroke\)-\(slate\|gray\|zinc\|neutral\|stone\|red\|orange\|amber\|yellow\|lime\|green\|emerald\|teal\|cyan\|sky\|blue\|indigo\|violet\|purple\|fuchsia\|pink\|rose\)-[0-9]\{2,3\}" "$SRC" --include="*.tsx" \
  | grep -v "hover:\|focus:\|dark:\|disabled:" | head -40
# Note: filter hover/focus/dark variants — those are less likely to be primary brand usage
# High-frequency named colors (>5 occurrences) are strong tokenization candidates

CSS-in-JS(styled-components / emotion)が検出された場合: 以下も実行します。

# Hex colors in template literals
grep -rn "#[0-9a-fA-F]\{3,8\}" "$SRC" --include="*.ts" --include="*.tsx" \
  | grep "styled\.\|css\`\|createGlobalStyle\|keyframes" | grep -v "//.*#"

# CSS property values inside template literals
grep -rn "color:\s*#\|background:\s*#\|border.*:\s*#" "$SRC" --include="*.ts" --include="*.tsx"
1b. ハードコードされたスペーシングをすべて見つける
# Off-grid pixel values in CSS (not on 4px scale)
echo "=== Off-grid spacing in CSS ===" && \
grep -rn "\b[0-9]*[13579]\b\s*px\|[0-9]\+\.\s*[0-9]\+\s*px" "$SRC" --include="*.css" \
  | grep -v "border\|outline\|shadow\|translate"

# Arbitrary Tailwind spacing values
echo "=== Arbitrary Tailwind spacing ===" && \
grep -rn "\(p\|m\|gap\|space\|w\|h\|inset\|top\|left\|right\|bottom\)-\[[0-9]" "$SRC" --include="*.tsx"

# Inline style pixel values
echo "=== Inline style px values ===" && \
grep -rn "style={{" "$SRC" --include="*.tsx" | grep "[0-9]px\|[0-9]rem" | head -20
1c. ハードコードされたタイポグラフィ値をすべて見つける
# Font sizes in CSS not from scale
echo "=== Hardcoded font sizes ===" && \
grep -rn "font-size:" "$SRC" --include="*.css" | grep -v "var(--"

# Arbitrary Tailwind font sizes
echo "=== Arbitrary Tailwind font sizes ===" && \
grep -rn "text-\[[0-9]" "$SRC" --include="*.tsx"

# Font weights not from scale
echo "=== Hardcoded font weights ===" && \
grep -rn "font-weight:\s*[0-9]" "$SRC" --include="*.css" | grep -v "var(--"

# Line heights
echo "=== Hardcoded line heights ===" && \
grep -rn "line-height:\s*[0-9]" "$SRC" --include="*.css" | grep -v "var(--"
1d. ハードコードされたシャドウ、角丸、z-index をすべて見つける
# Box shadows not from token
echo "=== Hardcoded shadows ===" && \
grep -rn "box-shadow:" "$SRC" --include="*.css" | grep -v "var(--" && \
grep -rn "shadow-\[" "$SRC" --include="*.tsx"

# Border radii not from token
echo "=== Hardcoded border radii ===" && \
grep -rn "border-radius:" "$SRC" --include="*.css" | grep -v "var(--" && \
grep -rn "rounded-\[[0-9]" "$SRC" --include="*.tsx"

# Magic z-index numbers
echo "=== Magic z-index ===" && \
grep -rn "z-index:\s*[0-9]\|z-\[[0-9]" "$SRC" --include="*.css" --include="*.tsx"
1e. 重複値と不統一な値を見つける
# Same color used with different names (group by hex value to find duplicates)
grep -rh "#[0-9a-fA-F]\{3,8\}" "$SRC" --include="*.tsx" --include="*.css" \
  | grep -oE "#[0-9a-fA-F]{3,8}" | sort | uniq -c | sort -rn | head -20

出現回数の多い値は、最も多く使われているハードコードカラーです。トークン化の優先度が最も高い項目です。

1f. 主要な画面のスクリーンショットをすべて撮る(変更前の状態)

各画面について、デスクトップ版とモバイル版を撮ります。スペーシングやタイポグラフィのトークン変更により、1280px では問題なく見えるモバイルレイアウトが崩れることがあります。

node _tokens_screenshot.mjs --url http://localhost:3000 --output /tmp/tokens/home-before-desktop.png
node _tokens_screenshot.mjs --url http://localhost:3000 --output /tmp/tokens/home-before-mobile.png --width 375 --height 812

見た目が特に異なる画面を 3~5 個選びます。**すべての画像を確認します。**これらを変更前の基準とします。

続行する前に、監査結果をカテゴリ別にまとめてすべて提示します。


フェーズ 2 — トークンマップ

コードを書く前にトークンマップを作成します。これは、すべてのハードコード値と提案するトークン名の対応表です。

形式:

Hardcoded value | Occurrences | Proposed token name | Layer
#3b82f6         | 23          | --color-primary      | semantic
#1d4ed8         | 8           | --color-primary-dark  | semantic
#f9fafb         | 31          | --color-surface       | semantic
13px            | 4           | --spacing-3-5         | primitive

命名規則(SYSTEMS.md より):

  • プリミティブトークン:--color-{hue}-{shade}、--space-{n}、--radius-{size}
  • セマンティックトークン:--color-primary、--color-surface、--color-text、--color-border
  • コンポーネントトークン:--button-bg、--card-shadow(コンポーネント固有の場合のみ)
  • 見た目の印象で命名しない:--blue や --dark-gray ではなく、セマンティック名を使います
  • セマンティック名はカラー変更後も使えます。ブランドカラーが青から緑に変わっても、--color-primary はそのまま適切です

トークンレイヤー別に分類します。

  1. 新しいプリミティブトークン — システムにまだない生の値
  2. 新しいセマンティックトークン — プリミティブに対応する、意味に基づいたエイリアス
  3. 使用する既存トークン — すでに定義済みのトークンに対応する値

フェーズ 3 を始める前に、トークンマップの確認をユーザーに求めます。


フェーズ 3 — 再帰的なトークンループ

カテゴリごとに作業します:色 → 余白 → タイポグラフィ → シャドウ → 角丸 → z-index。 カテゴリごとに最大 3 回の反復です。

3a. 新しいトークンを定義する

使用箇所を置き換える前に、新しいプリミティブトークンとセマンティックトークンをすべてトークンの定義ファイルに追加します。

CSS カスタムプロパティのパターン:

/* In globals.css */
:root {
  /* Primitives */
  --color-blue-500: #3b82f6;
  --color-blue-700: #1d4ed8;
  --space-1: 4px;
  --space-2: 8px;

  /* Semantic */
  --color-primary:      var(--color-blue-500);
  --color-primary-dark: var(--color-blue-700);
  --color-surface:      #f9fafb;
  --color-text:         #111827;
  --color-border:       #e5e7eb;
}

/* Dark mode — same semantic names, different values */
.dark {
  --color-primary:  var(--color-blue-400);
  --color-surface:  #111827;
  --color-text:     #f9fafb;
  --color-border:   #374151;
}

Tailwind のパターン:

// tailwind.config.ts
export default {
  theme: {
    extend: {
      colors: {
        primary:  'var(--color-primary)',
        surface:  'var(--color-surface)',
        border:   'var(--color-border)',
      },
      spacing: {
        '3.5': 'var(--space-3-5)',
      },
    },
  },
}
3b. コンポーネント内のハードコード値を置き換える

出現頻度の高い値から順に、ファイルごとに作業します。それぞれについて:

  1. ファイルを読みます
  2. ハードコード値をトークンに置き換えます
  3. ロジック、レイアウト、コンポーネント構造は変更せず、値のみを変更します

CSS の置き換え:

/* Before */
color: #3b82f6;

/* After */
color: var(--color-primary);

Tailwind クラスの置き換え:

/* Before */
<div className="bg-[#3b82f6] text-[#111827]">

/* After */
<div className="bg-primary text-text">

インラインスタイルの置き換え(最後の手段 — CSS/Tailwind への移動を優先):

/* Before */
style={{ backgroundColor: '#3b82f6' }}

/* After — move to CSS class if possible */
className="bg-primary"

/* Or if dynamic: */
style={{ backgroundColor: 'var(--color-primary)' }}
3c. カテゴリごとにスクリーンショットを撮る
sleep 3  # hot reload
node _tokens_screenshot.mjs --url http://localhost:3000 --output /tmp/tokens/home-after-<category>-desktop.png
node _tokens_screenshot.mjs --url http://localhost:3000 --output /tmp/tokens/home-after-<category>-mobile.png --width 375 --height 812

両方の画像を確認します。 2 つのビューポートで撮影した変更前のスクリーンショットと比較します。

両方のサイズで UI が同一に見える必要があります。変更があった場合:

  • 色が違う → トークン値が誤っています。トークン定義を修正します
  • モバイルでレイアウトがずれる → 余白/サイズのトークンがハードコード値とは異なる値に解決されています。トークン値を修正します
3d. トークンのチェックリスト

次のカテゴリに進む前に:

  • このカテゴリの値がすべてトークンに置き換えられている
  • 置き換え中に新たなハードコード値が追加されていない
  • デスクトップの変更前/変更後のスクリーンショットが視覚的に同一である
  • モバイル(375px)の変更前/変更後のスクリーンショットが視覚的に同一である — 余白/タイポグラフィのトークンによってレイアウトがずれていない
  • トークン名がプリミティブ → セマンティックの階層に従っている
  • ダークモードがある場合、ダークモードのブロックで、すべてのセマンティックトークンの上書きが定義されている
  • 値が同じで意味が異なるトークンがない(重複を統合する)
  • トークン名が見た目(--blue)ではなく、役割(--color-primary)を表している

不合格があれば → 3b に戻ります。3 回目の反復後は、問題として記録して次に進みます。


フェーズ 4 — クリーンアップとレポート

一時スクリプトを削除します:

rm -f _tokens_screenshot.mjs

カテゴリごとに:

✓ Colors (iteration 1) — 47 hardcoded values → 8 tokens
✓ Spacing (iteration 2) — 12 hardcoded values → 4 tokens

最終サマリーテーブル:

カテゴリ変更前のハードコード値作成したトークン置き換えた値反復回数状態
色............✓ / ⚠
余白............✓ / ⚠
タイポグラフィ............✓ / ⚠
シャドウ............✓ / ⚠
角丸............✓ / ⚠
Z-index............✓ / ⚠

トークン一覧 — 定義したすべてのトークン、その値、使用回数。

解決できなかった、問題として記録したハードコード値を、理由とともにすべて列挙します。


ルール

  1. 置き換える前に定義します。 トークンは、コンポーネントから参照される前にトークンの保存先に存在している必要があります。
  2. セマンティックトークンよりプリミティブトークンを先に定義します。 まず生の値を定義し、次にセマンティックエイリアスを定義します。コンポーネントトークンは必要な場合にのみ使用します。
  3. 見た目ではなく役割に基づいて命名します。 --color-primary はリブランド後も使えますが、--color-blue は使えません。
  4. スクリーンショットで回帰テストを行います。 変更前と変更後の見た目は完全に一致する必要があります。異なる場合、トークンの値が誤っています。
  5. 適切なセマンティックレイヤーかどうかは、ダークモードで確認します。 .dark の切り替えだけでコンポーネントを編集せずに色が正しく変わるなら、トークンは適切です。
  6. 1つの意味に1つのトークンを使います。 値と意味が同じトークンが2つある場合は、統合します。
  7. すべてをトークン化しないでください。 border: 1px solid の 1px はトークン化する必要はありません。テーマによって変わる値や、複数の場所で使われる値に注目します。
  8. カテゴリごとの反復は最大3回までです。 問題に印を付けて、次に進みます。
  9. ロジックは絶対に変更しません。 変更するのはデザインの値だけです。条件、コンポーネントの構造、機能は変更しません。

トークンのアーキテクチャ、命名規則、完全なコードパターンについては、SYSTEMS.md を参照してください。

コメント