Skip to content

Tooltip

A short label for a control, shown while the control is hovered or holds keyboard focus.

import { Tooltip } from '@zitdevs/ui';

Hover a button, or reach it with Tab. Press Escape to hide its tooltip without moving. These are server HTML: showing and hiding is CSS, and the page runs enhanceTooltips(document) for Escape.

<Tooltip id="copy-tip" label="Copy link">
<IconButton icon="copy" label="Copy link" />
</Tooltip>

8px from its trigger, on the side you choose. It does not move to stay on screen: pick the side with room, such as bottom for a toolbar at the top of the page.

Prop Type Default Notes
id string required The tooltip’s id: its trigger’s aria-describedby names it.
label string required A few words. Long ones wrap at 240px.
placement 'top' | 'bottom' | 'left' | 'right' 'top'
children ReactElement required The trigger: one focusable element. A disabled one renders with no tooltip.
ref Ref<HTMLSpanElement> — The wrapper around the trigger and its tooltip.
className string — On the wrapper.
anything else <span> attributes — On the wrapper.

Exported types: TooltipProps, TooltipPlacement. From @zitdevs/ui/enhance: enhanceTooltips.

  • It describes, it never names. The tooltip is the trigger’s description (aria-describedby, added to any it already has); an IconButton keeps its own label as its name, and the tooltip may repeat it for sighted readers.
  • Never the only place essential information lives. Touch screens show no hover, and some readers never focus the control. What a reader must know goes in the page itself.
  • Never on a disabled control, which a keyboard cannot reach: a disabled trigger renders alone. Say why something is unavailable next to it instead.
  • Content on hover or focus (WCAG 1.4.13). It shows on hover and on keyboard focus (not on a mouse click’s focus); it stays while the pointer moves onto it, since the 8px gap is part of it; and Escape hides it without moving the pointer or the focus. It stays hidden until both have left, and Escape goes to the tooltip first, so a dialog around it stays open.
  • Cream on navy, at 17.8:1. It fades in over 140ms, not under reduced motion.

Showing and hiding is CSS alone (group-hover, :has(:focus-visible)), so the tooltip works on server HTML with no script; only Escape needs the enhancer. React runs it once the tooltip hydrates; a page without React runs enhanceTooltips(document).

surface-inverse and on-inverse-quiet; radius-xs.

Use instead: a hint the reader must see to fill a field → that field’s hint text; a menu or anything interactive → a popover, not a tooltip.