mcpField
T
tailwind-token-consolidation

tailwind-token-consolidation

github.com/millionco设计与 UI3448 次安装17 周前更新
代码仓库

关于 tailwind-token-consolidation

用于精简 Tailwind v4 `globals.css` 设计令牌的工作流技能,重点是在保留视觉效果的同时减少令牌。它规定了使用情况分析、谨慎删除与合并、脚本化重命名及构建验证流程。

主要功能

  1. 01映射项目文件中的令牌用法
  2. 02传递式追踪仍在使用的令牌
  3. 03安全删除未使用令牌
  4. 04合并值相近的令牌
  5. 05通过脚本应用重命名
  6. 06验证构建与视觉值

适用场景

  1. 01审查 Tailwind v4 令牌集
  2. 02减少重复设计令牌
  3. 03保留作用域与深色模式样式

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/millionco/skills --skill tailwind-token-consolidation

安装后在 Claude Code 中输入/tailwind-token-consolidation确认已加载。

详细介绍

Tailwind 令牌整合

在保留视觉保真度的同时,减少 Tailwind v4 globals.css 中设计令牌的数量。

每项整合任务都必须按以下顺序执行:

  1. 先审计,绝不先编辑。 将 globals.css 解析为结构化区块(@theme inline、:root、.dark 及所有其他作用域区块)。统计令牌数量。扫描所有 .ts/.tsx/.css/.mdx 文件(排除 globals.css),通过类名和 var(--...) 引用构建用法映射。
  2. 传递式计算 LIVE 集合。 满足以下任一条件的令牌即为 live:(a)其后缀在任何位置作为 Tailwind 类出现;或(b)在任何位置以 var(--name) 的形式被引用;或(c)在另一个 live 令牌的值中被引用。迭代直至达到固定点。
  3. 也要扫描 globals.css 自身的工具类。 .X { ... } 区块( :root、.dark 和 @theme inline 之外)中的任何 var(--...) 都是硬依赖。这些类所需的令牌必须保留。
  4. 将确定性的重命名映射构建为数据(old_suffix -> new_suffix 或 DELETE)。通过单个脚本将其应用到 globals.css 和代码中。绝不要手动编辑整个代码库中的类名。
  5. **每轮完成后验证:**运行 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(请提前防范)

  1. 值优先级 Bug。 将 N 个令牌合并为一个规范名称时,解析顺序决定的“先写入者胜出”不可预测。结果往往会得到错误的颜色(例如,--danger 最终采用了 --diagnostics-table-error-border 的值,也就是浅色边框色调,而不是鲜亮的红色)。**修复:**添加显式的 CANONICAL_SOURCE 覆盖字典,将新令牌映射到其值应胜出的特定源令牌。

  2. 作用域覆盖 Bug。 .dashboard-page { --text-inline: 14px; } 之所以有效,只是因为 --text-inline 是一个通过 var() 引用的自定义属性。任何作用域区块(.dashboard-page、.dark .dashboard-page 等)中被覆盖的令牌都必须保留为令牌;删除它们会悄无声息地破坏作用域覆盖。

  3. globals 内部消费者 Bug。 globals.css 中的工具类(例如,.dropdown-menu-highlight { padding: var(--spacing-dropdown-menu-padding); })会读取任何 .tsx 文件中都未出现的令牌。从 @theme 中删除这些令牌会导致运行时 CSS 变量未定义。**修复:**扫描 CSS 文本前,先剔除 @theme inline/:root/.dark/等区块,然后收集剩余内容中的所有 var(--X) 引用。这些令牌必须保留。

  4. 类别交叉污染 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 别名,绝不要重命名裸名称。

  5. 图标颜色过度合并问题。 将所有“灰色图标”(#696969、#898989、#b3b3b3、#c1c1c1、#dddddd)合并为单个 --icon 令牌,会让原本较浅的图标明显变暗;而在深色表面(例如两种模式下都保持深色的下拉菜单)上,还会反转预期的对比度。**修复:**保留两种图标色调:--icon(约为 #696969 的中灰色)和 --icon-soft(约为 #b3b3b3 的浅灰色)。将原本较浅的图标映射到 --icon-soft。

  6. 具有条件透明度的表面问题。 某些令牌在一种模式下的值为 transparent,在另一种模式下则是具体颜色(例如,浅色模式下 --repository-card-panel: #ffffff,深色模式下为 transparent——深色版本有意保持透出效果)。将其合并到 --card 会让面板在深色模式下变得不透明,明显改变层叠效果。**修复:**如果某个令牌在一种模式下的值为 transparent,在另一种模式下为具体颜色,就不要将它与始终不透明的令牌合并。应将其单独保留,或在使用它的 className 中内联条件逻辑。

  7. 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)。

  8. 始终深色表面问题。 下拉菜单、工具提示、保持深色的弹出层等所用的令牌,在浅色和深色模式下的文本/图标颜色都为浅色(因为表面无论主题如何都保持深色)。将 --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> 的用法

宣布完成前

你必须:

  1. 运行 pnpm typecheck——退出码为 0。
  2. 运行 pnpm lint——0 个错误、0 个警告。
  3. 运行 pnpm format——已完成。
  4. 运行 pnpm --filter web build——退出码为 0。
  5. 使用 rg "^\s*--[a-z][a-zA-Z0-9-]*\s*:" apps/web/app/globals.css -o | sort -u | wc -l 统计最终令牌数量。报告修改前后的数量及减少百分比。
  6. 通过从头到尾阅读最终的 :root 块,并确认规范值与预期一致,确认至少 3 个最显眼的界面区域(卡片、页面背景、下拉菜单)以预期颜色呈现。

构建成功但视觉效果损坏,任务仍算失败。统计出减少数量却未经验证,任务也算失败。

评论

支持 Markdown