Accessibility
Use natively focusable elements
Tooltips should only be applied to natively focusable elements like <button>
or <input>
. Some screenreader software will not announce the tooltip on an
element like <div>
.
Non-interactive tooltips are accessible by default
The default trigger for tooltips is "mouseenter focus"
. This means both a
hover and focus via keyboard navigation will trigger a tooltip. The reference
element receives an aria-describedby
attribute once the tooltip is fully
shown:
<button aria-describedby="tippy-1">Text</button>
<div id="tippy-1" role="tooltip" class="tippy-popper">
<!-- inner elements -->
</div>
This allows screenreader software to announce the tooltip content describing the reference element. No work is required by you to achieve this functionality.
Creating accessible interactive tooltips
Although non-interactive tooltips are accessible by default, making interactive tooltips accessible requires a bit of work by you.
In your HTML, the element should have its own wrapper element (e.g. div
or
span
), and an aria-expanded
attribute:
<div>
<button aria-expanded="false">Text</button>
</div>
When initializing the tooltip, the following options are recommended:
tippy('button', {
interactive: true,
// `focus` is not suitable for buttons with dropdowns
trigger: 'click',
// Don't announce the tooltip's contents when expanded
aria: null,
// Important: the tooltip should be DIRECTLY after the reference element
// in the DOM source order, which is why it has its own wrapper element
appendTo: 'parent',
// Let the user know the dropdown has been expanded using these lifecycle
// functions
onMount({ reference }) {
reference.setAttribute('aria-expanded', 'true')
},
onHide({ reference }) {
reference.setAttribute('aria-expanded', 'false')
},
})
Using this technique, elements within the tippy can be tabbed to since they are directly after the reference element.
It's also possible to change the role
of the tippy to something other than
"tooltip"
:
tippy('button', {
role: 'menu',
})
Delays and keyboard navigation
When using keyboard navigation, it's probably best if there are no delays so that screenreaders can announce the tippy content as soon as possible if focus is currently on the reference element.
const delay = 500
const duration = [300, 200]
tippy('button', {
delay,
duration,
onTrigger(instance, event) {
// If the tippy was triggered by a `focus` event (e.g keyboard navigation)
// then set `delay: 0` and reduce the duration a bit.
if (event.type === 'focus') {
instance.set({ delay: 0, duration: [200, 150] })
} else {
instance.set({ delay, duration })
}
},
})