缺角卡片(就是优惠券两侧各挖一个半圆缺口、中间一条虚线的那种)有三种主流写法:用 clip-path 直接把角切掉,用 radial-gradient 在背景上"挖"出透明圆孔,或者用 mask 遮罩把不需要的部分盖掉。三者的差别不在视觉效果,而在能不能保留阴影、能不能透光、以及兼容性。

第一种最直观,但会把 box-shadow 一起裁掉;第二种能做出真正的透光缺口,是优惠券样式的标准答案;第三种最灵活,代价是要多写一层遮罩语法。
本文给出三种写法的完整代码、各自的适用边界和三个高频坑的修复办法,最后给一份可以直接照抄的验证清单。
先看结论
要"切除实体角"用 clip-path,写法最短;要"挖出透光的圆孔"用 radial-gradient 叠背景,这是缺角卡片最贴近设计稿的做法;要两者兼得又要兼容复杂形状,用 mask。
选择的核心判断只有一条:缺口后面要不要透出背景。需要透光就用渐变或遮罩,不需要就用 clip-path,后者还能少写一堆定位。
基础选择器还不熟的,可以先看 CSS 基础教程 把盒模型和定位过一遍。
一、方法一:clip-path 切角,写法最短
clip-path 的思路是直接裁剪元素的可见区域,超出多边形范围的都不显示。
.coupon {
width: 320px;
height: 120px;
background: linear-gradient(135deg, #ff8a3d, #fe6a00);
border-radius: 12px;
clip-path: polygon(
0 0, 100% 0, 100% 100%, 0 100%, 0 60%,
12px 50%, 0 40%
);
}
这段做的是:先画一个有渐变背景和圆角的卡片,再用多边形路径把左侧中间挖出一个尖角。坐标按顺序连成闭合路径,12px 50% 那个点就是缺角的尖端。
要注意三点:
- 坐标可以用百分比也可以用像素,混写是允许的,但同一条路径里保持单位一致更好维护;
- 尖角缺口和"半圆缺口"是两种效果,半圆需要很多个点近似,写起来很啰嗦;
- 最大的坑:
clip-path会连同box-shadow一起裁掉,卡片加阴影后会发现阴影消失。

二、方法二:radial-gradient 挖圆孔,最像优惠券
想要真正的半圆缺口,用径向渐变在背景上"抠"出透明区域,是缺角卡片的标准做法。
.coupon {
position: relative;
width: 320px;
height: 120px;
background:
radial-gradient(circle at left center, transparent 10px, #fff 10px) left / 51% 100% no-repeat,
radial-gradient(circle at right center, transparent 10px, #fff 10px) right / 51% 100% no-repeat;
}
.coupon::after {
content: "";
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
width: 0;
height: 60px;
border-left: 1px dashed #d9d9d9;
}
第一段做的是:用两个径向渐变各画半张卡片,渐变在圆心位置留出 10 像素的透明圆,其余部分填白,左右各占 51% 宽度;第二段用伪元素画中间那条虚线。
这个写法的关键在于 transparent 10px, #fff 10px ——两个色标落在同一位置,形成硬边界,圆内透明、圆外填充。半径改成变量就能适配不同尺寸。
它的局限也要说清楚:卡片背景必须是纯色,渐变背景不好做;而且两个半张卡片之间的接缝在缩放时可能出现 1 像素细线,需要把宽度设成 51% 让它互相重叠。
三、方法三:mask 遮罩,最灵活
mask 的思路是反过来:先画一个完整卡片,再用遮罩把不需要的部分"盖住"。
.coupon {
width: 320px;
height: 120px;
background: linear-gradient(135deg, #fff7f0, #ffe9d6);
border-radius: 12px;
--r: 12px;
mask:
radial-gradient(circle var(--r) at left 0 50%, transparent 98%, #000) ,
radial-gradient(circle var(--r) at right 0 50%, transparent 98%, #000);
mask-composite: intersect;
}
这段做的是:定义缺口半径变量,用两个径向渐变作为遮罩层,透明部分被剔除,再用 mask-composite: intersect 取两层交集,两侧各留一个半圆缺口。
| 写法 | 能否透光 | 能不能留阴影 | 兼容性 |
|---|---|---|---|
| clip-path | 可以 | 阴影会被裁掉 | 最好 |
| radial-gradient | 可以 | 可以,但要另加元素 | 好 |
| mask | 可以 | 可以 | 中等,需注意前缀 |
三条路都能做出缺角卡片,缺角卡片的实际选型只取决于两件事:卡片背后是否有背景图(有就必须透光),以及是否需要阴影(需要就避开 clip-path)。

四、三个高频坑与验证清单
4.1 坑一:加了 clip-path 之后阴影消失
clip-path 裁剪的是整个元素的可视区域,阴影属于被裁范围。修复办法是把阴影挪到外层容器上,或者改用 filter: drop-shadow(),它按裁剪后的实际形状生成阴影。
.card-wrap {
filter: drop-shadow(0 4px 12px rgba(254, 106, 0, 0.18));
}
.coupon {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, 0 60%, 12px 50%, 0 40%);
}
这段做的是:外层容器负责投影,内层负责裁剪。预期结果(未在本机执行,依据 drop-shadow 的定义)是阴影沿着裁剪后的轮廓生成,而不是被裁掉。
4.2 坑二:两个半张卡片接缝处有细线
缩放时浏览器会把 50% 宽度算出小数,两张卡片之间露出一条缝。修复办法是把宽度设成 51% 让它们略微重叠。
4.3 坑三:mask 遮罩在部分浏览器无效
旧版浏览器需要 -webkit-mask 前缀,且 mask-composite 的属性名曾是 -webkit-mask-composite,取值也不同。修复办法是两条都写,或者退回第二种方案。
4.4 验证清单
- 检查:缺口是否真的透光(把卡片放到带背景图的容器上试)
- 运行:改一次缺口半径变量,看两侧是否同步变化
- 预期:三种写法都能得到两侧半圆缺口
- 失败:缺口是方角 → 检查
radial-gradient的圆心位置 - 失败:整块卡片消失 →
mask-composite写错,遮罩把全部内容剔除了 - 修复:调整圆心坐标或补浏览器前缀
- 验证:在缩放 110% 与 90% 下各看一次,确认接缝无细线
总结
缺角卡片有三种写法:clip-path 最短但会裁掉阴影,radial-gradient 是优惠券样式的标准答案,mask 最灵活但要处理前缀。选型只看两件事——缺口要不要透光、卡片要不要阴影。
三条要点带走:
- 需要阴影就别用
clip-path,改用外层的drop-shadow; - 半圆缺口靠渐变硬边界实现,两个色标要落在同一位置;
- 两个半张卡片之间留 1% 重叠,避免缩放出现接缝。
下一步建议把缺口半径和颜色抽成 CSS 变量,一张卡片就能适配全站。属性细节记不住时,翻 CSS 属性参考手册 最快。
常见问题
Q:为什么用了 clip-path 之后卡片阴影不见了?
A:clip-path 会裁剪整个元素的可视区域,阴影也在其中。改用外层的 filter: drop-shadow,阴影会按裁剪后的轮廓生成。
Q:怎么让缺角后面透出背景图?
A:用 radial-gradient 或 mask 这两种方案,它们挖出的是真正的透明区域。clip-path 裁剪后同样是透光的,但它会连阴影一起裁掉。
Q:优惠券样式中间那条虚线怎么画?
A:用伪元素画一条高度为卡片一半、左边框为 dashed 的竖线,再用 margin: auto 垂直居中即可。
Q:mask 属性写了没效果是怎么回事?
A:多半是缺浏览器前缀,或者 mask-composite 取值不对。旧版用 -webkit-mask-composite,取值与标准写法并不一致。
延伸学习
想把卡片与遮罩这块补齐,可以按这个顺序来:

TRAE-AI编程



