Skip to content

Tooltip

A popup that displays contextual information when an element is hovered or focused.

Source Code

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.

PropTypeDefaultDescription
contentunknown-Content to display inside the tooltip popup.
childrenunknown-Trigger content when trigger is not provided.
triggerunknown-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(...).
arrowbooleantrueWhether to render the decorative arrow.
classstring-Additional CSS classes applied to the tooltip root.
classNamestring-Alias for class.
triggerClassstring-Additional CSS classes applied to the generated trigger.
triggerClassNamestring-Alias for triggerClass.
contentClassstring-Additional CSS classes applied to the tooltip popup.
contentClassNamestring-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.
sideOffsetnumber4Distance from the trigger in pixels.
alignOffsetnumber0Offset from the aligned edge in pixels.
avoidCollisionsbooleantrueFlip or shift to stay inside the viewport.
collisionPaddingnumber8Viewport edge padding used for collision handling.
delaynumber300Delay before showing the tooltip in milliseconds.
skipDelayDurationnumber300Warm-up window where another tooltip opens without delay. Set 0 to disable.
portalbooleantruePortal content to document.body while open.
onOpenChange(open: boolean) => void-Called when visibility changes.

Tooltip.Trigger

Generated trigger element.

PropTypeDefaultDescription
as"button" | "span" | "div" | "a""span"Trigger tag name.
childrenunknown-Trigger content.
classstring-Additional CSS classes.
classNamestring-Alias for class.

Tooltip.Content

Popup container.

PropTypeDefaultDescription
childrenunknown-Tooltip content.
arrowbooleantrueWhether to render Tooltip.Arrow.
side"top" | "right" | "bottom" | "left" | "inline-start" | "inline-end""top"Preferred side styling.
classstring-Additional CSS classes.
classNamestring-Alias for class.

Tooltip.Arrow

Decorative arrow element. The controller positions it with inline top/left values and data-side attributes.

PropTypeDefaultDescription
childrenunknownSVGCustom arrow content.
classstring-Additional CSS classes.
classNamestring-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 Escape hides 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-describedby on 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.