tailwind-token-consolidation について
Tailwind v4 の `globals.css` で、見た目を保ちながらデザイントークンを整理するワークフロー技能です。使用状況の分析、安全な削除と統合、スクリプトによる名前変更、ビルド検証の手順を示します。
主な機能
- 01プロジェクト内のトークン使用状況を把握
- 02依存関係をたどって使用中トークンを特定
- 03未使用トークンを安全に削除
- 04値の近いトークンを統合
- 05スクリプトで名前変更を適用
- 06ビルドと表示値を検証
活用シーン
- 01Tailwind v4 のトークンセットを監査
- 02重複するデザイントークンを削減
- 03スコープ別とダークモードのスタイルを維持
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ millionco/ skills --skill tailwind-token-consolidationインストール後、Claude Code で/tailwind-token-consolidationを入力して読み込まれたか確認します。
詳細
Tailwind トークンの統合
見た目を維持しながら、Tailwind v4 の globals.css にあるデザイントークンの数を減らします。
すべての統合タスクで、以下の手順を必ず順番どおりに実行してください。
- まず監査し、最初から編集しないでください。
globals.cssを構造化ブロック(@theme inline、:root、.dark、その他すべてのスコープ付きブロック)に解析します。トークン数を数えます。すべての.ts/.tsx/.css/.mdxファイル(globals.cssを除く)をスキャンし、クラス名とvar(--...)参照の使用状況マップを作成します。 - 推移的に LIVE セットを計算します。 トークンは、(a)そのサフィックスが Tailwind クラスとしてどこかに現れる、(b)どこかで
var(--name)として参照される、または(c)別の live トークンの値の中で参照される場合に live です。固定点に達するまで反復します。 globals.css内のユーティリティクラスもスキャンします。.X { ... }ブロック(:root/.dark/@theme inlineの外)にあるvar(--...)は、すべてハード依存関係です。これらのクラスが必要とするトークンは必ず残してください。- 決定的な名前変更マップをデータとして作成し(
old_suffix -> new_suffixまたはDELETE)、単一のスクリプトでglobals.cssとコードの両方に適用します。コードベース全体のクラス名を手作業で編集しないでください。 - 各パスの後に検証します。
pnpm typecheck、pnpm lint、pnpm format、pnpm buildを実行してください。完了を宣言する前に、すべて成功する必要があります。
どの手順も省略しないでください。手順を省略した場合、そのタスクは失敗です。
2 つの削減方法(この順番で適用)
| 方法 | 削減対象 | リスク |
|---|---|---|
| 1. 未使用トークンの削除 | コードでの使用も、globals 内の依存もないトークン | なし — 安全 |
| 2. 値が類似したトークンの統合 | ライトモードとダークモードの両方で値が約 5% 以内に収まる場合、--button → --card に名前を変更 | 小 — 本当に類似した値のみ選ぶ |
トークンを Tailwind の任意値(px-[17px]、text-[16px]/[1.6875rem]、shadow-[#000_0px_1px])にインライン化しないでください。インライン化しても、名前付きトークンの煩雑さが任意値の煩雑さに置き換わるだけで、ノイズの量は変わらず、場所が変わるだけです。名前付きトークンには意図が込められていますが、任意値はそれを損ないます。この 2 つの方法だけで現実的に削減できるのは 50~60% であり、70% ではありません。ユーザーがそれ以上の削減を求めた場合は、より多くのトークンを減らすのではなく、className 文字列内のノイズを同じだけ増やすことになると説明し、要求を再考してもらってください。
遭遇する 8 つのバグ(事前に対策してください)
-
値の優先順位に関するバグ。 N 個のトークンを 1 つの正規名に統合すると、解析順によって先勝ちになるため、結果が予測できません。誤った色になることもよくあります(例:
--dangerが--diagnostics-table-error-borderの値を持つようになる。これは鮮やかな赤ではなく、淡い枠線の色合いです)。対策: 新しいトークンに対して、どのソーストークンの値を優先するかを指定するCANONICAL_SOURCEの明示的なオーバーライド辞書を追加します。 -
スコープ付きオーバーライドに関するバグ。
.dashboard-page { --text-inline: 14px; }が機能するのは、--text-inlineがvar()で参照されるカスタムプロパティだからです。いずれかのスコープ付きブロック(.dashboard-page、.dark .dashboard-pageなど)で上書きされているトークンは、必ずトークンとして残してください。削除すると、スコープ付きオーバーライドが気づかれないまま壊れます。 -
globals 内の使用箇所に関するバグ。
globals.css内のユーティリティクラス(例:.dropdown-menu-highlight { padding: var(--spacing-dropdown-menu-padding); })は、どの.tsxファイルにも現れないトークンを読み取ります。これらを@themeから削除すると、実行時に CSS 変数が未定義になります。対策: スキャン前に CSS テキストから@theme inline/:root/.darkなどのブロックを取り除き、残りからすべてのvar(--X)参照を収集します。これらのトークンは必ず残してください。 -
カテゴリの混同に関するバグ。 1 つのサフィックスが複数のカテゴリに現れることがあります。たとえば、
organization-section-surfaceは色(white)であると同時に、シャドウのエイリアスの接頭辞(--shadow-organization-section-surface)でもあります。マップに色用の'organization-section-surface': 'card'とシャドウ用の'organization-section-surface': 'surface-soft'の両方があると、後者がグローバルマップ内の裸のトークンの名前変更を上書きします。対策: 裸の--Xトークンの名前を変更するのは色カテゴリだけにします。SHADOW/SPACING/TEXT/RADIUS/FONT/TRACKING では、裸の名前は変更せず、--<theme-prefix>-Xのエイリアスのみを変更します。 -
アイコンカラーを過度に統合するバグ。 すべての「グレーのアイコン」(#696969、#898989、#b3b3b3、#c1c1c1、#dddddd)を単一の
--iconトークンに統合すると、もともと明るかったアイコンが目に見えて暗くなります。また、暗い背景(両モードで暗いままのドロップダウンメニューなど)では、意図したコントラストが反転します。修正方法: アイコンの色調を2種類(--icon(約#696969のミディアムグレー)と--icon-soft(約#b3b3b3のライトグレー))維持します。もともと明るかったアイコンは--icon-softに割り当てます。 -
透明度が条件によって変わるサーフェスのバグ。 一部のトークンは、片方のモードでは
transparent、もう片方のモードでは具体的な色になります(例:ライトモードでは--repository-card-panel: #ffffff、ダークモードではtransparent。ダークモードでは意図的に背景が透けて見えるようになっています)。これを--cardに統合すると、ダークモードでパネルが不透明になり、レイヤー表現が目に見えて変わります。修正方法: あるモードではトークンの値がtransparent、もう片方では具体的な色の場合、常に不透明なトークンと統合しないでください。別々のトークンとして維持するか、使用するclassNameに条件分岐のロジックを直接記述します。 -
bg-Xの色と画像の衝突によるバグ。 Tailwind v4はbg-Xを--color-X(背景色)と--background-image-X(背景画像)の両方に照合します。--background-image-Xしか存在しない場合、bg-Xはグラデーションを正しく描画します。しかし、別のトークン(例:--install-button-text)を--install-buttonに名前変更すると、--color-install-buttonのエイリアスが作成されます。その結果、bg-install-buttonがグラデーションの上にbackground-color: whiteを設定し、ボタンが空白または見えなくなることがあります。修正方法: 色の接尾辞をXに変更する前に、--background-image-Xとbg-Xクラスの使用箇所をgrepで検索します。どちらかが存在する場合は、別の名前を選びます(例:--install-buttonに名前変更せず、--install-button-textを維持します)。 -
常に暗いサーフェスのバグ。 ドロップダウンメニュー、ツールチップ、常に暗いままのポップオーバーなどのトークンは、サーフェスがテーマに関係なく暗いままのため、テキストやアイコンの色がライトモードとダークモードの両方で明るくなっています。
--dropdown-menu-textを--foregroundに統合すると、ライトモードでドロップダウンのテキストが暗くなり(暗いサーフェスに暗いテキストが重なって見えなくなり)ます。修正方法: トークンを統合する前に、ライトモードとダークモードの値を比較します。どちらの値もL=0.5の境界線の同じ側(どちらも明るい、またはどちらも暗い)にある場合、そのトークンは常に明るい、または常に暗いサーフェス上にあり、--foregroundや--iconのようにモードによって切り替わるトークンとは統合できません。独立したトークンとして維持します。
3段階のブロック変換
:rootまたは.darkブロックを出力する際は、以下の順序でエントリーを記述します。
# Pass 1: canonical (non-renamed) entries — these set the source of truth.
for name, value in original_decls.items():
target = TOKEN_RENAME.get(name)
if target is None or target == name:
out[name] = fix_var_refs(value)
# Pass 2: CANONICAL_SOURCE overrides — explicit pick for collapsed groups.
for new_token, source_token in CANONICAL_SOURCE.items():
if source_token in original_decls:
out[new_token] = fix_var_refs(original_decls[source_token])
# Pass 3: other renamed entries — only if the target isn't already set.
for name, value in original_decls.items():
target = TOKEN_RENAME.get(name)
if target and target != name and target not in out:
out[target] = fix_var_refs(value)
これにより、(a)正規トークンの元の値が常に名前変更後の値より優先されること、(b)--dangerのような新しいトークンにどの値を割り当てるかを明示的に制御できること、(c)出力に重複したエントリーが現れないことが保証されます。
適用・検証ループ
名前変更マップを変更するたびに、必ずこのループを実行します。
git checkout -- apps/web # always start from a clean tree
python3 /tmp/transform.py # apply the rename + inline map
pnpm typecheck && pnpm lint && pnpm format && pnpm --filter web build
チェックに失敗した場合は、生成された出力ではなくマップを修正してから、再実行します。生成後のglobals.cssを手作業で編集するのは危険です。次の変換実行時に修正が上書きされます。
合理化の判断表
| こう考えてしまう | してはいけないこと |
|---|---|
| 「このトークンは削除しても大丈夫。使っている人はいない」— globals.css 内のクラスを検索せずに | @theme/:root/.dark ブロックを取り除き、まず残りの部分で var(--X) を検索してください |
| 「この2つのグレーはほとんど同じ」— ライトモードとダークモードの両方の値を確認せずに | 両方のモードを比較してください。ライトモードでは近い値でも、ダークモードでは大きく異なることがあります |
| 「削減効果を高めるために、1か所だけで使うトークンを任意の値にインライン化しよう」 | それはノイズを別の場所に移しているだけです。名前付きトークンを維持してください。インライン化は集約に反します |
| 「ユーザーが70%を指定したから、インライン化してでも達成しよう」 | 異議を唱えてください。任意の値で70%にするより、名前付きトークンで50~60%にするほうが適切です。正直な数値を報告してください |
| 「スクリプトの実行後に、壊れたクラスを手作業で直そう」 | 次にスクリプトを実行すると、変更は元に戻ります。成果物ではなく、スクリプトを修正してください |
| 「先に書かれた値を優先すれば問題ない。解析順は決定的だから」 | :root の順序が変更されるまでは問題ありません。CANONICAL_SOURCE で明示的に上書きしてください |
「この名前への変更で問題ない。衝突はない」— すでに --background-image-<name> があるのに | 変更先の名前を決める前に、--background-image-<target> と bg-<target> の使用箇所を grep で検索してください |
完了を宣言する前に
必ず以下を実施してください。
pnpm typecheckを実行してください — 終了コードが0であること。pnpm lintを実行してください — エラーも警告も0件であること。pnpm formatを実行してください — 完了していること。pnpm --filter web buildを実行してください — 終了コードが0であること。rg "^\s*--[a-z][a-zA-Z0-9-]*\s*:" apps/web/app/globals.css -o | sort -u | wc -lで最終トークン数を数えてください。変更前後の数と削減率を報告してください。- 生成された
:rootブロックを最初から最後まで読み、正規値が想定どおりであることを確認して、少なくとも上位3つの表示領域(カード、ページ背景、ドロップダウン)に意図した色が描画されていることを確認してください。
ビルドが成功していても、表示が壊れていればタスクは失敗です。削減数を数えても、検証していなければタスクは失敗です。