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
Prop | Type | Default | Description |
---|---|---|---|
isOpen | boolean | - | Whether the modal is currently open |
onClose | () => void | - | Callback function called when the modal should close |
children | React.ReactNode | - | The content of the modal |
size | 'sm' | 'md' | 'lg' | 'xl' | 'full' | 'md' | The size of the modal |
closeOnEsc | boolean | true | Whether the modal should close when the escape key is pressed |
closeOnOverlayClick | boolean | true | Whether the modal should close when clicking the overlay |
centered | boolean | true | Whether the modal is centered vertically |
hasCloseButton | boolean | true | Whether the modal has a close button in the top right corner |
scrollable | boolean | true | Whether to show a scrollbar when content overflows |
backdrop | boolean | 'static' | true | Whether the modal should have a backdrop |
animationDuration | number | 300 | Animation duration in milliseconds |
ModalHeader Props
Prop | Type | Default | Description |
---|---|---|---|
children | React.ReactNode | - | The content of the modal header |
onClose | () => void | - | Callback function called when the close button is clicked |
hasCloseButton | boolean | true | Whether the modal header has a close button |
ModalBody Props
Prop | Type | Default | Description |
---|---|---|---|
children | React.ReactNode | - | The content of the modal body |
ModalFooter Props
Prop | Type | Default | Description |
---|---|---|---|
children | React.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 dialogaria-modal="true"
- Indicates the dialog is modalaria-labelledby
- References the modal titlearia-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