Popover
Floating panel anchored to a trigger element with auto-positioning, flip behavior, and backdrop dismissal.
import { Popover } from 'phoundry-ui'; Basic Popover
Show code
<Popover>
{#snippet trigger(toggle)}
<Button onclick={toggle}>Open Popover</Button>
{/snippet}
<div class="p-3 bg-surface-base rounded-lg border border-border-muted shadow-lg">
<p class="text-sm">Popover content here</p>
</div>
</Popover>Placement Options
Show code
<Popover placement="top">...</Popover>
<Popover placement="bottom-end">...</Popover>
<Popover placement="right">...</Popover>Offset & flip
offset grows the gap from the anchor; flip=false keeps the requested placement even near viewport edges (may overflow — test on small screens).
Show code
<Popover placement="right-start" offset={12} flip={false}>
{#snippet trigger(t)}
<Button onclick={t}>Anchor</Button>
{/snippet}
<div class="p-3">…</div>
</Popover>Non-dismissible backdrop
Outside clicks no longer close the surface — pair with a button that calls toggle() or clears bind:open.
Show code
<Popover dismissible={false} bind:open>
{#snippet trigger(t)}<Button onclick={t}>Open</Button>{/snippet}
<div>Use an explicit Close action</div>
</Popover>Controlled Open
Show code
let open = $state(false);
<Popover bind:open>
{#snippet trigger(toggle)}
<Button onclick={toggle}>Toggle</Button>
{/snippet}
<div class="p-3">Controlled content</div>
</Popover>
<Button onclick={() => open = false}>Close from outside</Button>Programmatic overlay
Use when the anchor is not a stable child of the overlay host (selection rects, hover targets). Mount PopoverOverlay once in the root layout; optional content on open() overrides the default child.
Show code
import { getPopoverManager, PopoverOverlay } from 'phoundry-ui';
// In +layout.svelte after setupOverlays():
// <PopoverOverlay>{#snippet children()}…toolbar…{/snippet}</PopoverOverlay>
const popover = getPopoverManager();
popover.open({
anchor: elementOrDomRect,
placement: 'top',
ariaLabel: 'Actions',
});Props
| Prop | Type | Default | Description |
|---|---|---|---|
| open | boolean | false | Whether the popover is open. Bindable. |
| onOpenChange | (open: boolean) => void | — | Callback when open state changes. |
| placement | PopoverPlacement | 'bottom-start' | Positioning relative to the trigger element. |
| offset | number | 4 | Gap in pixels between trigger and popover. |
| flip | boolean | true | Flip to opposite side if there is not enough space. |
| dismissible | boolean | true | Close when clicking outside the popover. |
| class | string | — | Additional CSS classes for the floating panel. |
| trigger | Snippet<[toggle: () => void]> | — | Trigger region — receives `toggle`. Optional only if you drive `open` entirely from the outside (unusual). |
| children required | Snippet | — | Popover body content. |
Usage tips
- The
triggersnippet receives atogglefunction — call it on click to open/close. - Use
bind:openfor controlled state — you can open/close from external buttons or logic. - With
flip: true(default), the popover repositions if it would overflow the viewport. - Set
dismissible: falseto prevent closing on outside clicks (useful for forms). - The floating panel uses
role="dialog"; focus management is minimal — trap focus inside when hosting complex widgets. Escapecloses while open regardless ofdismissible(handled onwindow).