implement
Tooltip

Tooltip

A short label revealed by hovering or focusing a control.

import { Div } from "@implementjs/core";
import {
	Tooltip,
	TooltipContent,
	TooltipPortal,
	TooltipProvider,
	TooltipTrigger,
} from "@/lib/components/ui/tooltip";

export default function TooltipDemo() {
	return TooltipProvider(
		Div(
			{ class: "flex items-center gap-2" },
			Tooltip(
				TooltipTrigger({ variant: "outline" }, "Hover"),
				TooltipPortal(TooltipContent("Add to library")),
			),
			Tooltip(
				TooltipTrigger({ variant: "outline" }, "Or hover this"),
				TooltipPortal(TooltipContent("No delay the second time")),
			),
		),
	);
}

Installation

npx jsrepo add @implementjs/ui/tooltip

jsrepo pulls button along with it.

Usage

A small inverted bubble above the trigger. Tooltips label — they do not hold content, and nothing inside one should be interactive; for that, use a popover.

TooltipTrigger renders through the button styles, so the thing being labelled is usually the trigger itself.

import {
	Tooltip,
	TooltipContent,
	TooltipProvider,
	TooltipTrigger,
} from "@/lib/components/ui/tooltip";

TooltipProvider(
	Tooltip(TooltipTrigger({ variant: "outline" }, "Hover me"), TooltipContent("Add to library")),
);

The provider

TooltipProvider is what makes a group of tooltips feel like one: after the first opens, moving to a neighbour opens its tooltip immediately instead of waiting out the delay again. Wrap a toolbar — or the whole app — in one, and set delayDuration and skipDelayDuration there.

Placement

side, align, and offset are on the content, defaulting to top / center / 6. The bubble flips when it would leave the viewport and animates out of whichever side it settled on.

API Reference

Every prop the styling does not consume is forwarded to the Tooltip primitive, so the tables below are the whole surface — the behavior props and the styling ones together.

TooltipProvider

Shares timing across every tooltip inside it: one open at a time, and moving between triggers within skipDelayDuration skips the open delay. Optional — a Tooltip without one uses these defaults.

PropTypeDefaultDescription
delayDurationnumber700How long the pointer must rest on a trigger before the tooltip opens, in milliseconds.
skipDelayDurationnumber300After a tooltip closes, how long another trigger opens instantly instead of waiting through the delay again, in milliseconds.
disableHoverableContentbooleanfalseClose as soon as the pointer leaves the trigger instead of letting it travel to the content.
disableCloseOnTriggerClickbooleanfalseKeep the tooltip open when the trigger is clicked.
disabledbooleanfalseDisables every tooltip under the provider.
ignoreNonKeyboardFocusbooleanfalseOnly open on focus when the focus is keyboard-driven (matches :focus-visible).

Tooltip

The root. Owns whether the tooltip is open and provides that to the parts inside it. The timing props default to the provider's values.

PropTypeDefaultDescription
openSignal<boolean> | booleanfalseThe open state. Pass a signal to control it from outside; a boolean seeds uncontrolled state.
delayDurationnumberOverrides the provider's delayDuration for this tooltip.
disableHoverableContentbooleanOverrides the provider's disableHoverableContent for this tooltip.
disableCloseOnTriggerClickbooleanOverrides the provider's disableCloseOnTriggerClick for this tooltip.
disabledbooleanOverrides the provider's disabled for this tooltip.
ignoreNonKeyboardFocusbooleanOverrides the provider's ignoreNonKeyboardFocus for this tooltip.

TooltipTrigger

Opens the tooltip on hover after the delay, or instantly on keyboard focus. Clicking closes it. While open it points at the content with aria-describedby. Renders a Button; extra props are forwarded onto it.

PropTypeDefaultDescription
variant"default" | "destructive" | "outline" | "secondary" | "ghost" | "link""default"Which button style the part renders with. Also set as data-variant.
size"default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg""default"The button's height and padding scale. Also set as data-size.
disabledSignal<boolean> | booleanfalseWhile true this trigger never opens the tooltip.
Data attributeValue
[data-tooltip-trigger]Present
[data-state]"delayed-open" | "instant-open" | "closed"
[data-disabled]Present when disabled
[data-delay-duration]The resolved open delay in milliseconds

TooltipContent

The tooltip bubble. Styled as a small inverted panel that fades and scales in from the side it opens on, and hides itself when closed. Renders a Div; extra props are forwarded onto it.

PropTypeDefaultDescription
side"top" | "bottom" | "left" | "right""top"Preferred side of the trigger to place the bubble.
align"start" | "center" | "end""center"How the bubble aligns along the chosen side.
offsetnumber6Distance in pixels between the trigger and the bubble.
Data attributeValue
[data-tooltip-content]Present
[data-state]"delayed-open" | "instant-open" | "closed"
[data-side]"top" | "bottom" | "left" | "right"
[data-align]"start" | "center" | "end"
CSS variableDescription
--ip-tooltip-content-transform-originThe transform origin of the content element.
--ip-tooltip-content-available-widthThe available width of the content element.
--ip-tooltip-content-available-heightThe available height of the content element.
--ip-tooltip-anchor-widthThe width of the anchor element.
--ip-tooltip-anchor-heightThe height of the anchor element.

TooltipPortal

Renders its children into another DOM parent so the bubble escapes overflow and stacking. This is the core Portal helper; context still resolves from where the portal is declared.

PropTypeDefaultDescription
toHTMLElement | Readable<HTMLElement>document.bodyThe element to mount into. Also available as chained .To(target).
disabledboolean | Readable<boolean>falseMount in place instead of teleporting. Also available as chained .Disabled(value).