Overlay

Popover

A floating panel anchored to a trigger element. Supports positioning, arrows, custom content, and controlled state.

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

SlotDescription
contentMain popover content
arrowArrow element. Its fill is the panel color
arrowBorderStroke along the two slanted edges of the arrow, matching the panel ring

Snippets

SnippetDescription
childrenTrigger element (receives open state)
contentPopover content (receives open and close)

Props

PropTypeDefault
openbooleanfalse
arrowboolean | ArrowPropsfalse
transitionbooleantrue
portalbooleantrue
dismissiblebooleantrue
side'top'|'right'|'bottom'|'left''bottom'
sideOffsetnumber8
align'start'|'center'|'end''center'
refHTMLElement | nullnull
classstring-
uiRecord<Slot, Class>-