Директива prefers-reduced-motion

Медиа-выражение prefers-reduced-motion позволяет учитывать системные настройки пользователя, связанные с уменьшением анимации. На уровне операционной системы или браузера пользователь может указать, что избыточные движения интерфейса вызывают дискомфорт (например, из-за вестибулярной чувствительности). В таком случае веб-приложение должно адаптировать поведение анимаций.

Библиотека AOS (Animate On Scroll) активно использует трансформации и эффекты появления элементов, поэтому поддержка этой директивы становится важной частью доступности интерфейса.


Принцип работы

prefers-reduced-motion — это CSS media query, которая возвращает одно из двух значений:

  • no-preference — пользователь не ограничивает анимации
  • reduce — пользователь предпочитает минимальное количество анимации

Пример использования в CSS:

@media (prefers-reduced-motion: reduce) {
  * {
    animation: none !important;
    transition: none !important;
  }
}

В контексте AOS такая глобальная отмена может быть слишком грубой, поэтому чаще применяются более точечные решения.


Влияние на AOS

AOS по умолчанию управляет:

  • длительностью (duration)
  • задержкой (delay)
  • типом анимации (fade, slide, zoom и др.)
  • easing-функциями

При включённом prefers-reduced-motion: reduce рекомендуется:

  • отключать анимации полностью
  • либо заменять их на мгновенное появление
  • либо сокращать длительность до минимального значения

Проверка настройки в JavaScript

AOS конфигурируется через JavaScript, поэтому логично обрабатывать prefers-reduced-motion именно там:

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

Это значение можно использовать при инициализации AOS.


Условная инициализация AOS

Полное отключение анимаций

if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
  AOS.init();
}

В этом случае библиотека не запускается вообще.


Инициализация с минимальными эффектами

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

AOS.init({
  duration: reduceMotion ? 0 : 800,
  delay: reduceMotion ? 0 : 100,
  easing: 'ease-out-cubic'
});

При активной настройке анимации становятся мгновенными.


Управление через атрибуты

AOS использует HTML-атрибуты вида:

<div data-aos="fade-up"></div>

Для адаптации под prefers-reduced-motion можно динамически изменять атрибуты:

if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
  document.querySelectorAll('[data-aos]').forEach(el => {
    el.setAttribute('data-aos', 'none');
  });
}

Создание пользовательского поведения

Вместо полного отключения можно заменить анимации на менее агрессивные:

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

document.querySelectorAll('[data-aos]').forEach(el => {
  if (reduceMotion) {
    el.setAttribute('data-aos', 'fade');
    el.setAttribute('data-aos-duration', '200');
  }
});

Таким образом сохраняется базовая визуальная динамика без резких перемещений.


CSS-переопределения для AOS

AOS добавляет классы:

  • aos-init
  • aos-animate

Можно переопределить их поведение:

@media (prefers-reduced-motion: reduce) {
  [data-aos] {
    opacity: 1 !important;
    transform: none !important;
  }
}

Это позволяет элементам сразу отображаться без анимации, даже если AOS активен.


Динамическое отслеживание изменений

Настройка prefers-reduced-motion может изменяться во время работы страницы. Для отслеживания используется слушатель:

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

mediaQuery.addEventListener('change', (e) => {
  if (e.matches) {
    AOS.refreshHard();
  }
});

refreshHard() пересчитывает все элементы и применяет новые параметры.


Комбинирование с пользовательскими настройками

Часто добавляется собственный переключатель анимаций:

const userDisabledAnimations = localStorage.getItem('noAnimations') === 'true';

const shouldReduceMotion = 
  userDisabledAnimations || 
  window.matchMedia('(prefers-reduced-motion: reduce)').matches;

AOS.init({
  duration: shouldReduceMotion ? 0 : 800
});

Это позволяет учитывать как системные, так и пользовательские предпочтения.


Практические рекомендации

Минимизация движения:

  • избегать slide, zoom, flip
  • использовать fade или мгновенное появление

Снижение нагрузки:

  • уменьшать duration до 0–200 мс
  • отключать delay

Избежание триггеров дискомфорта:

  • не использовать параллакс-эффекты
  • исключать масштабирование и вращение

Ошибки при работе с prefers-reduced-motion

Игнорирование настройки Анимации остаются активными, несмотря на системные ограничения.

Частичное отключение Отключаются только CSS-анимации, но остаются JS-эффекты AOS.

Резкое исчезновение элементов При неправильной настройке элементы могут быть невидимыми до скролла.


Тестирование

Проверка выполняется через настройки системы:

  • macOS: Accessibility → Display → Reduce motion
  • Windows: Ease of Access → Display → Show animations in Windows
  • браузеры: DevTools → Rendering → Emulate CSS prefers-reduced-motion

После изменения настроек важно проверять:

  • загрузку страницы
  • поведение при скролле
  • видимость элементов

Расширенные сценарии

Ленивая загрузка с учётом motion:

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

if (reduceMotion) {
  document.body.classList.add('no-motion');
}
.no-motion [data-aos] {
  opacity: 1;
  transform: none;
}

Интеграция с SPA-фреймворками

При использовании React/Vue:

  • проверка prefers-reduced-motion выполняется при монтировании
  • состояние сохраняется в глобальном сторе
  • AOS пересоздаётся при изменении состояния

Итоговое поведение AOS с учётом директивы

Состояние Поведение
no-preference Полная анимация
reduce Упрощённые или отключённые эффекты
пользователь отключил вручную Полное отключение AOS

Грамотная интеграция prefers-reduced-motion делает интерфейс не только визуально аккуратным, но и доступным для пользователей с разными физиологическими особенностями, не нарушая при этом общую архитектуру анимаций в AOS.