Skip to content


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 -->

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:

  <button aria-expanded="false">Text</button>

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', {
  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 })