Tooltip
A short label that appears when the pointer rests on something.
import { Button, Tooltip } from 'neba';
<Tooltip content="Copy the deploy URL">
<Button variant="outline" startIcon={<LinkIcon />} />
</Tooltip>;Props
| Prop | Type | Default | Description |
|---|---|---|---|
| content * | ReactNode | — | What it says. A short phrase: a tooltip cannot be reached by touch and nothing inside it can be clicked |
| children * | ReactElement | — | The single element it hangs off. The trigger merges onto it rather than wrapping it, so the layout gains no element |
| sizeshared | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'sm' | The plate's type scale and padding |
| colorshared | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'secondary' | Colour family. A tooltip is always a note about something else, so the neutral family is the honest default |
| densityshared | 'default' | 'compact' | 'default' | Padding only |
| side | 'top' | 'right' | 'bottom' | 'left' | 'top' | Which edge of the trigger it appears on. Flips to the opposite side when there is no room |
| align | 'start' | 'center' | 'end' | 'center' | Where it sits along that edge |
| sideOffset | number | 6 | Distance from the trigger, in pixels |
| delay | number | 600 | How long the pointer has to rest before it opens, in ms |
| closeDelay | number | 0 | How long it waits before closing, in ms |
| arrow | boolean | true | The little wedge pointing at the trigger |
| open | boolean | — | Whether it is open. With onOpenChange |
| defaultOpen | boolean | false | The initial state of an uncontrolled tooltip |
| onOpenChange | (open: boolean) => void | — | Called when it opens or closes |
| disabled | boolean | false | Stops it opening without disabling the trigger — for the tooltip that only exists while a label is truncated |
Examples
Sides
The side may flip when there is no room, which is Base UI's doing and is the right behaviour — a tooltip clipped by the window edge is worse than one on the other side.
One delay for a whole toolbar
TooltipProvider shares the delay across a group: once any of them has opened, its neighbours open instantly, and the wait comes back after a pause. Without it, moving along a row of icon buttons means waiting out the full delay at every stop — which is what makes tooltips feel like they are fighting the pointer.
It wraps, it does not add
Base UI's trigger merges itself onto the child rather than rendering a box of its own, so the tooltip costs the layout nothing and the child stays whatever it was — a button, a chip, a truncated table cell. The child has to be a single element that accepts a ref and spreads props, which every Neba component does.
Keep it short
A tooltip is not a container. It cannot be reached by a pointer on a touch screen, it disappears the moment attention moves, and anything inside it that could be clicked cannot be. Content that needs either of those is a popover's job.
That is also why the plate is filled rather than frosted. Everything else that floats in this library is translucent acrylic; a tooltip is read in the half-second it is up, over whatever happens to be behind it, and a translucent sheet is the one surface that cannot promise contrast.
Accessibility
Base UI handles the delay and the group timeout, opening on keyboard focus but not on a focus that came from a click, closing on Escape, and keeping the plate off the edges of the window.
The part it deliberately leaves open — a popup can be many things and only the caller knows which — is wired here: the plate carries role="tooltip", and the trigger gets an aria-describedby pointing at it while it is open, and nothing while it is closed.
A tooltip is a description, never a name. If a control needs a name, give it one: an icon-only button wants aria-label, and the tooltip beside it is the extra sentence, not the label.