Overlay
Popover
A floating panel anchored to a trigger element. Supports positioning, arrows, custom content, and controlled state.
Updated in v2.6.1
The arrow is drawn properly now. v2.6.0 gave it the panel fill, and v2.6.1 gives it the panel
ring: it is stroked along both slanted edges through the new
ui.arrowBorder slot, and the
fill overlaps the panel edge so the ring reads as one unbroken outline rather than a line cutting
across the arrow's base.Basic Usage
<script lang="ts">
import { Popover, Button } from 'sv5ui';
</script>
<Popover>
<Button>Open Popover</Button>
{#snippet content({ close })}
<div class="p-4 space-y-2">
<p class="text-sm font-medium">Popover Title</p>
<p class="text-sm text-on-surface/60">This is some popover content.</p>
<Button onclick={close} size="sm" variant="soft">Close</Button>
</div>
{/snippet}
</Popover>Positions
Place on any side of the trigger.
<Popover side="top">
<Button variant="outline">Top</Button>
{#snippet content()}
<div class="p-3 text-sm">Top popover</div>
{/snippet}
</Popover>
<Popover side="right">
<Button variant="outline">Right</Button>
{#snippet content()}
<div class="p-3 text-sm">Right popover</div>
{/snippet}
</Popover>
<Popover side="bottom">
<Button variant="outline">Bottom</Button>
{#snippet content()}
<div class="p-3 text-sm">Bottom popover</div>
{/snippet}
</Popover>
<Popover side="left">
<Button variant="outline">Left</Button>
{#snippet content()}
<div class="p-3 text-sm">Left popover</div>
{/snippet}
</Popover>With Arrow
<Popover arrow>
<Button>With Arrow</Button>
{#snippet content()}
<div class="p-4">
<p class="text-sm">This popover has an arrow pointing to the trigger.</p>
</div>
{/snippet}
</Popover>
<Popover arrow={{ width: 16, height: 8 }}>
<Button variant="outline">Custom Arrow</Button>
{#snippet content()}
<div class="p-4">
<p class="text-sm">This popover has a custom-sized arrow.</p>
</div>
{/snippet}
</Popover>Alignment
Align to start, center, or end of the trigger.
<Popover align="start">
<Button variant="outline">Start</Button>
{#snippet content()}
<div class="p-3 text-sm">Aligned to start</div>
{/snippet}
</Popover>
<Popover align="center">
<Button variant="outline">Center</Button>
{#snippet content()}
<div class="p-3 text-sm">Aligned to center</div>
{/snippet}
</Popover>
<Popover align="end">
<Button variant="outline">End</Button>
{#snippet content()}
<div class="p-3 text-sm">Aligned to end</div>
{/snippet}
</Popover>Form Content
Rich content inside the popover.
<script lang="ts">
import { Popover, Button, Input, Select, FormField } from 'sv5ui';
let name = $state('');
let framework = $state('svelte');
const frameworks = [
{ value: 'svelte', label: 'Svelte' },
{ value: 'react', label: 'React' },
{ value: 'vue', label: 'Vue' }
];
</script>
<Popover>
<Button>Edit Settings</Button>
{#snippet content({ close })}
<div class="w-72 space-y-4 p-4">
<p class="text-sm font-semibold">Settings</p>
<FormField label="Name" size="sm">
<Input bind:value={name} placeholder="Enter name" size="sm" />
</FormField>
<FormField label="Framework" size="sm">
<Select items={frameworks} bind:value={framework} size="sm" />
</FormField>
<div class="flex justify-end gap-2">
<Button onclick={close} variant="outline" size="sm">Cancel</Button>
<Button onclick={close} size="sm">Save</Button>
</div>
</div>
{/snippet}
</Popover>Controlled
Use bind:open for programmatic control.
State: closed
<script lang="ts">
import { Popover, Button } from 'sv5ui';
let open = $state(false);
</script>
<div class="flex items-center gap-4">
<Button onclick={() => open = !open} variant="outline">
{open ? 'Close' : 'Open'} Popover
</Button>
<Popover bind:open>
<Button>Trigger</Button>
{#snippet content({ close })}
<div class="p-4 space-y-2">
<p class="text-sm">Controlled popover</p>
<p class="text-sm text-on-surface/60">State: {open ? 'Open' : 'Closed'}</p>
<Button onclick={close} size="sm" variant="soft">Close</Button>
</div>
{/snippet}
</Popover>
</div>UI Slots
| Slot | Description |
|---|---|
content | Main popover content |
arrow | Arrow element. Its fill is the panel color |
arrowBorder | Stroke along the two slanted edges of the arrow, matching the panel ring |
Snippets
| Snippet | Description |
|---|---|
children | Trigger element (receives open state) |
content | Popover content (receives open and close) |
Props
| Prop | Type | Default |
|---|---|---|
open | boolean | false |
arrow | boolean | ArrowProps | false |
transition | boolean | true |
portal | boolean | true |
dismissible | boolean | true |
side | 'top'|'right'|'bottom'|'left' | 'bottom' |
sideOffset | number | 8 |
align | 'start'|'center'|'end' | 'center' |
ref | HTMLElement | null | null |
class | string | - |
ui | Record<Slot, Class> | - |