component
A contextual floating component for displaying information and interactive content anchored to a trigger element. Uses Floating UI for intelligent positioning, with an arrow tying the panel to its trigger and click-outside-to-close behavior.
The default popover with a trigger button and positioned content.
---
import Popover from "../popover.astro";
import Button from "@pindoba/astro-button";
import { stack } from "@pindoba/styled-system/patterns";
---
<div class={stack({ gap: "md", direction: "column", align: "start" })}>
<Popover id="astro-demo-popover-default" title="Popover Title">
This is the popover content. Click outside to close.
</Popover>
<Button id="astro-demo-popover-trigger">Open Popover</Button>
</div>
<script>
import { usePopover } from "@pindoba/astro-popover/use-popover";
const trigger = document.getElementById("astro-demo-popover-trigger");
usePopover({ id: "astro-demo-popover-default", trigger });
</script>Every popover points back at its trigger. The arrow stays centred on the anchor
as the popover repositions and moves to the correct edge when the panel flips, and
it takes its fill and border from the panel itself — a recoloured or translucent
popover needs no extra configuration. Pass arrow={false} for surfaces that
shouldn’t read as tethered, such as a rail flyout or a submenu that overlaps the
panel it grows from.
---
import Popover from "../popover.astro";
import Button from "@pindoba/astro-button";
import { stack } from "@pindoba/styled-system/patterns";
---
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<Popover id="astro-demo-popover-arrow-on" title="Tethered">
The arrow tracks the trigger and follows the panel through a flip.
</Popover>
<Button id="astro-demo-popover-arrow-on-trigger">With arrow</Button>
<Popover id="astro-demo-popover-arrow-off" title="Untethered" arrow={false}>
No arrow — the panel reads as its own surface.
</Popover>
<Button id="astro-demo-popover-arrow-off-trigger">arrow={"{false}"}</Button>
</div>
<script>
import { usePopover } from "@pindoba/astro-popover/use-popover";
usePopover({
id: "astro-demo-popover-arrow-on",
trigger: document.getElementById("astro-demo-popover-arrow-on-trigger"),
});
usePopover({
id: "astro-demo-popover-arrow-off",
trigger: document.getElementById("astro-demo-popover-arrow-off-trigger"),
});
</script>Use the placement prop to control where the popover appears relative to its trigger. Automatically flips and shifts when there is insufficient space.
---
import Popover from "../popover.astro";
import Button from "@pindoba/astro-button";
import { stack } from "@pindoba/styled-system/patterns";
---
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<Popover id="astro-demo-popover-top" title="Top">
Placed above the trigger.
</Popover>
<Button id="astro-demo-popover-top-trigger">Top</Button>
<Popover id="astro-demo-popover-bottom" title="Bottom">
Placed below the trigger.
</Popover>
<Button id="astro-demo-popover-bottom-trigger">Bottom</Button>
<Popover id="astro-demo-popover-left" title="Left">
Placed to the left.
</Popover>
<Button id="astro-demo-popover-left-trigger">Left</Button>
<Popover id="astro-demo-popover-right" title="Right">
Placed to the right.
</Popover>
<Button id="astro-demo-popover-right-trigger">Right</Button>
</div>
<script>
import { usePopover } from "@pindoba/astro-popover/use-popover";
usePopover({
id: "astro-demo-popover-top",
trigger: document.getElementById("astro-demo-popover-top-trigger"),
placement: "top",
});
usePopover({
id: "astro-demo-popover-bottom",
trigger: document.getElementById("astro-demo-popover-bottom-trigger"),
placement: "bottom",
});
usePopover({
id: "astro-demo-popover-left",
trigger: document.getElementById("astro-demo-popover-left-trigger"),
placement: "left",
});
usePopover({
id: "astro-demo-popover-right",
trigger: document.getElementById("astro-demo-popover-right-trigger"),
placement: "right",
});
</script>Extra elements that open the popover on click (open-only — never toggle) and are treated as "inside" by the outside-click handler. Useful when an input field opens an attached calendar/listbox owned by a sibling button.
Element used as the Floating UI positioning reference. Falls back to the trigger element when omitted. Also counts as "inside" for outside-click dismissal, so clicks on the anchor cannot accidentally close the popover.
trueRender the small arrow that ties the popover's panel back to its trigger. It tracks the trigger's centre on every reposition and follows the panel through `flip()`, so it always points at the anchor. Set to `false` for surfaces that shouldn't look tethered — a rail flyout or a nested submenu that deliberately overlaps the panel it grows out of.
falseWhen true, focus moves naturally into the dialog on open. When false (the default), the popover restores focus + scroll position after the native dialog focusing steps. Set to true for autocomplete listboxes and similar overlays where keyboard navigation inside is the primary interaction.
"surface"Background style of the dialog panel. surface: default surface; sunken: recessed; elevated: raised with shadow; transparent: no background.
"default"Box-shadow border style of the dialog panel. none: no border; default: standard border; bold: stronger emphasis; muted: subtle separation.
Props to customize the close button (merged with defaults).
100Delay in ms before a hover trigger closes the popover. Ignored by non-hover strategies. Moving the cursor between trigger and dialog cancels a pending close.
Custom mousedown handler. When provided, replaces the default backdrop-click-to-close behavior. Used by Popover to also check the trigger element.
Positions the dialog along an edge of the screen. Automatically removes the border radius on the attached edge unless overridden.
falseWhen true, this dialog does not participate in the dialog stack. Useful for popovers and non-blocking overlays. Auto-detected for popovers.
Semantic feedback color scheme applied to the dialog panel.
Size the popover's height to fit its content rather than a fixed height.
Size the popover's width to fit its content rather than a fixed width.
Elements treated as "inside" by the outside-click handler without any trigger wiring — clicking them neither opens nor closes the popover. For nested floating surfaces that belong to this popover but render outside its DOM subtree (e.g. a menu's portaled submenu dialogs).
trueWhen true, opens as a modal dialog using showModal() — blocks interaction with the rest of the page and enables the backdrop.
trueWhen true, prevents body scroll while the dialog is open. Uses a counter to handle stacked dialogs correctly.
falseSize the popup's width to the anchor/trigger width (trigger-width dropdowns like combobox / select).
falseKeep the popup at least as wide as the anchor/trigger (`min-width`) while letting it grow to fit wider content — select-style dropdowns whose options can exceed the trigger width. Ignored when `matchReferenceWidth` is set.
Callback called when the dialog open state changes.
Callback called when the dialog closes.
Callback called when the dialog opens.
falseControls the open/closed state of the popover. Bindable in Svelte.
150Delay in ms before a hover trigger opens the popover. Ignored by non-hover strategies.
"none"Internal padding of the dialog panel.
Per-slot style and attribute override bag for customizing the popover's rendered elements.
"bottom"Preferred placement of the popover relative to its trigger/anchor. Consumed by the client positioning controller (Svelte/React); the Astro SSR wrapper applies it via the client `usePopover()` API rather than as a prop.
Override the Floating UI offset between the reference and the popup. `mainAxis` is the gap along the placement axis (default 8; negative values overlap the reference); `crossAxis` slides along the perpendicular axis (default 0).
"md"Border radius of the dialog panel. Automatically set to none on the attached edge for drawer dialogs.
Border radius for bottom-left and bottom-right corners.
Border radius for top-left and bottom-left corners.
Border radius for top-right and bottom-right corners.
Border radius for top-left and top-right corners.
"lg"Elevation shadow of the popover panel. Popovers float over the page, so they carry a shadow by default; pass "none" to flatten it. Popovers used as transparent positioning shells (select / combobox / menu) default to "none" — the child that paints the surface owns the elevation.
trueWhether to show the close button in the header.
Secondary text displayed below the title. Renders the subheading row, which also accepts subheading-leading and subheading-trailing slots.
Text displayed in the dialog header. When provided, the header is rendered with the title, close button, and any heading or subheading slots.
falseApply a frosted glass effect with backdrop blur to the dialog panel.
Render-prop for the trigger; spread the supplied props onto your element.
Bound trigger element used as the positioning anchor.
"click"How the trigger opens/closes the popover. "click" toggles on click. "hover" opens on mouseenter (after openDelay) and closes on mouseleave. "focus" opens on focus. "hover-focus" combines both. "manual" leaves event wiring to the consumer.
Explicit z-index for the popover dialog. Non-modal popovers paint in DOM order by default; nested surfaces (e.g. menu submenus) set this per nesting depth so a child always paints above the panel it overlaps.