better-ui
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseUI polish
UI 打磨
This skill holds the visual polish for surfaces, icons and motion, with the exact value each detail takes. It applies once the underlying interaction is sound, and a polish finding never outranks a broken interaction.
此 skill 规定了界面层、图标和动效的视觉打磨方式,并明确每个细节应采用的精确数值。只有在底层交互可靠后才应用这些规范;视觉打磨问题绝不应凌驾于交互故障之上。
Exact values, optional polish
精确数值,酌情打磨
The values below are exact, not ranges to approximate. is not , and is not . The optical nudges and the concentric padding cutoff are the exceptions. They are starting points, judged by eye.
cubic-bezier(0.2, 0, 0, 1)cubic-bezier(0.4, 0, 0.2, 1)0.960.95Keep the project's component library, tokens and density, and match its motion language wherever no rule here gives a value. A deliberate and consistent project convention, such as a style with no shadows, is a preference and not a finding. The same detail done two ways within the project is a finding.
Text wrapping, font rendering, tabular numbers and text spacing belong to . Hit areas, keyboard support, ARIA and the reduced-motion requirement belong to . Grouping, section spacing, breakpoints and spatial RTL belong to , except directional icon mirroring. Color tokens and contrast measurement belong to .
better-typographybetter-accessibilitybetter-layoutbetter-colors以下数值均为精确值,不是可供近似取值的范围。 不是 ,而 也不是 。光学微调与同心内边距阈值是例外;它们仅是起点,需通过肉眼判断。
cubic-bezier(0.2, 0, 0, 1)cubic-bezier(0.4, 0, 0.2, 1)0.960.95保留项目现有的组件库、design token 和密度;本 skill 未规定具体数值的地方,应与项目现有的动效语言保持一致。项目中有意且一致的约定(例如不使用阴影的风格)属于偏好,不应作为问题。同一细节在项目内采用两种实现方式,则应作为问题报告。
文本换行、字体渲染、tabular numbers 和文本间距属于 。点击区域、键盘支持、ARIA 和 reduced-motion 要求属于 。分组、章节间距、断点和空间 RTL 属于 ,但方向性图标镜像除外。颜色 token 和对比度测量属于 。
better-typographybetter-accessibilitybetter-layoutbetter-colorsOuter radius equals inner radius plus padding
外层圆角等于内层圆角加 padding
Where nested surfaces share a visible, even inset, the outer radius is the inner radius plus the padding plus any border width. Past of padding, or where the padding is deliberately asymmetric, treat the layers as separate surfaces and keep each one's radius token. Recipes are in surfaces.md.
24px当嵌套界面共享可见且均匀的 inset 时,外层圆角等于内层圆角加 padding,再加任何 border 宽度。当 padding 超过 ,或 padding 有意采用非对称布局时,应将各层视为独立界面,并分别保留各自的 radius token。相关 recipe 见 surfaces.md。
24pxAlign optically where geometry looks off
在几何布局显得不协调时进行光学对齐
Where geometric centering looks off, nudge by eye. Give a button less padding on its icon side, shift a play triangle toward its point and fix asymmetric glyphs in the SVG itself. Recipes are in surfaces.md.
2px如果几何居中看起来不协调,请凭视觉微调。按钮图标一侧的 padding 应减少 ,播放三角形应朝其尖端方向移动,不对称字形则应直接在 SVG 本身中修正。相关 recipe 见 surfaces.md。
2pxShadows for elevation, borders for structure
用阴影表现高度,用边框表达结构
Where a border exists only to create depth, replace it with layered transparent values. Keep borders on dividers, separators, table cells and selected states. Keep them on form inputs too, whose boundary needs 3:1 non-text contrast under . Focus rings belong to as well.
box-shadowbetter-accessibilitybetter-accessibilityForced-colors mode removes every . Keep under a shadow ring so that mode still draws an edge. Recipes are in surfaces.md.
box-shadowborder: 1px solid transparent如果 border 仅用于营造深度感,应将其替换为分层透明 值。border 应保留在分割线、分隔符、表格单元格和选中状态上。表单输入框也应保留 border;根据 ,其边界需要 3:1 的非文本对比度。focus ring 同样属于 的范畴。
box-shadowbetter-accessibilitybetter-accessibilityforced-colors 模式会移除所有 。应在 shadow ring 下保留 ,使该模式仍能绘制边缘。相关 recipe 见 surfaces.md。
box-shadowborder: 1px solid transparentOutline images in pure black or white
为图像描边时仅使用纯黑或纯白
Give content images a outline inset by . Use in light mode and in dark. Never use a palette near-black, a tinted neutral or the accent color, because the tint shows as a colored fringe on the image edge. Skip transparent artwork such as logos and illustrations. The recipe is in surfaces.md.
1px1pxoklch(0 0 0 / 0.1)oklch(1 0 0 / 0.1)为内容图片添加 outline,并向内偏移 。浅色模式使用 ,深色模式使用 。绝不要使用色板中的近黑色、带色偏的中性色或强调色,因为色偏会在图像边缘表现为彩色边缘。透明素材(如 logo 和插图)应跳过描边。相关 recipe 见 surfaces.md。
1px1pxoklch(0 0 0 / 0.1)oklch(1 0 0 / 0.1)Transitions, not keyframes, for interactive state
交互状态使用 transition,而非 keyframes
Drive interactive state changes with CSS transitions or a motion library's springs, which retarget when the user reverses mid-flight. Keyframes run a fixed timeline and cannot reverse, so reserve them for sequences that run once. See animations.md.
使用 CSS transition 或动效库中的 spring 驱动交互状态变化;当用户在动画进行中反向操作时,这些方式可以重新设定目标。keyframes 按固定时间线执行,无法反向,因此仅用于只执行一次的序列。参见 animations.md。
Press scales to 0.96
按下时缩放至 0.96
A pressed button scales to over with , and a disabled one never scales. Give the button an opt-out where the motion would distract, through the component's existing variant API or a prop. See recipes for CSS, Tailwind and Motion.
0.96150msease-outstatic按钮在按下时应在 内以 缩放至 ,禁用状态下绝不缩放。如果动效会造成干扰,应通过组件现有的 variant API 或 prop 为按钮提供退出机制。参见 CSS、Tailwind 和 Motion 的 recipe。
150msease-out0.96staticHigh-frequency interactions get no animation
高频交互不添加动画
Keystrokes, row hovers and tab switches get instant feedback, or a transition of or less on or . Reserve expressive motion for infrequent moments such as a view's first load, a success state or an empty state.
150msopacitybackground-colorEvery animated state change also leaves a static cue in the form of a color, icon or label. Motion is never the only feedback channel.
键盘输入、行悬停和标签页切换应即时反馈,或仅对 或 使用不超过 的 transition。表现力较强的动效应保留给低频场景,例如视图首次加载、成功状态或空状态。
opacitybackground-color150ms每个带动画的状态变化还应保留静态提示,例如颜色、图标或标签。动效绝不能成为唯一的反馈通道。
Gate motion behind the reduced-motion preference
在 reduced-motion 偏好下限制动效
better-accessibilityprefers-reduced-motion: no-preference此项要求由 负责,本节任何 recipe 交付时都必须包含它。位移、缩放和模糊仅可在 下运行。在 reduced motion 下,应改用 opacity 交叉淡化,而不是瞬间移除元素。CSS、Tailwind 和 Motion 的 recipe 见 animations.md。
better-accessibilityprefers-reduced-motion: no-preferenceStagger infrequent entrances by 100ms
低频入场动画间隔 100ms
Where sequence communicates hierarchy in a staged entrance, split the content into semantic chunks such as title, description and actions. Stagger them apart, each entering with opacity, of blur and of over with . Never stagger routine interactions. See enter-exit.md.
100ms4px12pxtranslateY300msease-out当一个序列通过分阶段入场传达层级关系时,将内容拆分为标题、描述和操作等语义区块。各区块间隔 依次入场,每块使用 opacity、 blur 和 ,持续 并采用 。常规交互绝不采用错峰入场。参见 enter-exit.md。
100ms4px12pxtranslateY300msease-outExits are shorter and smaller than enters
退场比入场更短、更小
Exit with opacity, of blur and a fixed over with , never the full container height. Slide fully out only where the destination carries meaning, such as a drawer closing. Remove the element instantly where motion adds no information. See enter-exit.md.
4px-12pxtranslateY150msease-out退场时使用 opacity、 blur 和固定的 ,持续 并采用 ;位移量绝不能达到容器的完整高度。仅当目标位置具有语义含义时,例如抽屉关闭,才让元素完全滑出。如果动效不提供任何信息,则应瞬间移除元素。参见 enter-exit.md。
4px-12pxtranslateY150msease-outSkip state animations on first render
首次渲染时跳过状态动画
Set on an that wraps a state swap, so the default state does not animate in on mount. Never set it around an intentional entrance such as a staggered hero. See animations.md.
initial={false}AnimatePresence对于包裹状态切换的 ,设置 ,使默认状态不会在挂载时执行入场动画。对于有意设计的入场(例如错峰显示 hero),绝不这样设置。参见 animations.md。
AnimatePresenceinitial={false}Cross-fade contextual icons with exact values
使用精确数值交叉淡化上下文图标
Where an icon swaps on an infrequent state change, such as play to pause or copy to copied, cross-fade it. Scale runs to , opacity to and blur to . With a motion library, use , and bounce is always . Without one, keep both icons in the DOM and cross-fade over with .
0.251014px0px{ type: "spring", duration: 0.3, bounce: 0 }0300mscubic-bezier(0.2, 0, 0, 1)A tab's icon swap and the actions a row reveals on hover fall under High-frequency interactions get no animation. Both recipes are in icon-transitions.md.
当图标在低频状态变化时切换,例如从播放切换到暂停,或从复制切换到已复制,应使用交叉淡化。scale 从 变为 ,opacity 从 变为 ,blur 从 变为 。使用动效库时,采用 ,且 bounce 始终为 。不使用动效库时,应让两个图标都保留在 DOM 中,并在 内以 交叉淡化。
0.251014px0px{ type: "spring", duration: 0.3, bounce: 0 }0300mscubic-bezier(0.2, 0, 0, 1)标签页的图标切换,以及某一行在 hover 时显示的操作,均属于 高频交互不添加动画。两项 recipe 均见 icon-transitions.md。
Suppress transitions on theme switch
主题切换时抑制 transition
Disable every transition for the theme swap, force a style flush and restore transitions after the next frame. Otherwise every color transition fires at once and the switch smears. See the recipe.
主题切换时禁用所有 transition,强制刷新样式,并在下一帧后恢复 transition。否则所有颜色 transition 会同时触发,导致切换过程出现拖影。参见 recipe。
Transition only what changes
仅 transition 发生变化的属性
Name the exact properties, as in , and never . See performance.md.
transition-property: scale, opacityall明确指定具体属性,例如 ,绝不使用 。参见 performance.md。
transition-property: scale, opacityallName the animated property in will-change
在 will-change 中指明动画属性
Add only after you see first-frame stutter, and name the property you animate: for , for . Limit it to transform properties, and , and never use . See performance.md.
will-changescalescaletransformtransformopacityfilterall只有在实际观察到首帧卡顿后才添加 ,并指明要动画的属性:动画 时写 ,动画 时写 。仅将其用于 transform 类属性、 和 ,绝不使用 。参见 performance.md。
will-changescalescaletransformtransformopacityfilterallHover effects only on hover-capable pointers
仅对支持 hover 的指针设备应用悬停效果
Put hover-only styling behind . On touch, latches after a tap and reads as a stuck selected state. Tailwind 4's variant already compiles under that query, and Tailwind 3's does not. Where a control draws its own pressed state, set so the default gray flash does not double it.
@media (hover: hover):hoverhover:-webkit-tap-highlight-color: transparent仅悬停样式应放在 查询内。在触摸设备上,点击后 会保持激活,看起来像卡在选中状态。Tailwind 4 的 variant 已编译到该查询下,而 Tailwind 3 的没有。如果控件自行绘制按下状态,应设置 ,避免默认灰色闪光与其叠加。
@media (hover: hover):hoverhover:-webkit-tap-highlight-color: transparentContain scroll inside overlays
将滚动限制在浮层内部
Give scrollable dialogs, drawers, menus and side panels , so scrolling past their end never scrolls the page behind.
overscroll-behavior: contain为可滚动的对话框、抽屉、菜单和侧边面板设置 ,使滚动越过其末端时绝不会带动后方页面滚动。
overscroll-behavior: containMatch icon stroke to text weight
图标描边粗细与文字字重匹配
An icon's rendered stroke tracks the weight of the text beside it, from at 400 to at 700. Use one icon library per surface. The table, sizing and grid rules are in icons.md.
1.5px2.5px图标渲染后的描边粗细应与旁边文字的字重一致:字重为 400 时为 ,字重为 700 时为 。每个界面层只使用一套 icon library。表格、尺寸和网格规则见 icons.md。
1.5px2.5pxOne SVG, recolored per state
单一 SVG,按状态重新着色
Icons use and take hover, selected and disabled states from CSS color and opacity, never from separate assets. Outline is the default variant, and fill marks the active state. Under , mirror only icons whose meaning follows reading direction. See icons.md.
currentColordir="rtl"图标使用 ,并通过 CSS color 和 opacity 呈现 hover、selected 和 disabled 状态,绝不使用单独的素材资源。outline 是默认变体,fill 用于标记 active 状态。在 下,仅镜像含义随阅读方向变化的图标。参见 icons.md。
currentColordir="rtl"Before you finish
完成前检查
| Detection | Fix |
|---|---|
Padded parent and child with the same | Add the padding to the outer radius |
| |
| |
| Name the changing properties |
| A transition on the same properties |
| |
| |
| |
| Hidden state in the |
| Gate it and cross-fade opacity under reduced motion |
Color transitions plus a theme toggle with no | The theme-switch suppression recipe |
| Name the animated property or delete it |
| |
Plain-CSS | Wrap it in the query |
| |
| 检测项 | 修复方式 |
|---|---|
带 padding 的父级和子级使用相同的 | 将 padding 加到外层圆角上 |
使用 | |
| |
| 明确指定发生变化的属性 |
在 | 对相同属性使用 transition |
| |
| |
| |
基础规则中包含 | 在 |
| 为其设置限制条件,并在 reduced motion 下使用 opacity 交叉淡化 |
颜色 transition 与主题切换并存,但未使用 | 使用主题切换抑制 recipe |
| 指明动画属性或删除 |
图标 SVG 内使用 | |
纯 CSS | 将其包裹在该查询中 |
对话框、抽屉或菜单使用 | |
Reporting
报告
Severity. breaks an interaction, as a keyframe toggle that cannot reverse or a hover state stuck on touch does. Two of 's escalation triggers land here and are on sight. One is motion that ignores , and the other is a state change carried by motion alone. is a visible inconsistency in surfaces, icons or motion. is isolated polish.
HIGHbetter-interfaceHIGHprefers-reduced-motionMEDIUMLOWVerification. Without a browser, read every state the component defines from the code, such as hover, pressed, selected, loading and empty, with its durations and easings. With one, walk each state and replay motion at 10% speed in the browser's Animations panel. Report every check you could not run as .
Not verifiedFormat. Group findings under the principle each violates, ordered by severity, one row per root cause listing every location it appears in:
| Severity | Location | Before | After | Why |
|---|
Locationpath/to/file:lineWhyEnd with when any remains, otherwise, leaving the rest in the table as work to do. Never coverage you did not inspect. With nothing to report, state "No actionable UI-polish findings" and report verification.
BlockHIGHApproveApprove严重程度。 会破坏交互,例如无法反向的 keyframe 切换,或在触摸设备上卡住的 hover 状态。 的两项升级触发条件也属于此类,看到即可判定为 :一项是忽略 的动效,另一项是仅依靠动效传达的状态变化。 表示界面层、图标或动效中存在可见的不一致。 表示孤立的打磨问题。
HIGHbetter-interfaceHIGHprefers-reduced-motionMEDIUMLOW验证。 如果没有浏览器,应从代码中读取组件定义的每个状态,例如 hover、pressed、selected、loading 和 empty,并检查其 duration 和 easing。如果有浏览器,则逐一走查每个状态,并在浏览器的 Animations 面板中以 10% 速度重放动效。每项未能执行的检查都应报告为“Not verified”。
格式。 按每项问题所违反的原则分组,并按严重程度排序;每个根因占一行,并列出其出现的所有位置:
| 严重程度 | 位置 | 修改前 | 修改后 | 原因 |
|---|
Locationpath/to/file:lineWhy只要仍有任何 问题,最后就以 结束;否则以 结束,并将其余问题保留在表中作为待办事项。绝不能对未检查的范围给出 。如果没有可报告的问题,请写明“No actionable UI-polish findings”,并报告验证情况。
HIGHBlockApproveApprove