Browse the docs
Tooltip
Supplementary label shown on hover and on keyboard focus. It is CSS only — no positioning library, no state, and it renders in a server component.
Props
| Prop | Values | Default | Description |
|---|---|---|---|
content | node | — | What the bubble says. |
side | "top", "bottom", "left", "right" | "top" | Which side of the trigger it sits on. |
id | string | — | Put the same value in the trigger's aria-describedby. |
className | string | "" | Applied to the wrapper, not to the bubble. |
children is the trigger. It must be focusable for the tooltip to be
reachable by keyboard — a <button> or a link, not a <span>.
Common patterns
// The usual case
<Tooltip content="Runs the last successful build again">
<Button variant="outline">Redeploy</Button>
</Tooltip>
// Associated as a description, so a screen reader reads it too
<Tooltip id="delete-tip" content="This cannot be undone.">
<Button variant="danger" aria-describedby="delete-tip">Delete</Button>
</Tooltip>
// An icon-only trigger still needs its own name
<Tooltip content="Copy to clipboard" side="right">
<Button variant="ghost" aria-label="Copy to clipboard">
<CopyIcon />
</Button>
</Tooltip>Notes
A tooltip is never the only label. The bubble is not read by default, so
an icon-only button that relies on it has no accessible name at all. Give the
trigger aria-label, then add the tooltip on top for sighted users.
It does not flip. Having no JavaScript means nothing measures the
viewport, so a side="top" tooltip near the top edge is clipped. Choose the
side that has room — in a toolbar pinned to the top, that is bottom.
It does not appear on touch. There is no hover on a touchscreen, and tap does not focus. Anything a phone user must know belongs in visible text.
If you need flipping and collision detection, that is a positioning problem rather than a styling one, and it needs real measurement code. This component is deliberately the small version.