useTooltip — это хук из библиотеки React
Aria, предназначенный для создания доступных подсказок, которые
корректно взаимодействуют с клавиатурой, экранными читалками и другими
средствами доступности. Подсказка, реализованная через
useTooltip, автоматически учитывает роли ARIA, управление
фокусом и позиционирование.
Для начала нужно импортировать хук из библиотеки:
import { useTooltip, useTooltipTrigger } from '@react-aria/tooltip';
import { useOverlayPosition } from '@react-aria/overlays';
import { useRef } from 'react';
useTooltip не работает самостоятельно — он всегда
используется вместе с триггером, который показывает
подсказку. Для триггера используется useTooltipTrigger.
function TooltipExample() {
const ref = useRef();
const { triggerProps, tooltipProps, isOpen } = useTooltipTrigger({
delay: 500 // задержка перед показом подсказки
}, useTooltip);
return (
<div>
<button {...triggerProps} ref={ref}>
Наведи на меня
</button>
{isOpen && <Tooltip {...tooltipProps}>Это подсказка</Tooltip>}
</div>
);
}
Здесь ключевые моменты:
triggerProps — свойства, которые нужно применить к
элементу-триггеру.tooltipProps — свойства для элемента подсказки.isOpen — состояние, показывающее, отображается ли
подсказка.delay) позволяет предотвратить мгновенное
появление подсказки при случайном наведении.Подсказка должна быть отдельным компонентом для удобного повторного использования:
function Tooltip({ children, ...props }) {
return (
<div
role="tooltip"
style={{
background: 'black',
color: 'white',
padding: '4px 8px',
borderRadius: '4px',
position: 'absolute',
zIndex: 1000
}}
{...props}
>
{children}
</div>
);
}
Особенности:
role="tooltip" обязателен для корректной работы
экранных читалок.position: absolute необходим для точного размещения
относительно триггера.useOverlayPosition).useOverlayPositionДля динамического позиционирования подсказки относительно триггера
используется useOverlayPosition:
import { useOverlayPosition } from '@react-aria/overlays';
function Tooltip({ children, triggerRef, isOpen }) {
const overlayRef = useRef();
const { overlayProps, placement } = useOverlayPosition({
targetRef: triggerRef,
overlayRef,
placement: 'top',
offset: 4
});
if (!isOpen) return null;
return (
<div
{...overlayProps}
ref={overlayRef}
role="tooltip"
style={{
background: 'black',
color: 'white',
padding: '4px 8px',
borderRadius: '4px',
position: 'absolute',
zIndex: 1000
}}
>
{children}
</div>
);
}
Ключевые параметры
useOverlayPosition:
targetRef — ссылка на триггер, относительно которого
позиционируется подсказка.overlayRef — ссылка на саму подсказку.placement — позиция подсказки (top,
bottom, left, right).offset — смещение между триггером и подсказкой.isOpen — состояние видимости, переданное извне.useTooltipTrigger автоматически управляет состоянием
подсказки. В дополнение можно использовать кастомное управление:
const [isOpen, setOpen] = useState(false);
const { triggerProps, tooltipProps } = useTooltipTrigger(
{ isOpen, onOpenChange: setOpen, delay: 500 },
useTooltip
);
Преимущества такого подхода:
React Aria обеспечивает правильные роли и управление фокусом:
aria-describedby автоматически связывает
триггер с подсказкой.Пример:
<button {...triggerProps} ref={ref} aria-describedby="tooltip1">
Наведи или сфокусируйся
</button>
<Tooltip id="tooltip1" {...tooltipProps} triggerRef={ref} isOpen={isOpen}>
Это подсказка для клавиатуры и мыши
</Tooltip>
role="tooltip" и
aria-describedby для соответствия стандартам WCAG.delay) улучшает опыт пользователя
и предотвращает лишние всплытия.function TooltipDemo() {
const triggerRef = useRef();
const { triggerProps, tooltipProps, isOpen } = useTooltipTrigger({ delay: 300 }, useTooltip);
return (
<div style={{ margin: '100px' }}>
<button {...triggerProps} ref={triggerRef}>
Наведи или сфокусируйся
</button>
{isOpen && (
<Tooltip triggerRef={triggerRef} {...tooltipProps} isOpen={isOpen}>
Подсказка с правильным позиционированием и доступностью
</Tooltip>
)}
</div>
);
}
Такой подход объединяет контроль видимости, позиционирование и доступность в одном удобном паттерне, который масштабируется на большие проекты и обеспечивает качественный UX для всех пользователей.