Клавиатурная навигация

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

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

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

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

function FocusableButton(props) {
  const { isFocusVisible, focusProps } = useFocusRing();
  return (
    <button {...focusProps} style={{ outline: isFocusVisible ? '2px solid blue' : 'none' }}>
      {props.children}
    </button>
  );
}
  • isFocusVisible — булевый флаг, указывающий, активен ли фокус для текущего элемента.
  • focusProps — набор пропсов, которые необходимо передать элементу для корректного управления фокусом.

useKeyboard — предоставляет возможность реагировать на нажатия клавиш:

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

function KeyboardHandler() {
  const { keyboardProps } = useKeyboard({
    onKeyDown: (e) => {
      if (e.key === 'Enter') {
        console.log('Нажата клавиша Enter');
      }
    }
  });

  return <div {...keyboardProps} tabIndex={0}>Нажмите Enter</div>;
}
  • Позволяет ловить события keydown, keyup, keypress.
  • Работает в сочетании с tabIndex, чтобы элемент был фокусируемым.

Организация табуляции

В React Aria часто используется tabIndex для управления последовательностью фокусируемых элементов. Хук useFocusable автоматизирует работу с фокусом:

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

function FocusableItem(props) {
  const { focusableProps } = useFocusable(props);
  return <div {...focusableProps}>{props.children}</div>;
}
  • focusableProps включает все необходимые атрибуты ARIA и tabIndex.
  • Совмещается с useFocusRing для визуального отображения текущего фокуса.

Управление списками и меню

Для компонентов типа списков, меню или табов важна поддержка стрелок клавиатуры. Хук useListKeyboardNavigation обеспечивает простую навигацию:

import { useListKeyboardNavigation } from '@react-aria/selection';
import { useState } from 'react';

function ListNavigation({ items }) {
  const [selectedIndex, setSelectedIndex] = useState(0);
  const { keyboardProps } = useListKeyboardNavigation({
    selectedKey: selectedIndex,
    onSelectionChange: setSelectedIndex,
    collection: items
  });

  return (
    <ul {...keyboardProps}>
      {items.map((item, index) => (
        <li key={index} style={{ background: index === selectedIndex ? 'lightgray' : 'white' }}>
          {item}
        </li>
      ))}
    </ul>
  );
}
  • Автоматически обрабатывает стрелки вверх/вниз, Home, End.
  • Обеспечивает правильное управление фокусом и выделением элементов в списке.

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

Клавиатурная навигация в модальных компонентах требует trap focus, чтобы фокус оставался внутри окна:

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

function Modal({ isOpen, onClose }) {
  const ref = React.useRef();
  const { dialogProps } = useDialog({}, ref);
  const { overlayProps } = useOverlay({ isOpen, onClose }, ref);

  return isOpen ? (
    <div {...overlayProps} ref={ref} style={{ position: 'fixed', top: 0, left: 0 }}>
      <div {...dialogProps} style={{ background: 'white', padding: 20 }}>
        Модальное окно
      </div>
    </div>
  ) : null;
}
  • useDialog добавляет необходимые ARIA-атрибуты.
  • useOverlay управляет фокусом и закрытием по Escape.

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

React Aria рекомендует разделять интерфейсную логику и состояние. Например, useSelectableCollection позволяет поддерживать клавиатурную навигацию и выделение элементов, при этом состояние можно хранить снаружи:

import { useSelectableCollection } from '@react-aria/selection';
import { useState } from 'react';

function SelectableList({ items }) {
  const [selectedKeys, setSelectedKeys] = useState(new Set());
  const { collectionProps, selectionManager } = useSelectableCollection({
    items,
    selectionMode: 'single',
    selectedKeys,
    onSelectionChange: setSelectedKeys
  });

  return (
    <ul {...collectionProps}>
      {items.map(item => (
        <li key={item.key} style={{ background: selectedKeys.has(item.key) ? 'lightblue' : 'white' }}>
          {item.name}
        </li>
      ))}
    </ul>
  );
}
  • selectionManager управляет логикой выделения и навигации.
  • Поддерживаются режимы: single, multiple, none.
  • Автоматически интегрируется с клавиатурными событиями (ArrowUp, ArrowDown, Enter).

Ключевые рекомендации

  • Всегда использовать хуки React Aria для управления фокусом и клавиатурой вместо ручного добавления событий.
  • Совмещать useFocusRing с useKeyboard для полной поддержки визуальной и функциональной доступности.
  • Для коллекций элементов применять useSelectableCollection или useListKeyboardNavigation вместо собственных реализаций стрелочной навигации.
  • Для модальных и всплывающих компонентов использовать useDialog + useOverlay для корректного управления фокусом и клавиатурным закрытием.

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