Skip to content

Tooltip

Open .md
Live preview

Loading example…

Short, non-interactive hint for a control. Opens on hover (after openDelay) and on keyboard focus, closes on Escape, blur, press and pointer leave. Touch never opens it by hover, so a tap always reaches the control. The trigger is described by it (aria-describedby), and the tooltip never takes focus.

import { Tooltip } from "@jcsoftdev/ui/overlays";

Single-component entry points tree-shake to just this component: @jcsoftdev/ui/overlays/tooltip.

<Tooltip content="Save changes" openDelay={50} closeDelay={0}>
<Button>Save</Button>
</Tooltip>
Prop Type Default Description
content * ReactNode The hint. It repeats or complements the trigger’s name; never put information only here.
shortcut ReactNode Keyboard shortcut shown next to the hint, rendered as a Kbd.
side "top" | "bottom" | "left" | "right" "top"
align "center" | "end" | "start" "center"
sideOffset number 6 Gap between the trigger and the tooltip, in px.
openDelay number 300 ms the pointer must rest on the trigger before the tooltip opens. Keyboard focus opens at once.
closeDelay number 100 ms before closing after the pointer leaves, so it can travel onto the tooltip (WCAG 1.4.13).
open boolean Controlled open state.
defaultOpen boolean false
onOpenChange ((open: boolean) => void)
disabled boolean false Never show the tooltip, e.g. when the label is already visible.
container HTMLElement | null Portal target. Defaults to document.body; pass an element to keep a scoped data-style.
material "solid" | "glass" | "glass-clear" | "liquid" | "liquid-clear" Surface material; see PopoverContent.
className string
classNames Partial<Record<TooltipPart, string>> Per-part classes merged with cn; consumer classes win. className still targets the content and is merged last.
children * ReactElement<unknown, string | JSXElementConstructor<any>> The element that gets the tooltip. It receives aria-describedby while the tooltip is open.

* marks a required prop.

packages/ui/src/overlays/tooltip/tooltip.tsx