Modal

Overlay dialogs for focused interactions, forms, and important information display.

Basic Usage

import { useState } from 'react'
import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from '@akitectio/aki-ui'

function BasicModal() {
  const [isOpen, setIsOpen] = useState(false)

  return (
    <>
      <Button onClick={() => setIsOpen(true)}>
        Open Modal
      </Button>
      
      <Modal isOpen={isOpen} onClose={() => setIsOpen(false)}>
        <ModalHeader onClose={() => setIsOpen(false)}>
          Modal Title
        </ModalHeader>
        <ModalBody>
          <p>This is the modal content.</p>
        </ModalBody>
        <ModalFooter>
          <Button variant="secondary" onClick={() => setIsOpen(false)}>
            Cancel
          </Button>
          <Button onClick={() => setIsOpen(false)}>
            Save
          </Button>
        </ModalFooter>
      </Modal>
    </>
  )
}

Modal Sizes

<div className="flex gap-2">
  <Button onClick={() => setModalOpen('sm')}>Small</Button>
  <Button onClick={() => setModalOpen('md')}>Medium</Button>
  <Button onClick={() => setModalOpen('lg')}>Large</Button>
  <Button onClick={() => setModalOpen('xl')}>Extra Large</Button>
  <Button onClick={() => setModalOpen('full')}>Full Screen</Button>
</div>

<Modal
  isOpen={modalSize === 'lg'}
  onClose={() => setModalSize('')}
  size="lg"
>
  <ModalHeader>Large Modal</ModalHeader>
  <ModalBody>Content for large modal...</ModalBody>
  <ModalFooter>
    <Button onClick={() => setModalSize('')}>Close</Button>
  </ModalFooter>
</Modal>

Centered Modal

<Modal
  isOpen={isOpen}
  onClose={() => setIsOpen(false)}
  centered={true}
>
  <ModalHeader>Centered Modal</ModalHeader>
  <ModalBody>
    This modal is vertically centered on the screen.
  </ModalBody>
  <ModalFooter>
    <Button onClick={() => setIsOpen(false)}>Close</Button>
  </ModalFooter>
</Modal>

Scrollable Modal

<Modal
  isOpen={isOpen}
  onClose={() => setIsOpen(false)}
  scrollable={true}
>
  <ModalHeader>Scrollable Modal</ModalHeader>
  <ModalBody>
    {/* Long content that needs scrolling */}
    <div>Lots of content here...</div>
  </ModalBody>
  <ModalFooter>
    <Button onClick={() => setIsOpen(false)}>Close</Button>
  </ModalFooter>
</Modal>

Custom Modal

<Modal
  isOpen={isOpen}
  onClose={() => setIsOpen(false)}
  hasCloseButton={false}
  closeOnOverlayClick={false}
  className="max-w-md"
>
  <div className="p-6 text-center">
    <div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-green-100 mb-4">
      <span className="text-green-600 text-xl"></span>
    </div>
    <h3 className="text-lg font-semibold mb-2">Success!</h3>
    <p className="text-gray-600 mb-6">
      Your account has been created successfully.
    </p>
    <div className="flex justify-center gap-3">
      <Button variant="secondary" onClick={() => setIsOpen(false)}>
        Close
      </Button>
      <Button onClick={() => setIsOpen(false)}>
        Get Started
      </Button>
    </div>
  </div>
</Modal>

Confirmation Modal

<Button variant="danger" onClick={() => setConfirmModalOpen(true)}>
  Delete Account
</Button>

<Modal
  isOpen={confirmModalOpen}
  onClose={() => setConfirmModalOpen(false)}
  size="sm"
  centered
>
  <ModalHeader>Confirm Deletion</ModalHeader>
  <ModalBody>
    <div className="flex items-start gap-3">
      <span className="text-red-500 text-xl">⚠️</span>
      <div>
        <p className="font-medium mb-2">
          Are you sure you want to delete your account?
        </p>
        <p className="text-gray-600 text-sm">
          This action cannot be undone.
        </p>
      </div>
    </div>
  </ModalBody>
  <ModalFooter>
    <Button variant="secondary" onClick={() => setConfirmModalOpen(false)}>
      Cancel
    </Button>
    <Button variant="danger" onClick={() => setConfirmModalOpen(false)}>
      Delete Account
    </Button>
  </ModalFooter>
</Modal>

API Reference

Modal Props

PropTypeDefaultDescription
isOpenboolean-Whether the modal is currently open
onClose() => void-Callback function called when the modal should close
childrenReact.ReactNode-The content of the modal
size'sm' | 'md' | 'lg' | 'xl' | 'full''md'The size of the modal
closeOnEscbooleantrueWhether the modal should close when the escape key is pressed
closeOnOverlayClickbooleantrueWhether the modal should close when clicking the overlay
centeredbooleantrueWhether the modal is centered vertically
hasCloseButtonbooleantrueWhether the modal has a close button in the top right corner
scrollablebooleantrueWhether to show a scrollbar when content overflows
backdropboolean | 'static'trueWhether the modal should have a backdrop
animationDurationnumber300Animation duration in milliseconds

ModalHeader Props

PropTypeDefaultDescription
childrenReact.ReactNode-The content of the modal header
onClose() => void-Callback function called when the close button is clicked
hasCloseButtonbooleantrueWhether the modal header has a close button

ModalBody Props

PropTypeDefaultDescription
childrenReact.ReactNode-The content of the modal body

ModalFooter Props

PropTypeDefaultDescription
childrenReact.ReactNode-The content of the modal footer

Accessibility

  • ✅ Focus trap within modal when open
  • ✅ Focus returns to trigger element when closed
  • ✅ Screen reader compatible with proper ARIA attributes
  • ✅ Keyboard navigation support
  • ✅ High contrast mode support
  • ✅ Portal rendering for proper stacking

Keyboard Navigation

  • Escape - Close modal (if closeOnEsc is true)
  • Tab - Navigate forward through focusable elements
  • Shift + Tab - Navigate backward through focusable elements

ARIA Attributes

  • role="dialog" - Identifies the modal as a dialog
  • aria-modal="true" - Indicates the dialog is modal
  • aria-labelledby - References the modal title
  • aria-describedby - References the modal description

Best Practices

✅ Do

  • Use modals for focused tasks that require user attention
  • Keep modal content concise and actionable
  • Provide clear close mechanisms (X button, Cancel, etc.)
  • Use appropriate sizes based on content complexity
  • Handle focus management properly
  • Use confirmation modals for destructive actions
  • Test with keyboard navigation and screen readers

❌ Don't

  • Use modals for simple notifications (use alerts or toasts instead)
  • Stack multiple modals on top of each other
  • Make modals too large or too small for their content
  • Use modals for primary navigation
  • Disable close mechanisms without good reason
  • Put forms with many fields in small modals
  • Use modals on mobile for complex workflows

Common Patterns

🎯 Confirmation Dialog

For confirming destructive actions like deletions

  • Small size, centered
  • Clear warning message
  • Two actions: Cancel and Confirm

📝 Form Modal

For data entry and editing

  • Medium to large size
  • Scrollable if content is long
  • Save and Cancel actions

ℹ️ Information Modal

For displaying detailed information

  • Size based on content
  • Single close action
  • Scrollable for long content

🎉 Success Modal

For celebrating completed actions

  • Small to medium size
  • Centered with success indicators
  • Next step or close actions