设计工程原则,让界面感觉更精致。在构建UI组件、审查前端代码、实现动画、悬停状态、阴影、边框、排版、微交互、进入/退出动画或任何视觉细节工作时使用。触发条件:UI打磨、设计细节、“让它感觉更好”、“感觉不对劲”、交错动画、圆角、光学对齐、字体平滑、表格数字、图像轮廓、盒子阴影。
让界面感觉更好的细节
优秀的界面很少来自单一因素。通常是许多小细节的累积,共同构成出色的体验。在构建或审查UI代码时应用这些原则。
快速参考
| 类别 | 使用场景 |
|---|---|
| 排版 | 文本换行、字体平滑、表格数字 |
| 表面 | 圆角、光学对齐、阴影、图像轮廓、点击区域 |
| 动画 | 可中断动画、进入/退出过渡、图标动画、按下缩放 |
| 性能 | 过渡属性指定、will-change 使用 |
核心原则
1. 同心圆角
外半径 = 内半径 + 内边距。嵌套元素半径不匹配是让界面感觉不对劲的最常见原因。
2. 光学对齐优于几何对齐
当几何居中看起来不对时,使用光学对齐。带图标的按钮、播放三角形和不对称图标都需要手动调整。
3. 阴影优于边框
使用多个透明的 box-shadow 值来营造自然深度。阴影能适应任何背景,而实线边框不能。
4. 可中断动画
使用CSS过渡处理交互状态变化——它们可以在动画中途被中断。保留关键帧用于只运行一次的阶段性序列。
5. 拆分并交错进入动画
不要动画化单个容器。将内容拆分为语义块,每个块延迟约100ms交错出现。
6. 微妙的退出动画
使用小的固定 translateY 而不是完整高度。退出应该比进入更柔和。
7. 上下文相关的图标动画
使用 opacity、scale 和 blur 动画化图标,而不是切换可见性。使用以下确切值:scale 从 0.25 到 1,opacity 从 0 到 1,blur 从 4px 到 0px。如果项目在 package.json 中有 motion 或 framer-motion,使用 transition: { type: "spring", duration: 0.3, bounce: 0 } —— bounce 必须始终为 0。如果没有安装运动库,将两个图标都保留在DOM中(一个绝对定位),并使用CSS过渡交叉淡入淡出,使用 cubic-bezier(0.2, 0, 0, 1) —— 这样无需任何依赖即可同时实现进入和退出动画。
8. 字体平滑
在macOS的根布局上应用 -webkit-font-smoothing: antialiased 以获得更清晰的文本。
9. 表格数字
对任何动态更新的数字使用 font-variant-numeric: tabular-nums,以防止布局偏移。
10. 文本换行
在标题上使用 text-wrap: balance。在正文中使用 text-wrap: pretty 以避免孤行。
11. 图像轮廓
为图像添加微妙的 1px 低透明度轮廓,以获得一致的深度。颜色在浅色模式下必须是纯黑色(rgba(0, 0, 0, 0.1)),在深色模式下必须是纯白色(rgba(255, 255, 255, 0.1))—— 绝不要使用接近黑色的颜色,如石板灰、锌色或任何带色调的中性色。带色调的轮廓会吸收其下方的表面颜色,在图像边缘看起来像污垢。
12. 按下缩放
点击时微妙的 scale(0.96) 给按钮带来触觉反馈。始终使用 0.96。绝不要使用小于 0.95 的值——任何低于此的值都显得夸张。添加 static 属性以在动画可能分散注意力时禁用它。
13. 页面加载时跳过动画
在 AnimatePresence 上使用 initial={false} 以防止首次渲染时的进入动画。验证它不会破坏有意的进入动画。
14. 绝不要使用 transition: all
始终指定确切属性:transition-property: scale, opacity。Tailwind 的 transition-transform 覆盖 transform, translate, scale, rotate。
15. 谨慎使用 will-change
仅用于 transform、opacity、filter —— GPU 可以合成的属性。绝不要使用 will-change: all。仅在注意到首帧卡顿时添加。
16. 最小点击区域
交互元素至少需要 40×40px 的点击区域。如果可见元素较小,使用伪元素扩展。绝不要让两个元素的点击区域重叠。
常见错误
| 错误 | 修复 |
|---|---|
| 父元素和子元素使用相同的圆角 | 计算 outerRadius = innerRadius + padding |
| 图标看起来不居中 | 使用内边距或直接修复SVG进行光学调整 |
| 部分之间的硬边框 | 使用带透明度的分层 box-shadow |
| 生硬的进入/退出动画 | 拆分、交错,并保持退出柔和 |
| 数字导致布局偏移 | 应用 tabular-nums |
| macOS上文本沉重 | 在根元素应用 antialiased |
| 页面加载时播放动画 | 在 AnimatePresence 上添加 initial={false} |
元素上的 transition: all |
指定确切属性 |
| 首帧动画卡顿 | 添加 will-change: transform(谨慎使用) |
| 小控件上的微小点击区域 | 使用伪元素扩展到40×40px |
审查输出格式
始终以Markdown表格形式呈现更改,包含之前和之后列。包括你做出的每一个更改——而不仅仅是子集。绝不要在表格之外将发现列为单独的“之前:”/“之后:”行。在每个表格上方使用标题按原则分组更改,并保持每行专注于单个差异,以便读者快速浏览整个列表。
示例
同心圆角
| 之前 | 之后 |
|---|---|
卡片上 rounded-xl + 内部按钮上 rounded-xl(p-2) |
卡片上 rounded-2xl(12 + 8),内部按钮上 rounded-lg |
两个嵌套表面上的 border-radius: 16px |
外部 24px,内部 16px,内边距 8px |
表格数字
| 之前 | 之后 |
|---|---|
动画计数器上的 <span>{count}</span> |
<span className="tabular-nums">{count}</span> |
| 计时器上的默认数字 | 在根元素添加 font-variant-numeric: tabular-nums |
按下缩放
| 之前 | 之后 |
|---|---|
<button className="..."> |
添加 active:scale-[0.96] transition-transform |
按下时 scale(0.9) |
提高到 scale(0.96) —— 任何低于 0.95 的值都显得夸张 |
当从代码片段中不明显时,行应引用具体文件和更改的具体属性。如果某个原则经过审查但无需更改,则完全省略该表格——空表格会增加噪音。
审查清单
- [ ] 嵌套圆角元素使用同心圆角
- [ ] 图标光学居中,而不仅仅是几何居中
- [ ] 在适当的地方使用阴影代替边框
- [ ] 进入动画被拆分并交错
- [ ] 退出动画柔和
- [ ] 动态数字使用 tabular-nums
- [ ] 应用了字体平滑
- [ ] 标题使用 text-wrap: balance
- [ ] 图像有微妙的轮廓
- [ ] 按钮在适当的地方使用按下缩放
- [ ] AnimatePresence 对默认状态元素使用
initial={false} - [ ] 没有
transition: all—— 只有特定属性 - [ ]
will-change仅用于 transform/opacity/filter,绝不用all - [ ] 交互元素至少有 40×40px 的点击区域
参考文件
- typography.md — 文本换行、字体平滑、表格数字
- surfaces.md — 圆角、光学对齐、阴影、图像轮廓
- animations.md — 可中断动画、进入/退出过渡、图标动画、按下缩放
- performance.md — 过渡属性指定、
will-change使用






