Skip to content

Tooltip

Wrap a trigger in .re-tooltip and add a .re-tooltip__bubble sibling with role="tooltip". Point the trigger’s aria-describedby at the bubble’s id so assistive technology announces the text — the reference resolves even while the bubble is visually hidden. The bubble appears on hover and on keyboard focus (:focus-within); no JavaScript involved. A small arrow points at the trigger.

The bubble stays hoverable and the gap to the trigger is bridged, so moving the pointer onto the tooltip doesn’t dismiss it — WCAG 1.4.13 Hoverable and Persistent. The CSS-only tooltip does not implement 1.4.13 Dismissable (there’s no Escape-to-hide without moving focus); add a small JS handler if you need that. On touch devices, where there is no hover, the tooltip shows while the trigger has focus — don’t put essential information only in a tooltip.

Set data-placement="bottom", "start", or "end" on the wrapper; the default is top. Start/end follow the writing direction.

Shown above Shown below Shown before Shown after
Live example
<div
  style="
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--re-space-4);
    padding: var(--re-space-12) var(--re-space-16);
  "
>
  <span class="re-tooltip">
    <button class="re-button" type="button" aria-describedby="tip-top">Top</button>
    <span class="re-tooltip__bubble" role="tooltip" id="tip-top">Shown above</span>
  </span>
  <span class="re-tooltip" data-placement="bottom">
    <button class="re-button" type="button" aria-describedby="tip-bottom">Bottom</button>
    <span class="re-tooltip__bubble" role="tooltip" id="tip-bottom">Shown below</span>
  </span>
  <span class="re-tooltip" data-placement="start">
    <button class="re-button" type="button" aria-describedby="tip-start">Start</button>
    <span class="re-tooltip__bubble" role="tooltip" id="tip-start">Shown before</span>
  </span>
  <span class="re-tooltip" data-placement="end">
    <button class="re-button" type="button" aria-describedby="tip-end">End</button>
    <span class="re-tooltip__bubble" role="tooltip" id="tip-end">Shown after</span>
  </span>
</div>

Add data-open to the wrapper to keep the bubble visible — useful for debugging, documentation, and visual tests.

Shown above Shown below Shown after
Live example
<div
  style="
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--re-space-6);
    padding: var(--re-space-12) var(--re-space-16);
  "
>
  <span class="re-tooltip" data-open>
    <button class="re-button" type="button" aria-describedby="tip-open-top">Top</button>
    <span class="re-tooltip__bubble" role="tooltip" id="tip-open-top">Shown above</span>
  </span>
  <span class="re-tooltip" data-placement="bottom" data-open>
    <button class="re-button" type="button" aria-describedby="tip-open-bottom">Bottom</button>
    <span class="re-tooltip__bubble" role="tooltip" id="tip-open-bottom">Shown below</span>
  </span>
  <span class="re-tooltip" data-placement="end" data-open>
    <button class="re-button" type="button" aria-describedby="tip-open-end">End</button>
    <span class="re-tooltip__bubble" role="tooltip" id="tip-open-end">Shown after</span>
  </span>
</div>
  • Keyboard — the tooltip has no keyboard model of its own; it’s CSS-only. Tab to the trigger (a native control such as a <button>) and the bubble appears via :focus-within; it also appears on :hover. There is no Escape-to-dismiss (see 1.4.13 caveat above) — add a JS handler if you need one.
  • Focus — only the trigger is focusable, so it keeps its own visible :focus-visible ring; the bubble is never a focus target. The bubble is hidden with visibility: hidden / opacity: 0 (not display: none) so its id stays referenceable while invisible.
  • Semantics — the bubble is a real element with role="tooltip", and the trigger’s aria-describedby points at the bubble’s id. Assistive technology announces the bubble text as the trigger’s description even while the bubble is visually hidden. Don’t put essential information only in a tooltip — on touch devices, where there is no hover, it shows only while the trigger has focus.
  • Notes — fade in/out is shortened to an instant transition under prefers-reduced-motion: reduce. The bubble derives its colors from the text/bg role tokens, inverting in dark mode and subtree themes without tooltip-specific tokens. See the accessibility guide for the broader WCAG 1.4.13 stance.