多重边框这玩意儿,听起来像是新手教程里才会出现的话题,但实际上,我见过不少写了几年前端的人,遇到需要两层以上边框的需求时,第一反应还是去问UI能不能把设计改简单点。原因无他,就是没把这几个属性的边界和脾气摸透。
今天这篇文章,我就把自己在实战里用多重边框解决过的几个典型场景——从按钮状态提示、卡片悬浮动效,到表单校验反馈、图片装饰边框——完整拆一遍。不光是给代码,更重要的是讲清楚每个方案背后的取舍逻辑:为什么这个场景必须用box-shadow,那个场景却要死磕伪元素。最后,我还会把项目里真实踩过的坑和排查思路一并整理出来,看完你基本就能在脑子里形成一个选型表,下次再遇到这类需求,不用纠结,直接动手。
1. 多重边框方案的整体设计思路
先说结论:在CSS世界里,实现多重边框没有银弹,每种方案都有它不可替代的适用场景,也都有致命短板。理解这一点,比背下十种写法都重要。
1.1 核心需求解析:从设计稿到代码的鸿沟
设计师交付的稿子里,经常会出现元素周围同时存在两层、三层不同颜色、不同间距的边框。比如一个卡片,外框是1px的浅灰细线,往里偏移4px再来一圈淡蓝色的描边,最里面可能还有一个内阴影营造层次感。
这种设计在视觉上很常见,但用传统的border属性实现时,第一个坑就出现了:border只有一层。想要两层以上的边框,你会发现CSS基础属性直接黔驴技穷。这时候能承担多层边框任务的候选方案主要有四个:
- box-shadow:利用投影不占布局空间的特性,通过逗号分隔叠加多层。这是目前应用最广、最灵活的手法。
- outline + outline-offset:可以和
border配合形成"border+outline"两层结构,但outline本身不参与圆角,且不支持多层叠加。 - 伪元素(::before / ::after):通过在元素内部叠加两个绝对定位的层,配合
border实现最多三层。要想更多层,就得嵌套DOM,灵活性受限。 - 渐变背景(background-image + linear-gradient):用渐变画出边框线条的视觉效果,可以无限叠加,但写起来复杂,且不适用于需要背景图片的场景。
这四个方案,其实对应着四种不同的取舍:box-shadow牺牲性能换灵活,outline牺牲圆角换简洁,伪元素牺牲DOM简洁换兼容,渐变牺牲可读性换上限。理解这个关系,你就能根据实际场景做判断了。
1.2 方案选型逻辑:为什么box-shadow会成为首选
在绝大多数前端项目里,提到多重边框,我首推的永远是box-shadow。原因有三点,都跟实际开发体验直接相关。
第一,box-shadow不占用布局空间。这一点极其关键。因为它本质上是"投影",视觉上虽然像边框,但它不会撑大元素的尺寸,也不会影响兄弟元素的排布位置。这和你用border实现的效果完全不同——加border会改变内容盒模型的实际大小,往往会引起布局抖动。在动效和状态切换场景中,这种"无布局副作用"的特性简直是救命稻草。
第二,box-shadow支持无限层叠加。通过逗号分隔,你可以写五层、十层、甚至更多。每层都拥有独立的水平偏移、垂直偏移、模糊半径、扩展半径、颜色和内外阴影模式。这意味着你不仅能把"边框"做出来,还能在同一套语法里混入阴影、光晕、内发光等多种效果,视觉层次一下子就丰富了起来。
第三,box-shadow的圆角跟随。当元素设置了border-radius,box-shadow会自动贴合圆角边缘,这对于现代设计语言里大量圆角卡片、按钮的适配非常友好。相比之下,outline在Chrome 84以前完全不跟随圆角,即使现在有outline-style: auto的修复,兼容性和可控性依然不如box-shadow。
当然,box-shadow也有它的短板,比如性能问题——多层大范围阴影会导致重绘成本升高,尤其是移动端页面,滥用box-shadow会让滚动变卡顿。但这不是"不能用",而是"要克制地用"。在下一节里,我会专门讲怎么在性能和安全之间找到平衡点。
1.3 四个方案的对比速查
在深入每个方案的实现细节之前,我先把这四个维度拉一个对比表,方便你建立整体认知。这张表我用了很久,每次遇到"多重边框"需求都会拿出来扫一眼,基本可以快速定位选型方向。
| 方案 | 可实现层数 | 是否随border-radius | 是否影响布局 | 兼容性 | 典型场景 |
|---|---|---|---|---|---|
| box-shadow | 无限层 | 是 | 否 | 极佳 | 悬浮动效、状态叠加、装饰边框 |
| outline + offset | 最多2层 | 部分(Chrome新版本支持auto) | 否 | 良 | 焦点框、简易双层描边 |
| 伪元素 | 理论3层(含border) | 是 | 需自行控制 | 极佳 | 复杂组合、圆角+渐变色边框 |
| 渐变背景 | 无限层 | 依赖background-clip | 否 | 良 | 渐变边框、条纹装饰框 |
提示:这里的"理论3层"指的是单元素加两个伪元素的极限。如果项目里允许嵌套一个子元素,层数可以突破到5层甚至更多,但代价是DOM结构变复杂,需要权衡维护成本。
2. 核心实现方法逐层拆解
选型思路讲清楚了,下面进入实操环节。我把每种方案的关键语法、参数含义和注意事项拆开揉碎讲一遍,确保你不仅会复制粘贴,还能在遇到变形需求时随机应变。
2.1 box-shadow实现多层边框从入门到进阶
先来看最基础的用法。假设页面上有一个宽200px、高100px的按钮,我想让它具备三层边框效果:最外层是1px的深灰色,偏移量为0;往内4px是1px的浅蓝色;再往内8px是内部投影效果。代码长这样:
.btn-multi-border { width: 200px; height: 100px; border-radius: 8px; /* box-shadow: 水平偏移 垂直偏移 模糊半径 扩展半径 颜色, ... */ box-shadow: 0 0 0 1px #333, /* 第一层:1px实线,无模糊 */ 0 0 0 5px #87ceeb, /* 第二层:偏移4px(5px - 1px),1px实线 */ inset 0 0 10px 2px rgba(0, 0, 0, 0.1); /* 第三层:内阴影 */ }这里的关键在于理解扩展半径和边框间距的换算关系。box-shadow的扩展半径决定了投影从元素边缘向外扩展多少像素。第一层0 0 0 1px,扩展1px,所以视觉上是一圈紧贴边缘的1px实线。第二层0 0 0 5px,整体向外扩展了5px,减去第一层的1px,两圈线之间的间距就是4px。
如果你想让第二圈线距离元素边缘N像素,而线宽本身是W像素,那么这一层的扩展半径应该是 N + W。这个换算逻辑一定要熟,否则在代码里调半天也调不出理想间距。
进阶一点,还可以利用box-shadow的inset关键字做向内延伸的多层边框,适用于例如头像、图标内部的多彩描边效果。更进阶的玩法是,将box-shadow全部设为inset,配合不同的偏移量,在元素内部打造出类似内发光、切角描边等多层视觉。例如:
.inset-multi-ring { width: 120px; height: 120px; border-radius: 50%; box-shadow: inset 0 0 0 6px #ff5252, inset 0 0 0 12px #ffb74d, inset 0 0 0 18px #4caf50, inset 0 0 0 24px #2196f3; }上面这段代码就是典型的内向多层彩色圆环,效果非常直观,适合用来做环形进度指示器的底色,或者状态多色标识。
2.2 outline和outline-offset的正确用法
outline相对box-shadow而言,细节更多,但也不是不能用。它的核心价值在于:和border天然形成"两层边框"结构,并且完全不占布局空间,也不会因为圆角产生视觉上的位置偏移。
基础示例如下:
.double-bordered { border: 2px solid #333; /* 内层边框 */ outline: 2px solid #fc0; /* 外层描边 */ outline-offset: 4px; /* 两框间距 */ }此时元素视觉上是两层边框:内层是紧贴边缘的2px深色线,向外4px处是2px橙色线。这种写法比box-shadow更简单,因为它不需要心算"扩展半径减去线宽"这种偏移换算式,语义也更贴近"描边"的原始概念。
但是,outline有两个不容忽视的缺陷。
其一,outline不跟随圆角。在旧版浏览器中,如果你把border-radius设为8px,outline会依然保持直角矩形,导致视觉上非常突兀。Chrome 84之后,默认的outline-style: auto在部分场景下行为正常,但一旦手动设置了具体的outline样式,跟随圆角的能力又不一定了。因此如果元素有圆角,且你的代码需要兼容旧浏览器,请放弃outline方案。
其二,outline不能叠加多层。你没办法写出两个outline间距错落的效果。它只能提供"额外一层"。想要更多层,必须回到box-shadow或者伪元素。
基于这些限制,outline最常见的落地场景是表单焦点框(focus ring)。因为焦点框本身要求清晰、不改变布局,且通常不需要圆角跟随,用outline + outline-offset来实现键盘导航的可见反馈,恰好是它最舒适的区域。
.input-field:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; border-radius: 4px; }2.3 伪元素叠加:突破层数限制的组合拳
当需求超过两层框,且对圆角、渐变、透明度等有复杂要求时,我通常会考虑伪元素方案。它的本质是利用::before和::after作为两个额外的"背板",在它们之上也绘制边框或者背景边框,从而与元素自身的border叠加出多框架效果。
一个典型的三层边框实现如下:
.triple-border { position: relative; border: 2px solid #333; /* 第一层 */ } .triple-border::before { content: ""; position: absolute; inset: -8px; /* 向外扩展8px */ border: 2px dashed #888; /* 第二层 */ border-radius: 8px; } .triple-border::after { content: ""; position: absolute; inset: -14px; /* 再向外扩展6px */ border: 1px solid #c0c0c0; /* 第三层 */ border-radius: 10px; }这里的inset是简写属性,从四个方向同时定义偏移值。负数代表向外扩展。如果你想要边框间距不均衡,也可以分别写top/right/bottom/left,用法和定位一样灵活。
伪元素方案最大的优势在于可定制性极强:你可以在伪元素上设置background: linear-gradient(...)实现渐变描边效果;可以设置filter: drop-shadow()给外框加阴影;可以设置z-index让部分边框压在其他元素上面。这让它在实现复杂装饰性边框时游刃有余。
但也要付出代价:相对定位与层级管理比较复杂。特别是当元素内部还有其他定位子元素时,伪元素很容易被意外遮挡,或者遮挡其他内容。通常需要配合z-index: -1来把伪元素压到背景层,但这样做又可能把伪元素推到父级背景后面,引发新的层级问题。
因此我建议:伪元素方案优先用于简单卡片装饰,如果目标元素内部结构复杂,先评估是否能用box-shadow替代。
2.4 渐变背景画边框的进阶思路
用background-image配合linear-gradient来实现多重边框,算是一个偏冷门但非常有想象力的方案。它不像box-shadow那样直观,但可以实现一些特殊效果,比如渐变色的多层边框、动态流动的描边等。
核心原理是使用多个渐变图层,通过background-clip控制每个图层的显示范围,从而模拟边框线条。一个简单的两层渐变边框如下:
.gradient-border { border: 4px solid transparent; background: linear-gradient(#fff, #fff) padding-box, linear-gradient(45deg, #f00, #00f) border-box; border-radius: 12px; }这里面一共两个背景层:第一层padding-box是白色背景,铺满除边框外的内容区域;第二层border-box是红蓝渐变,它铺满整个元素,但由于顶层白色背景的遮挡,只会在border区域露出。于是视觉上就形成了一层4px宽的渐变边框,而且圆角完全贴合。
更进一步,你可以写三层渐变,让边框呈现条纹、双渐变等多种效果。但这个方案的局限性也很明显:代码可读性差,稍微改动一个颜色或角度就要面对一段密密麻麻的background声明;调试困难,渐变本身的断裂位置不好预测,很多开发者在这上面花的时间比效果本身还多。
所以我的定位是:渐变画边框只适合"渐变边框"这个垂直需求,尤其是圆形进度条、勋章徽章、特殊标题装饰等场景,而普通的纯色多层边框,不必绕这条路。
3. 实操过程完整记录
理论铺垫足够,现在进入完整实操环节。我会以三个真实项目场景为例,按照从简单到复杂的顺序,把完整的代码实现和调试过程走一遍。所有代码都是可直接运行的,你拿去改改颜色和间距就能用在自己项目里。
3.1 按钮悬浮状态的多重边框切换
第一个场景是按钮。我们需要实现一个普通状态下带有单层边框,悬浮状态下出现"主边框+外层光晕+内层暗边"的多重边框效果,同时确保鼠标悬停时不会因为边框变色而抖动。
HTML结构很简单,一个按钮即可:
<button class="btn-demo">悬停我试试</button>关键CSS如下:
.btn-demo { padding: 10px 24px; font-size: 16px; color: #333; background: #fff; border: 2px solid #bbb; border-radius: 6px; cursor: pointer; /* 预先声明阴影规则,悬浮时只修改颜色值,避免重绘 */ box-shadow: 0 0 0 0 rgba(0, 0, 0, 0); transition: all 0.3s ease; } .btn-demo:hover { /* 第一层:1px实线,红色系 */ box-shadow: 0 0 0 1px #e63946, /* 第二层:向外4px,浅红色,模拟光晕 */ 0 0 0 4px rgba(230, 57, 70, 0.3), /* 第三层:向内2px的深色内阴影,增强按压感 */ inset 0 0 0 2px #b71c1c; color: #e63946; border-color: #e63946; }这段代码有一个非常重要的实战细节:把box-shadow的默认状态预先写成0 0 0 0透明阴影,而不是完全没有box-shadow。这么做是为了让CSS过渡动画在阴影层数从"0层"到"3层"时也能平滑渐变。如果你在默认状态下不写box-shadow属性,hover时突然产生多层阴影,是不会有transition过渡的,它只会硬切,观感很差。这一点新手很容易忽略。
从视觉原理上看,所谓的"多重边框"在这里实际上承担了三个不同角色:最外层的1px细线强化边界感,往外的4px光晕引导视觉注意力,内层的暗边则模拟物理按钮的按压深度。这三者组合,比单纯的border-color变化显得高级很多。
3.2 头像和卡片的多层装饰边框
这个场景更偏向装饰性。比如一张用户头像,外面要有一圈白色边框、一圈主题色边框、再往外还有一圈淡淡的阴影,这样才能在白色背景上显得立体、有层次。
这里我选择box-shadow方案,因为头像通常有圆角(圆形或圆角矩形),box-shadow的圆角跟随能力能省去大量适配麻烦。
.avatar-frame { width: 80px; height: 80px; border-radius: 50%; object-fit: cover; /* 保证图片铺满而不变形 */ box-shadow: 0 0 0 3px #fff, /* 最内层:白色描边,模拟留白 */ 0 0 0 6px #4f8ef7, /* 中层:主题色描边 */ 0 4px 12px rgba(0, 0, 0, 0.15); /* 外层:阴影,增加浮起感 */ }同理,卡片也可以这么玩。常见的设计是:卡片边框线 + 卡片外一圈同色系的渐变光圈。得益于box-shadow的多层叠加能力,代码十分简洁:
.card-multi-border { border-radius: 12px; border: 1px solid #e0e0e0; box-shadow: 0 0 0 4px #f5f7ff, 0 8px 24px rgba(80, 120, 255, 0.08); }这里的0 0 0 4px #f5f7ff是一层4px宽的浅色"虚拟边框",视觉上会比直接设置border: 5px solid #f5f7ff感觉更轻盈,因为它不参与盒模型,卡片尺寸不会改变。这一点在栅格布局里尤其重要——如果使用真实border,每张卡片的宽高都会发生变化,导致布局错位,而box-shadow方案完全规避了这个风险。
3.3 表单校验的下中上三层反馈实现
第三个场景是表单输入框的校验状态。通常我们希望:输入无误时,输入框周围没有任何额外元素;输入错误时,出现红色边框 + 外层光晕;输入正确时,变为绿色边框 + 淡绿色外圈。这种"状态驱动型"的多重边框,天然适合box-shadow。
CSS关键代码如下:
.input-status { border: 2px solid #ddd; border-radius: 6px; padding: 10px 12px; transition: box-shadow 0.3s, border-color 0.3s; } .input-status.error { border-color: #d32f2f; box-shadow: 0 0 0 3px rgba(211, 47, 47, 0.2); } .input-status.success { border-color: #2e7d32; box-shadow: 0 0 0 2px rgba(46, 125, 50, 0.15), 0 0 0 5px rgba(46, 125, 50, 0.08); }这里可以看到,错误状态与正确状态使用了不同的阴影层数。错误只用了单层外描边,因为错误信息要简洁有力;正确状态用了两层外描边,形成一个"水波纹"式的扩散视觉效果,比纯变色更显眼。
在实际项目里,你可以在JavaScript中通过条件class绑定来动态切换这些状态。比如用Vue时是:class="{ 'error': hasError }",用React时是className={hasError ? 'error' : 'success'}。无论框架怎么变,CSS侧的逻辑完全一致,复用它就行。
我在这里要额外提醒一个容易被忽视的点:给表单输入框加多重边框时,别忘了outline: none。因为很多浏览器会给聚焦状态的输入框叠加一个默认的focus outline,如果这个outline颜色是亮蓝色,它可能会和你自定义的多层边框混在一起,造成视觉污染。但注意,去掉outline的同时,必须提供替代的聚焦样式(比如上面代码里的success状态框),否则键盘导航用户会失去焦点指示,可访问性就差了。
3.4 纯CSS实现仪表盘圆环的渐变多圈
最后一个实操场景相对偏门,但效果相当惊艳——纯CSS实现一个多色仪表盘圆环。用到的依然是box-shadow,但它展示了这个属性在"环形叠加"上的潜力。
.gauge-ring { width: 120px; height: 120px; border-radius: 50%; background: #f0f0f0; box-shadow: inset 0 0 0 12px #9ccc65, /* 最内圈:绿色 */ inset 0 0 0 18px #ffb74d, /* 第二圈:橙色 */ inset 0 0 0 24px #64b5f6; /* 第三圈:蓝色 */ }这段代码的视觉结果是:中心留白,向内依次是绿色、橙色、蓝色的三层圆环,每层宽6px。它不需要任何SVG,不需要canvas,一个div就搞定。
实际使用时,你可以在圆环中心绝对定位放置具体数值文本,然后利用box-shadow的多个内阴影打造出"仪表盘刻度"的既视感。当然,真正复杂的仪表盘还是建议用SVG或Canvas,性能更好、可动态性更强,但这种纯CSS方案在需要快速实现静态展示图时非常高效。
4. 踩坑记录与排查思路
根据我自己的项目经验,多重边框看着简单,实际写起来容易在几个地方翻车。我把自己踩过的坑和一些容易被忽略的细节整理如下,供你排查时对照参考。
4.1 box-shadow性能陷阱:三层阴影就可能卡掉帧
box-shadow的性能问题,你一定要有意识。虽然现代浏览器对box-shadow做了很多优化,但模糊半径越大、层数越多、影响范围越广,重绘成本就越高。尤其是在长篇可滚动页面中,如果一个区域内大量元素都带有大模糊半径的box-shadow,帧率可能会明显下降,滚动时出现"粘滞感"。
我常用的优化手段有三个:
- 单层阴影优先于多层阴影。能用一层阴影表达清楚就不用两层。
- 模糊半径超过20px的多层阴影,尽量用伪元素加
filter: blur()替代,因为filter可以在某些场景下更高效地合成图层。 - 动态变化的box-shadow(如hover时才出现的多层光晕),配合
transform或者opacity的变化做动画,比直接改变box-shadow值要流畅得多。因为改变box-shadow会导致每一帧都重绘阴影区域,成本高。
4.2 圆角错位:为什么你的外框看起来是方的
这是outline最容易翻车的地方。当元素设置了border-radius: 8px,但使用outline做外边框时,边框四角依然是直角。视觉上外框"戳"出了圆角范围,非常突兀。
排查时,优先确认你用的是outline还是box-shadow。如果是outline,且目标是支持旧浏览器(Chrome < 84),建议改为box-shadow方案。如果是box-shadow,排查一下是否给box-shadow写的border-radius和元素的border-radius不一致。box-shadow的圆角会默认跟随元素的border-radius,但前提是你没有为box-shadow单独设置border-radius,实际上box-shadow也没有这个属性,所以一般不会出错,但如果你在调试工具里改了元素的border-radius,阴影形状也会跟着变化,这一点容易误判。
在Safari和Firefox上,box-shadow的圆角跟随做得都不错,这点可以放心。
4.3 盒模型变化引发的布局抖动
这个坑在"按钮悬浮加边框"的场景里极其常见。很多新手会用border属性去切换状态,比如默认状态border: 1px solid transparent,hover状态border: 1px solid #f00。这样虽然边框颜色变了,但border-width一直是1px,理论上不会引起抖动。
但一旦你使用多层真实边框(例如border: 2px切到border: 6px),按钮的宽高会实际改变,周围的元素随之发生位移,观感非常差。
排查方法很简单:打开开发者工具,看元素的计算盒模型。如果宽高在状态切换时变化了,那就说明你用了真实的border。此时要么换成box-shadow,要么预先为border-width留足空间(保持宽度恒定,只改颜色或不可见性)。
4.4 透明边框下的背景泄露
用border: 4px solid transparent配合渐变背景做渐变边框时,经常出现背景色"溢出"到border区域的异常。这通常是因为background-clip没有设置为border-box。
正确的顺序是:外层背景负责整个元素区域(border-box),内层背景负责内容区域(padding-box)。如果两层背景的顺序颠倒,或者内层背景没有设置padding-box,渐变就会覆盖到透明边框区域,导致边框被背景遮挡。
排查时直接检查background一行代码的顺序是否和人预期一致。这里也提醒一句:渐变边框的可读性差,不要在生产环境里写过多复杂图层,必要时候还是用伪元素或SVG方案更利于后续维护。
4.5 边缘锯齿与半透明色值
多重边框的视觉质感,很大程度上取决于颜色和透明度的选择。实线边框用纯色问题不大,但光晕类的外层描边如果使用了半透明颜色,会出现一个现象:边缘位置看起来偏淡,而中心位置颜色较深,整体观感不均匀。
这个现象在单层box-shadow上还不明显,但多层叠加后,重叠区域的透明度会相乘叠加。比如两层都是0.3透明度的红色阴影,重叠处实际透明度不是0.3,而是1 - 0.7×0.7 ≈ 0.51。这种叠加会让边框内部出现不自然的"加深带"。
解决办法有两个:一是严格计算各层期望的实际透明度,颜色值不要拍脑袋;二是优先使用rgba而非十六进制加opacity的方式,因为opacity作用于整个元素,会把元素内部的内容也搞透明,而rgba只影响特定阴影层。
5. 多重边框在真实项目中的扩展技巧
选型和基础写法都讲完了,最后分享几个在真实项目中让我省了不少事的扩展技巧。
5.1 利用CSS变量实现主题化多边框
如果项目有多种主题色,或者需要一键切换品牌色,别把颜色值写死在box-shadow里。用CSS变量管理是更优雅的方案:
:root { --primary-color: #4f8ef7; --ring-color-1: rgba(79, 142, 247, 0.2); --ring-color-2: rgba(79, 142, 247, 0.08); } .themeable-border { border: 2px solid var(--primary-color); box-shadow: 0 0 0 3px var(--ring-color-1), 0 0 0 6px var(--ring-color-2); }这样在换主题色时,只需要修改:root里的变量值,所有使用该变量的组件边框都会自动更新。这个技巧在组件库开发中尤其有用。
5.2 悬浮动态扩边框的GPU加速写法
如果你想要一个悬浮时边框像波纹一样从1px扩散到4px的效果,把box-shadow写在transition里会引发持续重绘,性能不够好。
更聪明的做法是用两层结构:元素本体只负责背景和文字,用伪元素绘制边框和阴影;悬浮时通过修改伪元素的transform: scale()来达到"扩容"的视觉效果。因为transform可以触发GPU合成,动画由GPU执行,流畅度远高于重绘box-shadow。
简化代码如下:
.expand-border { position: relative; background: #fff; border-radius: 8px; } .expand-border::before { content: ""; position: absolute; inset: -2px; border: 2px solid #4f8ef7; border-radius: inherit; transition: transform 0.3s ease; } .expand-border:hover::before { transform: scale(1.05); }这样悬浮时,边框像"胀"了一圈,但实际上只是transform的缩放,GPU处理得非常流畅。
5.3 结合背景图片与多边框的布局事项
当元素需要同时展示背景图片和多重边框时,box-shadow依旧好用,但要注意层级关系。box-shadow的阴影层既可以在背景之上(内阴影),也可以在背景之下(外阴影),这主要看你用的是inset还是普通阴影。
如果想做一个图片下方有渐变边框的装饰,可以这样写:
.img-with-frame { background-image: url("某图片路径"); background-size: cover; background-position: center; border-radius: 16px; box-shadow: inset 0 0 0 4px rgba(255, 255, 255, 0.6), inset 0 0 0 6px rgba(0, 0, 0, 0.1), 0 8px 24px rgba(0, 0, 0, 0.12); }这里的两层inset阴影叠加出"白色内描边+深色细线"的效果,同时外层阴影让图片有一种浮起的卡片感。这种组合在图片摄影类网站上非常常见,实现代码却很短。
我个人在实际操作中,最常踩的就是box-shadow层数一多,自己都忘了哪层对应哪个颜色间距。所以养成了一个习惯:每一层box-shadow都另起一行,并且在尾部加注释说明用途。不要嫌注释啰嗦,等你三个月后回来改代码时,会发现这些注释比什么都值钱。
多重边框这个需求,代码量不大,但要写出灵活、可维护、不抖动的效果,背后涉及的对盒模型、层叠上下文、性能开销的理解,一点都不浅。希望这篇文章能让你在下次遇到"需要两层以上边框"的设计稿时,不用再挠头,直接掏出box-shadow或者伪元素方案,五分钟内把效果做出来,而且知道为什么要这么写。