mcpField
A
accessibility

accessibility

github.com/verygoodopensource设计与 UI175133 次安装3 天前更新
代码仓库

关于 accessibility

用于按所选 WCAG 2.2 等级和目标平台审查、修复 Flutter 应用无障碍问题的工作流技能,涵盖分阶段审计、代码级问题记录与范围明确的修复。

主要功能

  1. 01选择 WCAG 2.2 合规等级
  2. 02按平台检查 Flutter 应用
  3. 03覆盖七类无障碍审计
  4. 04提供严重性和 Dart 修复代码
  5. 05确定修复范围并编写回归测试

适用场景

  1. 01按指定 WCAG 等级审查 Flutter 组件
  2. 02针对目标平台检查无障碍情况
  3. 03生成含代码替换的可执行问题记录
  4. 04按优先级修复并添加回归测试

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/verygoodopensource/vgv-ai-flutter-plugin --skill accessibility

安装后在 Claude Code 中输入/accessibility确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

agents/
openai.yaml
references/
audit-templates.md
examples.md
testing.md
wcag-criteria.md
widget-mapping.md
references/platforms/
android.md
ios.md
linux.md
macos.md
web.md
windows.md

详细介绍

无障碍

面向 WCAG 2.2 A、AA 和 AAA 一致性等级的 Flutter 无障碍审计与修复。本文件是工作流;附加资源中列出的参考文件会按需加载,以下每个阶段都会注明所需文件。

仅加载当前阶段指定的文件。


核心标准

所有无障碍工作都应遵循以下标准:

一致性等级 — 每次审计开始时,都要询问项目的目标 WCAG 2.2 一致性等级(A、AA、AA + 指定的 AAA,或 AAA)。绝不要默认是 AA。

平台选择 — 每次审计开始时,都要询问目标平台是哪一个或哪几个(iOS、Android、Web、macOS、Windows、Linux)。应用 references/platforms/ 中相应文件的平台规则。

图像语义(WCAG 1.1.1)— 每个 Image 都必须设置 semanticLabel,或用 Semantics(label:) 包裹。装饰性图像使用 excludeFromSemantics: true。

手势检测器(WCAG 2.1.1)— 绝不要将裸 GestureDetector 用作点击目标。使用 InkWell、ElevatedButton、TextButton 或 IconButton。GestureDetector 仅支持指针操作,无法通过键盘或开关访问。

目标尺寸(WCAG 2.5.8)— AA 级的目标尺寸最小为 24x24 CSS px(≈ 24 dp)。VGV 建议的最小尺寸为 48x48 dp。AA 级下,24 dp 到 48 dp 之间的问题标记为 VGV 风格的问题;AAA 级下,则依据 2.5.5(44 dp)标记为 WCAG 问题。

拖动替代方式(WCAG 2.5.7)— 所有基于拖动的功能都必须在同一屏幕上提供非拖动的替代方式。滑块需要键盘或步进器替代方式。Dismissible 需要明确的删除按钮。ReorderableListView 需要上移/下移或“移动到”控件。

焦点不被遮挡(WCAG 2.4.11)— 粘性标题栏、Snackbar、底部弹层、常驻 FAB 或覆盖层不得完全遮挡获得焦点的小部件。使用 Scrollable.ensureVisible 和 Scaffold.resizeToAvoidBottomInset: true。

颜色区分(WCAG 1.4.1)— 绝不能仅用颜色区分内容。始终搭配标签、图标或形状。

动画与动态效果(WCAG 2.3.3)— 所有动画都必须遵循 MediaQuery.disableAnimations。每个 AnimationController、AnimatedContainer、Hero 转场和 PageRouteBuilder 转场都必须受此标志控制。

图标按钮(WCAG 4.1.2)— 仅含图标的按钮必须有 Tooltip 或 Semantics(label:)。屏幕阅读器没有其他方式传达其用途。

排除语义(WCAG 1.1.1)— 绝不要对非装饰性内容使用 ExcludeSemantics。它会从语义树中移除整个子树,因此其中的任何按钮或其他控件都会从 TalkBack 和 VoiceOver 中消失,屏幕阅读器用户也无法触发其操作。拒绝将可操作内容包裹在其中的请求时,要说明这一后果。若要避免屏幕阅读器停留在每个子项上,只对静态标签和值这对内容使用 MergeSemantics,并将交互式同级项留在其外部,以便它们仍可独立获得焦点。

文本容器(WCAG 1.4.4)— 固定高度的容器不得包裹 Text。使用 minHeight 约束。固定高度在 Android 上会于字体缩放 1.5x 时裁切文本;在 iOS 上更早,因为“更大辅助功能字体”最高约为 3.1x。

对比度(WCAG 1.4.3)— 所有文本和 UI 组件都必须达到所选 WCAG 等级要求的对比度。参见 references/wcag-criteria.md。

Cupertino 语义(WCAG 4.1.2)— Cupertino 小部件(CupertinoSwitch、CupertinoSlider、CupertinoSegmentedControl、CupertinoButton)自带的语义默认值弱于对应的 Material 小部件。始终用 Semantics(label:, value:, button:) 包裹它们。

自动填充提示(WCAG 1.3.5)— 每个用于收集结构化个人数据(电子邮件、用户名、密码、姓名、地址、电话、oneTimeCode)的 TextField 都必须声明 autofillHints。AA 级下,1.3.5 要求必须如此;同时,这也是 A 级 3.3.7“重复输入”的基础。 发现格式 — 每项审计发现都必须附带 WCAG 标准编号及其名称(2.5.8 Target Size (Minimum),不得只写 2.5.8)、恰好为 CRITICAL、MAJOR 或 MINOR 之一的大写严重性等级,以及以修改前和修改后的 Dart 代码呈现的修复方案。不得将严重性评为 High/Medium/Low 或 Serious/Moderate/Minor。不得提交修复方案只有文字说明的发现。

异步播报(WCAG 4.1.3)— 每次异步的用户可见状态变更都必须通过 Semantics(liveRegion: true) 或 SemanticsService.announce 播报。


工作流

每次无障碍工作都必须依次完成四个阶段。不得跳过阶段 1 或阶段 2。

跨运行环境说明。 以下阶段 1、2 和 4 会用到 AskUserQuestion。如果运行环境没有该工具,请调用该环境提供的等效提问工具;如果没有,则用带编号的纯文本提出相同问题。无论采用哪种方式,都必须等待用户回复后再继续。

阶段 1:符合性等级选择

使用 AskUserQuestion 提问:

question: "Which WCAG 2.2 conformance level are you targeting?"
header: "WCAG level"
options:
  - label: "A"
    description: "Removes the most critical barriers. Includes the new 2.2 criteria 3.2.6 Consistent Help and 3.3.7 Redundant Entry."
  - label: "AA"
    description: "Standard most regulators require. Adds contrast, resize text, focus visible, plus the four new 2.2 AA criteria: 2.4.11, 2.5.7, 2.5.8, 3.3.8."
  - label: "AA + selected AAA"
    description: "AA across the app, plus specific AAA criteria scoped to flagged flows. Common: 1.4.6 enhanced contrast, 2.2.3 no timing, 2.4.13 focus appearance."
  - label: "AAA"
    description: "Full AAA. 7:1 contrast, no timing, no exceptions to keyboard. Rare for whole products."

如果用户选择“AA + selected AAA”,请接着用自由文本请求用户提供希望纳入的 AAA 标准编号(例如,“1.4.6, 2.2.3, 2.4.13”)。

**结果:**记录所选等级。所有审计检查、标准引用和修复建议都只适用该等级(以及所有较低等级)的规则,和用户选择纳入的任何 AAA 标准。

阶段 2:平台选择

使用 AskUserQuestion(如果支持则使用多选,否则提出一个问题并让用户以逗号分隔回复)提问:

question: "Which platforms is this app targeting? Select all that apply."
header: "Platforms"
options:
  - label: "iOS"
    description: "VoiceOver, Switch Control, Dynamic Type up to 3.1x, Voice Control, Bold Text, Reduce Motion, Reduce Transparency."
  - label: "Android"
    description: "TalkBack, Switch Access, font scale up to 2x, Voice Access, color inversion."
  - label: "Web"
    description: "Flutter Web rendered to a Semantics-mapped DOM. NVDA + Chrome, JAWS + Chrome, VoiceOver + Safari."
  - label: "macOS"
    description: "VoiceOver, Full Keyboard Access, Reduce Motion, Increase Contrast."
  - label: "Windows / Linux desktop"
    description: "Narrator, NVDA, JAWS (Windows), Orca (Linux), Windows High Contrast Mode."

**结果:**记录所选平台。加载 references/platforms/ 中匹配的文件(例如,iOS 对应 references/platforms/ios.md,Android 对应 references/platforms/android.md)。只加载所选平台对应的文件,不要加载不必要的文件。

阶段 3:按级别和平台进行审计

针对每个选定的平台,按以下七个类别依次审计提供的文件或组件:

  1. 语义和屏幕阅读器 — 标签、角色、实时区域、合并/排除是否正确、Cupertino 语义缺口,以及 TalkBack 和 VoiceOver 下的阅读顺序。

  2. 触控目标和拖动替代方案 — WCAG 2.2 2.5.8 AA 级别最低要求(24 CSS px)、2.5.5 AAA 级别增强要求(44 CSS px)、VGV 建议的 48 dp,以及 2.5.7 拖动替代方案。

  3. 焦点和键盘导航 — 可操作性、遍历顺序、对话框焦点捕获、焦点指示器,以及 2.4.11 / 2.4.12 焦点不被遮挡。

  4. 颜色对比度 — 文本和 UI 组件的比例需达到所选级别的阈值(见下表)。

  5. 文本缩放 — 不使用固定高度的文本容器,不限制文本缩放。Android 和 Web 的模拟上限为 2 倍,iOS 为 3 倍。

  6. 动画和运动 — 在 AnimationController、Hero、AnimatedContainer、PageRouteBuilder 中使用 disableAnimations 进行控制。AA 级别下,内容闪烁频率不得超过 3 Hz;AAA 级别下不得闪烁。

  7. 表单、身份验证和帮助 — autofillHints(1.3.5、3.3.7)、无障碍身份验证(3.3.8 / 3.3.9)、帮助机制的位置保持一致(3.2.6)。

仅应用所选级别下生效的标准(以及选择加入的 AAA 标准),并且仅应用于所选平台。

将每条发现都写在此区块中,不得省略任何一行:

### 1. Close button is a 16x16 GestureDetector
- **File:** lib/order/order_summary.dart ~L26
- **WCAG:** 2.1.1 Keyboard (Level A, WCAG 2.0)
- **Platform(s):** iOS, Android
- **Severity:** CRITICAL
- **Issue:** `GestureDetector` is pointer-only, so the close action is unreachable via VoiceOver, TalkBack, Switch Control, and Switch Access. Expected: a focusable, activatable button with an accessible name.
- **Fix:**

```dart
// Before
SizedBox(
  width: 16,
  height: 16,
  child: GestureDetector(
    onTap: onClose,
    child: const Icon(Icons.close, size: 16),
  ),
)
```

```dart
// After
IconButton(
  onPressed: onClose,
  tooltip: 'Close order summary',
  iconSize: 24,
  constraints: const BoxConstraints(minWidth: 48, minHeight: 48),
  icon: const Icon(Icons.close),
)
```

报告中每条发现都必须遵守以下规则,包括 MINOR 级别的发现:

  • 标准。 将编号和标准名称写在一起,随后注明级别和 WCAG 版本:2.5.8 Target Size (Minimum) (Level AA, WCAG 2.2)。仅写编号的发现不完整。
  • 严重程度。 必须且只能从 references/audit-templates.md 中的严重程度指南选择 CRITICAL、MAJOR、MINOR 之一。平台间存在差异时,保留这三个标签并注明平台:CRITICAL (iOS, Android), MAJOR (Web)。
  • 修复。 每条发现都必须包含 Before 和 After 代码块,展示真实的 Dart 代码,并在该发现下并排呈现。仅提供文字建议不算修复,仅提供 After 代码块也不算修复——读者必须看到被替换的确切代码。报告末尾集中提供一个修正后的组件,不能满足以上发现的要求。
  • 范围。 如果检查发现的问题无法仅凭代码确认(例如,缺少颜色信息无法判断对比度,或没有运行应用无法判断阅读顺序),应将其列在 Out of Scope 下,而不是作为发现。发现必须有依据、可评级且可修复。

结果: 完成全部七个类别后,使用 references/audit-templates.md 中的模板生成审计报告。选择与阶段 1 相符的级别对应的已通过检查列表。

阶段 4:选择修复范围

提交报告后,使用 AskUserQuestion:

question: "The audit is complete. How would you like to proceed with fixes?"
header: "Fix scope"
options:
  - label: "All issues"
    description: "Fix every CRITICAL, MAJOR, and MINOR finding"
  - label: "Critical + Major only"
    description: "Fix blockers and significant barriers; skip MINOR polish items"
  - label: "Critical only"
    description: "Fix only what blocks assistive technology users entirely"
  - label: "Specific findings"
    description: "List the finding numbers you want fixed"

结果: 严格按用户选择应用修复。应用修复后,确认:“已修复 [N] 条发现([severities])。剩余 [N] 条仍未解决。”

为每项修复编写测试,防止回归。如果不清楚特定组件的正确实现方式,请参阅 references/testing.md 中涵盖全部七个审计类别的测试套件,以及 references/widget-mapping.md。


WCAG 2.2 级别标准参考

references/wcag-criteria.md 中提供了各级别的标准表,并列出每项标准对应的 Flutter 检查。在阶段 3 加载该文档。


常见反模式速查

完整代码示例及修正版本见 references/examples.md。以下是此技能最常标记的模式。

语义(WCAG 1.1.1、4.1.2)
// WRONG: empty label, no label, ExcludeSemantics over actionable content removes the button
// from the semantics tree entirely, so the action is unreachable for screen reader users
Image.asset('assets/warning.png', semanticLabel: '')
Image.asset('assets/chart.png')
ExcludeSemantics(child: ElevatedButton(onPressed: _submit, child: const Text('Submit')))
// WRONG: Cupertino without semantic wrapper
CupertinoSwitch(value: _enabled, onChanged: _onChanged)
// WRONG: MergeSemantics around an interactive child folds the button's role away
MergeSemantics(
  child: Row(children: [const Text('Item'), IconButton(onPressed: _delete, icon: ...)]),
)
触控目标和拖动(WCAG 2.5.8、2.5.7)
// WRONG: 16x16 target, below WCAG 2.2 2.5.8 AA (24 dp)
SizedBox(width: 16, height: 16, child: GestureDetector(onTap: _onTap, child: const Icon(Icons.close, size: 16)))
// WRONG: Dismissible with no non-drag alternative (WCAG 2.2 2.5.7)
Dismissible(key: ValueKey(item.id), onDismissed: (_) => _delete(item), child: ListTile(title: Text(item.name)))
焦点(WCAG 2.1.1、2.4.11)
// WRONG: GestureDetector is not keyboard-accessible
GestureDetector(onTap: _onTap, child: const Text('Click me'))
// WRONG: bottom bar covers focused TextField (WCAG 2.2 2.4.11)
Scaffold(
  resizeToAvoidBottomInset: false,
  bottomNavigationBar: const BottomAppBar(child: ...),
  body: ListView(children: [..., TextField(focusNode: _last), ...]),
)
文本缩放与动态效果(WCAG 1.4.4、2.3.3)
// WRONG: clips text at 1.5x font scale
SizedBox(height: 48, child: Text('Status: Ready'))
// WRONG: animation always plays
AnimatedContainer(duration: const Duration(milliseconds: 500), color: ..., child: child)

修正后的代码片段、完整类(AccessibleTapTarget、AccessibleSlider、AccessibleReorderableList、AccessiblePageRoute、AccessibleHero)以及 Cupertino 语义包装器,请参阅 references/examples.md。


其他资源

官方参考资料:

评论

支持 Markdown