Хук useFocusRing из библиотеки React Aria предназначен
для управления визуальной индикацией фокуса элементов пользовательского
интерфейса. Он обеспечивает согласованное поведение фокуса, учитывая
клавиатурные и мышиные взаимодействия, что особенно важно для
доступности (accessibility) веб-приложений.
useFocusRing возвращает объект с двумя ключевыми
свойствами:
Дополнительно возвращается объект focusProps, который
содержит необходимые обработчики событий (onFocus,
onBlur, onFocusChange) для правильной работы
индикации фокуса. Его следует распространять на элемент интерфейса:
import {useFocusRing} from '@react-aria/focus';
function Button(props) {
let {focusProps, isFocusVisible} = useFocusRing();
return (
<button {...focusProps} className={isFocusVisible ? 'focus-ring' : ''}>
{props.children}
</button>
);
}
Таким образом, isFocusVisible контролирует отображение
фокусных рамок, соблюдая рекомендации WCAG.
Чаще всего для отображения фокуса используется CSS-класс с псевдоклассами или стилями:
.focus-ring {
outline: 2px solid #007aff;
outline-offset: 2px;
border-radius: 4px;
}
Комбинируя focusProps и isFocusVisible,
можно динамически применять этот класс только при необходимости:
<button
{...focusProps}
className={isFocusVisible ? 'focus-ring' : ''}
>
Отправить
</button>
Такой подход исключает появление визуальных индикаторов фокуса при кликах мышью, сохраняя интерфейс чистым и доступным.
useFocusRing особенно полезен в составных элементах,
таких как модальные окна, меню,
табы или списки с выбором. В таких
случаях можно комбинировать хук с useFocusManager или
useTabList для централизованного контроля фокуса внутри
компонента.
Пример для списка кнопок:
import {useFocusRing} from '@react-aria/focus';
function MenuItem({children}) {
let {focusProps, isFocusVisible} = useFocusRing();
return (
<li>
<button {...focusProps} className={isFocusVisible ? 'focus-ring' : ''}>
{children}
</button>
</li>
);
}
Каждая кнопка получает независимую подсветку фокуса, что повышает удобство навигации с клавиатуры.
useFocusRing можно использовать вместе с:
Пример комбинированного использования с useButton:
import {useButton} from '@react-aria/button';
import {useFocusRing} from '@react-aria/focus';
function CustomButton(props) {
let {buttonProps} = useButton(props);
let {focusProps, isFocusVisible} = useFocusRing();
return (
<button
{...buttonProps}
{...focusProps}
className={isFocusVisible ? 'focus-ring' : ''}
>
{props.children}
</button>
);
}
Для улучшения UX можно динамически менять стили фокуса в зависимости от состояния или темы:
className={`${isFocusVisible ? 'focus-ring' : ''} ${isDisabled ? 'disabled' : ''}`}
Это позволяет сохранять согласованность с дизайн-системой и избегать конфликтов с другими визуальными состояниями компонента.
Основной принцип работы с useFocusRing:
focusProps на интерактивный
элемент.isFocusVisible для условного применения
визуальной подсветки.:focus в CSS для всех случаев,
особенно если нужно различать клавиатурный и мышиный фокус.Этот подход обеспечивает доступность, соблюдение стандартов WCAG и чистый UX без лишних визуальных отвлекающих элементов.