tooltip()| Export | Signature | Description |
|---|---|---|
tooltip |
tooltip(element, text?, { placement? }) |
Attaches a tooltip to an element. Shown on hover, on focus, and on a held press (§8). |
tooltip_all |
tooltip_all(root?) |
Attaches tooltips to all [title] elements within root. Clean up before attaching tooltips again after a render (§10). |
tooltip_timing |
{ show_ms, hide_ms, press_ms } |
Configurable delays for hover/focus, hiding, and a long press. Tests can set a delay to zero to avoid waiting. |
placement: "top" (default) | "bottom" | "left" | "right"
· Appears after a 200 ms delay on hover, or after a 400 ms press from a pointer that has no
hover (§8) · Hidden on Escape, by the next press
elsewhere, or 150 ms after the pointer leaves both the trigger and the chip — the chip itself
can be hovered and read (§9)
· text is read from the title attribute if omitted; the helper saves this value and restores it during cleanup (§10)
· One line up to 30 ch, wrapping past it (§7) — longer text wraps onto additional lines
· Returns a cleanup function
Most common case: icon buttons without visible text. Tooltip shown on hover and keyboard focus.
Tooltip can be placed on different sides with the placement option. It is a
preference rather than a guarantee: a chip whose preferred side would put it off the viewport
flips to the trigger's other side instead of hanging off the page.
Near a corner the chip also slides along the trigger, because flipping alone is not enough there. A chip is centred on its trigger across the axis it is not placed on, so a wide chip on a trigger close to a side of the window runs off that side whichever way it flips. The chip's fallbacks therefore offer, in order: the preferred side with one edge pinned to the trigger's matching edge, then the opposite side, then the opposite side pinned the same way. The browser uses the first position that fits, minimizing movement.
Define fallback placement areas explicitly. Using only
flip-block, flip-inline, flip-block flip-inline provides fewer fallback positions than it appears to. A one-keyword position-area spans the whole opposite axis, and flipping
that span has no effect. These three entries therefore produce only one alternative: the opposite side. A tooltip without enough room on both axes can still overflow, and browsers handle this differently: one keeps the flipped option and slides the box into view, another returns it to the preferred side. On a trigger a few pixels from the top of the window, the second behavior can place the tooltip above the visible area.
Use aria-disabled="true" instead of disabled so the button
remains tabbable and screen readers can reach the tooltip via aria-describedby.
aria-disabled announces only that the control is unavailable, so the
tooltip should explain why. The explanation is available to assistive technology and to sighted keyboard users because it appears on focus.
When the explanation changes with the control's state:
aria-label if it has no visible
text. Without one, tooltip() derives the accessible name from the
tip text — so re-wording the tip to say why the control is unavailable renames the
control as well.tooltip() is called; call the cleanup and attach again with the new text
(§5 below is that lifecycle). Compare against the text
already showing, so the tooltip is reattached only when its text changes.Text can be provided directly instead of being read from title.
The cleanup function removes the tooltip and restores the original values — the ARIA
attributes, and the trigger's title (§10). Cleanup
followed by a fresh tooltip() is also how a tooltip's text changes — see
§3.
Tab between the buttons — the tooltip is shown automatically on focus. This is the main reason to use tooltip() instead of the title attribute.
Escape dismisses the tooltip wherever focus is — on the control, in a field on the other side of the page, or nowhere at all after a held press (§8). WCAG 2.2 SC 1.4.13 requires users to be able to dismiss the tooltip without moving the pointer or focus. A document-level key listener dismisses the visible tooltip. The event continues to propagate: a dialog, menu or popover behind the tip still closes on that same Escape.
The chip stays on one line up to 30 ch and wraps past it, so longer hints wrap within the tooltip background. A single word with no break opportunity is broken at the same cap.
Longer hints need more vertical space. Keep hints concise: two keyboard shortcuts can reasonably take two lines, while longer explanations belong in the panel the control opens.
An iPad is a supported device (Browser support) and
does not normally provide hover or keyboard focus. Hold the trigger for 400 ms to show the tooltip — the gesture Material specifies for touch, and the one the platform's own
interest invokers (interestfor, not Baseline yet) use. The helper implements this established behavior. Try it on a touch device or with
device emulation open; nothing changes for a mouse.
disabled button still receives
pointerdown — only its click is suppressed — so the press reveals
the tip on disabled and aria-disabled alike. This lets users read why a control is unavailable
(§3).pointerType rather than @media (hover: none): an iPad
with a trackpad supports both touch and hover, so one media-query result cannot describe every interaction.The tooltip stays open when the pointer moves from its trigger onto it. WCAG 2.2 SC 1.4.13 Content on Hover or Focus requires this behavior. It lets users of screen magnification move the magnified area onto the tooltip and read text that was initially outside their view.
pointer-events: none; that would prevent the hover behavior required by WCAG. Clicks inside the tooltip therefore reach it rather than the content behind it.
placement to open on a different side (§2). Keep the tooltip hoverable so users with screen magnification can move the pointer onto it and read it.
max-content wide, capped at 30ch, and
centred on what it is anchored to — so above a 10px icon it reaches roughly half its own
width past each side. Check the space beside the trigger as well as above it: a tooltip opening along a narrow gutter can cover other controls in it. Use the trigger's placement to open the tooltip across the gutter instead.title attributeWhen a tooltip uses the title text, the helper saves that text and empties the attribute to prevent the browser's native tooltip from appearing as well. Cleanup restores the saved text if the attribute is still empty. This lets repeated cleanup-and-attach cycles preserve the original tooltip.
Cleanup handles three possible attribute states. Attaching the tooltip sets the attribute to an empty string:
At cleanup, title is… | What it means | What cleanup does |
|---|---|---|
"" | unchanged since attachment | restores the saved text |
| absent | the calling code removed it | keeps it absent; no tooltip will be attached |
| non-empty | the calling code changed the text | keeps the new text for the next attachment |
To update an explanation when the state changes, write the new explanation to title,
or call removeAttribute('title') when no explanation is needed,
then clean up and reattach. Both changes take precedence over the saved text. Updating title without reattaching leaves the custom tooltip unchanged and may also show a native tooltip with different text.