skip to content
menu
components · tooltip

tooltip

A tooltip is a small bubble with a short detail that people can do without, such as what a figure measures. It shows while the pointer rests on its trigger, or while the trigger has keyboard focus.

142 msp95 over the last 5 minutes

when to use

  • To give the detail behind a short value or term: the time window behind “142 ms”, the full time behind “3 min ago”, what an abbreviation stands for.
  • For information people can do without. Anything they need to finish a task belongs on the screen.

when to use something else

  • To name a control that is only an icon. An icon button takes a label, which becomes its accessible name and the browser's own tooltip.
  • For anything people need, such as an error, a warning or the consequence of an action. Touch screens have no hover, so people there never see the bubble. Put it on the page: in the hint or the error of a field, or in a banner.
  • For more than a short phrase. The bubble doesn't wrap. Write it on the page, or in a modal when it needs a decision.
  • For links, buttons or anything else to act on. The pointer can't rest on the bubble, the keyboard can't reach it, and it disappears when the pointer leaves the trigger. Offer the actions on the page, or behind an action menu.

anatomy

  1. Wrapper. It goes around your trigger. It is a tab stop of its own, and the bubble is its description.
  2. Trigger. Your content: a value or a term.
  3. Bubble. A small square box with a thin border, holding one line of small monospace text. It has a narrow maximum width. It sits a little away from the trigger, centred on it, with no arrow.

variants and states

The bubble goes above the trigger (the default), below it, or to its left or its right, centred along it. Nothing flips it or shifts it near the edge of the viewport, and a container that clips what overflows it cuts the bubble off: choose the side with room.

topshown abovebottomshown belowleftshown to the leftrightshown to the right

The bubble fades in quickly when the pointer rests on the trigger or the trigger gets keyboard focus, and it hides at once when both end. There is no delay before it shows.

Triggers can sit inside running text:

The rollout of api-7f9cbuilt 2026-06-15 09:12 finished on worker-23 of 3 replicas ready in 48 sfrom first pull to all replicas ready.

behaviour

  • Resting the pointer on the wrapper shows the bubble, and moving the pointer away hides it. The bubble ignores the pointer, so it can't be hovered, selected or clicked.
  • The wrapper is a tab stop: when tab brings keyboard focus to it, the bubble shows, and it hides when focus leaves.

The full keyboard map is on the tooltip's React page.

accessibility

  • The bubble describes the wrapper, not your trigger: a screen reader reads it as the wrapper's description.
  • The wrapper always adds a tab stop. On a value or a term, that is what lets people reach the detail with the keyboard. On a button, it means two tab stops for one control, and the button itself gets no description.

Today:

  • The focused wrapper draws no focus outline. The bubble appearing is the only sign of focus.
  • esc doesn't hide the bubble, and the pointer can't rest on it: two of the three things that WCAG 1.4.13 asks of content that appears on hover or focus. Keep only optional detail in it.
  • Text longer than the bubble's maximum width doesn't wrap; it runs out of the bubble.

content

  • One short phrase, lowercase, with no full stop: “p95 over the last 5 minutes”, “built 2026-06-15 09:12”.
  • Add to the trigger; don't repeat it. A tooltip that says what the trigger already says is noise.
  • Plain text only: no links, no buttons, nothing that has to be read in a particular order.

do and don't

do
142 msp95 over the last 5 minutes
An optional detail behind a short value.
don't
restarts every worker; requests fail for a minute
The consequence hides behind hover: people on touch screens, and anyone who doesn't hover, never see it. Say it on the page.
do
An icon button names itself: its label is its accessible name and the browser's own tooltip.
don't
restart worker-2
Wrapped in a tooltip, it shows two tooltips, the browser's own and the bubble, and puts an extra tab stop in front of the button.
do
worker-23 of 3 replicas ready
One short line.
don't
worker-2worker-2 runs 3 replicas; they restart one at a time after each release, and the last restart took 48 seconds
A sentence doesn't wrap: past the bubble's maximum width, it runs out of the bubble. If it needs a sentence, it belongs on the page.
  • Icon button: names a control that is only an icon; its label is also the browser's own tooltip.
  • Field: hints and errors next to the input they explain.
  • Banner: information people need, on the page.
  • Modal: when the detail needs a decision.

specs

part spec
Wrapper A span around your children, with tabindex="0" and aria-describedby pointing at the bubble. outline: none, so it draws no focus ring.
Bubble role="tooltip", its text from content. --surface-2 fill, a 1px --border-2 border, square, padding 4px 8px. JetBrains Mono 12px / 1.4 in --fg, on one line (white-space: nowrap), at most 240px wide. 6px from the trigger, centred on it, no arrow; z-index: 50, pointer-events: none.
Showing While the wrapper is hovered or has keyboard focus (:focus-visible), the bubble fades in over 100ms ease, with no delay. It hides at once when both end. CSS only, with no script.
side in code
Above (the default) side="top"
Below side="bottom"
Left side="left"
Right side="right"
  • Placement: with CSS alone (position: absolute), so an ancestor with overflow: hidden clips the bubble, and nothing flips or shifts it at the viewport's edge.
  • Long text: past 240px, text runs out of the bubble instead of wrapping.
  • Icon button: its label becomes its aria-label and its title, the browser's own tooltip.