@ accesslint/ 核心
纯无障碍规则引擎,零浏览器依赖性. 包括具有最佳做法规则的WCAG 2.2 A级和AA级.
与AI编码代理?
@accesslint/mcp将此引擎包成模式背景协议服务器。 这个 Claude 代码插件 安装在一步.
找CI吗? 访问提示 对每个拉动请求进行无障碍检查,并直接在您的 GitHub PRs 上发布评论.
目录
为什么 @ accesslint/ core
- 同步 API —— 说
runAudit()立即返回结果,不需要同步/等待 - 与快乐的境界合作 ——全力支持快乐度,jsdom,以及没有多填或工作环的正版浏览器,包括虚拟 DOMs 中的色彩对比检查
- 轻巧 - 46 KB gzipped(IIFE),零运行时间依赖性
- 中断审计 ——按时间编制预算的处理方式
createChunkedAudit以避免主线条上的长任务 - 源头映射( 反应) ——选入
attachReactFiberSource行走 React DevTools 纤维并解决被捆绑的通克 URL 通过其源图,因此违规指向真实源文件(src/Card.tsx:42:7) 而不是不透明的选择器 - 无害环境管理、CJS和IIFE – 树可摇动的 ES 模块,用于节点的 CommonJS 和用于脚本注入任意一页的单文件 IIFE
安装
npm install @accesslint/core
快开始
Vitest + 反应测试库
审计您现有测试套件中已实现的组件 :
import { render } from "@testing-library/react";
import { runAudit } from "@accesslint/core";
import { LoginForm } from "./LoginForm";
test("LoginForm has no accessibility violations", () => {
const { container } = render(<LoginForm />);
const { violations } = runAudit(container.ownerDocument);
expect(violations).toEqual([]);
});
剧作家
将库输入页面并审计实时 DOM :
// a11y.spec.ts
import { test, expect } from "@playwright/test";
const iife = require.resolve("@accesslint/core/iife");
test("page has no accessibility violations", async ({ page }) => {
await page.goto("https://example.com");
await page.addScriptTag({ path: iife });
const violations = await page.evaluate(() => {
const { runAudit } = (window as any).AccessLint;
return runAudit(document).violations.map((v: any) => ({
ruleId: v.ruleId,
message: v.message,
selector: v.selector,
impact: v.impact,
}));
});
expect(violations).toEqual([]);
});
齐压
将库输入页面并审计实时 DOM :
// cypress/e2e/a11y.cy.js
Cypress.Commands.add("audit", () => {
return cy.readFile("node_modules/@accesslint/core/dist/index.iife.js").then((src) => {
return cy.window().then((win) => {
win.eval(src);
const result = win.AccessLint.runAudit(win.document);
return result.violations;
});
});
});
describe("sample.html accessibility audit", () => {
beforeEach(() => {
cy.visit("sample.html");
});
it("has no accessibility violations", () => {
cy.audit().should("have.length", 0);
});
});
API 密码
runAudit(doc: Document, options?: AuditOptions): AuditResult
运行所有针对文件的有效规则并返回违反 。 所有选择都是按要求进行的,不重复——没有触及全球状况.
interface AuditOptions {
additionalRules?: Rule[];
disabledRules?: string[];
includeAAA?: boolean;
componentMode?: boolean;
locale?: string;
}
interface AuditResult {
url: string;
timestamp: number;
testEngine: TestEngine;
testEnvironment: TestEnvironment;
violations: Violation[];
ruleCount: number;
skippedRules: { ruleId: string; error: string }[];
}
interface TestEngine {
name: "accesslint";
version: string;
}
interface TestEnvironment {
userAgent: string;
windowWidth: number;
windowHeight: number;
orientationAngle: number;
orientationType: string;
}
testEngine.version 这是 @accesslint/core 引擎版本. testEnvironment 描述已审计文档的域;不可读值返回到 "", (中文(简体) ), 0,以及 "portrait-primary"。 。 。 .
interface Violation {
ruleId: string;
selector: string;
html: string;
impact: "critical" | "serious" | "moderate" | "minor";
message: string;
context?: string;
element?: Element;
/** Source-code locations for the violating element, when populated by `attachReactFiberSource`. */
source?: SourceLocation[];
}
interface SourceLocation {
file: string;
line: number;
column?: number;
/** Component or function name, when known (e.g. "ProductCard"). */
symbol?: string;
/** 0 = JSX literal that produced the element; 1+ = enclosing component(s). */
ownerDepth: number;
}
version: string
引擎版本, 镜像 axe.version. 也作为 AccessLint.version 在独立的全球(IIFE)上.
import { version } from "@accesslint/core";
createChunkedAudit(doc: Document, options?: AuditOptions): ChunkedAudit
创建一个块式审计,在有时间框的批次中处理规则,以避免长的任务.
const audit = createChunkedAudit(document, { includeAAA: true });
function processNext() {
const hasMore = audit.processChunk(16); // 16ms budget per frame
if (hasMore) requestAnimationFrame(processNext);
else console.log(audit.getViolations());
}
processNext();
rules
所有捆绑的阵列 Rule 对象.
getActiveRules(options?: AuditOptions): Rule[]
返回被给定选项过滤的捆绑规则:排除AAA级规则,除非 includeAAA 设置,在 componentMode ,不包括在 disabledRules附录 additionalRules时 locale 通过,返回带有翻译描述和指导的浅层规则.
getRuleById(id, options?: { locale?: string; additionalRules?: Rule[] }): Rule | undefined
查查身份证明 过 locale 获取翻译规则,或 additionalRules 还搜索自定义规则.
attachReactFiberSource(violations): Promise<void>
附加的页后处理器 source 每个地点
通过行走回放纤维来破坏 非反应页面上的禁用,生产建设,
或无纤维时,永不抛出。 要求联合来文X __source 转换
和 dev sourcemaps(默认在 CRA, Next dev, Vite + React 中).
当纤维携带 _debugSource 它被信任为现实;
否则 _debugStack (例如,Next.js dev)通过
块 .js.map。 。 。 .
限制 : 编辑
_debugSource路径, dev 服务器 每个单元的序言(尤其是Vite,反应18和19)抵消所报告的 行/列——文件和组件保持正确,但行可以指向 比真源还低 这个_debugStack路径不受影响.
// In a Playwright test, after the audit:
const violations = await page.evaluate(async () => {
const { runAudit, attachReactFiberSource } = (window as any).AccessLint;
const result = runAudit(document);
await attachReactFiberSource(result.violations);
return result.violations;
});
console.log(violations[0].source);
// [{ file: "src/components/ProductCard.tsx", line: 42, column: 7, symbol: "ProductCard", ownerDepth: 0 }, ...]
公用事业
制定定制规则的助手:
getAccessibleName(el)——计算一个元素的可访问名称getComputedRole(el)得到计算 ARIA 角色getImplicitRole(el)——获得隐含(内在)ARIA角色isAriaHidden(el)——检查元素是否通过aria-hiddenisValidRole(role)——检查字符串是否是一个有效的 ARIA 角色getAccessibleTextContent(el)——获得文本内容尊重aria-hiddengetSelector(el)——为元素生成 CSS 选择器getHtmlSnippet(el)—— 获取元素的简略 HTML 片断
规则
包括WCAG 2.2 A级和AA级,外加最佳做法规则. 增加一个AAA级规则(distinguishable/color-contrast-enhanced捆绑但默认排除;通过 runAudit(doc, { includeAAA: true })。 。 。 .
规则 ID 匹配 ruleId 字段中的字段,并与 disabledRules 选项和 getRuleById()。 。 。 .
查看所有规则
| 规则 | 职等 | 西非咨询组 | 说明 |
|---|---|---|---|
navigable/document-title | A 类 | 2.4.2 | 文档必须有一个 <title> 键. |
navigable/bypass | A 类 | —— 说 | 页面必须有一个绕过重复块的机制. |
navigable/page-has-heading-one | A 类 | —— 说 | 页应包含一级标题. |
labels-and-names/frame-title | A 类 | 4.1.2 国家 | 框架必须有一个可访问的名称 . |
labels-and-names/frame-title-unique | A 类 | 4.1.2 国家 | 框架标题应当是独一无二的. |
distinguishable/meta-viewport | AA 对 | 1.4.4 (中文(简体) ) | Viewport 元不能禁用用户缩放 . |
enough-time/meta-refresh | A 类 | 2.2.1 联合国 | 元刷新不能自动重定向或刷新 . |
enough-time/meta-refresh-no-exception | A 类 | 2.2.1 联合国 | Meta刷新不能被延迟使用(没有例外). |
enough-time/blink | A 类 | 2.2.2 | <blink> 不得使用. |
enough-time/marquee | A 类 | 2.2.2 | <marquee> 不得使用. |
text-alternatives/img-alt | A 类 | 1.1.1 (中文(简体) ) | 图像必须有替代文本 . |
text-alternatives/svg-img-alt | A 类 | 1.1.1 (中文(简体) ) | SVG图像必须有一个可访问的名称. |
text-alternatives/input-image-alt | A 类 | 1.1.1, 4.1.2 (中文(简体) ) | 图像输入必须有替代文本 . |
text-alternatives/image-redundant-alt | A 类 | —— 说 | 图像备选案文不应重复相邻文本. |
text-alternatives/image-alt-words | A 类 | —— 说 | 备选文本不应包含"图像","相"等. |
text-alternatives/area-alt | A 类 | 1.1.1, 4.1.2 (中文(简体) ) | <area> 元素必须具有备选案文. |
text-alternatives/object-alt | A 类 | 1.1.1 (中文(简体) ) | <object> 元素必须具有备选案文. |
text-alternatives/role-img-alt | A 类 | 1.1.1 (中文(简体) ) | role="img" 元素必须有一个无障碍的名称. |
keyboard-accessible/server-image-map | A 类 | 2.1.1 国家 | 不得使用服务器侧图像映射 . |
labels-and-names/form-label | A 类 | 4.1.2 国家 | 形式元素必须具有标签. |
labels-and-names/multiple-labels | A 类 | —— 说 | 窗体字段不应有多个标签 . |
labels-and-names/input-button-name | A 类 | 4.1.2 国家 | 输入按钮必须有可识别文本 . |
adaptable/autocomplete-valid | AA 对 | 1.3.5 国家 | 自动完整属性必须使用有效值. |
labels-and-names/label-content-mismatch | A 类 | —— 说 | 可访问名称必须包含可见文本 . |
labels-and-names/label-title-only | A 类 | —— 说 | 表格不应使用标题作为唯一的标签. |
keyboard-accessible/tabindex | A 类 | —— 说 | 选项卡索引不应大于 0. |
keyboard-accessible/focus-order | A 类 | —— 说 | 可重点要素必须发挥适当作用. |
keyboard-accessible/nested-interactive | A 类 | 4.1.2 国家 | 互动控制不能被嵌入. |
keyboard-accessible/scrollable-region | A 类 | 2.1.1 国家 | 可滚动区域必须是可用键盘. |
keyboard-accessible/accesskeys | A 类 | —— 说 | Accesskey 值必须是独一无二的 . |
keyboard-accessible/focus-visible | AA 对 | 2.4.7 国家 | 重点顺序要素必须有一个明显的重点指标. |
navigable/heading-order | A 类 | —— 说 | 头级应增加一等. |
navigable/empty-heading | A 类 | —— 说 | 标题必须具有明显的文字. |
navigable/p-as-heading | A 类 | —— 说 | 段落不应被称作标题. |
landmarks/landmark-main | A 类 | —— 说 | Page应该有一个主要的地标. |
landmarks/no-duplicate-banner | A 类 | —— 说 | 没有重复的横幅地标. |
landmarks/no-duplicate-contentinfo | A 类 | —— 说 | 没有重复的内容info地标 . |
landmarks/no-duplicate-main | A 类 | —— 说 | 没有重复的主要地标. |
landmarks/banner-is-top-level | A 类 | —— 说 | 班纳地标应该是顶层的. |
landmarks/contentinfo-is-top-level | A 类 | —— 说 | Contentinfo地标应为顶级. |
landmarks/main-is-top-level | A 类 | —— 说 | 主要地标应为上层. |
landmarks/complementary-is-top-level | A 类 | —— 说 | 边地标应该是顶层的. |
landmarks/landmark-unique | A 类 | —— 说 | 同类地标应当有独特的标签. |
landmarks/region | A 类 | —— 说 | 所有内容都应在里程碑内. |
adaptable/list-children | A 类 | 1.3.1 国家 | <ul> 财务报告和审定财务报表 <ol> 必须有有效的孩子. |
adaptable/listitem-parent | A 类 | 1.3.1 国家 | <li> 一定是在一个 <ul>, (中文(简体) ), <ol>,或 <menu>。 。 。 . |
adaptable/dl-children | A 类 | 1.3.1 国家 | <dt> 财务报告和审定财务报表 <dd> 一定是在一个 <dl>。 。 。 . |
adaptable/definition-list | A 类 | 1.3.1 国家 | <dl> 必须有有效的孩子. |
distinguishable/letter-spacing | AA 对 | 1.4.12 (中文(简体) ) | 字母间距 !important 必须是至少0.12毫米. |
distinguishable/line-height | AA 对 | 1.4.12 (中文(简体) ) | 线高与 !important 必须是至少1.5. |
distinguishable/word-spacing | AA 对 | 1.4.12 (中文(简体) ) | 文字间距 !important 必须是至少0.16毫米. |
adaptable/orientation-lock | AA 对 | 1.3.4 国家 | 不得限制页面取向. |
aria/aria-roles | A 类 | 4.1.2 国家 | ARIA角色价值必须有效. |
aria/aria-valid-attr | A 类 | 4.1.2 国家 | ARIA属性必须有效(正确拼写). |
aria/aria-valid-attr-value | A 类 | 4.1.2 国家 | ARIA属性必须具有有效的值. |
aria/aria-required-attr | A 类 | 4.1.2 国家 | 需要的ARIA属性必须存在. |
aria/aria-allowed-attr | A 类 | 4.1.2 国家 | 角色必须允许ARIA属性. |
aria/aria-allowed-role | A 类 | 4.1.2 国家 | ARIA的作用必须适合这一要素. |
adaptable/aria-required-children | A 类 | 1.3.1 国家 | ARIA的作用必须需要儿童的作用. |
adaptable/aria-required-parent | A 类 | 1.3.1 国家 | ARIA的作用必须是必要的父母角色. |
aria/aria-hidden-body | A 类 | 4.1.2 国家 | aria-hidden 绝对不能上 <body>。 。 。 . |
aria/aria-hidden-focus | A 类 | 4.1.2 国家 | aria-hidden 各区域不得含有可突出重点的内容. |
labels-and-names/aria-command-name | A 类 | 4.1.2 国家 | ARIA命令必须有一个无障碍的名称. |
labels-and-names/aria-input-field-name | A 类 | 4.1.2 国家 | ARIA 输入字段必须有一个可访问的名称. |
labels-and-names/aria-toggle-field-name | A 类 | 4.1.2 国家 | ARIA 切换字段必须有一个无障碍的名称. |
labels-and-names/aria-meter-name | A 类 | 4.1.2 国家 | ARIA仪表必须有一个无障碍的名称. |
labels-and-names/aria-progressbar-name | A 类 | 4.1.2 国家 | ARIA 进步栏必须有一个无障碍的名称. |
labels-and-names/aria-dialog-name | A 类 | 4.1.2 国家 | ARIA 对话框必须有一个无障碍的名称 . |
labels-and-names/aria-tooltip-name | A 类 | 4.1.2 国家 | ARIA工具提示必须有一个无障碍的名称. |
labels-and-names/aria-treeitem-name | A 类 | 4.1.2 国家 | ARIA 树条必须有一个无障碍的名称. |
aria/aria-prohibited-attr | A 类 | 4.1.2 国家 | 不得禁止该角色的ARIA属性. |
aria/presentation-role-conflict | A 类 | 4.1.2 国家 | 展示角色不得与可聚焦性相冲突. |
aria/presentational-children-focusable | A 类 | 4.1.2 国家 | 介绍性儿童不得含有可关注的内容. |
labels-and-names/button-name | A 类 | 4.1.2 国家 | 按钮必须有明显的文本. |
labels-and-names/summary-name | A 类 | 4.1.2 国家 | <summary> 元素必须有一个无障碍的名称. |
navigable/link-name | A 类 | 2.4.4, 4.1.2 | 链接必须有可识别的文本. |
navigable/skip-link | A 类 | 2.4.1 国家 | 跳过链接必须指向有效的目标 . |
distinguishable/link-in-text-block | A 类 | 1.4.1 国家 | 文本块中的链接必须比颜色更能区分. |
readable/html-has-lang | A 类 | 3.1.1 国家 | <html> 必须有一个 lang 属性. |
readable/html-lang-valid | A 类 | 3.1.1 国家 | lang 打开 <html> 必须是有效的. |
readable/valid-lang | AA 对 | 3.1.2 国家 | lang 属性必须对所有元素具有有效的值. |
readable/html-xml-lang-mismatch | A 类 | 3.1.1 国家 | lang 财务报告和审定财务报表 xml:lang 必须匹配. |
adaptable/td-headers-attr | A 类 | 1.3.1 国家 | 表格头引用必须有效. |
adaptable/th-has-data-cells | A 类 | 1.3.1 国家 | 表格头应与数据单元格相连接. |
adaptable/td-has-header | A 类 | 1.3.1 国家 | 大表格中的数据单元格应当有相联标题. |
adaptable/scope-attr-valid | A 类 | 1.3.1 国家 | scope 属性必须具有有效的值. |
adaptable/empty-table-header | A 类 | —— 说 | 表格头应该有可见的文本. |
labels-and-names/duplicate-id-aria | A 类 | 4.1.2 国家 | ARIA中使用的身份证必须是独一无二的. |
time-based-media/video-captions | A 类 | 1.2.2 国家 | 视频必须有标题. |
time-based-media/audio-transcript | A 类 | 1.2.1 国家 | 音频元素应有一个文本选项. |
distinguishable/color-contrast | AA 对 | 1.4.3 国家 | 文本必须有足够的颜色对比 . |
input-assistance/accessible-authentication | AA 对 | 3.3.8 国家 | 密码输入不得屏蔽密码管理器. |
distinguishable/color-contrast-enhanced | AAA 亚军 | 1.4.6 国家 | 文本必须具有增强的颜色对比(AAA). |
兼容性
在下列环境中试验:
| 环境 | 支助 |
|---|---|
| 节点.js 18+ | 对 |
| 快乐的境界 | 对 |
| 日记 | 对 |
| 颜色/边缘 | 对 |
| 火花 | 对 |
| 萨法里 | 对 |
发展
npm install
npm test # 1191 tests
npm run bench # performance benchmarks
npm run build # produces dist/index.js, dist/index.cjs, dist/index.d.ts, dist/index.iife.js
找到窃听器还是有什么建议? 打开一个问题。 。 。 .
许可证
麻省理工学院