# Popover

## Overview

_[Design to fill]_

- **When to use**: _[Design to fill]_
- **When not to use**: _[Design to fill]_

## Anatomy

_[Design to fill]_

## Variants

_[Design to fill]_

## Props / API

You supply your own trigger (e.g. a `Button`) and link it to the popover: a `ref` on the trigger is passed to `Popover.Content` as `anchor` for positioning, and a shared `id` ties them together for accessibility via `aria-controls`.

### `Popover`

Wraps Base UI's `Popover.Root` and controls open/closed state.

| Prop           | Type                            | Default | Description                                                     |
| -------------- | ------------------------------- | ------- | --------------------------------------------------------------- |
| `open`         | `boolean`                       | -       | Controls whether the popover is open.                           |
| `onOpenChange` | `(open, event, reason) => void` | -       | Called when the open state changes (forwarded to Base UI Root). |
| `children`     | `React.ReactNode`               | -       | The trigger and `Popover.Content`.                              |

### `Popover.Content`

Renders the portalled, positioned popup.

| Prop              | Type                                           | Default    | Description                                                                   |
| ----------------- | ---------------------------------------------- | ---------- | ----------------------------------------------------------------------------- |
| `anchor`          | `Positioner["anchor"]`                         | -          | Element the popup is anchored to for positioning (typically the trigger ref). |
| `id`              | `string`                                       | -          | Popup id, match it to the trigger's `aria-controls`.                          |
| `role`            | `"dialog" \| "listbox" \| "menu" \| "tooltip"` | `"dialog"` | ARIA role of the popup (see below).                                           |
| `side`            | `Positioner["side"]`                           | `"bottom"` | Preferred side to place the popup relative to the anchor.                     |
| `sideOffset`      | `number`                                       | `8`        | Gap in pixels between the anchor and the popup.                               |
| `aria-label`      | `string`                                       | -          | Accessible name for the popup.                                                |
| `aria-labelledby` | `string`                                       | -          | Id of an element that labels the popup.                                       |
| `children`        | `React.ReactNode`                              | -          | Popup content.                                                                |

`role` guidance:

- `"dialog"` (default): popovers with generic interactive content such as a rich hover-card, a floating form, a date-picker, or a settings flyout.
- `"menu"`: a list of actions or commands (also set `aria-haspopup="menu"` on the trigger).
- `"listbox"`: a dropdown selection list.
- `"tooltip"`: plain, non-interactive text.

### `Popover.Close`

Renders a Base UI `Popover.Close` that dismisses the popover. Pass a single element (or a render function) as `children`; it is used as the close control via `render`.

| Prop       | Type                                               | Default | Description                                 |
| ---------- | -------------------------------------------------- | ------- | ------------------------------------------- |
| `children` | `ReactElement \| ((props, state) => ReactElement)` | -       | The element that acts as the close control. |

## States

- Closed
- Open

## Code examples

```tsx
import { Button, Popover } from "@mylighthouse/prism-react";
import { useId, useRef, useState } from "react";

export default function PopoverExample(): React.JSX.Element {
  const [open, setOpen] = useState(false);
  const triggerRef = useRef<HTMLButtonElement>(null);
  const popupId = useId();

  return (
    <>
      <Button
        aria-controls={popupId}
        aria-expanded={open}
        aria-haspopup="dialog"
        buttonType="secondary"
        onClick={() => setOpen(!open)}
        ref={triggerRef}
      >
        Open popover
      </Button>
      <Popover onOpenChange={setOpen} open={open}>
        <Popover.Content
          anchor={triggerRef}
          aria-label="More information"
          id={popupId}
          side="bottom"
        >
          <div className="flex flex-col gap-300 p-400">
            <p className="text-300--regular">
              This is a popover with arbitrary content. Use it for contextual
              actions or supplementary information.
            </p>
            <div className="flex justify-end">
              <Popover.Close>
                <Button buttonType="secondary" size="small">
                  Dismiss
                </Button>
              </Popover.Close>
            </div>
          </div>
        </Popover.Content>
      </Popover>
    </>
  );
}
```

## A11y intent

_[Design to fill]_

- The trigger needs `aria-expanded={open}` to communicate the open/closed state.
- The trigger needs `aria-haspopup` matching the popup role (e.g. `"dialog"`, `"menu"`) to indicate it controls a popup.
- The trigger's `aria-controls` must point to the same `id` passed to `Popover.Content`.
- Give the popup an accessible name via `aria-label` or `aria-labelledby` on `Popover.Content`.

## Cross-references

_[Design to fill]_
