# Modal

Modal is a neobrutalist React component from Cruise UI. A dialog with a --primary title bar, for confirmations and short forms.

Install (with the @cruiseui registry in components.json, see Installation):

```bash
npx shadcn@latest add @cruiseui/modal
```

Import: `import { CruiseModal } from "@/components/cruise/modal";`

## When to use

Confirming something (delete, cancel order) or a short focused task.

## When not to

Long content or navigation (use a page or a Drawer). Never use alert() or confirm().

## Example (React)

```tsx
"use client";

import { useState } from "react";
import { CruiseButton } from "@/components/cruise/button";
import { CruiseModal } from "@/components/cruise/modal";

export default function ModalExample() {
  const [open, setOpen] = useState(false);
  return (
    <CruiseModal
      open={open}
      onOpenChange={setOpen}
      title="Cancel this order?"
      trigger={<CruiseButton variant="secondary">Cancel order</CruiseButton>}
      footer={
        <>
          <CruiseButton variant="secondary" onClick={() => setOpen(false)}>
            Keep order
          </CruiseButton>
          <CruiseButton variant="danger" onClick={() => setOpen(false)}>
            Cancel order
          </CruiseButton>
        </>
      }
    >
      Your rider hasn&apos;t left yet, so you won&apos;t be charged.
    </CruiseModal>
  );
}
```

## HTML

Needs theme.css (https://cruiseui.dev/theme.css).

```html
<div class="drawer-backdrop"></div>
<div role="dialog" aria-modal="true" aria-labelledby="m-title" class="brutal" style="position: fixed; top: 50%; left: 50%; z-index: 61; width: min(90vw, 28rem); transform: translate(-50%, -50%); box-shadow: var(--shadow-lg); overflow: hidden">
  <div class="on-primary" style="padding: 12px 20px; border-bottom: var(--border-w) solid var(--ink)">
    <h2 id="m-title" style="margin: 0">Cancel this order?</h2>
  </div>
  <div style="padding: 20px">
    <p>Your rider hasn't left yet, so you won't be charged.</p>
    <button type="button" class="brutal btn btn-secondary">Keep order</button>
    <button type="button" class="brutal btn on-primary">Cancel order</button>
  </div>
</div>
```

## Props: CruiseModal

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` | `ReactNode` | — | — |
| `children` | `ReactNode` | — | — |
| `trigger?` | `ReactElement<unknown, string \| JSXElementConstructor<any>>` | — | Element that opens the modal, e.g. <Button>. Omit when controlled. |
| `footer?` | `ReactNode` | — | Buttons along the bottom. |
| `open?` | `boolean` | — | — |
| `onOpenChange?` | `((open: boolean) => void)` | — | — |
| `initialFocus?` | `RefObject<HTMLElement \| null>` | — | Element to focus when the modal opens; defaults to the first focusable. |

## Accessibility

- Focus moves into the dialog and back to the trigger on close.
- Esc and the close button both close it.
