Tooltip
A popup overlay that displays additional information when hovering over or focusing on a trigger element. Supports configurable positioning, delay duration, and max width.
import { Button } from "@tenstorrent/vesper/button";
import { Tooltip } from "@tenstorrent/vesper/tooltip";
export default function TooltipDemo() {
return (
<Tooltip content="This is a tooltip">
<Button variant="subtle">Hover me</Button>
</Tooltip>
);
}Options
| Prop | Type | Description | Default |
|---|---|---|---|
content | ReactNode | The content displayed inside the tooltip popup. | — |
side | "top" | "right" | "bottom" | "left" | The preferred side to render the tooltip against. | "top" |
sideOffset | number | The distance in pixels from the trigger to the tooltip (in addition to the arrow height). | 4 |
align | "center" | "start" | "end" | The alignment of the tooltip relative to the trigger. | "center" |
alignOffset | number | An offset in pixels from the aligned edge. | 0 |
open | boolean | Controls the open state of the tooltip (controlled mode). | — |
onOpenChange | (value: boolean) => void | Callback fired when the open state changes. | — |
delayDuration | number | Duration in milliseconds to wait before showing the tooltip. | 500 |
defaultOpen | boolean | Whether the tooltip is open by default (uncontrolled mode). | false |
maxWidth | number | The maximum width of the tooltip in pixels. Content wraps if exceeded. | 240 |
container | HTMLElement | ShadowRoot | null | RefObject<HTMLElement | ShadowRoot | null> | Specify the element or shadow root to portal the tooltip into. | – |
children | ReactNode | The trigger element that the tooltip is attached to. | — |
Examples
Basic usage
Render a Tooltip by wrapping children with it and giving it content. content can be any ReactNode, though it is highly advisable to just render plain text:
import { IconButton } from "@tenstorrent/vesper/icon-button";
import { Download } from "@tenstorrent/vesper/icons";
import { Tooltip } from "@tenstorrent/vesper/tooltip";
export default function BasicTooltip() {
return (
<Tooltip content="Download documents">
<IconButton
aria-label="Download"
variant="contrast"
icon={<Download />}
/>
</Tooltip>
);
}Tooltip's children must be a single React element and able to receive event handlers to listen for hover events. A Tooltip's content will not render otherwise.Positioning the tooltip
By default, a Tooltip floats above and is aligned to the center of its trigger. You can adjust the positioning of a Tooltip via the side, sideOffset, align, and alignOffset props.
The side prop determines which trigger edge the Tooltip sits against. Possible values are "top", "right", "bottom", and "left". The default value is "top".
The sideOffset prop determines how far away in pixels the Tooltip is from its trigger's edge. The default value is 4. This value scales with the base rem size.
The align prop determines which end of the side to align the Tooltip to. Possible values are "start", "end", and "center". The default value is "center".
The alignOffset prop adjusts how many pixels the Tooltip is offset from its alignment. The default value is 0. This value scales with the base rem size.
The demo below shows how the side and align props interact with each other:
import { useState } from "react";
import { Button } from "@tenstorrent/vesper/button";
import { Select } from "@tenstorrent/vesper/select";
import {
Tooltip,
TOOLTIP_ALIGNMENTS,
TOOLTIP_SIDES,
type TooltipAlign,
type TooltipSide,
} from "@tenstorrent/vesper/tooltip";
export default function TooltipPositionPlayground() {
const [side, setSide] = useState<TooltipSide>("top");
const [align, setAlign] = useState<TooltipAlign>("center");
return (
<div
style={{ display: "flex", flexDirection: "column", alignItems: "center" }}
>
<Tooltip
open
side={side}
align={align}
content={`${side} side, ${align} align`}
>
<Button
style={{
marginTop: "var(--vesper-spacing-16)",
marginBottom: "var(--vesper-spacing-20)",
}}
>
Trigger
</Button>
</Tooltip>
<div style={{ display: "flex", gap: "var(--vesper-spacing-4)" }}>
<Select
value={side}
aria-label="Tooltip side"
onValueChange={(side) => setSide(side as TooltipSide)}
options={[...TOOLTIP_SIDES]}
style={{ width: "var(--vesper-spacing-28)" }}
/>
<Select
value={align}
aria-label="Tooltip align"
onValueChange={(align) => setAlign(align as TooltipAlign)}
options={[...TOOLTIP_ALIGNMENTS]}
style={{ width: "var(--vesper-spacing-28)" }}
/>
</div>
</div>
);
}Adjusting the delay
A Tooltip does not instantly show when a user hovers its trigger. By default, a half-second delay occurs prior to showing a Tooltip. This duration can be adjusted via the delayDuration prop, which represents the amount of milliseconds to wait before showing a Tooltip:
import { Button } from "@tenstorrent/vesper/button";
import { Tooltip } from "@tenstorrent/vesper/tooltip";
export default function TooltipDelays() {
return (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
gap: "var(--vesper-spacing-4)",
}}
>
<Tooltip
content="This tooltip appears instantly"
delayDuration={0}
side="right"
>
<Button variant="contrast">No delay</Button>
</Tooltip>
<Tooltip
content="This tooltip appears delayed"
delayDuration={1000}
side="right"
>
<Button variant="contrast">1 second delay</Button>
</Tooltip>
</div>
);
}Setting the max width
The default maximum width a Tooltip can render at before its content wraps is 240 pixels. You can adjust this by passing a numeric value to the maxWidth prop. This value scales with the base rem size:
import { Button } from "@tenstorrent/vesper/button";
import { Tooltip } from "@tenstorrent/vesper/tooltip";
export default function WideTooltip() {
return (
<Tooltip
content="This tooltip contains a longer description that will wrap at the specified max width."
maxWidth={320}
side="right"
>
<Button variant="contrast">Details</Button>
</Tooltip>
);
}Controlling the open state
By default, a Tooltip tracks its own open state. If you need to control the open state of a Tooltip, you can do so via the open prop:
import { Button } from "@tenstorrent/vesper/button";
import { Tooltip } from "@tenstorrent/vesper/tooltip";
export default function ControlledTooltips() {
return (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
gap: "var(--vesper-spacing-4)",
}}
>
<Tooltip content="This tooltip is always showing" side="right" open>
<Button variant="subtle">Hover me</Button>
</Tooltip>
<Tooltip content="This tooltip never shows" side="right" open={false}>
<Button variant="subtle">Hover me</Button>
</Tooltip>
</div>
);
}Pair open with onOpenChange when you want to keep tracking hover and focus while still owning the state, eg. to keep a tooltip open while another part of the page is interacting with its trigger:
import { useState } from "react";
import { Button } from "@tenstorrent/vesper/button";
import { Checkbox } from "@tenstorrent/vesper/checkbox";
import { Tooltip } from "@tenstorrent/vesper/tooltip";
export default function ControlledTooltip() {
const [open, setOpen] = useState(false);
const [pinned, setPinned] = useState(false);
return (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
gap: "var(--vesper-spacing-4)",
}}
>
<Tooltip
content="The tooltip content"
side="right"
open={pinned || open}
onOpenChange={setOpen}
>
<Button variant="subtle">Hover me</Button>
</Tooltip>
<Checkbox
text="Keep the tooltip open"
checked={pinned}
onChange={(event) => setPinned(event.target.checked)}
/>
</div>
);
}Listening for open's value changing
You can respond to open's value changing via the onOpenChange prop:
Open state: false
import { useState } from "react";
import { Button } from "@tenstorrent/vesper/button";
import { Tooltip } from "@tenstorrent/vesper/tooltip";
import { Typography } from "@tenstorrent/vesper/typography";
export default function TooltipOpenChange() {
const [open, setOpen] = useState(false);
return (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
gap: "var(--vesper-spacing-4)",
}}
>
<Tooltip
content="The tooltip content"
side="right"
onOpenChange={(open) => setOpen(open)}
>
<Button variant="subtle">Hover me</Button>
</Tooltip>
<Typography variant="copy-sm">Open state: {String(open)}</Typography>
</div>
);
}Showing by default
Pass defaultOpen to show a Tooltip before the first user interaction. Because this is uncontrolled mode, normal interactions can subsequently close it (pressing Escape or moving the pointer away after hovering, for example):
import { Button } from "@tenstorrent/vesper/button";
import { Tooltip } from "@tenstorrent/vesper/tooltip";
export default function DefaultOpenTooltip() {
return (
<Tooltip content="This tooltip shows by default" side="right" defaultOpen>
<Button variant="subtle">Hover me</Button>
</Tooltip>
);
}