DocumentationComponentsTheme CreatorGitHub
Theme CreatorGitHubIntroduction
InstallationUsageTheming
ComponentsAccordionAction SheetAlertAlertDialogArea ChartAspectRatioAvatarBadgeBannerBar ChartBottom NavBreadcrumbButtonButtonGroupCalendarCardCarouselChartChatBubbleChatBubbleNewCheckboxChipCoachMarkCodeBlockCollapsibleColor PickerComboboxCommandContainerContextMenuDate PickerDividerDrawerDropdown MenuEmptyStateFABFieldFieldsetFile UploadFlexFormGridHoverCardIconButtonLabelLine ChartLinkMenubarMeterModalNavigation MenuNumberFieldOTP InputPaginationPickerPie ChartPopoverProgressPromptInputRadar ChartRadial ChartRadioRatingScroll AreaSearchSegmentedControlSelectShortcutSidebarSkeletonSliderCircularSliderMediaTrimmerSpacerSpinnerSplit ButtonStackStatusStepperSurfaceSwitchTableTabsTextareaTime PickerToastToggleButtonToggleGroupTokenizerToolbarTooltipTop Header DesktopTop Header Mobile
Contributing
Components

Tooltip

Short hover and keyboard-focus hint anchored to a control. Composable trigger and content on Base UI, role=tooltip, 4 sides × 3 alignments with collision flipping, 5 intent plates, 3 sizes, an optional arrow, grouped delays via a provider, and a label/description ARIA switch.

Playground

A tooltip never appears on touch. Base UI opens it on hover with mouseOnly, and on focus only under keyboard modality — so a phone or tablet user sees nothing, and there is no long-press fallback. Anything a touch user must be able to read cannot live in a tooltip: put it in visible text, in the control's aria-label, or behind a Popover, which opens on tap.

Installation

pnpm add @tessinaui/ui

Usage

import { Tooltip, TooltipTrigger, TooltipContent } from "@tessinaui/ui";
<Tooltip>
  <TooltipTrigger render={<Button variant="outline">Save</Button>} />
  <TooltipContent>Saves your draft without publishing it</TooltipContent>
</Tooltip>

TooltipTrigger renders a <button> by default; render swaps in any element and keeps the wiring. TooltipContent collapses Base UI's portal, positioner and popup into one part, and draws the arrow for you unless you pass arrow={false}.

Rules

These are house rules because the design systems we surveyed agree on them, not because of a local preference.

  • Nothing interactive inside. No links, no buttons, nothing focusable — a tooltip closes as the pointer leaves the trigger, so anything in it is a trap. Use Popover for content people need to reach.
  • Nothing essential inside. A tooltip is hidden by default, absent on touch, and easy to miss. If a task cannot be completed without the text, it belongs in helper text or a label.
  • The trigger must be focusable, so the tip opens for a keyboard user too.
  • Keep it short — a phrase or a sentence, sentence case.
  • Don't put a tooltip on a disabled control if you can avoid it. See Disabled triggers for the case where you cannot.

Which component?

You wantUse
A short hint on hover/focusTooltip
Content people tap, click or interact withPopover
A rich preview of an entity on hoverHoverCard
A tour step, coach mark or feature calloutCoachMark
A value readout on a chartChart's own ChartTooltip

Examples

Default

Sides

side picks the edge; the tip flips automatically when it would collide with the viewport. (Flipping is switched off in this example so all four stay put.)

Alignment

align slides the tip along the chosen side.

Intents

Five plates — none is the always-dark overlay, and the four semantic values paint the matching surface. The arrow reads the plate's own published fill, so it can never drift off the tip.

Delay

delay and closeDelay are separate, and both are inherited by the trigger.

Grouped delays

Wrap a cluster in TooltipProvider: the first tip waits out the delay, then every neighbour you sweep onto opens instantly for timeout ms. Without it, a toolbar makes you wait at every single icon.

Keyboard shortcuts

The most common piece of structure inside a real tooltip. Key renders on-color caps, so they read on the plate with no extra styling.

Naming vs describing an icon button

relationship decides what the tip is to assistive tech, and it is the one prop worth thinking about on every tooltip you write.

  • "description" (default) — the tip adds detail to a control that already has a name, via aria-describedby while the tip is open.
  • "label" — the tip becomes the trigger's accessible name, for an icon-only button. The text is also rendered into an always-present visually hidden copy, because a name that only exists while the pointer hovers is no name at all; the visible popup is then hidden from AT so nothing is announced twice.
  • "none" — no ARIA wiring, for a trigger that is already fully described.

label is for a bare trigger you built yourself. The house IconButton requires an aria-label or aria-labelledby in its type — it can never be the unnamed case — so pair a tooltip on one with description and let it add detail instead.

Disabled triggers

A disabled button fires no pointer events, so a tooltip attached to one never opens. Make the wrapper the trigger instead.

Prefer not to need this. In a survey of real products, nobody explained a disabled control with a hover tip — they used an inline banner or helper text, which a touch user and a screen-reader user can also reach.

API Reference

Tooltip

The root. Holds the style context and the open state.

PropTypeDefaultDescription
size"sm" | "md" | "lg""md"Padding and text size
rounded"none" | "sm" | "md" | "lg" | "full""md"Corner radius. The arrow's tip radius cascades from it
intent"none" | "error" | "warning" | "success" | "info""none"Plate colour
relationship"description" | "label" | "none""description"How the tip relates to the trigger for AT
delaynumber600ms before opening on hover
closeDelaynumber0ms before closing
openboolean—Controlled open state
defaultOpenboolean—Uncontrolled initial state
onOpenChange(open, event) => void—Fires on every open/close
disabledbooleanfalseThe tip never opens. Does not disable the trigger element
hoverablePopupbooleantrueKeep the tip open while the pointer is over the tip itself
trackCursorAxis"none" | "x" | "y" | "both""none"Follow the cursor instead of anchoring to the trigger's box
dir"ltr" | "rtl""ltr"Text direction applied to the popup

TooltipTrigger

Renders a <button>, or any element via render. Accepts delay and closeDelay to override the root's, plus every native button prop.

TooltipContent

PropTypeDefaultDescription
side"top" | "right" | "bottom" | "left""top"Which side of the trigger to sit on
align"start" | "center" | "end""center"Alignment along that side
sideOffsetnumber6Distance from the trigger, floored to 10 when the arrow is on
alignOffsetnumber0Offset along the side axis
collisionPaddingnumber8Keep this far from the viewport edge
collisionAvoidanceobject—Base UI's config. { side: "none" } pins the tip to its declared side
arrowbooleantrueRender the pointer
portalbooleantrueRender in a portal
containerHTMLElement | nulldocument.bodyPortal target
positionerClassNamestring—Classes for the positioner element

size, rounded and intent can also be set here to override the root.

TooltipProvider

PropTypeDefaultDescription
delaynumber600Shared open delay for the group
closeDelaynumber0Shared close delay
timeoutnumber400Window after a tip closes during which the next opens instantly

TooltipArrow

Rendered for you by TooltipContent. Pass one explicitly to add a className; yours wins over the automatic one.

Accessibility

  • The popup is role="tooltip" with an id, and the trigger points aria-describedby at it only while it is open — a permanent reference to an unmounted popup is a dangling IDREF that axe flags.
  • A caller's own aria-describedby / aria-labelledby is kept, and ours appended, never clobbered.
  • Escape closes the tip.
  • The tip opens on keyboard focus, not just hover.
  • Assistive tech: verified with VoiceOver on Safari and Chrome. NVDA and JAWS are untested — no Windows machine in the loop.
ToolbarTop Header Desktop

On this page

PlaygroundInstallationUsageRulesWhich component?ExamplesDefaultSidesAlignmentIntentsDelayGrouped delaysKeyboard shortcutsNaming vs describing an icon buttonDisabled triggersAPI ReferenceTooltipTooltipTriggerTooltipContentTooltipProviderTooltipArrowAccessibility