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

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


Хук useFocus

useFocus предназначен для отслеживания состояния фокуса на элементе. Он возвращает объект с ключевыми свойствами и обработчиками:

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

function FocusableButton() {
  const ref = useRef();
  const {isFocused, focusProps} = useFocus({});

  return (
    <button {...focusProps} ref={ref}>
      {isFocused ? 'В фокусе' : 'Не в фокусе'}
    </button>
  );
}

Основные моменты:

  • focusProps содержит стандартные обработчики событий onFocus и onBlur, а также дополнительную логику для корректного взаимодействия с клавиатурой и вспомогательными технологиями.
  • isFocused позволяет динамически изменять визуальное представление элемента при получении фокуса.

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

Для визуальной индикации фокуса используется хук useFocusRing:

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

function StyledButton() {
  const ref = useRef();
  const {isFocusVisible, focusProps} = useFocusRing();

  return (
    <button {...focusProps} ref={ref} className={isFocusVisible ? 'focus-ring' : ''}>
      Кнопка
    </button>
  );
}

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

  • isFocusVisible определяет, должен ли быть показан визуальный индикатор фокуса. React Aria учитывает, был ли фокус установлен с клавиатуры или мыши.
  • Стилизация фокусного состояния выполняется через CSS-класс, что позволяет разделять логику и презентацию.

Управление порядком табуляции

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

import {useTabList, useTab, useTabPanel} from '@react-aria/tabs';
import {useState} from 'react';

function TabsExample() {
  const [selectedKey, setSelectedKey] = useState('tab1');
  const {tabListProps} = useTabList({selectedKey, onSelectionChange: setSelectedKey});

  return (
    <div {...tabListProps}>
      <Tab id="tab1" label="Вкладка 1" />
      <Tab id="tab2" label="Вкладка 2" />
    </div>
  );
}

function Tab({id, label}) {
  const {tabProps} = useTab({id, key: id});
  return <button {...tabProps}>{label}</button>;
}

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

  • Хуки автоматически управляют фокусом при переключении вкладок.
  • Порядок навигации с клавиатуры соблюдается в соответствии со стандартами ARIA.
  • Нет необходимости вручную контролировать tabIndex — React Aria делает это автоматически.

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

Для модальных диалогов или всплывающих меню важно зафиксировать фокус внутри контейнера. Используется хук useOverlay:

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

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

  if (!isOpen) return null;

  return (
    <div {...overlayProps} ref={ref} className="modal">
      Контент модального окна
    </div>
  );
}

Принципы работы:

  • При открытии модального окна фокус автоматически переводится на первый фокусируемый элемент внутри.
  • При закрытии — возвращается на элемент, с которого был вызван модальный диалог.
  • Пользователь не сможет случайно выйти из модального окна с помощью клавиши Tab.

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

React Aria учитывает все стандартные паттерны взаимодействия с клавиатурой:

  • Стрелки навигации (ArrowUp, ArrowDown, ArrowLeft, ArrowRight) для перемещения между элементами в списках, вкладках или меню.
  • Tab и Shift+Tab для циклической навигации внутри контейнеров.
  • Поддержка клавиш Home, End, PageUp, PageDown в сложных списках или таблицах.

Пример управления фокусом в списке:

import {useListBox, useOption} from '@react-aria/listbox';
import {useState, useRef} from 'react';

function ListBoxExample({items}) {
  const [selectedKey, setSelectedKey] = useState(null);
  const ref = useRef();
  const {listBoxProps} = useListBox({items, selectedKey, onSelectionChange: setSelectedKey}, ref);

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

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

  return (
    <li {...optionProps} ref={ref} className={isFocused ? 'focused' : ''}>
      {item.name}
    </li>
  );
}

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

Для элементов, которые должны быть доступны для клавиатуры, но невидимы визуально, React Aria предоставляет поддержку через focusProps и атрибуты ARIA. Это позволяет создавать кастомные компоненты, где фокус управляется программно, сохраняя полную доступность.


Рекомендации по практическому использованию

  1. Всегда использовать хуки React Aria для управления фокусом вместо ручного ref.focus().
  2. В модальных и всплывающих элементах фокус должен быть замкнут.
  3. В сложных компонентах (таблицы, списки, вкладки) использовать предоставляемые хуки, чтобы автоматически поддерживать доступность.
  4. Стилизация фокуса должна отделяться от логики: использовать isFocused или isFocusVisible для классов CSS.
  5. Проверять фокусировку клавиатурой и вспомогательными технологиями на всех элементах.

Эти принципы обеспечивают соблюдение стандартов ARIA и делают интерфейсы React Aria полностью доступными.