useModal для модальных окон

Хук useModal предназначен для управления модальными окнами с соблюдением стандартов доступности (accessibility, a11y). Он обеспечивает правильное взаимодействие с фокусом, экранными ридерами и навигацией с клавиатуры. Основная задача — создать область интерфейса, которая блокирует фокус вне модального окна и корректно оповещает технологии доступности о появлении нового интерактивного контента.


Импорт и базовое использование

import { useModal } from '@react-aria/overlays';
import { useOverlay } from '@react-aria/overlays';
import { useDialog } from '@react-aria/dialog';

useModal часто применяется совместно с useOverlay и useDialog. Основная комбинация выглядит так:

function Modal({ isOpen, onClose, children }) {
  let ref = useRef();
  let { modalProps } = useModal();
  let { overlayProps } = useOverlay({ isOpen, onClose, isDismissable: true }, ref);
  let { dialogProps } = useDialog({}, ref);

  if (!isOpen) return null;

  return (
    
{children}
); }

Здесь ключевые моменты:

  • modalProps — добавляет атрибуты для блокировки фокуса вне модального окна.
  • overlayProps — управляет показом и скрытием окна, поддерживает закрытие по клику вне модального контента.
  • dialogProps — включает правильные ARIA-атрибуты (role="dialog", aria-modal="true") для экранных ридеров.

Управление фокусом

Главная задача useModal — удерживать фокус внутри модального окна. Это реализуется через:

  • Фокусировку первого интерактивного элемента при открытии.
  • Циклическую навигацию клавишей Tab.
  • Возврат фокуса к элементу, который открыл модалку, при закрытии.

Пример:

import { FocusScope } from '@react-aria/focus';

function ModalContent({ children }) {
  return (
    
      {children}
    
  );
}
  • contain — блокирует фокус внутри модального окна.
  • restoreFocus — возвращает фокус к исходному элементу при закрытии.
  • autoFocus — автоматически фокусирует первый интерактивный элемент.

Закрытие модального окна

useOverlay предоставляет механизмы для:

  • Закрытия по клику вне окна (isDismissable: true).
  • Закрытия по клавише Escape (shouldCloseOnEsc по умолчанию true).
let { overlayProps } = useOverlay({ isOpen, onClose, isDismissable: true }, ref);

В сочетании с useModal это обеспечивает полностью управляемый интерактивный опыт, соответствующий стандартам WCAG.


Работа с анимацией

Для плавного появления и исчезновения модальных окон можно комбинировать useModal с CSS-анимациями или библиотекой react-transition-group. Важно не прерывать фокусировку при анимации, чтобы пользовательский опыт оставался корректным:

import { CSSTransition } from 'react-transition-group';


  
    
      ...
    
  

Поддержка вложенных модальных окон

useModal корректно работает с вложенными оверлеями. Каждый новый модальный уровень:

  • Изолирует фокус внутри текущего окна.
  • Не мешает предыдущим модалкам, если они остаются открытыми.
  • Управляет правильным стеком aria-hidden, скрывая элементы вне текущей модалки от экранных ридеров.

Практические советы

  1. Всегда использовать FocusScope для управления фокусом.
  2. Использовать aria-labelledby и aria-describedby для информирования пользователя о содержимом окна.
  3. Закрывать модалку через onClose, а не через манипуляцию видимости. Это позволяет корректно восстановить фокус и уведомить экранные ридеры.
  4. Стараться избегать множественных интерактивных модальных окон одновременно, чтобы не усложнять управление фокусом.

Итоговая структура модального окна с useModal

function AppModal({ isOpen, onClose, title, children }) {
  let ref = useRef();
  let { modalProps } = useModal();
  let { overlayProps } = useOverlay({ isOpen, onClose, isDismissable: true }, ref);
  let { dialogProps } = useDialog({ 'aria-label': title }, ref);

  if (!isOpen) return null;

  return (
    

{title}

{children}
); }

Такое сочетание обеспечивает полностью доступное и управляемое модальное окно, соответствующее стандартам ARIA и лучшим практикам React.