Skip to content

Tooltip

OverlaysSince 0.2.0
import { Tooltip } from '@absoluteui/components/tooltip';
import '@absoluteui/components/tooltip/style.css';

Using Angular? See Tooltip for Angular.

A short text hint for a trigger, meeting WCAG 1.4.13 (Content on Hover or Focus). It shows after a delay on pointer hover and immediately on keyboard focus. The pointer can move onto it (hoverable), it stays visible while the trigger is hovered or focused (persistent), and Escape hides it without moving focus (dismissible). The trigger references it through aria-describedby, or through aria-labelledby for icon-only controls. Only one tooltip is visible at a time.

  • Supplementary hints: keyboard shortcuts, the full text of a truncated label, the purpose of an icon-only button.
  • Naming an icon-only control visually as well as programmatically (relation ‘label’).
  • Essential information or instructions: touch users never see tooltips; put it on the page.
  • Interactive content (links, buttons): use Popover.
  • Validation errors: use the field’s validation message.
  • Long text: tooltips are capped at tooltip-max-width; use Popover or inline help.
import { Tooltip } from '@absoluteui/components/tooltip';
import { Button } from '@absoluteui/components/button';
const save = document.createElement('button');
save.type = 'button';
save.textContent = 'Save';
Button.enhance(save);
document.body.append(save);
Tooltip.create(save, { content: 'Ctrl+S' });
import { Tooltip } from '@absoluteui/components/tooltip';
import { Button } from '@absoluteui/components/button';
const settings = document.createElement('button');
settings.type = 'button';
document.body.append(settings);
// The tooltip names the icon-only button (aria-labelledby), so create it before the Button.
Tooltip.create(settings, { content: 'Settings', relation: 'label', placement: 'bottom' });
Button.enhance(settings, { icon: 'menu', variant: 'ghost' });
import { configureAbsoluteUI } from '@absoluteui/core';
configureAbsoluteUI({ components: { tooltip: { showDelay: 300, hideDelay: 200 } } });
import { Tooltip } from '@absoluteui/components/tooltip';
import '@absoluteui/components/tooltip/style.css';
for (const el of document.querySelectorAll<HTMLElement>('[data-hint]')) {
Tooltip.create(el, { content: el.dataset.hint ?? '', placement: 'bottom' });
}
import { Tooltip } from '@absoluteui/components/tooltip';
const save = document.querySelector<HTMLButtonElement>('#save');
if (save) Tooltip.create(save, { content: 'Ctrl+S' });
<button aria-describedby=tooltip>Save</button> (consumer trigger)
...
<div class="aui-tooltip" role="tooltip" popover="manual">Ctrl+S</div> (end of body, or the trigger's <dialog>)
Tooltip.create(trigger: HTMLElement, options: TooltipOptions): Tooltip

Global configuration key: tooltip (see Configuration).

Option Type Default Description
content string Tooltip text (text only). Required.
relation 'description' | 'label' "description" Trigger gets aria-describedby (‘description’) or aria-labelledby (‘label’, for icon-only controls).
placement Placement "top" Preferred side and logical alignment; flips and shifts to fit. Globally configurable.
showDelay integer (0..5000) 500 Hover delay before showing, ms. Keyboard focus shows immediately. Globally configurable.
hideDelay integer (0..5000) 100 Delay after the pointer leaves the trigger and tooltip before hiding, ms. Globally configurable.
direction 'ltr' | 'rtl' "ltr" Resolves logical start/end placements. Globally configurable. Inherited from common configuration.
Property Type Description
element HTMLDivElement The tooltip (div.aui-tooltip[role=tooltip]).
trigger HTMLElement The trigger element.
isVisible boolean Whether the tooltip is visible.
options Readonly<TooltipOptions> Current options.
Method Description
show(): void Show immediately (hides any other visible tooltip).
hide(): void Hide immediately.
update(options: Partial<TooltipOptions>): void Merge and apply options (content, relation, placement, delays).
destroy(): void Hide; remove the element, the trigger ARIA reference, listeners, and subscriptions.
Event Detail Bubbles Cancelable Description
aui-open null No No After the tooltip became visible (dispatched on the tooltip element).
aui-close null No No After the tooltip was hidden.

All rules live in the aui.components cascade layer, so application styles override them without specificity battles.

Class Purpose
aui-tooltip Tooltip element (manual popover, top layer).
aui-tooltip--static Set when animation is disabled globally.

Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.

--aui-border-width | --aui-color-inverse | --aui-color-on-inverse | --aui-duration-fast | --aui-easing-standard | --aui-font-family-sans | --aui-font-size-sm | --aui-line-height-tight | --aui-radius-sm | --aui-shadow-md | --aui-space-1 | --aui-space-2 | --aui-tooltip-max-width

type TooltipRelation = 'description' | 'label';
interface TooltipConfig { placement: Placement; showDelay: number; hideDelay: number }
interface TooltipOptions extends Partial<TooltipConfig> { content: string; relation?: TooltipRelation; direction?: 'ltr' | 'rtl' }

Pattern: WAI-ARIA APG Tooltip pattern, with WCAG 1.4.13 Content on Hover or Focus.

Key Context Action
Tab / Shift+Tab Focus moves to the trigger (keyboard focus-visible) Shows the tooltip immediately.
Tab / Shift+Tab Focus leaves the trigger Hides the tooltip.
Escape Tooltip visible Hides the tooltip without moving focus; the key is consumed.
Escape No tooltip visible Not handled; reaches enclosing overlays and the page.
Element Role
tooltip div tooltip
Attribute Element Value
aria-describedby trigger id of the tooltip, added to existing ids (relation ‘description’)
aria-labelledby trigger id of the tooltip, added to existing ids (relation ‘label’)

The tooltip is never focusable and never moves focus. It shows on keyboard focus (:focus-visible) of the trigger, not on pointer focus.

Criterion Level Note
1.3.1 Info and Relationships A
1.4.3 Contrast (Minimum) AA
1.4.11 Non-text Contrast AA
1.4.13 Content on Hover or Focus AA
2.1.1 Keyboard A
4.1.2 Name, Role, Value A

Automated test results: Accessibility and compliance.

  • 1.4.13 dismissible: Escape hides it without moving pointer or focus.
  • 1.4.13 hoverable: moving the pointer from the trigger onto the tooltip keeps it open (hideDelay grace period).
  • 1.4.13 persistent: stays visible while the trigger is hovered or focused, until Escape or another tooltip opens.
  • Only one tooltip is visible at a time.
  • Touch pointers do not show tooltips; tooltips must never hold essential information.
  • Manual screen-reader verification (NVDA, JAWS, VoiceOver) is pending.

hidden, pending (show delay running), visible, hiding (hide delay running).

From Trigger To
hidden mouse or pen enters the trigger pending; visible after showDelay
pending pointer leaves the trigger hidden (never shown)
hidden keyboard focus on the trigger (:focus-visible) visible immediately
visible pointer leaves trigger and tooltip (not keyboard-focused) hiding; hidden after hideDelay
hiding pointer enters the trigger or the tooltip visible
visible trigger loses focus, Escape, pointer down elsewhere, another tooltip shows hidden
  • WCAG 1.4.13: dismissible (Escape), hoverable (the pointer can move onto the tooltip within hideDelay), persistent (stays while hovered or focused).
  • Pointer focus (a click) does not show the tooltip; keyboard focus does.
  • Touch pointers never show tooltips, so tooltips must never hold essential information.
  • relation: 'label' replaces the trigger’s accessible name; use it only for controls without visible text.
  • Manual screen-reader verification is pending (see Known limitations).

Width up to min(tooltip-max-width, 100vw - 1rem); text wraps. The positioning engine flips and shifts to stay in the viewport.

start and end placements are logical and mirror in RTL (direction option or global direction).

Inverse surface (color-inverse with color-on-inverse text), verified in the light, dark, and high-contrast themes. In forced-colors mode the tooltip has a CanvasText border. The fade-in uses @starting-style and is removed under reduced motion or animation: 'none'.

configureAbsoluteUI({ components: { tooltip: { showDelay: 300, hideDelay: 200 } } });

Changes apply to existing instances (a visible tooltip is repositioned).

Instance options override global and theme defaults. All options are dynamic.

Content is set with textContent and never parsed as HTML. CSP-safe: positioning uses the CSSOM.

No persistence, network access, or telemetry.

Five listeners on the trigger, two on the tooltip, and one configuration subscription per instance. While visible: one overlay-stack entry, one window pointerdown listener, and the positioning engine’s observers, all released on hide.

Any number of instances per page; at most one is visible at a time.

  • Moving the pointer straight from the trigger onto the tooltip keeps it open, even with hideDelay: 0 (hiding is always asynchronous).
  • A tooltip shown by keyboard focus stays visible when the pointer leaves.
  • Inside a Popover, the first Escape hides the tooltip and the second closes the popover.
  • Pointer down outside the trigger and tooltip hides the tooltip before the overlay stack runs, so the overlay below still gets its outside-pointer dismissal.
  • Inside a modal <dialog>, the tooltip is appended to that dialog so it stays hoverable (the rest of the page is inert).
Code Category Cause
TOOLTIP_TRIGGER configuration The trigger is not a connected HTMLElement.
TOOLTIP_OPTIONS configuration Empty or non-string content, or an invalid relation.
CFG_* configuration Invalid placement, showDelay, or hideDelay.

A failed construction leaves no element, ARIA reference, listeners, or subscriptions behind. A failed update() keeps the previous options.

  • Touch pointers never show the tooltip.
  • Disabled native buttons get no pointer or focus events; use aria-disabled if they need a tooltip.
  • One visible tooltip per loaded copy of the module.
  • The trigger gap is fixed at 6 px.
  • Screen-reader behavior has not yet been manually verified with NVDA, JAWS, and VoiceOver.
  • Rich or interactive content (use Popover).
  • Showing on tap or long press.