Навигация с клавиатуры

React Aria предоставляет набор хуков и утилит для создания доступных интерфейсов, в которых навигация с клавиатуры реализуется на профессиональном уровне. Основная цель — позволить пользователям перемещаться по компонентам без мыши, обеспечивая полное соответствие стандартам доступности (WCAG) и лучшим практикам ARIA.

Хуки для управления фокусом

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

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

function FocusableButton(props) {
  let { isFocusVisible, focusProps } = useFocusRing();
  return (
    <button {...focusProps} className={isFocusVisible ? 'focus-visible' : ''}>
      {props.children}
    </button>
  );
}

Особенности useFocusRing:

  • Автоматическое определение, какой тип взаимодействия вызвал фокус (клавиатура, мышь, сенсор).
  • Совместимость с кастомными компонентами.
  • Поддержка стилизации через CSS или inline-стили.

useFocus — обеспечивает базовое управление фокусом без визуальных индикаторов. Применяется для компонентов, где необходим контроль фокуса программно.

import { useFocus } from '@react-aria/interactions';

function CustomInput(props) {
  let { focusProps } = useFocus();
  return <input {...focusProps} {...props} />;
}

Клавиатурная навигация в списках и меню

Для реализации доступной навигации внутри списков, табов, меню и селектов используются хуки useListBox, useMenu, useMenuTrigger. Они обеспечивают управление фокусом и правильное реагирование на клавиши стрелок, Home, End, Enter и Escape.

Пример: управление списком с клавиатуры

import { useListBox, useOption } from '@react-aria/listbox';
import { useListState } from '@react-stately/list';

function SelectList({ items }) {
  let state = useListState({ items });
  let ref = React.useRef();
  let { listBoxProps } = useListBox({ autoFocus: true }, state, ref);

  return (
    <ul {...listBoxProps} ref={ref}>
      {state.collection.map(item => (
        <Option key={item.key} item={item} state={state} />
      ))}
    </ul>
  );
}

function Option({ item, state }) {
  let ref = React.useRef();
  let { optionProps, isSelected, isFocused } = useOption({ key: item.key }, state, ref);

  return (
    <li {...optionProps} ref={ref} className={`${isFocused ? 'focused' : ''} ${isSelected ? 'selected' : ''}`}>
      {item.rendered}
    </li>
  );
}

Ключевые моменты:

  • Стрелки вверх/вниз автоматически меняют фокус между элементами.
  • Enter выбирает элемент.
  • Escape закрывает меню или возвращает фокус к триггеру.
  • useListState управляет состоянием выбранных и активных элементов.

Работа с клавишами модификаторов

React Aria поддерживает работу с клавишами Shift, Ctrl, Alt и Meta. Для множественного выбора в списках применяются комбинации с Shift или Ctrl. Хуки обеспечивают правильное добавление атрибутов ARIA:

<li
  role="option"
  aria-selected={isSelected}
  onKeyD own={e => {
    if (e.shiftKey && e.key === 'ArrowDown') {
      // расширяем выделение
    }
  }}
>
  {item.name}
</li>

Фокусировка на интерактивных элементах

  • tabIndex={0} используется для элементов, доступных через Tab.
  • tabIndex={-1} позволяет программно переводить фокус без попадания в обычный порядок табуляции.
  • Комбинация с хуками React Aria (useFocus, useListBox) обеспечивает согласованное поведение и корректную поддержку скринридеров.

Управление модальными окнами и всплывающими элементами

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

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

function Modal({ isOpen, onClose, children }) {
  let ref = React.useRef();
  let { overlayProps } = useOverlay({ isOpen, onClose, isDismissable: true }, ref);

  if (!isOpen) return null;
  return (
    <div {...overlayProps} ref={ref} className="modal">
      {children}
    </div>
  );
}

Особенности:

  • Escape закрывает модальное окно.
  • Фокус циклично перемещается по интерактивным элементам внутри модалки.
  • Скринридеры получают правильные ARIA-атрибуты для описания состояния оверлея.

Важные ARIA-атрибуты для клавиатурной навигации

  • aria-selected — выделение элементов списка или меню.
  • aria-expanded — раскрытие и сворачивание меню.
  • aria-haspopup — наличие всплывающего меню.
  • aria-activedescendant — управление активным элементом для списков без прямого фокуса на каждом элементе.

Использование этих атрибутов совместно с хуками React Aria гарантирует правильное поведение при навигации с клавиатуры.

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

  1. Все интерактивные элементы должны быть доступны через Tab.
  2. Для сложных компонентов с множественным выбором использовать хуки useListState и useOption.
  3. Для модальных окон всегда применять useOverlay с замыканием фокуса.
  4. Стрелочные клавиши, Home и End должны логически перемещать фокус по визуальному порядку.
  5. ARIA-атрибуты должны отражать текущее состояние компонентов, чтобы поддерживать скринридеры.

Эта комбинация хуков и правильного применения атрибутов обеспечивает полное соответствие стандартам доступности и удобную клавиатурную навигацию в React-приложениях.