frontend-patterns

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Frontend Patterns

前端开发模式

Overview

概述

User interfaces exist to help users accomplish tasks. Every UI decision should make the user's task easier or the interface more accessible.
Core principle: Design for user success, not aesthetic preference.
Violating the letter of this process is violating the spirit of frontend design.
用户界面的存在是为了帮助用户完成任务。每一项UI决策都应让用户的任务更简单,或让界面更易于访问。
核心原则: 以用户成功为设计目标,而非审美偏好。
违反这一流程的字面要求,就是违背前端设计的核心精神。

Focus Areas (Reference Pattern)

重点领域(参考模式)

  • React component architecture (hooks, context, performance)
  • Responsive CSS with Tailwind/CSS-in-JS
  • State management (Redux, Zustand, Context API)
  • Frontend performance (lazy loading, code splitting, memoization)
  • Accessibility (WCAG compliance, ARIA labels, keyboard navigation)
  • React组件架构(hooks、context、性能优化)
  • 基于Tailwind/CSS-in-JS的响应式CSS
  • 状态管理(Redux、Zustand、Context API)
  • 前端性能优化(懒加载、代码分割、记忆化)
  • 无障碍设计(WCAG合规、ARIA标签、键盘导航)

Approach (Reference Pattern)

实施方法(参考模式)

  1. Component-first thinking - reusable, composable UI pieces
  2. Mobile-first responsive design - start small, scale up
  3. Performance budgets - aim for sub-3s load times
  4. Semantic HTML and proper ARIA attributes
  5. Type safety with TypeScript when applicable
  1. 组件优先思维 - 可复用、可组合的UI组件
  2. 移动端优先的响应式设计 - 从小屏布局开始,逐步扩展到大屏
  3. 性能预算 - 目标加载时间控制在3秒以内
  4. 语义化HTML与正确的ARIA属性
  5. 适用场景下使用TypeScript保证类型安全

Component Output Checklist

组件交付检查清单

Every frontend deliverable should include:
  • Complete React component with props interface
  • Styling solution (Tailwind classes or styled-components)
  • State management implementation if needed
  • Basic unit test structure
  • Accessibility checklist for the component
  • Performance considerations and optimizations
Focus on working code over explanations. Include usage examples in comments.
所有前端交付物必须包含:
  • 完整的React组件及Props接口定义
  • 样式实现方案(Tailwind类或styled-components)
  • 必要的状态管理实现
  • 基础单元测试结构
  • 组件无障碍设计检查清单
  • 性能考量与优化措施
优先保证可运行代码,而非文字说明。在注释中包含使用示例。

The Iron Law

铁律

NO UI DESIGN BEFORE USER FLOW IS UNDERSTOOD
If you haven't mapped what the user is trying to accomplish, you cannot design UI.
NO UI DESIGN BEFORE USER FLOW IS UNDERSTOOD
若未梳理清楚用户的目标流程,则无法进行UI设计。

Design Thinking (Pre-Code)

设计思维(编码前)

Before writing any UI code, commit to answers for:
  1. Purpose: What specific problem does this interface solve?
  2. Tone: Choose an aesthetic direction and commit to it:
    • Brutally minimal, maximalist, retro-futuristic, organic/natural
    • Luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw
    • Art deco/geometric, soft/pastel, industrial/utilitarian
  3. Constraints: Framework requirements, performance budget, accessibility level
  4. Differentiation: What's the ONE thing someone will remember about this UI?
Key insight: Bold maximalism and refined minimalism both work. The enemy is indecision and generic defaults.
在编写任何UI代码前,必须明确以下问题的答案:
  1. 目的:该界面要解决什么具体问题?
  2. 风格基调:选定一种美学方向并坚持执行:
    • 极简主义、极繁主义、复古未来主义、有机自然风
    • 奢华精致风、趣味玩具风、杂志编辑风、粗粝原始风
    • 装饰艺术几何风、柔和马卡龙风、工业实用风
  3. 约束条件:框架要求、性能预算、无障碍等级
  4. 差异化:用户对该UI最深刻的记忆点是什么?
核心见解: 大胆的极繁主义与精致的极简主义都能奏效。大忌是优柔寡断和使用通用默认样式。

Loading State Order (CRITICAL)

加载状态顺序(至关重要)

Always handle states in this order:
typescript
// CORRECT order
if (error) return <ErrorState error={error} onRetry={refetch} />;
if (loading && !data) return <LoadingState />;
if (!data?.items.length) return <EmptyState />;
return <ItemList items={data.items} />;
Loading State Decision Tree:
Is there an error? → Yes: Show error with retry
                   → No: Continue
Is loading AND no data? → Yes: Show loading indicator
                        → No: Continue
Do we have data? → Yes, with items: Show data
                 → Yes, but empty: Show empty state
                 → No: Show loading (fallback)
Golden Rule: Show loading indicator ONLY when there's no data to display.
必须按以下顺序处理状态:
typescript
// 正确顺序
if (error) return <ErrorState error={error} onRetry={refetch} />;
if (loading && !data) return <LoadingState />;
if (!data?.items.length) return <EmptyState />;
return <ItemList items={data.items} />;
加载状态决策树:
是否存在错误? → 是:显示带重试按钮的错误状态
                   → 否:继续
是否处于加载状态且无数据? → 是:显示加载指示器
                        → 否:继续
是否有数据? → 是且有内容:展示数据
                 → 是但为空:展示空状态
                 → 否:展示加载状态(兜底)
黄金法则: 仅当无数据可展示时,才显示加载指示器。

Skeleton vs Spinner

骨架屏 vs 加载动画

Use Skeleton WhenUse Spinner When
Known content shapeUnknown content shape
List/card layoutsModal actions
Initial page loadButton submissions
Content placeholdersInline operations
使用骨架屏的场景使用加载动画的场景
已知内容形状未知内容形状
列表/卡片布局弹窗操作
初始页面加载按钮提交
内容占位符内联操作

Motion & Animation

动效与动画

RuleDoDon't
Reduced motionHonor
prefers-reduced-motion
Ignore user preferences
PropertiesAnimate
transform
/
opacity
only
Animate
width
/
height
/
top
/
left
TransitionsList properties explicitlyUse
transition: all
Duration150-300ms for micro-interactionsToo fast (<100ms) or slow (>500ms)
InterruptibleAllow animation cancellationLock UI during animation
css
/* CORRECT: Compositor-friendly, respects preferences */
@media (prefers-reduced-motion: no-preference) {
  .card { transition: transform 200ms ease-out, opacity 200ms ease-out; }
  .card:hover { transform: translateY(-2px); opacity: 0.95; }
}
规则正确做法错误做法
减少动效尊重
prefers-reduced-motion
设置
忽略用户偏好
动画属性仅对
transform
/
opacity
执行动画
width
/
height
/
top
/
left
执行动画
过渡效果明确列出过渡属性使用
transition: all
时长微交互使用150-300ms过快(<100ms)或过慢(>500ms)
可中断性允许取消动画动画期间锁定UI
css
/* 正确示例: compositor友好,尊重用户偏好 */
@media (prefers-reduced-motion: no-preference) {
  .card { transition: transform 200ms ease-out, opacity 200ms ease-out; }
  .card:hover { transform: translateY(-2px); opacity: 0.95; }
}

Error Handling Hierarchy

错误处理层级

LevelUse For
Inline errorField-level validation
Toast notificationRecoverable errors, user can retry
Error bannerPage-level errors, data still partially usable
Full error screenUnrecoverable, needs user action
层级适用场景
内联错误字段级验证
Toast通知可恢复错误,用户可重试
错误横幅页面级错误,数据仍可部分使用
全屏错误页不可恢复错误,需要用户操作

Success Criteria Framework

成功标准框架

Every UI must have explicit success criteria:
  1. Task completion: Can user complete their goal?
  2. Error recovery: Can user recover from mistakes?
  3. Accessibility: Can all users access it?
  4. Performance: Does it feel responsive?
每个UI必须具备明确的成功标准:
  1. 任务完成度:用户能否完成目标?
  2. 错误恢复能力:用户能否从错误中恢复?
  3. 无障碍性:所有用户能否访问该界面?
  4. 性能表现:界面是否响应迅速?

Typography Rules

排版规则

RuleCorrectWrong
Ellipsis
(single character)
...
(three periods)
Quotes
" "
curly quotes
" "
straight quotes
Units
10&nbsp;MB
(non-breaking)
10 MB
(can break)
Shortcuts
⌘&nbsp;K
(non-breaking)
⌘ K
(can break)
Loading text
Loading…
Loading...
Numbers in tables
font-variant-numeric: tabular-nums
Default proportional
Headings
text-wrap: balance
Unbalanced line breaks
Line length65-75 characters maxUnlimited width
规则正确示例错误示例
省略号
(单个字符)
...
(三个句号)
引号
“ ”
弯引号
" "
直引号
单位
10&nbsp;MB
(非断空格)
10 MB
(可能换行)
快捷键
⌘&nbsp;K
(非断空格)
⌘ K
(可能换行)
加载文本
Loading…
Loading...
表格中的数字
font-variant-numeric: tabular-nums
默认比例数字
标题
text-wrap: balance
不平衡的换行
行长度最多65-75个字符无限制宽度

Content Overflow Handling

内容溢出处理

Prevent broken layouts from user-generated content:
ScenarioSolution
Single-line overflow
truncate
(Tailwind) or
text-overflow: ellipsis
Multi-line overflow
line-clamp-2
/
line-clamp-3
Long words/URLs
break-words
or
overflow-wrap: break-word
Flex child truncationAdd
min-w-0
to flex children (critical!)
Empty strings/arraysShow placeholder, not broken UI
tsx
{/* Flex truncation pattern - min-w-0 is REQUIRED */}
<div className="flex items-center gap-2 min-w-0">
  <Avatar />
  <span className="truncate min-w-0">{user.name}</span>
</div>
Test with: short text, average text, and absurdly long text (50+ characters).
防止用户生成的内容导致布局破裂:
场景解决方案
单行溢出
truncate
(Tailwind)或
text-overflow: ellipsis
多行溢出
line-clamp-2
/
line-clamp-3
长单词/URL
break-words
overflow-wrap: break-word
Flex子元素截断为Flex子元素添加
min-w-0
(至关重要!)
空字符串/数组显示占位符,而非破裂的UI
tsx
{/* Flex截断模式 - min-w-0是必须的 */}
<div className="flex items-center gap-2 min-w-0">
  <Avatar />
  <span className="truncate min-w-0">{user.name}</span>
</div>
测试用例: 短文本、普通文本、超长文本(50+字符)。

Universal Questions (Answer First)

通用问题(必须先回答)

ALWAYS answer before designing/reviewing:
  1. What is the user trying to accomplish? - Specific task, not feature
  2. What are the steps? - Click by click
  3. What can go wrong? - Every error state
  4. Who might struggle? - Accessibility needs
  5. What's the existing pattern? - Project conventions
设计/评审前必须明确:
  1. 用户的目标是什么? - 具体任务,而非功能
  2. 步骤有哪些? - 逐点击流程
  3. 可能出现哪些问题? - 所有错误状态
  4. 哪些用户可能遇到困难? - 无障碍需求
  5. 现有模式是什么? - 项目约定

User Flow First

用户流程优先

Before any UI work, map the flow:
User Flow: Create Account
1. User lands on signup page
2. User enters email
3. User enters password
4. User confirms password
5. System validates inputs (inline)
6. User clicks submit
7. System processes (loading state)
8. Success: User sees confirmation + redirect
9. Error: User sees error + can retry
For each step, identify:
  • What user sees
  • What user does
  • What feedback they get
  • What can go wrong
在任何UI工作前,先梳理流程:
用户流程:创建账户
1. 用户进入注册页面
2. 用户输入邮箱
3. 用户输入密码
4. 用户确认密码
5. 系统验证输入(内联验证)
6. 用户点击提交
7. 系统处理请求(加载状态)
8. 成功:用户看到确认信息并跳转
9. 错误:用户看到错误信息并可重试
针对每个步骤,明确:
  • 用户看到什么
  • 用户做什么
  • 用户得到什么反馈
  • 可能出现什么问题

UX Review Checklist

UX评审检查清单

CheckCriteriaExample Issue
Task completionCan user complete goal?Button doesn't work
DiscoverabilityCan user find what they need?Hidden navigation
FeedbackDoes user know what's happening?No loading state
Error handlingCan user recover from errors?No error message
EfficiencyCan user complete task quickly?Too many steps
Severity levels:
  • BLOCKS: User cannot complete task
  • IMPAIRS: User can complete but with difficulty
  • MINOR: Small friction, not blocking
检查项标准示例问题
任务完成度用户能否完成目标?按钮无法点击
可发现性用户能否找到所需功能?隐藏的导航
反馈机制用户能否知晓当前状态?无加载状态
错误处理用户能否从错误中恢复?无错误提示
效率用户能否快速完成任务?步骤过多
严重等级:
  • 阻塞:用户无法完成任务
  • 影响:用户可完成但存在困难
  • 轻微:小摩擦,不阻塞任务

Accessibility Review Checklist (WCAG 2.1 AA)

无障碍评审检查清单(WCAG 2.1 AA)

CheckCriterionHow to Verify
KeyboardAll interactive elements keyboard accessibleTab through entire flow
Focus visibleCurrent focus clearly visibleTab and check highlight
Focus orderLogical tab orderTab matches visual order
LabelsAll inputs have labelsCheck
<label>
or
aria-label
Alt textImages have meaningful altCheck
alt
attributes
Color contrast4.5:1 for text, 3:1 for largeUse contrast checker
Color aloneInfo not conveyed by color onlyCheck without color
Screen readerContent accessible via SRTest with VoiceOver/NVDA
For each issue found:
markdown
- [WCAG 2.1 1.4.3] Color contrast at `component:line`
  - Current: 3.2:1 (fails AA)
  - Required: 4.5:1
  - Fix: Change text color to #333 (7.1:1)
检查项标准验证方法
键盘导航所有交互元素支持键盘访问按Tab键遍历整个流程
焦点可见性当前焦点清晰可见按Tab键检查高亮效果
焦点顺序合理的Tab顺序Tab顺序与视觉顺序一致
标签所有输入框均有标签检查
<label>
aria-label
替代文本图片有意义的alt文本检查
alt
属性
颜色对比度文本4.5:1,大文本3:1使用对比度检查工具
仅用颜色传递信息信息不能仅通过颜色传递移除颜色后检查
屏幕阅读器内容可通过屏幕阅读器访问使用VoiceOver/NVDA测试
针对发现的每个问题:
markdown
- [WCAG 2.1 1.4.3] 颜色对比度问题位于 `component:line`
  - 当前值:3.2:1(不满足AA标准)
  - 要求值:4.5:1
  - 修复方案:将文本颜色改为#333(对比度7.1:1)

Form Best Practices

表单最佳实践

RuleImplementation
AutocompleteAdd
autocomplete="email"
,
autocomplete="name"
, etc.
Input typesUse
type="email"
,
type="tel"
,
type="url"
for mobile keyboards
Input modesAdd
inputMode="numeric"
for number-only fields
Never block pasteNo
onPaste
+
preventDefault()
— users paste passwords
Spellcheck off
spellCheck={false}
on emails, codes, usernames
Unsaved changesWarn before navigation (
beforeunload
or router guard)
Error focusFocus first error field on submit
Shared hit targetsCheckbox/radio label + control = one clickable area
tsx
<input
  type="email"
  autoComplete="email"
  spellCheck={false}
  inputMode="email"
  // Never: onPaste={(e) => e.preventDefault()}
/>
规则实现方式
自动填充添加
autocomplete="email"
autocomplete="name"
输入类型使用
type="email"
type="tel"
type="url"
适配移动端键盘
输入模式为纯数字字段添加
inputMode="numeric"
禁止阻止粘贴不要使用
onPaste
+
preventDefault()
— 用户需要粘贴密码
关闭拼写检查对邮箱、验证码、用户名设置
spellCheck={false}
未保存更改提示导航前提示用户(使用
beforeunload
或路由守卫)
错误焦点提交后聚焦第一个错误字段
共享点击区域复选框/单选框的标签与控件合并为一个可点击区域
tsx
<input
  type="email"
  autoComplete="email"
  spellCheck={false}
  inputMode="email"
  // 禁止:onPaste={(e) => e.preventDefault()}
/>

Visual Design Checklist

视觉设计检查清单

CheckGoodBad
HierarchyClear visual priorityEverything same size
SpacingConsistent rhythmRandom gaps
AlignmentElements aligned to gridMisaligned elements
Interactive statesHover/active/focus distinctNo state changes
FeedbackClear response to actionsSilent interactions
检查项良好示例不良示例
层级结构清晰的视觉优先级所有元素大小一致
间距一致的节奏随机间距
对齐元素对齐到网格元素错位
交互状态悬停/激活/焦点状态区分明显无状态变化
反馈机制对操作有清晰响应无反馈的交互

Visual Creativity (Avoid AI Slop)

视觉创意(避免AI通用风格)

When creating frontends, avoid generic AI aesthetics:
  • Fonts: Choose distinctive typography, not defaults (avoid Inter, Roboto, Arial, system fonts)
  • Colors: Commit to cohesive palette. Dominant colors with sharp accents > safe gradients
  • Avoid: Purple gradients on white, predictable layouts, cookie-cutter Bootstrap/Tailwind defaults
  • Icons: Use SVG icons (Heroicons, Lucide, Simple Icons). NEVER use emoji as UI icons
  • Cursor: Add
    cursor-pointer
    to ALL clickable elements
  • Hover: Use color/opacity transitions. Avoid
    scale
    transforms that shift layout
  • Backgrounds: Add depth with subtle textures, gradients, or grain instead of flat colors
Make creative choices that feel designed for the specific context. No two designs should look the same.
创建前端界面时,避免使用通用AI美学:
  • 字体:选择独特的排版,而非默认字体(避免Inter、Roboto、Arial、系统字体)
  • 颜色:坚持使用统一的调色板。主色调搭配鲜明强调色 > 安全渐变
  • 避免:白色背景上的紫色渐变、可预测的布局、千篇一律的Bootstrap/Tailwind默认样式
  • 图标:使用SVG图标(Heroicons、Lucide、Simple Icons)。绝对不要使用emoji作为UI图标
  • 光标:为所有可点击元素添加
    cursor-pointer
  • 悬停效果:使用颜色/透明度过渡。避免使用会改变布局的
    scale
    变换
  • 背景:使用微妙的纹理、渐变或颗粒感增加深度,而非纯色
根据设计思维阶段选定的美学方向做出创意选择。不同设计不应千篇一律。

Spatial Composition (Break the Grid)

空间构图(打破常规网格)

Move beyond safe, centered layouts:
TechniqueEffectWhen to Use
AsymmetryDynamic tension, visual interestHero sections, feature highlights
OverlapDepth, connection between elementsCards, images, testimonials
Diagonal flowEnergy, movementLanding pages, marketing
Grid-breakingEmphasis, surpriseKey CTAs, focal points
Generous negative spaceLuxury, breathing roomPremium products, editorial
Controlled densityInformation-rich, productiveDashboards, data-heavy UIs
Rule: Match spatial composition to the aesthetic direction chosen in Design Thinking. Minimalist = negative space. Maximalist = controlled density.
超越安全的居中布局:
技巧效果适用场景
不对称布局动态张力,视觉吸引力首页横幅、功能亮点
元素重叠层次感,元素间的关联性卡片、图片、用户评价
对角线流活力感,动感着陆页、营销页面
打破网格强调,惊喜感关键CTA、焦点元素
充足的留白奢华感,呼吸空间高端产品、编辑类页面
受控的密度信息丰富,高效仪表盘、数据密集型UI
规则: 空间构图需与设计思维阶段选定的美学方向匹配。极简主义=留白。极繁主义=受控密度。

Component Patterns

组件模式

Buttons

按钮

tsx
// Primary action button with all states
<button
  type="button"
  onClick={handleAction}
  disabled={isLoading || isDisabled}
  aria-busy={isLoading}
  aria-disabled={isDisabled}
  className={cn(
    'btn-primary',
    isLoading && 'btn-loading'
  )}
>
  {isLoading ? (
    <>
      <Spinner aria-hidden />
      <span>Processing...</span>
    </>
  ) : (
    'Submit'
  )}
</button>
tsx
// 包含所有状态的主要操作按钮
<button
  type="button"
  onClick={handleAction}
  disabled={isLoading || isDisabled}
  aria-busy={isLoading}
  aria-disabled={isDisabled}
  className={cn(
    'btn-primary',
    isLoading && 'btn-loading'
  )}
>
  {isLoading ? (
    <>
      <Spinner aria-hidden />
      <span>处理中...</span>
    </>
  ) : (
    '提交'
  )}
</button>

Forms with Validation

带验证的表单

tsx
<form onSubmit={handleSubmit} noValidate>
  <div className="form-field">
    <label htmlFor="email">
      Email <span aria-hidden>*</span>
      <span className="sr-only">(required)</span>
    </label>
    <input
      id="email"
      type="email"
      value={email}
      onChange={handleChange}
      aria-invalid={errors.email ? 'true' : undefined}
      aria-describedby={errors.email ? 'email-error' : 'email-hint'}
      required
    />
    <span id="email-hint" className="hint">
      We'll never share your email
    </span>
    {errors.email && (
      <span id="email-error" role="alert" className="error">
        {errors.email}
      </span>
    )}
  </div>
</form>
tsx
<form onSubmit={handleSubmit} noValidate>
  <div className="form-field">
    <label htmlFor="email">
      邮箱 <span aria-hidden>*</span>
      <span className="sr-only">(必填)</span>
    </label>
    <input
      id="email"
      type="email"
      value={email}
      onChange={handleChange}
      aria-invalid={errors.email ? 'true' : undefined}
      aria-describedby={errors.email ? 'email-error' : 'email-hint'}
      required
    />
    <span id="email-hint" className="hint">
      我们绝不会分享您的邮箱
    </span>
    {errors.email && (
      <span id="email-error" role="alert" className="error">
        {errors.email}
      </span>
    )}
  </div>
</form>

Loading States

加载状态

tsx
function DataList({ isLoading, data, error }) {
  if (isLoading) {
    return (
      <div aria-live="polite" aria-busy="true">
        <Spinner />
        <span>Loading items...</span>
      </div>
    );
  }

  if (error) {
    return (
      <div role="alert" className="error-state">
        <p>Failed to load items: {error.message}</p>
        <button onClick={retry}>Try again</button>
      </div>
    );
  }

  if (!data?.length) {
    return (
      <div className="empty-state">
        <p>No items found</p>
        <button onClick={createNew}>Create your first item</button>
      </div>
    );
  }

  return <ul>{data.map(item => <Item key={item.id} {...item} />)}</ul>;
}
tsx
function DataList({ isLoading, data, error }) {
  if (isLoading) {
    return (
      <div aria-live="polite" aria-busy="true">
        <Spinner />
        <span>加载数据中...</span>
      </div>
    );
  }

  if (error) {
    return (
      <div role="alert" className="error-state">
        <p>加载失败:{error.message}</p>
        <button onClick={retry}>重试</button>
      </div>
    );
  }

  if (!data?.length) {
    return (
      <div className="empty-state">
        <p>暂无数据</p>
        <button onClick={createNew}>创建第一条数据</button>
      </div>
    );
  }

  return <ul>{data.map(item => <Item key={item.id} {...item} />)}</ul>;
}

Error Messages

错误提示

tsx
// Inline error with recovery action
<div role="alert" className="error-banner">
  <Icon name="error" aria-hidden />
  <div>
    <p className="error-title">Upload failed</p>
    <p className="error-detail">File too large. Maximum size is 10MB.</p>
  </div>
  <button onClick={selectFile}>Choose different file</button>
</div>
tsx
// 带恢复操作的内联错误
<div role="alert" className="error-banner">
  <Icon name="error" aria-hidden />
  <div>
    <p className="error-title">上传失败</p>
    <p className="error-detail">文件过大,最大支持10MB。</p>
  </div>
  <button onClick={selectFile}>选择其他文件</button>
</div>

Responsive Design Checklist

响应式设计检查清单

BreakpointCheck
Mobile (< 640px)Touch targets 44px+, no horizontal scroll
Tablet (640-1024px)Layout adapts, navigation accessible
Desktop (> 1024px)Content readable, not too wide
断点检查项
移动端(< 640px)触摸目标≥44px,无横向滚动
平板端(640-1024px)布局自适应,导航可访问
桌面端(> 1024px)内容可读,宽度不过宽

Performance Rules

性能规则

RuleWhyImplementation
Virtualize large lists>50 items kills performanceUse
virtua
,
react-window
, or
content-visibility: auto
No layout reads in renderCauses forced reflowAvoid
getBoundingClientRect
,
offsetHeight
in render
Lazy load imagesReduces initial load
loading="lazy"
on below-fold images
Prioritize critical imagesFaster LCP
fetchpriority="high"
or Next.js
priority
Preconnect CDNFaster asset loading
<link rel="preconnect" href="https://cdn...">
Preload fontsPrevents FOUT
<link rel="preload" as="font" crossorigin>
规则原因实现方式
虚拟化长列表超过50条数据会严重影响性能使用
virtua
react-window
content-visibility: auto
渲染中不读取布局会导致强制重排避免在渲染中使用
getBoundingClientRect
offsetHeight
懒加载图片减少初始加载时间对首屏以下的图片使用
loading="lazy"
优先加载关键图片更快的LCP使用
fetchpriority="high"
或Next.js的
priority
预连接CDN更快的资源加载
<link rel="preconnect" href="https://cdn...">
预加载字体避免FOUT(无样式文本闪烁)
<link rel="preload" as="font" crossorigin>

URL & State Management

URL与状态管理

URL should reflect UI state. If it uses
useState
, consider URL sync.
State TypeURL Strategy
Filters/search
?q=term&category=books
Active tab
?tab=settings
Pagination
?page=3
Expanded panels
?panel=details
Sort order
?sort=price&dir=asc
Benefits: Shareable links, back button works, refresh preserves state.
tsx
// Use nuqs, next-usequerystate, or similar
const [tab, setTab] = useQueryState('tab', { defaultValue: 'overview' })
URL应反映UI状态。 如果使用
useState
,考虑与URL同步。
状态类型URL策略
筛选/搜索
?q=term&category=books
激活标签页
?tab=settings
分页
?page=3
展开面板
?panel=details
排序顺序
?sort=price&dir=asc
优势: 可分享链接、返回按钮正常工作、刷新保留状态。
tsx
// 使用nuqs、next-usequerystate或类似库
const [tab, setTab] = useQueryState('tab', { defaultValue: 'overview' })

Touch & Mobile

触摸与移动端

RuleImplementation
44px touch targetsMinimum for buttons, links, controls
No double-tap delay
touch-action: manipulation
on interactive elements
Modal scroll lock
overscroll-behavior: contain
in modals/drawers
Safe areas
padding: env(safe-area-inset-bottom)
for notches
Tap highlightSet
-webkit-tap-highlight-color
intentionally
规则实现方式
44px触摸目标按钮、链接、控件的最小尺寸
消除双击延迟对交互元素使用
touch-action: manipulation
弹窗滚动锁定在弹窗/抽屉中使用
overscroll-behavior: contain
安全区域对刘海屏使用
padding: env(safe-area-inset-bottom)
点击高亮明确设置
-webkit-tap-highlight-color

Red Flags - STOP and Reconsider

危险信号 - 立即停止并重新考虑

If you find yourself:
  • Designing UI before mapping user flow
  • Focusing on aesthetics before functionality
  • Ignoring accessibility ("we'll add it later")
  • Not handling error states
  • Not providing loading feedback
  • Using color alone to convey information
  • Making decisions based on "it looks nice"
STOP. Go back to user flow.
如果你发现自己在做以下事情:
  • 未梳理用户流程就开始设计UI
  • 先关注美学再关注功能
  • 忽略无障碍设计(“以后再加”)
  • 未处理错误状态
  • 未提供加载反馈
  • 仅用颜色传递信息
  • 基于“看起来不错”做决策
立即停止。回到用户流程梳理。

Rationalization Prevention

避免合理化借口

ExcuseReality
"Most users don't use keyboard"Some users ONLY use keyboard.
"We'll add accessibility later"Retrofitting is 10x harder.
"Error states are edge cases"Errors happen. Handle them.
"Loading is fast, no need for state"Network varies. Show state.
"It looks better without labels"Unlabeled inputs are inaccessible.
"Users can figure it out"If it's confusing, fix it.
借口现实
“大多数用户不使用键盘”有些用户只能使用键盘。
“我们以后再加无障碍设计”后期改造的难度是前期的10倍。
“错误状态是边缘情况”错误总会发生。必须处理。
“加载很快,不需要状态提示”网络状况多变。必须显示状态。
“没有标签看起来更好”无标签输入框是无障碍违规的。
“用户能自己弄明白”如果用户感到困惑,就需要修复。

Anti-patterns Blocklist (Flag These)

反模式黑名单(必须标记)

Anti-patternWhy It's WrongFix
user-scalable=no
Blocks accessibility zoomRemove it
maximum-scale=1
Blocks accessibility zoomRemove it
transition: all
Performance + unexpected effectsList properties explicitly
outline-none
without replacement
Removes focus indicatorAdd
focus-visible:ring-*
<div onClick>
Not keyboard accessibleUse
<button>
or
<a>
Images without
width
/
height
Causes layout shift (CLS)Add explicit dimensions
Form inputs without labelsInaccessibleAdd
<label>
or
aria-label
Icon buttons without
aria-label
Unnamed to screen readersAdd
aria-label
Emoji icons (🚀 ✨ 💫)Unprofessional, inconsistentUse SVG icons
Hardcoded date/number formatsBreaks internationalizationUse
Intl.DateTimeFormat
autoFocus
everywhere
Disorienting, mobile issuesUse sparingly, desktop only
反模式错误原因修复方案
user-scalable=no
阻止无障碍缩放移除该设置
maximum-scale=1
阻止无障碍缩放移除该设置
transition: all
性能问题 + 意外效果明确列出过渡属性
outline-none
且无替代方案
移除了焦点指示器添加
focus-visible:ring-*
<div onClick>
不支持键盘访问使用
<button>
<a>
图片无
width
/
height
导致布局偏移(CLS)添加明确的尺寸
表单输入框无标签无障碍违规添加
<label>
aria-label
图标按钮无
aria-label
屏幕阅读器无法识别添加
aria-label
使用emoji图标(🚀 ✨ 💫)不专业,不一致使用SVG图标
硬编码日期/数字格式破坏国际化使用
Intl.DateTimeFormat
滥用
autoFocus
造成困扰,移动端问题谨慎使用,仅限桌面端

Light/Dark Mode

明暗模式

RuleLight ModeDark Mode
Glass/transparent
bg-white/80
or higher
bg-black/80
or darker
Text contrastMinimum 4.5:1 (slate-900)Minimum 4.5:1 (slate-100)
Borders
border-gray-200
border-white/10
HTML attribute
color-scheme: dark
on
<html>
Meta tagMatch backgroundMatch background
html
<!-- Dark mode setup -->
<html style="color-scheme: dark">
<head><meta name="theme-color" content="#0f172a"></head>
规则浅色模式深色模式
玻璃态/透明
bg-white/80
或更高透明度
bg-black/80
或更深
文本对比度最低4.5:1(slate-900)最低4.5:1(slate-100)
边框
border-gray-200
border-white/10
HTML属性
<html>
上设置
color-scheme: dark
Meta标签匹配背景色匹配背景色
html
<!-- 深色模式设置 -->
<html style="color-scheme: dark">
<head><meta name="theme-color" content="#0f172a"></head>

Output Format

输出格式

markdown
undefined
markdown
undefined

Frontend Review: [Component/Feature]

前端评审:[组件/功能]

User Flow

用户流程

[Step-by-step what user is trying to do]
[用户试图完成的步骤]

Success Criteria

成功标准

  • User can complete [task]
  • User can recover from errors
  • All users can access (keyboard, screen reader)
  • Interface feels responsive
  • 用户可完成[任务]
  • 用户可从错误中恢复
  • 所有用户可访问(键盘、屏幕阅读器)
  • 界面响应迅速

UX Issues

UX问题

SeverityIssueLocationImpactFix
BLOCKS[Issue]
file:line
[Impact][Fix]
严重等级问题位置影响修复方案
阻塞[问题]
file:line
[影响][修复方案]

Accessibility Issues

无障碍问题

WCAGIssueLocationFix
1.4.3[Issue]
file:line
[Fix]
WCAG标准问题位置修复方案
1.4.3[问题]
file:line
[修复方案]

Visual Issues

视觉问题

IssueLocationFix
[Issue]
file:line
[Fix]
问题位置修复方案
[问题]
file:line
[修复方案]

Recommendations

建议

  1. [Most critical fix]
  2. [Second fix]
undefined
  1. [最关键的修复]
  2. [次要修复]
undefined

UI States Checklist (CRITICAL)

UI状态检查清单(至关重要)

Before completing ANY UI component:
完成任何UI组件前必须检查:

States

状态

  • Error state handled and shown to user
  • Loading state shown ONLY when no data exists
  • Empty state provided for all collections/lists
  • Success state with appropriate feedback
  • 错误状态已处理并展示给用户
  • 加载状态仅在无数据时显示
  • 所有集合/列表都有空状态
  • 提供适当的成功状态反馈

Buttons & Mutations

按钮与变更操作

  • Buttons disabled during async operations
  • Buttons show loading indicator
  • Mutations have onError handler with user feedback
  • No double-click possible on submit buttons
  • 异步操作期间按钮禁用
  • 按钮显示加载指示器
  • 变更操作有onError处理并提供用户反馈
  • 提交按钮无法被双击

Data Handling

数据处理

  • State order: Error → Loading (no data) → Empty → Success
  • All user actions have feedback (toast/visual)
  • 状态顺序:错误 → 加载(无数据) → 空 → 成功
  • 所有用户操作都有反馈(toast/视觉提示)

Final Check

最终检查

Before completing frontend work:
  • User flow mapped and understood
  • All states handled (loading, error, empty, success)
  • Keyboard navigation works
  • Screen reader tested
  • Color contrast verified (4.5:1 minimum)
  • Touch targets adequate on mobile (44px+)
  • Error messages clear and actionable
  • Success criteria met
  • No emoji icons (SVG only)
  • prefers-reduced-motion
    respected
  • Light/dark mode contrast verified
  • cursor-pointer
    on all clickable elements
  • No
    transition: all
    in codebase
完成前端工作前:
  • 用户流程已梳理并理解
  • 所有状态已处理(加载、错误、空、成功)
  • 键盘导航正常工作
  • 已测试屏幕阅读器
  • 已验证颜色对比度(最低4.5:1)
  • 移动端触摸目标足够(≥44px)
  • 错误提示清晰且可操作
  • 满足成功标准
  • 无emoji图标(仅使用SVG)
  • 已尊重
    prefers-reduced-motion
    设置
  • 已验证明暗模式的对比度
  • 所有可点击元素都有
    cursor-pointer
  • 代码库中无
    transition: all
    ",