Tooltip
An accessible hover and focus description for an element you already have, such as an icon button, a link, or an abbreviation. It holds non-interactive text only; for a click-triggered panel with rich content, use Popover.
Import
import { tooltip } from "@hyzer-labs/ui"Demo
An attachment, not a component
tooltip attaches to an element you already have, with {@attach tooltip('…')}, rather than wrapping it. Tooltips are text only. For a
click-triggered panel with interactive content, use Popover.Hover or focus the icon button. ariaLabel gives the button its own
accessible name. The tooltip's aria-describedby is separate: it adds context
rather than replacing that name.
<Button ariaLabel="Add to bag" {@attach tooltip('Your bag holds 20 discs')}>
{#snippet iconStart()}<IconPlus />{/snippet}
</Button>placement prefers a side and alignment. It flips automatically if the preferred
side would overflow the viewport.
<Button {@attach tooltip({ text: 'Placement: top-start', placement: 'top-start' })}>
top-start
</Button>
<!-- …one per placement: 'top' | 'top-end' | 'left' | 'right' | 'bottom-start' | 'bottom' | 'bottom-end' -->Tooltip text wraps at a fixed max-width instead of stretching edge to edge or
overflowing the viewport.
<Button ariaLabel="Delete round" intent="danger" {@attach tooltip({
text: 'This permanently deletes the round and every scorecard linked to it. This cannot be undone.',
placement: 'top'
})}>
{#snippet iconStart()}<IconTrash />{/snippet}
</Button>Dismissible: press Escape while a tooltip is showing. It hides without moving focus, and will not reopen until you leave and come back. Hoverable: move the pointer from the trigger onto the tooltip itself and it stays open. Persistent: it never disappears on its own while you hover or keep focus on it.
Props
| Name | Type | Default | Note |
|---|---|---|---|
text | string | — | Required. The tooltip text. `tooltip('Save changes')` is shorthand for `tooltip({ text: 'Save changes' })`. |
placement | 'top' | 'bottom' | 'left' | 'right' | '<side>-start' | '<side>-end' | 'top' | 'top' == 'top-center'; -start/-end add alignment. left/right follow the trigger's writing direction, so RTL flips the physical side. |
offset | number | 8 | Gap from the trigger, in px. |
openDelay | number | 400 | A hover-intent filter, in ms. A pointer that only passes over the trigger never opens it. Keyboard focus shows the tooltip right away, with no delay. |
closeDelay | number | 150 | The hoverable bridge, in ms. It gives the pointer time to travel from the trigger onto the tooltip itself without the tooltip disappearing. |
class | string | — | Merged after the hz-tooltip class. |
Theme hooks
What this component promises your CSS. The reference theme styles exactly these — from @layer hz-theme, so your unlayered rules win. See Styling Components for the how.
Root class: .hz-tooltip
Data attributes
| Hook | Values | Styles |
|---|---|---|
data-state | 'open' | 'closed' | Drives visibility (inline display, not the theme) — the hook to animate the entrance/exit against. |
data-placement | Placement | The requested placement string, e.g. "top" or "bottom-start" — mirrors the placement option verbatim. |
data-side | 'top' | 'bottom' | 'left' | 'right' | The resolved, physical side — after any overflow flip and RTL resolution. See Positioning for the caret recipe. |
data-align | 'start' | 'center' | 'end' | The resolved cross-axis alignment (RTL-aware for a top/bottom placement). |
Custom properties
| Hook | Values | Styles |
|---|---|---|
--hz-z-tooltip | <number> — default 150 | Matters mainly on the non-top-layer fallback path (older browsers) — a real top-layer tooltip stacks above everything regardless of z-index. |
Accessibility
The trigger gains aria-describedby pointing at the tooltip's role="tooltip" node. Any existing aria-describedby is kept, with the tooltip appended to it, and restored on teardown. The tooltip shows on hover, after openDelay filters out incidental passes, and on keyboard focus, immediately and with no delay. The APG Tooltip pattern requires both, not just one.
WCAG 2.2 SC 1.4.13 (Content on Hover or Focus) is satisfied. Dismissible: Escape hides the tooltip without moving focus or the pointer, and it will not re-open until you leave and re-enter or re-focus the trigger. Hoverable: the pointer can travel from the trigger onto the tooltip itself, across the offset gap, without it disappearing, because closeDelay bridges the gap. Persistent: it stays visible until you dismiss it, blur the trigger, or leave both the trigger and the tooltip. It never times out on its own.
tooltip never adds tabindex. It enhances a control that is already focusable. Attach it to a non-focusable element (a plain <span>, say) and it still shows on hover, but keyboard users cannot reach it. That case logs a warning in development.
There is no hover on touch. With a touch or coarse pointer, a tap activates the control underneath rather than revealing the tooltip, though aria-describedby still exposes the text to assistive tech. For content revealed by a tap, use Popover.
References: APG Tooltip pattern · MDN: Popover API · WCAG 2.2 SC 1.4.13: Content on Hover or Focus