Capítulo 47 de 51

Chapter 47: Tooltip

Core Idea

A sighted mouse/keyboard-only visual label — disabled entirely on touch devices (no reliable touch-reveal gesture exists, and it would conflict with long-press context menus), never a substitute for labeling the trigger itself, and the wrong choice whenever the trigger's own purpose is "open this popup" rather than something else.

Key Concepts

  • Usage guideline (critical) — trigger still needs its own label: tooltips are visual-only; the trigger must carry its own aria-label closely matching the tooltip content, since screen-reader users never see the tooltip at all.
  • Tooltip vs. Popover decision rule: if the trigger's purpose is to open the popup, it's a Popover (with openOnHover); if the trigger does something else and the popup is incidental clarification, it's a Tooltip. "Infotip" info-icon patterns are Popover, not Tooltip, specifically so touch/AT users can still reach the content.
  • Never gate critical information behind a tooltip: if the content matters for understanding the element, use inline text or a Popover instead — tooltips are unreachable on touch and by screen readers.
  • Disabled on touch by design: no system-standard touch affordance exists (iOS has none; Android's long-press conflicts with browser contextual menus) — this is deliberate library behavior, not a bug to work around.
  • Contextual feedback: use Toast's anchoring instead of a tooltip for transient feedback messages that need to be screen-reader-announced.
  • Anatomy: ProviderRootTrigger, PortalPositionerPopupArrow, Viewport.
  • Provider: sets shared delay/closeDelay/timeout for every tooltip beneath it — the same "consistent hover timing app-wide" pattern as Navigation Menu's Root-level delay.
  • trackCursorAxis (on Root): makes the tooltip follow the cursor along one axis instead of staying anchored to the trigger element — useful for tooltips on wide elements (e.g. a chart) where position should reflect pointer location.
  • disableHoverablePopup: prevents the tooltip itself from being hoverable (normally you can move the pointer from trigger into the tooltip content without it closing) — disable when the tooltip is purely informational and shouldn't be interactive.

Reference Tables

PartNotable propsNotable data attributes
Providerdelay, closeDelay, timeout
Rootopen/onOpenChange, trackCursorAxis, disableHoverablePopup, handle
TriggercloseOnClick, delay/closeDelay (override), disableddata-popup-open, data-trigger-disabled
Positionerfull anchored-positioning family (side/align/collisionBoundary/...)data-anchor-hidden, data-align, data-side

Key Takeaways

  1. Before reaching for Tooltip, ask what the trigger's own purpose is — "opens more info" → Popover; "does something else, tooltip just clarifies" → Tooltip. Getting this backwards breaks touch/screen-reader access to real content.
  2. Always set aria-label on the trigger to mirror the tooltip text — this is not optional, since AT users have zero other way to get that information.
  3. Use Provider once per app (or per logical UI region) to set consistent delay/closeDelay, rather than tuning timing per tooltip.

Connects To

  • ch033 (Popover): the accessible alternative whenever content must reach touch/AT users — "infotip" pattern uses Popover with openOnHover, not Tooltip.
  • ch043 (Toast): preferred over Tooltip for transient feedback messages needing screen-reader announcement.