讲解

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() 带兜底,值是文本替换。最后一章总结全教程的工程规范。