Skip to main content Eleven Ways (Home)

All articles

Tooltips that work for everyone

Written by Rembrand Le Compte on 25 March 2026 (Average reading time: 10 minutes)

Here at Eleven Ways, we often get asked for “the correct, accessible tooltip pattern”. Building accessible tooltips is a minefield — especially when you mix in rich content and screen reader quirks. Let’s examine the issues and go in search of a tooltip pattern that truly covers all our bases. 

Overhead view of a desk with partial view of a laptop as well as various paper mockups of website and application pages. There are several colourful sticky notes on them and the hands of 3 people hovering above the papers, with pens in hand.

Tooltips that work for everyone

Here at Eleven Ways, we often get asked for “the correct, accessible tooltip pattern”. Building accessible tooltips is a minefield — especially when you mix in rich content and screen reader quirks. Let’s examine the issues and go in search of a tooltip pattern that truly covers all our bases.

TL;DR

  • The Standard: The aria-describedby pattern is the best starting point for simple controls.
  • The Issues:
    • NVDA and JAWS ignore tooltips within text blocks while in Browse mode
    • Narrator fails on links.
    • Tooltips with links are functionally inaccessible to keyboard users.
  • The Recommendation: Use standard tooltips for simple UI, (non-)modal dialogs for complex content, and accessible footnotes for text definitions.

Read on to understand how we came to these conclusions…


What defines a tooltip?

There is no native tooltip component in HTML, but we can define a functional pattern based on common implementations and industry research. We distill this into a pattern consisting of:

  • A non-modal (non-blocking) overlay.
  • Containing supplementary information about a UI control.
  • Remaining hidden by default, appearing only on hover or focus of that control.

A strict definition calls for plain, descriptive text only but we frequently see implementations with formatting or links. So let’s expand our criteria:

  • May include rich content
  • May include links

The baseline standard

Considering accessibility requirements, the tooltip implementation by Deque University provides an excellent baseline. Its principal attribute is the use of aria-describedby, a method favoured by almost every implementation aiming for an accessibility standard.

Key features of this implementation:

  • Focusable: The tooltip is paired with a focusable element, such as a form field, link, or button.
  • Purpose: It gives more information about that element.
  • Semantics: The tooltip is referenced by the focusable element using aria-describedby. This ensures the tooltip is read when the element receives focus.
    • This means that the tooltip can be hidden (using CSS display:none, visibility:hidden, or the HTML hidden attribute) and is still found and read by assistive technology.
    • The tooltip needs a role, ensuring ARIA attributes can be used; in this case, role="tooltip".
  • Interaction: It is dismissible, hoverable and persistent:
    • The pointer can be moved over the additional content, without it disappearing (hoverable).
    • Remains on the screen as long as the trigger has focus (persistent).
    • Can be dismissed using the Esc key (dismissible) or by moving the focus away from the triggering element.
  • Deque Best Practice: Keeping the tooltip open, is done by implementing a delay in the hover-out behaviour. It’s not perfect though, as this requires some speed and fine motor control from the user.

Testing for real-world use cases

To test screen reader behaviour for our baseline standard, we can use the tooltip demo page by Deque University.

In addition, we also created our own demo page, to test these real-world use cases:

  • Does the tooltip work when it contains a link?
  • Does the tooltip work when it is read as part of a block of text? We can limit ourselves to buttons and functional links, as those make the most practical sense.
  • If so, is it read in context (after the triggering element)?

We tested these using NVDA, Narrator, VoiceOver (macOS/iOS), and Android TalkBack.

From Webaim Screen Reader User Survey #10 Results:

"NVDA is again the most commonly used screen reader at 65.6% of respondents outpacing JAWS at 60.5%. Narrator ... is commonly used by 37.3% of respondents ... Respondents with disabilities (72.4%) used iOS devices at a higher rate than those without disabilities (56%)".

The results

  • Desktop
    • VoiceOver (MacOS):
      • Focussing on elements (tab key on keyboard):
        • The element is named (eg. ‘link, link 1’, ‘button 1), the tooltip is shown, the label (aria-label)of the tooltip as well as the tooltip itself are read
      • Reading text (using Interaction and Text commands):
        • Select and read an element with a tooltip: the same results
        • Reading text continuously: When VoiceOver gets to the trigger, it reads out the element, opens the tooltip and reads it.
    • JAWS (Windows)
      • Focussing on elements (a.k.a. ‘Form mode’, using tab key on keyboard)
        • The element is named, the tooltip is shown, the label of the tooltip and text is being read by screen reader
          • names the item (eg. ‘link, link 1’, ‘button 1) or the field’s name (’Last name’)
          • It reads the tooltip aria-label first (’Tooltip : `), followed by the content text
      • Reading text (a.k.a. ‘Browse mode’, using commands)
        • Select and read elements: the element and tooltip are read, but not shown
        • Reading text continuously: the tooltip is not opened and not read
    • NVDA (Windows):
      • Focussing on elements (using tab key):
        • Reads the element + the label ("tooltip") + the content of the visible tooltip
      • Reading text (a.k.a. ‘Browse mode’, using arrow keys)
        • For standalone elements: reads the element, then (on next arrow down) the content of the tooltip. But does not say the label "tooltip" anymore.
        • When the tooltip trigger (eg. as a button) is in a block of text, the tooltip is not read out
    • Microsoft Narrator (Windows):
      • Narrator with Edge reads the tooltips for the form fields and the buttons, but not for the links
  • Mobile
    • TalkBack (Android)
      • the tooltip is being read: label first, followed by the tooltip content text
      • when using touch to access the elements instead of a keyboard, the tooltip is not visible when focusing on an element.
    • iOS VoiceOver (iPhone/iPad)
      • It reads the tooltip’s label, followed by the tooltip content text
      • when using touch to access the elements instead of a keyboard, the tooltip is not visible when focusing on an element.

The issues we encountered

Our testing revealed several critical gaps where standard implementations fail to meet user needs:

  1. NVDA and JAWS browse modes:
    • For standalone elements, NVDA fails to announce the aria-label (the "Tooltip:" prefix) when navigating with arrow keys.
    • Both NVDA and JAWS’ tooltips for elements embedded within blocks of text are not read in context at all.
  2. Microsoft Narrator: Tooltips on links are simply not announced by Narrator.
  3. Functional inaccessibility: Links located inside tooltips cannot be reached via keyboard navigation, meaning they cannot be opened that way.

1. NVDA and JAWS browse modes

Discrepancy between NVDA’s Focus and Browse modes can be attributed to inherent verbosity settings, as Adrian Roselli notes in the comments of his article on aria-describedby. While users can often tweak these settings, we want to ensure tooltips in text blocks are read by default. We found no success in tinkering with these settings to resolve the issue.

Attempted workarounds for NVDA / JAWS

We do not currently have a "clean" fix, as our attempts kept adding more complexity or confusion.

  1. Fixing the aria-label: To ensure the "Tooltip:" prefix is heard, you can add a visually hidden span within the tooltip text.

    The CSS:

    .sr-only {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
        border-width: 0;
    }
    

    The JS:

    data-tooltip="<span class=\"sr-only\">Tooltip: </span>This is the tooltip content"
  2. The "Double Duty" Label: For triggers within blocks of text, the only viable workaround we found, was to hijack the trigger's label to inform the user that a tooltip is available, along with its ‘standard’ function. In doing so, we are at risk of losing the distinction between the Action (navigating somewhere) and the extra Information (the tooltip content).

    <!-- For a button --><button aria-label="funambulist - focus or hover to get the definition of 'funambulist'" data-tooltip="This tooltip contains the definition of the word funambulist">funambulist</button>
    <!-- For a link --><a href="https://some-site" aria-label="funambulist - focus or hover to get important information about an article on funambulists" data-tooltip="This tooltip contains important context about a linked article">funambulist</a>

2. The Narrator problem

The failure of links with aria-describedby in Microsoft Narrator appears to be a bug. While the attribute is technically allowed on any element with an ARIA role, Narrator simply does not find the description for links.

Looking at historical data about aria-describedby from A11ySupport.io and tests by Adrian Roselli, aria-describedby support in Narrator has been broken for years. While button support was eventually resolved, link support remains broken four years later. But given that Narrator is used by 37.3% of respondents, we still want to account for it.

Attempted workarounds for Narrator

  1. Accessibility tree hacks: We tried forcing elements into compliance using common hacks like role switching and explicit tabindex, but these failed. The obvious reason is that issue is in Narrator’s implementation (or Microsoft Speech API), not the browser's interpretation.
  2. Alternative descriptions: Using title or aria-label on the link itself is not recommended. Screen readers that do support aria-describedby would read the text twice, and these properties are meant to provide the link's name, not a supplementary description.

3. Functional inaccessibility

The reason that links can not be reached, is simply because of how aria-describedby is implemented in browsers. The trigger needs focus to make the description appear/read by screen readers. Once that focus is gone (eg. by moving it to a nested element), the connection is severed. It makes sense now, why aria-describedby is meant to only contain plain text.

Technically, if tooltip content is extensive or contains useful semantics, aria-details is a more appropriate attribute. However, aria-details was not read out by our screen readers, so it’s not a viable option.

A note on markup

In our tests, markup called via aria-describedby was displayed visually without issue. How screen readers handle inline markup (like <strong>) or special characters depends on the specific reader and the user's individual verbosity settings. For example, NVDA announced emojis out of the box, whereas VoiceOver did not. Consequently, we’re treating "markup support" as a non-issue.

Intermediate conclusion: building an alternative?

Since aria-describedby fails in several cases, can we build a tooltip without it? The answer is: "It depends on which criteria you are willing to let go of".

To recap, we wanted to address our original failures:

  • Tooltips must work within paragraphs and on functional links.
  • Links inside tooltips must be keyboard-accessible.

While we are looking into alternatives, we can also take a look at some "nice-to-have" improvements:

  • Closing a tooltip should happen when another opens to avoid visual clutter.
  • Adding a Close button to improve the experience for users who lack precise pointer control.

The non-modal dialog experiment

The solution that most closely aligns with this behaviour is a non-modal dialog. (Note: "Modal" refers to the state of blocking the rest of the page, while "Dialog" refers to the component itself). However, dialog patterns don’t provide a method for a screen reader to read the information out, when the dialog opens.

We investigated 2 possible avenues to resolve this but hit major obstacles on both:

  1. Live regions: using aria-live is problematic because announcements cannot be easily dismissed, interrupting the user's flow. It also requires the region to be present in the DOM at page load to be detected, creating potential headaches for Javascript-oriented solutions and frameworks which are very much on the forefront of web development at the moment.
  2. Moving focus: Automatically moving focus to the tooltip violates WCAG 2.2 SC 3.2.1: On Focus. But even if it didn’t, we still face issues:
    • Managing focus: this is technically complex and prone to unexpected behaviour. For example: how do you retrigger a dialog when you’ve closed it? Focus returns to the trigger but that doesn’t mean you want to immediately retrigger the dialog.
    • Persistent Narrator/NVDA Issues: Even with a focussed dialog, Narrator and NVDA failed to announce the content in context when scanning a full paragraph.

For sake of completion, we created a proof-of-concept to test the hover behaviour (keeping in mind it fails WCAG 3.2.1 On focus).

Recommendations

There is no one-size-fits-all solution, you must choose based on your specific use case.

1. Do you really need a tooltip?

Our first recommendation is to place information in an easy-to-reach way near the element. For input fields, adding a description below the field and using aria-describedby is the most straightforward method.

2. Best overall tooltip

For most scenarios, a tooltip based on aria-describedby (as exemplified by Deque) remains the best option.

  1. Caveat: Do not include links inside the tooltip.
  2. Caveat: Do not use them in text blocks where they must be read in context.

If you do want to use them on links, you’ll have to wait until Microsoft Narrator is fixed.

If you want to help move that forward, file an issue using the Microsoft Feedback Hub. The more folks complain about it, the better chance we have at getting an update that resolves the problem.

If your tooltip requires links, you must use a (non-)modal dialog triggered by a click or Enter key.

If you want to play around with an example, we’ve created a demo page for one here.

Understand though, that dialogs also have their own quirks, which we won’t get into here. For example, Adrian Roselli has an excellent article on dialog focus.

4. For definitions within text

To add information to words in a paragraph, we recommend accessible footnotes. Our preferred pattern is based on research by Nicki Bright, which uses "back links" and visually hidden text for clarity.

You can it out on our demo page or take a look at this code example:

<p>
 A bit of text that needs a footnote
  <a id="footnote-01-link" href="#footnote-01">
   <sup>[1] <span class="sr-only">Footnote details</span></sup>
  </a>.</p>
<ol>
 <li id="footnote-01">
  The footnote text goes here
  <a href="#footnote-01-link" aria-label="Back to content">↩</a>
 </li></ol>

Conclusion

At Eleven Ways, we believe that accessibility means understanding how real people interact with technology. Tooltips may seem like only a small part of the UI, but the impact of a broken implementation means a dead-end for the users that rely on that information. By choosing the right pattern for the right context, we can build a web that truly works for everyone.


Further reading

For further reading on the foundations of accessible tooltips, we recommend:

Latest articles

Feasible accessibility tips in your mailbox

With our practical tips you will learn how to make the website or app of your organization (or customer) accessible to everyone.

You can unsubscribe with one click.

How can we help your organisation?

All contact details