Tooltip
since v0.4.0Tooltip root — pairs with TooltipTrigger and TooltipContent.
Wraps @radix-ui/react-tooltip (per ADR-0001) — Radix owns the focus,
dismiss, hover/blur timing, and portal semantics; we own class output and
the public API. Each <Tooltip> instance includes its own Provider so
consumers don't need to wire one at the app root.
Install
Add the package and import the component.
pnpm add @hey-mike/tungstenpnpm add @hey-mike/tungstenimport { Tooltip } from '@hey-mike/tungsten';import { Tooltip } from '@hey-mike/tungsten';Preview
Same fixtures used by the visual-regression suite.
Usage
apps/docs/app/snapshots/tooltip/page.tsx
import { Tooltip, TooltipTrigger, TooltipContent } from '@hey-mike/tungsten';
import { VariantGrid } from '../_components/VariantGrid';
import { HeroSpecimen } from '../_components/HeroSpecimen';
export default function TooltipSnapshot() {
return (
<VariantGrid
title="Tooltip"
// Gallery thumbnail: a static, non-portaled mock of the open tooltip. The
// real Tooltip portals to document.body, which escapes the hidden hero
// wrapper (it would leak a stray bubble onto the detail page) and positions
// nondeterministically inside the 160px iframe. The variants below stay
// closed — the interactive preview reveals the real tooltip on hover.
hero={
<HeroSpecimen className="max-w-[180px]">
<div className="flex flex-col items-center gap-1.5">
<span className="bg-ink-1 text-on-ink rounded-md px-2.5 py-1.5 text-xs">
Helpful description
</span>
<button
type="button"
className="border-stroke text-ink-2 rounded-md border px-2.5 py-1 text-xs"
>
Hover me
</button>
</div>
</HeroSpecimen>
}
variants={[
{
label: 'default',
node: (
<Tooltip>
<TooltipTrigger>
<button type="button">Hover me</button>
</TooltipTrigger>
<TooltipContent>Helpful description text</TooltipContent>
</Tooltip>
),
},
{
label: 'with kbd',
node: (
<Tooltip>
<TooltipTrigger>
<button type="button">Open palette</button>
</TooltipTrigger>
<TooltipContent kbd="⌘K">Command palette</TooltipContent>
</Tooltip>
),
},
{
label: 'side right',
node: (
<Tooltip>
<TooltipTrigger>
<button type="button">Right</button>
</TooltipTrigger>
<TooltipContent side="right">Appears on the right</TooltipContent>
</Tooltip>
),
},
{
label: 'side bottom',
node: (
<Tooltip>
<TooltipTrigger>
<button type="button">Bottom</button>
</TooltipTrigger>
<TooltipContent side="bottom">Appears below</TooltipContent>
</Tooltip>
),
},
]}
/>
);
}
import { Tooltip, TooltipTrigger, TooltipContent } from '@hey-mike/tungsten';
import { VariantGrid } from '../_components/VariantGrid';
import { HeroSpecimen } from '../_components/HeroSpecimen';
export default function TooltipSnapshot() {
return (
<VariantGrid
title="Tooltip"
// Gallery thumbnail: a static, non-portaled mock of the open tooltip. The
// real Tooltip portals to document.body, which escapes the hidden hero
// wrapper (it would leak a stray bubble onto the detail page) and positions
// nondeterministically inside the 160px iframe. The variants below stay
// closed — the interactive preview reveals the real tooltip on hover.
hero={
<HeroSpecimen className="max-w-[180px]">
<div className="flex flex-col items-center gap-1.5">
<span className="bg-ink-1 text-on-ink rounded-md px-2.5 py-1.5 text-xs">
Helpful description
</span>
<button
type="button"
className="border-stroke text-ink-2 rounded-md border px-2.5 py-1 text-xs"
>
Hover me
</button>
</div>
</HeroSpecimen>
}
variants={[
{
label: 'default',
node: (
<Tooltip>
<TooltipTrigger>
<button type="button">Hover me</button>
</TooltipTrigger>
<TooltipContent>Helpful description text</TooltipContent>
</Tooltip>
),
},
{
label: 'with kbd',
node: (
<Tooltip>
<TooltipTrigger>
<button type="button">Open palette</button>
</TooltipTrigger>
<TooltipContent kbd="⌘K">Command palette</TooltipContent>
</Tooltip>
),
},
{
label: 'side right',
node: (
<Tooltip>
<TooltipTrigger>
<button type="button">Right</button>
</TooltipTrigger>
<TooltipContent side="right">Appears on the right</TooltipContent>
</Tooltip>
),
},
{
label: 'side bottom',
node: (
<Tooltip>
<TooltipTrigger>
<button type="button">Bottom</button>
</TooltipTrigger>
<TooltipContent side="bottom">Appears below</TooltipContent>
</Tooltip>
),
},
]}
/>
);
}
Props
Surface specific to <Tooltip />.
| Prop | Type | Default | Description |
|---|---|---|---|
| defaultOpen | boolean | false | Render the content open on mount. Useful for visual snapshots. Uncontrolled mode. |
| open | boolean | — | Controlled open state. Pair with onOpenChange. |
| onOpenChange | ((open: boolean) => void) | — | Fires whenever Radix wants to open or close the tooltip. |
| delayDuration | number | 200 | Hover-intent delay before opening, in ms. Forwarded to Radix's Provider + Root. Defaults to 200. |
| skipDelayDuration | number | — | Window after a tooltip closes during which the next opens instantly, in ms. Forwarded to Radix's Provider. Defaults to Radix's 300. |
Sub-components
Composition slots re-exported from the same module.
TooltipTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
| children* | ReactNode | — | A single focusable element (e.g. <button>, <a href>). Radix forwards
its trigger props onto this element via asChild, so it must be a single
element that accepts a ref and renders a real, focusable DOM node — not
plain text or a fragment. This is what makes the tooltip keyboard- and
screen-reader-accessible. |
TooltipContent
| Prop | Type | Default | Description |
|---|---|---|---|
| side | "top" | "right" | "bottom" | "left" | top | Which side of the trigger the tooltip appears on. Forwarded to Radix's positioner. |
| kbd | string | — | Optional keyboard shortcut rendered as a mono-caps chip beside the label. |