<!-- Protocore Design System — Popover -->
# Popover

- **Category:** Overlay (`overlay`)
- **Slug:** `overlay/popover`
- **Status:** stable
- **Platforms:** web
- **Import:** `import { Popover } from "@protocore/pds";`
- **Docs:** https://pds.protocore.io/components/overlay/popover

> A non-modal floating panel anchored to a trigger — for secondary controls, filters, and rich detail that shouldn't block the page.

## When to use it

A Popover floats **interactive content** next to the control that summoned it without dimming the page or trapping focus in a modal — inline filters, a quick edit, a color picker, supplementary detail. It's dismissed by clicking outside or pressing Esc.

If the panel is a **list of commands or actions**, use a **[DropdownMenu](/overlay/dropdown-menu)** instead — it brings roving-focus keyboard semantics and menu roles for free. If the content is **read-only and advisory**, a **[Tooltip](/overlay/tooltip)** (one line) or a **[HoverCard](/overlay/hover-card)** (rich entity preview) is lighter. If the task must block the rest of the UI, escalate to a **[Dialog](/overlay/dialog)**. Popover is the middle ground: interactive, but non-blocking.

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `asChild` | `boolean` | no | — | — |
| `className` | `string` | no | — | — |
| `style` | `CSSProperties` | no | — | — |

## Examples

### Basics

`Popover.Root` owns state, `Popover.Trigger` (via `asChild`) toggles it, and `Popover.Content` portals a raised panel with collision-aware placement — `side="bottom"`, `align="start"` by default.

```tsx
import { Popover, Button, Text, VStack } from "@protocore/pds";

export default function PopoverBasics() {
  return (
    <Popover.Root>
      <Popover.Trigger asChild>
        <Button variant="secondary">Region details</Button>
      </Popover.Trigger>
      <Popover.Content style={{ width: 260 }}>
        <VStack gap={2}>
          <Text weight={500}>eu-central-1</Text>
          <Text size="sm" color="secondary">
            3 availability zones · 812 req/s · p99 42ms
          </Text>
        </VStack>
      </Popover.Content>
    </Popover.Root>
  );
}
```

### Interactive content

Unlike a Tooltip, a Popover can hold real controls — inputs, buttons, a small form. Wrap the confirm action in `Popover.Close asChild` to dismiss on apply. Focus moves into the panel when it opens.

```tsx
import { useState } from "react";
import { Popover, Button, Field, NumberInput, VStack } from "@protocore/pds";
import { SlidersHorizontal } from "lucide-react";

export default function PopoverForm() {
  const [min, setMin] = useState(100);

  return (
    <Popover.Root>
      <Popover.Trigger asChild>
        <Button variant="secondary" startIcon={<SlidersHorizontal size={15} />}>
          Filter
        </Button>
      </Popover.Trigger>
      <Popover.Content style={{ width: 260 }}>
        <VStack gap={4}>
          <Field label="Min latency (ms)">
            <NumberInput value={min} onValueChange={setMin} min={0} step={10} />
          </Field>
          <Popover.Close asChild>
            <Button full>Apply</Button>
          </Popover.Close>
        </VStack>
      </Popover.Content>
    </Popover.Root>
  );
}
```

## Do & don't

**Do**

- Use it for interactive secondary content — filters, quick edits, pickers.
- Anchor it to the control that opened it; rely on collision flipping near edges.
- Wrap a confirm/apply control in Popover.Close so the panel dismisses.
- Keep the panel compact; if it needs the whole viewport, use a Dialog or Sheet.

**Don't**

- Put a menu of actions inside — DropdownMenu is the right primitive.
- Use it for read-only hints — that's a Tooltip or HoverCard.
- Stack Popovers on Popovers; flatten the interaction instead.
- Trap focus or dim the page — a Popover is deliberately non-modal.

## Accessibility

**Keyboard**

| Keys | Action |
| --- | --- |
| `Enter / Space` | On the trigger, toggles the popover open and closed. |
| `Esc` | Closes the popover and returns focus to the trigger. |
| `Tab` | Moves through focusable content inside the open panel. |

**Notes**

- Non-modal: content outside stays interactive, and focus is not trapped.
- Opening moves focus into the panel; closing restores it to the trigger.
- Closes on outside pointer-down or Esc; collision handling keeps it in the viewport.
- Use Popover.Anchor to position the panel against an element other than the trigger.

## Related

`dropdown-menu`, `tooltip`, `hover-card`, `dialog`

---

© Protocore. All rights reserved. Use of the Protocore Design System requires prior written authorization from Protocore (contact@protocore.io). These machine-readable materials must not be ingested into ML-training datasets or derivative design systems. See https://pds.protocore.io/legal/
