把模态弹窗写成从打开到焦点返回的完整流程

使用 AI 辅助编写与复核。

本文目录

模态弹窗请求应说明:打开对话框时键盘焦点去哪里、用户怎样在内部移动,以及关闭后返回哪里。还要覆盖原触发控件已经消失的情况。一张遮罩层截图,无法单独回答这些问题。

下面的虚构 Rowan Board 示例是一个只读条目详情对话框。它不删除条目、不提交表单,也不打开另一个对话框。这份完整交互约定可以直接交给 Agent,无需先选 UI 框架。

先画出对象,再描述焦点移动

Rowan Board 最初显示两个条目:第一个是 C7,标题为“Inspect build output”;第二个是 C8,标题为“Check release wording”。每项都有 Details 按钮。打开 C7 后,即使周围列表发生变化,也必须继续显示 C7 的详情。

页面
└─ board-region
   ├─ board-title: "Review board"(稳定标题;可通过程序获得焦点)
   ├─ item-C7: "Inspect build output"
   │  └─ details-C7: "Details for Inspect build output" 按钮
   └─ item-C8: "Check release wording"
      └─ details-C8: "Details for Check release wording" 按钮

独立模态层,位于背景区域之外
└─ item-dialog: 由 details-title 命名的 dialog
   ├─ details-title: "Inspect build output — details"(可获得焦点的标题)
   ├─ details-body: 下方两段正文
   ├─ show-files: "Show file list" 切换按钮
   ├─ file-list: 初始隐藏的静态文字,由 show-files 控制
   └─ close-details: "Close details" 按钮

完整虚构内容如下,保留英文示例正文并附中文释义:

This item collects the build output that a reviewer needs before reading the proposed release wording. Keep the original output available beside any summary. 中文释义:此条目汇集复核者在阅读拟议发布措辞前所需的构建输出。无论是否有摘要,都应把原始输出保留在旁边,供人查阅。

The next step is to inspect the listed files and record a review outcome in the board. Opening these details does not mark the item reviewed. 中文释义:下一步是检查列出的文件,并在看板中记录复核结果。打开这些详情,不会把条目标记为已复核。

文件列表包含 build-summary.txt 和 release-notes-draft.md。这些是示例名称,只显示为文字,不打开或下载。切换按钮只改变列表是否可见。在这次打开期间,对话框标题、正文和身份始终属于 C7。

选择初始阅读位置

W3C 的模态对话框模式介绍了焦点进入对话框、留在其 Tab 序列内,并在关闭后回到合适位置的行为。它也讨论了当内容适合结构化阅读时,先让一个静态起始元素获得焦点的做法。

在 Rowan Board 中,选择 details-title 作为初始焦点目标,并设置 tabindex="-1"。这样,阅读起点位于两段正文之前。标题可以通过程序获得焦点,但不会在普通 Tab 序列里多占一个停靠点。

两个普通 Tab 停靠点依次是 show-files 和 close-details。对话框打开期间,两者始终存在且可用。展开文件列表不引入链接或额外控件,因此不会悄悄改变这个序列。

这些是为本对话框作出的选择。用途不同的简短确认框,可能需要另一个初始目标。

给每次键盘移动指定去向

使用下面这份完整移动表,而不是只要求“把焦点困在弹窗内”。

起始位置 操作 去向或结果
details-C7 激活 Details 打开 C7 对话框;焦点到 details-title
details-title Tab 焦点到 show-files
details-title Shift+Tab 焦点到 close-details
show-files Tab 焦点到 close-details
close-details Tab 循环回 show-files
close-details Shift+Tab 焦点到 show-files
show-files Shift+Tab 循环回 close-details
show-files 激活 切换文件列表;焦点留在 show-files
对话框内任一当前焦点 Escape 关闭一次,再应用下方返回规则
close-details 激活 关闭一次,应用相同返回规则

对话框处于活动状态时,看板不可接受指针或键盘交互。本设计中,点击背景遮罩不关闭对话框;关闭路径是可见的关闭按钮和 Escape。背景变暗用来表达模态状态,实际的交互处理负责落实限制。

对话框的无障碍名称与可见标题关联。控件都位于对话框容器内。两段描述正文保留各自结构;这份方案不把它们压成一段很长的容器描述。ARIA 命名和模态属性描述预期语义,实施仍需提供真正的焦点和背景交互行为。

用一个简明决定定义返回目标

正常关闭时,如果打开本次对话框的那个 Details 按钮仍存在且可用,就把焦点还给它。不要查找“第一个 Details 按钮”,因为那时它可能已经属于 C8。

关闭时的状态 返回目标 为什么选择它
原 details-C7 仍然可见且启用 details-C7 用户可从刚才打开的条目继续
原触发控件被移除、隐藏或禁用 board-title 稳定标题可以标识所在工作区域

先恢复背景交互,再把焦点移回背景。如果实施过程中临时改变了可获得焦点的状态或背景状态,应恢复它们之前的状态,而不是把页面上的所有内容一律启用。关闭本身不应改变哪些看板条目已经被复核。

本约定假设看板页面和 board-title 仍然挂载。销毁整个页面的导航会结束这段对话框流程;路由和下一页的焦点规则属于另一项工作。

走一遍触发控件消失的情况

以下历史是虚构的。它故意通过本地数据更新测试材料,在对话框仍打开时移除 C7。用户并不是通过操作不可交互的背景来造成这次变化。

  1. C7 和 C8 可见。用户通过 details-C7 打开 C7。
  2. 焦点移到 details-title;对话框仍描述 C7。
  3. 用户按 Tab 到 show-files 并激活它。两个文件名出现,焦点仍在该按钮。
  4. 一次预先设定的本地数据模型更新将 C7 标为已复核,当前“未复核”筛选因此从背景中移除该行。C8 和 board-title 仍保留。这份教学测试材料不涉及服务器事件或真实账户。
  5. 用户按 Tab 到 close-details 并激活它。
  6. 对话框关闭,背景交互恢复,焦点移到 board-title。它不跳到 C8 的 Details 按钮,也不留在已经移除的节点上。
  7. 用户下次打开某个条目的详情时,文件列表重新从折叠状态开始。之前的展开状态不泄漏到新一次打开中。

W3C 的焦点返回指引允许在调用元素已不存在时,选择合乎工作流逻辑的目标。本例选择看板标题,把这个原则具体化;它不要求每个应用都把焦点还给标题。

复制这份有范围的请求

为虚构 Rowan Board 实现一个只读条目详情模态框。
使用给定对象地图、C7/C8 内容和移动表。
不要增加嵌套对话框、编辑、复核提交或文件下载。

首个人工检查目标:macOS 26 上的 Safari 26,启用键盘导航;
同时使用该目标的 VoiceOver 设置检查对话框。
这些是选定目标,不是作者电脑的环境清单。
报告结果前,记录精确构建版本和设置。

打开时,用可见标题为对话框命名,填入选定条目的内容,
折叠其静态文件列表,并让标题获得焦点(tabindex=-1)。
两个 Tab 停靠点依次是 Show file list、Close details。
实现表中每个 Tab 和 Shift+Tab 转移,包括初始标题、
第一个控件和最后一个控件的边界。

整个模态生命周期内,背景内容不可接受键盘或指针交互。
Show files 只切换静态列表,报告展开状态,并保留焦点。
点击背景遮罩不关闭。Escape 和 Close details 使用同一套、只关闭一次的过程。

恢复背景交互后,如果保存的原触发控件仍连接在文档中、可见且启用,
则返回它。否则,让仍挂载的 board-title 标题获得焦点。
不要改用另一行的按钮。只恢复本模态框实际改变的背景状态。

走查正常返回,以及完整的 C7 移除历史。
在本次打开期间保留该条目的内容快照。
不要从这项修改推断全站无障碍达标或认证。
返回实施结果,并把实际结果放在预期流程旁边。

这是一份完整交互提案,不是测试过的 HTML 组件。实施者可以采用合适的原生平台行为或已有应用设施,但可观察的焦点去向应保持一致。

分开走查循环与退出

下表的实际观察栏有意留空;作者尚未在浏览器或辅助技术会话中运行这个对话框。

走查项 预期结果 实际观察 状态
打开 C7 C7 标题获得焦点;名称对应正确内容 NOT_RUN
从标题按 Tab Show file list 获得焦点 NOT_RUN
从标题按 Shift+Tab Close details 获得焦点 NOT_RUN
在两个按钮之间正向循环 最后一个回到第一个;背景始终不获得焦点 NOT_RUN
在两个按钮之间反向循环 第一个回到最后一个 NOT_RUN
切换文件列表 可见性和展开状态正确;焦点保留 NOT_RUN
尝试背景交互 模态打开时,看板不被激活 NOT_RUN
原触发控件存在时按 Escape 关闭一次;回到同一触发控件 NOT_RUN
原触发控件存在时用关闭按钮 与 Escape 相同的返回目标 NOT_RUN
打开期间 C7 被移除 内容仍是 C7;关闭后回到看板标题 NOT_RUN
再次打开 显示正确的新条目;文件列表初始折叠 NOT_RUN

即使对话框看起来正确,返回目标放错也会让流程显得中断。先写清退出去向,Agent 和复核者就都有一个明确的任务终点。