# Popover

A panel anchored to a control, holding content you can interact with.

- Group: Overlays
- Import: `import { Popover } from '@misoto22/folio'`
- Page: https://ui.misoto22.com/components/popover/
- Related: tooltip, dropdown-menu

## When to reach for it

Anything with a link, a field or a button in it. A tooltip describes and cannot be entered — put a control inside one and it becomes unreachable.

## Anatomy

- **Trigger** (required) — PopoverTrigger, a passthrough. Pass asChild to keep your own control; Radix returns focus here when the panel closes on Escape.
- **Panel** (required) — The anchored dialog: a flat 18rem wide with 1rem of padding, 8px off the trigger, named by label. Focus moves into it on open, but it is not trapped there.
- **Anchor** — PopoverAnchor, for when the panel should be positioned against something other than the control that opened it — the row an overflow button acts on, a selection in text.
- **Close** — PopoverClose, and the 32px X that showClose renders in the top-end corner. Off by default, unlike Dialog’s.

## Best practices

### Do

- Make label say what the panel holds rather than echoing the trigger: it is announced on entry, so “Filter options” tells a reader where they have landed where a repeat of the button text tells them nothing new.
- Use PopoverAnchor when the visual anchor is not the trigger — a toolbar button acting on a selected row — otherwise the panel tracks the button and drifts away from the thing it is editing.
- Wrap the dismissing control in PopoverClose rather than flipping your own state, so the close runs through Radix and focus goes back to the trigger instead of to the top of the document.
- Turn showClose on when the panel holds a form. It is off by default, and a non-modal panel whose only exit is clicking away gives an in-progress edit no deliberate end.

### Don’t

- It is not modal — Radix defaults modal to false and nothing here changes that, so there is no focus trap and no scroll lock: tabbing past the last control inside moves focus into the page, which Radix reads as a focus-outside and closes the panel mid-task.
- A list of actions belongs in a DropdownMenu. A popover’s contents are ordinary tab stops, so ten actions is ten stops with no type-ahead, where a menu is one stop with arrow keys and a letter jump inside it.
- The panel sits at --z-dropdown, which resolves to 220 — above a Dialog’s 210, so that a popover opened FROM a dialog is reachable. A popover the page opened and your own state holds open therefore paints over a modal that arrives afterwards; Radix closes it on the interaction outside, and a controlled open that ignores that is the one way to see it.

## Accessibility

- Inside a bounded frame — a device preview, an embedded console — wrap the subtree in `<OverlayContainer container={el}>`. The panel then renders into that element and collides with its edges instead of the viewport’s, and inherits the `dir` and `data-density` set there.
- label is required: a popover is a dialog, and an unnamed one announces nothing.
- Its contents Tab like the rest of the page, unlike a menu’s arrow-key list.

## Keyboard

- Enter / Space — Opens it.
- Tab — Moves through its contents like the rest of the page.
- Escape — Closes it and returns focus to the trigger.

## Popover

Re-export of `PopoverPrimitive.Root`.

Radix Popover root, trigger, anchor and close, as typed passthroughs.

## PopoverTrigger

Re-export of `PopoverPrimitive.Trigger`.

## PopoverAnchor

Re-export of `PopoverPrimitive.Anchor`.

## PopoverClose

Re-export of `PopoverPrimitive.Close`.

## PopoverContent

A panel anchored to a control, holding content the reader can interact with. The line against `Tooltip` is not visual, it is behavioural: a tooltip describes and cannot be entered; a popover holds things you tab to. Anything with a link, a field or a button in it is a popover, and putting that inside a tooltip makes it unreachable — the tooltip closes as soon as focus tries to move into it. Against `DropdownMenu`: a menu is a list of actions with menu semantics and arrow-key navigation. A popover is free-form, and its contents Tab like the rest of the page.

### Props

- `label` (required) — `string`. Names the panel for assistive tech. Required — a popover is a dialog.
- `showClose` — `boolean` default `false`. Show the top-end close control.

Also accepts: `ComponentProps<typeof PopoverPrimitive.Content>`.

## Example — a filter panel

```tsx
import { Button, Field, Popover, PopoverClose, PopoverContent, PopoverTrigger, Select, SelectItem } from '@misoto22/folio'

<Popover>
  <PopoverTrigger asChild>
    <Button variant="secondary">Filters</Button>
  </PopoverTrigger>
  <PopoverContent label="Deploy filters" showClose>
    <div className="flex flex-col gap-4">
      <Field label="Status">
        <Select label="Status" defaultValue="all">
          <SelectItem value="all">Any</SelectItem>
          <SelectItem value="live">Live</SelectItem>
        </Select>
      </Field>
      <PopoverClose asChild>
        <Button size="sm">Apply</Button>
      </PopoverClose>
    </div>
  </PopoverContent>
</Popover>
```

## Example — anchored elsewhere

```tsx
<Popover>
  <div className="flex w-full max-w-sm flex-col gap-3">
    <div className="flex items-center justify-between gap-3">
      <Text size="sm" tone="muted">Selected row</Text>
      <PopoverTrigger asChild>
        <Button variant="secondary" size="sm">Rename</Button>
      </PopoverTrigger>
    </div>
    <PopoverAnchor asChild>
      <div className="flex items-center justify-between gap-3 rounded-(--radius) border border-(--accent) bg-(--accent-muted) px-4 py-3">
        <span className="font-mono text-sm text-(--ink)">codex/photo-cache</span>
        <span className="mono-meta text-(--ink-3-aa)">62s</span>
      </div>
    </PopoverAnchor>
  </div>
  <PopoverContent label="Rename this branch" showClose>
    <div className="flex flex-col gap-4">
      <Field label="Branch name">
        <Input defaultValue="codex/photo-cache" />
      </Field>
      <PopoverClose asChild>
        <Button size="sm">Rename</Button>
      </PopoverClose>
    </div>
  </PopoverContent>
</Popover>
```

## Example — panel or menu

```tsx
<div className="flex flex-wrap items-center gap-3">
  <Popover>
    <PopoverTrigger asChild>
      <Button variant="secondary">Export size</Button>
    </PopoverTrigger>
    <PopoverContent label="Export size" showClose>
      <div className="flex flex-col gap-4">
        <Slider
          label="Longest edge"
          defaultValue={[2400]}
          min={800}
          max={6000}
          step={100}
          showValue
          format={(pixels) => `${pixels}px`}
        />
        <PopoverClose asChild>
          <Button size="sm">Export</Button>
        </PopoverClose>
      </div>
    </PopoverContent>
  </Popover>

  <DropdownMenu>
    <DropdownMenuTrigger asChild>
      <Button variant="secondary">Share</Button>
    </DropdownMenuTrigger>
    <DropdownMenuContent align="start">
      <DropdownMenuItem icon={RiFileCopyLine}>Copy link</DropdownMenuItem>
      <DropdownMenuItem icon={RiShareLine}>Post to the channel</DropdownMenuItem>
      <DropdownMenuItem icon={RiDownloadLine}>Download original</DropdownMenuItem>
      <DropdownMenuSeparator />
      <DropdownMenuItem icon={RiDeleteBinLine} destructive>Revoke the link</DropdownMenuItem>
    </DropdownMenuContent>
  </DropdownMenu>
</div>
```
