Tooltip
A short hint for one control, on hover and keyboard focus: what it does, or the name of an icon-only control.
On this page
import { Button } from "@/components/Button";
import { Tooltip } from "@/components/Tooltip";
export function TooltipExample() {
return (
<Tooltip content="Copies a link to this page">
<Button iconStart="copy">Share</Button>
</Tooltip>
);
}Examples
Description
Announced as: “Save draft, button, Saves without publishing”
Icon-only names
No tooltip on the last: nameTooltip={false}.
Sides
Disabled reason
Press or tap it to see why it's disabled.
In running text
Browse the token galleryEvery color, type and space token for every value the components use.
Controlled
The tooltip is hidden.
Customized with component tokens
.customTooltip {
--component-tooltip-background: var(--color-accent-2-fill);
--component-tooltip-text: var(--color-accent-2-fill-text);
}Props
Any native prop not listed here goes to the root element unchanged.
Its one child is the trigger: a focusable element that takes a ref and DOM props, as every library control does. content holds no links, buttons or other interactive content. Use kind="name" only when content repeats the trigger's accessible name. className and other native props go to the tooltip popup.
| Prop | Type | Default | Description |
|---|---|---|---|
content | ReactNode | — | Required. The tooltip's text. No links, buttons or other interactive content. |
kind | "description" | "name" | "description" | "description" becomes the trigger's accessible description. "name" only shows the trigger's name and isn't announced: use it only when content matches that name. |
side | "top" | "bottom" | "start" | "end" | "top" | The side of the trigger it shows on, centered. It flips when it doesn't fit, and shifts to stay in the viewport. |
open | boolean | — | Controlled open state; pair with onOpenChange. |
defaultOpen | boolean | false | Uncontrolled initial state. |
onOpenChange | (open: boolean) => void | — | Called on every requested show or hide, controlled or not: hover, focus, Escape, a press, and another tooltip showing. |
Component tokens
| Token | Copy | Description | Aliases | Light | Dark | Preview |
|---|---|---|---|---|---|---|
--component-tooltip-background | Bubble and arrow fill. | color.surface.inverse | #17202bfrom color.gray.900 | #e6effffrom color.slate.50 | ||
--component-tooltip-text | Tooltip text. | color.text.inverse | #fbf8f2from color.sand.50 | #0b1220from color.slate.950 | ||
--component-tooltip-border-width | Bubble and arrow outline: transparent, drawn only in forced colors. | effect.border.width | 2pxfrom effect.line.2 | 2pxfrom effect.line.2 | ||
--component-tooltip-radius | Bubble corner radius. | radius.control | 4pxfrom radius.4 | 4pxfrom radius.4 | ||
--component-tooltip-padding-block | Block padding of the bubble. | space.stack.xs | 0.25remfrom space.1 | 0.25remfrom space.1 | ||
--component-tooltip-padding-inline | Inline padding of the bubble. | space.inset.sm | 0.5remfrom space.2 | 0.5remfrom space.2 | ||
--component-tooltip-max-width | Widest bubble; the text wraps beyond it (and it's never wider than the viewport less twice the offset). | size.column.md | 16remfrom size.64 | 16remfrom size.64 | ||
--component-tooltip-font-family | Tooltip text font. | type.family.body | 'Inter Variable', 'Inter Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.sans | 'Inter Variable', 'Inter Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.sans | ||
--component-tooltip-font-size | Tooltip text size. | type.size.small | 0.875remfrom type.font-size.sm | 0.875remfrom type.font-size.sm | ||
--component-tooltip-font-weight | Tooltip text weight. | type.weight.body | 400from type.font-weight.400 | 400from type.font-weight.400 | ||
--component-tooltip-name-font-weight | Weight of the name line above a disabled button's reason. | type.weight.strong | 600from type.font-weight.600 | 600from type.font-weight.600 | ||
--component-tooltip-line-height | Tooltip line height. | type.leading.body | 1.6from type.line-height.normal | 1.6from type.line-height.normal | ||
--component-tooltip-offset | Gap to the trigger, and from the viewport edge when shifted. | space.stack.sm | 0.5remfrom space.2 | 0.5remfrom space.2 | ||
--component-tooltip-arrow-size | Side of the arrow's rotated square. | space.stack.sm | 0.5remfrom space.2 | 0.5remfrom space.2 | ||
--component-tooltip-transition-duration | Duration of the fade (0ms under reduced motion). | motion.duration.fast | 150msfrom motion.time.150Reduced motion: 0ms | 150msfrom motion.time.150 | No preview | |
--component-tooltip-transition-easing | Easing of the fade. | motion.easing.enter | cubic-bezier(0, 0, 0.2, 1)from motion.curve.out | cubic-bezier(0, 0, 0.2, 1)from motion.curve.out | No preview |
Accessibility
- A "description" tooltip has role="tooltip" and becomes the trigger's accessible description. A "name" tooltip repeats the trigger's name for sighted users, hidden from assistive technology so the name isn't announced twice.
- Its text is in the page from the start, so the description is there before JavaScript loads and without hovering.
- It shows after a 300ms hover, and at once while moving along a row of controls. Keyboard focus shows it at once, since a keyboard user has nothing else to wait for; focus from a mouse press doesn't, and neither does focus coming back from the trigger's own menu.
- It stays until dismissed: Escape, moving the pointer off both the trigger and the tooltip, moving focus away, or pressing the trigger. The pointer can move onto it, and it never hides on a timer (WCAG 1.4.13).
- Around a library control, one bubble shows: the control's disabledReason while it's disabled, and this content otherwise. An icon-only control's name tooltip steps aside; its name is still its accessible name, and both descriptions are announced.
- Touch shows no tooltips, except a disabled Button's reason, which a tap shows and a tap elsewhere hides.
- Never put essential information only in a tooltip: many visitors never see it.
- It fades over component.tooltip.transition-duration, 0ms under reduced motion.
- In forced colors, the bubble and its arrow are outlined in a system color.
| Key | Action |
|---|---|
| Tab | Moves focus to the trigger, which shows its tooltip. |
| Escape | Hides the tooltip; focus stays on the trigger. |