Управление фокусом является ключевым аспектом доступности модальных окон. Библиотека React Aria предоставляет инструменты для обеспечения правильного поведения фокуса, предотвращения “потери” пользователя внутри интерфейса и соблюдения стандартов WCAG.
useModalВ React Aria управление модальными окнами начинается с хука
useModal. Этот хук позволяет обозначить область как
модальную и автоматически блокирует взаимодействие с элементами вне
модального окна.
import { useModal } from '@react-aria/overlays';
function ModalContainer(props) {
let ref = React.useRef();
let { modalProps } = useModal();
return (
<div {...modalProps} ref={ref}>
{props.children}
</div>
);
}
Ключевые моменты:
useModal автоматически управляет атрибутами
aria-hidden для элементов вне модального окна.FocusScopeХук useFocusScope или компонент
<FocusScope> отвечает за изолирование фокуса
внутри модального окна. Это предотвращает уход фокуса на
элементы, находящиеся под модальным окном.
import { FocusScope } from '@react-aria/focus';
function ModalContent({ children }) {
return (
<FocusScope contain restoreFocus autoFocus>
{children}
</FocusScope>
);
}
Опции FocusScope:
contain: удерживает фокус внутри области.restoreFocus: возвращает фокус на элемент, который был
активен до открытия модального окна.autoFocus: автоматически устанавливает фокус на первый
фокусируемый элемент при монтировании.React Aria предоставляет механизмы для автоматической фокусировки первого интерактивного элемента. Это часто требуется для кнопок закрытия или полей формы внутри модального окна.
import { useFocus } from '@react-aria/focus';
function CloseButton() {
let ref = React.useRef();
let { focusProps } = useFocus({ autoFocus: true });
return <button {...focusProps} ref={ref}>Закрыть</button>;
}
Параметр autoFocus гарантирует, что элемент станет
активным сразу после открытия модального окна, что повышает удобство
клавиатурного управления.
Правильное управление фокусом в модальных компонентах невозможно без
корректной обработки клавиш Tab и Shift+Tab.
Компонент <FocusScope> автоматически
циклически перемещает фокус, предотвращая его выход за
пределы модального окна.
<FocusScope contain restoreFocus autoFocus>
<input type="text" placeholder="Имя" />
<button>Сохранить</button>
</FocusScope>
Tab фокус переходит на следующий
фокусируемый элемент.Tab,
фокус возвращается на первый элемент.Shift+Tab также корректно
циклична.Модальные окна чаще всего реализуются через оверлеи. React Aria
предоставляет OverlayProvider и хук useOverlay
для безопасного управления фокусом при наложении:
import { OverlayProvider, useOverlay } from '@react-aria/overlays';
function MyModal({ isOpen, onClose, children }) {
let ref = React.useRef();
let { overlayProps } = useOverlay({ onClose, isOpen, isDismissable: true }, ref);
if (!isOpen) return null;
return (
<div {...overlayProps} ref={ref}>
{children}
</div>
);
}
// Оборачиваем приложение в OverlayProvider
<OverlayProvider>
<App />
</OverlayProvider>
Особенности:
isDismissable позволяет закрывать модальное окно через
клавишу Escape или клики вне окна.useOverlay автоматически работает совместно с
FocusScope, обеспечивая безопасный контроль фокуса при
открытии и закрытии оверлея.Очень важный аспект доступности — возврат фокуса на элемент, который
открыл модальное окно. Опция restoreFocus в
FocusScope автоматически сохраняет и восстанавливает
предыдущий фокус:
<FocusScope contain restoreFocus autoFocus>
<ModalContent />
</FocusScope>
Без этой функции пользователь после закрытия модального окна не потеряется и сможет продолжить взаимодействие с интерфейсом с того места, где был до открытия модала.
<FocusScope contain restoreFocus autoFocus> внутри
модального окна.display: none или visibility: hidden —
использовать встроенные возможности React Aria.FocusScope с useFocus на интерактивных
элементах, чтобы гарантировать правильное поведение фокуса.OverlayProvider, если
планируется работа с несколькими оверлеями или модальными окнами
одновременно.Эти подходы позволяют создавать модальные компоненты с полностью управляемым фокусом, соответствующие стандартам доступности, без лишней ручной логики и с полной интеграцией с клавиатурной навигацией.