可访问性红线是工程中不可逾越的强制规则:违反即CI/CD失败、代码禁止合入,如role="button"未配keydown监听、input缺失label、blockquote误加role="note"等,须由eslint-plugin-jsx-a11y与axe-core等工具在开发阶段精准拦截并定位到行号。

大型HTML工程里,可访问性不是上线前补的“附加题”,而是必须从第一天就卡死的硬性边界——没设红线,后期修复成本会指数级上涨,甚至重构。
什么是可访问性红线?
它不是 checklist,而是工程中不可逾越的强制规则:一旦违反,CI/CD 流水线直接失败,代码无法合入主干。比如 role="button" 出现在非 button 元素上却没实现 keydown 监听、input 缺失 label 或 aria-label、blockquote 被误加 role="note" —— 这些都该被工具拦截。
- 红线必须可检测:靠人工 review 无法覆盖大型项目,得靠 linter + axe-core + 自定义 AST 规则
- 红线必须可定位:报错信息要精确到文件、行号、DOM 路径,不能只说“存在 a11y 问题”
- 红线必须分层级:P0(阻断合并)如缺失
lang属性或表单控件无标签;P1(警告但不阻断)如tabindex="1"
如何用 ESLint + axe-core 设 P0 红线
别只依赖运行时扫描。把可访问性检查前置到开发阶段,用 eslint-plugin-jsx-a11y 和自定义 rule 拦截高危写法:
-
jsx-a11y/label-has-associated-control:强制label必须通过htmlFor或嵌套关联input - 自定义 rule 检测
role="button"是否配了onKeyDown处理Enter/Space - 禁用
aria-label在div上直接使用(除非有明确role),因为屏幕阅读器会忽略无语义容器的 aria-label - 对
img强制要求alt非空,且禁止alt=""出现在非装饰性图上(用eslint-plugin-jsx-a11y的alt-text规则)
为什么 blockquote 不能加 role="note" 是条真红线
这不是风格偏好,是破坏原生语义的硬伤:blockquote 已有明确的 AT(辅助技术)暴露逻辑,加 role="note" 会覆盖其默认 role,导致屏幕阅读器跳过引用上下文、丢失引述来源信息。更糟的是,某些旧版 NVDA 会因此完全静音该元素。
立即学习“前端免费学习笔记(深入)”;
- 真正要表达“备注”语义,用
aside+aria-label或section+h2 - 所有带破折号、斜体、缩进的文字,先问一句:“它是否在复述他人原话并标明出处?”——不是就别用
blockquote - CI 中加入 axe CLI 扫描,配置
--rules=region,landmark-one等基础规则,但关键要禁用aria-allowed-role对blockquote的宽松校验,改用自定义脚本校验其子树中是否出现role属性
动态组件的焦点陷阱怎么设成红线
模态框、下拉菜单、标签页这些组件,焦点逃逸是高频可访问性事故点。红线不是“建议加 focus()”,而是“没实现焦点陷阱(focus trap)就禁止渲染”。
- 所有
role="dialog"必须在挂载后立即将焦点移入,且在关闭前将焦点返回触发元素 —— 这要用 React 的useEffect或自定义 hook 封装,禁止手写重复逻辑 - 组件库中每个可展开区域(如
details替代方案)必须导出useFocusTraphook,并在文档中标明“未调用即视为未通过 a11y 审查” - 自动化测试中用 Puppeteer + axe-core,在打开模态框后执行
document.activeElement断言,失败即 CI 报错
最难守的不是技术实现,是让所有人接受:可访问性红线没有“例外”。哪怕 PM 说“这个弹窗下周就下线”,只要它在线上,就必须满足 aria-modal="true" 和焦点管理——临时逻辑最容易成为长期债务。



















