box-shadow 的工作原理
box-shadow 在元素的边框盒周围绘制阴影。它会跟随 border-radius,不占用布局空间,并且至少需要两个值:水平方向和垂直方向各移动多远。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Box shadow</title>
<style>
body { font-family: system-ui, sans-serif; background: #f3f4f6; padding: 16px; }
.row { display: flex; gap: 24px; flex-wrap: wrap; }
.card {
width: 150px; padding: 20px 12px; text-align: center;
background: white; border-radius: 10px; font-size: 14px;
}
.hard { box-shadow: 6px 6px 0 #111827; }
.soft { box-shadow: 0 4px 12px rgb(0 0 0 / 15%); }
.inset { box-shadow: inset 0 2px 6px rgb(0 0 0 / 25%); }
</style>
</head>
<body>
<div class="row">
<div class="card hard">6px 6px 0 #111827</div>
<div class="card soft">0 4px 12px, 15% black</div>
<div class="card inset">inset 0 2px 6px</div>
</div>
</body>
</html>
语法
box-shadow: [inset] <offset-x> <offset-y> [<blur>] [<spread>] [<color>];
| 值 | 含义 | 默认值 |
|---|
inset | 把阴影画在盒子内部,看起来像一块凹陷的区域 | 外部 |
| offset-x | 正值向右移,负值向左移 | 必需 |
| offset-y | 正值向下移,负值向上移 | 必需 |
| blur | 边缘有多柔和;不能为负 | 0(硬边) |
| spread | 放大(正值)或缩小(负值)阴影 | 0 |
| color | 任意颜色;半透明的黑色通常效果最好 | 文字颜色 |
长度值的个数决定了它们的含义:两个是偏移,三个则加上模糊,四个再加上扩展。试一试:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Box shadow tester</title>
<style>
body { font-family: system-ui, sans-serif; }
#box {
width: 160px; height: 90px; margin: 32px;
background: #ffffff; border: 1px solid #e5e7eb; border-radius: 10px;
}
#out { font-size: 13px; }
</style>
</head>
<body>
<label>box-shadow: <input id="val" value="0 10px 20px -5px rgb(37 99 235 / 50%)" size="36"></label>
<div id="box"></div>
<code id="out"></code>
<script>
const box = document.getElementById('box');
const input = document.getElementById('val');
const show = () => {
box.style.boxShadow = input.value;
document.getElementById('out').textContent =
'computed: ' + getComputedStyle(box).boxShadow;
};
input.addEventListener('input', show);
show();
</script>
</body>
</html>
试试 0 0 0 4px #f59e0b(一个环)、inset 0 0 12px #2563eb 和 -8px -8px 0 #16a34a。
扩展:环形阴影和单侧阴影
扩展值在模糊之前改变阴影的大小。由此可以得到两种常用效果:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Spread</title>
<style>
body { font-family: system-ui, sans-serif; padding: 16px; }
.row { display: flex; gap: 28px; flex-wrap: wrap; align-items: center; }
.box { width: 140px; padding: 18px 10px; text-align: center; background: white; border: 1px solid #e5e7eb; font-size: 14px; }
.ring { box-shadow: 0 0 0 4px #2563eb; border-radius: 8px; }
.double { box-shadow: 0 0 0 3px white, 0 0 0 6px #16a34a; border-radius: 8px; }
.bottom { box-shadow: 0 10px 8px -8px rgb(0 0 0 / 50%); }
</style>
</head>
<body>
<div class="row">
<div class="box ring">4px ring</div>
<div class="box double">gap then ring</div>
<div class="box bottom">bottom only</div>
</div>
</body>
</html>
- 环形: 没有偏移,没有模糊,只有扩展。和边框不同,它不会改变元素的尺寸,所以很适合做焦点指示(
:focus-visible { box-shadow: 0 0 0 3px #2563eb; })。
- 带间隙的环: 把一个白色的扩展阴影叠在一个更大的彩色扩展阴影上面。
- 只有一侧: 把阴影向下移动,再用负的扩展值把它缩小,这样它只会从底部露出来。
多层阴影营造真实的立体感
真实的阴影在靠近物体的地方又深又清晰,离得远的地方则柔和而淡。两三层阴影比一个大面积的模糊更能模仿这种效果:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Elevation</title>
<style>
body { font-family: system-ui, sans-serif; background: #f3f4f6; padding: 24px 16px; }
.row { display: flex; gap: 28px; flex-wrap: wrap; }
.card { width: 140px; padding: 24px 10px; text-align: center; background: white; border-radius: 12px; font-size: 14px; }
.one { box-shadow: 0 10px 30px rgb(0 0 0 / 30%); }
.layered {
box-shadow:
0 1px 2px rgb(0 0 0 / 12%),
0 4px 8px rgb(0 0 0 / 8%),
0 12px 24px rgb(0 0 0 / 8%);
}
.lift { transition: transform 0.2s, box-shadow 0.2s; }
.lift:hover {
transform: translateY(-4px);
box-shadow: 0 2px 4px rgb(0 0 0 / 10%), 0 16px 32px rgb(0 0 0 / 14%);
}
</style>
</head>
<body>
<div class="row">
<div class="card one">One heavy shadow</div>
<div class="card layered">Three layers</div>
<div class="card layered lift">Hover me</div>
</div>
</body>
</html>
列表中的第一个阴影画在最上面。阴影颜色要用半透明的黑色或背景色的深色调,而不要用纯灰色,这样它们才能让下面的任何内容都变暗。
内阴影
inset 把阴影画在内边距盒的内部,就像表面被按下去了一样。它适合文本输入框、凹槽区域和按下状态的按钮:
input {
box-shadow: inset 0 1px 3px rgb(0 0 0 / 20%);
}
button:active {
box-shadow: inset 0 3px 6px rgb(0 0 0 / 30%);
}
内阴影画在背景之上、内容之下。跟随字母形状而不是盒子形状的阴影,请使用 text-shadow。
常见错误
- 纯黑且完全不透明的阴影。 看起来就像一条深色轮廓线。请使用 10% 到 30% 的透明度。
- 用一个巨大的模糊来表现立体感。 把一个小而清晰的阴影和一个大而柔和的阴影叠在一起。
- 阴影在边缘被截断。 带有
overflow: hidden 的祖先元素会把它裁掉;给祖先元素加上内边距,或者把 overflow 规则移到别处。
- 负的模糊值。 这是无效的,整条声明都会被忽略。
- 在很多元素上对
box-shadow 做动画。 它每一帧都会重绘。对于较重的页面,请改为给一个带有更大阴影的伪元素的 opacity 做动画。