Tooltip
Supplemental label for a trigger, shown on hover and focus, hidden on dismissal.
Tooltips are supplemental: data-tooltip on the trigger wires a .tooltip
element (role="tooltip") that appears near the trigger. Position, placement,
and arrow position are written by JavaScript; the element is shown and hidden
by promoting it to the top layer with popover="manual".
- Use
data-tooltipon the trigger. With text (data-tooltip="Help"), the tooltip element is generated. Empty (data-tooltip) marks an explicit tooltip connected viaaria-describedby. - Generated text is a snapshot at show time: changing
data-tooltipnever rewrites a tooltip that is already shown, and an always-visible generated tooltip keeps its initial text. For content that must update dynamically, use an explicit tooltip. See the tooltips bench (demo/templates/tooltips.html) for all six cases side by side. - Generated text is always plain text (
textContentonly), so a shorthand value is safe for untrusted strings once they are in the DOM. An explicit tooltip is author-owned DOM: its sanitization belongs to whoever produces it. Escape the attribute server-side — a DOM already compromised at render cannot be repaired at read time. There is nodata-tooltip-html, by design. - Tooltips are supplemental. Do not put required information or interactive controls inside them.
- Show on hover and focus. Hide on Escape, blur, or pointer leave.
- A trigger scrolled out of view takes its tooltip down and brings it back when it returns; only a dismissal ends the tooltip.
- Escape outlasts the hover and focus that justified the tooltip (WCAG 2.1 SC 1.4.13); showing it again takes a fresh hover or focus, not a second click on a trigger that already has focus.
- Author an explicit tooltip with
hidden. The runtime removes it when it takes the lifecycle over; until then it is what keeps the tooltip off screen. - JavaScript can generate tooltip elements from
data-tooltip. - Add
data-tooltip-clickto toggle it on click instead of hover/focus. - Add
data-tooltip-visibleto show it immediately and keep it visible while its trigger is in view. - Use
data-tooltip-placementto set the preferred placement (defaulttop). - The arrow inherits the tooltip background — custom gradients carry through automatically.
- Avoid relying on the native
titleattribute as the primary implementation.
A .tooltip[popover] may use a native or third-party-owned popover lifecycle.
Actual removes the native inset, margin, and overflow that conflict with
positioning and with the projecting arrow, while leaving bare [popover]
elements untouched. The lifecycle owner still writes position, the
coordinates, and the arrow position; Actual only supplies the presentation.
Choose one lifecycle owner. Use data-tooltip when Actual should show and
hide the tooltip; the runtime sets popover="manual". To manage a
.tooltip[popover] yourself, leave it unconnected to any data-tooltip trigger.
An explicit tooltip stays where you wrote it and inherits that location's theme, density, and custom properties. Place it inside the dialog when its trigger is in a modal dialog; content outside the modal is inert.
A generated tooltip (data-tooltip="Help") does not inherit the trigger's
local CSS context. Use an explicit tooltip when that context matters.
<button class="btn ghost"
type="button"
data-tooltip
aria-describedby="tooltip-save"
aria-label="Save">
<i class="ti ti-device-floppy" aria-hidden="true"></i>
</button>
<div class="tooltip" role="tooltip" id="tooltip-save" hidden>
Save changes
</div>
<button class="btn" type="button" data-tooltip="Save changes">
Save
</button>
Display modes
Click tooltips stay open when the pointer leaves and close on a second click or Escape. They remain supplemental, non-interactive content; use a flyout when the floating content contains controls.
<p class="cluster">
<button class="btn outline" type="button"
data-tooltip="Click again or press Escape to close"
data-tooltip-click>
Toggle on click
</button>
<button class="btn outline" type="button"
data-tooltip="Always visible while this trigger is in view"
data-tooltip-visible
data-tooltip-placement="bottom">
Always visible
</button>
</p>
HTML and long content
The data-tooltip="…" shorthand is intentionally plain text. For trusted HTML,
use an explicit tooltip referenced by aria-describedby. Keep its content
non-interactive and concise; long content wraps up to the tooltip's
viewport-aware maximum width.
<button class="btn" type="button"
data-tooltip
aria-describedby="tip-rich-long"
data-tooltip-placement="bottom">
Long HTML tooltip
</button>
<div class="tooltip" role="tooltip" id="tip-rich-long" hidden>
<strong>Keyboard shortcut:</strong> press <kbd>Ctrl</kbd> + <kbd>K</kbd> to open search.
This longer explanation demonstrates wrapping when a supplemental label needs a little more context.
</div>
Positioning
Set data-tooltip-placement to control where the tooltip appears relative to
its trigger: a side (top, bottom, left, right), optionally followed by
-start or -end. An unknown value logs a console warning and falls back to
top. The arrow follows the placement automatically.
The runtime also picks the coordinate space, and writes position to match. A
tooltip whose trigger scrolls with the page is placed in document coordinates
and position: absolute, so the browser scrolls it with the page instead of
the positioner correcting it a frame later — the lag visible while scrolling on
a touch device. A trigger that is viewport-anchored — inside a fixed or sticky
ancestor, an open popover, or a modal dialog — keeps position: fixed. Do not
override position on a tooltip the runtime manages. See
Tooltip coordinate space.
<p>
<button class="btn outline" type="button" data-tooltip="Above the button" data-tooltip-placement="top">Top</button>
<button class="btn outline" type="button" data-tooltip="To the right" data-tooltip-placement="right">Right</button>
<button class="btn outline" type="button" data-tooltip="Below the button" data-tooltip-placement="bottom">Bottom</button>
<button class="btn outline" type="button" data-tooltip="To the left" data-tooltip-placement="left">Left</button>
</p>
Custom styling
Override --tooltip-bg and --tooltip-fg on the tooltip element to change the
background and text color. The arrow picks up background: inherit so gradients
and solid colors both work.
<button class="btn primary" type="button"
data-tooltip
aria-describedby="tip-grad"
data-tooltip-placement="right">
Hover for gradient
</button>
<div class="tooltip" role="tooltip" id="tip-grad" hidden
style="background-color: #5b2d9e;
--tooltip-bg: linear-gradient(135deg, oklch(0.45 0.22 280), oklch(0.5 0.2 10));
--tooltip-fg: white;
padding: 0.4em 0.8em;
font-weight: var(--font-weight-medium);">
<strong>Custom gradient tooltip:</strong> the arrow matches the background, and this longer
sentence demonstrates how the tooltip wraps and flips when the preferred side lacks space.
</div>
CSS hooks
--tooltip-bg— bubble background (defaults tovar(--surface-solid)); the arrow follows it.--tooltip-fg— text color.--tooltip-arrow-size— arrow size.
--arrow-x and --arrow-y are written by the positioner at runtime, not set by
the author. See the JavaScript runtime documentation.