Build a production-ready Modal component with React Portal, full accessibility support, and focus trap.
1. React Portal
- Render modal outside DOM hierarchy (at document.body level)
- Prevent z-index conflicts
- Isolate modal from parent component styles
2. Accessibility (ARIA)
- Proper ARIA attributes (role="dialog", aria-modal, aria-labelledby, aria-describedby)
- Screen reader announcements
- Keyboard navigation support
- Focus management (trap focus within modal)
3. Focus Trap
- Trap focus within modal when open
- Return focus to trigger element when closed
- Handle Tab/Shift+Tab navigation
- Skip to first/last focusable element at boundaries
4. Advanced Features
- Support for reduced motion preferences
- Proper semantic HTML structure
- Announce modal title/description to screen readers
- Handle focus restoration on unmount
5. Data Structure
interface ModalProps {
isOpen: boolean;
onClose: () => void;
title: string;
description?: string;
children: React.ReactNode;
initialFocusRef?: React.RefObject<HTMLElement>;
finalFocusRef?: React.RefObject<HTMLElement>;
size?: "sm" | "md" | "lg" | "fullscreen";
}
Examples
const buttonRef = useRef<HTMLButtonElement>(null);
<button ref={buttonRef} onClick={() => setIsOpen(true)}>
Open Modal
</button>
<Modal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
title="Accessible Modal"
description="This modal has full accessibility support"
initialFocusRef={buttonRef}
>
<p>Modal content here</p>
</Modal>