关于 accessibility
用于开发 VS Code 交互界面的无障碍技能,为贡献者提供键盘操作、屏幕阅读器支持、无障碍视图和动态播报的要求与实现指引。
主要功能
- 01添加无障碍帮助对话框
- 02以纯文本呈现视觉内容
- 03配置无障碍提示设置
- 04提供声音与语音信号
- 05使用 ARIA 警报和状态消息
- 06支持键盘导航
适用场景
- 01构建新的 VS Code 面板或控件
- 02改进现有交互的无障碍支持
- 03让屏幕阅读器读取视觉内容
- 04无障碍播报错误和进度
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ microsoft/ vscode --skill accessibility安装后在 Claude Code 中输入/accessibility确认已加载。
详细介绍
何时使用此技能
对于任何引入或更改交互式 UI 的 VS Code 功能开发,都应使用此技能。新功能和贡献默认使用此技能,即使请求未明确提及无障碍性。
触发示例: -“添加新功能” -“实现新的面板/视图/小组件” -“添加新命令或工作流” -“在 workbench/editor/extensions 中添加新贡献” -“更新现有 UI 交互”
不要仅仅因为提示中没有提及无障碍性就跳过此技能。
在 VS Code 中添加新的交互式 UI 界面时——例如面板、视图、小组件、编辑器叠加层、对话框,或任何用户可以交互且可聚焦的丰富组件——如果该功能尚未提供以下无障碍组件,则必须添加:
- 无障碍帮助对话框——当功能获得焦点时,可通过无障碍帮助快捷键打开。
- 无障碍视图——以纯文本只读编辑器的形式呈现功能内容,供屏幕阅读器用户使用(当功能显示非简单的视觉内容时)。
- 无障碍详细程度设置——布尔设置,用于控制是否播报“打开无障碍帮助”提示。
具备全部三项的现有功能包括:终端、聊天面板、笔记本、差异编辑器、内联补全、评论、调试 REPL、悬停和通知。仅提供帮助对话框(没有无障碍视图)的功能包括:查找小组件、源代码管理输入框、键盘快捷方式编辑器、问题面板和入门教程。
以下第 4–7 节(信号、ARIA 播报、键盘导航、ARIA 标签)适用于范围更广的任何 UI 更改,包括对现有功能的修改。
更新现有功能时(例如添加新命令、键盘快捷键或交互能力),还必须更新该功能现有的无障碍帮助对话框(provideContent()),以说明新增功能。屏幕阅读器用户依赖帮助对话框来了解可用操作。
1. 无障碍帮助对话框
无障碍帮助对话框会告知用户该功能的用途、可用的键盘快捷键,以及如何通过屏幕阅读器与其交互。
步骤
-
创建一个实现
IAccessibleViewImplementation的类,并设置type = AccessibleViewType.Help。- 设置
priority(值越大,在多个提供程序匹配时越先显示)。 - 将
when设置为与功能处于焦点状态时匹配的ContextKeyExpression。 getProvider(accessor)返回一个AccessibleContentProvider。
- 设置
-
创建一个实现
IAccessibleViewContentProvider的内容提供程序类。id— 在src/vs/platform/accessibility/browser/accessibleView.ts中的AccessibleViewProviderId枚举里新增一项。verbositySettingKey— 引用新增的AccessibilityVerbositySettingId项(参见 §3)。options—{ type: AccessibleViewType.Help }。provideContent()— 返回本地化的多行帮助文本。
-
实现
onClose(),将焦点恢复到帮助对话框打开前获得焦点的元素。这可确保键盘用户和屏幕阅读器用户返回之前的上下文。 -
在功能的
*.contribution.ts文件中注册该实现:AccessibleViewRegistry.register(new MyFeatureAccessibilityHelp());
示例框架
最简单的做法是在 getProvider() 中直接返回 AccessibleContentProvider。这是代码库中最常见的模式(chat、inline chat、quick chat 等都采用此模式):
import { AccessibleViewType, AccessibleContentProvider, AccessibleViewProviderId } from '../../../../platform/accessibility/browser/accessibleView.js';
import { IAccessibleViewImplementation } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js';
import { AccessibilityVerbositySettingId } from '../../../../platform/accessibility/common/accessibilityConfiguration.js';
export class MyFeatureAccessibilityHelp implements IAccessibleViewImplementation {
readonly priority = 100;
readonly name = 'my-feature';
readonly type = AccessibleViewType.Help;
readonly when = MyFeatureContextKeys.isFocused;
getProvider(accessor: ServicesAccessor) {
const helpText = [
localize('myFeature.help.overview', "You are in My Feature. …"),
localize('myFeature.help.key1', "- {0}: Do something", '<keybinding:myFeature.doSomething>'),
].join('\n');
return new AccessibleContentProvider(
AccessibleViewProviderId.MyFeature,
{ type: AccessibleViewType.Help },
() => helpText,
() => { /* onClose — refocus whatever was focused before */ },
AccessibilityVerbositySettingId.MyFeature,
);
}
}
或者,如果提供程序需要注入服务或必须跟踪状态(例如,保存先前获得焦点的元素引用),则可以创建一个扩展 Disposable 并实现 IAccessibleViewContentProvider 的自定义类,然后通过 IInstantiationService 实例化该类(示例请参阅 CommentsAccessibilityHelpProvider):
class MyFeatureAccessibilityHelpProvider extends Disposable implements IAccessibleViewContentProvider {
readonly id = AccessibleViewProviderId.MyFeature;
readonly verbositySettingKey = AccessibilityVerbositySettingId.MyFeature;
readonly options: IAccessibleViewOptions = { type: AccessibleViewType.Help };
provideContent(): string { /* … */ }
onClose(): void { /* … */ }
}
// In getProvider():
getProvider(accessor: ServicesAccessor) {
return accessor.get(IInstantiationService).createInstance(MyFeatureAccessibilityHelpProvider);
}
2. 无障碍视图
无障碍视图会在只读编辑器中以纯文本形式呈现功能的视觉内容。当功能会渲染屏幕阅读器无法直接读取的富内容或视觉内容时(例如:聊天回复、悬停提示、通知、终端输出、内联补全),必须提供无障碍视图。
如果功能完全通过键盘操作,并使用原生文本输入/输出(例如:简单的输入框),则不需要无障碍视图,只需提供无障碍帮助对话框。
步骤
- 创建一个实现
IAccessibleViewImplementation的类,并设置type = AccessibleViewType.View。 - 创建一个内容提供器,形式类似帮助对话框,但需满足以下要求:
options—{ type: AccessibleViewType.View },可选设置用于语法高亮的language。provideContent()— 以纯文本形式返回功能当前的内容。- 可选实现
provideNextContent()/providePreviousContent(),以便逐项导航。 - 实现
onClose(),将焦点恢复到打开无障碍视图之前的焦点位置。 - 可选提供
actions,用于让用户在无障碍视图中执行操作。
- 注册,与帮助对话框一起注册:
AccessibleViewRegistry.register(new MyFeatureAccessibleView());
示例框架
export class MyFeatureAccessibleView implements IAccessibleViewImplementation {
readonly priority = 100;
readonly name = 'my-feature';
readonly type = AccessibleViewType.View;
readonly when = MyFeatureContextKeys.isFocused;
getProvider(accessor: ServicesAccessor) {
// Retrieve services, build content from the feature's current state
const content = getMyFeatureContent();
if (!content) {
return undefined;
}
return new AccessibleContentProvider(
AccessibleViewProviderId.MyFeature,
{ type: AccessibleViewType.View },
() => content,
() => { /* onClose — refocus whatever was focused before the accessible view opened */ },
AccessibilityVerbositySettingId.MyFeature,
);
}
}
3. 无障碍详细程度设置
详细程度设置用于控制功能获得焦点时是否播报提示,例如“按 Alt+F1 获取无障碍帮助”。已熟悉快捷键的用户可以禁用此提示。
步骤
-
在
src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts的AccessibilityVerbositySettingId中添加一项:export const enum AccessibilityVerbositySettingId { // … existing entries … MyFeature = 'accessibility.verbosity.myFeature' } -
在同一文件的
configuration.properties对象中注册配置属性:[AccessibilityVerbositySettingId.MyFeature]: { description: localize('verbosity.myFeature.description', 'Provide information about how to access the My Feature accessibility help menu when My Feature is focused.'), ...baseVerbosityProperty },baseVerbosityProperty会为其设置type: 'boolean'、default: true和tags: ['accessibility']。 -
在帮助对话框提供器(
verbositySettingKey)和无障碍视图提供器中引用此设置键,以便运行时检查是否显示提示。
4. 无障碍信号(声音和播报)
无障碍信号会为视觉上发生的事件提供声音和语音反馈。请使用 IAccessibilitySignalService 在发生重要事件时播放信号(例如,出现错误、任务完成或内容发生变化)。
使用时机
- 当事件已有对应信号时,使用现有信号(参见
AccessibilitySignal.*静态成员,例如AccessibilitySignal.error、AccessibilitySignal.terminalQuickFix、AccessibilitySignal.clear)。 - 如果没有合适的现有信号,请联系 @meganrogge,讨论添加新信号。未经事先沟通,请勿注册新信号。
信号的工作方式
每个信号都有两种由用户设置控制的模式:
- 声音 — 短促的音频提示,可设置为
auto(连接屏幕阅读器时开启)、on或off。 - 播报 — 通过
aria-live播放语音消息,可设置为auto或off。
用法
// Inject the service via constructor parameter
constructor(
@IAccessibilitySignalService private readonly _accessibilitySignalService: IAccessibilitySignalService
) { }
// Play a signal
this._accessibilitySignalService.playSignal(AccessibilitySignal.terminalQuickFix);
// Play with options
this._accessibilitySignalService.playSignal(AccessibilitySignal.error, { userGesture: true });
5. ARIA 警报与状态消息
请使用 src/vs/base/browser/ui/aria/aria.ts 中的 alert() 和 status() 函数,向屏幕阅读器播报动态变化。
alert(msg) — 强提示实时区域(role="alert")
- 适用于:用户必须立即知晓的紧急重要信息。
- 示例:错误、警告、关键状态变化、用户主动操作的结果。
- 行为:会打断屏幕阅读器当前的播报。
status(msg) — 礼貌提示实时区域(aria-live="polite")
- 适用于:非紧急的信息更新,应在屏幕阅读器空闲时播报。
- 示例:进度更新、搜索结果数量、后台状态变化。
- 行为:消息会排队,并在屏幕阅读器完成当前播报后播放。
指南
- 优先使用
status()而非alert(),除非信息具有时效性,或是直接用户操作的结果。过度使用alert()会造成嘈杂且令人分心的体验。 - 保持消息简洁。 屏幕阅读器会读出完整消息;消息过长会让用户等待。
- 不要重复播报——如果无障碍信号已经播报了事件,就不要再为同一信息调用
alert()/status()。 - 使用
nls.localize()本地化所有消息。
6. 键盘导航
所有交互式 UI 元素都必须能够通过键盘完整操作。
要求
- Tab 顺序:所有交互式元素都必须能通过
Tab/Shift+Tab按逻辑顺序访问。 - 方向键导航:列表、树、网格和工具栏必须按照 WAI-ARIA 模式支持方向键导航。
- 焦点可见性:获得焦点的元素必须显示可见的焦点指示器(VS Code 的主题系统通过
focusBorder提供此功能)。 - 不得仅支持鼠标操作:所有可通过点击或悬停执行的操作,也必须能够通过键盘执行(上下文菜单、按钮、切换开关等)。
- 按 Escape 关闭:覆盖层、对话框和弹出窗口必须能够通过
Escape关闭,并将焦点返回到先前的元素。 - 焦点陷阱:模态对话框必须将焦点限制在对话框内,直到对话框关闭。
说明不可用的选项
遵循可发现的不可用功能模式:文档中提及的功能,不应仅仅因为用户无法启用,就变得莫名其妙地不可见。使用控件支持的 ARIA 语义,同时播报禁用状态及其原因;仅使用灰色标签、公文包图标或悬停提示并不足够。文本已能表达含义时,图标应保持为装饰元素。
说明性链接必须能够独立通过鼠标和键盘操作,且不应触发已禁用的选项。请验证实际的 Tab/Shift+Tab 和 Enter 行为,而不只是检查 tabIndex 或以编程方式调用 focus();列表导航可能会跳过禁用的行。策略刷新时应保留焦点和选中状态,防止执行过时操作,并更新该功能的无障碍帮助,提供可用的说明和后续步骤。请复用设计指南中链接的设置、模型、测试框架和权限选择器示例。
7. ARIA 标签和角色
所有交互式 UI 元素都必须具有适当的 ARIA 属性,以便屏幕阅读器识别并描述它们。
要求
aria-label:每个没有可见文本的交互式元素(图标按钮、仅图标的操作、自定义控件)都必须有描述性的aria-label。标签应进行本地化。aria-labelledby/aria-describedby:使用这些属性将元素与现有的可见文本关联起来,而不是重复字符串。role:未使用原生 HTML 元素的自定义控件必须声明正确的 ARIA 角色(例如,role="button"、role="tree"、role="tablist")。aria-expanded、aria-selected、aria-checked:必须使用相应的 ARIA 状态属性传达切换和选择状态。aria-hidden="true":装饰性或冗余元素(文本标签旁的图标、装饰性分隔符)必须从无障碍树中隐藏。
指南
- 避免使用“按钮”或“图标”等笼统标签,应描述操作,例如:“关闭面板”、“切换侧边栏”、“运行任务”。
- 使用屏幕阅读器进行测试(macOS 上的 VoiceOver、Windows 上的 NVDA),确认标签在上下文中能够正确读出。
- 列表和树在虚拟化时应使用
aria-setsize和aria-posinset,以便屏幕阅读器报告正确的数量。
每项新功能的检查清单
- 在
accessibleView.ts中添加新的AccessibleViewProviderId条目 - 在
accessibilityConfiguration.ts中添加新的AccessibilityVerbositySettingId条目 - 在配置属性中使用
...baseVerbosityProperty注册详细程度设置 - 创建并注册
type = Help的IAccessibleViewImplementation - 内容提供程序引用正确的
verbositySettingKey - 使用
nls.localize()完整本地化帮助文本 - 帮助文本中的键绑定使用
<keybinding:commandId>语法,以便动态解析 - 设置
when上下文键,确保只有在功能获得焦点时才显示对话框 - 如果功能包含丰富/可视内容:创建并注册
type = View的IAccessibleViewImplementation - 在功能的
*.contribution.ts文件中调用注册方法 - 为重要事件播放无障碍信号(使用现有的
AccessibilitySignal.*或注册新信号) - 对动态变化适当地使用
aria.alert()或aria.status()(除非情况紧急,否则优先使用status()) - 所有交互元素均可通过键盘访问和操作
- 所有没有可见文本的交互元素都具有本地化的
aria-label - 自定义小组件声明正确的 ARIA
role和状态属性 - 使用
aria-hidden="true"隐藏装饰性元素
关键文件
src/vs/platform/accessibility/browser/accessibleView.ts—AccessibleViewProviderId、AccessibleContentProvider、IAccessibleViewContentProvidersrc/vs/platform/accessibility/browser/accessibleViewRegistry.ts—AccessibleViewRegistry、IAccessibleViewImplementationsrc/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts—AccessibilityVerbositySettingId、详细程度设置注册src/vs/platform/accessibilitySignal/browser/accessibilitySignalService.ts—IAccessibilitySignalService、AccessibilitySignalsrc/vs/base/browser/ui/aria/aria.ts— 用于 ARIA 实时区域播报的alert()、status()