designing-user-experience
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDesigning User Experience
用户体验设计
Overview
概述
Visual design decides what an interface looks like when everything goes right. UX decides what happens the rest of the time — and the rest of the time is most of the time.
Core principle: Design the states, not the screens. A "screen" is one state of a component; shipping only that one is why generated UI looks finished and breaks on contact with real data.
Companion skills: for visual craft, for access, to audit a finished UI.
designing-frontend-interfacesbuilding-accessible-interfacesreviewing-interface-quality视觉设计决定一切正常时界面的外观。UX则决定其他所有情况下的表现——而这些“其他情况”才是大多数时候的常态。
**核心原则:设计状态,而非屏幕。**一个“屏幕”只是组件的一种状态;只交付这一种状态,就是为什么生成式UI看起来完工,但一接触真实数据就失效的原因。
配套技能: 用于视觉设计, 用于无障碍设计, 用于审核已完成的UI。
designing-frontend-interfacesbuilding-accessible-interfacesreviewing-interface-qualityThe Iron Law
铁律
NO COMPONENT IS DONE UNTIL ITS EMPTY, LOADING, ERROR, AND PARTIAL STATES EXISTA list that only renders when it has items is not a list component — it is a demo. If the design has no answer for "what does a new user with no data see", the design is incomplete.
NO COMPONENT IS DONE UNTIL ITS EMPTY, LOADING, ERROR, AND PARTIAL STATES EXIST仅在有数据项时才渲染的列表不是列表组件——只是一个演示。如果设计没有回答“无数据的新用户会看到什么”,那么这个设计就是不完整的。
Checklist
检查清单
Create a task for each item:
- Map the flow — entry point → steps → success, and every branch off it
- Fill the state matrix — for every component that touches data
- Design the failure paths — what breaks, what the user sees, how they recover
- Write the copy — buttons, errors, empty states, confirmations
- Set the feedback budget — what happens at 100ms, 1s, 10s
- Handle the destructive and irreversible — confirm, undo, or both
- Check touch and small viewport — targets, reach, keyboard-open behavior
- Walk the flow as a first-time user — with no data, no permissions, on a slow connection
为每一项创建任务:
- 梳理流程 —— 入口点 → 步骤 → 成功状态,以及所有分支路径
- 填充状态矩阵 —— 针对所有涉及数据的组件
- 设计故障路径 —— 什么会失效、用户会看到什么、如何恢复
- 撰写文案 —— 按钮、错误提示、空状态、确认信息
- 设定反馈阈值 —— 100ms、1s、10s时分别触发什么反馈
- 处理破坏性与不可逆操作 —— 确认、撤销,或两者兼具
- 检查触控与小视口适配 —— 目标区域、可触及范围、键盘弹出后的行为
- 以首次用户身份走查流程 —— 无数据、无权限、慢网络环境下的体验
Step 1: Map the Flow
步骤1:梳理流程
Before designing screens, write the flow as text. It takes two minutes and exposes gaps no mockup will.
GOAL: Reviewer approves a pending expense
Entry: Email link → deep link into the item (may not be authenticated)
Step 1: Authenticate → already logged in? skip
Step 2: View item + receipt
Step 3: Approve | Reject | Request info
Success: Confirmation + next pending item queued
Exit: No items left → done state, not an empty table
Branches:
Not authorized for this item → explain who is, offer to forward
Item already actioned by someone → show who and when, no error framing
Receipt fails to load → approve still possible, receipt shows retry
Offline mid-approve → queue and retry, tell them it is queuedThe branches are the design work. Any flow whose branch list is empty has not been thought about yet.
Reduce steps before styling them. Each step is a chance to abandon. Ask of every one: can this be defaulted, inferred, deferred to later, or removed? A form field that can be derived should not be asked.
Worked flows for the patterns that recur — onboarding, search and filtering, multi-step wizards, undo, permissions and paywalls: references/flow-patterns.md.
在设计屏幕之前,先用文字写出流程。这只需要两分钟,但能暴露任何原型都无法发现的漏洞。
目标:审核人批准待处理的费用报销
入口: 邮件链接 → 深度链接至报销项(可能未认证)
步骤1: 身份认证 → 已登录则跳过
步骤2: 查看报销项及收据
步骤3: 批准 | 拒绝 | 请求补充信息
成功状态: 确认提示 + 自动加载下一个待处理报销项
退出: 无待处理项 → 显示完成状态,而非空表格
分支路径:
无此报销项权限 → 说明有权限的角色,提供转发选项
报销项已被他人处理 → 显示处理人及时间,不按错误场景展示
收据加载失败 → 仍可批准,收据区域显示重试按钮
批准过程中离线 → 加入队列并重试,告知用户操作已排队**分支路径才是设计的核心工作。**任何分支列表为空的流程都还未经过深思熟虑。
**在样式化前减少步骤。**每一步都可能导致用户放弃。对每一步都要问:能否默认填充、自动推断、延后处理或直接移除?可推导的表单字段不应让用户填写。
重复模式的成熟流程参考——入职引导、搜索与筛选、多步骤向导、撤销、权限与付费墙:references/flow-patterns.md。
Step 2: The State Matrix
步骤2:状态矩阵
Every component that displays data or accepts input has these states. Enumerate them explicitly; do not discover them in production.
| State | Trigger | Requirement |
|---|---|---|
| Empty — first use | New user, nothing created yet | Explain the feature and give the primary action. Never "No data." |
| Empty — no results | Filter or search matched nothing | Echo the query, offer to clear filters. Different from first-use empty. |
| Empty — cleared | User completed everything | Acknowledge it. This is a success state, not an absence. |
| Loading — first | No cached data | Skeleton matching real layout, delayed ~200ms |
| Loading — refresh | Data exists, updating | Keep old data visible, subtle indicator. Never blank the screen. |
| Partial | Some sources failed | Show what loaded, mark what did not, offer retry for that part |
| Error — recoverable | Timeout, 5xx, network | What happened, what to do, a retry control |
| Error — permanent | 404, deleted, revoked | Say it plainly, offer the nearest useful destination |
| Error — permission | 403 | Who does have access, how to request it |
| Offline | Connection lost | What still works, what is queued |
| Too much | 10,000 rows | Pagination or virtualization, plus a count |
| Stale | Cache older than threshold | Timestamp and a refresh control |
| Success | Action completed | Confirm it, and say what changed |
The four empty states are genuinely different. Collapsing them into one "No items found" is the most common UX shortcut in generated interfaces.
First use: "No expenses yet — add your first to start tracking." [Add expense]
No results: "No expenses match 'q3 travel'." [Clear filters]
Cleared: "All caught up. Nothing needs review."
Error: "Couldn't load expenses. Check your connection." [Retry]所有展示数据或接受输入的组件都具备以下状态。需明确枚举,不要等到生产环境才发现遗漏。
| 状态 | 触发条件 | 要求 |
|---|---|---|
| 空状态——首次使用 | 新用户,尚未创建任何内容 | 说明功能并提供主要操作按钮。绝对不要只显示“无数据”。 |
| 空状态——无结果 | 筛选或搜索未匹配到内容 | 回显查询内容,提供清除筛选选项。与首次使用的空状态不同。 |
| 空状态——已完成 | 用户完成所有操作 | 给予确认。这是成功状态,而非缺失状态。 |
| 加载状态——首次加载 | 无缓存数据 | 显示与真实布局匹配的骨架屏,延迟约200ms触发 |
| 加载状态——刷新 | 已有数据,正在更新 | 保留旧数据可见,显示微妙的加载指示器。绝对不要清空屏幕。 |
| 部分加载状态 | 部分数据源加载失败 | 显示已加载内容,标记未加载部分,提供该部分的重试选项 |
| 错误状态——可恢复 | 超时、5xx错误、网络问题 | 说明问题、解决方案、重试控件 |
| 错误状态——永久 | 404、资源已删除、权限被撤销 | 直白说明,提供最近的有用跳转目标 |
| 错误状态——权限不足 | 403错误 | 说明谁拥有访问权限,以及申请方式 |
| 离线状态 | 连接中断 | 说明仍可使用的功能,以及已排队的操作 |
| 数据过多状态 | 10000行数据 | 提供分页或虚拟滚动,加上数据计数 |
| 数据过期状态 | 缓存超过阈值 | 显示时间戳及刷新控件 |
| 成功状态 | 操作完成 | 确认操作成功,并说明产生的变化 |
**四种空状态完全不同。**将它们合并为单一的“未找到项目”是生成式界面中最常见的UX捷径错误。
首次使用: "暂无报销记录 — 添加你的第一笔报销开始跟踪。" [添加报销]
无结果: "没有匹配'Q3差旅'的报销记录。" [清除筛选]
已完成: "全部处理完毕,暂无待审核项。"
错误: "无法加载报销记录,请检查网络连接。" [重试]Step 3: Forms
步骤3:表单设计
Forms are where most UX failures live because they are where users actually type.
表单是大多数UX故障的重灾区,因为这是用户实际输入内容的地方。
Structure
结构
- One column. Multi-column forms cause skipped fields and are ambiguous with keyboard order. Exceptions: genuinely paired fields (city/state, expiry/CVC).
- Labels above inputs, always visible. Placeholder-as-label disappears on focus, fails at low contrast, and breaks autofill and screen readers.
- Group related fields with real spacing (see the proximity rule in ).
designing-frontend-interfaces - Mark whichever is rarer — if most fields are required, mark the optional ones instead. Never mark nothing.
- Show requirements before typing, not as an error afterwards. "8+ characters, one number" belongs under the field from the start.
- 单列布局。多列表单会导致用户跳过字段,且键盘导航顺序模糊。例外:真正配对的字段(城市/州、有效期/CVC)。
- 标签置于输入框上方,始终可见。用占位符当标签会在聚焦时消失,低对比度下可读性差,还会破坏自动填充和屏幕阅读器功能。
- 分组相关字段,使用真实间距(参考中的邻近原则)。
designing-frontend-interfaces - 标记少数派字段——如果大多数字段是必填项,就标记可选字段。绝对不要什么都不标记。
- 在输入前显示要求,而非事后显示错误提示。“8个字符以上,包含一个数字”应从一开始就放在字段下方。
Validation Timing
验证时机
| When | Do |
|---|---|
| While typing, field never blurred | Nothing. Validating on keystroke 3 of an email is hostile. |
| On blur, field non-empty | Validate this field |
| While typing, after an error was shown | Re-validate live so the error clears as they fix it |
| On submit | Validate all, focus the first invalid field, summarize at top if more than 2 |
| Server rejects | Map errors back to specific fields; never only a banner |
Errors appear next to the field, not only in a summary. A summary at the top is an addition for long forms, never a replacement.
| 时机 | 操作 |
|---|---|
| 输入中,字段从未失焦 | 无操作。在用户输入邮箱的第3个字符时就进行验证是不友好的。 |
| 字段失焦,且非空 | 验证该字段 |
| 输入中,已显示错误后 | 实时重新验证,让错误在用户修复时自动清除 |
| 提交时 | 验证所有字段,聚焦第一个无效字段,若超过2个错误则在顶部汇总 |
| 服务器拒绝 | 将错误映射到具体字段;绝对不要只显示顶部横幅 |
错误提示应出现在字段旁边,而不仅仅是汇总。顶部汇总仅作为长表单的补充,绝不能替代字段旁的提示。
Input Types
输入类型
Getting these right is most of mobile form UX — the correct keyboard appears automatically.
html
<input type="email" inputmode="email" autocomplete="email">
<input type="tel" inputmode="tel" autocomplete="tel">
<input type="text" inputmode="numeric" autocomplete="one-time-code" pattern="[0-9]*">
<input type="password" autocomplete="current-password">
<input type="password" autocomplete="new-password">
<input type="text" autocomplete="street-address">Use rather than for codes, PINs, and card numbers — adds spinners, allows and , and silently drops leading zeros.
inputmode="numeric"type="number"type="number"e-Never block paste. Blocking paste on password or confirmation fields breaks password managers and helps no one.
正确设置这些是移动端表单UX的关键——会自动弹出对应的键盘。
html
<input type="email" inputmode="email" autocomplete="email">
<input type="tel" inputmode="tel" autocomplete="tel">
<input type="text" inputmode="numeric" autocomplete="one-time-code" pattern="[0-9]*">
<input type="password" autocomplete="current-password">
<input type="password" autocomplete="new-password">
<input type="text" autocomplete="street-address">对于验证码、PIN码和卡号,使用而非——会添加 spinner、允许输入和,还会自动丢弃前导零。
inputmode="numeric"type="number"type="number"e-**绝对不要阻止粘贴。**在密码或确认字段阻止粘贴会破坏密码管理器,对任何人都没有好处。
Data Handling
数据处理
- Never clear the form on error. Losing typed data is the single most infuriating form behavior.
- Preserve input across navigation where the form is long — sessionStorage or server draft.
- Be liberal in what you accept. Strip spaces from card numbers and phone numbers rather than rejecting them. Accept and normalize it yourself.
+1 (555) 123-4567 - Disable submit only while submitting, never as a validation gate. A permanently disabled button with no explanation gives the user nothing to act on.
- **错误时绝不清空表单。**丢失已输入的数据是最让用户愤怒的表单行为。
- 长表单需保留输入内容——使用sessionStorage或服务器草稿功能。
- **兼容多样输入格式。**自动去除卡号和手机号中的空格,而不是拒绝。接受并自行标准化。
+1 (555) 123-4567 - 仅在提交时禁用提交按钮,绝不作为验证门槛。永久禁用且无说明的按钮会让用户无从下手。
Step 4: Copy
步骤4:文案设计
Interface copy is UX, not decoration. It is often the entire difference between a usable and an unusable state.
界面文案是UX的一部分,而非装饰。它往往是可用与不可用状态之间的唯一区别。
Buttons
按钮文案
Name the action, not the abstraction.
| Weak | Strong |
|---|---|
| Submit | Send invitation |
| OK | Delete 3 files |
| Yes / No | Discard changes / Keep editing |
| Continue | Review order |
A confirmation dialog's buttons must be readable without the dialog text. "OK" and "Cancel" on a delete confirmation force the user to re-read the prompt to know which is destructive.
命名具体操作,而非抽象概念。
| 不佳 | 优秀 |
|---|---|
| Submit | 发送邀请 |
| OK | 删除3个文件 |
| Yes / No | 放弃更改 / 继续编辑 |
| Continue | 查看订单 |
确认对话框的按钮必须脱离对话框文本也能读懂。删除确认对话框上的“确定”和“取消”会迫使用户重新阅读提示才能判断哪个是破坏性操作。
Errors
错误文案
Three parts: what happened, why, what to do. Never expose a stack trace or a raw status code to a user.
| Bad | Good |
|---|---|
| "Error: invalid input" | "Enter a date after today — this event has already passed." |
| "Something went wrong" | "Couldn't save. Your connection dropped — we kept your changes. [Retry]" |
| "Error 403" | "You don't have access to this project. Ask its owner, Dana Reyes, for access." |
| "Invalid email" | "Emails need an @ — did you mean dana@example.com?" |
Never blame the user. "You entered an invalid date" → "That date has already passed."
包含三部分:发生了什么、原因、解决方案。绝对不要向用户暴露堆栈跟踪或原始状态码。
| 糟糕 | 优秀 |
|---|---|
| "Error: invalid input" | "请输入今日之后的日期——该活动已结束。" |
| "Something went wrong" | "保存失败。你的连接已中断——我们已保留你的更改。[重试]" |
| "Error 403" | "你无权访问此项目,请联系项目所有者Dana Reyes申请权限。" |
| "Invalid email" | "邮箱需包含@符号——你是不是想输入dana@example.com?" |
绝不指责用户。“你输入了无效日期” → “该日期已过期。”
Voice
语气
- Second person, active voice, present tense.
- Short. Cut every sentence that does not change what the user does.
- No exclamation marks in error states.
- No jargon the user did not introduce — "authentication token expired" → "you've been signed out."
- Sentence case for buttons and headings; Title Case reads as shouting in UI.
- 使用第二人称、主动语态、现在时态。
- 简洁。删除所有不影响用户操作的句子。
- 错误状态中不要使用感叹号。
- 不要使用用户未提及的术语——“authentication token expired” → “你已退出登录。”
- 按钮和标题使用句子大小写;标题大小写在UI中会显得像在大喊大叫。
Step 5: Feedback and Latency
步骤5:反馈与延迟处理
Every user action needs acknowledgment within 100ms, even if the result takes longer.
| Elapsed | Requirement |
|---|---|
| 0-100ms | Feels instant. Show nothing extra. A flashing spinner is worse than none. |
| 100ms-1s | Immediate local feedback — button enters a pressed/busy state |
| 1-10s | Skeleton or determinate progress. Keep the layout stable. |
| 10s+ | Real progress, an estimate, and a cancel control |
Optimistic updates for actions that nearly always succeed (like, star, reorder): apply the change immediately, reconcile on response, and revert with a clear message on failure. Do not use them for anything with financial or destructive consequences.
Never move content under a pointer. Content that shifts after load causes mis-clicks. Reserve space for anything that will arrive: images with /, ad slots, async badges.
widthheight每个用户操作都需要在100ms内得到确认,即使结果需要更长时间。
| 耗时 | 要求 |
|---|---|
| 0-100ms | 感觉即时。无需额外显示内容。闪烁的加载指示器比没有更糟。 |
| 100ms-1s | 即时本地反馈——按钮进入按下/忙碌状态 |
| 1-10s | 显示骨架屏或确定进度条。保持布局稳定。 |
| 10s+ | 显示真实进度、预估时间,以及取消控件 |
乐观更新适用于几乎总能成功的操作(如收藏、排序):立即应用更改,在响应后调和数据,失败时清晰提示并恢复。不要将其用于涉及财务或破坏性后果的操作。
**绝对不要在指针下方移动内容。**加载后内容移位会导致误点击。为所有将加载的内容预留空间:设置/的图片、广告位、异步徽章。
widthheightStep 6: Destructive and Irreversible Actions
步骤6:破坏性与不可逆操作
Prefer undo over confirm. A confirmation dialog interrupts every user to protect against a rare mistake; undo protects against the mistake without interrupting anyone.
Do: [Delete] → item removed → toast: "Message deleted. [Undo]" (8-10s)
Instead of: [Delete] → "Are you sure?" → [Yes] [No]Confirm when undo is genuinely impossible — sending an email, charging a card, permanently destroying data, anything affecting other people.
A real confirmation dialog:
- States the specific consequence with the count and name: "Delete 47 records from Q3 Archive? This cannot be undone."
- Labels the buttons with the actions, never Yes/No.
- Puts the destructive action in a destructive color and distinct wording.
- Requires typing the resource name only when the action is catastrophic and irreversible. Reserve that friction; overusing it trains users to type past it.
- Focuses the safe option by default.
Never use in production UI — it is unstyleable, blocks the main thread, and reads poorly to screen readers.
window.confirm()**优先选择撤销而非确认。**确认对话框会打断所有用户以防范罕见错误;撤销则能在不打断用户的情况下防范错误。
推荐: [删除] → 项目已移除 → 提示框:"消息已删除。[撤销]" (8-10秒)
而非: [删除] → "确定要删除吗?" → [是] [否]当撤销确实不可能时使用确认——发送邮件、扣款、永久删除数据、任何影响他人的操作。
标准确认对话框:
- 说明具体后果,包含数量和名称:“确定要删除Q3归档中的47条记录?此操作不可撤销。”
- 按钮命名为具体操作,绝不使用是/否。
- 破坏性操作使用破坏性颜色并搭配明确措辞。
- 仅在操作具有灾难性且不可逆时才要求输入资源名称。保留这种摩擦;过度使用会让用户养成无视输入的习惯。
- 默认聚焦安全选项。
绝不在生产UI中使用——它无法自定义样式、会阻塞主线程,且屏幕阅读器可读性差。
window.confirm()Step 7: Navigation and Information Architecture
步骤7:导航与信息架构
- The user must always know where they are. Current section indicated in nav; page title matching the destination they clicked.
- Breadcrumbs for hierarchies deeper than two levels.
- Back must work. Modals, filters, tabs, and steps should be URL-addressable so back and refresh behave. A filtered view that resets on refresh is broken.
- Deep links must survive auth. Land on a login, then return to the originally requested resource — not the dashboard.
- Flat beats deep. Seven items at one level are easier than three levels of three. Add a level only when a category genuinely has its own landing content.
- Search is a feature, not a fallback. If the answer to "how do users find X" is "search", the IA needs work — but ship search anyway.
- **用户必须始终知道自己所处位置。**导航中标记当前章节;页面标题与用户点击的目标一致。
- 层级超过两级时使用面包屑。
- **返回按钮必须有效。**模态框、筛选器、标签页和步骤应支持URL寻址,以便返回和刷新行为正常。刷新后筛选视图重置的设计是失效的。
- **深度链接需支持认证跳转。**先进入登录页,然后返回原本请求的资源——而非仪表盘。
- **扁平结构优于深层结构。**一级7个项目比三级各3个项目更容易使用。仅当某个类别确实有自己的落地内容时才增加层级。
- **搜索是功能,而非备选方案。**如果“用户如何找到X”的答案是“搜索”,说明信息架构需要优化——但仍要提供搜索功能。
Step 8: Touch and Small Viewports
步骤8:触控与小视口适配
| Rule | Value |
|---|---|
| Minimum touch target | 44×44 CSS px (WCAG 2.2 SC 2.5.8) |
| Spacing between targets | 8px minimum |
| Primary actions | Bottom half of the screen — thumb reach |
| Destructive actions | Away from primary actions, never adjacent |
| Hover-only affordances | Forbidden. Touch has no hover. |
| Fixed bottom bars | Account for the on-screen keyboard and safe areas |
css
.bottom-bar { padding-bottom: max(var(--space-s), env(safe-area-inset-bottom)); }
body { min-height: 100dvh; } /* not 100vh — browser chrome makes it overflow */Every gesture needs a visible equivalent. Swipe-to-delete is fine as an accelerator; it cannot be the only way to delete.
| 规则 | 要求 |
|---|---|
| 最小触控目标 | 44×44 CSS像素(WCAG 2.2 SC 2.5.8) |
| 目标间距 | 最小8像素 |
| 主要操作 | 屏幕下半部分——拇指可触及范围 |
| 破坏性操作 | 远离主要操作,绝不相邻 |
| 仅悬停可见的交互元素 | 禁止使用。触控没有悬停。 |
| 固定底部栏 | 适配屏幕键盘和安全区域 |
css
.bottom-bar { padding-bottom: max(var(--space-s), env(safe-area-inset-bottom)); }
body { min-height: 100dvh; } /* 不要用100vh——浏览器控件会导致溢出 */每个手势都需要可见的替代操作。滑动删除作为快捷方式没问题,但不能是唯一的删除方式。
Self-Review
自我检查
- Every data component has all four empty states distinguished
- Loading never blanks already-visible content
- Every error says what to do next, and has a control to do it
- No error text exposes a status code or stack trace
- Forms never clear on error
- Validation fires on blur, not on keystroke
- Every input has the right ,
type, andinputmodeautocomplete - Button labels name their action and are readable out of context
- Destructive actions have undo, or a confirmation naming the specific consequence
- Filters, tabs, and modals are reflected in the URL
- Touch targets ≥ 44px, primary actions in thumb reach
- Nothing is reachable by hover alone
- Walked the whole flow with no data, no permission, and a throttled connection
- 每个数据组件都区分了四种空状态
- 加载时绝不清空已可见内容
- 每个错误提示都说明了下一步操作,并提供对应控件
- 错误文本未暴露状态码或堆栈跟踪
- 表单错误时绝不清空内容
- 验证在失焦时触发,而非按键时
- 每个输入框都设置了正确的、
type和inputmodeautocomplete - 按钮标签命名了具体操作,脱离上下文也能读懂
- 破坏性操作支持撤销,或有明确说明具体后果的确认对话框
- 筛选器、标签页和模态框的状态反映在URL中
- 触控目标≥44px,主要操作位于拇指可触及范围
- 没有仅通过悬停才能触发的交互元素
- 在无数据、无权限、限速网络环境下走查了完整流程
Common Mistakes
常见错误
The happy path is the only path. Everything renders beautifully with three seeded items and collapses with zero or ten thousand.
"No data" as an empty state. It tells the user nothing and offers no action. Empty states are the highest-leverage onboarding surface in the product.
Spinner replacing the whole page on refresh. The user had content; now they have nothing. Keep it and indicate the update.
Disabled submit with no explanation. The user cannot tell what is missing. Enable it and validate on click, or state what is incomplete.
Confirmation dialogs everywhere. Confirming everything trains users to dismiss without reading, which is worse than no confirmation. Undo where possible.
Error toasts that auto-dismiss. A 4-second toast carrying the only description of a failure is a message the user will miss. Errors persist until dismissed; only successes auto-dismiss.
Modal-in-modal. Almost always a sign the flow needs a page, not a layer.
Icon-only buttons without labels. Recognizable icons are a short list: close, search, menu, plus. Everything else needs a visible label or, at minimum, a tooltip plus an accessible name.
**只设计了愉快路径。**在有三条预设数据时渲染完美,但在零条或一万条数据时崩溃。
**空状态仅显示“无数据”。**这对用户毫无意义,也不提供任何操作选项。空状态是产品中最高效的引导入口之一。
**刷新时用加载器替换整个页面。**用户原本有内容,现在什么都没有了。保留原有内容并显示更新提示。
**禁用提交按钮且无说明。**用户无法知道缺少什么。要么启用按钮并在点击时验证,要么说明未完成的内容。
**到处使用确认对话框。**事事确认会让用户养成无视提示的习惯,比没有确认更糟。尽可能使用撤销。
**错误提示框自动消失。**仅用4秒提示框展示故障信息,用户很可能错过。错误提示需保留至用户手动关闭;仅成功提示可自动消失。
**模态框嵌套模态框。**几乎总是说明流程需要页面,而非层级弹窗。
**仅图标无标签的按钮。**可识别的图标屈指可数:关闭、搜索、菜单、加号。其他所有按钮都需要可见标签,或至少有工具提示及无障碍名称。