必须显式绑定label与input,使用for/id配对或隐式包裹;fieldset+legend分组控件;错误提示用aria-invalid与aria-describedby关联;必填项设aria-required和required;确保键盘可访问性。

label 必须显式绑定 input,不能只靠视觉对齐
屏幕阅读器不会读取“看起来像标签”的文字,只认 label 与 input 的真实关联。常见错误是用 CSS 把文字放在输入框旁边,却不加任何语义连接——用户聚焦到 input 时,听到的可能是“编辑文本”而不是“邮箱地址”。
两种可靠方式:
- 用
for和id显式配对:<label for="email">邮箱</label><input type="email" id="email"> - 用隐式包裹(更简洁):
<label>邮箱<input type="email"></label>,无需维护 ID,且天然支持表单验证逻辑
禁用 aria-label 替代 label:它绕过浏览器原生验证、不触发 :valid/:invalid 伪类,且部分老旧辅助技术无法稳定读取。
fieldset + legend 是分组控件的唯一可靠方案
单选按钮或复选框组如果没有 fieldset 和 legend,屏幕阅读器会把每个选项当成孤立控件,用户完全不知道它们属于同一逻辑组(比如“付款方式”或“订阅偏好”)。
立即学习“前端免费学习笔记(深入)”;
正确结构示例:
使用 Puppeteer + Chrome 将 HTML 渲染为中文 PDF,自动处理图表等待、Tab 展开、动画、测高、白边消除、防分页,适用于看板、报表、网页和交互图表转 PDF。
<fieldset> <legend>您希望接收哪些通知?</legend> <input type="checkbox" id="email-notif" name="notif" value="email"> <label for="email-notif">邮件提醒</label> <input type="checkbox" id="sms-notif" name="notif" value="sms"> <label for="sms-notif">短信提醒</label> </fieldset>
注意:legend 必须是 fieldset 的第一个子元素;不要用 div + aria-label 模拟,那无法被所有屏幕阅读器识别为分组标题。
错误提示必须用 aria-invalid + aria-describedby 关联
仅靠视觉红框或弹窗提示,对键盘或屏幕阅读器用户无效。验证失败时,要同步操作三件事:
- 给
input设置aria-invalid="true" - 让错误消息有唯一
id(如error-username) - 在
input上添加aria-describedby="error-username"
这样屏幕阅读器会在用户聚焦该字段时,自动朗读错误说明。别把错误消息塞进 placeholder——它会在用户输入时消失,且不被所有辅助技术读取。
必填项要用 aria-required,别只靠星号
视觉上的 * 号对屏幕阅读器不可见。必须显式声明 aria-required="true",配合 required 属性(两者都设更稳妥)。但注意:aria-required 不触发浏览器原生验证,只是辅助技术提示;required 才真正阻止提交。
如果表单含动态字段(如条件显示),需用 JS 同步更新 aria-required 值,并确保焦点能落到首个错误字段——否则用户可能反复 Tab 却卡在不可见的必填项上。
最易被忽略的是键盘流完整性:所有交互控件(包括自定义下拉、开关)必须可 Tab 进入、用空格/回车触发,且焦点轮廓不能被 CSS 全局清除(outline: none 是大忌)。可访问性不是加几个 ARIA 就完事,而是整条键盘路径和语音反馈链都得跑通。


















