Back

Использование prefers-reduced-motion для доступной анимации

Использование prefers-reduced-motion для доступной анимации

Некоторые пользователи испытывают головокружение, тошноту или дезориентацию от анимированных интерфейсов. CSS-медиафункция prefers-reduced-motion позволяет определить системную настройку пользователя и реагировать на неё более безопасным движением — без полного отказа от визуальной выразительности интерфейса. В этой статье рассматриваются практические подходы к реализации на CSS и JavaScript для распространённых паттернов компонентов.

Ключевые тезисы

  • prefers-reduced-motion — это CSS-медиазапрос, считывающий настройку доступности уровня ОС, и имеет два значения: reduce и no-preference.
  • Цель — уменьшить или заменить несущественное движение, а не полностью отказаться от анимации; плавное изменение прозрачности и сокращённые длительности остаются более безопасными альтернативами.
  • Используйте прогрессивный (opt-in) CSS-подход, чтобы защитить пользователей, которые явно не задавали предпочтения, но всё же могут быть чувствительны к движению.
  • Для анимаций, управляемых JavaScript, window.matchMedia и слушатели вроде хука useReducedMotion() из Motion позволяют синхронизировать поведение с системными изменениями.
  • Панель Rendering в Chrome DevTools эмулирует эту настройку, поэтому тестировать можно без изменения настроек ОС.
  • prefers-reduced-motion помогает соблюсти WCAG 2.3.3, но не решает проблему автовоспроизводимого или зацикленного контента, для которого по-прежнему нужны явные элементы управления паузой согласно WCAG 2.2.2.

Что на самом деле делает prefers-reduced-motion

prefers-reduced-motion — это зрелый и широко поддерживаемый CSS-медиазапрос, а не новое API. Он считывает системную настройку доступности, которую пользователь включил в своей ОС (macOS, Windows, iOS, Android, Linux). Два возможных значения:

  • reduce — пользователь запросил меньше движения
  • no-preference — предпочтение не задано

Стоит отметить: @media (prefers-reduced-motion) — это краткая запись, эквивалентная @media (prefers-reduced-motion: reduce).

Статья в MDN Web Docs описывает полный синтаксис и таблицу совместимости браузеров, а Can I use подтверждает широкую поддержку в современных движках.

Правильная модель мышления: уменьшать, а не удалять

Распространённая ошибка — рассматривать это как «выключатель» для всех анимаций. Это не так. Рекомендации, в том числе WCAG 2.3.3 («Animation from Interactions», уровень AAA), предлагают уменьшать или заменять несущественное движение, особенно:

  • Крупномасштабные перемещения (параллакс, масштабирование, выезжающие панели)
  • Вращающиеся или поворачивающиеся элементы
  • Анимации, запускаемые прокруткой, которые перемещают контент по области просмотра

Плавные изменения прозрачности, цветовые переходы и сокращённые длительности обычно являются более безопасной альтернативой. Модальное окно, появляющееся через fade, а не вылетающее снизу, всё равно сообщает об изменении состояния, не вызывая вестибулярного дискомфорта.

Реализация в CSS: два подхода

Защитный (opt-out): определите анимированные стили по умолчанию, а затем переопределите их внутри медиазапроса.

.modal {
  transform: translateY(20px);
  opacity: 0;
  transition: transform 300ms ease, opacity 300ms ease;
}

@media (prefers-reduced-motion: reduce) {
  .modal {
    transform: none;
    transition: opacity 200ms ease;
  }
}

Прогрессивный (opt-in): определите статичные стили по умолчанию и добавляйте движение только тогда, когда пользователь не отказался от него.

/* Static by default */
.modal {
  opacity: 0;
  transition: opacity 200ms ease;
}

@media (prefers-reduced-motion: no-preference) {
  .modal {
    transform: translateY(20px);
    transition: transform 300ms ease, opacity 300ms ease;
  }
}

Прогрессивный подход безопаснее для пользователей, не задавших предпочтение, но потенциально чувствительных к движению.

CSS-переменные делают подход масштабируемым в больших кодовых базах:

:root {
  --duration: 300ms;
  --easing: ease;
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --duration: 0.01ms;
  }
}

.drawer {
  transition: transform var(--duration) var(--easing);
}

Определение предпочтения в JavaScript

Для анимаций, управляемых через JS, используйте window.matchMedia:

const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)');

if (prefersReduced.matches) {
  // skip or simplify animation
}

// React to live preference changes (e.g., user toggles OS setting)
prefersReduced.addEventListener('change', (e) => {
  if (e.matches) {
    // pause or replace animations
  }
});

Это полезно для каруселей, эффектов, запускаемых прокруткой, и любых анимаций, реализованных вне CSS.

Библиотеки анимации: Framer Motion и Motion.dev

Если вы используете Motion for React (библиотеку, которую многие до сих пор называют Framer Motion — её документация теперь располагается на Motion.dev), хук useReducedMotion() решает задачу элегантно:

import { motion } from "motion/react";
import { useReducedMotion } from "motion/react";

function Drawer({ isOpen }) {
  const reduce = useReducedMotion();

  return (
    <motion.div
      animate={{ x: isOpen ? 0 : -300, opacity: isOpen ? 1 : 0 }}
      transition={reduce ? { duration: 0 } : { duration: 0.3 }}
    />
  );
}

Хук реактивно считывает системную настройку, поэтому состояние остаётся синхронизированным, если пользователь изменит настройки ОС в середине сессии.

Тестирование через Chrome DevTools

Чтобы протестировать поведение, нет необходимости менять настройки ОС. В Chrome DevTools:

  1. Откройте DevTools → вкладка Rendering (через меню с тремя точками → More toolsRendering)
  2. Найдите «Emulate CSS media feature prefers-reduced-motion»
  3. Установите значение reduce

Страница отреагирует мгновенно, позволяя проверить каждый анимированный компонент без изменения системных настроек.

Замечание о покрытии WCAG

prefers-reduced-motion помогает соблюсти WCAG 2.3.3, но охватывает не всё. Автовоспроизводимое видео, зацикленные GIF и непрерывно движущийся контент по-прежнему могут требовать явных элементов управления паузой/остановкой согласно WCAG 2.2.2 («Pause, Stop, Hide»). Медиазапрос хорошо справляется с анимациями, запускаемыми взаимодействием, — для постоянного фонового движения нужно отдельное решение.

Практический вывод

Проведите аудит ваших анимированных компонентов — модальных окон, выдвижных панелей, эффектов наведения, каруселей, переходов между страницами — и для каждого решите, что использовать: сократить длительность, заменить движение на изменение прозрачности или полностью отключить анимацию. Используйте CSS-переменные для централизации логики, matchMedia — когда анимацией управляет JavaScript, и Chrome DevTools — для проверки без изменения настроек ОС.

Заключение

Учёт prefers-reduced-motion — одна из самых малозатратных и при этом наиболее эффективных побед в области доступности, доступных фронтенд-разработчикам. Технология стабильна, поддержка в браузерах превосходна, а паттерны реализации просты как в CSS, так и в JavaScript и современных анимационных библиотеках. Настоящая работа — изменить модель мышления: анимация становится слоем, который можно «приглушить» для чувствительных пользователей, а не функцией, которую нужно выключать. Сделайте эту привычку частью каждого компонента, который вы выпускаете.

FAQ

Установка длительности в очень малое значение, например 0.01ms, часто предпочтительнее нуля. Это сохраняет событие transitionend, поэтому JavaScript-логика, ожидающая завершения анимации, продолжает срабатывать. Истинный ноль в некоторых браузерах может пропустить событие и сломать конечные автоматы, зависящие от него. Визуальный результат идентичен мгновенному изменению.

Может влиять, но поведение различается между браузерами и реализациями. Более безопасный подход — обернуть CSS плавной прокрутки в @media (prefers-reduced-motion: no-preference), чтобы пользователи с включённым reduced-motion всегда получали мгновенную прокрутку. Если вы реализуете кастомную анимацию прокрутки на JavaScript, следует также вручную проверять это предпочтение и переключаться на мгновенную прокрутку с помощью window.scrollTo со значением behavior равным auto.

Нет. Более короткие или пропущенные анимации обычно улучшают воспринимаемую отзывчивость, потому что пользователи быстрее видят финальное состояние контента. Метрика Interaction to Next Paint часто выигрывает, поскольку переходы больше не задерживают визуальную обратную связь. Единственная оговорка — удаление анимаций, сообщавших об изменениях состояния: замените их на плавное изменение прозрачности или цвета, чтобы интерфейс по-прежнему ощущался отзывчивым, а не резким.

Да, и это хорошая практика для пользователей общих устройств или тех, кто не знает о соответствующей опции в ОС. Сохраняйте предпочтение в localStorage и комбинируйте его с результатом медиазапроса через логическое ИЛИ. Таким образом, либо настройка ОС, либо внутренний переключатель приложения смогут включить уменьшенное движение, давая пользователям максимальный контроль без переопределения их системных предпочтений.

Truly understand users experience

See every user interaction, feel every frustration and track all hesitations with OpenReplay — the open-source digital experience platform. It can be self-hosted in minutes, giving you complete control over your customer data. . Check our GitHub repo and join the thousands of developers in our community..

OpenReplay