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

Do not combine them. tooltip.js writes popover="manual" on every tooltip it manages and calls showPopover() / hidePopover() itself, so a tooltip handed to the runtime has its mode overwritten. The runtime only ever reaches a tooltip that a data-tooltip trigger points at, so a .tooltip[popover] with no such trigger stays yours to drive. Use data-tooltip and the built-in runtime when Actual should own showing and hiding.

Where the element lives is part of the contract. An explicit tooltip stays exactly where you wrote it, so it keeps every scope that reaches it by inheritance — a theme island, a density scope, .inverted, your own scoped custom properties. It also means placement is yours to get right: a tooltip used from inside a modal dialog must be authored inside that dialog, because the top layer does not lift an element out of a modal's inertness.

A generated tooltip (data-tooltip="Help") is Actual's to place, and it goes at document level rather than beside the trigger, so it cannot disturb the trigger's structural position — a generated sibling would take .join > :last-child from a trigger that ends a group. It therefore makes no inheritance promise. Use the explicit form when the local CSS 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. 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