Others
Tooltip
AvailableSupplementary text that appears on focus or hover. Uses aria-describedby with keyboard support.
Specification changes under review
- #3431(opens in a new tab)Tooltip: role="tooltip" to become name-prohibited
What Is a Tooltip?
A tooltip is a small popup that appears when an element receives focus or hover, providing supplementary description for that element. They are commonly used to explain icon buttons or add hints to input fields.
An important caveat: tooltips are "supplementary," not primary information. Essential information required for an operation should never be placed only in a tooltip.
Why Does Accessibility Matter?
Custom tooltips or relying on the title attribute cause the following problems:
- Not shown to keyboard users. If the tooltip only appears on hover (mouse), users who navigate with Tab won't be able to read the description.
- Not shown to touch users. Smartphones and tablets don't have hover.
- The
titleattribute is unreliable. It displays slowly, can't be styled, and assistive technology support is inconsistent. - Can't be dismissed. WCAG requires that supplementary popups can be dismissed with Esc.
The solution is to associate the tooltip with aria-describedby, show it on both focus and hover, and allow dismissal with Esc.
Live Demo (Recommended Implementation)
The button below has an APG-compliant tooltip. Verify that it appears not only on mouse hover but also when focused with Tab, and can be dismissed with Esc.
Try it: Tab to focus the button and the tooltip appears → Esc to dismiss → Hover with mouse and it appears again. The tooltip itself does not receive focus.
Tip
When a screen reader focuses the button, it announces something like "Save, button, Saves your changes to the server" — the name followed by the description. This is the effect of aria-describedby.
Keyboard Interaction
| Key | Action | Priority |
|---|---|---|
| Tab | Focus on the trigger displays the tooltip | Required |
| Esc | Dismiss the visible tooltip (focus remains on the trigger) | Required |
Note
The tooltip itself does not receive focus (it is not a Tab target). Since it's merely supplementary to the trigger, avoid placing links or buttons inside it (if you need interactive content, use a popover or dialog instead).
Required WAI-ARIA Roles & Properties
| Target | Attribute / Role | Meaning |
|---|---|---|
| Trigger | aria-describedby="<tooltip id>" | Associates the tooltip as a description. Read aloud when the trigger receives focus. |
| Tooltip element | role="tooltip" | Identifies the element as a tooltip. |
| Tooltip element | hidden (when not visible) | Excludes it from screen readers and rendering while hidden. |
| Trigger | Focusable element (e.g. <button>) | Ensures keyboard accessibility. Do not use <div> or similar. |
Recommended Pattern (Good)
Good / Recommended
Associate with aria-describedby, show on both focus and hover, and dismiss with Esc.
Markup:
<!-- The trigger must be a focusable element (e.g. button) -->
<button type="button"
id="tip-trigger"
aria-describedby="tip-text">
Save
</button>
<!-- The tooltip itself. Not focusable, role="tooltip" -->
<div id="tip-text" role="tooltip" hidden>
Saves your changes to the server
</div>Show/hide script (supporting both focus and hover + Esc is the key):
const trigger = document.getElementById('tip-trigger');
const tip = document.getElementById('tip-text');
if (trigger && tip) {
const show = () => { tip.hidden = false; };
const hide = () => { tip.hidden = true; };
// Must show on both focus and hover
trigger.addEventListener('focus', show);
trigger.addEventListener('blur', hide);
trigger.addEventListener('mouseenter', show);
trigger.addEventListener('mouseleave', hide);
// Allow dismissing with Esc
trigger.addEventListener('keydown', (e) => {
if (e.key === 'Escape') hide();
});
}Note
As a note, tooltip content should be kept to "nice-to-have" supplementary information.Information essential for operation should be written directly in the label or body text. The displayed tooltip should be dismissible with Esc and should not disappear when the mouse moves over it (WCAG 1.4.13).
Anti-Pattern (Bad)
The buttons below rely on the title attribute only, or use a hover-only custom tooltip. Focusing with Tab shows no description, and Esc doesn't dismiss anything.
Try it: Tab to focus shows no description (left: title doesn't appear on keyboard focus / right: hover-only). Neither works on touch devices, and Esc has no effect.
<!-- ❌ Anti-pattern 1: title attribute only -->
<button type="button" title="Saves your changes">Save</button>
<!-- ❌ Anti-pattern 2: hover-only custom tooltip -->
<span class="has-tip">Save
<span class="tip">Saves your changes</span>
</span>Bad / Avoid
Problems with this implementation:
- Doesn't appear on keyboard focus — Neither
titlenor hover-only CSS shows on Tab focus. - Doesn't appear on touch — On devices without hover, the tooltip never displays.
- No association — Without
aria-describedby, the description and trigger are not linked. - Can't be dismissed — No mechanism to close with Esc (WCAG 1.4.13 violation).
Tip
The title attribute is "better than nothing," but it displays slowly, can't be styled, and doesn't appear on keyboard focus. If you want to properly show supplementary descriptions, implement them with aria-describedby + custom show/hide logic.
Implementation Checklist
- The trigger is a focusable element (e.g. <button>)
- The trigger has aria-describedby linking to the tooltip
- The tooltip element has role="tooltip"
- The tooltip appears on both focus and hover
- Esc dismisses the tooltip (focus remains on the trigger)
- The tooltip itself does not receive focus and contains no interactive elements
- Information essential for operation is not placed only in the tooltip
Source (English):Tooltip Pattern — W3C APG(opens in a new tab)