CSS变量的局部化与文件模块无关,仅取决于声明位置;必须绑定到组件根选择器(如.button-root)才能实现真正局部作用域,:root或顶层声明均为全局。

CSS变量和CSS模块化根本不是一回事,它们不冲突,但也不自动协同——CSS Modules 不处理 --* 变量的作用域,你写在 .module.css 文件里的 :root { --color: red } 依然是全局的。
为什么 CSS Modules 对 --* 变量完全没用
CSS Modules 的核心动作只有类名哈希化,它不解析、不重写、不封装任何 var(--x) 或 --x: y。浏览器读到 :root { --primary-color: #007bff },不管这行代码来自 Button.module.css 还是 App.css,它都直接挂进全局 CSSOM。
- 常见错误:在
Modal.module.css里写:root { --modal-z-index: 1050 },结果所有用var(--modal-z-index)的组件(包括 Header、Toast)全被这个值影响 - 构建工具(如 Vite 的
css-loader)只匹配.className类选择器,对--xxx视而不见 -
<style scoped>在 Vue SFC 中同样无效——scoped只加属性选择器,不隔离自定义属性
真正局部化 CSS 变量的唯一办法:绑定到组件根选择器
别指望文件或模块名能“天然”限制变量作用域。要让 --button-bg 只在按钮内生效,必须把它声明在按钮容器元素上,靠 CSS 继承向下透传。
- 正确写法:
.button-root { --button-bg: #007bff; --button-text: white; },然后子元素用.button { background-color: var(--button-bg); } - 多个
.button-root实例互不影响——每个都维护自己的变量副本 - 禁止在
:root或顶层写组件级变量,哪怕它只在当前文件里;否则就等于主动广播给全站 - Hover 等交互状态直接改根元素变量:
.button-root:hover { --button-bg: #0056b3; },子元素自动响应
和 Sass/Less 变量混用时最容易踩的坑
Sass 的 $color 是编译期静态值,CSS 变量 --color 是运行时动态属性,二者不能嵌套、不能互相赋值。
立即学习“前端免费学习笔记(深入)”;
- 错误:
--bg: $brand-blue;—— Sass 不认识--bg,会原样输出成非法 CSS - 错误:
$btn-bg: var(--primary-color);—— Sass 编译时无法读取运行时变量,结果变成background: var(--primary-color);,但语义已丢失 - 安全做法:Sass 负责生成基础色板(
$blue-500: #007bff),再在组件根选择器里用它初始化 CSS 变量:.button-root { --button-bg: #007bff; } - 如果需要主题切换,CSS 变量必须由 JS 控制:
el.style.setProperty('--button-bg', theme.color),Sass 无法参与
最常被忽略的一点:变量是否“局部”,和它写在哪份文件里毫无关系,只取决于它声明在哪个 DOM 节点的选择器下。哪怕你把所有变量都塞进一个 theme.module.css,只要它们挂在 :root 上,就还是全局的。


















