Photon Design System

Tooltip

A brief label that appears on hover or focus — most often naming an icon-only control. Built on Base UI Tooltip. Tooltips are supplementary and visual-only: never put essential or interactive content in one (use Popover for that).

Import

import { Tooltip } from "@photon-ai/pho-ui/components/tooltip";

Basic

Wrap your app (or a section) in a single Tooltip.Provider to share timing. The trigger must be focusable — hover it or tab to it. Because the tooltip is visual-only, give the trigger an aria-label that matches the popup text.

<Tooltip.Provider>
  <Tooltip.Root>
    <Tooltip.Trigger
      render={
        <Button svgOnly aria-label="Copy deployment URL">
          <Copy />
        </Button>
      }
    />
    <Tooltip.Portal>
      <Tooltip.Positioner>
        <Tooltip.Popup>Copy deployment URL</Tooltip.Popup>
      </Tooltip.Positioner>
    </Tooltip.Portal>
  </Tooltip.Root>
</Tooltip.Provider>

Placement

Tooltip.Positioner places the popup with side (top by default) and align (center). It flips to the opposite side to avoid collisions. The pho-ui positioner sets a sideOffset of 6; override it or alignOffset for more gap.

<Tooltip.Positioner side="right">
  <Tooltip.Popup>Copy deployment URL</Tooltip.Popup>
</Tooltip.Positioner>

Grouping

Tooltips under one Tooltip.Provider share a delay. Once one opens, moving to an adjacent trigger within the provider’s timeout (400 ms) opens the next instantly — so a toolbar of icon buttons feels responsive rather than making you wait through the open delay each time.

<Tooltip.Provider delay={400}>
  {formats.map(({ label, icon: Icon }) => (
    <Tooltip.Root key={label}>
      <Tooltip.Trigger
        render={
          <Button svgOnly aria-label={label}>
            <Icon />
          </Button>
        }
      />
      <Tooltip.Portal>
        <Tooltip.Positioner>
          <Tooltip.Popup>{label}</Tooltip.Popup>
        </Tooltip.Positioner>
      </Tooltip.Portal>
    </Tooltip.Root>
  ))}
</Tooltip.Provider>

Anatomy

  • Tooltip.Provider — shares open/close delays across tooltips
  • Tooltip.Root — owns a single tooltip’s open state
  • Tooltip.Trigger — the focusable anchor
  • Tooltip.Portal → Positioner → Popup — floating content

Props

Set on Tooltip.Provider:

  • delay — number — ms before a tooltip opens
  • closeDelay — number — ms before a tooltip closes
  • timeout — number (default 400) — window for the next tooltip to open instantly

Set on Tooltip.Root:

  • open — boolean — controlled open state
  • defaultOpen — boolean (default false) — uncontrolled initial open state
  • onOpenChange — (open, eventDetails) => void — fires when the tooltip opens or closes
  • disabled — boolean (default false) — prevent the tooltip from opening
  • trackCursorAxis — none · x · y · both (default none) — follow the cursor along an axis
  • disableHoverablePopup — boolean (default false) — close when the pointer leaves the trigger instead of allowing hover onto the popup

Set on Tooltip.Trigger:

  • delay — number (default 600) — ms before this trigger opens the tooltip
  • closeDelay — number (default 0) — ms before this trigger closes the tooltip
  • closeOnClick — boolean (default true) — close when the trigger is clicked

Set on Tooltip.Positioner:

  • side — top · bottom · left · right · inline-start · inline-end (default top)
  • align — start · center · end (default center)
  • sideOffset — number (default 6 in pho-ui) — gap between trigger and popup
  • alignOffset — number (default 0) — offset along the alignment axis

Accessibility

Tooltips serve sighted mouse and keyboard users; they are not exposed to touch or screen-reader users. Because of that, the tooltip text is not a substitute for labelling the trigger — the trigger must have its own aria-label (or visible text) that closely matches the popup, so screen-reader users get the same information. If the content is essential or interactive, use Popover with openOnHover instead.

The trigger renders a focusable <button> and gets data-popup-open while the tooltip is showing.

Behavior:

  • Opens on hover or keyboard focus; closes on blur or pointer leave
  • Escape closes the open tooltip
  • Opening waits out the delay; adjacent triggers in the same Provider open instantly within the timeout window

Best practices

  • Use a tooltip to name an icon-only control or add a short hint — keep it to a few words, no links or buttons.
  • Give the trigger an aria-label matching the tooltip text; the tooltip alone won’t reach touch or screen-reader users.
  • The trigger must be focusable so keyboard users get the tooltip too; an icon button is ideal.
  • Don’t hide critical information behind a tooltip; if losing it would block the task, put it inline or in a Popover.