# Button and popover menu

Build a trigger that opens a popover card of arbitrary content with kai-popover — the ChatGPT-style header menu of a model row, a nested group, and a toggle.

A trigger that opens a floating card — a model picker, a settings menu, an account dropdown. `<kai-popover>` handles the open/close, positioning, and dismissal; you slot the trigger and whatever content the card needs. The menu below rebuilds the ChatGPT header popover: a flagship model row, an expandable group, and a toggle.

## How it works

`<kai-popover>` takes two slots — `trigger` for the control, and the default slot for the panel. Clicking the trigger toggles the panel; Escape or a click outside closes it (clicks inside don't). The panel is a `role="dialog"` region, not a menu, so it holds any markup — rows, a nested group, a toggle switch.

```html
<kai-popover placement="bottom-start">
  <button slot="trigger">GPT-5.5 ▾</button>

  <div>
    <button class="row">GPT-5.5 — Flagship model</button>
    <button class="row">Legacy models ▾</button>
    <label class="row">Temporary chat <span class="switch"></span></label>
  </div>
</kai-popover>

<script type="module">

  // optional: react to open/close
  document.querySelector('kai-popover')
    .addEventListener('kai-open-change', (e) => console.log('open:', e.detail.open));
</script>
```

The panel content lives in your light DOM, so your own styles apply to it directly — no shadow-DOM piercing. `kai-popover` only frames and positions the card.

### Controlled or uncontrolled

By default the popover manages its own open state. To drive it from your app, set the `open` property and update it from `kai-open-change`:

```js
const pop = document.querySelector('kai-popover');
pop.open = true;                       // open it programmatically
pop.addEventListener('kai-open-change', (e) => { pop.open = e.detail.open; });
```

## Props and events

| Name | Type | Notes |
|---|---|---|
| `placement` | `Placement` | Floating placement (`bottom-start`, `top`, `right`, …). Defaults to `bottom-start`. |
| `gutter` | `number` | Gap in px between trigger and panel. Defaults to `6`. |
| `open` | `boolean` | Controlled open state. Omit for click-to-toggle. |
| `kai-open-change` | event | Fires `{ open }` on every open/close (click, Escape, outside-click). |

## Next steps

- **[Compose your own shell](/patterns/compose-your-own/)** — drop a header menu into a custom chat layout.
- **[`kai-model-switcher` reference](/components/model-switcher/)** — the built-in model dropdown when you only need a flat model list.
- **[Theming](/guides/theming/)** — the tokens the popover panel inherits.
