Modal

Organism

Focus-trapped dialog. Closes on Escape and backdrop click. Requires role="dialog" + aria-modal + aria-labelledby; supports sm/md/lg sizes. Uses the shared Overlays focus-trap, scroll-lock and presence hooks; nested modals are layer-aware so Escape only dismisses the topmost panel.

Confirmation dialog

Preview
Code
const [open, setOpen] = useState(false);
<Button variant="primary" onClick={() => setOpen(true)}>Open Modal</Button>
<Modal open={open} onClose={() => setOpen(false)} title="Confirm action"
  description="Are you sure you want to proceed?"
  footer={<><Button variant="outline">Cancel</Button><Button variant="danger">Delete</Button></>}>
  <p>This will permanently delete all selected items.</p>
</Modal>

Sizes (sm / md / lg)

Preview
Code
<Modal open={open} onClose={onClose} title="Small" size="sm">...</Modal>
<Modal open={open} onClose={onClose} title="Medium" size="md">...</Modal>
<Modal open={open} onClose={onClose} title="Large" size="lg">...</Modal>

Scrollable body

Preview
Code
<Modal open={open} onClose={onClose} title="Long Content" scrollable
  footer={<Button>OK</Button>}>
  {/* long content scrolls inside */}
</Modal>

Fullscreen

Preview
Code
<Modal open={open} onClose={onClose} title="Fullscreen Dialog" fullscreen>...</Modal>

Nested modals (layer-aware Escape)

Preview
Code
// Opens a second modal on top — Escape only dismisses the inner one.
const [outer, setOuter] = useState(false);
const [inner, setInner] = useState(false);
<Modal open={outer} onClose={() => setOuter(false)} title="Outer">
  <Button onClick={() => setInner(true)}>Open Nested</Button>
</Modal>
<Modal open={inner} onClose={() => setInner(false)} title="Nested" size="sm">
  ...
</Modal>
Sourcemodules/ui/Overlays/Modal/index.tsx