Tooltip
A short label shown on hover (after a delay) or keyboard focus. Touch is ignored. No ARIA is added; give the trigger its own accessible name.
WAI-ARIA patternInstallation
npx nuecrea-ui add tooltipUsage
import { Button } from '@/components/ui/button';
import * as Tooltip from '@/components/ui/tooltip';
function Tip({
label,
tip,
side,
arrowPadding,
disabled,
}: {
label: string;
tip: string;
side?: 'top' | 'bottom' | 'left' | 'right';
arrowPadding?: number;
disabled?: boolean;
}) {
return (
<Tooltip.Root disabled={disabled}>
<Tooltip.Trigger render={<Button variant="outline" size="sm" />}>
{label}
</Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Positioner side={side} arrowPadding={arrowPadding}>
<Tooltip.Popup>
<Tooltip.Arrow />
{tip}
</Tooltip.Popup>
</Tooltip.Positioner>
</Tooltip.Portal>
</Tooltip.Root>
);
}
export default function TooltipStory() {
return (
<Tooltip.Provider>
<main className="relative h-[520px]">
{/* At the very top: side="top" has no room and flips to bottom. */}
<div className="absolute top-0 left-40">
<Tip label="Save" tip="Save (⌘S)" />
</div>
{/* A toolbar: once one tooltip has shown, the next opens without delay. */}
<div className="absolute top-40 left-40 flex gap-2">
<Tip label="Bold" tip="Bold (⌘B)" />
<Tip label="Italic" tip="Italic (⌘I)" />
<Tip label="Underline" tip="Underline (⌘U)" side="bottom" />
<Tip label="Locked" tip="Not shown" disabled />
</div>
{/* Against the left edge with a large arrowPadding: the arrow is uncentered. */}
<div className="absolute top-72 left-0">
<Tip label="Help" tip="Keyboard shortcuts and docs" arrowPadding={70} />
</div>
{/* Room to scroll a trigger out of view (data-anchor-hidden). */}
<div aria-hidden="true" className="absolute top-full h-[600px] w-px" />
</main>
</Tooltip.Provider>
);
}API reference
Tooltip.Provider
Groups tooltips: after one shows, the next opens instantly within `timeout`.
| Prop | Type | Default | Description |
|---|---|---|---|
delay | number | — | Overrides each trigger's open delay. |
closeDelay | number | — | Overrides each trigger's close delay. |
timeout | number | 400 | Window after a tooltip closes in which the next opens instantly. |
Tooltip.Root
Owns one tooltip's open state.
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | — | Controlled open state. |
defaultOpen | boolean | false | Initial open state. |
disabled | boolean | false | Never opens. |
disableHoverablePopup | boolean | false | Close as soon as the pointer leaves the trigger. |
| Event | Payload | Description |
|---|---|---|
onOpenChange | boolean | Angular: the `open` model's change event. |
onOpenChangeComplete | boolean | After the animation finishes. |
Tooltip.Trigger
Shows the tooltip on hover and keyboard focus. Renders <button>.
| Prop | Type | Default | Description |
|---|---|---|---|
delay | number | 600 | Hover delay before opening (ms). |
closeDelay | number | 0 | Delay before closing (ms). |
closeOnClick | boolean | true | Close when the trigger is pressed. |
disabled | boolean | false | Disables the tooltip (not the trigger). |
| Data attribute | Present when |
|---|---|
data-popup-open | open |
data-trigger-disabled | the tooltip is disabled |
Tooltip.Portal
Moves the tooltip to the end of <body>. Renders <div>.
| Prop | Type | Default | Description |
|---|---|---|---|
keepMounted | boolean | false | Keep in the DOM while closed. |
Tooltip.Positioner
Positions the tooltip; same options as Popover, default side top. Renders <div> with role presentation.
| Prop | Type | Default | Description |
|---|---|---|---|
side | 'top' | 'bottom' | 'left' | 'right' | "top" | Preferred side; flips on collision. |
align | 'start' | 'center' | 'end' | "center" | Alignment. |
sideOffset | number | 0 | Gap (styled default 8). |
alignOffset | number | 0 | Alignment shift. |
arrowPadding | number | 5 | Arrow distance from popup edges. |
collisionPadding | number | 5 | Distance from viewport edges. |
| Data attribute | Present when |
|---|---|
data-open | open |
data-closed | closed / animating out |
data-anchor-hidden | trigger scrolled out of view |
data-align | always: resolved alignment |
data-side | always: resolved side |
data-instant | 'focus' (keyboard focus), 'delay' (group handover), 'dismiss' (Escape): skip animation |
Tooltip.Popup
The tooltip content. Hovering it keeps it open. Renders <div>.
| Data attribute | Present when |
|---|---|
data-open | open |
data-closed | closed / animating out |
data-align | always: resolved alignment |
data-instant | 'focus' (keyboard focus), 'delay' (group handover), 'dismiss' (Escape): skip animation |
data-side | always: resolved side |
data-starting-style | animating in |
data-ending-style | animating out |
Tooltip.Arrow
Points at the trigger. Renders <div>.
| Data attribute | Present when |
|---|---|
data-open | open |
data-closed | closed / animating out |
data-uncentered | clamped by arrowPadding |
data-align | always: resolved alignment |
data-instant | 'focus' (keyboard focus), 'delay' (group handover), 'dismiss' (Escape): skip animation |
data-side | always: resolved side |
Keyboard
| Key | Action |
|---|---|
| Tab | Focusing the trigger with the keyboard shows the tooltip at once. |
| Escape | Hides it. |