因为CSS变量支持运行时主题切换、组件级样式覆盖和JS动态控制,而Sass变量编译后即固化为静态值,无法在DOM中读写或实时重绘;--primary-color是浏览器真实存在的可操作属性,$primary-color仅存于编译期。

因为 CSS 变量能真正支撑运行时主题切换、组件级样式覆盖和 JS 动态控制——Sass 变量编译完就消失,做不到这些。
为什么 $primary-color 不能替代 --primary-color
根本区别在于生命周期:$primary-color 是构建时静态值,输出后就固化;--primary-color 是浏览器真实存在的属性,DOM 可读可写。
-
document.documentElement.style.setProperty('--primary-color', '#ff6b6b')立刻触发重绘,所有var(--primary-color)自动更新 -
document.documentElement.style.setProperty('$primary-color', '#ff6b6b')直接报错:CSS property name is invalid - Vue/React 中
style="{ '--primary-color': themeColor }"能响应式更新;若 themeColor 来自 Sass 变量,则永远是编译那一刻的快照
为什么大型项目必须用 :root + 单一 design-tokens.css 文件
不是为了“规范好看”,而是避免硬编码导致的维护灾难。搜索替换 #007bff 会误伤注释、JS 字符串、带 alpha 的颜色值(如 #007bffaa),且无法区分语义——同一色值在按钮背景和危险文字中含义不同。
- 所有 Token 必须收口到一个
:root声明文件(如src/styles/design-tokens.css) - 禁止在组件内重复声明
--spacing-md,否则层级、优先级、覆盖逻辑全乱 - fallback 必须语义化:
color: #333; color: var(--text-color);,而非color: var(--text-color, #333)—— 后者在构建校验工具里无法识别降级意图
为什么 z-index 用变量反而更危险
--z-modal: 1000 写在 :root 里,等于给所有依赖它的组件埋雷:Ant Design 遮罩层内联 z-index: 1000,你调成 1001,内容反而被自己的遮罩盖住;uni-modal 这类挂载到 body 的第三方弹窗完全不受 scoped 影响。
立即学习“前端免费学习笔记(深入)”;
- 正确做法是分层基数:
--z-layer-1: 10、--z-layer-2: 100、--z-layer-3: 1000 - 组件内就近声明:
.modal { --z-element: calc(var(--z-layer-3) + 0); } - 数值差至少留 10,方便插中间层;避免嵌套
calc(var(--z-base) + 100),旧版 Safari 支持不稳定
为什么换肤必须用 CSS 变量,而不是 class 切换或 SCSS 多主题打包
class 切换要写 N 套重复规则,SCSS 多主题打包带来 FOUC、缓存断裂、transition 失效;而 CSS 变量只需动态改几行 setProperty,所有 var() 自动响应,连 SVG fill 和 box-shadow 颜色都能统一收敛。
- IE 不支持?加 fallback:
color: #333; color: var(--text-color); - 变量名必须加前缀防冲突,比如
--shop-theme-primary,别用--color - 真正难的不是设变量,而是把所有可变区域(包括渐变 stop、border-image、filter 参数)都映射到同一套语义变量体系里——多数团队只做了 70%,剩下 30% 在暗色模式边缘 case 里突然崩掉


















