最可靠方式是用 :root 定义语义化 CSS 变量存完整 box-shadow 值,如 --shadow-card;必须用 var() 调用且仅用于属性值位置,支持 fallback;禁止拆分参数拼接,因 CSS 不支持字符串运算;Sass 变量更适合构建时逻辑;需注意 iOS 模糊半径限制、深色模式重定义及负 spread-radius 的毛边问题。

用 :root 定义语义化 CSS 变量存完整阴影值
直接把整条 box-shadow 值赋给变量,是最简单也最可靠的封装方式。比如卡片浮起效果就该叫 --shadow-card,而不是 --shadow-0204 这类像素导向的命名。
必须挂载在 :root 下才能全局生效:
code:root {
--shadow-card: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
--shadow-input-focus: 0 0 0 2px rgba(59, 130, 246, 0.2);
}
调用时务必用 var() 函数,且只能出现在属性值位置:
-
card { box-shadow: var(--shadow-card); }✅ -
card { box-shadow: --shadow-card; }❌(浏览器忽略非法值) -
card { var(--shadow-card): ...; }❌(var()不能用在属性名上)
加 fallback 是防错关键:box-shadow: var(--shadow-card, 0 2px 4px rgba(0,0,0,.1));
别拆参数拼接,CSS 不支持字符串运算
有人想分别定义 --shadow-x、--shadow-blur,再用 var(--shadow-x) var(--shadow-y) var(--shadow-blur) 拼成 box-shadow——这行不通。
CSS 没有字符串拼接能力,calc(var(--shadow-x) + "px") 是无效语法,浏览器会静默失败。
更实际的问题是:你真需要单独换模糊半径而不动偏移和颜色?多数场景中,三者是视觉绑定的。强行拆开反而增加维护成本,还容易漏掉某一层阴影的同步更新。
多层阴影(比如外光晕 + 内压边)也应存为一整串逗号分隔的值,而非拆成多个变量再合并。
Sass 变量更适合构建时逻辑控制
如果项目用 Sass,优先用 $shadow-card 这类变量,而不是依赖运行时 CSS 变量。
原因很实在:
- 能参与
calc()和字符串拼接,比如悬停时叠加增强:box-shadow: #{$shadow-card}, #{$shadow-sm}; - 可嵌套进
map实现主题批量切换:$shadows: (card: 0 1px 3px rgba($black, .1), ...); - 颜色可复用全局色变量:
rgba($primary, .15),换主题时自动响应
注意:Sass 变量无法被 JS 动态修改,适合构建期统一风格;CSS 变量适合运行时主题切换。两者不冲突,可以用 Sass 输出 CSS 变量::root { --shadow-card: #{$shadow-card}; }
移动端与深色模式下容易忽略的细节
模糊半径超过 24px 在部分 iOS Safari 中会退回到 CPU 渲染,导致滚动卡顿,这不是 bug,是已知限制。
深色模式下,同一 rgba(0,0,0,.1) 在暗背景上几乎不可见,必须配合 @media (prefers-color-scheme: dark) 重定义变量值:
code[data-theme="dark"] {
--shadow-card: 0 1px 3px rgba(255, 255, 255, 0.08), 0 1px 2px rgba(255, 255, 255, 0.04);
}
另外,带 border-radius 的元素用负 spread-radius 控制单边阴影时,圆角处容易因裁切不齐出现毛边——这时负 spread 值得微调,不是越大越好。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











