component

Popover

A contextual floating component for displaying information and interactive content anchored to a trigger element. Uses Floating UI for intelligent positioning, with an arrow tying the panel to its trigger and click-outside-to-close behavior.

Default

The default popover with a trigger button and positioned content.

Popover Title

This is the popover content. Click outside to close.
---
import Popover from "../popover.astro";
import Button from "@pindoba/astro-button";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "md", direction: "column", align: "start" })}>
  <Popover id="astro-demo-popover-default" title="Popover Title">
    This is the popover content. Click outside to close.
  </Popover>

  <Button id="astro-demo-popover-trigger">Open Popover</Button>
</div>

<script>
  import { usePopover } from "@pindoba/astro-popover/use-popover";

  const trigger = document.getElementById("astro-demo-popover-trigger");
  usePopover({ id: "astro-demo-popover-default", trigger });
</script>

Arrow

Every popover points back at its trigger. The arrow stays centred on the anchor as the popover repositions and moves to the correct edge when the panel flips, and it takes its fill and border from the panel itself — a recoloured or translucent popover needs no extra configuration. Pass arrow={false} for surfaces that shouldn’t read as tethered, such as a rail flyout or a submenu that overlaps the panel it grows from.

Tethered

The arrow tracks the trigger and follows the panel through a flip.

Untethered

No arrow — the panel reads as its own surface.
---
import Popover from "../popover.astro";
import Button from "@pindoba/astro-button";
import { stack } from "@pindoba/styled-system/patterns";
---

<div
  class={stack({
    gap: "md",
    direction: "row",
    align: "center",
    flexWrap: "wrap",
  })}
>
  <Popover id="astro-demo-popover-arrow-on" title="Tethered">
    The arrow tracks the trigger and follows the panel through a flip.
  </Popover>
  <Button id="astro-demo-popover-arrow-on-trigger">With arrow</Button>

  <Popover id="astro-demo-popover-arrow-off" title="Untethered" arrow={false}>
    No arrow — the panel reads as its own surface.
  </Popover>
  <Button id="astro-demo-popover-arrow-off-trigger">arrow={"{false}"}</Button>
</div>

<script>
  import { usePopover } from "@pindoba/astro-popover/use-popover";

  usePopover({
    id: "astro-demo-popover-arrow-on",
    trigger: document.getElementById("astro-demo-popover-arrow-on-trigger"),
  });
  usePopover({
    id: "astro-demo-popover-arrow-off",
    trigger: document.getElementById("astro-demo-popover-arrow-off-trigger"),
  });
</script>

Placement

Use the placement prop to control where the popover appears relative to its trigger. Automatically flips and shifts when there is insufficient space.

Top

Placed above the trigger.

Bottom

Placed below the trigger.

Left

Placed to the left.

Right

Placed to the right.
---
import Popover from "../popover.astro";
import Button from "@pindoba/astro-button";
import { stack } from "@pindoba/styled-system/patterns";
---

<div
  class={stack({
    gap: "md",
    direction: "row",
    align: "center",
    flexWrap: "wrap",
  })}
>
  <Popover id="astro-demo-popover-top" title="Top">
    Placed above the trigger.
  </Popover>
  <Button id="astro-demo-popover-top-trigger">Top</Button>

  <Popover id="astro-demo-popover-bottom" title="Bottom">
    Placed below the trigger.
  </Popover>
  <Button id="astro-demo-popover-bottom-trigger">Bottom</Button>

  <Popover id="astro-demo-popover-left" title="Left">
    Placed to the left.
  </Popover>
  <Button id="astro-demo-popover-left-trigger">Left</Button>

  <Popover id="astro-demo-popover-right" title="Right">
    Placed to the right.
  </Popover>
  <Button id="astro-demo-popover-right-trigger">Right</Button>
</div>

<script>
  import { usePopover } from "@pindoba/astro-popover/use-popover";

  usePopover({
    id: "astro-demo-popover-top",
    trigger: document.getElementById("astro-demo-popover-top-trigger"),
    placement: "top",
  });
  usePopover({
    id: "astro-demo-popover-bottom",
    trigger: document.getElementById("astro-demo-popover-bottom-trigger"),
    placement: "bottom",
  });
  usePopover({
    id: "astro-demo-popover-left",
    trigger: document.getElementById("astro-demo-popover-left-trigger"),
    placement: "left",
  });
  usePopover({
    id: "astro-demo-popover-right",
    trigger: document.getElementById("astro-demo-popover-right-trigger"),
    placement: "right",
  });
</script>
props · 42 shown · 42 total
additionalTriggerElements
HTMLElement[]

Extra elements that open the popover on click (open-only — never toggle) and are treated as "inside" by the outside-click handler. Useful when an input field opens an attached calendar/listbox owned by a sibling button.

anchorElement
nullHTMLElement

Element used as the Floating UI positioning reference. Falls back to the trigger element when omitted. Also counts as "inside" for outside-click dismissal, so clicks on the anchor cannot accidentally close the popover.

arrow
boolean
default true

Render the small arrow that ties the popover's panel back to its trigger. It tracks the trigger's centre on every reposition and follows the panel through `flip()`, so it always points at the anchor. Set to `false` for surfaces that shouldn't look tethered — a rail flyout or a nested submenu that deliberately overlaps the panel it grows out of.

autoFocus
boolean
default false

When true, focus moves naturally into the dialog on open. When false (the default), the popover restores focus + scroll position after the native dialog focusing steps. Set to true for autocomplete listboxes and similar overlays where keyboard navigation inside is the primary interaction.

background
"surface.peak""surface.hill""surface.base""surface.valley""surface.ground""transparent"
default "surface"

Background style of the dialog panel. surface: default surface; sunken: recessed; elevated: raised with shadow; transparent: no background.

border
"none""bold""default""muted""accent"
default "default"

Box-shadow border style of the dialog panel. none: no border; default: standard border; bold: stronger emphasis; muted: subtle separation.

closeButtonProps
Partial<DialogCloseButtonProps>

Props to customize the close button (merged with defaults).

closeDelay
number
default 100

Delay in ms before a hover trigger closes the popover. Ignored by non-hover strategies. Moving the cursor between trigger and dialog cancels a pending close.

customMouseDown
(e: MouseEvent) => void

Custom mousedown handler. When provided, replaces the default backdrop-click-to-close behavior. Used by Popover to also check the trigger element.

drawer
"bottom""left""right""top"

Positions the dialog along an edge of the screen. Automatically removes the border radius on the attached edge unless overridden.

excludeFromStack
boolean
default false

When true, this dialog does not participate in the dialog stack. Useful for popovers and non-blocking overlays. Auto-detected for popovers.

feedback
"primary""neutral""success""warning""danger""inherit"

Semantic feedback color scheme applied to the dialog panel.

fitContentHeight
boolean

Size the popover's height to fit its content rather than a fixed height.

fitContentWidth
boolean

Size the popover's width to fit its content rather than a fixed width.

insideElements
HTMLElement[]

Elements treated as "inside" by the outside-click handler without any trigger wiring — clicking them neither opens nor closes the popover. For nested floating surfaces that belong to this popover but render outside its DOM subtree (e.g. a menu's portaled submenu dialogs).

isModal
boolean
default true

When true, opens as a modal dialog using showModal() — blocks interaction with the rest of the page and enables the backdrop.

lockScroll
boolean
default true

When true, prevents body scroll while the dialog is open. Uses a counter to handle stacked dialogs correctly.

matchReferenceWidth
boolean
default false

Size the popup's width to the anchor/trigger width (trigger-width dropdowns like combobox / select).

minWidthReference
boolean
default false

Keep the popup at least as wide as the anchor/trigger (`min-width`) while letting it grow to fit wider content — select-style dropdowns whose options can exceed the trigger width. Ignored when `matchReferenceWidth` is set.

onChange
(open: boolean) => void

Callback called when the dialog open state changes.

onClose
() => void

Callback called when the dialog closes.

onOpen
() => void

Callback called when the dialog opens.

open
boolean
default false

Controls the open/closed state of the popover. Bindable in Svelte.

openDelay
number
default 150

Delay in ms before a hover trigger opens the popover. Ignored by non-hover strategies.

padding
"sm""md""lg""xl""2xl""none""xs""3xl""4xl""5xl""6xl""7xl""8xl""4xs""3xs""2xs""9xl""10xl""11xl"
default "none"

Internal padding of the dialog panel.

passThrough
PopoverPassThrough

Per-slot style and attribute override bag for customizing the popover's rendered elements.

placement
"bottom""left""right""top""bottom-end""bottom-start""left-end""left-start""right-end""right-start""top-end""top-start"
default "bottom"

Preferred placement of the popover relative to its trigger/anchor. Consumed by the client positioning controller (Svelte/React); the Astro SSR wrapper applies it via the client `usePopover()` API rather than as a prop.

positionOffset
{ mainAxis?: number; crossAxis?: number }

Override the Floating UI offset between the reference and the popup. `mainAxis` is the gap along the placement axis (default 8; negative values overlap the reference); `crossAxis` slides along the perpendicular axis (default 0).

radius
"sm""md""lg""xl""2xl""none""xs""3xl""4xl""5xl""6xl""full""2xs""inner""inherit"
default "md"

Border radius of the dialog panel. Automatically set to none on the attached edge for drawer dialogs.

radiusBottom
"sm""md""lg""xl""2xl""none""xs""3xl""4xl""5xl""6xl""full""2xs""inner""inherit"

Border radius for bottom-left and bottom-right corners.

radiusLeft
"sm""md""lg""xl""2xl""none""xs""3xl""4xl""5xl""6xl""full""2xs""inner""inherit"

Border radius for top-left and bottom-left corners.

radiusRight
"sm""md""lg""xl""2xl""none""xs""3xl""4xl""5xl""6xl""full""2xs""inner""inherit"

Border radius for top-right and bottom-right corners.

radiusTop
"sm""md""lg""xl""2xl""none""xs""3xl""4xl""5xl""6xl""full""2xs""inner""inherit"

Border radius for top-left and top-right corners.

shadow
"sm""md""lg""xl""none""xs"
default "lg"

Elevation shadow of the popover panel. Popovers float over the page, so they carry a shadow by default; pass "none" to flatten it. Popovers used as transparent positioning shells (select / combobox / menu) default to "none" — the child that paints the surface owns the elevation.

showCloseButton
boolean
default true

Whether to show the close button in the header.

subtitle
nullstring

Secondary text displayed below the title. Renders the subheading row, which also accepts subheading-leading and subheading-trailing slots.

title
nullstring

Text displayed in the dialog header. When provided, the header is rendered with the title, close button, and any heading or subheading slots.

translucent
boolean
default false

Apply a frosted glass effect with backdrop blur to the dialog panel.

trigger slot svelte
Snippet<[TriggerSnippetProps]>

Render-prop for the trigger; spread the supplied props onto your element.

triggerElement svelte
HTMLElementnull

Bound trigger element used as the positioning anchor.

triggerStrategy
"click""hover""focus""hover-focus""manual"
default "click"

How the trigger opens/closes the popover. "click" toggles on click. "hover" opens on mouseenter (after openDelay) and closes on mouseleave. "focus" opens on focus. "hover-focus" combines both. "manual" leaves event wiring to the consumer.

zIndex
number

Explicit z-index for the popover dialog. Non-modal popovers paint in DOM order by default; nested surfaces (e.g. menu submenus) set this per nesting depth so a child always paints above the panel it overlaps.

Type

  • Components
  • Blocks