PostCSS静态扫描废弃CSS变量的核心是源码文本匹配与AST分析,覆盖var(--x)调用和--x声明,排除伪类误匹配,不支持模板字符串拼接及动态setProperty,需结合作用域级联验证与大小写敏感检查。

用 PostCSS 插件静态扫描 var(--xxx) 引用
检测废弃 CSS 变量的核心是:不依赖运行时,只靠源码文本匹配 + CSS AST 分析。PostCSS 是最轻量、最可控的选择,尤其适合 Design Token 场景。
关键点在于正则必须覆盖所有可能的引用形式:var(--token-name)、--token-name(自定义属性声明)、color: var(--primary);、甚至模板字符串里的 \`var(--\${theme})\` —— 但注意:后者需额外处理变量拼接,静态扫描默认不支持,应避免。
- 推荐正则:
/var\(\s*--[a-zA-Z0-9-_]+\s*\)|--[a-zA-Z0-9-_]+(?=\s*:)/g,它能捕获var()调用和顶层声明,但排除伪类选择器中的误匹配(如:--foo) - 扫描范围必须包含所有能写 CSS 变量的地方:TSX/TS/Vue/HTML 模板、JS 字符串、甚至 Markdown 中的代码块(如有内联样式)
- 不要依赖 CSS 文件自身内容做“反向查找”——
:root { --x: 1; --y: 2 }里声明了 100 个变量,不代表它们都被用了;真正要扫的是“谁在消费”
为什么不能只靠 PurgeCSS 或 Tailwind JIT
PurgeCSS 类工具只识别类名、ID、属性选择器等 HTML/CSS 选择器层面的使用痕迹,对 var(--xxx) 这种 CSS 自定义属性引用完全无感。Tailwind JIT 同理——它摇树的是 class="text-primary" 这类 class,不是 color: var(--primary)。
常见误操作是把 PurgeCSS 配置里的 content 列表当成万能扫描入口,结果发现 --spacing-md 明明在 JS 里被 el.style.setProperty('--spacing-md', '12px') 动态设置,PurgeCSS 却没扫到——因为它不解析 JS 执行逻辑,只做字符串匹配。
立即学习“前端免费学习笔记(深入)”;
- 动态设置(
setProperty、CSSStyleDeclaration)必须单独处理,PostCSS 插件无法覆盖 - 如果项目用
styled-components或emotion的插值语法(如color: ${props => props.theme.primary}),那得靠 Babel 插件或 TS AST 分析,PostCSS 无效 - Tailwind 的
@layer utilities里定义的@apply var(--x)?别试了,Tailwind 不支持@apply解析var()
执行剪枝前必须验证 :root 块结构
剪枝不是简单删行。CSS 变量常以级联方式组织::root 声明基础 token,.dark :root 覆盖深色模式值,[data-theme="high-contrast"] :root 再覆盖高对比度。直接删掉未使用的 --x,可能导致某个主题分支缺失 fallback 值。
正确做法是:先提取所有 :root 及其变体作用域下的声明节点,再逐个检查该作用域内是否被当前扫描出的 usedTokens 集合引用。例如:
/* ✅ 安全删除:整个 .dark 块里都没用 --spacing-xs */
.dark :root {
--spacing-xs: 4px; /* ← 未在任何 dark 模式相关文件中出现 */
--color-text: #333;
}
<p>/<em> ❌ 不能删:--color-text 被用了,但 --spacing-xs 是陪绑的 </em>/
.light :root {
--spacing-xs: 2px;
--color-text: #666;
}- PostCSS AST 中,
Rule节点的selector属性可判断是否为:root或带条件的作用域 - 剪枝时保留空的
:root块是安全的,但删除后若导致后续@import或@layer顺序错乱,可能引发 CSS 优先级问题 - 如果变量被
@property显式注册(用于动画或类型约束),即使未被var()引用,也不应删除——它属于 CSS Houdini API 的运行时契约
上线前必查的三个静默失效点
废弃变量清理后,最危险的不是删错了,而是删得“太干净”导致某些路径下颜色/间距突然归零。这类问题不会报错,只会让 UI 在特定设备或主题下失常。
-
var(--x, fallback)中的--x被删了,但 fallback 值本身也无效(比如fallback是另一个已被删的变量),整条声明退化为var(--x, --y)→var(--x, initial)→ 最终计算为initial - CSS Custom Property 在
@keyframes中被引用,但动画本身未在任何animation属性中启用——这种“死引用”会被静态扫描漏掉 - 构建产物里 CSS 变量名被 CSS Minifier(如 cssnano)压缩重命名(如
--btn-bg→--a),而 JS 里仍写var(--btn-bg),导致运行时取不到值。必须关掉 minifier 的自定义属性压缩(cssnano 默认不开,但某些 preset 会开)
真正容易被忽略的是:变量名大小写敏感,且 CSS 中 --MyVar 和 --myvar 是两个变量,而 JS 字符串扫描时若没加 i 标志,会漏掉一个。别假设团队命名规范统一——查一下 git history 就知道。


















