使用 var() 时必须提供合法回退值,否则变量未定义会导致样式失效为 unset;CSS 无法检测自定义属性是否定义,须通过 :root 预设默认值或 JS 控制类名切换主题。

使用 var() 时必须提供回退值,否则会失效
当浏览器不支持 CSS 自定义属性(即 --my-color 未定义或被忽略),var(--my-color) 会直接计算为 unset,进而可能触发继承、初始值或布局塌陷。这不是“取不到就用默认”,而是“取不到就丢弃”。所以回退值不是可选项,是安全底线。
常见错误是只写 color: var(--text-color);,一旦 --text-color 未声明,这段样式实际等价于 color: unset; —— 在多数元素上表现为透明或继承父级,极易引发视觉异常。
- 回退值必须是合法的 CSS 值,且类型需与使用上下文兼容(例如在
background中不能回退为14px) - 可嵌套使用
var()作为回退:var(--primary, var(--fallback, #007bff)),但注意仅支持一层间接回退(Chrome 115+ 开始支持多层,旧版仍受限) - 回退值里不能包含
var()以外的函数(如calc()、rgb()),否则整个var()声明会被视为无效
用 :not(:defined) 或媒体查询检测自定义属性支持?别试了
CSS 没有原生机制检测某个 --xxx 是否已定义。所谓“特性查询”如 @supports (--color: red) 是无效语法,CSS 规范明确禁止在 @supports 中检查自定义属性——它永远返回 false。同理,:not(:defined) 只适用于自定义元素(Custom Elements),对 CSS 变量完全无感。
这意味着你无法用纯 CSS 实现“如果变量存在就用它,否则走另一套样式”的分支逻辑。所有“条件式回退”都必须靠预设和层级控制来模拟。
立即学习“前端免费学习笔记(深入)”;
- 真正可行的做法是:在根节点(
:root)中集中声明带合理默认值的变量,确保即使 JS 未执行或变量未注入,页面仍有可用样式 - 若需运行时动态切换主题,应由 JS 控制类名(如
theme-dark),再用.theme-dark { --bg: #1a1a1a; }覆盖,而非依赖“检测后设置” - 构建工具(如 PostCSS)可做静态回退注入,但那是编译期行为,不属于运行时 CSS 能力
回退值写成 initial 或 inherit 很危险
initial 和 inherit 看似“通用”,实则极易引发不可控行为。比如 border: var(--border, initial) 在 button 上会变成 border: medium none currentColor(即 3px 无边框),而你本意可能是 1px solid #ddd;又如 font-size: var(--size, inherit) 在嵌套深的组件里可能意外放大或缩小。
- 优先使用具体字面值:数字、颜色、长度单位、关键字(
auto、none、solid等) - 避免用
unset作回退——它等于inherit或initial的组合,语义模糊且难调试 - 若真需继承行为,显式写
inherit并加注释说明理由,而不是当作“省事兜底”
JS 注入变量时,要防覆盖、防竞态、防空字符串
通过 document.documentElement.style.setProperty('--color', value) 设置变量很常见,但几个细节常被忽略:
- 空字符串
''、undefined、null传入后,实际设置的是无效值,导致后续var(--color, red)仍走回退(因为变量“存在”但值非法) - 多个 JS 模块同时调用
setProperty可能产生竞态,建议封装统一 setter,或用Map缓存待设值,批量提交 - 不要在非
:root元素上随意设变量并期望全局生效——只有:root或显式继承链上的变量才能被子元素var()正确读取 - 调试时可用
getComputedStyle(document.documentElement).getPropertyValue('--color')查真实值,注意返回值带空格且不含单位(如" 16px ")
最易被忽略的一点:回退机制不是用来掩盖变量管理混乱的。如果发现大量地方依赖多层回退(var(--a, var(--b, var(--c, #000)))),通常意味着变量职责不清晰或作用域失控——该重构变量命名和注入时机,而不是堆砌回退。


















