PostCSS 不解析 CSS 自定义属性,需插件在消费前注入或替换;postcss-custom-properties 仅静态替换,不支持动态主题或嵌套 fallback;多主题应通过构建期 JSON 注入实现,且顺序须为 postcss-import → 变量注入 → postcss-preset-env → postcss-custom-properties → autoprefixer。

PostCSS 本身不解析或运行时处理 CSS 自定义属性(--color-primary),它只做静态 AST 操作;想让自定义属性“生效”或参与构建期逻辑(如主题生成、颜色校验、单位转换),必须靠插件介入,且关键在于:插件必须在自定义属性被消费前完成替换或注入。
postcss-custom-properties 只做静态替换,不是运行时变量
这个插件会把 :root { --c: red; } 和 div { color: var(--c); } 编译成 div { color: red; },但它不会感知环境变量、无法切换主题、也不支持嵌套计算(比如 var(--c, var(--fallback)) 中的 fallback 层级)。常见错误是把它当“CSS-in-JS 变量”用——结果开发时改了 JSON 配置,但 CSS 没更新,因为插件只读取当前 CSS 文件里的 :root 块。
- 它只处理已声明的
var(),对未声明变量默认留空或报错(取决于preserve配置) - 不支持
color-mix(from ...)或hsl(from ...)这类新函数中的变量引用,那些需postcss-preset-env提前转义 - 若你用
@import引入变量文件,必须确保postcss-import在它之前执行,否则插件看不到跨文件声明
多主题/动态变量要用构建期注入,别依赖 runtime
真正需要环境区分(如 dark/light 主题)、Figma Token 同步、或从 colors.json 生成 CSS 的场景,硬写 :root 块 + postcss-custom-properties 就会卡死。这时候得换思路:用插件在构建时把 JSON 数据直接注入 AST。
- 推荐
postcss-advanced-variables:支持从外部 JSON 加载,并生成带注释的源码(比如/* from colors.json: primary */),方便调试 - 自己写插件更可控:读取
process.env.THEME,动态插入不同:root块,再让后续插件(如postcss-custom-properties)去替换 - 注意时机:注入必须在
postcss-custom-properties之前,否则它看不到新注入的变量声明 - 避免覆盖:如果项目里既有手写的
:root,又有注入的,得用插件逻辑判断优先级,否则后者可能被前者覆盖
autoprefixer 和 postcss-preset-env 对自定义属性的处理差异
这两个插件都可能和自定义属性打交道,但行为完全不同:
立即学习“前端免费学习笔记(深入)”;
-
autoprefixer完全不碰var(),它只管属性名(如display)和值里的标准关键字(如flex),var(--gap)中的--gap它直接跳过 -
postcss-preset-env会识别并降级新语法,比如把color: color-mix(in srgb, var(--a), var(--b));转成兼容格式,但它不会展开var()—— 那是postcss-custom-properties的事 - 顺序必须是:
postcss-import→ 变量注入插件 →postcss-preset-env→postcss-custom-properties→autoprefixer - 漏掉
postcss-preset-env,aspect-ratio: var(--ar)这种写法在旧浏览器里会直接失效,因为aspect-ratio本身就需要降级
最容易被忽略的是:自定义属性的“作用域”在 PostCSS 构建期就固定了。你不能指望它像 JS 那样在页面加载后根据用户操作重算 —— 那属于 runtime 主题切换,得靠 class 切换 + CSSOM 操作,和 PostCSS 无关。


















