讲解
CSS 自定义属性(俗称 CSS 变量)以 -- 开头声明,用 var() 读取:--brand: #1c7ed6; color: var(--brand);。它和 Sass 变量的本质区别是:CSS 变量是「运行时的、活的」——遵循层叠与继承规则,可以在不同选择器上重新定义,可以被媒体查询改写,甚至能被 JS 在运行时修改(element.style.setProperty('--x', '10px')),改动即时反映到所有引用处。Sass 变量编译后就消失了,CSS 变量活在页面里。
惯例是在 :root(等价于 html 选择器,优先级更高)上定义「设计令牌」(design tokens):品牌色、间距阶梯、圆角、阴影、z-index 档位,全站引用。组件作用域的变量定义在组件根类上,天然实现「主题隔离」。var() 支持兜底值:var(--card-bg, #fff) 在变量未定义时用 #fff,还可以多层嵌套 var(--a, var(--b, #fff))。
主题切换是自定义属性的招牌应用:把颜色变量按 [data-theme='dark'] 或 @media (prefers-color-scheme: dark) 重新定义,组件代码零改动全站换肤。两个进阶点:变量值是「原样替换」的文本,--gap: 16; margin: var(--gap) 会因缺单位而失效(写 --gap: 16px);想让自定义属性可过渡/动画(比如渐变色相),需要用 @property 注册类型,这是 2023 年后的现代能力。
示例
/* :root 上定义设计令牌 */
:root {
--brand: #1c7ed6;
--text: #343a40;
--bg: #ffffff;
--radius: 8px;
--space-2: 8px;
--space-4: 16px;
}
/* 深色主题:只改变量,组件不动 */
[data-theme='dark'] {
--text: #e9ecef;
--bg: #1a1b1e;
}
/* 组件全部引用变量 */
.card {
background: var(--bg);
color: var(--text);
border-radius: var(--radius);
padding: var(--space-4);
}
.button {
background: var(--brand);
color: #fff;
}
/* 组件级变量 + var() 兜底 */
.badge {
--badge-bg: #ffe8cc; /* 默认橙色,可被实例覆盖 */
background: var(--badge-bg, #f1f3f5);
}
.badge--success {
--badge-bg: #d3f9d8; /* 只改变量即变体 */
}
常见坑
- 在媒体查询条件里用 var():@media (min-width: var(--bp)) 无效——媒体查询在变量解析之前生效。断点不能用 CSS 变量(这是 Sass 变量残留的习惯)。
- 值缺单位:--size: 16 再 width: var(--size) 会失效,变量是文本替换不是数字。值里带上单位。
- 大小写混用:--Brand 和 --brand 是两个不同变量,自定义属性大小写敏感。统一小写连字符命名。
- 层层覆写找不到来源:变量可以在任意层级重定义,组件表现异常时在 DevTools 的 Computed 面板里搜变量名,能看到它在哪条规则上被定义、最终值是多少。
- 把变量当常量滥用嵌套兜底:var(--a, var(--b, var(--c, red))) 三层兜底没人读得懂。兜底一层为宜,默认值尽量在 :root 显式定义。
小结
自定义属性是运行时的层叠变量::root 定义设计令牌、组件级覆写做变体、配合媒体查询做主题;var() 带兜底,值是文本替换。最后一章总结全教程的工程规范。