opsinjs
ComponentsOverlay

Tooltip

A short supplementary label that appears when a pointer rests on a control or a keyboard moves focus to it, for information a reader can do without.

Preview

Theme
Density
Text
Status
tooltip · base base · style base-lyraOpen under the product theme

Installation

pnpm dlx shadcn@latest add @opsinjs/tooltip

The @opsinjs namespace is declared in your components.json. Everything it installs is code you then own. There is no runtime package to keep in step.

Usage

import { Tooltip } from "@/components/ui/tooltip"
<Tooltip content="A rolling mean across the nights you have logged.">
  <button type="button">Overnight average</button>
</Tooltip>

When to use it

Use it when

  • Expanding an icon-only control's name, already carried through aria-label.
  • Adding a short optional note beside a label that already stands on its own.
  • Surfacing a value's full form on demand, where the reading is already on screen.

Do not use it when

  • The label carries something a touch reader needs. A phone has no hover, so the tooltip never opens for them.Use callout instead.
  • You are explaining what a clinical word means. That belongs inline, in the accessibility tree.Use term instead.
  • You are attaching a hint to a form control, such as a date format. A field hint is always visible.Use field instead.

Anatomy

  1. TooltipThe root. Owns open state, delay and portal.Controlled by content
  2. Tooltip.TriggerThe caller's own focusable element, keeping its own aria-label.Controlled by children
  3. Tooltip.PopupA raised card at the footnote step, portalled against the preferred side.Controlled by side
  4. Tooltip.ArrowA rotated square in the popup's card fill.
  • Tooltip 1Provider plus root
    • Tooltip.Trigger 1data-slot="tooltip-trigger"
    • Tooltip.Popup 1data-slot="tooltip-popup", portalled
      • Tooltip.Arrow 1data-slot="tooltip-arrow"

Examples

On an icon button

Each icon button carries its own name through aria-label; content repeats it visibly. The labels name fictional actions.

Theme
Density
Text
Status
tooltip-on-an-icon-button · base base · style base-lyraOpen under the product theme

A supplementary hint

The setting is named in plain words, so a touch reader needs no hover, and content adds one optional sentence.

Theme
Density
Text
Status
tooltip-a-supplementary-hint · base base · style base-lyraOpen under the product theme

Content guidelines

Keep a tooltip to a phrase in sentence case, with no terminal punctuation unless it is a full sentence. Write what the control does or means, never an instruction that only appears on hover.

Do

"Reminders wait until the morning during the hours you set here" beside a labelled setting.

Don’t

The only label on an icon-only control, shown on hover alone. A touch reader sees a bare glyph.

Accessibility

Audited against WCAG 2.2 AA: a source pass over the whole rubric, and a rendered pass over the measured findings. Author-run, not independent; clinical review is still pending. The rendered pass found no axe violation, no target-size breach, no focus-visible gap and no clipping at 320px. The source pass found nothing to change in tooltip.tsx.

What holds:

  • Base UI wires the popup through aria-describedby, so the label is a description, never a replacement for the control's own name.
  • Focus opens the tooltip, Escape dismisses it without moving focus, and the label follows focus off the trigger.
  • No status colour, no colour literal, no ramp step: footnote step, no px size, neither colour axis.

Open:

  • A tooltip is unreachable on a hover-less touch device, so content stays supplementary.
  • The popup's non-text contrast (SC 1.4.11) is the shared border-border role token, reasoned rather than measured.
  • The popup staying hoverable across the gap (SC 1.4.13) is a Base UI 1.7.0 default, not a manual trace.
KeyActionNotes
TabMoves focus onto the trigger and shows the tooltipNo open delay on focus.
Shift+TabMoves focus off the trigger and hides the tooltipThe label follows focus.
EscapeDismisses the tooltipFocus stays on the trigger and reopens it on the next pointer rest.
Enter, SpaceActivates the underlying controlThe tooltip binds neither key.
PairThemeAPCA LcWCAG 2.2Floor
body text on the pagelight101.617.18:1Pass
body text on the pagedark-100.518.00:1Pass
body text on a cardlight104.717.96:1Pass
body text on a carddark-99.616.32:1Pass
body text on the muted groundlight98.116.32:1Pass
body text on the muted grounddark-97.513.77:1Pass
secondary text on the pagelight83.97.42:1Pass
secondary text on the pagedark-80.713.44:1Pass
secondary text on a cardlight87.07.76:1Pass
secondary text on a carddark-79.812.19:1Pass
secondary text on the muted groundlight80.47.05:1Pass
secondary text on the muted grounddark-77.810.28:1Pass
a hairline boundary on the pagelight19.31.41:1Below floor
a hairline boundary on the pagedark-8.21.90:1Below floor
a hairline boundary on a cardlight22.41.47:1Below floor
a hairline boundary on a carddark-7.31.72:1Below floor
a hairline boundary on the muted groundlight15.81.34:1Below floor
a hairline boundary on the muted grounddark0.01.45:1Below floor
a placeholder boundary on the pagelight69.84.52:1Pass
a placeholder boundary on the pagedark-51.07.62:1Pass
a placeholder boundary on a cardlight72.84.72:1Pass
a placeholder boundary on a carddark-50.16.91:1Pass
a placeholder boundary on the muted groundlight66.24.29:1Pass
a placeholder boundary on the muted grounddark-48.15.83:1Pass
a placeholder fill on the pagelight8.31.18:1Below floor
a placeholder fill on the pagedark-8.21.90:1Below floor
a placeholder fill on a cardlight11.41.23:1Below floor
a placeholder fill on a carddark-7.31.72:1Below floor
a placeholder fill on the muted groundlight0.01.12:1Below floor
a placeholder fill on the muted grounddark0.01.45:1Below floor
a placeholder boundary on its own filllight58.93.84:1Pass
a placeholder boundary on its own filldark-41.54.01:1Below floor
the card hairline on the pagelight69.84.52:1Pass
the card hairline on the pagedark-28.74.19:1Below floor
the card hairline on a cardlight72.84.72:1Pass
the card hairline on a carddark-27.93.80:1Below floor
body text on the canvas materiallight104.717.96:1Pass
body text on the canvas materialdark-100.518.00:1Pass
body text on the inset materiallight98.116.32:1Pass
body text on the inset materialdark-100.117.18:1Pass
body text on the card materiallight104.717.96:1Pass
body text on the card materialdark-99.616.32:1Pass
body text on the raised materiallight104.717.96:1Pass
body text on the raised materialdark-97.513.77:1Pass
body text on the sheet material over the darkest backdroplight99.916.76:1Pass
body text on the sheet material over the darkest backdropdark-99.716.47:1Pass
body text on the sheet material over the lightest backdroplight104.717.96:1Pass
body text on the sheet material over the lightest backdropdark-80.86.07:1Pass
body text on the overlay material over the darkest backdroplight85.913.48:1Pass
body text on the overlay material over the darkest backdropdark-99.916.77:1Pass
body text on the overlay material over the lightest backdroplight104.717.96:1Pass
body text on the overlay material over the lightest backdropdark-54.22.69:1Below floor

These are the measured token pairs this component draws colour from, not a measurement of the component itself.

API reference

Prop

Type

Generated from TooltipProps in registry/bases/base/tooltip.tsx.

content is required; an empty one raises a development warning. children must be focusable. side defaults to top and Base UI flips it at the screen edge. delay defaults to 600 and governs the pointer only; focus opens the tooltip at once.

  • Term attaches a plain-English meaning to a clinical word inline.
  • Field carries a hint that is part of a form control and always visible.
  • Callout sets essential information apart on the surface.

On this page