卡片变奏 101 · 第 01 课

Draft

Inverted Corner内凹圆角

从网页最常见的矩形、圆形和胶囊出发,改变一小段边界;再把连续曲率延伸到 Tabs、Notch Bar 与多元素接缝。

形状原理 + 参数模型更新于 2026 年 8 月

A three-step shape variation

让卡片拥有新的轮廓

从内容关系开始,先把转角画顺,再让同一条轮廓进入响应式 DOM。

先给动作留位置

从一个真实的按钮、头像或标签开始,让凹角回应内容,而不是装饰性地贴在卡片外面。

内容先于路径

再把转角画顺

用两段贝塞尔曲线连接直线,控制内凹半径与张力,让转弯拥有连续的切线。

曲线决定气质

最后让它响应

把固定 path 换成百分比和 CSS 变量,让边框、背景与 DOM 内容一起适应不同尺寸。

shape() 承担变化

本课目录

01卡片变奏的起点

提到卡片,我们通常先想到圆角矩形

网页长期使用一套很小的形状词汇。矩形适合承载文字、图片与复杂布局,圆形常用在头像和状态上,胶囊适合按钮、标签与切换项。卡片再给矩形加上一点圆角,就成了今天最常见的界面容器。

这些几何稳定、好排版,也容易和浏览器的背景、边框、阴影配合。它们被反复使用之后,页面的轮廓也会越来越接近。Vibe Coding 让组件更快被做出来,默认组件之间的相似感同样会更快累积。

01Rectangle

矩形

02Rounded rect

圆角矩形

03Circle

圆形

04Capsule

胶囊

05 / VARIATIONINVERTED CORNER

SAME CARD · NEW EDGE

只改一个角,内容结构仍然熟悉

从基础几何开始变奏

内凹圆角仍然保留卡片的主体,只在相邻元素出现的位置改变边界。它比完全自由的异形更容易替换,也更容易进入现有布局。 后面还会把同一段凹曲线从 corner 移到 edge 中段,形成状态条和标签页需要的缺口。

想给页面增加一点新意,可以先从形状语言开始。少加一层装饰,直接改变卡片怎样包住内容、怎样靠近按钮或图片,界面的节奏就会不同。

内凹圆角是一个合适的起点。它只改动一小段边界,卡片原来的文字、图片和交互都能留下;把现有圆角卡片换过来时,宽度适配与内容布局也不需要推倒重来。

那这条凹下去的边要怎么实现?如果手边正好有 Figma,第一反应多半是先把它画出来。

02先试试 SVG

先画成 SVG,放进网页后会遇到什么问题

看到右上角的缺口,最直接的做法是在 Figma 里画好轮廓,再导出一张 SVG。只看一个固定尺寸,这条路没有明显问题:曲线可以反复调,边框也能沿 path 绘制,交付时拿到的外观和画板基本一致。

问题会在它进入真实布局后出现。把同一张 SVG 放进大卡片和小卡片,viewBox 会把路径、内容与 stroke 一起缩放。按钮需要固定点击热区,缺口需要继续容纳它,边框又希望在不同尺寸下保持相近的视觉重量,这几件事开始互相牵制。

ORIGINAL FRAME640 × 320
FIGMA EXPORT / 640 × 320One fixed drawingpath, content and stroke scale together

路径、文字和 6px stroke 一起保存在同一张图里。

SAME SVG210 × 105
FIGMA EXPORT / 640 × 320One fixed drawingpath, content and stroke scale together

stroke 随整体缩放,文字也快速失去可读性。

要保留固定缺口和边框,需要额外改 viewBox、path 或拆出 DOM 内容。

同一份 Figma SVG 放进两个尺寸。外观能保持相似,内容净空、文字和边框却很难继续各自响应。

SVG 可以通过 vector-effect="non-scaling-stroke" 保住描边宽度,也可以作为 CSS 的 background-image 使用。不过,SVG 里面的填色和路径很难继续跟着页面状态变化;外面的 DOM 仍然是矩形,内容、背景、边框和点击区域也要另外处理。同步的问题越多,这张 SVG 就越像一份需要单独维护的资源。

所以这次实现要同时满足三件事:卡片宽度可以变化,缺口和圆角的尺寸可以单独控制,内容与背景继续使用普通 DOM。在选择 CSS 写法之前,还要先看清一件事:曲线怎样连接才会顺?

03看懂曲线

接缝没有折痕,圆角看起来才会顺

先暂时放下 CSS。沿着卡片顶部向右看,轮廓会从直线进入凹面,经过缺口内壁,再回到卡片外沿。半径调大不一定更好看,每一段曲线都要顺着上一段接下去,整个缺口才会像一笔画成。

第一层检查是方向。曲线进入凹面时要顺着顶部直线,离开时也要顺着下一段边界。端点虽然对在一起,控制柄方向却不一致,连接处仍会留下细小折痕。

第二层检查是转弯速度。两段曲线共享切线以后,如果一侧突然收紧、另一侧过分舒缓,眼睛仍会读出拼接感。控制柄长度、内外半径和两侧留白需要一起调,曲率才会自然地传下去。

下面先只看外角怎样接进内角。沿着轮廓走一遍,眼睛在哪一处被绊住,折点、细缝或曲率突变通常就藏在那里。

外角和内角各走各的

接缝可见
  • 两段 DOM 各自计算圆角,连接点的斜率没有对齐
  • 独立边框在 P3 / P0 收边,半像素处暴露缺口和锯齿
不推荐

外角和内角连成一个整体

轮廓连续
共享切线轮廓一口气走完
  • 一个元素画完整轮廓,曲线会平滑地接进下一段
  • P3 与 P0 属于同一路径,背景和边框只收边一次
推荐
两侧远看是同一个轮廓。左侧不用 SVG,而是让主体、外角和内角分别由多个 CSS DOM 拼接;桥接元素的轻微偏转让斜率不再连续,独立收边也会在 P3 / P0 附近留下细缝或锯齿。右侧则让外角和内角共享方向,并由同一条路径连续绘制背景与边框。

三次贝塞尔

CUBIC · P0 → P1 → P2 → P3

P0P1P2P3

锚点

01 · P0 / P3

两个实心节点定义曲线的起点与终点。

控制点

02 · P1 / P2

两个空心节点控制进入方向与曲线张力。

控制柄与相切

03 · HANDLE

每段曲线的两端各有一根;锚点连接两段曲线时,才会显示两侧手柄。

沿轮廓观察 · FOLLOW THE EDGE

接得上,方向顺,转弯才会自然

相邻线段先在同一位置相遇,进入与离开沿着同一条切线,转弯速度也不要在接缝处突然变化。

端点决定曲线从哪里开始、在哪里结束,控制柄决定它怎样进入和离开。控制柄短,凹角会更快转向;控制柄变长,曲线会沿着直线多走一段。可以先画一个四分之一圆,再根据缺口大小、卡片比例和旁边的元素慢慢调整。

这套判断也适用于多个 DOM。元素可以拆开,视觉轮廓仍要像一条曲线连续经过它们。后面看到头像包角、Notch Bar 和 Tabs 时,都可以先找端点、切线与曲率,再看它们用了哪一种 CSS。

04用 CSS 实现

用 CSS 画出会跟着卡片伸缩的缺口

先从改动最小的情况开始。如果四条直边还在原位,只想改变矩形的四个角,corner-shape 已经提供 round、bevel、scoop、notch、squircle 与 superellipse()。四个角还可以分别设置,目标能由内置形状完成时,不需要手写整条路径。

当缺口离开角落、进入一条边,或轮廓已经不再是矩形时,再使用 shape()。会伸展的直线写成百分比,缺口尺寸、内外半径与控制柄长度保留为 CSS 变量;卡片宽度改变时,直线段重新计算,刚才学到的端点和切线关系仍然保留。

corner-shape

内置角形状

corner-shape: scoop

向角落内部舀出一段圆弧。

只改变右上角的 radius 区域

border-shape

自定义轮廓

SINGLE NOTCH

把缺口移到边上,净空尺寸与曲线进出可以分别调整。

一条路径同时绘制直线、外角与内凹

01corner-shape

只改四个角

四条边仍是矩形,直接选内置形状。

02border-shape

完整轮廓改变

缺口进入边上,背景和边框沿同一路径绘制。

03mask / rounded-out

多个元素相接

只补一小段接缝,让现有 DOM 结构继续工作。

先按轮廓范围选择材料,再考虑曲线参数。内置角形状能覆盖目标时,CSS 会短很多。

轮廓进入边缘以后,还要决定它只裁切填充,还是让背景与边框一起服从新边界。clip-path 与 border-shape 可以使用同一条 shape(),但承担的绘制范围不同。

clip-path裁切结果

ORDINARY DOM

内容照常留在 DOM 里

childrenbackground-image
+

border 仍按原来的矩形盒子绘制,再被轮廓裁掉。缺口内壁没有一条完整、连续的边框。

不适合带描边
border-shape背景 · 边框 · 动效

ORDINARY DOM

内容照常留在 DOM 里

childrenbackground-image
+

同一条 shape() 同时描述背景与边框边界,凹面、外角和直线段可以形成连续轮廓。

适合带描边
两张卡片使用同一个普通 DOM 与圆点 background-image。左侧黑色边框保持静止,只裁切绘制结果;右侧 3px 呼吸边框与背景共同服从盒子的真实边界。

05明确绘制责任

多元素接缝和单元素轮廓,分别由谁来画

先看真实 DOM 结构。多元素接缝可以让相邻元素用伪元素或 mask 补出局部曲线;单元素内凹卡片则由自身的 border-shape: shape() 定义完整轮廓,没有嵌入元素。下面把两种绘制责任放在一起比较。

这段曲线由谁来画多元素拼接 / 单元素完整轮廓

01 · 多元素拼接

局部补出曲线

2 个 DOM
多元素拼接内凹接缝上方标签元素和下方卡片元素各自绘制,标签再用伪元素或 mask 在盒模型外补出两段接缝曲线。元素 A · 标签元素 B · 卡片紫色 = 伪元素 / mask 补面

上方标签与下方卡片是两个元素。标签通过伪元素或 mask 在自身盒模型外补出紫色曲面,与卡片背景拼成局部接缝。

02 · 绘制结果

border-shape

同一个元素
border-shape 绘制内凹轮廓同一个卡片元素使用 border-shape 绘制包含内凹圆角的完整边界,矩形布局盒保持不变。CARD内凹区域实线 = border-shape 边界

border-shape: shape() 直接改写这个卡片元素的完整边界。背景和 border 都沿同一条路径绘制,内凹曲线不属于其他元素。

两种实现

多元素补局部接缝 / 单元素画完整轮廓

左侧的曲线属于标签的补面;右侧没有嵌入元素,整条可见边界由卡片自己的 border-shape 一次画完。两者外观可以相似,但 DOM 结构和绘制责任不同。

这段曲线由谁来画

左侧是多元素方案:标签用伪元素或 mask 补出两侧曲线,再与卡片拼接。右侧是单元素方案:卡片自身的 border-shape 定义完整边界,背景和边框都沿这条路径绘制,不存在嵌入元素。

免费部分到这里

继续完成内凹圆角的实现与调试

免费部分已经讲清怎样判断曲线、怎样用 CSS 画轮廓,以及多元素接缝与单元素 border-shape 的绘制责任。解锁后可以继续选择实现方法、整理参数、复制完整代码,并用 Corner Inverter 直接调试。

解锁后从这里继续,前面的公开内容始终可以回来阅读。

学习进度

正在读取你的学习记录…