Dev Tools & Workflow

真正有帮助的 ARIA 标签开发者指南

ARIA 标签不是一层神奇的无障碍能力。用得好,它们能让控件更容易理解。随意使用,则会隐藏有用文本,并造成令人困惑的界面。

The Wux Webtools Team The Wux Webtools Team 7 分钟阅读 人工智能辅助,人工审核
Illustration of a developer reviewing accessible labels and UI components on a screen.
目录
  1. ARIA 标签用于命名,而不是补救
  2. 用简单的话理解可访问名称
  3. 第一条规则:优先使用原生 HTML 和可见标签
  4. 什么时候 `aria-label` 是正确工具
  5. 什么时候 `aria-label` 是错误工具
  6. 当可见文本已经存在时,优先考虑 `aria-labelledby`
  7. 用 `aria-describedby` 表示帮助文本,而不是名称
  8. 重复控件需要唯一名称
  9. 不要给所有东西都加标签
  10. 检查计算后的名称,而不只是代码
  11. 实用审查清单
  12. 良好 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 中,检查元素并查找角色、名称和描述等无障碍信息。你要检查三件事:

  1. 角色是否符合预期?
  2. 可访问名称是否清晰且具体?
  3. 描述是否有帮助,并且没有取代名称?

然后用真实屏幕阅读器测试几个流程。你不需要成为全职辅助技术专家,也能发现基础问题。在 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 是为填补空白而存在的。真正的能力在于知道什么时候确实存在空白。

常见问题

每个按钮都应该有 aria-label 吗?
不需要。带有清晰可见文本的按钮通常已经有良好的可访问名称。只有在可见文本缺失或不足时才添加 `aria-label`,例如仅图标按钮,或需要上下文的重复“Delete”按钮。
aria-label 和 aria-labelledby 有什么区别?
`aria-label` 直接在属性中提供文本字符串。`aria-labelledby` 指向页面其他位置已有的文本。如果已经存在合适的可见文本,`aria-labelledby` 通常更易维护。
aria-label 能修复用作按钮的 div 吗?
它可以提供名称,但不会让该元素像真正的按钮一样工作。你仍然需要处理键盘行为、焦点、状态和预期语义。在大多数情况下,请使用原生 `<button>`。
aria-label 应该与可见文本完全一致吗?
它通常应包含可见文本,尤其是对交互控件而言。这能支持语音识别用户,并符合 WCAG label-in-name 指南的意图。
我怎么知道屏幕阅读器会朗读什么?
先在浏览器开发者工具中检查 accessibility tree,查看角色、名称和描述。然后使用真实屏幕阅读器测试关键交互,例如 VoiceOver、NVDA、TalkBack 或 JAWS。

来源与进一步阅读

  1. WAI-ARIA Authoring Practices Guide
  2. MDN: aria-label attribute
  3. Accessible Name and Description Computation 1.2
  4. WCAG 2.2 Success Criterion 2.5.3: Label in Name
关于作者
The Wux Webtools Team

最后更新:

继续阅读