Skip to content

HyTooltip

Usage examples on this page are written for Lit / plain HTML (<hy-tooltip>). The same component ships as HyTooltip in @whitespaceux/harmony-react (native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.

A lightweight tooltip component that displays contextual information when hovering or focusing on an element.

Trigger Patterns

Slotted trigger (preferred for client-side apps): The trigger element is passed via the trigger slot, ensuring ARIA relationships work correctly across shadow DOM boundaries.

for attribute (fallback for SSR frameworks): For SSR frameworks like Next.js, Nuxt, or SvelteKit, use the for attribute to reference a trigger element by ID. This avoids hydration timing issues with the slotchange event.

Key Features

  • Automatic positioning with Floating UI
  • Configurable show/hide delays
  • Hover, focus, or combined triggers
  • Dismissible, hoverable, and persistent
  • Full keyboard support (Escape to dismiss)
  • SSR-compatible via for attribute fallback

Accessibility

  • Automatic aria-describedby on the trigger element
  • role="tooltip" for screen readers
  • Focus stays on trigger (tooltips never steal focus)

Examples

Basic Usage (Slotted Trigger)

html
<hy-tooltip>
  <button slot="trigger">Save</button>
  Save your changes
</hy-tooltip>

SSR Framework Usage (for attribute)

html
<button id="save-btn">Save</button> <hy-tooltip for="save-btn">Save your changes</hy-tooltip>

With Placement

html
<hy-tooltip placement="bottom">
  <button slot="trigger">?</button>
  Click for help
</hy-tooltip>

Hover Only

html
<hy-tooltip trigger="hover">
  <span slot="trigger" tabindex="0">API</span>
  Application Programming Interface
</hy-tooltip>

Custom Delay

html
<hy-tooltip delay="500">
  <button slot="trigger">Delete</button>
  This cannot be undone
</hy-tooltip>

Note: show / hide are dispatched with bubbles: false. A tooltip's visibility is a purely local concern (no parent-coordination protocol like menubar↔menu-item), and a bubbling composed hide would collide with an ancestor overlay's same-named handler — e.g. a tooltip inside an hy-dialog would trip the dialog's onhide and close the modal. Listen directly on the <hy-tooltip> element (onshow / onhide) — target-phase delivery is unaffected. Matches the React mirror, whose onShow / onHide are callbacks


## API

### Properties

| Property | Attribute | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `placement` | `placement` | `'top' \| 'top-start' \| 'top-end' \| 'bottom' \| 'bottom-start' \| 'bottom-end' \| 'left' \| 'left-start' \| 'left-end' \| 'right' \| 'right-start' \| 'right-end'` | `'top'` | Where to place the tooltip relative to the trigger. Uses Floating UI with automatic fallback when there isn't enough space. |
| `distance` | `distance` | `number` | `6` | Distance between the tooltip and its trigger element in pixels. |
| `trigger` | `trigger` | `'hover' \| 'focus' \| 'hover focus'` | `'hover focus'` | How the tooltip is triggered. - 'hover': Show on mouseenter, hide on mouseleave - 'focus': Show on focus, hide on blur - 'hover focus': Show on either, requires both to be removed to hide |
| `delay` | `delay` | `number` | `200` | Delay before showing the tooltip in milliseconds. |
| `hideDelay` | `hide-delay` | `number` | `100` | Delay before hiding the tooltip in milliseconds. |
| `arrow` | `arrow` | `boolean` | `true` | Whether to show the arrow indicator. |
| `open` | `open` | `boolean` | `false` | Controls the open state of the tooltip. |
| `disabled` | `disabled` | `boolean` | `false` | Disables the tooltip entirely. When disabled, the tooltip will not show on any trigger. |
| `for` | `for` | `string \| undefined` | — | ID of the trigger element. Use this as a fallback when the slotted trigger pattern isn't available (e.g., in SSR frameworks where slot forwarding may not work). |

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `show` | — | Fired after the tooltip opens (does NOT bubble). Detail: `{ source: 'hover' \| 'focus' \| 'programmatic' }` |
| `hide` | — | Fired after the tooltip closes (does NOT bubble). Detail: `{ source: 'hover' \| 'focus' \| 'escape-key' \| 'programmatic' }` |

### Slots

| Slot | Description |
| --- | --- |
| `trigger` | The element that triggers the tooltip (required) |
| `default` | The tooltip content |

### CSS Parts

| Part | Description |
| --- | --- |
| `body` | The tooltip content wrapper |
| `arrow` | The arrow indicator element |

### CSS Custom Properties

| Property | Description |
| --- | --- |
| `--hy-tooltip-foreground` | Tooltip text color (inverted: uses surface-base) **Default:** `var(--hy-background-surface-base)` |
| `--hy-tooltip-background` | Tooltip background color (inverted: uses surface-inverse) **Default:** `var(--hy-background-surface-inverse)` |
| `--hy-tooltip-padding-block` | Block padding (default: var(--hy-padding-overlay-floating-sm-block)) |
| `--hy-tooltip-padding-inline` | Inline padding (default: var(--hy-padding-overlay-floating-sm-inline)) |
| `--hy-tooltip-radius` | Border radius (default: var(--hy-radius-overlay-floating-sm)) |
| `--hy-tooltip-max-width` | Maximum width (default: surface-width-sm) |
| `--hy-tooltip-ornament` | Arrow indicator size (default: var(--hy-ornament-bullet)) |
| `--hy-tooltip-safe-zone` | Safe hover zone around tooltip, set dynamically via JS (default: 12px) |
| `--hy-tooltip-shadow` | Box shadow for the tooltip |
| `--hy-tooltip-font-family` | Font family override for tooltip text |
| `--hy-tooltip-enter-duration` | Transition duration for the show and hide animation |
| `--hy-tooltip-enter-easing` | Transition easing for the show and hide animation |

## Methods

### show()

Shows the tooltip programmatically.

### hide()

Hides the tooltip programmatically.

Built with Lit. Documented with VitePress.