Components

Tooltip

A floating label that surfaces a short hint when the user hovers or focuses an element — built on Radix UI with a directional arrow and smooth enter/exit animation.

Basic usage

Wrap a trigger with asChild and pair it with TooltipContent. Hover or tab to the button to reveal the hint. There is no provider to mount first — every Tooltip carries its own.

Placement

Use side to set the preferred direction. The tooltip automatically flips to stay in view when space is tight.

On icon buttons

Tooltips shine on icon-only controls, giving each glyph a discoverable text label without cluttering the toolbar.

Open delay

Tooltip mounts its own TooltipProvider with delayDuration={0} so a single tooltip works with no setup. Change the timing by passing delayDuration straight to the Tooltip.

Trap: an outer provider cannot change the delay

Radix resolves the innermost provider, and the one built into Tooltip is always innermost. Wrapping a region in TooltipProvider therefore has no effect on these tooltips — including the delayDuration={200} provider that ShellProvider mounts in @comitor/ui/shell. To share timing across a screen, pass the same delayDuration to each Tooltip, or keep a constant next to your other layout values.

Props

Every part forwards the full Radix prop set and exports its own prop type — TooltipProps, TooltipTriggerProps, TooltipContentProps and TooltipProviderProps.

Tooltip — the root, which also carries the timing.

PropTypeDefaultDescription
delayDurationnumber0Hover delay in ms before the tooltip opens. This is the only place the delay can be set — a surrounding TooltipProvider has no effect.
openbooleanControlled open state. Pair with onOpenChange.
defaultOpenbooleanfalseOpen state when initially rendered (uncontrolled).
onOpenChange(open: boolean) => voidCalled when the open state changes.
disableHoverableContentbooleanfalseCloses as soon as the pointer leaves the trigger, instead of letting the pointer travel into the tooltip.

TooltipContent — the floating label, portaled to the body. A TooltipPrimitive.Arrow is always rendered inside it; there is no prop to turn it off.

PropTypeDefaultDescription
childrenrequiredReact.ReactNodeTooltip content. Keep it to a short, plain-text label.
side'top' | 'right' | 'bottom' | 'left''top'Preferred side of the trigger the content renders against.
sideOffsetnumber0Distance in pixels between the trigger and the content. The built-in arrow already supplies the visual gap.
align'start' | 'center' | 'end''center'Alignment of the content against the trigger.
alignOffsetnumber0Offset in pixels along the alignment axis.
avoidCollisionsbooleantrueFlips and shifts the tooltip to keep it inside the viewport.
classNamestringAdditional Tailwind classes merged onto TooltipContent. The surface is w-fit on bg-foreground / text-background.

TooltipProvider — exported for API parity with shadcn. You rarely need it: see the trap above.

PropTypeDefaultDescription
delayDurationnumber0Shared hover delay. Kept for shadcn API compatibility — it only reaches Radix Tooltip roots you mount yourself, never a Tooltip from this package.
skipDelayDurationnumber300Window in ms during which moving to another trigger skips the delay. Same caveat: it does not reach this package's Tooltip.
disableHoverableContentbooleanfalseProvider-level default. Resolved against the nearest provider exactly like delayDuration, so it does not reach this package's Tooltip either — set it on the Tooltip.

TooltipTrigger takes asChild (default false) plus every native button prop.

Accessibility

  • Opens on hover and keyboard focus, so the hint is reachable without a pointer, and dismisses on Esc.
  • The content is wired to the trigger via aria-describedby, so screen readers announce it alongside the trigger's name.
  • Always give an icon-only trigger its own aria-label — a tooltip is a supplemental hint, not a replacement for an accessible name.
  • Reserve tooltips for non-essential hints; never hide content required to complete a task behind a hover-only tooltip.
  • The surface inverts the page — bg-foreground with text-background — so contrast holds in both the default and high-contrast palettes. Restyle it with role tokens if you must, never with a raw scale.