Components / overlays

Tooltip

Escape does not stopPropagation, so a dialog above still sees it. APG requires Escape to dismiss a tooltip even while the trigger keeps focus — a tooltip can cover the very content the reader is trying to see, and without Escape a keyboard user has no way to get rid of it short of leaving the control.

Rendered from the package · open on its own

Summary

One sentence of explanation attached to a control.

APG patternTooltip
Built onnative elements
Statusstable
Since0.1.0
<Tooltip id="tip-cname" text="A DNS record that points your domain at Studio Edit.">
  <Button intent="neutral" emphasis="transparent" iconOnly={<Icon path={ICONS.info} />} label="What is a CNAME?" />
</Tooltip>

Anatomy

A wrapper carrying data-se="tooltip" around the trigger, with the bubble as floater.

PartElementRequired
floater<span>yes

Properties

PropertyValuesDefaultWhat it does
idstringrequiredId for the bubble; the trigger's aria-describedby points at it.
textstringrequiredOne sentence. If it needs more, it is not a tooltip.
childrenReact.ReactElementrequiredThe trigger — cloned to receive aria-describedby.

States

StateExpressed as
Hover:hover
Open or closeddata-state

Behavior

It appears on hover and on focus, and goes on blur or Escape. It never takes focus itself.

Accessibility

The text is announced via aria-describedby, so an icon-only trigger still needs its own label: the tooltip describes, it does not name.

Never put a link or button inside one. It cannot be reached by keyboard or by touch — the tooltip is gone before the pointer arrives.

It has no upstream test suite. Tooltip is one of four patterns the W3C ships no regression tests for, so nothing external will catch a mistake here.

Keyboard

KeyResult
EscapeDismisses it, while the trigger keeps focus

Roles, states and properties

AttributeSet by
rolealways tooltip

Focus

It never takes focus. It appears when the trigger is focused and goes when the trigger is not. Escape does not stopPropagation, so a dialog above still sees it — APG requires the dismissal because a tooltip can cover the very content the reader is trying to read.

Screen reader

Not recorded as verified. Stage 4 of the readiness ladder — driving each component with VoiceOver and NVDA — is unstarted across the package, and no component should claim otherwise until docs/journal/ records the session that did it.

Contrast and target size

npm run check:contrast audits every color pair this component uses across all four skins, light and dark, and fails the build on a shortfall. Two waivers are on the record in docs/audit/DEFERRED.md.

It sets no target size of its own; anything interactive inside it brings its own.

Tested

  • test/apg/batch-a.test.jsx

Known gaps

_No upstream requirement is recorded as not applicable._

Appearance

TokenUsed for
--se-borderborder
--se-border-widthborder
--se-duration-fastanimation
--se-ease-decelerateanimation
--se-layer-floatingz-index
--se-leading-bodyline-height
--se-radius-lgborder-radius
--se-radius-mdborder-radius
--se-shadow-floatingbox-shadow
--se-space-2inset-block-start, padding
--se-space-3padding
--se-surfacebackground
--se-textbackground, border-color
--se-text-inversecolor
--se-text-xsfont-size

Motion

It rises over --se-duration-fast. Leaving is not animated: a tooltip fading out follows the pointer to whatever it moved to.

Content

One sentence. If it needs two, the interface has not explained itself and the fix is a description in the page, not a longer bubble.

Never repeat the label. A tooltip on a button reading Delete that says "Delete" has cost a reader a hover for nothing.

Usage

Hover and focus only, and nothing interactive inside. That is the whole rule, and it is what separates a tooltip from a Popover — a panel that needs a click is a popover.

Not for anything the reader must have. A tooltip is invisible on touch until the reader guesses to long-press. If the information is required to use the control, put it in the page.

Notes

Hand-written, not Radix. The whole component is a wrapper, a bubble and one key handler.

Escape does not stopPropagation, so a dialog above still sees it. APG requires Escape to dismiss a tooltip even while the trigger keeps focus — a tooltip can cover the very content the reader is trying to see, and without Escape a keyboard user has no way to get rid of it short of leaving the control.