直接写死 box-shadow 值会失效,因其依赖阴影与背景色的动态匹配;应使用语义化 CSS 自定义属性(如 --light-source-top、--pressed-depth)驱动 box-shadow、渐变、圆角和 transform,实现主题自适应与物理真实感。

为什么直接写死 box-shadow 值会失效
拟物按钮的凹凸感依赖阴影与背景色的微妙匹配,不是套个 rgba(0,0,0,0.2) 就完事。在浅灰背景(如 #f8f9fa)上用深黑内阴影,会发灰发闷;换到深色主题(#1e1e1e),同样值又糊成一片。硬编码导致主题切换时按钮“塌陷”或“浮空”,失去物理真实感。
解决办法是把颜色锚定到背景:用 CSS 自定义属性 提前声明基础色、高光色、暗部色,再在 box-shadow 中引用。例如:
button {
--bg: #e9ecef;
--highlight: rgba(255, 255, 255, 0.7);
--shadow-dark: rgba(0, 0, 0, 0.12);
--shadow-deep: rgba(0, 0, 0, 0.2);
background-color: var(--bg);
box-shadow: 0 2px 4px var(--shadow-dark), inset 0 -2px 4px var(--highlight);
}
:active 状态下如何联动更新变量值
点击时不能只改 transform,必须同步加深内阴影、收窄外阴影——而这些都该由变量驱动,否则主题切换后 :active 效果就脱节了。
关键点:
立即学习“前端免费学习笔记(深入)”;
- 不要在
:active里重写整条box-shadow,而是覆盖对应变量 - 用
inset阴影方向保持一致(比如始终是inset 0 -Xpx),仅调深度和透明度 - 移动端需额外加
touch-action: manipulation,否则变量变更延迟导致点击无反馈
示例:
button:active {
--highlight: rgba(255, 255, 255, 0.3);
--shadow-deep: rgba(0, 0, 0, 0.25);
transform: translateY(1px);
box-shadow: inset 0 -4px 6px var(--shadow-deep), 0 1px 2px var(--shadow-dark);
}变量命名要带语义,别用 --c1 --c2
拟物效果靠的是光照逻辑,变量名得反映这个意图,否则协作或维护时根本看不懂哪层阴影对应哪块受力面。
推荐命名方式:
-
--light-source-top:模拟顶部高光,用于默认态inset 0 -2px的浅色内阴影 -
--pressed-depth:控制按下时内阴影的垂直偏移量(如-4px) -
--edge-softness:统一控制所有box-shadow的模糊半径,避免某层太锐利破坏质感
错误示范:--s1: 0 2px 4px rgba(0,0,0,0.1); —— 完全看不出用途,也无法被其他组件复用。
渐变 + 圆角 + 变量三者必须咬合
纯色按钮加再多阴影也是塑料感。拟物的真实感来自表面反光不均,所以 background 必须用 radial-gradient,且渐变中心、颜色、圆角都要和变量对齐。
实操要点:
- 圆角值不能固定
8px,应设为calc(var(--btn-height) * 0.3),保证不同尺寸按钮弧度协调 - 渐变起点用
circle at top,高光色取var(--light-source-top),避免硬切 - 加
background-clip: padding-box,防止渐变溢出圆角边界
否则变量再精准,视觉上仍是割裂的。
拟物按钮最难的不是写多少行 CSS,而是让阴影、渐变、位移、圆角全部基于同一套光照假设推演——变量只是把这套假设显性化。漏掉任意一环,凹凸感就断了。


















