应优先使用原生语义化标签(如<a>、<label>),而非滥用ARIA;role="button"仅适用于无原生对应标签的场景,且需手动实现键盘交互与状态同步;语义结构决定读屏逻辑,错误嵌套或隐藏会直接破坏无障碍。

直接用 <a> 或 <label>,别硬套 ARIA —— 90% 的“无障碍可点击区域”问题,根源是语义错位,不是属性漏写。
为什么加 role="button" + tabindex="0" 反而更不无障碍
屏幕阅读器会把 role="button" 元素识别为独立按钮,但若它内部还有个真实链接(比如 <a href="/profile">),键盘用户 Tab 两次才能触发:第一次聚焦到伪按钮,第二次才到链接。读屏会重复播报“button”和“link”,违反 WCAG 单一可操作元素原则。
- 原生
<a>自带焦点、回车/空格触发、无障碍名称自动继承文本内容 - 原生
<label>包裹表单控件时,热区+语义+键盘支持全天然生效,无需任何 ARIA -
role="button"只应在真正无对应原生标签的交互场景中使用(如 Canvas 内点击区域),且必须手动实现Enter/Space监听和aria-pressed状态同步
卡片整块可点时,<a> 必须包裹标题,不能只包文字或加在 <article> 上
语义结构决定读屏逻辑:<article><h3><a href="/post/1">标题</a></h3></article> 被朗读为 “article, heading level 3, link, 标题”,上下文完整;而给 <article> 加 role="link" 或监听 click 后跳转,读屏只会说 “article”,无法关联内容主题。
- 跳转链接必须是语义化锚点,且包裹至少一个标题级元素(
<h2>–<h6>) - 视觉上想隐藏下划线?用
text-decoration: none,别删<a>标签本身 - 避免在
<a>外层再套<div onclick="...">—— 事件目标错位、冒泡中断、移动端热区被裁切都由此引发
<label> 扩大复选框/单选框热区时,三个条件缺一不可
<label> 映射热区不是靠 CSS 或 JS,而是浏览器对 DOM 结构的硬性解析。哪怕你把 <input type="checkbox"> 用 position: absolute 移出视口,只要它仍是 <label> 的直接子节点、未被 display: none 隐藏、且 <label> 有渲染尺寸,点击 label 任意位置仍有效。
立即学习“前端免费学习笔记(深入)”;
-
<input>必须是<label>的**直接子节点**(中间不能有<span>、<div>或 React Fragment) -
<input>不能被display: none、visibility: hidden、opacity: 0隐藏 -
<label>必须有可渲染尺寸:默认display: inline不占空间,需设display: inline-block或block+padding+min-height: 44px
图像热点(<map> + <area>)必须配 alt,否则无障碍直接失败
<area> 本身不渲染内容,屏幕阅读器完全依赖其 alt 属性提供可访问文本。没有 alt,该区域对视障用户就是“不可见、不可达、不可知”。
-
<img>的alt描述整图用途(如“中国省份分布图”),<area>的alt描述该区域功能(如“点击进入广东省详情页”) - 坐标系原点固定为图片左上角,CSS 缩放会破坏
coords对应关系 —— 若需响应式适配,必须用 JS 动态重算比例,不能只靠媒体查询 -
<area>不支持title属性做无障碍替代,title仅用于鼠标悬停提示,读屏不读
真正容易被忽略的是:无障碍不是加一堆 aria- 属性就能修好的。当结构语义断裂(比如用 div 模拟卡片、用 span 包裹复选框)、或视觉与 DOM 错位(比如 pointer-events: none 父容器、overflow: hidden 裁切热区)时,再多 ARIA 也补不回基础可访问性。先理清“这是什么”,再决定“怎么让它可操作”。



















