Focus management

Radix UI предоставляет мощный и гибкий механизм управления фокусом в интерфейсах на основе React. Контроль фокуса критически важен для доступности (accessibility) и пользовательского опыта, особенно при работе с компонентами, которые открываются динамически, такими как модальные окна, всплывающие панели и меню.


Принципы работы фокуса

Фокус определяет, какой элемент интерфейса активен и готов к взаимодействию с клавиатурой. Radix UI управляет фокусом, исходя из следующих принципов:

  1. Autofocus при открытии компонентов – компоненты Radix UI автоматически устанавливают фокус на первый интерактивный элемент внутри при открытии (например, первый элемент в меню или поле ввода в модальном окне).
  2. Trapping focus – при открытом модальном окне или диалоговом компоненте фокус ограничивается его пределами, предотвращая случайное перемещение пользователя за пределы модального контекста.
  3. Return focus – при закрытии компонента фокус возвращается к элементу, который его инициировал, обеспечивая предсказуемое поведение интерфейса.

Эти механизмы особенно важны для пользователей, которые полагаются на клавиатуру и вспомогательные технологии.


Управление фокусом в диалогах и модальных окнах

Radix UI предоставляет компонент Dialog с встроенной поддержкой управления фокусом. Основные возможности:

import * as Dialog from '@radix-ui/react-dialog';

<Dialog.Root>
  <Dialog.Trigger>Открыть модальное окно</Dialog.Trigger>
  <Dialog.Content>
    <Dialog.Title>Заголовок</Dialog.Title>
    <Dialog.Description>Описание контента</Dialog.Description>
    <input type="text" placeholder="Ваше имя" />
    <Dialog.Close>Закрыть</Dialog.Close>
  </Dialog.Content>
</Dialog.Root>
  • Автоматическая установка фокуса: при открытии Dialog.Content фокус автоматически перемещается на первый интерактивный элемент, в данном примере – <input>.
  • Фокус-трап: клавиша Tab ограничена элементами внутри диалога, циклично перемещаясь между ними.
  • Возврат фокуса: после закрытия диалога фокус возвращается на Dialog.Trigger.

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

Компоненты Menu и DropdownMenu также имеют встроенное управление фокусом:

import * as Menu from '@radix-ui/react-menu';

<Menu.Root>
  <Menu.Trigger>Открыть меню</Menu.Trigger>
  <Menu.Content>
    <Menu.Item>Пункт 1</Menu.Item>
    <Menu.Item>Пункт 2</Menu.Item>
    <Menu.Item>Пункт 3</Menu.Item>
  </Menu.Content>
</Menu.Root>
  • Навигация с клавиатуры: стрелки вверх/вниз перемещают фокус между пунктами меню.
  • Автофокус первого элемента: при открытии меню Radix UI автоматически фокусирует первый доступный пункт.
  • Поддержка disabled элементов: элементы с disabled не получают фокус, пропускаясь при навигации стрелками.

Фокусировка по умолчанию и кастомизация

Radix UI позволяет переопределять стандартное поведение фокуса:

  • autofocus – атрибут компонентов FocusScope или Dialog.Content позволяет указать, какой элемент получать фокус при открытии.
  • disableFocusEscape – отключает возможность выхода фокуса за пределы компонента.
  • contain – включение режима “trap focus” для вложенных элементов.

Пример использования FocusScope:

import { FocusScope } from '@radix-ui/react-focus-scope';

<FocusScope contain restoreFocus autoFocus>
  <button>Элемент 1</button>
  <button>Элемент 2</button>
</FocusScope>
  • contain – фокус не может покинуть область FocusScope.
  • restoreFocus – при размонтировании область возвращает фокус на предыдущий активный элемент.
  • autoFocus – первый интерактивный элемент внутри автоматически получает фокус.

Интеграция с кастомными элементами

Если компонент использует нестандартные HTML-элементы, можно явно указать, какой элемент будет получать фокус:

<FocusScope autoFocus>
  <div tabIndex={0}>Фокусируемый div</div>
</FocusScope>
  • Элемент с tabIndex={0} становится интерактивным и доступным для фокусировки.
  • Radix UI гарантирует, что при Tab/Shift+Tab фокус будет перемещаться корректно внутри FocusScope.

Доступность и клавиатурная навигация

Radix UI обеспечивает соответствие стандартам WAI-ARIA:

  • aria-modal – при открытом модальном окне добавляется атрибут aria-modal="true".
  • aria-disabled – disabled элементы корректно игнорируются при навигации.
  • role и aria-labelledby – компоненты Dialog, Menu и другие используют правильные роли и связи для скринридеров.

Особенности работы с Nested FocusScopes

При вложенных областях фокуса (FocusScope внутри FocusScope) Radix UI управляет контекстами:

  • Вложенный FocusScope получает приоритет при Tab навигации.
  • При выходе из вложенного фокус возвращается на родительский уровень.
  • restoreFocus работает корректно для каждого уровня вложенности.
<FocusScope restoreFocus>
  <button>Главный элемент</button>
  <FocusScope contain restoreFocus>
    <button>Вложенный элемент 1</button>
    <button>Вложенный элемент 2</button>
  </FocusScope>
</FocusScope>

Практические рекомендации

  1. Использовать FocusScope вокруг модальных окон, диалогов и кастомных интерактивных областей.
  2. Всегда включать restoreFocus для обеспечения предсказуемого поведения интерфейса.
  3. Для списков и меню полагаться на встроенные компоненты Radix UI, чтобы минимизировать ручное управление фокусом.
  4. При использовании кастомных элементов не забывать про tabIndex и aria-* атрибуты для совместимости с ассистивными технологиями.

Эффективное управление фокусом с Radix UI позволяет создавать интерфейсы, которые безопасно и удобно использовать как с мышью, так и с клавиатурой, обеспечивая доступность и согласованность поведения на всех уровнях приложения.