Arcana UI
Components

Modal

Accessible dialog overlay with focus trap, keyboard dismissal, and configurable sizes.

Import

import { Modal } from '@arcana-ui/core'

Usage

const [open, setOpen] = useState(false)
 
<Button onClick={() => setOpen(true)}>Open modal</Button>
 
<Modal
  open={open}
  onClose={() => setOpen(false)}
  title="Confirm delete"
  description="This action cannot be undone."
  footer={
    <>
      <Button variant="secondary" onClick={() => setOpen(false)}>Cancel</Button>
      <Button variant="danger">Delete</Button>
    </>
  }
>
  <p>Are you sure you want to delete this item?</p>
</Modal>

Props

PropTypeDefaultDescription
openbooleanWhether the modal is visible
onClose() => voidCalled when user dismisses the modal
titlestringDialog title (sets aria-labelledby)
descriptionstringSubtitle below title
size'sm' | 'md' | 'lg' | 'xl' | 'full''md'Dialog width
closeOnOverlayClickbooleantrueClose when clicking backdrop
closeOnEscbooleantrueClose on Escape key
footerReact.ReactNodeFooter content (buttons)
childrenReact.ReactNodeDialog body
classNamestringAdditional CSS classes on dialog

Sizes

SizeMax width
sm400px
md560px
lg720px
xl960px
full100% screen

No overlay close

<Modal
  open={open}
  onClose={() => setOpen(false)}
  closeOnOverlayClick={false}
  closeOnEsc={false}
  title="Required step"
>
  <p>You must complete this before continuing.</p>
</Modal>

Without title

When no title is provided, a close button still appears in the top-right corner. Add aria-label to describe the dialog purpose:

<Modal open={open} onClose={() => setOpen(false)}>
  <img src="/preview.png" alt="Preview" />
</Modal>

Accessibility

  • role="dialog" and aria-modal="true" on the dialog element
  • aria-labelledby points to the title element when title is provided
  • aria-describedby points to the description when provided
  • Focus trap: Tab/Shift+Tab cycle only through focusable elements inside the dialog
  • Focus returns to the triggering element when modal closes
  • Escape closes the modal (configurable)
  • Prevents body scroll when open
  • Card — non-overlay content containers
  • Alert — inline status messages
  • Toast — transient notifications

On this page