Skip to content

Tooltip

hc-tooltip styles a popover element as a small label that describes its trigger. The trigger references the tooltip via aria-describedby so screen readers announce the description when the trigger receives focus; installTooltip() then handles the visual show / hide on hover, focus, and Escape.

PrimitiveRequired version
HTML popoverChrome 114, Edge 114, Firefox 125, Safari 17
CSS Anchor PositioningChrome 125, Edge 125, Firefox 147, Safari 26

Browsers without Anchor Positioning fall back to a getBoundingClientRect-based positioning hook so the tooltip still appears above the trigger. Tooltips remain functional everywhere popover is supported.

We use popover="manual" rather than the newer popover="hint" because Safari had no hint support as of 2026-05. manual + JS toggling matches the hint semantics — separate tooltips coexist without dismissing each other — in every browser that supports popover at all.

Save document (Ctrl+S)
Move to trash
import { installTooltip } from '@hypermedia-components/core';
installTooltip();
  • Auto-attributes the tooltip element if the author has not: popover="manual" (full JS control of show / hide) and role="tooltip" (screen-reader semantics).
  • Wires every trigger that references the tooltip via aria-describedby — one tooltip can serve multiple triggers.
  • Injects matching anchor-name (on the active trigger) and position-anchor (on the tooltip) so the tooltip lands above the trigger via CSS Anchor Positioning. Re-binds the anchor when a different shared trigger is hovered / focused.
  • Show / hide timing:
    • mouseenter → show after a 300 ms delay (industry-standard “intent to hover” threshold).
    • mouseleave → hide after a 100 ms grace period (cancels a pending show if it fires during the delay).
    • focus → show immediately (no delay for keyboard users — APG guidance).
    • blur → hide immediately.
    • Escape while focused → hide without moving focus.

The tooltip sits above the trigger by default. Override it per instance with data-side (top / right / bottom / left) and optional data-align (start / center / end, default center). Add data-arrow for a small pointer.

<button aria-describedby="hint">Help</button>
<div class="hc-tooltip" id="hint" data-side="right" data-arrow>
Appears to the right
</div>

Placement uses CSS Anchor Positioning (position-area) where supported and the same JS fallback as the default placement otherwise — data-side / data-align drive both paths identically. The shared mechanics — both paths, the arrow, and the --hc-anchored-* offset / arrow knobs — are documented in Fundamentals → Anchored positioning; the mechanism reached Baseline in 2026 with a JS fallback for older engines.

Use a real description, not the title attribute

Section titled “Use a real description, not the title attribute”

The browser’s native tooltip rendered from the title attribute cannot be styled, behaves inconsistently across platforms, and does not appear on touch devices. Prefer the hc-tooltip + aria-describedby pattern. If you must keep a title, the behavior won’t conflict — but the browser will render its own tooltip on top of yours, so remove it.

  • Always reference the tooltip from its trigger via aria-describedby="<tooltip-id>". Screen readers will announce the tooltip text when the trigger gains focus, regardless of whether the visual tooltip is currently rendered.
  • Tooltips are for short text labels only. If you need interactive content (buttons, form fields), use Popover or Dialog instead. The tooltip surface is pointer-events: none so it cannot intercept clicks.
  • The tooltip must remain reachable while the trigger keeps focus — do not hide it on a timer. The behavior does not auto-dismiss while focus is held; only Escape, blur, or mouseleave close it.
  • Touch devices have no hover event. The behavior does not poly-fill long-press → show; design with the assumption that the trigger’s accessible name carries the essential meaning, and the tooltip augments it.

Component tokens (in component.tokens.json):

Token pathPurpose
tooltip.bg / fgSurface and text color. Defaults to dark on light per the convention.
tooltip.radiusCorner radius.
tooltip.padding-x / padding-yInner padding.
tooltip.font-sizeText size.
tooltip.max-widthCap before wrapping.
tooltip.offsetDistance between the trigger and the tooltip.
Show the generated CSS variables
--hc-tooltip-bg | -fg | -radius
--hc-tooltip-padding-x | -padding-y | -font-size
--hc-tooltip-max-width | -offset
  • Popover — bare popover surface for richer transient content.
  • Menu — action menu sharing the popover + anchor positioning architecture.
  • Dialog — modal alternative.