Skip to content
llms.txt
llms.txt

Popover

Open the live popover demo

Live demo

Popover

Source for this preview

PopoverExample.astro
<article id="popover-demo" class="demo-card popover-card">
<div class="card-top">
<span class="number">B</span><span class="chip">named slots</span>
</div>
<h3>Anchored popover</h3>
<p>The trigger and panel are composed declaratively through named slots.</p>
<div class="card-action">
<floating-root
id="popover-root"
interactions="click dismiss"
placement="bottom-start"
>
<floating-reference>
<button class="coral-button">
Open coordinates <span aria-hidden="true">+</span>
</button>
</floating-reference>
<template slot="content">
<div class="popover-panel">
<span class="panel-kicker">NATIVE POPOVER / 42.8°</span>
<strong>Popover, still connected.</strong>
<p>ARIA and outside-press behavior are owned by the component.</p>
<button class="text-button" data-fup-close>Close panel</button>
</div>
</template>
</floating-root>
</div>
<code>&lt;template slot="content"&gt; + native popover</code>
</article>
<script>
import {
FloatingRootElement,
flip,
offset,
shift,
transformOrigin,
} from '@floating-ui-plus/web-components';
import {initializeExample} from './initialize-example';
initializeExample('popover', (scope) => {
const floating = FloatingRootElement.query(scope, '#popover-root');
floating.configure({
middleware: [
offset(12),
flip(),
shift({padding: 18}),
transformOrigin({padding: 12}),
],
});
});
</script>

Open the full demo ↗

The trigger and panel remain in one root. The template is inert while closed and becomes a native Popover when opened:

<floating-root
interactions="click dismiss"
placement="bottom-start"
>
<floating-reference><button>Open coordinates</button></floating-reference>
<template slot="content">
<section aria-label="Coordinates">
Popover, still connected.
<button data-fup-close>Close panel</button>
</section>
</template>
</floating-root>

This is the browser’s standard Popover API in action. The demo keeps the surface in the root tree so the platform owns top-layer stacking and light dismissal; it does not emulate a popover with a portal.

The demo uses offset(12), flip(), and shift({padding: 18}). The Vue version receives the same native Popover behavior by default. Use a portal only when the panel must escape a clipping ancestor; it is not required for this composition.

The panel also demonstrates CSS-only entry and exit motion with :popover-open, @starting-style, and discrete display/overlay transitions. See Entry and exit animation for the complete pattern and its rendering requirements.