Actual CSS

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-tooltip on the trigger. With text (data-tooltip="Help"), the tooltip element is generated. Empty (data-tooltip) marks an explicit tooltip connected via aria-describedby.
  • Generated text is a snapshot at show time: changing data-tooltip never 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 (textContent only), 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 no data-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-click to toggle it on click instead of hover/focus.
  • Add data-tooltip-visible to show it immediately and keep it visible while its trigger is in view.
  • Use data-tooltip-placement to set the preferred placement (default top).
  • The arrow inherits the tooltip background — custom gradients carry through automatically.
  • Avoid relying on the native title attribute 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 to var(--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.

Actual CSS

Search documentation