最可靠多主题阴影方案是用--elevation-1至--elevation-4等语义化变量存储完整box-shadow字符串,因其不依赖构建工具、JS可读写、深色模式仅需重定义变量,且避免拆分变量导致的声明失效、维护复杂、调试困难等问题。

直接用 --elevation-1 到 --elevation-4 这类语义化变量存完整 box-shadow 字符串,是目前最可靠、最易维护的多主题阴影方案。它不依赖构建工具,JS 可读可写,深色模式切换也只需重定义这些变量。
为什么不能把偏移、模糊、颜色拆成独立变量再拼接
CSS 不支持字符串拼接,calc(var(--elev-x) + "px") 是非法语法,浏览器直接忽略;box-shadow: var(--elev-x) var(--elev-y) var(--elev-blur) var(--elev-color) 看似合理,但一旦某个变量未定义(比如 --elev-y 缺失),整条声明就会失效——因为 var() fallback 只对单个变量生效,无法兜底整个属性值。
更实际的问题是:你在深色模式下真的只想改颜色?还是连偏移和模糊都要微调?多数设计系统中,elevation-2 在浅色和深色下是两套完全不同的参数组合,硬拆只会让维护变复杂。
- 拆分变量写法冗长,每个用到阴影的地方都要重复调用 4–5 个
var() - 主题切换时需同步更新多个变量,漏一个就导致阴影错乱
- 无法利用浏览器 DevTools 的“颜色/长度”编辑器快速调试(它们只识别单个 CSS 值)
如何定义和使用 --elevation-N 变量
所有变量统一挂载在 :root 下,值为完整的 box-shadow 字符串,含 fallback:
立即学习“前端免费学习笔记(深入)”;
:root {
--elevation-1: 0 1px 2px rgba(0, 0, 0, 0.05);
--elevation-2: 0 2px 4px rgba(0, 0, 0, 0.1);
--elevation-3: 0 4px 8px rgba(0, 0, 0, 0.15);
--elevation-4: 0 8px 16px rgba(0, 0, 0, 0.2);
}
[data-theme="dark"] {
--elevation-1: 0 1px 2px rgba(0, 0, 0, 0.3);
--elevation-2: 0 2px 4px rgba(0, 0, 0, 0.4);
--elevation-3: 0 4px 8px rgba(0, 0, 0, 0.5);
--elevation-4: 0 8px 16px rgba(0, 0, 0, 0.6);
}
组件中直接调用:
.card {
box-shadow: var(--elevation-2, 0 2px 4px rgba(0, 0, 0, 0.1));
}
.button:focus {
box-shadow: var(--elevation-3, 0 4px 8px rgba(0, 0, 0, 0.15));
}
- 务必加 fallback,避免变量未定义时阴影彻底消失
- 深色模式用
[data-theme="dark"]而非@media (prefers-color-scheme: dark),后者无法响应 JS 主动切换 - 层级命名(
-1到-4)比-sm/-md更利于自动化(比如通过 JS 根据 z-index 动态计算)
多层阴影怎么放进一个变量里
box-shadow 多层本身就是逗号分隔的合法值,变量完全可以存整段:
:root {
--elevation-card:
0 1px 3px rgba(0, 0, 0, 0.1),
0 1px 2px -1px rgba(0, 0, 0, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
注意三点:
- 换行和空格不影响解析,但逗号后必须有空格或换行,否则可能被某些旧版 Safari 解析失败
- 不要在变量值里嵌套
calc()或rgba()中再套var(),IE 和部分安卓 WebView 会崩溃 - 多层阴影性能敏感,超过 4 层时滚动卡片区域可能出现掉帧,建议用
will-change: box-shadow提前提示渲染引擎
JavaScript 动态修改 elevation 变量的注意事项
JS 修改变量本质是改样式属性,不是“重新编译 CSS”:
document.documentElement.style.setProperty('--elevation-2', '0 4px 12px rgba(0,0,0,.25)');
但要注意:
- 修改后不会触发重排(reflow),但会触发重绘(repaint),频繁修改(如 scroll 事件中)需节流
- 若变量值含单位(如
px),JS 中必须显式带上,setProperty('--elev-y', 4)无效 - 服务端渲染(SSR)场景下,首次 JS 执行前变量值来自 HTML 中的
:root,确保初始值与主题一致
真正容易被忽略的是:elevation 的“深度感”不只取决于模糊半径,还高度依赖背景对比度。同一组 --elevation-3 值,在纯白背景和深灰背景上视觉层级可能完全失真——所以主题变量不只是换个颜色,而是整套光照模型的映射。


















