# Popover

> Non-modal floating content anchored to a trigger, for small forms, filters and details.

Category: Overlays · Status: stable · Package: @bindshq/stance-react@0.1.1 · Docs: https://stanceui.com/components/popover

Popover shows interactive content next to the element that opened it without blocking the rest of the page. It positions itself, flips when there is no room, and closes on Escape or an outside click. Use Tooltip for short text labels, DropdownMenu for lists of actions, and Dialog when the task needs the user’s full attention.

## Import

```tsx
import { Popover, PopoverTrigger, PopoverContent, PopoverClose, PopoverAnchor } from '@bindshq/stance-react';
```

Styles are imported once per app: `@bindshq/stance-tokens/css` and `@bindshq/stance-react/styles.css`.

## Examples

### Basic

```tsx
import { Button, Icon, Popover, PopoverContent, PopoverTrigger } from '@bindshq/stance-react';

export function Basic() {
  return (
    <Popover>
      <PopoverTrigger asChild>
        <Button variant="outline" leadingIcon={<Icon name="info" />}>
          Seat usage
        </Button>
      </PopoverTrigger>
      <PopoverContent aria-label="Seat usage">
        <p style={{ margin: 0, fontWeight: 600 }}>48 of 50 seats used</p>
        <p style={{ margin: '4px 0 0' }}>
          Seats free up when a user is removed from your organisation.
        </p>
      </PopoverContent>
    </Popover>
  );
}
```

### With a form

Use trapFocus when the popover contains a form.

```tsx
import { Button, FormField, Input, Popover, PopoverClose, PopoverContent, PopoverTrigger } from '@bindshq/stance-react';

export function WithForm() {
  return (
    <Popover>
      <PopoverTrigger asChild>
        <Button variant="secondary">Set reminder</Button>
      </PopoverTrigger>
      <PopoverContent aria-label="Set reminder" trapFocus align="start">
        <div style={{ display: 'grid', gap: 12, width: 260 }}>
          <FormField label="Remind me in (days)">
            <Input type="number" defaultValue={3} min={1} />
          </FormField>
          <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
            <PopoverClose asChild>
              <Button size="sm" variant="ghost">
                Cancel
              </Button>
            </PopoverClose>
            <PopoverClose asChild>
              <Button size="sm">Save</Button>
            </PopoverClose>
          </div>
        </div>
      </PopoverContent>
    </Popover>
  );
}
```

### Placement

Choose a side and alignment; it flips automatically near viewport edges.

```tsx
import { Button, Popover, PopoverContent, PopoverTrigger } from '@bindshq/stance-react';

export function Placement() {
  return (
    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 12 }}>
      {(['top', 'right', 'bottom', 'left'] as const).map((side) => (
        <Popover key={side}>
          <PopoverTrigger asChild>
            <Button variant="outline">{side}</Button>
          </PopoverTrigger>
          <PopoverContent side={side} aria-label={`Placed ${side}`}>
            Placed on the {side}.
          </PopoverContent>
        </Popover>
      ))}
    </div>
  );
}
```

## API reference

| Component | Prop | Type | Default | Description |
| --- | --- | --- | --- | --- |
| Popover | `open` | `boolean` | None | Controlled open state. |
| Popover | `defaultOpen` | `boolean` | `false` | Initial state when uncontrolled. |
| Popover | `onOpenChange` | `(open: boolean) => void` | None | Called when open state changes. |
| PopoverTrigger | `asChild` | `boolean` | `false` | Render your own element as the trigger. |
| PopoverAnchor | `asChild` | `boolean` | `false` | Position against this element instead of the trigger. |
| PopoverContent | `side` | `'top' \| 'right' \| 'bottom' \| 'left'` | `'bottom'` | Preferred side. |
| PopoverContent | `align` | `'start' \| 'center' \| 'end'` | `'center'` | Alignment along the side. |
| PopoverContent | `sideOffset` | `number` | `8` | Gap from the anchor in px. |
| PopoverContent | `alignOffset` | `number` | `0` | Shift along the alignment axis in px. |
| PopoverContent | `collisionPadding` | `number` | `8` | Minimum distance from the viewport edge. |
| PopoverContent | `avoidCollisions` | `boolean` | `true` | Flip and shift to stay in view. |
| PopoverContent | `trapFocus` | `boolean` | `false` | Keep Tab inside the popover. |
| PopoverContent | `autoFocus` | `boolean` | `true` | Move focus into the popover on open. |
| PopoverContent | `aria-label` | `string` | None | Accessible name when there is no visible heading. |
| PopoverContent | `onEscapeKeyDown / onPointerDownOutside` | `(event) => void` | None | Call preventDefault() to keep it open. |
| PopoverClose | `asChild` | `boolean` | `false` | Wrap your own Button to close. |

## Accessibility

- Content has role="dialog" (non-modal); give it an aria-label or a visible heading.
- The trigger exposes aria-haspopup="dialog", aria-expanded and aria-controls.
- Focus moves into the popover on open and returns to the trigger on Escape.
- Clicking the trigger again closes it; clicks inside nested overlays do not dismiss it.

## Keyboard

| Keys | Action |
| --- | --- |
| `Enter` `Space` | On the trigger: toggles the popover. |
| `Tab` | Moves through the content (wraps when trapFocus is on). |
| `Esc` | Closes the popover and returns focus to the trigger. |

## Guidelines

**Do**

- Keep content small and directly related to the trigger.
- Provide an explicit close or apply button for forms.

**Don’t**

- Put critical, page-blocking decisions in a popover.
- Open popovers on hover; use HoverCard for previews.

## Related

- https://stanceui.com/components/tooltip
- https://stanceui.com/components/hover-card
- https://stanceui.com/components/dropdown-menu
- https://stanceui.com/components/dialog
