Skip to content

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>

Props

NameTypeDefaultNote
textstringRequired. 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.
offsetnumber8Gap from the trigger, in px.
openDelaynumber400A 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.
closeDelaynumber150The hoverable bridge, in ms. It gives the pointer time to travel from the trigger onto the tooltip itself without the tooltip disappearing.
classstringMerged 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

HookValuesStyles
data-state'open' | 'closed'Drives visibility (inline display, not the theme) — the hook to animate the entrance/exit against.
data-placementPlacementThe 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

HookValuesStyles
--hz-z-tooltip<number> — default 150Matters 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