# I9kModal

> Accessible modal dialog built on the native <dialog> element, labelled by its title, with a close button, a scrolling body, and an optional footer. The parent owns whether it is open.

```ts
import { I9kModal } from '@9klabs/design';
```

## Examples

### Edit form

```vue
<I9kButton @click="open = true">Edit module</I9kButton>
<I9kModal v-model:open="open" title="Edit module" description="Both languages are required.">
  <p>Module fields go here.</p>
  <template #footer>
    <I9kButton @click="open = false">Cancel</I9kButton>
    <I9kButton variant="primary" @click="open = false">Save</I9kButton>
  </template>
</I9kModal>
```

## Watch out

- The parent owns `open`: the close button and Escape only emit `update:open(false)`, so bind `v-model:open` or the modal never closes.
- Backdrop clicks never close the modal.
- Set `dismissible` to false while saving; Escape is then ignored and the close button is disabled.
- The body and footer render only while open, so a form inside remounts on every opening; put `autofocus` on its first field.
- The footer renders outside the body, so a submit button there needs `form="<id>"` pointing at the form in the body.
- The `after` slot sits on the backdrop, not on the card: keep it to a short link, which reads on the dimmed page.

## API

### Props

| Prop | Type | Default | Required |
| --- | --- | --- | --- |
| `open` | `boolean` | `false` | no |
| `title` | `string` | — | yes |
| `description` | `string` | `undefined` | no |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | no |
| `closeLabel` | `string` | `'Close'` | no |
| `dismissible` | `boolean` | `true` | no |

### Emits

| Event | Payload |
| --- | --- |
| `update:open` | `[open: boolean]` |
| `close` | `[]` |

### Slots

- `default`
- `footer`
- `after`

## Agent prompt

```text
Use I9kModal from @9klabs/design for a focused task that must interrupt the page, such as an edit form.

import { I9kModal } from '@9klabs/design';

Props:
- open?: boolean (default false) — bind with v-model:open. The modal opens with showModal() when it becomes true and closes when it becomes false.
- title: string (required) — rendered as the <h2> that labels the dialog (aria-labelledby).
- description?: string — rendered under the title and wired to aria-describedby.
- size?: 'sm' | 'md' | 'lg' (default 'md') — the maximum width of the dialog.
- closeLabel?: string (default 'Close') — the accessible name of the icon-only close button; translate it.
- dismissible?: boolean (default true) — when false, Escape is ignored and the close button is disabled.

Emits:
- update:open(false) — the user asked to close (close button or Escape).
- close() — emitted together with update:open(false).

Slots:
- default — the dialog body; it scrolls when taller than the viewport.
- footer — actions such as Cancel and Save, aligned to the end.
- after — under the dialog's card, on the backdrop, centred: a way out of the dialog's task, such as "Forgot your password?" under a sign-in form. It is inside the <dialog>, so it is reachable by keyboard while the modal is open.

Behavior: the parent owns `open`. The close button and Escape only request a close by emitting update:open(false); the modal closes when the parent sets `open` to false, and focus returns to the element that was focused when it opened.

IMPORTANT: clicking the backdrop never closes the modal, so an unsaved form is not lost to a stray click.

IMPORTANT: set `dismissible` to false while a save is in flight so the user cannot close the modal halfway through.

IMPORTANT: the body and footer render only while the modal is open, so a form inside remounts on every opening and starts from fresh state. Put `autofocus` on the first field so focus lands there.

Usage:
<I9kButton @click="open = true">Edit module</I9kButton>
<I9kModal v-model:open="open" title="Edit module" description="Both languages are required." close-label="Close">
  <form id="edit-module-form" @submit.prevent="save">
    <I9kInput v-model="title" label="Title" autofocus />
  </form>
  <template #footer>
    <I9kButton @click="open = false">Cancel</I9kButton>
    <I9kButton variant="primary" type="submit" form="edit-module-form">Save</I9kButton>
  </template>
</I9kModal>
```
