一言以蔽之
CSS 的 box-shadow 属性可以让你从元素的框架投射出一个或多个阴影,从而在网页上营造出深度、悬浮或内发光的效果。
它解决了什么问题
在早期网页设计的“二维世界”里,所有东西都是平的。没有深度,没有高度。如果你想让某个东西脱颖而出,你只能把它做得更大或者颜色更亮。如果你真的想加个阴影让按钮看起来更像能点的,那你可就有的受了。
当时的“解决方案”是打开 Photoshop,创建一个带投影的盒子,然后把它导出为图片。接着,你把这张图片用作元素的背景。说得客气点,这是一种极其糟糕的取巧方式。这些图片文件很大,拖慢你的网站;它们在高分辨率屏幕上看起来全是像素点;如果盒子尺寸需要改变,它们也无法缩放。而且,如果设计师想把阴影从“模糊的灰色”改成“稍微再模糊一点的灰色”,你就得回到 Photoshop 从头再来。这套方法脆弱、低效,简直是维护的噩梦。
然后,CSS3 如同超级英雄般横空出世。box-shadow 属性让开发者能用代码创建阴影。突然之间,阴影不再是笨重的位图产物;它们成了文档本身动态、可缩放、可动画的一部分。
起初,开发者们用它来创建和 Photoshop 里一样简单的投影效果。但 box-shadow 的真正威力并非只是复刻旧方法,而是开创了新纪元。它成为了像 Google Material Design 这类设计系统的基石,这些系统利用阴影和高度来传达层级关系,创造出一种有触感的、类似纸张的界面。box-shadow 通过提供一种原生的、基于代码的方式,让我们直接在浏览器里用光影作画,从而解决了用图片伪造深度的问题。
底层工作原理
乍一看,box-shadow 似乎很简单。但就像一个精彩的魔术,它的优雅之下隐藏着惊人的深度(一语双关,你懂的)。
单个阴影的剖析
一个阴影由一系列值定义,这些值告诉浏览器把它放在哪里、多模糊、多大以及用什么颜色。
基本语法是 box-shadow: offset-x offset-y blur-radius spread-radius color;。我们来分解一下。
| 参数 | 作用 | 示例值 |
|---|---|---|
offset-x |
水平移动阴影。正值向右,负值向左。 | 10px |
offset-y |
垂直移动阴影。正值向下,负值向上。 | 10px |
blur-radius |
控制模糊度。0 是锐利、边缘清晰的阴影。值越大,模糊效果越柔和、扩散。 |
20px |
spread-radius |
扩大或缩小阴影的源形状。正值使阴影比元素大;负值则使其缩小。 | 5px |
color |
阴影的颜色。使用像 rgba(0, 0, 0, 0.1) 这样的半透明颜色是实现逼真效果的关键。 |
rgba(0,0,0,0.5) |
只有 offset-x 和 offset-y 是必需的,但一个没有模糊或颜色的阴影也算不上什么阴影。
/* 一个简单的阴影:向下 10px,向右 10px,模糊 20px,50% 透明度的黑色 */
.card {
box-shadow: 10px 10px 20px rgba(0, 0, 0, 0.5);
}
inset 关键字:深入盒子内部
默认情况下,阴影是从元素向外投射的,就好像元素浮在页面之上。通过在开头添加 inset 关键字,你可以完全反转这个效果。
.pressed-button {
/* 这个阴影被投射在元素的 padding-box *内部* */
box-shadow: inset 5px 5px 10px rgba(0, 0, 0, 0.2);
}
内嵌阴影绘制在元素的边框内部、背景之上。这对于创建“被按下”的按钮效果、看起来凹陷的输入框或图片上的暗角效果来说是完美的。这就像是在页面上进行雕刻,而不是在其上搭建。
叠加阴影:实现逼真深度的秘诀
这才是真正体现艺术性的地方。box-shadow 属性可以接受一个用逗号分隔的阴影列表。这是创建看起来自然、而不是像 2008 年 Photoshop 效果那样笨拙的阴影的秘诀。
现实世界中的阴影不是一个单一、均匀的色块。想想你桌上的一个物体。通常在它正下方会有一个小而深、边缘清晰的阴影(本影),还有一个更大、更柔和、更透明的向外扩散的阴影(半影)。
我们可以通过叠加多个 box-shadow 定义来复制这一点。浏览器会把它们堆叠起来,列表中第一个阴影在最顶层。
.super-card {
box-shadow:
/* 第 1 层 (顶层): 一个紧凑、深色的阴影,用于“本影” */
0 4px 6px -1px rgba(0, 0, 0, 0.1),
/* 第 2 层 (底层): 一个更宽、更柔和的阴影,用于“半影” */
0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
这段小小的代码片段创建的阴影,比任何单层阴影都要真实得多。通过组合两个、三个(或更多!)微妙的阴影,你可以创造出令人信服的深度和高度错觉,让你的 UI 感觉更具体、更精致。这项技术是许多现代设计系统的基础,并且常常有工具为你生成这些分层堆栈。
真实世界的案例
“悬浮”操作按钮的案例
一家初创公司正在开发他们热门移动应用的网页版。其标志性的 UI 元素是一个悬浮在右下角的浮动操作按钮(FAB)。负责这个任务的初级开发者随手给它加了个 box-shadow: 0 5px 10px black;。在设计评审时,首席设计师皱起了眉头。“它看起来不像在漂浮,”她说。“看起来像是沾上了一块奇怪的深色污迹。”那个按钮看起来像是粘在页面上,而不是悬浮在上面。
这位开发者感到很沮丧,于是深入研究了高级阴影技术。他发现了分层。通过用三层阴影堆栈替换掉那个单一、生硬的阴 ઉ,他破解了难题。第一层是紧贴按钮下方一个非常小、相对较暗的阴影,用来“固定”它。第二层是一个中等大小、颜色稍浅的阴影,用来定义它的形状。第三层是一个大范围、非常透明且高度模糊的阴影,用以模拟光线扩散到周围区域。结果是革命性的。按钮现在看起来真的有了悬浮感,投射出一个柔和、复杂的阴影,赋予了它真实的高度感。
经验教训: 逼真的深度来自于模仿真实光线。叠加大小、模糊度和透明度各不相同的微妙阴影,是让元素“浮动”起来的关键。
性能消失之谜
一家数据分析公司推出了一个漂亮的新仪表盘。上面布满了卡片,每张卡片显示一个不同的指标,并且都有一个很酷的悬停效果:卡片会随着阴影的扩大而“升起”。在他们的高端开发笔记本上,效果如丝般顺滑。但很快,技术支持的工单就如雪片般飞来。“新仪表盘太慢了!”“滚动时我的电脑风扇转得像喷气式发动机!”
性能团队介入调查。使用浏览器的开发者工具,他们发现在用户每次滚动或悬停在卡片上时,性能分析器的时间轴里都是一片红。浏览器在“Paint”(绘制)上花费了大量时间。罪魁祸首是什么?每张卡片在悬停时都有一个单一、巨大的阴影:box-shadow: 0 50px 100px -20px rgba(0,0,0,0.5);。那个巨大的模糊半径(100px)意味着浏览器在动画的每一帧都必须重绘屏幕上的一大片区域。
解决方案有两步。首先,他们用一个更优化的、多层阴影替换了那个昂贵的单一阴影,用小得多的模糊半径达到了类似的效果。其次,对于悬停效果,他们过渡的是 transform: scale(1.05) 属性,而不是 box-shadow 本身。这对浏览器来说,动画成本要低得多。卡顿消失了,用户也满意了。
经验教训: 天下没有免费的午餐。一个大而模糊的阴影带来的视觉吸引力是有实实在在的性能成本的。要留意模糊半径,尤其是在给阴影添加动画时。
那个“没按下去”的内凹按钮
一位 UI 设计师想要一组切换筛选器,在激活时看起来像是“被按进去”了。一位开发者初次尝试时,只是用了一个简单的类切换:激活的按钮获得一个更深的背景和一个 1px 的实线 inset 边框。它能用,但感觉不对。它看起来像一个平坦的按钮,里面画了一条线,而不是一个被按入表面的按钮。
一位资深开发者看到了代码,并给出了一个单行建议。“试试 box-shadow: inset ...”。他们没有用边框来伪造效果,而是使用了真正的内嵌阴影。顶部和左侧是一个柔和、深色的 inset 阴影,底部和右侧是一个非常浅、近乎白色的 inset 阴影。这完美地模拟了来自左上方的光源在“凹陷”区域内投下阴影,并在相对边缘产生高光。效果立竿见影,非常逼真。按钮不再仅仅是看起来“激活”了;它看起来像是物理上被按了进去。
经验教训: 当你想创建凹陷或内凹效果时,不要跟边框和背景较劲。box-shadow 的 inset 关键字正是为此而生的工具,专门用来创建那种内阴影的错觉。
常见误区和陷阱
- 使用纯黑色: 现实世界中没有任何东西会投下
#000000的阴影。真实的阴影会受到环境光的影响,并从周围环境中吸收颜色。一个巨大的进步是使用半透明的黑色,比如rgba(0, 0, 0, 0.1)。更进一步的做法是使用你应用主色或背景色的一个深、低饱和度的版本。 - 单一、生硬的阴影: 一个单一、深色、边缘锐利的
box-shadow是让你的设计看起来过时的最快方法。这在 2010 年还行,但今天它充满了典型的“开发者审美”既视感。分层才是通往现代、专业感 UI 的正道。 - 给
box-shadow属性添加动画: 这是可以做到的,但通常是个性能陷阱。对blur-radius或spread-radius等属性进行动画处理会强制浏览器在每一帧都执行昂贵的重绘操作,这可能导致动画卡顿。通常,对transform或opacity进行动画会更平滑。 - 忘记光源: 一个常见的错误是,不考虑元素在页面上的位置,对每个元素都应用完全相同的
box-shadow(例如10px 10px ...)。好的设计暗示着一个一致的虚拟光源,通常来自左上方。这意味着阴影通常应该落在右下方(offset-y为正,offset-x为正或零)。不一致的阴影会让 UI 感觉混乱和不自然。 - 没有阴影好过一个糟糕的阴影: 如果你没有时间或工具来创建一个漂亮的、分层的阴影,通常最好使用其他技术(如边框或细微的背景颜色变化)来创建分隔。一个执行不佳的阴影比完全没有阴影更能拉低设计的档次。
为什么它值得你关注
对于任何接触前端代码或设计的人来说,理解 box-shadow 是必备技能。它是 CSS 工具箱中用于创建现代、直观界面的最强大的工具之一。
- 它创造了层级: 阴影告诉我们的大脑什么是重要的。一个有更突出阴影的元素看起来离我们“更近”,因此也更重要。这对于引导用户在复杂 UI 中注意到关键点至关重要,无论是从页面上弹出的模态对话框,还是一个主要的号召性用语按钮。
- 它柔和地定义了边界: 在
box-shadow出现之前,我们用border: 1px solid grey;来分隔元素。阴影让我们能以一种更柔和、更有机的方式创建同样的分隔。侧边栏和主内容之间的一个微妙阴影比一条硬邦邦的线感觉更舒服。 - 它构建了有触感的界面: 精心制作的阴影,无论是外阴影还是内阴影,都能让数字界面感觉可触摸。它们给按钮带来可点击的感觉,给输入框带来可填写的感觉。这不仅仅是装饰;它是可用性的一个功能性部分。
掌握 box-shadow——尤其是掌握分层和微妙处理的艺术——是区分一个“能让功能实现”的开发者和一个“能让功能实现得漂亮”的开发者的明显标志。
深入探索
- MDN Web Docs:
box-shadow: 终极日常参考手册。内容全面,并带有交互式示例。 - W3C Spec: CSS Backgrounds and Borders Module Level 3: 官方规范。这是浏览器制造商实现的真理之源。内容密集,但规则都写在这里。
- Designing Beautiful Shadows in CSS by Josh W. Comeau: 一篇非常棒的深度文章,深入探讨了创建华丽、逼真、分层阴影的艺术和科学。
- Smashing Magazine: A New Way To Create “Glassmorphism”: 一个关于如何将
box-shadow与其他 CSS 属性(如backdrop-filter)结合,创造复杂、现代设计效果的例子。