Tooltip
A short label that appears on hover or focus. It is the component most likely to be handed information it must not hold, because it is the easiest place to put something when a layout is full.
Overview
This system bans hover as a sole carrier of information — see Interaction states — which puts the tooltip in an awkward position. The resolution is not to forbid it, but to be exact about what it is for: a tooltip supplements something already visible. It never carries anything a clinician needs in order to act.
Roughly half the surfaces this system targets are touch-only, where hover does not exist at all. A tooltip on a cart display is not a degraded experience — it is nothing. Anything that would be lost there was never safe to put in a tooltip.
Supplementary
Removing every tooltip in the product must change nothing about what a clinician can do or decide. If it does, that content belongs on the page.
Reachable
Hover, keyboard focus and long-press all open it. A tooltip only a mouse can reach is unavailable to most of the people using it.
Brief
A few words naming a thing. A tooltip containing a sentence is a popover; one containing a decision is a defect.
Anatomy
Both bubbles are shown open so they can be read here. In use they appear on hover, focus or long-press, one at a time.
| Element | Rule |
|---|---|
| Trigger | An interactive control, or an <abbr>. Never a bare span — a
tooltip on something unfocusable is unreachable by keyboard. |
| Bubble | Inverse surface, one line where possible, 200 px maximum when wrapping. |
| Relationship | aria-describedby. The control still carries its own accessible name via
aria-label — the tooltip does not supply it. |
An icon-only button needs both an aria-label (so assistive
technology and voice control can name it) and a tooltip (so a sighted user can
discover what the glyph means). Using the tooltip as the label leaves the control unnamed for
anyone not hovering it; using the label alone leaves a sighted clinician guessing at an icon
under time pressure.
What must never go in a tooltip
| Never | Because | Instead |
|---|---|---|
| Units of a measurement | The value becomes unitless on touch and in print | Permanent suffix or label — Text field |
| A confidence score | Reliance information must be visible at the moment of the decision | Confidence disclosure |
| Patient identifiers | Identity is never conditional on a pointer | Patient header |
| An error or validation message | Errors must persist and be announced | Inline error text with aria-describedby |
| The full text of a truncated clinical value | Truncation of clinical content is itself prohibited | Wrap, reflow, or shorten the label |
| Anything needed to choose between two actions | Decisions are not made by hovering | Visible helper text, or a Dialog |
What a tooltip is legitimately for
- Naming an icon-only control, alongside its
aria-label. - Expanding an abbreviation on first use in an interface — ACS, LBBB, GSPR.
- Restating a non-clinical label that has been shortened for a dense column header, where the full text is available elsewhere.
- Naming a keyboard shortcut for an action that is already visible.
Behaviour
WCAG 2.2 SC 1.4.13 sets three requirements for content shown on hover or focus, and all three are met by the component rather than by each call site.
| Requirement | Implementation |
|---|---|
| Dismissible | Esc closes without moving the pointer or focus. |
| Hoverable | The pointer can travel into the bubble without it closing — so a magnified user can reach content that overlaps their cursor. |
| Persistent | Stays until dismissed, focus moves, or the pointer leaves. Never auto-hides on a timer. |
- Open delay 400 ms, close delay 0. Long enough that a pointer crossing the screen does not trail bubbles; instant on exit so nothing is left obscuring content.
- No delay when the trigger receives keyboard focus. A keyboard user asked for it explicitly.
- Long-press opens on touch, and dismisses on the next tap anywhere.
- Never covers its own trigger, and never covers an active alarm — a tooltip yields position rather than overlapping the alert region.
- One at a time. Tooltips never stack or chain.
Writing it
- One to four words for a control name; one short clause for an abbreviation.
- Name the action, not the icon. "Compare with prior ECG", never "Arrows".
- No full stop, sentence case, no "Click to…".
- Never duplicate visible text. A tooltip on a labelled button is noise that obscures the row beneath it.
Names the action. The button is independently labelled, so nothing is lost on a touch display.
Unit, confidence and model version behind a hover. On a cart display this is the number 47, unqualified, in a clinical record.
Do's and don'ts
Tooltip and aria-label both present. Nothing is lost on a touch display.
The tooltip used as the accessible name. Voice control and screen readers have nothing to call this button.
Accessibility
- Trigger must be focusable. Buttons, links and
<abbr>withtabindexwhere it carries a tooltip. aria-describedby, notaria-labelledby. The tooltip describes; the control's own label names.- Never use the
titleattribute. It is unreachable by keyboard, inconsistently announced, cannot be styled, and cannot meet SC 1.4.13. - Contrast. The inverse surface meets AA in both themes — the same computed pairs as Colour.
- Reduced motion removes the fade; it never removes the tooltip.
- Zoom. At 200 % the bubble reflows and repositions inside the viewport rather than clipping.
Outcomes of use
What this contributes to, in the terms of Usability & context of use. These are attributes believed to contribute to an outcome; the outcome itself is settled by observing real use in a specified context, not by this page.
- Effectiveness — icon meaning learned without guessing. Since nothing decision-relevant lives here, a tooltip that never appears cannot cause an error.
- Efficiency — removes the hesitation an unlabelled glyph produces, without spending the space a permanent label would.
- Satisfaction — reduces the low-grade uncertainty of an unfamiliar toolbar, which is most of what makes a first shift with a new tool unpleasant.
Clinical safety notes
Trace these in your risk file (ISO 14971) and usability engineering file (IEC 62366-1).
- Never the sole carrier of anything. Mitigates: information silently absent on touch surfaces, in print, and for keyboard users who never trigger it.
- Never holds units, identifiers, confidence or errors. Mitigates: a clinical value read without the qualifier that makes it meaningful.
- Not the accessible name. Mitigates: icon-only controls being unnamed for assistive technology and voice control.
- Dismissible, hoverable, persistent. Mitigates: a bubble obscuring content a magnified user cannot get out from under.
- Never overlaps the alert region. Mitigates: a hover artefact hiding an active alarm.
- No
titleattribute anywhere. Mitigates: content that appears to be provided and is not reachable.
Implementation
$ npx shadcn@latest add https://md.notjustany.tech/r/tooltip.json
// Radix Tooltip gives dismissible / hoverable / persistent for free.
<Tooltip content="Compare with prior ECG">
<Button size="icon" variant="outline" aria-label="Compare with prior ECG">
<CompareIcon aria-hidden />
</Button>
</Tooltip>
// Development guard: the tooltip must not be doing the label's job.
if (content === trigger.props["aria-label"]) {
console.warn("[Tooltip] duplicates the accessible name — remove one.")
}
| Prop | Type | Default | Notes |
|---|---|---|---|
content | string | — | Plain text only. Rich content means you wanted a Dialog or popover. |
openDelay | number | 400 |
Ignored for keyboard focus, which opens immediately. |
side | top | right | bottom | left | top |
Auto-flips to stay in the viewport and clear of the alert region. |
Related
- Interaction states — why hover carries nothing.
- Button — icon-only controls and their labels.
- Dialog — when content is a decision, not a label.
- Accessibility — SC 1.4.13 in context.