必须将可访问性检查嵌入每轮迭代提交前校验:HTMLHint启用alt-require等规则拦截结构问题,axe在E2E扫描运行时DOM,<main>唯一性、标题层级连续性、lang白名单须纳入DoD并强制PR检查。

不能靠“后期补”或“靠测试卡”,必须把可访问性检查塞进每轮迭代的提交前校验环节——否则每次 sprint 结束时,都会堆积一堆 alt 缺失、<main></main> 嵌套、标题断裂的硬伤,修复成本呈指数上升。
HTMLHint 配置必须显式启用可访问性规则
默认不生效是最大陷阱。装了插件 ≠ 自动拦截问题,.htmlhintrc 里没写 "alt-require": true 和 "input-requires-label": true,就等于没开开关。
-
alt-require会拒绝所有未带alt的<img>,包括 SVG 内联图(<svg>不算,但<img src="icon.svg">必须有) -
input-requires-label只认原生语义:要么<label><input></label>,要么<label for="id"></label>+<input id="id">,大小写严格匹配 - 它不接受
aria-label或title替代,这不是缺陷,是设计选择——强制你用对的标签,而不是绕路打补丁
CI 流程里 HTMLHint 和 axe 分工要清晰
HTMLHint 查源码结构,axe 查运行时 DOM;一个拦在代码提交前,一个卡在测试环境启动后。混用或只用其一,必然漏掉一半问题。
- HTMLHint 在
pre-commit或 CI 的 lint 阶段运行,报错直接阻断构建:<section>没标题、<main>出现两次、<html lang="">值非法(如lang="zh") - axe 在 E2E 测试启动后扫描真实 DOM,能捕获 JS 动态插入的
<img>没补alt、模态框打开后焦点没落到首个可聚焦元素、aria-live区域未声明等场景 - axe 报出的问题标记为“需人工确认”,比如某张图是否真为装饰性——工具无法判断上下文,人得看业务逻辑
<main> 和标题层级必须作为准入卡点
这两项不是“建议”,是屏幕阅读器能否正确导航的物理边界。它们一旦出错,用户根本找不到正文,所有后续优化都失效。
立即学习“前端免费学习笔记(深入)”;
-
<main>必须是<body>的**直接子元素**,且全局唯一;React/Vue 组件中意外输出<main>(比如布局组件和页面组件各 render 一个),就会触发 axe 的landmark-is-uniquecritical 错误 - 标题层级必须连续:允许
<h2>开头,但不能<h2>后直接<h4>;CSS 隐藏的标题(display: none)仍算在层级里,必须删 DOM 或加aria-hidden="true" - 多语言 SSR 页面中,
<html lang="{{ locale }}">的{{ locale }}必须白名单校验,否则产出lang="ch"或lang="zh",NVDA 会 fallback 到英文朗读,中文内容完全不可理解
语义化不是“锦上添花”,而是减少 JS 补救的刚需
当 <input> 没绑定 <label>,后期就得用 JS 监听焦点、手动播报提示;当图片缺 alt,就得靠 runtime 注入 ARIA 属性——这些逻辑不仅增加 bundle 体积,还引入竞态和维护黑洞。
- 用
<button>代替<div role="button" tabindex="0">,浏览器自带 focus/active 状态、空格回车触发、键盘导航支持 -
<figure><img><figcaption></figure>是图片语义闭环,比单独<img>+ 外部<p>更可靠,SEO 和读屏都认 - 所有表单控件必须显式关联
<label>,哪怕视觉上用 CSS 对齐,DOM 结构也必须满足语义要求——样式可变,结构不能妥协
真正卡住团队的,从来不是“要不要做”,而是“谁在哪个环节负责哪条规则”。把 lang 格式、<main> 唯一性、标题连续性这三件事写进每个 story 的 Definition of Done,并在 PR 模板里加 checklist,才能让可访问性从口号变成每天敲出来的代码。



















