Фокус и захват клавиатуры

Основы управления фокусом

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

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

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

function FocusableButton() {
  const { isFocused, focusProps } = useFocusRing();

  return (
    <button {...focusProps} className={isFocused ? 'focused' : ''}>
      Нажми меня
    </button>
  );
}

Здесь useFocusRing обеспечивает отслеживание состояния фокуса и автоматически добавляет нужные атрибуты ARIA для доступности.


Захват клавиатуры (Keyboard trapping)

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

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

function Modal() {
  return (
    <Dialog.Root>
      <Dialog.Trigger>Открыть модалку</Dialog.Trigger>
      <Dialog.Portal>
        <Dialog.Overlay />
        <Dialog.Content>
          <Dialog.Title>Заголовок</Dialog.Title>
          <Dialog.Description>Описание модалки</Dialog.Description>
          <button>Закрыть</button>
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
  );
}
  • Dialog.Content автоматически захватывает фокус при открытии.
  • Overlay предотвращает взаимодействие с элементами за модалкой.
  • Клавиша Escape по умолчанию закрывает диалог, возвращая фокус к Dialog.Trigger.

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


Навигация с клавиатуры внутри компонентов

Для компонентов, таких как Tabs, DropdownMenu, Accordion, важно корректно реализовать навигацию стрелками. Radix UI предоставляет встроенную логику:

  • ArrowUp / ArrowDown — перемещение по вертикальным спискам.
  • ArrowLeft / ArrowRight — перемещение по горизонтальным вкладкам.
  • Home / End — переход к первому или последнему элементу.
  • Enter / Space — активация выбранного элемента.
import * as Tabs from '@radix-ui/react-tabs';

<Tabs.Root defaultValue="tab1">
  <Tabs.List>
    <Tabs.Trigger value="tab1">Вкладка 1</Tabs.Trigger>
    <Tabs.Trigger value="tab2">Вкладка 2</Tabs.Trigger>
  </Tabs.List>
  <Tabs.Content value="tab1">Контент 1</Tabs.Content>
  <Tabs.Content value="tab2">Контент 2</Tabs.Content>
</Tabs.Root>

Radix автоматически управляет фокусом при переключении вкладок и обеспечивает доступность с клавиатуры без дополнительного кода.


Использование FocusScope для сложных интерфейсов

FocusScope позволяет создавать области с ограниченным фокусом, что особенно полезно для кастомных всплывающих меню или многоуровневых модальных окон. Можно управлять:

  • Автоматическим переносом фокуса на первый интерактивный элемент при открытии.
  • Возвратом фокуса на исходный элемент при закрытии.
  • Циклическим обходом элементов внутри контейнера.
import { FocusScope } from '@radix-ui/react-focus-scope';

<FocusScope trapped>
  <input placeholder="Имя" />
  <input placeholder="Фамилия" />
  <button>Отправить</button>
</FocusScope>

Параметр trapped активирует цикл фокуса, предотвращая выход за пределы области.


Работа с tabIndex и ARIA

Для корректного управления фокусом важно правильно использовать:

  • tabIndex={0} — элемент доступен через Tab.
  • tabIndex={-1} — элемент может получить фокус программно, но пропускается при обходе клавишами.
  • ARIA-атрибутыaria-modal, aria-labelledby, aria-describedby.

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


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

  1. Минимизировать использование tabIndex > 0 — это нарушает естественный порядок табуляции.
  2. Использовать FocusScope для всех всплывающих окон.
  3. Всегда возвращать фокус к элементу-триггеру после закрытия диалогов.
  4. Тестировать клавиатурную навигацию и убедиться, что стрелки, Tab и Escape работают корректно.

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