真正有帮助的 ARIA 标签开发者指南
ARIA 标签不是一层神奇的无障碍能力。用得好,它们能让控件更容易理解。随意使用,则会隐藏有用文本,并造成令人困惑的界面。
目录
ARIA 标签用于命名,而不是补救
ARIA 很有用,但它经常被用来修补不清晰的 HTML。团队往往正是在这里遇到麻烦。
最常见的例子是 aria-label。它看起来无害:添加一个字符串,满足 linter,然后继续。但可访问名称不是装饰。它是许多辅助技术在用户按按钮、链接、表单字段、标题、地标和控件导航时向用户呈现的名称。
如果这个名称含糊、重复、过时,或与可见标签不同,界面就会变得更难使用。有时更糟:aria-label 可能会覆盖 DOM 中已经存在的更好文本。
目标不是添加更多 ARIA。目标是让每个界面元素的名称、角色、状态和用途都清晰明确。
用简单的话理解可访问名称
大多数交互元素都有一个可访问名称。屏幕阅读器使用这个名称来朗读该元素是什么。
例如:
<button>Save changes</button>
屏幕阅读器可能会朗读类似:“Save changes, button。”角色来自原生 button 元素。名称来自其中的文本。
这是理想情况:可见文本与可访问名称一致。
当可见界面没有提供完整名称,或名称必须来自另一个元素时,ARIA 标注属性就会有用。主要属性包括:
aria-label:直接在元素上提供一个字符串。aria-labelledby:指向一个或多个元素,这些元素的文本会成为名称。aria-describedby:指向辅助说明文本,而不是主要名称。
这三者相关,但不可互换。
第一条规则:优先使用原生 HTML 和可见标签
如果可以在控件上放置可见文本,请先这样做。
这样更好:
<button>Delete invoice</button>
而不是这样:
<button aria-label="Delete invoice">
<svg aria-hidden="true" focusable="false">...</svg>
</button>
第二种模式对于仅图标按钮是有效的。但如果设计可以容纳可见文本,可见文本会帮助所有人:屏幕阅读器用户、语音识别用户、认知负荷较高的人、快速浏览的人,以及使用翻译工具的人。
这是无障碍工作中反复出现的主题。原生 HTML 和可见的可操作提示比隐藏的元数据能解决更多问题。同样的原则也适用于更广义的按钮语义;如果你的团队正在审查 UI 控件,我们的无障碍网页按钮检查清单是本指南的良好补充。
什么时候 aria-label 是正确工具
当元素需要可访问名称,并且没有合适的可见文本可供引用时,使用 aria-label。
经典场景是仅图标按钮:
<button aria-label="Search">
<svg aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<!-- icon -->
</svg>
</button>
这是合理的。可见图标暗示搜索,但 SVG 路径本身并不能提供可靠名称。aria-label 提供了这个名称。
其他合适场景包括:
- 仅用“X”表示的关闭按钮。
- 需要更具体名称的导航地标,例如
aria-label="Product"。 - 可见上下文不属于按钮文本的一组重复控件。
例如:
<nav aria-label="Primary">
...
</nav>
<nav aria-label="Footer">
...
</nav>
两者都是导航地标,但它们的标签能帮助用户在按地标移动时区分它们。
什么时候 aria-label 是错误工具
不要只是因为测试说某个元素需要标签,就添加 aria-label。请先修正标记。
不好:
<div role="button" tabindex="0" aria-label="Submit">Submit</div>
更好:
<button>Submit</button>
第一个示例制造了不必要的工作。你现在必须重新实现键盘行为、禁用状态、表单行为,以及原生按钮已经提供的预期语义。
也要避免用 aria-label 以改变含义的方式重命名可见文本。
<button aria-label="Delete invoice">Remove</button>
这看似细微,但可能会让依赖语音输入的用户感到困惑。如果一个可见按钮写着“Remove”,但它的可访问名称是“Delete invoice”,用户尝试说“click Remove”时,可能得不到预期结果。WCAG 的“label in name”要求正是为此而存在:可见文本通常应包含在可访问名称中。
更好的版本:
<button aria-label="Remove invoice">Remove</button>
通常更好的做法仍然是:
<button>Remove invoice</button>
当可见文本已经存在时,优先考虑 aria-labelledby
如果标签文本已经在页面上,aria-labelledby 通常比 aria-label 更好。
示例:
<h2 id="billing-title">Billing address</h2>
<section aria-labelledby="billing-title">
...
</section>
该 section 的可访问名称现在来自可见标题。你避免了重复字符串,从而减少翻译错误和过时标签。
这对表单分组尤其有用:
<fieldset aria-labelledby="shipping-speed-title">
<legend id="shipping-speed-title">Shipping speed</legend>
<label>
<input type="radio" name="shipping" value="standard">
Standard
</label>
<label>
<input type="radio" name="shipping" value="express">
Express
</label>
</fieldset>
在许多情况下,原生 legend 已经足够,无需 ARIA。重点是可见标签应当优先。ARIA 应连接已有含义,而不是创建第二套私有版本。
用 aria-describedby 表示帮助文本,而不是名称
描述不是标签。
看看这个字段:
<label for="password">Password</label>
<input id="password" type="password" aria-describedby="password-help">
<p id="password-help">Use at least 12 characters.</p>
可访问名称是“Password”。描述是“Use at least 12 characters.” 屏幕阅读器可能会同时朗读二者,但它们服务于不同目的。
不要这样做:
<input type="password" aria-label="Use at least 12 characters">
这会用说明来命名字段,而不是用概念来命名。用户浏览表单时,首先想知道字段是什么,然后才是有哪些约束。
这种区分在错误状态中同样重要:
<label for="email">Email</label>
<input
id="email"
type="email"
aria-invalid="true"
aria-describedby="email-error"
>
<p id="email-error">Enter an email address in the format [email protected].</p>
标签保持稳定。错误消息成为辅助上下文。
重复控件需要唯一名称
列表和卡片是 ARIA 标签经常变得必要的地方。
不好:
<button>Delete</button>
<button>Delete</button>
<button>Delete</button>
屏幕阅读器用户按按钮导航时,可能会听到三次“Delete, button”,却没有任何上下文。
好:
<button aria-label="Delete report: Q4 revenue">Delete</button>
<button aria-label="Delete report: Hiring plan">Delete</button>
<button aria-label="Delete report: Vendor list">Delete</button>
这是 aria-label 的合法用法:可见文本保持简洁,而可访问名称包含对象。
但要谨慎使用这种模式。如果对象名称在附近可见,aria-labelledby 可能更易维护:
<article>
<h3 id="report-q4">Q4 revenue</h3>
<button aria-labelledby="delete-q4 report-q4" id="delete-q4">Delete</button>
</article>
可访问名称变为“Delete Q4 revenue”。这避免了在属性中重复报告标题。
不要给所有东西都加标签
并非每个元素都需要 ARIA 标签。
静态文本通常不需要。装饰性图标不需要。容器也不需要,除非它们具有有意义的地标或 widget 角色。过度标注会让页面变得嘈杂,并更难导航。
对于图片,请使用图片专用模型:有意义的图片需要有用的 alt;装饰性图片需要空的 alt=""。不要用 ARIA 标签替代良好的图片文本。如果你的团队混淆了这些概念,请重新阅读务实的图片 alt 文本指南,并将图片替代文本与控件名称区分开。
一个常见错误是给每个 SVG 都添加 aria-label。如果 SVG 位于按钮内部,而按钮已经有名称,该图标通常应对辅助技术隐藏:
<button aria-label="Open menu">
<svg aria-hidden="true" focusable="false">...</svg>
</button>
否则,根据浏览器和辅助技术组合的不同,用户可能会听到重复或奇怪的朗读。
检查计算后的名称,而不只是代码
无障碍缺陷经常在代码审查中漏掉,因为标记看起来似乎合理。
现代浏览器开发者工具可以显示计算后的 accessibility tree。在 Chrome、Edge、Firefox 和 Safari 中,检查元素并查找角色、名称和描述等无障碍信息。你要检查三件事:
- 角色是否符合预期?
- 可访问名称是否清晰且具体?
- 描述是否有帮助,并且没有取代名称?
然后用真实屏幕阅读器测试几个流程。你不需要成为全职辅助技术专家,也能发现基础问题。在 macOS 上,VoiceOver 是内置的。在 Windows 上,NVDA 被广泛使用且免费。在移动端,相关场景下请使用 iOS 上的 VoiceOver 和 Android 上的 TalkBack 进行测试。
自动化工具很有用,但它们无法可靠判断“Open”、“Read more”或“Delete”是否具有足够上下文。把自动化当作一张网,而不是裁判。这类似于性能审计:报告可以指出可疑区域,但你仍需解读影响。我们建议在不惊慌地阅读 Lighthouse 报告时采用的同样冷静方法,也适用于这里。
实用审查清单
在发布 ARIA 标签之前,请先询问:
- 这是否可以改用原生 HTML?
- 是否有应当用作标签的可见文本?
- 如果存在可见文本,可访问名称是否包含它?
- 重复控件在脱离视觉上下文导航时是否仍然唯一?
- 帮助文本是否通过
aria-describedby关联,而不是被强行放进标签? - 装饰性图标是否已对辅助技术隐藏?
- 是否有人在浏览器开发者工具中检查过计算后的可访问名称?
- 关键流程是否至少用真实屏幕阅读器走查过一次?
这份清单能在大多数标签问题变成用户问题之前捕捉到它们。
良好 ARIA 的安静纪律
好的 ARIA 工作很少惊天动地。它主要是一种克制。
使用真正的按钮。使用真正的标签。让可见名称和可访问名称保持一致。只有在没有更好的可见来源时,才添加 aria-label。当页面已经包含正确文本时,使用 aria-labelledby。使用 aria-describedby 提供辅助说明和错误信息。
当我们直接使用 Web 平台时,它会免费提供许多能力。ARIA 是为填补空白而存在的。真正的能力在于知道什么时候确实存在空白。