Tooltip
Contextual help revealed on hover or focus. Small dark pill with an arrow pointing at the trigger. Never place critical information here — it must be discoverable.
Anatomy
- 1Trigger
Any focusable element. Pass asChild so the tooltip wraps YOUR element instead of adding its own.
- 2Content
The dark pill: bg-foreground, text-background, text-xs, rounded-md, shadow-sm, sideOffset 6.
- 3Arrow
Small triangle (10×5) pointing at the trigger, filled with the same foreground token. Included by default in TooltipContent.
- 4Provider
Global context that controls delayDuration + skipDelayDuration. Mount once at layout root (already done in this kit).
Sides
Set side="top" | "right" | "bottom" | "left" on TooltipContent. Default is 'top'. The arrow follows the side automatically — always points at the trigger. Radix auto-flips to the opposite side if there isn't enough room.
<Tooltip>
<TooltipTrigger asChild><Button variant="outline">Top</Button></TooltipTrigger>
<TooltipContent side="top">Hint on top</TooltipContent>
</Tooltip>With and without arrow
The arrow is on by default and is the Yuno default for most tooltips (a small triangle points at the trigger). Pass arrow={false} for compact toolbars or dense zones where the arrow would feel noisy.
<Tooltip>
<TooltipTrigger asChild><Button variant="outline">Hover me</Button></TooltipTrigger>
<TooltipContent>Create new rule</TooltipContent>
</Tooltip><Tooltip>
<TooltipTrigger asChild><Button variant="outline">Hover me</Button></TooltipTrigger>
<TooltipContent arrow={false}>Compact toolbar hint</TooltipContent>
</Tooltip>Delay
delayDuration on <TooltipProvider> (or per-<Tooltip>) controls how long the user hovers before the tooltip opens. Default in the kit: 200ms. Use 0 for critical hints, 700ms for casual ones.
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild><Button variant="outline">Instant</Button></TooltipTrigger>
<TooltipContent>Opens immediately</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipProvider delayDuration={700}>
<Tooltip>
<TooltipTrigger asChild><Button variant="outline">Slow</Button></TooltipTrigger>
<TooltipContent>Opens after 700ms</TooltipContent>
</Tooltip>
</TooltipProvider>Length limits
Yuno tooltips are short. Ideal: under 40 characters (a single line). OK: up to ~350 characters (about 5 wrapped lines). Anything longer belongs in a Popover. The kit caps content width at max-w-sm (384px) so long strings wrap into multiple lines instead of stretching horizontally.
<TooltipContent>Create new rule</TooltipContent><TooltipContent>
Blocks payments over $500 USD from countries flagged as high-risk in the last 30 days.
Applies only to card and wallet payment methods.
</TooltipContent>Recipes
Ready-to-copy compositions covering the most common Yuno usages of this atom.
The most common Yuno usage: expose the meaning of an icon-only button. Always paired with aria-label on the button.
<Tooltip>
<TooltipTrigger asChild>
<button type="button" aria-label="Actions"
className="inline-flex h-8 w-8 items-center justify-center rounded-md border border-transparent bg-transparent text-muted-foreground transition-all hover:border-border hover:bg-background hover:text-foreground hover:shadow-xs data-[state=open]:border-border data-[state=open]:bg-background data-[state=open]:text-foreground">
<DotsThreeOutline weight="fill" className="size-4" />
</button>
</TooltipTrigger>
<TooltipContent>Actions</TooltipContent>
</Tooltip>Explain why a field is disabled. Because a disabled Input can't receive hover events, the Tooltip wraps a small parent span that stays hoverable.
<Tooltip>
<TooltipTrigger asChild>
{/* Wrap the disabled Input in a span so hover still fires */}
<span tabIndex={0}>
<Input placeholder="Managed by admin" disabled />
</span>
</TooltipTrigger>
<TooltipContent>Ask an admin to unlock this field.</TooltipContent>
</Tooltip>Reveal the full text of a truncated label (table cell, chip). The trigger is the truncated span itself.
<Tooltip>
<TooltipTrigger asChild>
<span className="block max-w-40 truncate text-sm">
Very long rule name that gets truncated in the table cell
</span>
</TooltipTrigger>
<TooltipContent>
Very long rule name that gets truncated in the table cell
</TooltipContent>
</Tooltip>Import
Copy this import line at the top of the file where you compose this atom.
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";Props
Everything else from the underlying HTML or Radix primitive is forwarded via ...props.
| Prop | Type | Default | Description |
|---|---|---|---|
| Tooltip.delayDuration | number | provider (200) | Per-tooltip override for how long to hover before opening (ms). |
| Tooltip.open / defaultOpen | boolean | — | Controlled or uncontrolled open state. Rare — usually hover-driven. |
| TooltipTrigger.asChild | boolean | false | Merges props onto the child element (Radix Slot). Always pass asChild when wrapping a real element (Button, span, native button). |
| TooltipContent.side | "top" | "right" | "bottom" | "left" | "top" | Preferred side. Radix auto-flips if there's no room. The arrow follows the side automatically. |
| TooltipContent.sideOffset | number | 6 | Distance in px between the trigger and the content. |
| TooltipContent.arrow | boolean | true | Show the small triangle pointing at the trigger. Turn off for compact toolbars or dense zones. |
| TooltipContent.className | string | — | Merged with base classes via cn(). Base includes max-w-xs so long strings wrap into 2 lines. |
| TooltipProvider.delayDuration | number | 200 (kit default) | Global hover delay before opening. Applies to every Tooltip inside. |
| TooltipProvider.skipDelayDuration | number | 300 | If the user moves between triggers within this window, the next tooltip opens instantly. |
When to use
- Explain an icon-only button.
- Give context to a truncated label.
- Short definitions of technical terms.
- Explain why a control is disabled.
When not to use
- Critical info the user must see — must be visible, not hidden.
- Long content — use Popover.
- Actionable content (links, buttons) — use Popover or DropdownMenu.
- Mobile-only prototypes — hover doesn't exist there.
Usage
- Wrap the app with <TooltipProvider> once at layout level.
- Keep tooltip text under 8 words.
- Always pair icon-only buttons with a tooltip AND aria-label.
- For disabled triggers, wrap them in a hoverable <span> so the tooltip still opens.
- Don't put actionable elements (links, buttons, forms) inside a Tooltip.
- Don't use Tooltip on touch-only surfaces — hover doesn't exist.
- Don't hide information the user would need to complete a task.
Related
Cross-links to atoms and patterns you may reach for next.
- PopoverFor longer content or when the user needs to interact with what's inside.
- Hover cardFor richer preview content (avatars, previews) with a bigger surface.
- Dropdown menuWhen the hover reveal should offer actions, not just info.