Tooltip
A short, non-interactive hint shown on hovering or focusing an element.
Installation
- Install the package.
npm install @hashintel/ds-components- Import the component.
import { Button, Tooltip } from "@hashintel/ds-components";
export const EntityUpdatedAt = () => (
<Tooltip content="Updated 14 Oct 2026, 09:32">
<Button variant="subtle" iconName="clockRotateLeft">
Updated 2 hours ago
</Button>
</Tooltip>
);When to use
- Do: Use Tooltip for brief supplementary context: the absolute time behind a relative timestamp, a keyboard shortcut, the full value behind a truncation.
- Do: For icon-only buttons, prefer Button's
tooltipprop — it renders this component and doubles as the accessible name. - Do: Use HelpTooltip for the info-icon-beside-a-label convention in forms and property panels.
When not to use
- Don't: Don't put essential instructions or values in a tooltip — touch users never see one; put them in visible text such as a form Description.
- Don't: Don't put interactive content in a tooltip — it dismisses on pointer-leave, making links and buttons inside unreachable; use Popover.
- Don't: Don't use a tooltip for rich previews, menus, or multi-paragraph help — use Popover or Menu.
Examples
Basic
Wrap the trigger and pass content. The default tooltip is dark, placed on
top, and opens after a medium delay on hover — or immediately on keyboard
focus.
Variants
Tooltips are dark by default. Use light where the dark overlay loses
contrast, such as over dark surfaces or imagery.
Positions
position prefers one of twelve placements — four sides, each with optional
-start/-end alignment — and falls back to a better fit when the viewport
cuts it off. gapX/gapY set the distance to the trigger in pixels (default
8).
Delays
openDelay sets the hover-to-open wait: none, fast,
medium (the default), slow. closeDelay takes the same
values for closing. Keyboard focus always opens immediately.
Disabling the tooltip
Set disableTooltip to render only the trigger — for hints that apply
conditionally, such as showing a full name only while it's truncated. The
wrapper also gives up its tab stop, so keyboard navigation is unaffected.
Rich content
content accepts any React node — keep it to one short line and strictly
non-interactive.
import { Button, Tooltip } from "@hashintel/ds-components";
export const SearchShortcutHint = () => (
<Tooltip
content={
<>
Search entities <kbd>⌘ K</kbd>
</>
}
>
<Button variant="subtle" iconName="search" aria-label="Search entities" />
</Tooltip>
);Custom overlays with BaseTooltip
BaseTooltip is the unstyled primitive behind Tooltip: identical trigger
behavior, positioning, and delays, but no overlay styling. Its content must
be a single element that carries its own styling — it is rendered as the
tooltip element itself.
import { BaseTooltip, Button } from "@hashintel/ds-components";
export const FlowRunSummary = () => (
<BaseTooltip
position="bottom"
content={
<div
style={{
width: 240,
padding: 12,
borderRadius: 8,
background: "white",
boxShadow: "0 8px 24px rgba(0, 0, 0, 0.16)",
}}
>
<strong>Sync flow</strong>
<p>Last run finished 12 minutes ago — 148 entities updated.</p>
</div>
}
>
<Button variant="subtle" iconName="diagramProject">
Run summary
</Button>
</BaseTooltip>
);Behavior
- The tooltip closes on pointer-leave (after
closeDelay), blur,Escape, page scroll, and clicking the trigger. SetcloseOnClick={false}for triggers whose keyboard activation fires a native click — radios checked via arrow keys would otherwise dismiss the tooltip that just opened on focus. - When nothing inside the trigger is keyboard-focusable, the wrapper becomes a
tab stop so the hint stays keyboard-reachable; it stops being one when the
child becomes focusable or
disableTooltipis set. - The menu's content mounts renders in a portal above the page, preventing most z-index + overflow issues.
Content
- Keep content to a short phrase or sentence, under 20 words, under 10 words is even better.
- Write sentence case. Skip the period on fragments; punctuate complete sentences.
- Add information instead of restating: the absolute timestamp behind "2 hours ago" — never "Save" on a button labelled "Save".
- When naming an icon-only control, use verb-first labels of 1–3 words: "Copy entity ID", "Run flow".
Accessibility
Tooltip implements the WAI-ARIA tooltip pattern.
What Tooltip provides
- The content has
role="tooltip"and is linked to the trigger witharia-describedbywhile open, so screen readers announce it as the trigger's description. - Keyboard focus opens the tooltip without delay;
Escapeand blur close it. - Triggers containing nothing focusable get a tab stop, keeping the hint reachable by keyboard.
What you must do
- Give the trigger its own accessible name — the tooltip is a description, not
a name. For icon-only buttons, prefer Button's
tooltipprop, which wires both. - Keep content non-interactive: the tooltip dismisses on pointer-leave and blur, so links or buttons inside are unreachable — use Popover.
API reference
Tooltip
| Prop | Type | Description |
|---|---|---|
| children | ReactNode | The tooltip trigger |
| content | ReactNode | The content rendered inside the tooltip overlay |
| aria-label | string | Accessible name for the trigger wrapper. Only for triggers where the wrapper itself becomes the focusable element (icon-only, non-interactive children) — pair it with a role that permits naming. When the trigger contains a focusable control, label that control instead. |
| role | AriaRole | ARIA role for the trigger wrapper, e.g. img for an icon-only trigger |
| position | Position default: "top" | The preferred position of the tooltip; another may be chosen for a better fit. |
| disableTooltip | boolean | Whether to disable the tooltip |
| openDelay | "none" | "medium" | "fast" | "slow" default: "medium" | The delay before the tooltip opens on hover or focus (fast 200ms, medium 500ms, slow 1s). |
| closeDelay | "none" | "medium" | "fast" | "slow" default: "medium" | The delay before the tooltip closes after hover or focus ends (fast 100ms, medium 200ms, slow 300ms). |
| gapX | number default: 8 | The horizontal distance in pixels between the tooltip and the trigger. |
| gapY | number default: 8 | The vertical distance in pixels between the tooltip and the trigger. |
| closeOnClick | boolean default: true | Whether clicking the trigger closes the tooltip. Disable when keyboard activation dispatches a native click — e.g. a radio checked via arrow keys — which would instantly dismiss the tooltip that just opened on focus. |
| onOpen | (() => void) | |
| onClose | (() => void) | |
| variant | "light" | "dark" default: "dark" | Whether to display a light or dark tooltip |
BaseTooltip
| Prop | Type | Description |
|---|---|---|
| children | ReactNode | The tooltip trigger |
| content | ReactNode | The content rendered inside the tooltip overlay |
| aria-label | string | Accessible name for the trigger wrapper. Only for triggers where the wrapper itself becomes the focusable element (icon-only, non-interactive children) — pair it with a role that permits naming. When the trigger contains a focusable control, label that control instead. |
| role | AriaRole | ARIA role for the trigger wrapper, e.g. img for an icon-only trigger |
| position | Position default: "top" | The preferred position of the tooltip; another may be chosen for a better fit. |
| disableTooltip | boolean | Whether to disable the tooltip |
| openDelay | "none" | "medium" | "fast" | "slow" default: "medium" | The delay before the tooltip opens on hover or focus (fast 200ms, medium 500ms, slow 1s). |
| closeDelay | "none" | "medium" | "fast" | "slow" default: "medium" | The delay before the tooltip closes after hover or focus ends (fast 100ms, medium 200ms, slow 300ms). |
| gapX | number default: 8 | The horizontal distance in pixels between the tooltip and the trigger. |
| gapY | number default: 8 | The vertical distance in pixels between the tooltip and the trigger. |
| closeOnClick | boolean default: true | Whether clicking the trigger closes the tooltip. Disable when keyboard activation dispatches a native click — e.g. a radio checked via arrow keys — which would instantly dismiss the tooltip that just opened on focus. |
| onOpen | (() => void) | |
| onClose | (() => void) |
Was this page helpful?