Skip to content

Popover

import {Popover} from '@delightstack/components';
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>

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>

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>

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>

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>

Control the padding with dense or comfortable props.

View code
<Popover dense>Compact</Popover>
<Popover>Default</Popover>
<Popover comfortable>Spacious</Popover>
PropTypeDefaultDescription
ref_elementHTMLElementundefinedElement to position relative to
openedbooleanfalseControls visibility ($bindable())
placementPlacement'bottom'Preferred Floating UI placement
strategy'absolute' | 'fixed''fixed'CSS positioning strategy. 'fixed' renders through a Portal.
arrowbooleantrueShow arrow pointer toward the trigger
xnumberundefinedX coordinate for virtual positioning (used instead of ref_element)
ynumberundefinedY coordinate for virtual positioning (used instead of ref_element)
open_on_hoverbooleanfalseOpen when the reference element is hovered
open_on_clickbooleanfalseOpen when the reference element is clicked
open_on_focusbooleanfalseOpen when the reference element receives focus
close_on_outside_clickbooleantrueClose when clicking outside the popover
close_on_inside_clickbooleanfalseClose when a button-like element inside is clicked
close_on_escape_keybooleantrueClose on Escape key
disable_initial_focusbooleanfalseDo not auto-focus content when opened
hover_delaynumber100Delay in ms before opening on hover
densebooleanfalseReduced padding (0.5rem 0.75rem)
comfortablebooleanfalseIncreased padding (1.5rem 2rem)
radiusstringundefinedBorder radius override via --popover-radius
childrenSnippetundefinedPopover content
idstringauto-generatedElement ID
classstring''Additional CSS classes
stylestring''Inline CSS styles
EventDetailDescription
opened (bindable)booleanReactive state reflecting whether the popover is open
  • Focus trap via focusTrap from @delightstack/utilities keeps 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_click or open_on_focus is used)
  • Proper z-index stacking with data-popover-index for nested popovers