Tooltip
ImplementedHover/focus tooltip portaled to document.body as a <div role='tooltip'> (role='dialog' when interactive), linked to its single trigger via aria-describedby. No show/hide delay exists and the portaled surface is positioned against the viewport, not the anchor — surface positioning is being overhauled (tracked).
patterns/molecules/tooltip.htmlTier-2 molecule alias for Tooltip. Same component as the base Tooltip; the long-form alias keeps the *Molecule-family naming consistent.
TooltipMolecule
Overlay molecule — Family 6 (Tier 2 alias)
Floating surface that surfaces contextual text on hover or focus. Clones the child to attach hover/focus/blur listeners; portals to document.body to escape parent overflow contexts.
This component supports light and dark mode.
Usage
TooltipMolecule accepts one anchor child (a single React element). It attaches the hover/focus listeners via React.cloneElement — anchor components that already define onMouseEnter/onFocus are preserved.
import { Tooltip as TooltipMolecule, Button } from '@epasslive/design-system'
<TooltipMolecule content="Saves the event." placement="right">
<Button variant="primary">Save</Button>
</TooltipMolecule>Placement — known limitation
placement is a static hint ("top" | "right" | "bottom" | "left", default "top"). There is no arrow and no collision logic. Be aware: the surface portals to document.body with position: fixed while using viewport-relative utility classes, so the percentage offsets resolve against the viewport rather than the trigger — the bubble currently renders off-screen in every placement and the demos above stay silent on hover. This is a tracked defect (positioning overhaul: anchor measurement via getBoundingClientRect + viewport clamping — see cascade step 6); no show/hide delay exists either, despite older docs claiming 200ms.
Props
content*React.ReactNodeBody of the tooltip surface.
children*React.ReactElementA single React element used as the anchor.
placementTooltipPlacement"top" | "right" | "bottom" | "left". Defaults to "top".
openbooleanControlled visibility. When omitted, hover/focus drive the internal state.
onOpenChange(open: boolean) => voidCalled on every hover/focus state change. Pair with `open` for the controlled (Popover-style) contract where the parent owns the open state.
interactivebooleanWhen true the floating content receives pointer events (links/controls inside stay clickable). Default false keeps the bubble pointer-events-none. Use controlled mode (`open` + `onOpenChange`) with interactive content so the pointer can move from trigger to bubble without closing it.
ariaLabelstringAccessible name forwarded to the tooltip surface.
idstringOptional id for the surface; auto-generated when omitted.
Props
Tooltip
contentReact.ReactNode—Tooltip text/content; i18n belongs to the consumer.
childrenReact.ReactElement—Single trigger element — exactly one child (its props are cloned to attach aria-describedby).
placement'top' | 'right' | 'bottom' | 'left''top'Positioning preset — currently a static hint; the surface is not measured against the trigger (tracked fix).
openbooleanuncontrolledControlled visibility; omit for the internal hover/focus state (pairs with onOpenChange).
onOpenChange(open: boolean) => void—Notified on hover/focus change; pairs with controlled `open` for the parent-owns-state contract (Popover replacement, D1 merge 3).
interactivebooleanfalseFloating content receives pointer events so links/controls inside stay clickable; the surface then renders role='dialog'.
ariaLabelstring—Accessible name for the interactive surface (used when interactive=true).
idstringauto (useId)Optional id for the surface; auto-generated when omitted — the trigger's aria-describedby points at it.
<Tooltip />Accessibility
Type
1 ruleARIA
1 ruleUsage
Import
import { Tooltip } from "@epasslive/design-system"Example 1
<Tooltip content="Save the event"><Button>Save</Button></Tooltip>Example 2
<Tooltip content={help} open={open} onOpenChange={setOpen} interactive><a href="/help">Help</a></Tooltip>Anti-patterns
— Do not put critical actions inside a non-interactive tooltip — content is pointer-events-none unless interactive
— Do not assume anchor-aware positioning — the portaled surface is currently viewport-positioned (tracked overhaul)