React Aria предоставляет набор хуков и утилит для реализации доступных и управляемых модальных интерфейсов. Центральная идея заключается в разделении логики управления состоянием и рендеринга компонентов, что позволяет создавать как простые, так и сложные модальные окна, соответствующие стандартам доступности (WCAG).
useModal и useOverlayДля создания модального окна используется хук useModal,
который устанавливает необходимые ARIA-атрибуты и фокусную ловушку
(focus trap). Он гарантирует, что при открытом модальном окне фокус
клавиатуры не выйдет за пределы окна.
import { useModal } from '@react-aria/overlays';
import { useRef } from 'react';
function Modal({ children }) {
let ref = useRef();
let { modalProps } = useModal();
return (
<div {...modalProps} ref={ref}>
{children}
</div>
);
}
Для управления визуальной частью и позиционированием применяется
useOverlay. Он обеспечивает корректное закрытие модального
окна при клике вне области, нажатии клавиши Escape и совместим с
анимацией появления/исчезновения.
import { useOverlay, usePreventScroll } from '@react-aria/overlays';
function Overlay({ isOpen, onClose, children }) {
let ref = useRef();
usePreventScroll(); // предотвращает прокрутку фона
let { overlayProps } = useOverlay({ isOpen, onClose, isDismissable: true }, ref);
return (
<div {...overlayProps} ref={ref}>
{children}
</div>
);
}
useOverlayTriggerStateДля более сложных сценариев, где требуется открывать/закрывать
несколько окон или управлять их вложенностью, используется
useOverlayTriggerState. Он обеспечивает централизованное
управление состоянием модального окна.
import { useOverlayTriggerState } from '@react-stately/overlays';
function App() {
let state = useOverlayTriggerState({});
return (
<>
<button onCl ick={state.open}>Открыть модальное окно</button>
{state.isOpen && <Modal onCl ose={state.close}>Содержимое окна</Modal>}
</>
);
}
Ключевое преимущество — синхронизация состояния UI с фокусом и закрытием через клавиши или клики вне окна.
React Aria автоматически применяет focus trap,
который блокирует перемещение фокуса за пределы активного модального
окна. Это достигается через комбинацию useModal и
useOverlay, но при необходимости можно управлять фокусом
вручную с помощью useFocusRing и useFocus.
import { useFocusRing } from '@react-aria/focus';
function FocusableButton(props) {
let { isFocusVisible, focusProps } = useFocusRing();
return (
<button {...props} {...focusProps} style={{ outline: isFocusVisible ? '2px solid blue' : 'none' }}>
{props.children}
</button>
);
}
Для создания стека модальных окон важно учитывать правильное управление порядком фокуса и слойностью (z-index). React Aria автоматически обеспечивает правильное распределение фокуса через внутренний стек оверлеев.
function NestedModals() {
let parentState = useOverlayTriggerState({});
let childState = useOverlayTriggerState({});
return (
<>
<button onCl ick={parentState.open}>Открыть родительское окно</button>
{parentState.isOpen && (
<Modal onCl ose={parentState.close}>
Родительское окно
<button onCl ick={childState.open}>Открыть дочернее окно</button>
{childState.isOpen && <Modal onCl ose={childState.close}>Дочернее окно</Modal>}
</Modal>
)}
</>
);
}
Важно, что каждый уровень модального окна получает свои ARIA-атрибуты, что сохраняет корректную доступность для скринридеров.
React Aria предоставляет хук usePreventScroll, который
блокирует прокрутку контента за модальным окном. Этот подход особенно
важен для мобильных устройств и сложных интерфейсов с длинными
списками.
import { usePreventScroll } from '@react-aria/overlays';
function ScrollBlockingModal({ children }) {
usePreventScroll();
return <div>{children}</div>;
}
Хук useOverlay совместим с библиотеками анимации, такими
как Framer Motion. Для плавного появления/исчезновения модального окна
достаточно использовать состояние isOpen и контролировать
рендер с анимацией.
import { motion, AnimatePresence } from 'framer-motion';
function AnimatedModal({ isOpen, onClose, children }) {
return (
<AnimatePresence>
{isOpen && (
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}>
<Modal onCl ose={onClose}>{children}</Modal>
</motion.div>
)}
</AnimatePresence>
);
}
useModal и useOverlay
вместе для обеспечения доступности.useOverlayTriggerState.usePreventScroll.useFocusRing.React Aria превращает управление модальными окнами в структурированную и доступную систему, где отдельные хуки отвечают за состояние, фокус, доступность и визуализацию. Такой подход позволяет строить как простые модальные диалоги, так и сложные интерфейсы со стеком окон, полностью соответствующие стандартам WCAG.