Skip to content
cccskills
FreePublished on GitHub

accessibility-implementation

实现可访问性(无障碍)时使用。覆盖 WCAG 2.2 AA / ARIA / 键盘导航 / 屏幕阅读器 / 颜色对比 / 焦点管理。融合 React Aria / Radix UI / Headless UI 实践 + axe-core 自动化测试。

The English summary is being prepared. Showing the original description.

Works withPlaywright
See how to install

Files included (2)

  • SKILL.md12.9 KB
  • templates/a11y-checklist.md3.4 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

可访问性实现(Accessibility Implementation)

参考来源:W3C WCAG 2.2 (2024)、WAI-ARIA Authoring Practices、MDN Accessibility、Adobe React Aria、Radix UI a11y、deque axe-core。

适用场景

  • 所有面向用户的界面
  • 表单 / 模态 / 菜单 / 表格 / 标签
  • 键盘可操作
  • 屏幕阅读器友好
  • 视觉障碍(对比度 / 字体)
  • 运动障碍(点击区域 / 减少动画)
  • 认知障碍(清晰文案 / 错误恢复)

核心原则

1. 不是 nice-to-have
   - 法律要求(ADA、欧盟无障碍法案)
   - 占用户 15%+
   - SEO 受益

2. 默认无障碍(不是事后补救)
   - 用语义化 HTML
   - 用 Headless 库(自带 a11y)

3. 键盘可操作 = 屏幕阅读器友好的第一步
   - Tab 导航
   - Enter / Space 激活
   - Esc 取消
   - 方向键(菜单 / 标签)

4. 不仅靠颜色传达信息
   - 错误:红色 + ✗ 图标 + 文字
   - 不止颜色

5. 焦点环必须可见
   - 不要 outline: none

6. 测试三层
   - 自动(axe-core)
   - 手动(键盘 + 缩放)
   - 真实工具(NVDA / VoiceOver)

WCAG 2.2 关键标准(AA 级)

标准要求
1.3.1 Info and Relationships信息和结构能被程序理解(语义化 HTML / ARIA)
1.4.3 Contrast (Minimum)文字对比度 ≥ 4.5:1(大字 ≥ 3:1)
1.4.10 Reflow320px 宽度可用(不需要横滚)
1.4.11 Non-text ContrastUI 控件对比度 ≥ 3:1
2.1.1 Keyboard全功能键盘可达
2.4.3 Focus Order焦点顺序合理
2.4.7 Focus Visible焦点可见
2.5.5 Target Size (AAA → 2.2 加入 AA)点击区域 ≥ 24x24px
3.3.1 Error Identification错误明确识别
3.3.2 Labels or Instructions输入有 label
4.1.2 Name, Role, Value控件有 name / role / value

语义化 HTML(最重要)

<!-- ❌ 反例 -->
<div onclick="...">点我</div>
<div class="title">标题</div>
<div class="list">
  <div>项目 1</div>
  <div>项目 2</div>
</div>

<!-- ✅ 正确 -->
<button type="button" onclick="...">点我</button>
<h2>标题</h2>
<ul>
  <li>项目 1</li>
  <li>项目 2</li>
</ul>

<!-- 表单 -->
<form>
  <label for="email">邮箱</label>
  <input id="email" type="email" required aria-describedby="email-help" />
  <p id="email-help">我们不会泄露您的邮箱</p>
  
  <button type="submit">提交</button>
</form>

<!-- 导航 -->
<nav aria-label="主导航">
  <ul>
    <li><a href="/">首页</a></li>
    <li><a href="/about">关于</a></li>
  </ul>
</nav>

<!-- 主要内容 -->
<main>
  <article>
    <h1>文章标题</h1>
    ...
  </article>
</main>

<!-- 章节 -->
<section aria-labelledby="features-heading">
  <h2 id="features-heading">特性</h2>
  ...
</section>

ARIA 速查

角色(Role)

landmark 角色(页面结构):
  banner / navigation / main / contentinfo / complementary / search / form / region

widget 角色:
  button / link / checkbox / radio / textbox / combobox / listbox / option
  tab / tabpanel / tablist / menu / menuitem
  dialog / alertdialog / tooltip / status / alert
  tree / treeitem / grid / row / cell

非交互(仅当 HTML 不够用时用):
  heading / list / listitem / table

属性(Property / State)

描述:
  aria-label="按钮标签"
  aria-labelledby="id"
  aria-describedby="id"
  aria-details="id"

状态:
  aria-disabled="true"
  aria-checked="true | false | mixed"
  aria-selected="true"
  aria-expanded="true | false"
  aria-pressed="true | false"
  aria-hidden="true"        # 屏幕阅读器忽略
  aria-invalid="true"
  aria-required="true"
  aria-busy="true"

关系:
  aria-controls="id"
  aria-owns="id"
  aria-haspopup="true | menu | listbox | dialog"
  
实时区域:
  aria-live="polite | assertive | off"
  role="status"             # = aria-live=polite
  role="alert"              # = aria-live=assertive

React 完整示例

按钮

<button
  type="button"
  onClick={handleClick}
  disabled={isLoading}
  aria-busy={isLoading}
  aria-label={iconOnly ? '关闭' : undefined}  // icon-only 必须
>
  {isLoading ? <Spinner aria-hidden="true" /> : null}
  {!iconOnly && <span>提交</span>}
</button>

输入框

<div className="form-field">
  <label htmlFor="email" className="label">
    邮箱 <span aria-hidden="true">*</span>
    <span className="sr-only">必填</span>
  </label>
  <input
    id="email"
    type="email"
    required
    aria-required="true"
    aria-invalid={!!error}
    aria-describedby={error ? 'email-error' : 'email-help'}
    autoComplete="email"
  />
  {!error && (
    <p id="email-help" className="help">
      我们不会泄露您的邮箱
    </p>
  )}
  {error && (
    <p id="email-error" role="alert" className="error">
      {error}
    </p>
  )}
</div>

模态对话框(用 Radix)

import * as Dialog from '@radix-ui/react-dialog';

<Dialog.Root>
  <Dialog.Trigger asChild>
    <button>打开</button>
  </Dialog.Trigger>
  <Dialog.Portal>
    <Dialog.Overlay className="overlay" />
    <Dialog.Content
      className="dialog"
      aria-labelledby="dialog-title"
      aria-describedby="dialog-desc"
    >
      <Dialog.Title id="dialog-title">确认删除</Dialog.Title>
      <Dialog.Description id="dialog-desc">
        此操作不可撤销
      </Dialog.Description>
      <button>确认</button>
      <Dialog.Close asChild>
        <button aria-label="关闭">×</button>
      </Dialog.Close>
    </Dialog.Content>
  </Dialog.Portal>
</Dialog.Root>

Radix 自动处理:

  • 焦点陷阱(Tab 不出 dialog)
  • Esc 关闭
  • 打开时聚焦首个可聚焦元素
  • 关闭时焦点回到触发器
  • aria-modal / role=dialog
  • 屏幕阅读器读 title + description

自定义按钮(如非要)

<div
  role="button"
  tabIndex={0}
  onClick={handleClick}
  onKeyDown={(e) => {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault();
      handleClick();
    }
  }}
  aria-disabled={disabled}
  className={cn('custom-btn', disabled && 'disabled')}
>
  点击我
</div>

// 但首选 <button>,自定义除非有强理由

实时反馈

// Toast 通知
<div role="status" aria-live="polite">
  操作成功
</div>

// 紧急错误
<div role="alert" aria-live="assertive">
  网络错误,请重试
</div>

// Loading
<div role="status" aria-live="polite" aria-busy="true">
  加载中...
</div>

键盘导航

标准键盘交互

Tab:下一个可聚焦
Shift+Tab:上一个
Enter:激活按钮 / 链接
Space:激活按钮 / 切换 checkbox
Esc:关闭模态 / 取消
方向键:菜单 / 标签 / 单选组

不要:
  ❌ 拦截 Tab 改变行为
  ❌ tabindex > 0(破坏顺序)
  ❌ outline: none 不替代焦点环

焦点管理

// 模态打开聚焦首个输入
useEffect(() => {
  if (isOpen) {
    inputRef.current?.focus();
  }
}, [isOpen]);

// 模态关闭还原焦点(Radix 自动)
const triggerRef = useRef<HTMLButtonElement>(null);
const closeAndRestore = () => {
  setOpen(false);
  triggerRef.current?.focus();
};

// 路由切换聚焦 main
useEffect(() => {
  document.getElementById('main')?.focus();
}, [pathname]);

// 表单提交失败聚焦首个错误
const onError = (errors) => {
  const firstError = Object.keys(errors)[0];
  document.getElementById(firstError)?.focus();
};

Skip Link

<!-- 让键盘用户跳过导航 -->
<a href="#main" className="skip-link">跳到主内容</a>

<style>
.skip-link {
  position: absolute;
  top: -40px;
  left: 0;
  background: #000;
  color: #fff;
  padding: 8px;
}
.skip-link:focus {
  top: 0;
}
</style>

颜色与对比

WCAG AA:
  - 普通文字 ≥ 4.5:1
  - 大文字(18pt / 14pt 加粗)≥ 3:1
  - UI 控件 ≥ 3:1
  - 图标边框 ≥ 3:1

WCAG AAA(更高):
  - 普通文字 ≥ 7:1
  - 大文字 ≥ 4.5:1

工具:
  - WebAIM Contrast Checker
  - Chrome DevTools - Lighthouse Accessibility
  - Stark(Figma 插件)
不仅靠颜色:
  ❌ 错误:红色边框
  ✅ 错误:红色边框 + ✗ 图标 + "邮箱格式不正确"

  ❌ 链接:仅蓝色
  ✅ 链接:蓝色 + 下划线

暗黑模式

需要:
  □ 切换控件
  □ 持久化(localStorage)
  □ 跟随系统(默认)
  □ 所有图片 / 图标暗黑适配
  □ 对比度暗黑下也达标

移动端 / 触摸

□ 点击区域 ≥ 44x44px(WCAG 2.2 至少 24x24)
□ 间距足够(≥ 8px)
□ 不依赖 hover(用 :focus / 持续显示)
□ 横屏 / 缩放可用
□ 视口设置正确
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">

减少动画

/* 用户偏好 */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

测试方法

自动化(必跑)

// 1. axe-core 集成(开发时)
import { AxeBuilder } from '@axe-core/playwright';

test('a11y check', async ({ page }) => {
  await page.goto('/');
  const results = await new AxeBuilder({ page }).analyze();
  expect(results.violations).toEqual([]);
});

// 2. jest-axe / vitest-axe(单元测试)
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);

test('Button is accessible', async () => {
  const { container } = render(<Button>Click</Button>);
  expect(await axe(container)).toHaveNoViolations();
});

// 3. Lighthouse CI Accessibility

手动(必做)

1. 仅键盘操作(拔鼠标)
   □ 能 Tab 到所有交互
   □ 焦点环可见
   □ Enter / Space 激活
   □ Esc 取消模态

2. 缩放 200%
   □ 不出现横滚
   □ 内容不重叠

3. 屏幕阅读器
   □ NVDA(Windows)
   □ VoiceOver(Mac,Cmd+F5)
   □ TalkBack(Android)
   □ VoiceOver iOS

用户测试

□ 邀请障碍用户测试
□ 录屏 + 反馈

Headless 库(推荐)

库框架特点
Radix UIReact业界 a11y 标杆
React AriaReactAdobe 出品,最严格
Headless UIReact/VueTailwind 配套
AriakitReact全面
Reka UI(前 Radix Vue)VueRadix Vue 移植
Melt UISvelteHeadless
Bits UISvelte简化
KobalteSolida11y 优秀

工作流程

1. 设计阶段
   - 与 UI/UX 同步:对比度 / 焦点 / 错误传达

2. 实现阶段
   - 用语义化 HTML
   - 用 Headless 库
   - 焦点管理

3. 自动化测试
   - jest-axe / @axe-core/playwright
   - CI 阻塞

4. 手动测试
   - 键盘导航
   - 屏幕阅读器
   - 缩放 200%

5. 用户测试(可选)

6. 监控
   - 定期 Lighthouse

配套模板

  • templates/a11y-checklist.md — WCAG 2.2 + ARIA + 键盘 + 测试 + 移动端

质量自检

□ 语义化 HTML(button/nav/main/h1...)
□ 表单 label 关联
□ ARIA 角色 / 属性正确
□ 键盘可操作(Tab/Enter/Esc/方向键)
□ 焦点环可见(focus-visible)
□ 焦点管理(模态聚焦 / 还原)
□ Skip Link
□ 不仅靠颜色(图标 + 文字)
□ 对比度 AA(4.5:1 普通 / 3:1 大)
□ 点击区域 ≥ 24x24
□ aria-live 状态变化
□ 减少动画偏好支持
□ 暗黑模式对比度
□ 缩放 200% 无横滚
□ 自动化测试(axe-core)
□ 手动测试(键盘 + 屏幕阅读器)

常见坑

  1. outline: none——焦点不可见
  2. div onclick——键盘不可达
  3. 图标按钮无 aria-label——读屏器读"按钮"
  4. 错误只用红色——色盲看不到
  5. 对比度不达 4.5——视障读不清
  6. 模态打开不聚焦——键盘用户找不到
  7. 模态关闭焦点丢失——回到第一个 Tab
  8. tabindex 滥用 > 0——顺序乱
  9. placeholder 当 label——填了就看不见
  10. 不测屏幕阅读器——无法验证
  11. 动画无控制——前庭疾病不适
  12. 图片无 alt——读屏器跳过
  13. alt 重复 img / image——浪费
  14. 链接文字"点这里"——脱离上下文无意义
  15. 表格无 caption / scope——读屏器混乱

与其他 skill 的协作

上游:
  ui-ux-designer 工作流 accessibility skill → 设计阶段保证

下游:
  component-architecture → 组件 a11y 内置
  styling-system → 焦点 / 对比度 / 动画
  forms-validation → 字段错误关联
  testing-frontend → axe-core 测试

相关参考

  • 项目根 references/frontend-tech-stack-guide.md — Headless 库
  • ui-ux-designer 工作流 accessibility skill

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

A/B 测试设计与分析时使用。适用于产品实验、功能验证、转化率优化。融合假设驱动 + 样本量计算 + 置信度判断。

The English summary is being prepared. Showing the original description.

zhaoxuya520/AI-Fullstack-Delivery-Workflow54Updated May 19, 2026

上线前业务方/产品方验收时使用。适用于功能交付确认、UAT、发布会签。融合 BDD Given-When-Then、Specification by Example、ATDD。

The English summary is being prepared. Showing the original description.

zhaoxuya520/AI-Fullstack-Delivery-Workflow54Updated May 19, 2026

设计无障碍可访问的界面时使用。适用于满足 WCAG 标准、键盘导航、屏幕阅读器支持。优先使用 WCAG 2.2 AA 级标准 + 键盘 + 焦点 + 对比度 + ARIA。

The English summary is being prepared. Showing the original description.

zhaoxuya520/AI-Fullstack-Delivery-Workflow54Updated May 19, 2026

AI 模型部署时使用。适用于推理服务 / GPU / TensorRT / 监控 / 扩缩容。

The English summary is being prepared. Showing the original description.

zhaoxuya520/AI-Fullstack-Delivery-Workflow54Updated May 19, 2026

数据分析报告输出时使用。适用于业务分析报告、周报、专项分析、决策支持。融合洞察 + 建议 + 行动项。

The English summary is being prepared. Showing the original description.

zhaoxuya520/AI-Fullstack-Delivery-Workflow54Updated May 19, 2026

API 契约测试时使用。适用于消费者驱动契约测试(Pact)/ OpenAPI 验证(Dredd / Schemathesis)。融合 Pact + Schemathesis + 契约优先。

The English summary is being prepared. Showing the original description.

zhaoxuya520/AI-Fullstack-Delivery-Workflow54Updated May 19, 2026

See all skills by zhaoxuya520

Report a problem with this skill