Хук useFocusVisible предназначен для управления
состоянием фокуса в приложениях на React с учетом режимов ввода
пользователя. Он позволяет различать, был ли элемент
сфокусирован с помощью клавиатуры (например, Tab) или мыши, что особенно
важно для обеспечения доступности (accessibility) интерфейсов.
В стандартной HTML-практике браузеры автоматически показывают
индикатор фокуса для клавиатуры, но при использовании мыши этот
индикатор часто мешает визуальной эстетике. useFocusVisible
решает эту проблему, предоставляя возможность контролировать видимость
фокуса программно.
import {useFocusVisible} from '@react-aria/interactions';
function Button() {
const {isFocusVisible, focusProps} = useFocusVisible();
return (
<button {...focusProps} className={isFocusVisible ? 'focus-visible' : ''}>
Click me
</button>
);
}
Объяснение ключевых моментов:
focusProps – набор пропсов, которые необходимо передать
элементу, чтобы отслеживать фокус.isFocusVisible – булево значение, которое указывает,
нужно ли отображать индикатор фокуса.isFocusVisible изменяется автоматически в
зависимости от способа взаимодействия пользователя: клавиатура или
мышь/тач.React Aria использует алгоритм, аналогичный CSS-псевдоклассу
:focus-visible:
element.focus()), также учитывается, но отображение
индикатора зависит от предыдущего режима ввода.Это позволяет создавать интерфейсы, где визуальный индикатор фокуса появляется только тогда, когда он действительно нужен пользователю для навигации.
При создании сложных компонентов, например, кастомных кнопок, полей
ввода или списков с выбором, важно передавать focusProps
внутрь интерактивного элемента, а не на обертку.
function CustomInput(props) {
const {isFocusVisible, focusProps} = useFocusVisible();
return (
<div className="input-wrapper">
<input {...props} {...focusProps} className={isFocusVisible ? 'focus-visible' : ''} />
</div>
);
}
Ключевой момент: индикатор фокуса применяется к
элементу, который фактически получает фокус. Если передать
focusProps на контейнер, но фокус на input, индикатор
работать не будет.
Для управления стилями состояния фокуса рекомендуется использовать
класс .focus-visible. Например:
button {
outline: none;
border: 1px solid transparent;
}
button.focus-visible {
outline: 2px solid #0078d4;
outline-offset: 2px;
}
Преимущество этого подхода:
useFocusVisible интегрируется с другими хуками из
@react-aria/interactions, такими как usePress
или useHover. Например, можно комбинировать фокус и нажатие
для создания визуального отклика:
import {usePress} from '@react-aria/interactions';
function PressableButton() {
const {isFocusVisible, focusProps} = useFocusVisible();
const {pressProps, isPressed} = usePress();
return (
<button
{...focusProps}
{...pressProps}
className={`${isFocusVisible ? 'focus-visible' : ''} ${isPressed ? 'pressed' : ''}`}
>
Press me
</button>
);
}
Принцип работы:
isFocusVisible управляет отображением индикатора
фокуса.isPressed позволяет добавить визуальный отклик при
удержании или клике.focusProps на элементе, который
принимает фокус.:focus, если
проект требует контроля видимости фокуса.Форма с полями ввода:
function LoginForm() {
const usernameFocus = useFocusVisible();
const passwordFocus = useFocusVisible();
return (
<form>
<input
type="text"
placeholder="Username"
{...usernameFocus.focusProps}
className={usernameFocus.isFocusVisible ? 'focus-visible' : ''}
/>
<input
type="password"
placeholder="Password"
{...passwordFocus.focusProps}
className={passwordFocus.isFocusVisible ? 'focus-visible' : ''}
/>
<button type="submit">Login</button>
</form>
);
}
Вывод: каждое поле отслеживает состояние видимого фокуса отдельно, позволяя создавать удобные и доступные формы.
useFocusVisible решает проблему различия визуального
фокуса между клавиатурой и мышью.isFocusVisible автоматически синхронизируется
с режимом ввода пользователя.