Popover Hint and Hover (Explainer)

A followup proposal to the original Popover proposal, which adds several features related to building hints/tooltips.

Table of Contents


The Popover API (MDN, OpenUI Explainer) is now part of the HTML standard. It allows developers to create popovers of varying types and also easily trigger them from buttons, without using Javascript.

This explainer discusses two important extensions of the Popover API:

  1. The ability to trigger popovers via a “hover” type interaction (plus other interactions for non-mouse users).
  2. The ability to show a new type of popover for “hints”.

Importantly, these two capabilities are orthogonal and independent but closely related, and this explainer therefore discusses them both.

The motivating use case, but certainly not the only use case, for these two capabilities is in the construction of “tooltips”. Typically, tooltips are transient bits of additional information related to a bit of content. This additional information is not required and is supplementary in nature. Usually the user accesses this tooltip by hovering, keyboard-focusing, or long-pressing a control.

“Hover” triggering

The existing popover API includes the concept of “invoking elements”:

<button popovertarget=foo popovertargetaction=show>
  Click me to show a popover
<div popover id=foo> I'm a popover </div>

The popovertarget attribute can be used on buttons (and <input type=button>), and it points to the idref of an element with the popover attribute. In this configuration, clicking the button automatically shows the popover, without requiring Javascript. In addition, this linkage ensures that the accessibility tree is properly updated to represent the connection. The popovertargetaction attribute determines what action to take when the button is activated: show, hide, or toggle.

Add the interest value

To support hover triggering, a new value is added: popovertargetaction=interest. The name of “interest” was chosen to represent the fact that the popover will be triggered not just via mouse-hover, but also by keyboard focus or touchscreen long-press. When set to the interest value, the popover will be shown when the invoking element is a) hovered with the mouse, b) focused, or c) long-pressed (mobile).

CSS values to control delay

The majority of hover-triggered UI has a set of delays associated with it. It tends to be jarring to the user if UI elements show up immediately upon hovering an element. For example, that causes elements to show and hide rapidly as the user moves their mouse across the screen. To allow the user to be more intentional, there is an expectation that the element of interest must be hovered for a short period of time before any popovers are triggered. Additionally, it is common for such hover-triggered popovers to also be hidden automatically when they are no longer hovered, again after a sufficient delay. This “hide delay” is particularly important in the case that there is a physical gap between the invoking element and the hint popover: the user must move the mouse from the invoker to the popover and cross that gap, and it is frustrating if the popover closes while that traversal is taking place.

To facilitate these delays, two new CSS properties are added:

  • popover-show-delay - applied to the invoking element. The invoking element must be hovered, focused, or long-pressed for this long before the popover is shown. The default value for this property is 0.5s.
  • popover-hide-delay - applied to the popover. The invoking element or the popover must be not hovered, focused, or long-pressed for this long before the popover is hidden. The default value for this property is infinity.

It is important to “debounce” these delays. Said another way, the delays are interpreted as the time an element must be continuously hovered or de-hovered. The naive Javascript implementation of this behavior is to just call setTimeout(() => show/hidePopover(), delayValue). I.e. after the invoking element is hovered, wait delayValue, and then show the popover. But that does not properly handle the situation where the user hovers the element, and then de-hovers it, all within the delay time. In that case, no popover should be shown. Similarly, once the popover is shown and the user de-hovers the popover briefly but re-hoveres it again quickly, the popover should not close.

Example buttons

Let’s examine a small example:

<button popovertarget=hint1 popovertargetaction=interest>
  Button 1
<div popover=hint id="hint1">Hint 1</div>
<button>Button 2</button>

  button    { popover-show-delay: 0.3s; }
  [popover] { popover-hide-delay: 0.6s; }

In this example, because of the popover-show-delay: 0.3s, hovering (with the mouse) or keyboard-focusing “Button 1”, and then waiting for 0.3 seconds will cause “Hint 1” to be shown. It will stay showing as long as the button or the popover are hovered with the mouse or keyboard-focused.

Because of the popover-hide-delay: 0.6s, the hint will be automatically hidden after 0.6 seconds, once “interest has left” the popover. That depends on how the popover was triggered:

  • If the invoker was hovered to trigger “Hint 1”, then it will stay open as long as the invoker or the hint are still hovered. If the mouse if moved off of both the invoker and the hint for 0.6 seconds, the hint will be closed.
  • If the invoker was keyboard focused to trigger “Hint 1”, then it will stay open as long as focus is contained within either the invoker or the hint. If keyboard focus leaves both invoker and hint for 0.6 seconds, the hint will be closed.

Popover types

The new hover-triggering functionality should work for all popover types. There are use cases, e.g. for popover=auto; for example, a nested menu structure where sub-menus are shown when items in the primary menu are hovered or focused.

Example menu

<button popovertarget=menu1>Menu</button>
<ul popover id=menu1>
    <button popovertarget=submenu1 popovertargetaction=interest>
      Sub menu 1
    <button popovertarget=submenu2 popovertargetaction=interest>
      Sub menu 2
<ul class=submenu popover id=submenu1>
  Sub-menu #1
<ul class=submenu popover id=submenu2>
  Sub-menu #2
  button { popover-show-delay: 0.5s; }
  [popover].submenu { popover-hide-delay: 0.5s; }
  ul { list-style-type: none; }
  ul button { border: none; }

This example implements a popover menu (id=menu1) which has sub-menus (id=submenu1 and id=submenu2) which can be triggered simply by hovering or focusing the main menu’s buttons.

popover=hint Behavior

When a hint is shown, it behaves differently from other popover types such as popover=auto and popover=manual in a few ways:

Light dismissYesYesNo
Force-hides:Other autos and hintsOther hintsNothing

First, hints should always be light dismissible. They are transient, supplementary information, so they should not require affirmative action to close them. Clicking or tapping outside or hitting the ESC key will close a hint.

Second, when a hint is opened, it should not immediately close other popover=auto popovers that are open. For example, if a select picker UI is opened as a popover=auto, and the user hovers or focuses an unrelated element to see its hint, that action should not close the select picker. In essence, the hint is even more transient than the picker, so it shouldn’t take precedence. However, if the user subsequently hovers/focuses a second unrelated element and another hint is shown, the first hint should be closed. It would be confusing to have two hints open at a time. So hints should close other hints.

Finally, popover=auto popovers support “nesting”: the idea that one popover is a descendant of another popover, for example in the case of nested menus. It makes sense in that example for the parent menu to stay open while the child menu opens. The same is not true for hints - it would be confusing for one hint to be nested inside another, and wouldn’t make sense. Therefore, hints do not support nesting.

To summarize the above, there can only be one open popover=hint on a page, many nested (but only 1 “root”) popover=auto, and many popover=manual.


A key distinction between types of hints is whether it may be important for a user to explore or interact with the hint. Consensus on these types of hints/tooltips can help with the resolution of ARIA#979: Clarify the use of role=tooltip.

  • A plain hint contains only elements with a computed role of generic, text and images.
  • A rich hint contains other elements, such as links, form controls, lists or tables. These are hints that a screen reader user may need to interact with or explore.

Why differentiate between rich and plain hints?

For a screen reader user, there already exists simple settings and commands to read accessible descriptions, which are just an additional piece of text associated with an object, such as used by a title or aria-description attribute. When a hint is just used for a prettier tooltip, a screen reader can reuse this simple mechanism and act like a title was present. There is no need for the user to navigate to it.

However, when a hint is used for something more complex, such as a dialog that appears on hover, or a data table, then the user needs to be able to learn it’s there and navigate to it, so that they can use additional screen reader commands to interact with it.

A11y API support

For the best screen reading experience, the implementation will need to expose different properties for a plain hint vs a rich hint (classification described above).

  • Plain hints: the browser will expose the flattened text string comprised of the hint’s text and text alternative content via the invoking element. The actual popover target element and its descendants can be invisible/ignored in the AX tree. Note: this would be similar to how title attribute tooltips are treated today.

    1. If the invoking element lacks an accessible name, use the computed text of the hint as the element’s accessible name.
    2. If the hint is not used as the accessible name, then the hint would be treated as the accessible description of the invoking element. A described by relation would point from the trigger to the hint element.
  • Rich hints:

    1. Add aria-expanded on the triggering element: the same treatment as for popover=auto.
    2. Minimum role of tooltip on the target — see spec PR describing minimum role.
    3. Add aria-details pointing from the trigger to the popover. This allows screen readers to announce the presence of the popover and a command for navigating to it.
    4. Problem: in general, the details relation does not work for hidden objects, and we can’t necessarily expect the hint to be complete before it becomes visible, as that may happen on-demand. So what happens if the user navigates to a hint popover’s trigger, and it’s not open. If they tabbed there, then we can make sure the popover is shown on focus. But, if they are navigating with a virtual buffer as is likely, how will they trigger the hint popover to open (the user would need to know to focus the trigger, or JAWS/NVDA would need to start focusing objects during navigation as other screen readers do), and then and how will the user know it’s there (should we use a new value of the haspopup object attribute for these, e.g. haspopup=hint)?

::tooltip pseudo element

CSSWG Issue 8930 discusses the addition of a ::tooltip (or similarly named) pseudo element that could be used to generate tooltips via CSS, and style them. These are intended (though see the discussion) to be Plain Hints (as described above). This proposal may be complimentary to this explainer’s feature proposals, or it might replace some of them.

Monkeypatch spec

{To-do: document the changes necessary to the WHATWG/html spec for this proposal.}

Articles and References