当 AI 工具正在生成内容、处理数据集或运行自主任务时,用户需要一个清晰的退出方式。太多的界面将“紧急停止”视为事后才考虑的事。他们只是把按钮标签从“停止”改为“已停止”,就认为任务完成了。颜色可能变灰了,动画可能看起来很平滑,但任务仍在服务器上继续运行,而用户对此一无所知。对于依赖屏幕阅读器的人来说,这种失败更为严重。他们听到音频确认进程已结束,而工作却在后台默默地继续。这不仅仅是一个小 bug,这是信任的崩塌。
静默停止按钮的谎言
一个糟糕的停止按钮在欺骗你的用户。它显示“已停止”字样,而任务却仍在容器或远程工作节点中继续执行。这种情况之所以发生,是因为前端开发人员通常会在服务器确认停止之前,就“乐观地”更新界面。视觉用户如果看到进度条仍在移动或日志仍在滚动,可能会发现这种不匹配,但屏幕阅读器用户没有这样的辅助渠道。他们完全依赖界面所播报的内容。如果按钮文本过早更改,且没有音频反馈来澄清真实状态,用户会误以为紧急情况已经结束,而事实并非如此。在这里,无障碍性(Accessibility)不是一个功能请求,而是一项安全要求。
两种不同的状态
一个真正的紧急控制必须处理两个截然不同的职责。首先,系统接受你的请求。其次,系统撤销权限。这两者并非同一回事。“接受”意味着前端听到了你的指令并传递了消息。“撤销”意味着后端确实终止了进程。由于网络延迟、任务队列和编排层的存在,这两个时刻之间可能会有几秒钟的间隔。在这段窗口期内,你的界面必须如实告知用户当前处于哪个阶段。将这两个阶段合并为一个瞬间,是假设了并不存在的底层架构。你的用户将为这种乐观主义付出代价。
将四种状态映射到你的界面
围绕四种明确的状态构建你的 UI,以便用户始终清楚自己的处境。
- 运行中 (Running): 显示一个标签清晰的“停止任务”按钮。始终保持其可见,不要将其隐藏在标签页或折叠面板下。
- 请求中 (Requesting): 禁用按钮,防止用户连续多次发送请求。显示“已请求停止”的消息。这种诚实至关重要,它告诉用户其指令正在传输中,系统尚未确认完成。
- 已停止 (Stopped): 禁用按钮。显示一个回执 ID。这为用户提供了服务器已响应且停止操作已记录的证明。它将一个声明变成了一条记录。
- 失败 (Failed): 启用“再次尝试停止”按钮。显示具体的失败信息。永远不要让用户处于沉默的迷茫中。如果服务器超时或返回错误,请明确说明。
这些状态应当驱动视觉和听觉反馈。当状态发生变化时,屏幕阅读器必须通过正确管理的实时区域(live region)播报新的标签和状态。禁用按钮结合文本播报,可以防止用户对控制是否仍然有效产生困惑。
经得起压力考验的设计规则
紧急控制的设计负担与普通按钮不同。用户在压力下可能会感到焦虑、匆忙,或者正在应对意外的输出。你的界面必须在这种压力下保持可用。
不要仅使用颜色作为信号。 按钮从红色变为绿色可以帮助部分视觉用户,但色盲用户和屏幕阅读器用户需要文本和结构上的变化。请将颜色与明确的标签、图标与文本替代方案以及状态播报结合使用。
不要将控件隐藏在悬停菜单中。 在紧急情况下,不应该让任何人去下拉菜单中搜寻。停止按钮应当位于主视口内,始终可以触达,无需精确的鼠标轨迹操作。
让按钮易于通过指针点击。 压力会降低精细动作控制能力。请使用宽大的内边距(padding)和较大的点击目标区域。如果用户正在发抖,或者在行驶的火车上使用触控板,他们仍应能够成功点击。
确保键盘用户可以快速触达按钮。 Tab 键的焦点顺序不应强迫用户在触达紧急控制之前,先循环经过三十个可聚焦元素。考虑使用跳过链接(skip link)或逻辑焦点布局,使停止操作处于触手可及的位置。
避免误触键盘快捷键。 用于停止进程的全局快捷键应使用难以误触发的组合键。如果常用的保存或打印快捷键与你的停止命令冲突,用户可能会意外触发并导致工作丢失。
不要在紧急情况下使用多步确认。 确认对话框是一堵墙,而不是安全护栏。当用户读完“你确定吗?”并再次点击时,不想要的结果可能已经输出了。一次果断的操作就足够了。
回执、网络丢失与诚实的局限性
回执 ID 只能证明服务器已响应,并不能证明所有的下游影响都已撤销。在停止命令到达时,你的 AI 任务可能已经触发了外部 API、文件写入或消息队列。停止编排器(orchestrator)并不保证每个子进程都会立即中止。请在你的消息提示和文档中诚实地说明这一局限性。
你还需要针对服务器机房之外的故障模式进行设计。测试用户在点击停止后立即丢失网络连接时会发生什么。测试响应时间从 100 毫秒变为 10 秒时会发生什么。如果请求挂起,你的界面应该超时并进入失败状态,而不是永远卡在“请求中(Requesting)”。用户有权知道连接何时已中断。
如何进行至关重要的测试
验证不能是事后才考虑的事情。让你的界面经历残障用户日常遇到的真实情况。
仅限键盘导航。 拔掉鼠标。通过 Tab 键遍历每一个状态。确保你可以从工作流中的任何地方触达停止按钮,而不会导致焦点被困住或产生不可见的 Tab 停止点。
200% 浏览器缩放。 放大页面。检查停止按钮是否会重新排列或消失。视力受损的用户依赖缩放,而布局崩溃往往会隐藏关键控件。
减弱动态效果设置。 你的“请求中(Requesting)”状态可能会使用脉冲动画或旋转加载器。请尊重 prefers-reduced-motion 设置。在任何动态效果旁边提供静态视觉指示器,以便禁用动画的用户仍能获得清晰的状态反馈。
屏幕阅读器播报顺序。 使用 live region 来广播状态变化,但要仔细测试其顺序。播报顺序应与事件的逻辑进展相匹配。如果按钮在屏幕阅读器说出“已请求停止”之前就禁用了,请测试这种顺序是否会造成困惑。辅助技术中的微小时序错误可能会导致信息混乱,因此请使用真实的屏幕阅读器进行验证,而不要仅仅假设标记语言本身就足够了。
核心启示
构建一个无障碍的紧急停止功能,意味着要给予用户足够的尊重,并向他们告知真相。界面应当表达直白、动作可预测,并且永远不要假装“请求”等同于“结果”。当压力巨大且数据面临风险时,清晰度节省的不只是时间,更是信任。一个诚实的停止按钮不仅仅是中止一项任务,它首先证明了你的产品在操作上是安全的。
