Popover
A panel that hangs off a trigger and holds whatever you put in it — a filter form, a colour picker, a profile card, a confirmation.
Walkthrough
1. Basic
The trigger snippet gets the props that wire it to the panel, so the ARIA lands on the real button. close comes back through children.
<script lang="ts"> import { Button, Popover, Text } from 'omaris';</script><Popover title="Retention" arrow> {#snippet trigger(props)} <Button variant="outlined" {...props}>What happens on delete?</Button> {/snippet} {#snippet children({ close })} <Text variant="body-sm" tone="muted"> Deleted items sit in the bin for 30 days, then they are purged. Restoring one puts it back where it was. </Text> <Button size="sm" variant="text" class="mt-3" onclick={close}>Got it</Button> {/snippet}</Popover> 2. Form
A panel that holds a form. Focus moves to the first field on open, Tab cycles inside, Escape closes and hands focus back to the button.
<script lang="ts"> import { Button, Checkbox, Input, Popover } from 'omaris'; let contains = $state(''); let archived = $state(false); let mine = $state(true);</script><Popover title="Filters" description="Narrowing the invoice list." size="lg" align="start" arrow> {#snippet trigger(props)} <Button variant="outlined" {...props}>Filters</Button> {/snippet} <div class="flex flex-col gap-3"> <Input label="Contains" placeholder="invoice" bind:value={contains} /> <Checkbox label="Include archived" bind:checked={archived} /> <Checkbox label="Only mine" bind:checked={mine} /> </div> {#snippet footer({ close })} <Button variant="text" size="sm" onclick={close}>Cancel</Button> <Button size="sm" onclick={close}>Apply</Button> {/snippet}</Popover> 3. Placement
side and align are preferences: the panel flips when there isn't room and shifts to stay on screen. The arrow follows the trigger, not the panel's middle.
<script lang="ts"> import { Button, Popover, Text } from 'omaris'; const sides = ['top', 'bottom', 'left', 'right'] as const;</script><div class="grid grid-cols-2 gap-3"> {#each sides as side (side)} <Popover {side} size="sm" arrow title="Placed {side}"> {#snippet trigger(props)} <Button variant="tonal" class="w-full" {...props}>{side}</Button> {/snippet} <Text variant="body-sm" tone="muted">Scroll the page and it keeps up.</Text> </Popover> {/each}</div> 4. Hover card
openOn="hover" is the preview card: it waits before opening, and stays open long enough for the pointer to travel into it. A click still works on touch.
Merged by after review.
<script lang="ts"> import { Avatar, Button, Popover, Text } from 'omaris';</script><Text variant="body-md"> Merged by <Popover openOn="hover" side="bottom" align="start" size="lg" arrow> {#snippet trigger(props)} <Button variant="text" size="sm" {...props}>@omer</Button> {/snippet} <div class="flex items-start gap-3"> <Avatar name="Omer Chetin" /> <div class="flex flex-col gap-0.5"> <Text variant="title-sm">Omer Chetin</Text> <Text variant="body-sm" tone="muted"> Builds design systems and the tools that write them. </Text> </div> </div> </Popover> after review.</Text> 5. Overridden
size="auto" drops the width and the padding, so the panel is exactly what you put in it — and classes reaches every part, including the scrolling content and the arrow.
<script lang="ts"> import { Button, IconButton, Popover, Text } from 'omaris';</script><div class="flex items-center gap-3"> <Popover size="auto" class="w-64 max-w-full overflow-hidden" align="end"> {#snippet trigger(props)} <IconButton aria-label="Help" {...props}> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <circle cx="12" cy="12" r="9" /> <path d="M9.5 9.5a2.5 2.5 0 1 1 3 2.45V14" stroke-linecap="round" /> <circle cx="12" cy="17.5" r="0.75" fill="currentColor" stroke="none" /> </svg> </IconButton> {/snippet} {#snippet children({ close })} <div class="bg-primary-container p-4 text-primary-container-foreground"> <Text variant="title-sm">Shortcut</Text> </div> <div class="flex flex-col gap-2 p-4"> <Text variant="body-sm" tone="muted">Press ⌘K anywhere to jump between projects.</Text> <Button size="sm" variant="text" class="self-end" onclick={close}>Close</Button> </div> {/snippet} </Popover> <Popover matchWidth size="auto" classes={{ trigger: 'w-64', content: 'p-3' }}> {#snippet trigger(props)} <Button variant="outlined" class="w-full" {...props}>Matches its trigger</Button> {/snippet} <Text variant="body-sm" tone="muted"> <code>matchWidth</code> pins the panel to the trigger's width. </Text> </Popover></div>