web-ui-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWeb UI Best Practices
Web UI 最佳实践
When invoked generally, apply these opinionated constraints for building better interfaces and for any UI work in this conversation.
When invoked with a file specified (i.e. ), review the file against all constraints below and output:
web-ui-best-practices <file>- violations (quote the exact line/snippet)
- why it matters (1 short sentence)
- a concrete fix (code-level suggestion)
在通用场景下,构建更优质界面或进行本次对话中的任何UI工作时,请遵循这些约束规则。
当指定文件调用时(例如 ),请对照以下所有约束规则评审该文件,并输出:
web-ui-best-practices <file>- 违规内容(引用确切的代码行/片段)
- 违规影响(一句话简要说明)
- 具体修复方案(代码层面的建议)
Stack
技术栈
- MUST use Tailwind CSS defaults unless custom values already exist or are explicitly requested
- MUST use (formerly
motion/react) when JavaScript animation is requiredframer-motion - SHOULD use for entrance and micro-animations in Tailwind CSS
tw-animate-css - MUST use utility (
cn+clsx) for class logictailwind-merge
- 除非已存在自定义值或被明确要求,否则必须使用Tailwind CSS的默认配置
- 当需要JavaScript动画时,必须使用 (原
motion/react)framer-motion - 在Tailwind CSS中实现入场动画和微动画时,建议使用
tw-animate-css - 处理类名逻辑时,必须使用 工具(
cn+clsx)tailwind-merge
Components
组件
- MUST use accessible component primitives for anything with keyboard or focus behavior (,
Base UI,Radix)React-Aria - MUST use the project’s existing component primitives first
- NEVER mix primitive systems within the same interaction surface
- SHOULD prefer for new primitives if compatible with the stack
Base UI - MUST add an to icon-only buttons
aria-label - NEVER rebuild keyboard or focus behavior by hand unless explicitly requested
- 对于任何涉及键盘或焦点行为的组件,必须使用可访问的组件基础库(、
Base UI、Radix)React-Aria - 必须优先使用项目中已有的组件基础库
- 禁止在同一交互界面中混合使用不同的基础组件系统
- 如果与技术栈兼容,新增基础组件时建议优先选择
Base UI - 纯图标按钮必须添加
aria-label - 除非被明确要求,否则禁止手动实现键盘或焦点行为
Interaction
交互
- MUST use an for destructive or irreversible actions
AlertDialog - SHOULD use structural skeletons for loading states
- NEVER use , use
h-screenh-dvh - MUST show errors next to where the action happens
- NEVER block paste in or
inputelementstextarea
- 对于破坏性或不可逆操作,必须使用
AlertDialog - 加载状态建议使用结构化骨架屏
- 禁止使用 ,请使用
h-screenh-dvh - 错误提示必须显示在操作发生位置的附近
- 禁止在 或
input元素中阻止粘贴操作textarea
Navigation & State
导航与状态
- SHOULD reflect UI state in the URL (filters, tabs, pagination, expanded panels via query params)
- MUST use /
<a>for navigation (Cmd/Ctrl+click and middle-click should work)<Link> - SHOULD deep-link stateful UI (if it uses , consider URL sync via
useStateor similar)nuqs - MUST require confirmation or undo window for destructive actions (never immediate)
- 建议将UI状态同步到URL中(通过查询参数实现筛选、标签页、分页、展开面板等状态的同步)
- 导航必须使用 /
<a>标签(支持Cmd/Ctrl+点击和中键点击打开新页面)<Link> - 建议为有状态的UI实现深度链接(如果使用了 ,可考虑通过
useState或类似工具实现与URL的同步)nuqs - 破坏性操作必须要求确认或提供撤销窗口,禁止直接执行
Touch & Interaction
触摸与交互
- SHOULD set on tappable controls (prevents double-tap zoom delay)
touch-action: manipulation - SHOULD set intentionally
-webkit-tap-highlight-color - MUST use in modals/drawers/sheets
overscroll-behavior: contain - SHOULD during drag: disable text selection, on dragged elements
inert - SHOULD use sparingly (desktop only, single primary input; avoid on mobile)
autoFocus
- 可点击控件建议设置 (避免双击缩放延迟)
touch-action: manipulation - 建议显式设置
-webkit-tap-highlight-color - 模态框/侧边栏/底部弹窗中必须使用
overscroll-behavior: contain - 拖拽过程中建议:禁用文本选择,为拖拽元素设置
inert - 建议谨慎使用 (仅在桌面端的单个主要输入框使用;避免在移动端使用)
autoFocus
Animation
动画
- NEVER add animation unless it is explicitly requested
- MUST animate only compositor props (,
transform)opacity - NEVER animate layout properties (,
width,height,top,left,margin)padding - SHOULD avoid animating paint properties (,
background) except for small, local UI (text, icons)color - SHOULD use on entrance
ease-out - NEVER exceed for interaction feedback
200ms - MUST pause looping animations when off-screen
- SHOULD respect
prefers-reduced-motion - NEVER introduce custom easing curves unless explicitly requested
- SHOULD avoid animating large images or full-screen surfaces
- SHOULD never —list properties explicitly
transition: all - SHOULD set correct
transform-origin - SHOULD have animations be interruptible—respond to user input mid-animation
- 除非被明确要求,否则禁止添加动画
- 必须仅对合成器属性(、
transform)执行动画opacity - 禁止对布局属性(、
width、height、top、left、margin)执行动画padding - 建议避免对绘制属性(、
background)执行动画,小型局部UI(文本、图标)除外color - 入场动画建议使用 缓动函数
ease-out - 交互反馈动画时长不得超过
200ms - 元素移出视口时,必须暂停循环动画
- 建议遵循 偏好设置
prefers-reduced-motion - 除非被明确要求,否则禁止自定义缓动曲线
- 建议避免对大型图片或全屏元素执行动画
- 禁止使用 ——需明确列出要过渡的属性
transition: all - 建议设置正确的
transform-origin - 动画必须支持中断——在动画过程中响应用户输入
Typography
排版
- MUST use for headings and
text-balancefor body/paragraphstext-pretty - MUST use for data
tabular-nums - SHOULD use or
truncatefor dense UIline-clamp - NEVER modify (
letter-spacing) unless explicitly requestedtracking-* - SHOULD use not
…... - SHOULD use curly quotes
"not straight"" - SHOULD have non-breaking spaces: ,
10 MB, brand names⌘ K - SHOULD have loading states end with :
…,"Loading…""Saving…" - SHOULD use for number columns/comparisons
font-variant-numeric: tabular-nums - SHOULD have flex children need to allow text truncation
min-w-0 - SHOULD handle empty states—don't render broken UI for empty strings/arrays
- SHOULD consider that user-generated input fields may have short, average, and very long inputs
- 标题必须使用 ,正文/段落必须使用
text-balancetext-pretty - 数据类内容必须使用
tabular-nums - 高密度UI中建议使用 或
truncate处理文本溢出line-clamp - 除非被明确要求,否则禁止修改 (
letter-spacing)tracking-* - 建议使用 而非
…... - 建议使用弯引号
“而非直引号”"" - 建议使用不换行空格:、
10 MB、品牌名称等⌘ K - 加载状态文本建议以 结尾:
…、"加载中…""保存中…" - 数字列/数字对比场景建议使用
font-variant-numeric: tabular-nums - Flex子元素需要设置 以支持文本截断
min-w-0 - 建议处理空状态——空字符串/数组场景下禁止渲染损坏的UI
- 建议考虑用户生成的输入内容可能存在短文本、普通文本和超长文本的情况
Layout
布局
- MUST use a fixed scale (no arbitrary
z-index)z-* - SHOULD use for square elements instead of
size-*+w-*h-*
- 必须使用固定的 层级体系(禁止使用任意的
z-index值)z-* - 方形元素建议使用 而非同时设置
size-*+w-*h-*
Safe Areas & Layout
安全区域与布局
- MUST respect for full-bleed layouts and fixed elements on notched devices
env(safe-area-inset-*) - SHOULD avoid unwanted scrollbars (use on containers and fix overflow at the source)
overflow-x-hidden - SHOULD prefer flex/grid over JS measurement for layout
- 对于刘海屏设备的全屏布局和固定定位元素,必须遵循 规则
env(safe-area-inset-*) - 建议避免不必要的滚动条(在容器上使用 并从根源修复溢出问题)
overflow-x-hidden - 布局实现建议优先使用flex/grid而非JavaScript测量
Dark Mode & Theming
深色模式与主题
- MUST set on
color-scheme: darkfor dark themes (fixes scrollbars and native inputs)<html> - SHOULD set to match the page background
<meta name="theme-color"> - SHOULD set explicit and
background-coloron nativecolor(Windows dark mode)<select>
- 深色主题下,必须在 标签上设置
<html>(修复滚动条和原生输入框的显示问题)color-scheme: dark - 建议设置 以匹配页面背景色
<meta name="theme-color"> - 建议为原生 标签显式设置
<select>和background-color(适配Windows深色模式)color
Locale & i18n
本地化与国际化
- MUST use (no hardcoded date/time formats)
Intl.DateTimeFormat - MUST use (no hardcoded number/currency formats)
Intl.NumberFormat - SHOULD detect language via /
Accept-Language, not IPnavigator.languages
- 必须使用 (禁止硬编码日期/时间格式)
Intl.DateTimeFormat - 必须使用 (禁止硬编码数字/货币格式)
Intl.NumberFormat - 建议通过 /
Accept-Language检测语言,而非通过IP地址navigator.languages
Hydration Safety
水化安全性
- MUST provide for inputs with
onChange(or usevaluefor uncontrolled inputs)defaultValue - MUST guard date/time rendering against hydration mismatches (server vs client)
- SHOULD use only where truly needed
suppressHydrationWarning
- 带 的输入框必须提供
value(或对非受控输入框使用onChange)defaultValue - 必须避免日期/时间渲染时出现服务端与客户端的水化不匹配问题
- 建议仅在真正必要的场景下使用
suppressHydrationWarning
Hover & Interactive States
悬停与交互状态
- SHOULD provide states for buttons/links (visual feedback)
hover: - SHOULD increase contrast for interactive states (hover/active/focus more prominent than rest)
- 按钮/链接建议设置 状态(提供视觉反馈)
hover: - 交互状态(悬停/激活/焦点)的对比度建议高于普通状态
Images
图片
- MUST set explicit and
widthonheight(prevents CLS)<img> - SHOULD set for below-fold images
loading="lazy" - SHOULD set or
priorityfor above-fold critical imagesfetchpriority="high"
- 标签必须设置明确的
<img>和width(避免CLS布局偏移)height - 屏幕外的图片建议设置
loading="lazy" - 首屏关键图片建议设置 或
priorityfetchpriority="high"
Performance
性能
- NEVER animate large or
blur()surfacesbackdrop-filter - NEVER apply outside an active animation
will-change - NEVER use for anything that can be expressed as render logic
useEffect - SHOULD virtualize large lists (>50 items) (e.g. or
virtuawhere appropriate)content-visibility: auto - MUST avoid layout reads during render (,
getBoundingClientRect,offsetHeight,offsetWidth)scrollTop - SHOULD batch DOM reads/writes (avoid interleaving)
- SHOULD prefer uncontrolled inputs; controlled inputs must be cheap per keystroke
- SHOULD add for CDN/asset domains
<link rel="preconnect"> - SHOULD preload critical fonts () and use
<link rel="preload" as="font">font-display: swap
- 禁止对大面积元素执行 或
blur()动画backdrop-filter - 禁止在非活跃动画场景下使用
will-change - 任何可通过渲染逻辑实现的功能,禁止使用
useEffect - 大型列表(超过50项)建议实现虚拟化(例如使用 或在合适场景下使用
virtua)content-visibility: auto - 渲染过程中必须避免读取布局信息(、
getBoundingClientRect、offsetHeight、offsetWidth)scrollTop - 建议批量处理DOM的读/写操作(避免交替执行)
- 建议优先使用非受控输入框;受控输入框必须保证每次按键操作的性能开销极低
- 建议为CDN/资源域名添加
<link rel="preconnect"> - 建议预加载关键字体()并使用
<link rel="preload" as="font">font-display: swap
Design
设计
- NEVER use gradients unless explicitly requested
- NEVER use purple or multicolor gradients
- NEVER use glow effects as primary affordances
- SHOULD use Tailwind CSS default shadow scale unless explicitly requested
- MUST give empty states one clear next action
- SHOULD limit accent color usage to one per view
- SHOULD use existing theme or Tailwind CSS color tokens before introducing new ones
- 除非被明确要求,否则禁止使用渐变
- 禁止使用紫色或多色渐变
- 禁止将发光效果作为主要交互提示
- 除非被明确要求,否则建议使用Tailwind CSS的默认阴影层级
- 空状态必须提供一个清晰的下一步操作
- 单个视图中建议限制强调色的使用数量为一种
- 引入新颜色前建议优先使用现有主题或Tailwind CSS的颜色变量
Design Direction (when aesthetics requested)
设计方向(当需要调整美学风格时)
- MUST choose a clear aesthetic direction and execute consistently (e.g., brutally minimal, editorial, utilitarian, playful)
- SHOULD use deliberate typography (1 display + 1 body) and avoid accidental defaults; follow product font system when it exists
- SHOULD define theme/palette via CSS variables; use one dominant base + sharp accent
- SHOULD match implementation complexity to the aesthetic (minimal = restraint; maximal = elaborate but controlled)
- If motion is explicitly requested: prefer one cohesive high-impact sequence (load stagger, scroll reveal) over scattered micro-interactions
- SHOULD avoid cookie-cutter layout/component patterns; make 1-2 distinctive choices grounded in context
- 必须选择清晰的美学方向并保持执行一致性(例如:极简主义、编辑风格、实用主义、趣味风格)
- 建议使用刻意设计的排版组合(1种标题字体 + 1种正文字体),避免随意使用默认字体;如果产品已有字体体系,请遵循该体系
- 建议通过CSS变量定义主题/调色板;使用一种主导基础色 + 鲜明的强调色
- 实现复杂度建议与美学风格匹配(极简风格=克制;繁复风格=精心设计但保持可控)
- 如果明确要求添加动画:优先选择一个连贯的高影响力动画序列(加载 stagger 动画、滚动揭露动画)而非零散的微交互
- 建议避免千篇一律的布局/组件模式;结合场景做出1-2个独特的设计选择
Content & Copy
内容与文案
- SHOULD use active voice ("Install the CLI" not "The CLI will be installed")
- SHOULD use specific button labels ("Save API Key" not "Continue")
- SHOULD write error messages with a fix/next step, not just the problem
- SHOULD write in second person; avoid first person
- SHOULD use over "and" where space-constrained
&
- 建议使用主动语态(例如“安装CLI”而非“CLI将被安装”)
- 按钮标签建议使用具体表述(例如“保存API密钥”而非“继续”)
- 错误提示建议包含修复方案/下一步操作,而非仅说明问题
- 建议使用第二人称表述;避免使用第一人称
- 空间有限时建议使用 替代“和”
&
Anti-patterns (flag these)
反模式(需标记)
- or
user-scalable=nodisabling zoommaximum-scale=1 - with
onPastepreventDefault transition: all- without a
outline-nonereplacementfocus-visible - Inline navigation without
onClick/<a><Link> - /
<div>with click handlers (use<span>)<button> - Images without dimensions
- Large arrays without virtualization
.map() - Form inputs without labels
- Icon buttons without
aria-label - Hardcoded date/number formats (use )
Intl.* - without clear justification
autoFocus
- 使用 或
user-scalable=no禁用缩放maximum-scale=1 - 事件中使用
onPastepreventDefault - 使用
transition: all - 使用 但未提供
outline-none替代方案focus-visible - 使用内联 实现导航而非
onClick/<a><Link> - 使用 /
<div>绑定点击事件(应使用<span>)<button> - 图片未设置尺寸
- 大型数组使用 但未实现虚拟化
.map() - 表单输入框未设置标签
- 纯图标按钮未添加
aria-label - 硬编码日期/数字格式(应使用 )
Intl.* - 无合理理由使用
autoFocus
Rules if not found in primitives
基础库未覆盖场景下的规则
The below rules apply if the pattern is either explicitly requested or not found in the primitive itself already.
以下规则适用于场景被明确要求或基础库未覆盖的情况。
Accessibility
可访问性
- Icon-only buttons need
aria-label - Form controls need or
<label>aria-label - Interactive elements need keyboard handlers (/
onKeyDown)onKeyUp - for actions,
<button>/<a>for navigation (not<Link>)<div onClick> - Images need (or
altif decorative)alt="" - Decorative icons need
aria-hidden="true" - Async updates (toasts, validation) need
aria-live="polite" - Use semantic HTML (,
<button>,<a>,<label>) before ARIA<table> - Headings hierarchical –
<h1>; include skip link for main content<h6> - on heading anchors
scroll-margin-top
- 纯图标按钮需要
aria-label - 表单控件需要 或
<label>aria-label - 交互元素需要键盘事件处理器(/
onKeyDown)onKeyUp - 动作使用 ,导航使用
<button>/<a>(禁止使用<Link>)<div onClick> - 图片需要 属性(装饰性图片使用
alt)alt="" - 装饰性图标需要设置
aria-hidden="true" - 异步更新(提示框、验证信息)需要设置
aria-live="polite" - 优先使用语义化HTML(、
<button>、<a>、<label>)而非ARIA<table> - 标题层级需遵循 –
<h1>的结构;主内容区域需包含跳转链接<h6> - 标题锚点需要设置
scroll-margin-top
Focus States
焦点状态
- Interactive elements need visible focus: or equivalent
focus-visible:ring-* - Never /
outline-nonewithout focus replacementoutline: none - Use over
:focus-visible(avoid focus ring on click):focus - Group focus with for compound controls
:focus-within
- 交互元素需要可见的焦点样式:或同等替代方案
focus-visible:ring-* - 禁止使用 /
outline-none但未提供焦点替代样式outline: none - 优先使用 而非
:focus-visible(避免点击时显示焦点环):focus - 复合控件建议使用 实现组焦点
:focus-within
Forms
表单
- Inputs need and meaningful
autocompletename - Use correct (
type,email,tel,url) andnumberinputmode - Never block paste (+
onPaste)preventDefault - Labels clickable (or wrapping control)
htmlFor - Disable spellcheck on emails, codes, usernames ()
spellCheck={false} - Checkboxes/radios: label + control share single hit target (no dead zones)
- Submit button stays enabled until request starts; spinner during request
- Errors inline next to fields; focus first error on submit
- Placeholders end with and show example pattern
… - on non-auth fields to avoid password manager triggers
autocomplete="off" - Warn before navigation with unsaved changes (or router guard)
beforeunload
- 输入框需要设置 和有意义的
autocomplete属性name - 使用正确的 (
type、email、tel、url)和numberinputmode - 禁止阻止粘贴操作(+
onPaste)preventDefault - 标签需可点击(使用 属性或包裹控件)
htmlFor - 邮箱、验证码、用户名输入框需禁用拼写检查()
spellCheck={false} - 复选框/单选框:标签与控件需共享同一个点击区域(无无效点击区域)
- 提交按钮需保持可用直到请求开始;请求过程中显示加载指示器
- 错误提示需显示在字段旁;提交时自动聚焦第一个错误字段
- 占位符需以 结尾并显示示例格式
… - 非认证字段设置 以避免触发密码管理器
autocomplete="off" - 存在未保存更改时,导航前需发出警告(使用 或路由守卫)",
beforeunload