修复我的工具提示无障碍错误
Fixing my tooltip accessibility mistake

原始链接: https://jakearchibald.com/2026/my-tooltip-a11y-mistake/

作者分享了关于使用 `popover="hint"` 特性实现提示框(tooltip)时,在网页可访问性方面获得的一项经验教训。 最初的错误在于使用 `aria-describedby` 将一个仅含图标的按钮与包含该按钮标签的提示框关联起来。这导致屏幕阅读器用户获取到了冗余信息。作者随后尝试通过移除 `aria-label` 来修复问题,但这使得按钮失去了可访问名称,从而在不同的屏幕阅读器(如 JAWS)中触发了不可预测的行为。 可访问性专家指出,`aria-describedby` 提供的是补充信息,不能替代元素的可访问名称。正确的解决方法是使用 `aria-labelledby`,这使得按钮可以直接从提示框内容中获取其可访问名称。或者,对于更复杂的提示框,可以同时使用这两个属性来区分名称和描述。 作者总结了两个关键点: 1. **确保所有交互元素都有可访问名称。** 2. **使用多种屏幕阅读器进行测试**,因为仅依赖单一工具(如 VoiceOver)无法全面了解用户体验。

这篇 Hacker News 讨论聚焦于使用 HTML `title` 属性作为工具提示(tooltip)时存在的无障碍缺陷。尽管一些用户认为 `title` 属性是一种简洁明了的显示悬停文本的方式,但另一些用户——在 W3C 建议的支持下——解释了为什么它不足以胜任。 对 `title` 属性的主要批评在于,屏幕阅读器对它的支持程度不一致,且键盘用户和触摸屏用户难以使用,同时也无法让精细动作受损的用户轻松触发。此外,`title` 属性的设计初衷是提供“辅助信息”,而非作为主要标签。 现代无障碍实践的支持者主张使用 `aria-labelledby` 或 `aria-label`。尽管一些评论者抱怨这些 ARIA 方法增加了不必要的代码“臃肿”或复杂性,但另一些人坚持认为,为了确保包容性的用户体验,这种权衡是必要的。这场辩论突显了网页开发中的一个普遍痛点:目前缺乏一种通用、语义正确且跨平台的工具提示标准,这迫使开发者必须在简单但不可访问的遗留属性,与更健壮但冗长的 ARIA 实现方案之间做出选择。
相关文章

原文

I made an accessibility error, and I want you to learn from my mistakes.

I've been making short videos recently about web platform features as they land in Firefox, and also about other web standards & development stuff. If you'd prefer to watch a 3-minute video version of this article, pick your platform, and give the account a follow if this kind of thing interests you:

The account is also on Bluesky, but video uploading has been broken there for a while.

Otherwise, here's the written version…

I made the accessibility error when I created a tooltip like this:

A text formatting toolbar, featuring buttons like bold, italic, underline etc. A mouse pointer is over the bold button, and a tooltip is visible, saying 'Bold (⌘B)'.

That was part of a video on popover="hint", which also covers how to show the tooltip for visual users. Oh ok, here are links to that video too:

I did try to get it right! I spoke to friends who know more about accessibility than me, and they pointed me towards these demos by Scott O'Hara.

Now, Scott knows what he's talking about, so I figured I could just copy the patterns in his demo. Scott's demos predate hint popovers, so here's a modernised version of one of his examples:

<button aria-describedby="edit-tooltip">Edit</button><div id="edit-tooltip" popover="hint">Modify account settings.</div>

The button is connected to the tooltip with aria-describedby, so screen readers will usually read the tooltip when the button is focused.

One of the nice things about aria-describedby is that it works even if the element it's pointing at is hidden, which is the case here.

So, I adapted Scott's code for my toolbar demo:

<button aria-label="Bold" aria-describedby="bold-tooltip">    <svg>(bold icon)</svg></button><div id="bold-tooltip" popover="hint">Bold (⌘B)</div>

It's a very similar pattern, although I have aria-label on the button, because it contains an SVG icon rather than text.

Like Scott, I added aria-describedby to the button, to connect the button to the tooltip.

I tested it in VoiceOver, and it said "Bold. Bold. Command B. Button". And I thought, that's not quite right - it doesn't need to say "bold" twice.

That was happening because, unlike Scott's example, where the tooltip contained just additional content, my tooltip also contains the label. Bold is said twice, because it's there, twice.

So I dropped the aria-label…

<button aria-label="Bold" aria-describedby="bold-tooltip"><button aria-describedby="bold-tooltip">    <svg>(bold icon)</svg></button><div id="bold-tooltip" popover="hint">Bold (⌘B)</div>

…and VoiceOver now said "Clickable image. Bold. Command B. Button". I thought, that'll do! I hit publish on my video, and I went to the pub.

I got a message from Léonie Watson and Gez Lemon from the accessibility agency TetraLogical, telling me, very politely, that I'd done a silly.

Scott's pattern was correct, but I'd gone off the rails when I removed the aria-label, because now, the button has no accessible name. aria-describedby provides additional information - it isn't a replacement for the accessible name.

In fact, JAWS on Windows tries to gather accessible text from nearby elements, and ends up announcing the bold button as both bold and italic, since the italic tooltip element was a sibling in the DOM.

There was a bit of a clue when VoiceOver said "clickable image" - this was VoiceOver indicating it didn't have an accessible name to announce. I just didn't realise it at the time.

Léonie and Gez told me how to fix it: I switched from aria-describedby to aria-labelledby.

<button aria-describedby="bold-tooltip"><button aria-labelledby="bold-tooltip">    <svg>(bold icon)</svg></button><div id="bold-tooltip" popover="hint">Bold (⌘B)</div>

aria-labelledby works like aria-describedby, but it provides the accessible name, rather than additional description.

Now the button gets its accessible name from the tooltip, and VoiceOver says "Bold. Command B. Button." - perfect!

If I wanted, I could split this up, so the accessible name is just "Bold", and the additional description is "Command B":

<button aria-labelledby="bold-label" aria-describedby="bold-description">    <svg>(bold icon)</svg></button><div id="bold-tooltip" popover="hint">    <span id="bold-label">Bold</span>    <span id="bold-description">(⌘B)</span></div>

I think that's overkill in this case, but it might make sense if you have a tooltip with a longer description.

So there you go! Always ensure interactive elements have an accessible name. And test with multiple screen readers if you can. Testing in one screen reader is like testing in one browser - it doesn't always give you the full picture.

Here's the fixed demo, although it doesn't work quite right in Safari, as it doesn't yet support popover="hint".

联系我们 contact @ memedata.com