How to Use prefers-reduced-motion in WordPress Themes

January 5, 2026
How to Use prefers-reduced-motion in WordPress Themes

Animations can improve perceived polish, but they can also trigger motion sickness or discomfort for some users.
Most operating systems provide a “Reduce Motion” accessibility setting, and browsers expose it via
prefers-reduced-motion.

A WordPress theme should respect this preference by disabling or simplifying non-essential motion while keeping the UI functional.
This is a best practice for accessibility and often improves perceived performance on slower devices.

What prefers-reduced-motion Means

The media query can return:

  • prefers-reduced-motion: reduce (user requests less motion)
  • prefers-reduced-motion: no-preference (no special preference)

Your theme should avoid:

  • Parallax and scroll-linked animations
  • Long easing transitions for UI state changes
  • Autoplay sliders and looping animations
  • Smooth scrolling when the user requests reduced motion

1) Add a Safe CSS Baseline

A practical baseline is to reduce animation and transition durations and disable smooth scrolling.
This keeps components working but removes motion.

@media (prefers-reduced-motion: reduce) {
  html:focus-within {
    scroll-behavior: auto;
  }

  * {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

This is effective, but it may be too broad if you embed third-party widgets that rely on transitions.
If you want tighter control, scope it to theme components.

2) Scope Reduced Motion Rules to Theme Components (Recommended)

Instead of applying to every element, target your own UI building blocks:
navigation, modals, accordions, and hero effects.

@media (prefers-reduced-motion: reduce) {
  .site-header,
  .site-nav,
  .mobile-nav,
  .modal,
  .accordion,
  .hero,
  .reveal {
    animation: none !important;
    transition: none !important;
    transform: none !important;
  }
}

This reduces the risk of breaking external components.

3) Avoid “Hidden Until Animated” Bugs

A common pattern is to start elements hidden and reveal them with JS/CSS animations.
If you disable motion, you must ensure content is still visible.

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

.reveal.is-visible {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

This ensures reduced-motion users don’t get “stuck” hidden content.

4) JavaScript: Skip Animation Logic When Reduced Motion Is Enabled

If you use JS-driven animations (IntersectionObserver effects, sliders, or animation libraries),
respect the preference in JavaScript as well.

function wpctPrefersReducedMotion() {
  return window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
}

if (!wpctPrefersReducedMotion()) {
  // Initialize non-essential animations here
}

This prevents wasted work and avoids UI glitches when animations are disabled in CSS.

5) JavaScript: Disable Autoplay for Sliders

Autoplay is one of the most common motion triggers.
If you have a custom slider, gate autoplay like this:

var reduceMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var shouldAutoplay = !reduceMotion;

If you use a third-party slider library, look for an autoplay option you can turn off.

6) WordPress Pattern: Add a reduce-motion Class (Optional)

If you want to keep your CSS simpler, you can add a class to <html> and use it as a switch.
This is optional, but useful on large themes with many components.

functions.php: Add Inline Script Before Your Main Script

<?php
add_action( 'wp_enqueue_scripts', function () {
  if ( is_admin() ) {
    return;
  }

  wp_enqueue_script(
    'theme-main',
    get_template_directory_uri() . '/assets/js/main.js',
    array(),
    '1.0.0',
    true
  );

  $inline = <<<JS
(function () {
  if (!window.matchMedia) return;
  var mq = window.matchMedia('(prefers-reduced-motion: reduce)');
  function apply() {
    document.documentElement.classList.toggle('reduce-motion', mq.matches);
  }
  apply();
  if (mq.addEventListener) mq.addEventListener('change', apply);
  else if (mq.addListener) mq.addListener(apply);
})();
JS;

  wp_add_inline_script( 'theme-main', $inline, 'before' );
}, 20 );

CSS Example

.reduce-motion .hero,
.reduce-motion .modal,
.reduce-motion .accordion {
  animation: none !important;
  transition: none !important;
}

7) Smooth Scrolling: Disable It for Reduced Motion

If your theme enables smooth scrolling, always provide a reduced-motion override.

html {
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

Testing Checklist

  • Enable “Reduce Motion” in OS accessibility settings
  • Test key UI: mobile menu, modal, accordion, tabs
  • Ensure no content stays hidden due to animation-off states
  • Confirm smooth scrolling is disabled when reduced motion is enabled

Common Mistakes

  • Disabling motion but leaving elements at opacity: 0 by default
  • Only fixing CSS while JS still runs expensive animation logic
  • Applying a global kill switch that breaks third-party widgets
  • Keeping autoplay enabled on sliders for reduced-motion users

Summary

  • prefers-reduced-motion is an accessibility preference your theme should respect
  • Use CSS media queries to disable non-essential transitions/animations
  • Ensure “hidden until animated” elements become visible under reduced motion
  • Gate JS-driven animation logic and autoplay
  • Optionally add a reduce-motion class for simpler theme-wide control

Respecting reduced motion makes your WordPress theme more accessible, more stable, and often faster—without giving up modern UI patterns.

Avatar

Written by

satoshi

I’ve been building and customizing WordPress themes for over 10 years. In my free time, you’ll probably find me enjoying a good football match.