关于 designing-user-experience
面向界面设计者和开发者的 UX 技能,强调为组件设计空、加载、错误和部分状态,并覆盖流程、表单、反馈、导航及小屏幕体验。它提供检查清单,帮助在实现前发现失败路径和恢复方式。
主要功能
- 01绘制用户流程与分支
- 02枚举数据组件状态
- 03设计表单验证与错误文案
- 04规划反馈时机和恢复路径
- 05检查导航与小屏幕交互
适用场景
- 01为新用户设计空状态
- 02规划搜索无结果与错误恢复
- 03改进多步骤表单验证
- 04检查删除等不可逆操作
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ osmontero/ opencode-skills --skill designing-user-experience安装后在 Claude Code 中输入/designing-user-experience确认已加载。
附带文件references/flow-patterns.md
详细介绍
设计用户体验
概述
视觉设计决定界面在一切顺利时是什么样子。UX 决定其他时候会发生什么,而大多数时候都并不顺利。
核心原则: 设计状态,而不是屏幕。“屏幕”只是组件的一种状态;只交付这一种状态,就是生成式 UI 看起来已经完成、却经不起真实数据考验的原因。
配套技能: designing-frontend-interfaces 用于视觉设计,building-accessible-interfaces 用于无障碍访问,reviewing-interface-quality 用于审查完成的 UI。
铁律
NO COMPONENT IS DONE UNTIL ITS EMPTY, LOADING, ERROR, AND PARTIAL STATES EXIST
一个只有在有项目时才会渲染的列表,不是列表组件,而只是个演示。如果设计没有回答“没有任何数据的新用户会看到什么”,那设计就不完整。
检查清单
为每一项创建一个任务:
- 梳理流程 — 入口 → 步骤 → 成功,以及每个分支
- 补全状态矩阵 — 涉及数据的每个组件都要检查
- 设计失败路径 — 什么会出错、用户会看到什么、如何恢复
- 撰写文案 — 按钮、错误、空状态、确认信息
- 设定反馈时限 — 100 毫秒、1 秒、10 秒时会发生什么
- 处理具有破坏性和不可逆的操作 — 确认、撤销,或两者兼有
- 检查触控和小视口 — 点击目标、触达范围、键盘弹出时的行为
- 以首次使用者的身份走一遍流程 — 没有数据、没有权限、网络缓慢时
第 1 步:梳理流程
设计屏幕之前,先用文字写出流程。这只需两分钟,却能暴露出模型图无法揭示的缺口。
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 queued
分支才是设计工作的重点。 任何分支列表为空的流程,都还没有经过充分思考。
先精简步骤,再美化它们。 每一步都可能让用户放弃。逐一思考:这一步能否设为默认值、推断得出、留待以后,或直接移除?可以推导得出的表单字段就不该询问用户。
常见模式的流程示例——引导入门、搜索与筛选、多步骤向导、撤销、权限和付费墙:references/flow-patterns.md。
第 2 步:状态矩阵
每个显示数据或接受输入的组件都有以下状态。逐一明确列出,不要等到上线后才发现它们。
| 状态 | 触发条件 | 要求 |
|---|---|---|
| 空 — 首次使用 | 新用户,尚未创建任何内容 | 说明该功能并提供主要操作。不要只写“没有数据”。 |
| 空 — 无结果 | 筛选或搜索没有匹配结果 | 回显查询内容,并提供清除筛选条件的选项。此状态不同于首次使用时的空状态。 |
| 空 — 已清空 | 用户已完成所有事项 | 对此予以确认。这是成功状态,而不是缺失状态。 |
| 加载中 — 首次加载 | 没有缓存数据 | 显示与实际布局一致的骨架屏,延迟约 200 毫秒 |
| 加载中 — 刷新 | 已有数据,正在更新 | 保留旧数据可见,并显示不显眼的指示器。绝不要清空屏幕。 |
| 部分加载 | 部分来源加载失败 | 显示已加载的内容,标记未加载的内容,并提供针对该部分的重试选项 |
| 错误 — 可恢复 | 超时、5xx、网络问题 | 说明发生了什么、该怎么做,并提供重试控件 |
| 错误 — 永久性 | 404、已删除、已撤销 | 明确说明情况,并提供最接近的有用目标位置 |
| 错误 — 权限 | 403 | 说明谁有访问权限,以及如何申请 |
| 离线 | 连接中断 | 说明哪些功能仍可用,以及哪些操作已排队 |
| 数据过多 | 10,000 行 | 使用分页或虚拟化,并显示数量 |
| 数据过期 | 缓存超过阈值 | 显示时间戳和刷新控件 |
| 成功 | 操作已完成 | 确认操作,并说明发生了什么变化 |
四种空状态确实各不相同。 把它们统统简化为“找不到项目”,是生成式界面中最常见的 UX 捷径。
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]
第 3 步:表单
大多数 UX 问题都出在表单上,因为用户正是在这里实际输入内容。
结构
- 单列。 多列表单会导致字段被跳过,且键盘导航顺序不明确。例外:确实成对的字段(城市/州、有效期/CVC)。
- 标签放在输入框上方,并始终可见。用占位符充当标签会在聚焦时消失、对比度低时难以辨认,还会影响自动填充和屏幕阅读器。
- 用恰当的间距将相关字段分组(参见
designing-frontend-interfaces中的邻近原则)。 - 标注较少见的情况——如果大多数字段为必填项,就标注选填项。绝不能什么都不标。
- 在用户输入前说明要求,不要等到输入后再报错。“至少 8 个字符,包含一个数字”应从一开始就显示在字段下方。
验证时机
| 何时 | 怎么做 |
|---|---|
| 用户正在输入,且字段从未失焦 | 不做任何操作。在用户输入邮箱的第 3 个字符时就验证,会让人反感。 |
| 字段非空且失焦时 | 验证该字段 |
| 输入过程中,且已显示错误后 | 实时重新验证,以便用户修正后清除错误 |
| 提交时 | 验证所有字段,将焦点移至第一个无效字段;若无效字段超过 2 个,则在顶部汇总 |
| 服务端拒绝时 | 将错误对应到具体字段;绝不能只显示横幅 |
错误应显示在字段旁边,不能只在汇总中显示。对于较长的表单,顶部汇总是补充,绝不能替代字段旁的错误提示。
输入类型
正确设置这些内容,是移动端表单体验的关键所在——这样就会自动显示正确的键盘。
<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 和银行卡号,应使用 inputmode="numeric",而不是 type="number"——type="number" 会添加微调按钮、允许输入 e 和 -,还会悄悄去掉前导零。
绝不要阻止粘贴。 在密码或确认字段中阻止粘贴会破坏密码管理器的功能,对任何人都没有帮助。
数据处理
- 出错时绝不要清空表单。 输入的数据丢失,是表单最令人恼火的行为。
- 表单较长时,应在页面导航过程中保留输入内容——可以使用 sessionStorage 或服务端草稿。
- 尽量宽松地接受输入。 对银行卡号和电话号码去除空格,而不是直接拒绝。接受
+1 (555) 123-4567,并自行将其规范化。 - 仅在提交过程中禁用提交按钮,绝不能将其用作验证门槛。按钮始终处于禁用状态又没有任何说明,会让用户无从操作。
第 4 步:文案
界面文案属于用户体验,而不是装饰。它往往决定了某种状态是否可用。
按钮
写明具体操作,而不是抽象概念。
| 弱 | 强 |
|---|---|
| 提交 | 发送邀请 |
| 确定 | 删除 3 个文件 |
| 是/否 | 放弃更改/继续编辑 |
| 继续 | 查看订单 |
确认对话框中的按钮必须在不看对话框文字的情况下也能看懂。在删除确认框中使用“确定”和“取消”,会迫使用户重新阅读提示,才能判断哪个操作具有破坏性。
错误
包含三个部分:发生了什么、为什么、该怎么做。绝不要向用户显示堆栈跟踪或原始状态码。
| 差 | 好 |
|---|---|
| “错误:输入无效” | “请输入今天之后的日期——此活动已经结束。” |
| “出了点问题” | “无法保存。连接已断开——我们保留了你的更改。[重试]” |
| “错误 403” | “你无权访问此项目。请向项目所有者 Dana Reyes 申请访问权限。” |
| “邮箱无效” | “邮箱中需要包含 @——你想输入的是 [email protected] 吗?” |
绝不要责怪用户。“你输入了无效日期”→“该日期已经过去。”
语气
- 使用第二人称、主动语态和现在时。
- 简短。删掉所有不会影响用户操作的句子。
- 错误状态中不要使用感叹号。
- 不要使用用户未提及的术语——“身份验证令牌已过期”→“你已退出登录。”
- 按钮和标题使用句首字母大写;在界面中,标题式大小写会显得像在大喊。
第 5 步:反馈与延迟
每次用户操作都需要在 100ms 内得到确认,即使结果需要更长时间才能完成。
| 耗时 | 要求 |
|---|---|
| 0-100ms | 感觉即时完成。不要显示额外内容。闪烁的加载指示器还不如不显示。 |
| 100ms-1s | 立即提供本地反馈——按钮进入按下/忙碌状态 |
| 1-10s | 显示骨架屏或确定进度。保持布局稳定。 |
| 10s+ | 显示实际进度、预计时间和取消控件 |
对于几乎总能成功的操作(如点赞、加星、重新排序),使用乐观更新:立即应用更改,在收到响应后进行校准;如果失败,则清晰地提示并还原。对于任何涉及财务或破坏性后果的操作,都不要使用乐观更新。
绝不要让内容在指针下方移动。 加载后发生位移的内容会导致误点击。为即将加载的内容预留空间:图片使用 width/height,以及广告位、异步徽章。
第 6 步:破坏性和不可逆操作
优先使用撤销,而不是确认。 确认对话框会打断每位用户,只为防止罕见的错误;撤销则能防止错误,又不会打断任何人。
Do: [Delete] → item removed → toast: "Message deleted. [Undo]" (8-10s)
Instead of: [Delete] → "Are you sure?" → [Yes] [No]
仅当撤销确实不可能时才要求确认——例如发送电子邮件、扣款、永久销毁数据,以及任何会影响他人的操作。
真正有效的确认对话框:
- 用具体信息说明后果,包括数量和名称:“要删除 Q3 Archive 中的 47 条记录吗?此操作无法撤销。”
- 按钮应标明具体操作,绝不要使用“是/否”。
- 破坏性操作的按钮应使用醒目的破坏性颜色,并且采用明确的措辞。
- 仅当操作后果严重且不可逆时,才要求用户输入资源名称。谨慎使用这种阻碍;过度使用会让用户习惯于不加思考地输入。
- 默认将焦点放在安全选项上。
生产环境的 UI 中绝不要使用 window.confirm()——它无法设置样式、会阻塞主线程,而且屏幕阅读器难以朗读。
第 7 步:导航与信息架构
- 用户必须始终知道自己身处何处。 在导航中标明当前分区;页面标题应与用户点击进入的目标一致。
- 层级超过两层时使用面包屑。
- 返回功能必须有效。 模态框、筛选器、标签页和步骤都应可通过 URL 访问,以确保返回和刷新时行为正常。刷新后筛选视图重置,就说明功能有问题。
- 深层链接在身份验证后也必须有效。 用户应先进入登录页,然后返回最初请求的资源,而不是仪表板。
- 扁平胜过深层。 同一层级的七个项目,比三层、每层三个项目更容易理解。只有当某个类别确实有自己的落地内容时,才增加层级。
- 搜索是一项功能,不是备用方案。 如果“用户如何找到 X”的答案是“搜索”,说明信息架构还需改进——但仍要提供搜索功能。
第 8 步:触控与小视口
| 规则 | 数值 |
|---|---|
| 最小触控目标 | 44×44 CSS px(WCAG 2.2 SC 2.5.8) |
| 目标之间的间距 | 至少 8px |
| 主要操作 | 屏幕下半部分——方便拇指触及 |
| 破坏性操作 | 与主要操作保持距离,绝不相邻 |
| 仅悬停时显示的操作提示 | 禁止。触控设备没有悬停。 |
| 固定底栏 | 需考虑屏幕键盘和安全区域 |
.bottom-bar { padding-bottom: max(var(--space-s), env(safe-area-inset-bottom)); }
body { min-height: 100dvh; } /* not 100vh — browser chrome makes it overflow */
每种手势都必须有可见的替代方式。滑动删除可以作为快捷操作,但不能成为唯一的删除方式。
自我检查
- 每个数据组件都明确区分四种空状态
- 加载时绝不清空已显示的内容
- 每条错误信息都说明下一步该做什么,并提供相应操作控件
- 错误文本不会暴露状态码或堆栈跟踪
- 表单出错时绝不清空内容
- 在失去焦点时触发验证,而不是在按键时触发
- 每个输入框都设置了正确的
type、inputmode和autocomplete - 按钮标签明确说明其操作,且脱离上下文也能看懂
- 破坏性操作可撤销,或会显示确认信息并说明具体后果
- 筛选器、标签页和模态框的状态都反映在 URL 中
- 触控目标 ≥ 44px,主要操作位于拇指可触及范围内
- 任何内容都不能只通过悬停才能访问
- 完整走查整个流程,测试无数据、无权限和网络受限的情况
常见错误
只考虑顺利完成的路径。 使用 3 个预置条目时,一切显示得十分完美;条目数为 0 或 10,000 时,界面却会崩溃。
把“无数据”当作空状态。 这既没有告诉用户任何信息,也没有提供任何操作。空状态是产品中引导新用户效果最显著的界面。
刷新时用加载指示器替换整个页面。 用户原本能看到内容,现在却什么也看不到。保留现有内容,并提示内容正在更新。
禁用提交按钮,却不作解释。 用户不知道还缺少什么。应启用按钮,并在点击时验证;或者说明哪些内容尚未完成。
到处都用确认对话框。 对所有操作都要求确认,会让用户养成不看内容就关闭对话框的习惯,这比不提供确认更糟。可以撤销时,就提供撤销功能。
会自动消失的错误提示。 用 4 秒后消失的提示来传达故障的唯一说明,用户很可能会错过。错误信息应持续显示,直到用户将其关闭;只有成功提示才自动消失。
模态框中再嵌套模态框。 这几乎总是说明该流程需要一个页面,而不是再叠加一层界面。
没有标签的纯图标按钮。 容易识别的图标只有少数几种:关闭、搜索、菜单和加号。其他图标都需要配上可见标签;至少也要提供工具提示和无障碍名称。