better-ui

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Details that make interfaces feel better

让界面更舒适的细节

Great interfaces rarely come from a single thing. It's usually a collection of small details that compound into a great experience. Apply these principles when building or reviewing UI code.
Typography (text wrapping, font smoothing, tabular numbers, spacing) is covered by the
better-typography
skill — use that for anything text-related.
优秀的界面很少源于单一设计,通常是众多微小细节共同作用,才形成极佳的体验。在构建或评审UI代码时,请遵循以下原则。
排版相关内容(文本换行、字体平滑、表格数字、间距)由
better-typography
技能覆盖——任何文本相关工作都请使用该技能。

Quick Reference

快速参考

CategoryWhen to Use
SurfacesBorder radius, optical alignment, shadows, image outlines, hit areas
AnimationsInterruptible animations, enter/exit transitions, icon animations, scale on press
PerformanceTransition specificity,
will-change
usage
分类使用场景
Surfacesborder radius、光学对齐、阴影、image outlines、点击区域
Animations可中断动画、进入/退出过渡、图标动画、按压缩放
Performance过渡属性指定、
will-change
使用

Core Principles

核心原则

1. Concentric Border Radius

1. 同心圆角

Outer radius = inner radius + padding. Mismatched radii on nested elements is the most common thing that makes interfaces feel off.
外圆角半径 = 内圆角半径 + 内边距。嵌套元素圆角不匹配是导致界面违和的最常见问题。

2. Optical Over Geometric Alignment

2. 光学对齐优先于几何对齐

When geometric centering looks off, align optically. Buttons with icons, play triangles, and asymmetric icons all need manual adjustment.
当几何居中看起来不协调时,采用光学对齐。带图标的按钮、播放三角形和不对称图标都需要手动调整。

3. Shadows Over Borders

3. 用阴影替代边框

Layer multiple transparent
box-shadow
values for natural depth. Shadows adapt to any background; solid borders don't.
叠加多层半透明
box-shadow
值以营造自然的层次感。阴影能适配任何背景,而实色边框则无法做到。

4. Interruptible Animations

4. 可中断动画

Use CSS transitions for interactive state changes — they can be interrupted mid-animation. Reserve keyframes for staged sequences that run once.
使用CSS过渡实现交互状态变化——它们可以在动画中途被中断。关键帧动画仅用于一次性的阶段性序列。

5. Split and Stagger Enter Animations

5. 拆分并交错进入动画

Don't animate a single container. Break content into semantic chunks and stagger each with ~100ms delay.
不要对单个容器应用动画。将内容拆分为语义化区块,并为每个区块设置约100ms的延迟交错播放。

6. Subtle Exit Animations

6. 柔和的退出动画

Use a small fixed
translateY
instead of full height. Exits should be softer than enters.
使用小幅度固定
translateY
而非全高度位移。退出动画应比进入动画更柔和。

7. Contextual Icon Animations

7. 上下文图标动画

Animate icons with
opacity
,
scale
, and
blur
instead of toggling visibility. Use exactly these values: scale from
0.25
to
1
, opacity from
0
to
1
, blur from
4px
to
0px
. If the project has
motion
or
framer-motion
in
package.json
, use
transition: { type: "spring", duration: 0.3, bounce: 0 }
— bounce must always be
0
. If no motion library is installed, keep both icons in the DOM (one absolute-positioned) and cross-fade with CSS transitions using
cubic-bezier(0.2, 0, 0, 1)
— this gives both enter and exit animations without any dependency.
使用
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. Image Outlines

8. 图片轮廓

Add a subtle
1px
outline with low opacity to images for consistent depth. The color must be pure black in light mode (
oklch(0 0 0 / 0.1)
) and pure white in dark mode (
oklch(1 0 0 / 0.1)
) — never a near-black like slate, zinc, or any tinted neutral. A tinted outline picks up the surface color underneath it and reads as dirt on the image edge.
为图片添加1px低透明度的细微轮廓,以保持一致的层次感。亮色模式下颜色必须为纯黑色(
oklch(0 0 0 / 0.1)
),暗色模式下必须为纯白色(
oklch(1 0 0 / 0.1)
)——绝不能使用深灰、锌灰或任何带色调的中性色。带色调的轮廓会沾染下方的表面颜色,看起来像图片边缘的污渍。

9. Scale on Press

9. 按压缩放

A subtle
scale(0.96)
on click gives buttons tactile feedback. Always use
0.96
. Never use a value smaller than
0.95
— anything below feels exaggerated. Add a
static
prop to disable it when motion would be distracting.
点击时应用细微的
scale(0.96)
为按钮提供触觉反馈。请始终使用
0.96
。绝不要使用小于
0.95
的值——低于该值会显得过于夸张。如果动画会分散注意力,请添加
static
属性禁用该效果。

10. Skip Animation on Page Load

10. 页面加载时跳过动画

Use
initial={false}
on
AnimatePresence
to prevent enter animations on first render. Verify it doesn't break intentional entrance animations.
AnimatePresence
上使用
initial={false}
以防止首次渲染时播放进入动画。请确认此设置不会破坏有意设置的入场动画。

11. Never Use
transition: all

11. 切勿使用
transition: all

Always specify exact properties:
transition-property: scale, opacity
. Tailwind's
transition-transform
covers
transform, translate, scale, rotate
.
始终指定精确的属性:
transition-property: scale, opacity
。Tailwind的
transition-transform
涵盖
transform, translate, scale, rotate

12. Use
will-change
Sparingly

12. 谨慎使用
will-change

Only for
transform
,
opacity
,
filter
— properties the GPU can composite. Never use
will-change: all
. Only add when you notice first-frame stutter.
仅用于
transform
opacity
filter
——这些是GPU可合成的属性。绝不要使用
will-change: all
。仅当你注意到第一帧卡顿才添加该属性。

13. Minimum Hit Area

13. 最小点击区域

Interactive elements need a 44×44px hit area for touch or mobile contexts. In desktop interfaces, use at least 40×40px. Extend with a pseudo-element if the visible element is smaller. Never let hit areas of two elements overlap.
交互元素在触摸或移动场景下需要44×44px的点击区域。在桌面界面中,至少使用40×40px。如果可见元素较小,可使用伪元素扩展点击区域。绝不要让两个元素的点击区域重叠。

Common Mistakes

常见错误

MistakeFix
Same border radius on parent and childCalculate
outerRadius = innerRadius + padding
Icons look off-centerAdjust optically with padding or fix SVG directly
Hard borders between sectionsUse layered
box-shadow
with transparency
Jarring enter/exit animationsSplit, stagger, and keep exits subtle
Animation plays on page loadAdd
initial={false}
to
AnimatePresence
transition: all
on elements
Specify exact properties
First-frame animation stutterAdd
will-change: transform
(sparingly)
Tiny hit areas on small controlsExtend with a pseudo-element to 44×44px for touch/mobile, or at least 40×40px in desktop UI
错误修复方案
父元素和子元素使用相同的圆角半径计算
outerRadius = innerRadius + padding
图标看起来偏移中心使用内边距进行光学调整,或直接修改SVG
区块间使用实色边框使用多层半透明
box-shadow
替代
进入/退出动画过于突兀拆分、交错动画,并让退出动画更柔和
页面加载时播放动画
AnimatePresence
添加
initial={false}
元素上使用
transition: all
指定精确的过渡属性
动画第一帧卡顿谨慎添加
will-change: transform
小型控件的点击区域过小使用伪元素扩展至触摸/移动场景的44×44px,或桌面UI的至少40×40px

Review Output Format

评审输出格式

Always present changes as a markdown table with Before and After columns. Include every change you made — not just a subset. Never list findings as separate "Before:" / "After:" lines outside of a table. Group changes by principle using a heading above each table, and keep each row focused on a single diff so the reader can scan the whole list quickly.
请始终以包含BeforeAfter列的Markdown表格呈现修改内容。列出所有修改——不要只列出部分内容。绝不要在表格外以单独的「Before:」/「After:」行列出修改。按原则分组,在每个表格上方添加标题,每行聚焦于单个差异,以便读者快速浏览整个列表。

Example

示例

Concentric border radius

同心圆角

BeforeAfter
rounded-xl
on card +
rounded-xl
on inner button (
p-2
)
rounded-2xl
on card (
8 + 8 = 16
),
rounded-lg
on inner button
border-radius: 16px
on both nested surfaces
Outer
24px
, inner
16px
with
8px
padding
BeforeAfter
卡片使用
rounded-xl
+ 内部按钮使用
rounded-xl
p-2
卡片使用
rounded-2xl
8 + 8 = 16
),内部按钮使用
rounded-lg
两个嵌套元素都使用
border-radius: 16px
外部元素使用
24px
,内部元素使用
16px
并设置
8px
内边距

Scale on press

按压缩放

BeforeAfter
<button className="...">
Added
active:scale-[0.96] transition-transform
scale(0.9)
on press
Raised to
scale(0.96)
— anything below
0.95
feels exaggerated
Rows should cite the specific file and the specific property that changed when it isn't obvious from the snippet. If a principle was reviewed but nothing needed to change, omit that table entirely — empty tables add noise.
BeforeAfter
<button className="...">
添加
active:scale-[0.96] transition-transform
按压时使用
scale(0.9)
调整为
scale(0.96)
——低于
0.95
的值会显得过于夸张
如果修改内容不明显,每行应注明具体的文件和修改的属性。如果某条原则已评审但无需修改,请完全省略该表格——空表格会增加冗余信息。

Review Checklist

评审检查清单

  • Nested rounded elements use concentric border radius
  • Icons are optically centered, not just geometrically
  • Shadows used instead of borders where appropriate
  • Enter animations are split and staggered
  • Exit animations are subtle
  • Images have subtle outlines
  • Buttons use scale on press where appropriate
  • AnimatePresence uses
    initial={false}
    for default-state elements
  • No
    transition: all
    — only specific properties
  • will-change
    only on transform/opacity/filter, never
    all
  • Interactive elements have 44×44px hit areas for touch/mobile, or at least 40×40px in desktop UI
  • 嵌套圆角元素使用同心圆角
  • 图标采用光学对齐,而非仅几何对齐
  • 适当使用阴影替代边框
  • 进入动画已拆分并交错播放
  • 退出动画柔和自然
  • 图片添加了细微轮廓
  • 按钮已按需添加按压缩放效果
  • 默认状态元素的
    AnimatePresence
    使用了
    initial={false}
  • 未使用
    transition: all
    ——仅指定了精确属性
  • will-change
    仅用于transform/opacity/filter,从未使用
    all
  • 交互元素在触摸/移动场景下有44×44px的点击区域,或在桌面UI中至少有40×40px

Reference Files

参考文件

  • surfaces.md — Border radius, optical alignment, shadows, image outlines
  • animations.md — Interruptible animations, enter/exit transitions, icon animations, scale on press
  • performance.md — Transition specificity,
    will-change
    usage
  • surfaces.md —— border radius、光学对齐、阴影、image outlines
  • animations.md —— 可中断动画、进入/退出过渡、图标动画、按压缩放
  • performance.md —— 过渡属性指定、
    will-change
    使用