Tooltip
Dark mono tooltip on hover, positioned above the child. Pure CSS — shown on hover and keyboard focus, no JavaScript required.
Basic
role="tooltip" is the correct ARIA role for the bubble itself, so it
doubles as the styling hook — no class on the bubble. The wrapping <span>
has no attribute of its own, but it does not need one: :has() selects “a
span that contains a [role=tooltip] bubble” precisely, so the wrapper stays
bare too. Pair the trigger’s aria-describedby with the bubble’s id so the
tooltip is announced, not just shown.
Hover or tab to the button below.
Delete
Markup
<span>
<button class="icon-button" aria-label="Delete" aria-describedby="tooltip-basic">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13" /></svg>
</button>
<span role="tooltip" id="tooltip-basic">Delete</span>
</span>
On any element
A Tooltip wraps whatever it labels — most often an IconButton, since that is the control with no visible text of its own.
3d overdue
Due Tuesday, was Saturday
Markup
<span>
<span class="badge" data-tone="danger" tabindex="0" aria-describedby="tooltip-badge">3d overdue</span>
<span role="tooltip" id="tooltip-badge">Due Tuesday, was Saturday</span>
</span>
API
| Element / attribute | Values | Notes |
|---|---|---|
span | Required wrapper around the trigger and the bubble. Bare — matched structurally via :has(), no class. | |
[role="tooltip"] | string content | Required — the bubble. Give it an id. |
aria-describedby | the bubble's id | Put it on the trigger element, which needs a real accessible name of its own — a tooltip is not one. |