Accessible, unstyled Tooltip compound primitive.
|
|
| Package |
@vizejs/ui/tooltip |
| Maturity |
stable |
| Own the source |
vize lib pull tooltip |
| Requires |
context, controllable-state, dismissable-layer, hover, id, portal, positioner, presence |
| Aliases |
hover tip, description bubble, help text, floating hint |
| Covers |
WAI-ARIA tooltip pattern, Radix Tooltip, Reka UI Tooltip |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { Tooltip, TooltipRoot, TooltipContent, TooltipTrigger } from "@vizejs/ui/tooltip";
Or copy the source into your project with vize lib pull tooltip (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components
Copy the example
<!-- Tooltip that describes an icon-only trigger on hover and focus. -->
<script setup lang="ts">
import { TooltipContent, TooltipRoot, TooltipTrigger } from "@vizejs/ui/tooltip";
</script>
<template>
<TooltipRoot :delay-duration="300">
<TooltipTrigger aria-label="Copy link">
<span aria-hidden="true">🔗</span>
</TooltipTrigger>
<TooltipContent placement="top" :offset="6">Copy a shareable link to this page</TooltipContent>
</TooltipRoot>
</template>
API
Source: src/families/overlays/tooltip/tooltip-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned Tooltip base id. null and undefined select a deterministic fallback. |
open |
boolean |
undefined |
Controlled open state. undefined selects uncontrolled behavior. |
defaultOpen |
boolean |
false |
Initial open state for uncontrolled use. |
disabled |
boolean |
false |
Disable trigger-driven opening and request closure when already open. |
delayDuration |
number |
700 |
Milliseconds before hover or focus opens the tooltip. |
skipDelayDuration |
number |
300 |
Milliseconds after closing where a new hover or focus opens without delay. |
Events
| Event |
Payload |
Description |
update:open |
[value: boolean] |
Fired when the Tooltip requests a controlled open value. |
open-change |
[value: boolean, previous: boolean, nativeEvent: Event | null] |
Fired after any distinct open-state request. |
Slots
| Slot |
Slot props |
Description |
default |
TooltipSlotState |
Compound Tooltip children. Receives the current open and disabled state. |
Exposed
| Member |
Type |
Description |
cancelOpen |
— |
|
close |
— |
|
contentId |
— |
|
delayDuration |
— |
|
disabled |
— |
|
id |
— |
|
open |
— |
|
openTooltip |
— |
|
scheduleOpen |
— |
|
setOpen |
— |
|
skipDelayDuration |
— |
|
state |
— |
|
triggerId |
— |
|
Source: src/families/overlays/tooltip/tooltip-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned Tooltip base id. null and undefined select a deterministic fallback. |
open |
boolean |
undefined |
Controlled open state. undefined selects uncontrolled behavior. |
defaultOpen |
boolean |
false |
Initial open state for uncontrolled use. |
disabled |
boolean |
false |
Disable trigger-driven opening and request closure when already open. |
delayDuration |
number |
700 |
Milliseconds before hover or focus opens the tooltip. |
skipDelayDuration |
number |
300 |
Milliseconds after closing where a new hover or focus opens without delay. |
Events
| Event |
Payload |
Description |
update:open |
[value: boolean] |
Fired when the Tooltip requests a controlled open value. |
open-change |
[value: boolean, previous: boolean, nativeEvent: Event | null] |
Fired after any distinct open-state request. |
Slots
| Slot |
Slot props |
Description |
default |
TooltipSlotState |
Compound Tooltip children. Receives the current open and disabled state. |
Exposed
| Member |
Type |
Description |
open |
boolean |
Whether the tooltip content is currently visible. |
disabled |
boolean |
Whether trigger-driven opening is disabled by the root. |
state |
TooltipState |
Stable state token for styling and tests. |
id |
string |
Root-owned base id for the compound tooltip family. |
triggerId |
string |
Id wired to the native trigger button. |
contentId |
string |
Id wired from TooltipTrigger to TooltipContent. |
delayDuration |
number |
Delay before hover or focus opens the tooltip. |
skipDelayDuration |
number |
Window after closing where hover or focus opens without delay. |
setOpen |
(value: boolean, event?: Event | null) => boolean |
Request a specific open value and report whether it differs. |
openTooltip |
(event?: Event | null) => boolean |
Request the open state immediately. |
close |
(event?: Event | null) => boolean |
Request the closed state and clear a pending delayed open. |
scheduleOpen |
(event?: Event | null) => boolean |
Request opening after the configured delay. |
cancelOpen |
() => boolean |
Clear a pending delayed open. |
TooltipContent
Source: src/families/overlays/tooltip/tooltip-content.vue
Props
| Prop |
Type |
Default |
Description |
forceMount |
boolean |
false |
Keep the content mounted while the tooltip is closed. |
to |
string | HTMLElement |
"body" |
CSS selector or element the tooltip layer is moved into. |
portalDisabled |
boolean |
false |
Render in place instead of teleporting. |
defer |
boolean |
true |
Keep content in place until the target exists, avoiding SSR mismatch. |
placement |
Placement |
"top" |
Preferred placement before collision handling. |
strategy |
PositionerStrategy |
"fixed" |
CSS positioning mode published on the floating host. |
offset |
number |
6 |
Gap on the main axis between trigger and content. |
collisionPadding |
number |
4 |
Viewport padding the floating element should not cross. |
flip |
boolean |
true |
Flip to the opposite side when the preferred side overflows more. |
shift |
boolean |
true |
Shift the floating box back into the viewport after flip. |
hide |
boolean |
true |
Hide when the trigger no longer intersects the viewport. |
updateOnScroll |
boolean |
true |
Recalculate while ancestors scroll. |
updateOnResize |
boolean |
true |
Recalculate when the document or visual viewport resizes. |
viewport |
Rect |
undefined |
Viewport used for flip, shift, and hide. |
closeOnEscape |
boolean |
true |
Let Escape request dismissal while the tooltip is open. |
ariaLabel |
string |
undefined |
Accessible name when visible text is not enough. |
Events
| Event |
Payload |
Description |
escape-key-down |
[event: DismissableLayerEscapeKeyDownEvent] |
Fired before Escape requests dismissal. |
dismiss |
[event: DismissableLayerDismissEvent] |
Fired after an unprevented dismissal request. |
Slots
| Slot |
Slot props |
Description |
default |
TooltipContentSlotState |
Tooltip content. Receives open state and resolved placement. |
Exposed
| Member |
Type |
Description |
open |
boolean |
Whether the tooltip content is currently visible. |
disabled |
boolean |
Whether trigger-driven opening is disabled by the root. |
state |
TooltipState |
Stable state token for styling and tests. |
element |
HTMLDivElement | null |
Rendered tooltip content element. |
Source: src/families/overlays/tooltip/tooltip-trigger.vue
Props
| Prop |
Type |
Default |
Description |
type |
"button" | "reset" | "submit" |
"button" |
Native button submission behavior. |
disabled |
boolean |
false |
Remove the trigger from activation and sequential keyboard focus. |
ariaLabel |
string |
undefined |
Accessible name when no visible label supplies one. |
Slots
| Slot |
Slot props |
Description |
default |
TooltipSlotState |
Trigger contents. Receives the current Tooltip state and trigger availability. |
Exposed
| Member |
Type |
Description |
element |
HTMLButtonElement | null |
Rendered native button. |
focus |
(options?: FocusOptions) => void |
Move focus to the trigger. |
Behavior
@vizejs/ui/component-tooltip.css is an optional visual stylesheet. The
component entry stays headless. Its enter motion starts only after Positioner
has measured the floating host; reduced-motion and forced-colors policies stop
the animation. Closed content follows the component's native presence policy.
| Case |
Given |
Action |
Expected |
Evidence |
| T1 |
tooltip-root.vue owns a closed tip |
trigger receives mouse or pen hover |
content opens after delayDuration, trigger receives aria-describedby, content has role="tooltip" |
tooltip.test.ts |
| T2 |
tooltip-root.vue recently closed |
trigger is hovered or focused within skipDelayDuration |
content opens without waiting for the full delay |
tooltip.test.ts |
| T3 |
tooltip-trigger.vue receives focus |
delay completes |
content opens through the same state contract as hover |
tooltip.test.ts |
| T4 |
tooltip-trigger.vue is open |
trigger blurs, pointer leaves, pointer presses, or Escape is pressed |
pending open is canceled and content closes |
tooltip.test.ts |
| T5 |
root or trigger is disabled |
trigger is hovered, focused, or pressed |
no open request is emitted and disabled data hooks are present |
tooltip.test.ts |
| T6 |
open is controlled |
trigger requests open or close |
update:open and open-change fire without mutating visible state before parent acceptance |
tooltip.test.ts |
| T7 |
tooltip-content.vue is closed |
content remains force-mounted in the DOM |
visible tooltip semantics are hidden and aria-describedby is omitted |
tooltip.test.ts |
| T8 |
tooltip renders on the server |
repeated SSR requests and hydration run |
deterministic ids match and no document listeners appear in HTML |
tooltip-ssr.test.ts |