Pho Design System

Popover

A click-triggered floating panel anchored to its trigger, for rich, interactive content — a share box, a filter form, quick details. Built on Base UI Popover. Unlike a Tooltip, it can hold focusable controls; unlike a Dialog, it stays anchored and doesn’t trap the user.

Import

import { Popover } from "@photon-ai/pho-ui/components/popover";

Basic

Popover.Trigger opens the panel; it closes on click-away or Escape. Compose Title / Description and any controls inside Popover.Popup. The render prop lets the trigger and close reuse a Button.

<Popover.Root>
  <Popover.Trigger render={<Button variant="outlined">Share</Button>} />
  <Popover.Portal>
    <Popover.Positioner>
      <Popover.Popup>
        <Popover.Title>Share project</Popover.Title>
        <Popover.Description>
          Anyone with the link can view.
        </Popover.Description>
        <Popover.Close render={<Button size="sm">Copy link</Button>} />
      </Popover.Popup>
    </Popover.Positioner>
  </Popover.Portal>
</Popover.Root>

Placement

Popover.Positioner controls where the panel sits: side picks the edge (bottom by default), align sets alignment along it (center by default), and sideOffset is the gap — defaulted to 8 here. Placement flips automatically when the chosen side would overflow the viewport.

<Popover.Positioner side="right" align="start">
  <Popover.Popup>…</Popover.Popup>
</Popover.Positioner>

Anatomy

Props

Popover.Root

Popover.Trigger (renders a <button>)

Popover.Positioner (renders a <div>)

Popover.Popup (renders a <div role="dialog">)

Accessibility

The popup renders as role="dialog". Popover.Title supplies its accessible name via aria-labelledby and Popover.Description its aria-describedby, so including a Title is what makes the panel announce its purpose on open — otherwise give the Popup an aria-label. The trigger is wired with aria-haspopup="dialog", aria-expanded, and aria-controls.

On open, focus moves to the first tabbable element inside the popup (or the popup itself when opened by touch); on close it returns to the trigger. Focus is not trapped by default — set modal to true or 'trap-focus' to trap it, and render a Popover.Close inside the popup so touch screen-reader users can exit.

Best practices