Floating UI предоставляет возможности для управления позиционируемыми элементами с клавиатуры, что критически важно для доступности и удобства использования интерфейсов. В контексте всплывающих меню, тултипов, модальных окон и других плавающих компонентов клавиатурная навигация обеспечивает контроль без использования мыши.
Floating UI предоставляет утилиты для обработки событий клавиш,
особенно стрелок вверх (ArrowUp) и вниз
(ArrowDown). Эти клавиши позволяют пользователю
перемещаться по элементам списка или меню:
import { useFloating, shift, flip } from '@floating-ui/react';
const { refs, floatingStyles } = useFloating({
placement: 'bottom-start',
middleware: [flip(), shift({ padding: 5 })],
});
function handleKeyDown(event) {
switch (event.key) {
case 'ArrowDown':
focusNextItem();
break;
case 'ArrowUp':
focusPreviousItem();
break;
}
}
Особенности:
focusNextItem и focusPreviousItem
управляют фокусом по DOM-элементам меню.Для интерактивных элементов Floating UI часто используют клавиши Enter, Space и Escape:
Пример:
function handleKeyDown(event) {
if (event.key === 'Escape') {
closeFloating();
referenceRef.current.focus();
}
if (event.key === 'Enter' || event.key === ' ') {
toggleFloating();
}
}
Важные моменты:
event.preventDefault() для предотвращения стандартного
скролла при нажатии пробела.Floating UI сам по себе не управляет фокусом, поэтому для удобной клавиатурной навигации часто комбинируют его с библиотеками вроде focus-trap или реализуют кастомные функции:
function focusFirstItem() {
const firstItem = floatingRef.current.querySelector('[tabindex="0"]');
firstItem?.focus();
}
function focusLastItem() {
const items = floatingRef.current.querySelectorAll('[tabindex="0"]');
items[items.length - 1]?.focus();
}
Рекомендации:
tabindex="0" для корректного обхода с клавиатуры.tabindex="-1" для элементов, которые не
должны быть доступными напрямую через Tab, но могут получать фокус
программно.Правильная поддержка клавиши Tab обеспечивает логичный переход между элементами формы и всплывающими компонентами. Floating UI требует явного управления:
function handleTab(event) {
const focusableItems = Array.from(
floatingRef.current.querySelectorAll('[tabindex="0"]')
);
const currentIndex = focusableItems.indexOf(document.activeElement);
if (event.shiftKey && currentIndex === 0) {
focusableItems[focusableItems.length - 1].focus();
event.preventDefault();
} else if (!event.shiftKey && currentIndex === focusableItems.length - 1) {
focusableItems[0].focus();
event.preventDefault();
}
}
Ключевой момент: циклический переход предотвращает потерю фокуса и закрытие меню по ошибке при стандартной навигации.
Для доступности следует использовать ARIA-атрибуты совместно с клавиатурным управлением:
aria-haspopup="menu" – указывает, что элемент открывает
всплывающее меню.aria-expanded="true/false" – отражает текущее
состояние.role="menu" и role="menuitem" – помогают
вспомогательным технологиям корректно интерпретировать структуру.Пример:
<button aria-haspopup="menu" aria-expanded="false" id="menuButton">
Открыть меню
</button>
<ul role="menu" id="menu" hidden>
<li role="menuitem" tabindex="0">Пункт 1</li>
<li role="menuitem" tabindex="0">Пункт 2</li>
</ul>
Floating UI предоставляет низкоуровневые инструменты для позиционирования, но клавиатурная логика строится вручную. Практический подход:
flip, shift).ArrowUp,
ArrowDown, Enter, Escape,
Tab).Такой подход обеспечивает полностью управляемое и доступное всплывающее взаимодействие с клавиатуры, сохраняя гибкость Floating UI.