Overlay

Popover

Display rich content in a floating panel anchored to a trigger element. Supports positioning, alignment, arrows, focus trapping, and dismissible behavior.

Playground

Experiment with different props in real-time.

Basic Usage

Place a trigger element as a child and use the content snippet for the popover body.

<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

Use the side prop to control which side of the trigger the popover appears on.

<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

Set arrow to show a directional arrow, or pass an object to customize its dimensions.

<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

Use the align prop to control the alignment relative to 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>

With Form Content

Popover content can contain interactive form elements like inputs, selects, and buttons.

<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 to control the popover state externally.

<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

Use the ui prop to override classes on specific internal elements of the Popover.

SlotDescription
contentThe popover content panel — controls background, border, shadow, and rounding
arrowThe arrow element pointing to the trigger — controls size and color

Snippets

Use Svelte 5 snippets to customize specific parts of the Popover.

SnippetTypeDescription
childrenSnippet<[{ open: boolean }]>Trigger element — receives open state
contentSnippet<[{ open: boolean; close: () => void }]>Popover panel content — receives open state and close function

Props

PropTypeDefault
openbooleanfalse
onOpenChange(open: boolean) => void-
arrowboolean | { width?: number; height?: number }false
transitionbooleantrue
dismissiblebooleantrue
side'top' | 'right' | 'bottom' | 'left''bottom'
sideOffsetnumber8
align'start' | 'center' | 'end''center'
trapFocusbooleantrue
portalbooleantrue
childrenSnippet<[{ open: boolean }]>-
contentSnippet<[{ open: boolean; close: () => void }]>-
classstring-
uiRecord<Slot, Class>-