关于 tailwind-token-consolidation
用于精简 Tailwind v4 `globals.css` 设计令牌的工作流技能,重点是在保留视觉效果的同时减少令牌。它规定了使用情况分析、谨慎删除与合并、脚本化重命名及构建验证流程。
主要功能
- 01映射项目文件中的令牌用法
- 02传递式追踪仍在使用的令牌
- 03安全删除未使用令牌
- 04合并值相近的令牌
- 05通过脚本应用重命名
- 06验证构建与视觉值
适用场景
- 01审查 Tailwind 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 集合。 满足以下任一条件的令牌即为 live:(a)其后缀在任何位置作为 Tailwind 类出现;或(b)在任何位置以
var(--name)的形式被引用;或(c)在另一个 live 令牌的值中被引用。迭代直至达到固定点。 - 也要扫描
globals.css自身的工具类。.X { ... }区块(:root、.dark和@theme inline之外)中的任何var(--...)都是硬依赖。这些类所需的令牌必须保留。 - 将确定性的重命名映射构建为数据(
old_suffix -> new_suffix或DELETE)。通过单个脚本将其应用到globals.css和代码中。绝不要手动编辑整个代码库中的类名。 - **每轮完成后验证:**运行
pnpm typecheck、pnpm lint、pnpm format、pnpm build。全部通过后才能宣布完成。
不得跳过任何步骤。跳过任何一步都意味着任务失败。
两种精简手段(按此顺序应用)
| 手段 | 移除内容 | 风险 |
|---|---|---|
| 1. 删除无用令牌 | 在代码中用量为零且在 globals 内无需求的令牌 | 无 — 安全 |
| 2. 合并值相近的令牌 | 当浅色和深色模式下的值都相差约 5% 以内时,将 --button 重命名为 --card | 较小 — 只选择确实相似的值 |
不要将令牌内联为 Tailwind 任意值(px-[17px]、text-[16px]/[1.6875rem]、shadow-[#000_0px_1px])。内联只是用任意值的杂乱替换了命名令牌的杂乱——噪声相同,只是换了文件。命名令牌承载意图;任意值则会破坏意图。仅用这两种手段,实际可减少 50–60%,而非 70%。如果用户要求更高的比例,应予以说明:正确的做法是减少令牌,而不是让 className 字符串里充斥同样的噪声。
你会遇到的八个 Bug(请提前防范)
-
值优先级 Bug。 将 N 个令牌合并为一个规范名称时,解析顺序决定的“先写入者胜出”不可预测。结果往往会得到错误的颜色(例如,
--danger最终采用了--diagnostics-table-error-border的值,也就是浅色边框色调,而不是鲜亮的红色)。**修复:**添加显式的CANONICAL_SOURCE覆盖字典,将新令牌映射到其值应胜出的特定源令牌。 -
作用域覆盖 Bug。
.dashboard-page { --text-inline: 14px; }之所以有效,只是因为--text-inline是一个通过var()引用的自定义属性。任何作用域区块(.dashboard-page、.dark .dashboard-page等)中被覆盖的令牌都必须保留为令牌;删除它们会悄无声息地破坏作用域覆盖。 -
globals 内部消费者 Bug。
globals.css中的工具类(例如,.dropdown-menu-highlight { padding: var(--spacing-dropdown-menu-padding); })会读取任何.tsx文件中都未出现的令牌。从@theme中删除这些令牌会导致运行时 CSS 变量未定义。**修复:**扫描 CSS 文本前,先剔除@theme inline/:root/.dark/等区块,然后收集剩余内容中的所有var(--X)引用。这些令牌必须保留。 -
类别交叉污染 Bug。 一个后缀可能出现在多个类别中。例如,
organization-section-surface既是颜色(white),也是阴影别名前缀(--shadow-organization-section-surface)。如果映射中同时有'organization-section-surface': 'card'(颜色)和'organization-section-surface': 'surface-soft'(阴影),第二条会覆盖全局映射中的裸令牌重命名。**修复:**只有 COLOR 类别重命名裸--X令牌。对于 SHADOW/SPACING/TEXT/RADIUS/FONT/TRACKING,只重命名--<theme-prefix>-X别名,绝不要重命名裸名称。 -
图标颜色过度合并问题。 将所有“灰色图标”(#696969、#898989、#b3b3b3、#c1c1c1、#dddddd)合并为单个
--icon令牌,会让原本较浅的图标明显变暗;而在深色表面(例如两种模式下都保持深色的下拉菜单)上,还会反转预期的对比度。**修复:**保留两种图标色调:--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-color)和--background-image-X(background-image)。如果只存在--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类用法。如果任一存在,就选择其他目标名称(例如,保留--install-button-text,而不是将其重命名为--install-button)。 -
始终深色表面问题。 下拉菜单、工具提示、保持深色的弹出层等所用的令牌,在浅色和深色模式下的文本/图标颜色都为浅色(因为表面无论主题如何都保持深色)。将
--dropdown-menu-text合并到--foreground会让下拉菜单中的文本在浅色模式下变暗(深色表面上的深色文本会导致不可见)。**修复:**合并令牌之前,比较其浅色模式和深色模式的值。如果两个值都位于 L=0.5 线的同一侧(都浅或都深),说明它属于始终浅色或始终深色的表面,不能与--foreground或--icon这类会随模式切换的令牌合并。应将其作为独立令牌保留。
三遍式块转换
生成 :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) |
| “这两个灰色基本一样”——却没有检查浅色和深色模式下的值 | 对比两种模式。浅色模式下接近的值,在深色模式下可能差异很大 |
| “我会把仅用一次的令牌内联为任意值,以便进一步减少数量” | 这只是在转移冗余内容。保留命名令牌;内联与合并精简的目标背道而驰 |
| “用户要求减少 70%——就算必须内联,我也要达到” | 应当提出异议。保留命名令牌、减少 50–60%,胜过使用任意值达到 70%。如实报告数字 |
| “脚本运行后,我会手动修复损坏的类” | 下次运行脚本时,你的修改会被还原。修复脚本,而不是产物 |
| “先写入者优先没问题,解析顺序是确定的” | 直到有人调整 :root 的顺序。使用显式的 CANONICAL_SOURCE 覆盖 |
“这个重命名目标名称没问题,不会冲突”——但已经存在 --background-image-<name> | 选择目标名称前,先搜索 --background-image-<target> 和 bg-<target> 的用法 |
宣布完成前
你必须:
- 运行
pnpm typecheck——退出码为 0。 - 运行
pnpm lint——0 个错误、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 个最显眼的界面区域(卡片、页面背景、下拉菜单)以预期颜色呈现。
构建成功但视觉效果损坏,任务仍算失败。统计出减少数量却未经验证,任务也算失败。