CI 中无障碍
在构建管道自动 a11y 检查使回归在 PR 抓非发布后。
为什么
无障碍静默回归——开发者加 <div onClick> 而非 <button>、设计师选不过对比的新灰、重构移 aria-label——这些都不被功能测试抓。到有人跑手动 a11y 审计时(如果有,年度),应用累积 50 违规,修是大努力。CI 自动在 PR 抓回归: axe-core 每页跑、jsx-a11y 每文件 lint、引入新违规则坏构建。这把 a11y 从周期慌乱变维护基线——同测试和 lint 为 bug 和代码质量提供的左移。成本是每 PR 几分钟 CI 时间;收益是从不累积 a11y 债的应用。
何时使用
从项目第一天。首 PR 前加 axe-core 和 jsx-a11y 到 CI,使 a11y 是基线非追溯。
如何做
1. 加 eslint-plugin-jsx-a11y lint 时检查 - 写时在编辑器 PR 前抓问题 - 规则: alt-text、aria-role、label-has-associated-control、no-static-element-interactions - 比 axe-core 快(无需浏览器),抓约 20% 问题 2. 加 axe-core 到 Playwright 集成测试 - 导航到每页后跑,断言无违规 - withTags(["wcag2a", "wcag2aa"]) A 级和 AA - 用 withRules 禁假阳性规则带记录原因 - 抓约 30% 问题含 jsx-a11y 漏的 3. 加 Lighthouse a11y 审计到 CI - lighthouse-ci 查部署预览上 a11y 分 - 设最低分(如 90)降则坏构建 - 抓渲染页对比、alt 和标签问题 4. 处理违规 - 修违规(首选——代码错) - 或带记录原因禁规则(假阳性) - 永不全局禁——每元素禁带注释解释为何 5. 随时间追踪 a11y 分 - 每 PR 贴 Lighthouse a11y 分(如测试覆盖徽章) - 降超 5 分告警 - 回顾审查分趋势
示例
// 1. eslint-plugin-jsx-a11y(lint 时)
// .eslintrc.json
{
"extends": ["plugin:jsx-a11y/recommended"],
"rules": {
"jsx-a11y/no-autofocus": "error",
"jsx-a11y/click-events-have-key-events": "error",
"jsx-a11y/no-static-element-interactions": "error"
}
}
// 2. Playwright 中 axe-core(集成)
import { test, expect } from "@playwright/test";
import AxeBuilder from "@axe-core/playwright";
const pages = ["/", "/products", "/cart", "/checkout", "/account"];
for (const path of pages) {
test(`${path} 无 a11y 违规`, async ({ page }) => {
await page.goto(path);
const results = await new AxeBuilder({ page })
.withTags(["wcag2a", "wcag2aa"])
.disable(["color-contrast"]) // 禁带原因: 单独测
.analyze();
expect(results.violations).toEqual([]);
});
}
// 3. Lighthouse CI
// lighthouserc.json
{
"ci": {
"collect": {
"url": ["https://staging.example.com/", "https://staging.example.com/checkout"]
},
"assert": {
"assertions": {
"categories:accessibility": ["error", { "minScore": 0.9 }]
}
}
}
}
// 4. 处理假阳性(每元素禁非全局)
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
// 原因: 此 div 是拖放放置区,通过单独按钮键盘可访问。见 issue #1234。
<div onDrop={handleDrop} />
// 5. CI 工作流
// .github/workflows/a11y.yml
name: 无障碍
on: [pull_request]
jobs:
a11y:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx eslint . # jsx-a11y 规则
- run: npx playwright test # axe-core 检查
- run: npx lhci autorun # Lighthouse a11y 分常见问题
- ⚠加 axe-core 到 CI 但禁每失败规则使构建过——检查跑但抓无,团队信 a11y"已处理"而违规累积。
- ⚠仅首页跑 axe-core 漏结账、账户和管理页——首页过,但结账流程(用户花钱处)有 15 违规发到生产。
- ⚠Lighthouse a11y 分阈值设 50"开始"永不提——分留 50,应用半无障碍,阈值无改进压力。
- ⚠把 axe-core 违规当唯一 a11y 工作——CI 过但无人做键盘或屏幕阅读器测试,axe 不能抓的 70% 问题发到生产。
最佳实践
- ✓第一天加 jsx-a11y(lint)+ axe-core(集成)+ Lighthouse(分)到 CI——分层自动化在 PR 抓机械问题,手动测试在发布前抓其余。
- ✓每页跑 axe-core 非仅首页—— 建页列表循环,使结账和账户页与着陆页同严查。
- ✓永不全局禁规则——每元素禁带注释解释原因并链 issue,使禁在审查可见且底层问题修时可移。
- ✓把 CI a11y 检查当地板非天花板——抓 30% 问题,另 70% 需每发布前安排的手动键盘和屏幕阅读器测试。