accessibility-auditor

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Accessibility Auditor

可访问性审计指南

Instructions

操作说明

When auditing accessibility:
  1. Run automated checks (axe, Lighthouse)
  2. Manual keyboard testing
  3. Screen reader testing
  4. Check WCAG criteria
  5. Provide fixes
进行可访问性审计时:
  1. 运行自动化检查(axe、Lighthouse)
  2. 手动键盘测试
  3. 屏幕阅读器测试
  4. 检查WCAG标准
  5. 提供修复方案

Automated Testing

自动化测试

bash
undefined
bash
undefined

Lighthouse accessibility audit

Lighthouse可访问性审计

npx lighthouse https://yoursite.com --only-categories=accessibility --view
npx lighthouse https://yoursite.com --only-categories=accessibility --view

axe-core CLI

axe-core CLI

npx @axe-core/cli https://yoursite.com
npx @axe-core/cli https://yoursite.com

Pa11y

Pa11y

undefined
undefined

React Testing Library + jest-axe

React Testing Library + jest-axe

typescript
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';

expect.extend(toHaveNoViolations);

test('Button has no accessibility violations', async () => {
  const { container } = render(<Button>Click me</Button>);
  const results = await axe(container);
  expect(results).toHaveNoViolations();
});
typescript
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';

expect.extend(toHaveNoViolations);

test('Button has no accessibility violations', async () => {
  const { container } = render(<Button>Click me</Button>);
  const results = await axe(container);
  expect(results).toHaveNoViolations();
});

Playwright Accessibility Testing

Playwright可访问性测试

typescript
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';

test('page has no accessibility violations', async ({ page }) => {
  await page.goto('/');

  const results = await new AxeBuilder({ page }).analyze();
  expect(results.violations).toEqual([]);
});
typescript
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';

test('page has no accessibility violations', async ({ page }) => {
  await page.goto('/');

  const results = await new AxeBuilder({ page }).analyze();
  expect(results.violations).toEqual([]);
});

WCAG Checklist

WCAG检查清单

Level A (Minimum)

A级(最低要求)

Perceivable

可感知性

  • 1.1.1 Non-text content has alt text
  • 1.3.1 Info and relationships are programmatically determined
  • 1.3.2 Meaningful reading sequence
  • 1.4.1 Color is not the only way to convey info
  • 1.1.1 非文本内容配有替代文本
  • 1.3.1 信息与关联关系可通过程序识别
  • 1.3.2 阅读顺序符合逻辑
  • 1.4.1 不单独依赖颜色传递信息

Operable

可操作性

  • 2.1.1 All functionality available via keyboard
  • 2.1.2 No keyboard traps
  • 2.4.1 Skip navigation link provided
  • 2.4.2 Pages have descriptive titles
  • 2.4.3 Focus order is logical
  • 2.4.4 Link purpose is clear
  • 2.1.1 所有功能可通过键盘操作
  • 2.1.2 无键盘陷阱(无法跳出的组件)
  • 2.4.1 提供跳转到导航的链接
  • 2.4.2 页面配有描述性标题
  • 2.4.3 焦点顺序符合逻辑
  • 2.4.4 链接用途清晰明确

Understandable

可理解性

  • 3.1.1 Page language is specified
  • 3.2.1 Focus doesn't cause unexpected changes
  • 3.3.1 Errors are identified and described
  • 3.3.2 Labels or instructions provided
  • 3.1.1 页面语言已指定
  • 3.2.1 焦点变化不会导致意外的页面变更
  • 3.3.1 错误可被识别并配有描述
  • 3.3.2 提供标签或操作说明

Robust

健壮性

  • 4.1.1 Valid HTML (no duplicate IDs, proper nesting)
  • 4.1.2 Name, role, value for all UI components
  • 4.1.1 HTML有效(无重复ID、嵌套正确)
  • 4.1.2 所有UI组件具备名称、角色和值属性

Level AA (Standard Target)

AA级(标准目标)

  • 1.4.3 Contrast ratio 4.5:1 for text
  • 1.4.4 Text resizable to 200%
  • 1.4.10 Content reflows at 320px width
  • 2.4.6 Headings and labels are descriptive
  • 2.4.7 Focus indicator is visible
  • 3.2.3 Navigation is consistent
  • 3.2.4 Components identified consistently
  • 1.4.3 文本对比度达到4.5:1
  • 1.4.4 文本可放大至200%
  • 1.4.10 内容在320px宽度下可自动重排
  • 2.4.6 标题和标签具备描述性
  • 2.4.7 焦点指示器可见
  • 3.2.3 导航方式保持一致
  • 3.2.4 组件标识保持一致

Common Issues & Fixes

常见问题与修复方案

1. Missing Alt Text

1. 缺少替代文本

tsx
// Bad
<img src="/hero.jpg" />

// Good - Informative image
<img src="/hero.jpg" alt="Team collaborating in modern office" />

// Good - Decorative image
<img src="/decoration.jpg" alt="" role="presentation" />

// Good - Icon button
<button aria-label="Close dialog">
  <XIcon aria-hidden="true" />
</button>
tsx
// 错误示例
<img src="/hero.jpg" />

// 正确示例 - 信息性图片
<img src="/hero.jpg" alt="团队在现代化办公室协作" />

// 正确示例 - 装饰性图片
<img src="/decoration.jpg" alt="" role="presentation" />

// 正确示例 - 图标按钮
<button aria-label="关闭对话框">
  <XIcon aria-hidden="true" />
</button>

2. Missing Form Labels

2. 缺少表单标签

tsx
// Bad
<input type="email" placeholder="Email" />

// Good - Visible label
<div>
  <label htmlFor="email">Email</label>
  <input id="email" type="email" />
</div>

// Good - Visually hidden label
<div>
  <label htmlFor="search" className="sr-only">Search</label>
  <input id="search" type="search" placeholder="Search..." />
</div>
tsx
// 错误示例
<input type="email" placeholder="Email" />

// 正确示例 - 可见标签
<div>
  <label htmlFor="email">邮箱</label>
  <input id="email" type="email" />
</div>

// 正确示例 - 视觉隐藏标签
<div>
  <label htmlFor="search" className="sr-only">搜索</label>
  <input id="search" type="search" placeholder="搜索..." />
</div>

3. Poor Color Contrast

3. 颜色对比度不足

tsx
// Bad - 2.5:1 ratio
<p className="text-gray-400 bg-white">Low contrast text</p>

// Good - 4.5:1+ ratio
<p className="text-gray-700 bg-white">Accessible text</p>

// Check contrast: https://webaim.org/resources/contrastchecker/
tsx
// 错误示例 - 对比度2.5:1
<p className="text-gray-400 bg-white">低对比度文本</p>

// 正确示例 - 对比度≥4.5:1
<p className="text-gray-700 bg-white">可访问文本</p>

// 对比度检查工具:https://webaim.org/resources/contrastchecker/

4. Missing Focus Styles

4. 缺少焦点样式

css
/* Bad - Removes focus */
*:focus { outline: none; }

/* Good - Custom focus style */
*:focus-visible {
  outline: 2px solid #3b82f6;
  outline-offset: 2px;
}

/* Tailwind */
.btn {
  @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2;
}
css
/* 错误示例 - 移除焦点样式 */
*:focus { outline: none; }

/* 正确示例 - 自定义焦点样式 */
*:focus-visible {
  outline: 2px solid #3b82f6;
  outline-offset: 2px;
}

/* Tailwind CSS示例 */
.btn {
  @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2;
}

5. Non-semantic HTML

5. 非语义化HTML

tsx
// Bad
<div onClick={handleClick}>Click me</div>

// Good
<button onClick={handleClick}>Click me</button>

// Bad
<div className="header">...</div>

// Good
<header>...</header>
tsx
// 错误示例
<div onClick={handleClick}>点击我</div>

// 正确示例
<button onClick={handleClick}>点击我</button>

// 错误示例
<div className="header">...</div>

// 正确示例
<header>...</header>

6. Missing ARIA for Dynamic Content

6. 动态内容缺少ARIA属性

tsx
// Loading state
<button disabled aria-busy="true">
  <span className="sr-only">Loading</span>
  <Spinner aria-hidden="true" />
</button>

// Live region for updates
<div aria-live="polite" aria-atomic="true">
  {message && <p>{message}</p>}
</div>

// Modal
<div
  role="dialog"
  aria-modal="true"
  aria-labelledby="modal-title"
>
  <h2 id="modal-title">Dialog Title</h2>
</div>
tsx
// 加载状态
<button disabled aria-busy="true">
  <span className="sr-only">加载中</span>
  <Spinner aria-hidden="true" />
</button>

// 实时更新区域
<div aria-live="polite" aria-atomic="true">
  {message && <p>{message}</p>}
</div>

// 模态框
<div
  role="dialog"
  aria-modal="true"
  aria-labelledby="modal-title"
>
  <h2 id="modal-title">对话框标题</h2>
</div>

7. Skip Link

7. 跳转到主内容链接

tsx
// Add as first focusable element
<a
  href="#main-content"
  className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:px-4 focus:py-2 focus:bg-white"
>
  Skip to main content
</a>

<main id="main-content">
  ...
</main>
tsx
// 作为页面第一个可获取焦点的元素
<a
  href="#main-content"
  className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:px-4 focus:py-2 focus:bg-white"
>
  跳转到主内容
</a>

<main id="main-content">
  ...
</main>

Screen Reader Only Class

仅屏幕阅读器可见类

css
/* Visually hidden but accessible */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Show on focus (for skip links) */
.sr-only-focusable:focus {
  position: static;
  width: auto;
  height: auto;
  padding: inherit;
  margin: inherit;
  overflow: visible;
  clip: auto;
  white-space: normal;
}
css
/* 视觉隐藏但屏幕阅读器可访问 */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* 焦点时显示(用于跳转链接) */
.sr-only-focusable:focus {
  position: static;
  width: auto;
  height: auto;
  padding: inherit;
  margin: inherit;
  overflow: visible;
  clip: auto;
  white-space: normal;
}

Keyboard Testing Checklist

键盘测试清单

  1. Tab through page - Logical order?
  2. Enter/Space on buttons - Activates?
  3. Arrow keys in menus - Navigates?
  4. Escape - Closes modals/dropdowns?
  5. Focus visible - Always visible?
  6. No traps - Can tab out of all components?
  1. 按Tab键遍历页面 - 顺序符合逻辑吗?
  2. 按钮按Enter/Space键 - 能触发功能吗?
  3. 菜单中按方向键 - 能导航吗?
  4. 按Escape键 - 能关闭模态框/下拉菜单吗?
  5. 焦点可见性 - 是否始终可见?
  6. 无陷阱 - 能从所有组件中跳出吗?

Common ARIA Patterns

常见ARIA模式

tsx
// Tabs
<div role="tablist">
  <button role="tab" aria-selected="true" aria-controls="panel-1">Tab 1</button>
  <button role="tab" aria-selected="false" aria-controls="panel-2">Tab 2</button>
</div>
<div role="tabpanel" id="panel-1">Content 1</div>
<div role="tabpanel" id="panel-2" hidden>Content 2</div>

// Accordion
<button aria-expanded="true" aria-controls="content-1">Section 1</button>
<div id="content-1">Content</div>

// Menu
<button aria-haspopup="menu" aria-expanded="false">Options</button>
<ul role="menu" hidden>
  <li role="menuitem">Option 1</li>
  <li role="menuitem">Option 2</li>
</ul>

// Alert
<div role="alert">Error: Please fix the form</div>

// Progress
<div role="progressbar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100">
  50%
</div>
tsx
// 标签页
<div role="tablist">
  <button role="tab" aria-selected="true" aria-controls="panel-1">标签页1</button>
  <button role="tab" aria-selected="false" aria-controls="panel-2">标签页2</button>
</div>
<div role="tabpanel" id="panel-1">内容1</div>
<div role="tabpanel" id="panel-2" hidden>内容2</div>

// 手风琴
<button aria-expanded="true" aria-controls="content-1">章节1</button>
<div id="content-1">内容</div>

// 菜单
<button aria-haspopup="menu" aria-expanded="false">选项</button>
<ul role="menu" hidden>
  <li role="menuitem">选项1</li>
  <li role="menuitem">选项2</li>
</ul>

// 提示框
<div role="alert">错误:请修复表单</div>

// 进度条
<div role="progressbar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100">
  50%
</div>

Tools

工具列表

  • axe DevTools - Browser extension
  • WAVE - Browser extension
  • Lighthouse - Built into Chrome
  • NVDA - Free Windows screen reader
  • VoiceOver - Built into macOS (Cmd+F5)
  • Color Contrast Analyzer - Desktop app
  • axe DevTools - 浏览器扩展
  • WAVE - 浏览器扩展
  • Lighthouse - Chrome内置工具
  • NVDA - 免费Windows屏幕阅读器
  • VoiceOver - macOS内置工具(Cmd+F5开启)
  • Color Contrast Analyzer - 桌面应用