Skip to content
llms.txt
llms.txt

Tooltip

Open the live tooltip demo

Live demo

Tooltip

Source for this preview

TooltipExample.astro
---
import {TOOLTIP_ARROW} from '../../example-config';
---
<article id="tooltip-demo" class="demo-card tooltip-card">
<div class="card-top">
<span class="number">A</span><span class="chip">hover + focus</span>
</div>
<h3>Signal tooltip</h3>
<p>
One floating surface wires pointer intent, keyboard focus, dismissal, and
descriptive ARIA.
</p>
<div class="card-action">
<floating-root
id="tooltip-root"
placement="top"
>
<floating-reference>
<button class="ink-button">
Inspect signal <span aria-hidden="true">↗</span>
</button>
</floating-reference>
<template slot="content">
<div class="tooltip">
Positioned by <b>autoUpdate</b>
<floating-arrow
class="tooltip-arrow"
width={TOOLTIP_ARROW.width}
height={TOOLTIP_ARROW.height}
></floating-arrow>
</div>
</template>
</floating-root>
</div>
<code>hover() → focus() → dismiss()</code>
</article>
<script>
import {
FloatingRootElement,
arrow,
dismiss,
flip,
focus,
hover,
offset,
role,
safePolygon,
shift,
transformOrigin,
} from '@floating-ui-plus/web-components';
import {TOOLTIP_ARROW} from '../../example-config';
import {initializeExample} from './initialize-example';
initializeExample('tooltip', (scope) => {
const floating = FloatingRootElement.query(scope, '#tooltip-root');
floating.configure({
middleware: [
offset(TOOLTIP_ARROW.gap),
flip(),
shift({padding: 12}),
transformOrigin({padding: 8}),
],
plugins: [
hover({handleClose: safePolygon({buffer: 4})}),
focus(),
dismiss(),
role({role: 'tooltip'}),
],
});
floating.on('floatingmount', ({element}) => {
const arrowElement = element.querySelector<HTMLElement>('floating-arrow');
floating.middleware = [
offset(TOOLTIP_ARROW.gap),
flip(),
shift({padding: 12}),
...(arrowElement ? [arrow({element: arrowElement})] : []),
transformOrigin({padding: 8}),
];
});
});
</script>

Open the full demo ↗

The tooltip combines pointer and keyboard entry without duplicating open state:

floating.configure({
middleware: [offset(3), flip(), shift({padding: 12})],
plugins: [
hover({handleClose: safePolygon({buffer: 4})}),
focus(),
dismiss(),
role({role: 'tooltip'}),
],
});

safePolygon() keeps the tooltip open while the pointer travels toward the surface. focus() covers keyboard focus, while dismiss() handles Escape and focus-out. The demo registers floating-arrow after the template mounts so the arrow height participates in the visual offset.

<floating-root placement="top" interactions="hover focus dismiss">
<floating-reference><button>Inspect signal</button></floating-reference>
<template slot="content">
<div role="tooltip">Positioned by autoUpdate</div>
</template>
</floating-root>

Use hover(), focus(), dismiss(), and role({role: 'tooltip'}) in the Vue adapter with the same behavior contract.

The tooltip is a native Popover surface, so its entry and exit motion uses the same CSS-only discrete transition described in Entry and exit animation. Keep the surface rendered while closed so the browser can finish its display and overlay transitions.