Popover
Import
Section titled “Import”import {Popover} from '@delightstack/components';Basic Usage
Section titled “Basic Usage”View code
<script> import { Button, Popover } from '@delightstack/components/actions'; let ref_element = $state(undefined);</script>
<Button bind:button_element={ref_element}>Toggle Popover</Button><Popover {ref_element} open_on_click> <h2>Popover Title</h2> <p>Positioned automatically using Floating UI.</p> <p>Click outside to dismiss.</p></Popover>Examples
Section titled “Examples”Popover State Demo
Section titled “Popover State Demo”Control the state of the popover externally
View code
<script lang="ts"> import { Select, Toggle } from '@delightstack/components/form'; import { Popover, type PopoverPlacement } from '@delightstack/components/actions';
let ref_element = $state<HTMLElement | undefined>(undefined); let opened = $state(false); let placement = $state<PopoverPlacement>('bottom'); const placements: PopoverPlacement[] = [ 'top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-start', 'left-end', 'right', 'right-start', 'right-end', ];
</script>
<div class="controls"> <Toggle bind:checked={opened} label={opened ? 'Popover Opened' : 'Popover Closed'} /> <Select options={placements.map((p) => ({ label: p, value: p }))} bind:value={placement} /></div><div bind:this={ref_element} class="ref-element"> Popover position reference <p style="margin: 0; font-size: 0.875rem; opacity: 0.8;"> Resize the container to see the popover reposition. </p></div><Popover bind:opened bind:ref_element close_on_outside_click={false} close_on_escape_key={false} {placement}> <h2>Popover Title</h2> <p>Popover opened state is controlled outside the popover component.</p></Popover>
<style> .controls { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; align-items: center; width: 100%; } .ref-element { background-color: var(--color-bg-active); border: dashed 2px var(--color-outline); padding: 0.5rem 1rem; border-radius: 4px; margin: 1rem 0 0; resize: both; overflow: auto; }</style>Hover Info Popover
Section titled “Hover Info Popover”Open on hover with a delay. The hover trapezoid allows safe diagonal mouse movement between the trigger and the popover.
View code
<span bind:this={ref_element}>Hover over me for info</span><Popover {ref_element} open_on_hover> Helpful tooltip-like information appears on hover.</Popover>Context Menu Positioning (x/y)
Section titled “Context Menu Positioning (x/y)”Position the popover at an arbitrary coordinate instead of relative to a reference element. Useful for right-click menus.
View code
<div oncontextmenu={(e) => { e.preventDefault(); menuX = e.clientX; menuY = e.clientY; menuOpen = true; }}> Right-click in this area</div>
<Popover bind:opened={menuOpen} x={menuX} y={menuY} strategy="fixed"> <p>Custom context menu</p></Popover>Focus-Triggered Popover
Section titled “Focus-Triggered Popover”Open when the reference element receives focus, close on blur.
View code
<input bind:this={inputRef} placeholder="Focus to see suggestions" />
<Popover ref_element={inputRef} open_on_focus placement="bottom-start"> <ul style="padding: 0.5rem;"> <li>Suggestion 1</li> <li>Suggestion 2</li> <li>Suggestion 3</li> </ul></Popover>Dense and Comfortable
Section titled “Dense and Comfortable”Control the padding with dense or comfortable props.
View code
<Popover dense>Compact</Popover><Popover>Default</Popover><Popover comfortable>Spacious</Popover>| Prop | Type | Default | Description |
|---|---|---|---|
ref_element | HTMLElement | undefined | Element to position relative to |
opened | boolean | false | Controls visibility ($bindable()) |
placement | Placement | 'bottom' | Preferred Floating UI placement |
strategy | 'absolute' | 'fixed' | 'fixed' | CSS positioning strategy. 'fixed' renders through a Portal. |
arrow | boolean | true | Show arrow pointer toward the trigger |
x | number | undefined | X coordinate for virtual positioning (used instead of ref_element) |
y | number | undefined | Y coordinate for virtual positioning (used instead of ref_element) |
open_on_hover | boolean | false | Open when the reference element is hovered |
open_on_click | boolean | false | Open when the reference element is clicked |
open_on_focus | boolean | false | Open when the reference element receives focus |
close_on_outside_click | boolean | true | Close when clicking outside the popover |
close_on_inside_click | boolean | false | Close when a button-like element inside is clicked |
close_on_escape_key | boolean | true | Close on Escape key |
disable_initial_focus | boolean | false | Do not auto-focus content when opened |
hover_delay | number | 100 | Delay in ms before opening on hover |
dense | boolean | false | Reduced padding (0.5rem 0.75rem) |
comfortable | boolean | false | Increased padding (1.5rem 2rem) |
radius | string | undefined | Border radius override via --popover-radius |
children | Snippet | undefined | Popover content |
id | string | auto-generated | Element ID |
class | string | '' | Additional CSS classes |
style | string | '' | Inline CSS styles |
Events
Section titled “Events”| Event | Detail | Description |
|---|---|---|
opened (bindable) | boolean | Reactive state reflecting whether the popover is open |
Accessibility
Section titled “Accessibility”- Focus trap via
focusTrapfrom@delightstack/utilitieskeeps Tab cycling within the popover - Escape key closes the popover (respects nested popovers — closes innermost first)
- Focus returns to the trigger element on close
- Keyboard activation on the trigger element via Enter/Space (when
open_on_clickoropen_on_focusis used) - Proper
z-indexstacking withdata-popover-indexfor nested popovers