prefers-reduced-motion — это медиа-запрос CSS и
соответствующая логика в веб-платформах, позволяющая учитывать
предпочтения пользователя по минимизации анимаций и движения на экране.
Он особенно важен для пользователей с повышенной чувствительностью к
движению или проблемами с вестибулярным аппаратом. Игнорирование этих
предпочтений может привести к дискомфорту и даже физическим
недомоганиям. В контексте React Aria этот механизм
используется для создания доступных интерфейсов, где анимации
адаптируются под реальные предпочтения пользователя.
В вебе доступ к prefers-reduced-motion осуществляется
через CSS или JavaScript. В CSS это выглядит так:
@media (prefers-reduced-motion: reduce) {
.animated-element {
animation: none;
transition: none;
}
}
В JavaScript проверка осуществляется с помощью
window.matchMedia:
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
В React Aria рекомендуется использовать хук
useReducedMotion, который абстрагирует проверку и
обеспечивает реактивное поведение:
import { useReducedMotion } from '@react-aria/interactions';
function AnimatedButton() {
const reducedMotion = useReducedMotion();
return (
<button style={{
transition: reducedMotion ? 'none' : 'transform 0.3s ease-in-out'
}}>
Нажми меня
</button>
);
}
Ключевой момент: если reducedMotion
равен true, анимации и переходы должны быть полностью
отключены или заменены на минимальные эффекты.
React Aria не предоставляет встроенных визуальных анимаций, но часто
используется совместно с библиотеками вроде Framer Motion
или React Spring. В таких случаях
prefers-reduced-motion следует учитывать при создании
анимационных компонентов.
Пример с Framer Motion:
import { motion } from 'framer-motion';
import { useReducedMotion } from '@react-aria/interactions';
function Box() {
const shouldReduceMotion = useReducedMotion();
return (
<motion.div
animate={shouldReduceMotion ? {} : { x: 100, opacity: 1 }}
transition={{ duration: shouldReduceMotion ? 0 : 0.5 }}
>
Контент
</motion.div>
);
}
Если пользователь выбрал уменьшение анимации, свойства
animate остаются статичными, а длительность анимации
устанавливается в 0. Это предотвращает непреднамеренные эффекты движения
и делает интерфейс безопасным.
1. Кнопки и интерактивные элементы
Для кнопок с hover-эффектами:
import { useButton } from '@react-aria/button';
import { useReducedMotion } from '@react-aria/interactions';
function AccessibleButton(props) {
const ref = React.useRef();
const { buttonProps } = useButton(props, ref);
const reducedMotion = useReducedMotion();
return (
<button
{...buttonProps}
ref={ref}
style={{
transition: reducedMotion ? 'none' : 'transform 0.2s',
}}
>
{props.children}
</button>
);
}
2. Модальные окна и выпадающие списки
Для появления и скрытия модальных окон или Popover:
import { useOverlay } from '@react-aria/overlays';
import { useReducedMotion } from '@react-aria/interactions';
function Modal({ isOpen, onClose, children }) {
const ref = React.useRef();
const { overlayProps } = useOverlay({ isOpen, onClose }, ref);
const reducedMotion = useReducedMotion();
return isOpen ? (
<div
{...overlayProps}
ref={ref}
style={{
opacity: reducedMotion ? 1 : 0,
transform: reducedMotion ? 'none' : 'translateY(-10px)',
transition: reducedMotion ? 'none' : 'all 0.3s ease-in-out'
}}
>
{children}
</div>
) : null;
}
3. Карточки и списки с анимацией появления
Для списков с анимацией появления элементов:
import { useReducedMotion } from '@react-aria/interactions';
import { motion, AnimatePresence } from 'framer-motion';
function AnimatedList({ items }) {
const reducedMotion = useReducedMotion();
return (
<ul>
<AnimatePresence>
{items.map(item => (
<motion.li
key={item.id}
initial={reducedMotion ? {} : { opacity: 0, y: -10 }}
animate={reducedMotion ? {} : { opacity: 1, y: 0 }}
exit={reducedMotion ? {} : { opacity: 0, y: 10 }}
transition={{ duration: reducedMotion ? 0 : 0.3 }}
>
{item.content}
</motion.li>
))}
</AnimatePresence>
</ul>
);
}
useReducedMotion к анимационным
компонентам, чтобы избежать потенциального дискомфорта.prefers-reduced-motion лучше минимизировать не
только анимации, но и параллакс, мерцающие эффекты и сложные
переходы.Вывод: соблюдение
prefers-reduced-motion является обязательным элементом
доступного интерфейса. В React Aria это достигается через хуки и
адаптацию стилей, анимаций и переходов под предпочтения пользователя.
Такой подход делает интерфейс безопасным, комфортным и инклюзивным.