KAIROS.WORKSPACE
技术宇宙 / ONLINE
返回文章列表
前端工程advanced9 分钟

一个可访问的命令面板:从快捷键到焦点管理的全清单

实现 Ctrl/Cmd+K 命令面板时的键盘协议、combobox 语义、焦点陷阱与恢复、移动端适配和加载态处理的完整检查清单。

#可访问性#键盘导航#ARIA#交互设计
排版
字号
行宽
目录 · 9 节

命令面板是“高级感”功能里最容易做出形、丢出神的:视觉上一眼像模像样,键盘用户却在三米外迷路。本文把验收一个命令面板需要过的问题列成清单,每一条都对应真实实现过的代码。

1. 唤起与关闭

  • 快捷键:Ctrl/Cmd + K 全局监听;在 input/textarea/[contenteditable] 聚焦时不劫持其它单键快捷键。
  • 关闭途径必须至少三条:Esc、遮罩点击、显式关闭按钮。面板内的按钮不要用 tabindex="-1" 把自己藏起来。
  • 关闭时清理:移除焦点圈监听、恢复 body 滚动锁、移除临时 toast。

2. 语义:dialog + combobox 两层结构

外层面板是模态:

<div
  role="dialog"
  aria-modal="true"
  aria-labelledby="palette-title"
  hidden
></div>

内层输入框是组合框(combobox),结果列表是 listbox,两者用 aria-controls / aria-activedescendant 关联:

<input
  role="combobox"
  aria-expanded="true"
  aria-controls="palette-results"
  aria-activedescendant="option-3"
/>
<div id="palette-results" role="listbox">
  <a id="option-3" role="option" aria-selected="true">…</a>
</div>

关键决策:选项用 aria-activedescendant 而不是让焦点真的落进列表。焦点始终留在输入框,上下键只移动“激活”状态。这样输入法组合、退格编辑都不会丢,也是 ARIA 作者工具箱里 listbox 模式的正统做法。

3. 焦点:去哪了,怎么回来

打开面板:

  1. 记录 document.activeElement 作为恢复锚点;
  2. 焦点进入输入框(focus({ preventScroll: true }),避免弹层刚出现就滚动页面);
  3. 焦点陷阱:监听 Tab,到达最后一个可聚焦元素后回卷到第一个(Shift+Tab 反向)。aria-modal="true" 只是给读屏器的声明,不能阻止 Tab 走出弹层,陷阱必须自己写。

关闭面板:焦点还给锚点。跳过这一步,键盘用户会被扔回页面顶部。

4. 键盘协议全表

行为
↑ / ↓ 移动激活项,scrollIntoView({ block: 'nearest' }) 保证可见
Enter 激活当前项(链接跳转或执行动作命令)
Esc 关闭并恢复焦点
Tab 在面板内圈闭移动(访问关闭按钮等)

列表为空时上下键应当无操作而不是报错;只有一个结果时 Enter 直接命中,省一次按键。

5. 加载态与空状态是设计的一部分

索引异步加载时,面板不能装作“无结果”。三态文案:

  • 索引未到:正在加载站内索引…(可先渲染无需索引的导航推荐项,索引到达后增量合并);
  • 有输入无匹配:没有找到匹配内容。试试更短的关键词,或浏览推荐入口。
  • 无输入:展示推荐入口 + 最近公开内容。

面板打开的视觉就绪不应等待任何 fetch——先聚焦、先渲染骨架,网络只是后续的数据补全。

6. 跳转不要整页刷新

面板内导航若用 window.location.href =,会把站点的 View Transitions 客户端路由整个旁路掉:整页重载、过渡动画丢失、SPA 状态清零。同域跳转改为程序化锚点点击,让路由拦截器接管:

const link = document.createElement('a');
link.href = target;
document.body.append(link);
link.click();
link.remove();

路由不可用时浏览器自动回退为普通导航,零额外成本。

7. 移动端与 reduced-motion

  • 宽度断点:≤640px 时面板改为全宽贴顶,内边距收紧,选项网格从三列降为一列;
  • 触屏用户没有 Esc:遮罩点击与关闭按钮权重上升,关闭区触控高度不低于 44px;
  • prefers-reduced-motion: reduce:关闭选项 hover 位移与面板缩放入场,保留边框高亮等静态可辨状态。

8. 验收脚本思路

把上表变成可回归的测试:Playwright 下 Control+k → 断言 document.activeElement 为输入框 → 连按 12 次 Tab → 断言焦点仍在 #command-palette 内 → 输入关键词 Enter → 断言 URL 变化且 window 上的标记变量存活(证明 SPA 导航未重载)。比人工点击可靠,也比快照测试更能抓住“焦点逃逸”这类逻辑缺陷。

小结

命令面板的“高级感”来自毫秒级的响应与丝滑动画,而它的专业底线是那几条看不见的规矩:焦点进得来、出不去、还得回来;空与加载各有各的话;跳转不破坏路由。先把规矩跑通,再谈惊艳。

Conversation

评论与互动

正在加载评论…

提交后需审核,不会立即公开。

Keep exploring

继续探索