Tooltip
A popup that displays contextual information when an element is hovered or focused.
Areia's Tooltip is built on @data-slot/tooltip and rendered as an Ilha island when you use Tooltip. It handles hover/focus interactions, warm-up behavior between nearby tooltips, collision-aware positioning, Escape dismissal, and accessible tooltip attributes.
import ilha from "ilha";
import { Plus } from "lucide";
import { Button, Icon, Tooltip } from "areia";
export default ilha.render(() => (
<Tooltip content="Create project">
<Button
shape="square"
icon={<Icon icon={Plus} />}
aria-label="Create project"
/>
</Tooltip>
));Import
import { Tooltip } from "areia";
Usage
Call Tooltip(...) for an interactive island. The root renders the tooltip markup and initializes the @data-slot/tooltip controller after mount.
import ilha from "ilha";
import { Button, Tooltip } from "areia";
export default ilha.render(() => (
<Tooltip content="Helpful contextual information">
<Button>Hover me</Button>
</Tooltip>
));For static server-rendered markup or custom initialization, use Tooltip.Static(...). For most application usage, call Tooltip(...).
Examples
Basic Tooltip
Use content for the popup and children for the trigger content.
import ilha from "ilha";
import { Info } from "lucide";
import { Button, Icon, Tooltip } from "areia";
export default ilha.render(() => (
<Tooltip content="This action cannot be undone.">
<Button
variant="secondary"
shape="square"
icon={<Icon icon={Info} />}
aria-label="More information"
/>
</Tooltip>
));Text Trigger
When the trigger is not an action, render it as a span.
Areia
import ilha from "ilha";
import { Tooltip } from "areia";
export default ilha.render(() => (
<Tooltip
triggerAs="span"
triggerClass="underline decoration-dotted underline-offset-4"
content="Areia is a vanilla TypeScript component library."
>
Areia
</Tooltip>
));Multiple Tooltips
Tooltips share @data-slot/tooltip warm-up behavior automatically. After one tooltip closes, hovering another tooltip within skipDelayDuration opens it without the normal delay.
import ilha from "ilha";
import { Languages, Plus, Settings } from "lucide";
import { Button, Icon, Tooltip } from "areia";
export default ilha.render(() => (
<div class="flex items-center gap-2">
<Tooltip content="Create project">
<Button
shape="square"
icon={<Icon icon={Plus} />}
aria-label="Create project"
/>
</Tooltip>
<Tooltip content="Translate">
<Button
shape="square"
icon={<Icon icon={Languages} />}
aria-label="Translate"
/>
</Tooltip>
<Tooltip content="Settings">
<Button
shape="square"
icon={<Icon icon={Settings} />}
aria-label="Settings"
/>
</Tooltip>
</div>
));Side
Use side to choose the preferred side of the trigger. Collision handling may flip the tooltip at runtime when there is not enough space.
import ilha from "ilha";
import { Button, Tooltip } from "areia";
export default ilha.render(() => (
<div class="grid grid-cols-2 gap-3">
<Tooltip side="top" content="Top tooltip">
<Button>Top</Button>
</Tooltip>
<Tooltip side="bottom" content="Bottom tooltip">
<Button>Bottom</Button>
</Tooltip>
<Tooltip side="left" content="Left tooltip">
<Button>Left</Button>
</Tooltip>
<Tooltip side="right" content="Right tooltip">
<Button>Right</Button>
</Tooltip>
</div>
));Alignment
Use align to align the tooltip along the selected side.
import ilha from "ilha";
import { Button, Tooltip } from "areia";
export default ilha.render(() => (
<div class="flex items-center gap-3">
<Tooltip align="start" content="Start aligned">
<Button>Start</Button>
</Tooltip>
<Tooltip align="center" content="Center aligned">
<Button>Center</Button>
</Tooltip>
<Tooltip align="end" content="End aligned">
<Button>End</Button>
</Tooltip>
</div>
));Delay Control
Use delay to control how long to wait before opening. Use skipDelayDuration to control the warm-up window after a tooltip closes.
import ilha from "ilha";
import { Button, Tooltip } from "areia";
export default ilha.render(() => (
<div class="flex flex-wrap items-center gap-3">
<Tooltip delay={1000} content="Opens after one second">
<Button>1s delay</Button>
</Tooltip>
<Tooltip
delay={0}
skipDelayDuration={0}
content="Opens instantly with no warm-up window"
>
<Button>Instant</Button>
</Tooltip>
</div>
));Without Arrow
Set arrow={false} to hide the decorative arrow.
import ilha from "ilha";
import { Button, Tooltip } from "areia";
export default ilha.render(() => (
<Tooltip arrow={false} content="No arrow">
<Button>Hover me</Button>
</Tooltip>
));Custom Content
The content prop accepts markup.
import ilha from "ilha";
import { Button, Tooltip } from "areia";
export default ilha.render(() => (
<Tooltip
content={
<div class="flex max-w-48 flex-col gap-1">
<span class="font-medium">Deploy preview</span>
<span class="text-areia-subtle">
Creates a temporary preview environment.
</span>
</div>
}
>
<Button>Deploy</Button>
</Tooltip>
));Custom Trigger
Use trigger when you need complete control over the trigger markup. The custom trigger must include data-slot="tooltip-trigger".
import ilha from "ilha";
import { Tooltip } from "areia";
export default ilha.render(() => (
<Tooltip
content="Custom trigger markup"
trigger={
<span
data-slot="tooltip-trigger"
tabindex="0"
class="inline-flex rounded-full bg-areia-surface-muted px-2 py-1 text-sm text-areia-default"
>
Beta
</span>
}
/>
));Static Composition
Use low-level parts for static markup or when you want to call createTooltip yourself.
Hover me
import ilha from "ilha";
import { Tooltip } from "areia";
export default ilha.render(() => (
<Tooltip.Static
content="Static tooltip markup"
triggerAs="span"
>
Hover me
</Tooltip.Static>
));API Reference
Tooltip
Tooltip is an Ilha island. It accepts standard HTML div attributes plus @data-slot/tooltip options.
| Prop | Type | Default | Description |
|---|---|---|---|
content | unknown | - | Content to display inside the tooltip popup. |
children | unknown | - | Trigger content when trigger is not provided. |
trigger | unknown | - | Custom trigger markup. Must include data-slot="tooltip-trigger". |
triggerAs | "button" | "span" | "div" | "a" | "span" | Tag used for the generated trigger. Defaults to span to avoid nested buttons when wrapping Button(...). |
arrow | boolean | true | Whether to render the decorative arrow. |
class | string | - | Additional CSS classes applied to the tooltip root. |
className | string | - | Alias for class. |
triggerClass | string | - | Additional CSS classes applied to the generated trigger. |
triggerClassName | string | - | Alias for triggerClass. |
contentClass | string | - | Additional CSS classes applied to the tooltip popup. |
contentClassName | string | - | Alias for contentClass. |
side | "top" | "right" | "bottom" | "left" | "inline-start" | "inline-end" | "top" | Preferred side relative to the trigger. |
align | "start" | "center" | "end" | "center" | Preferred alignment along the selected side. |
sideOffset | number | 4 | Distance from the trigger in pixels. |
alignOffset | number | 0 | Offset from the aligned edge in pixels. |
avoidCollisions | boolean | true | Flip or shift to stay inside the viewport. |
collisionPadding | number | 8 | Viewport edge padding used for collision handling. |
delay | number | 300 | Delay before showing the tooltip in milliseconds. |
skipDelayDuration | number | 300 | Warm-up window where another tooltip opens without delay. Set 0 to disable. |
portal | boolean | true | Portal content to document.body while open. |
onOpenChange | (open: boolean) => void | - | Called when visibility changes. |
Tooltip.Trigger
Generated trigger element.
| Prop | Type | Default | Description |
|---|---|---|---|
as | "button" | "span" | "div" | "a" | "span" | Trigger tag name. |
children | unknown | - | Trigger content. |
class | string | - | Additional CSS classes. |
className | string | - | Alias for class. |
Tooltip.Content
Popup container.
| Prop | Type | Default | Description |
|---|---|---|---|
children | unknown | - | Tooltip content. |
arrow | boolean | true | Whether to render Tooltip.Arrow. |
side | "top" | "right" | "bottom" | "left" | "inline-start" | "inline-end" | "top" | Preferred side styling. |
class | string | - | Additional CSS classes. |
className | string | - | Alias for class. |
Tooltip.Arrow
Decorative arrow element. The controller positions it with inline top/left values and data-side attributes.
| Prop | Type | Default | Description |
|---|---|---|---|
children | unknown | SVG | Custom arrow content. |
class | string | - | Additional CSS classes. |
className | string | - | Alias for class. |
Accessibility
Behavior
- Opens on pointer hover for mouse/pen input.
- Opens on keyboard focus.
- Touch hover is ignored; touch devices use focus behavior.
- Pressing
Escapehides the tooltip. - Moving from the trigger to the tooltip content keeps it open.
- Disabled triggers do not open tooltips.
ARIA
The controller automatically handles:
role="tooltip"on the popup content.aria-describedbyon the trigger while the tooltip is open.aria-hidden="true"/"false"on the tooltip content.- Stable generated IDs for content when needed.
Trigger Labels
Tooltips are supplemental. Icon-only triggers still need an accessible name, usually with aria-label on the trigger button.