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: 0by 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-motionis 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-motionclass 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.
🎨 Want to learn more? Visit our WordPress Customization Hub for tips and advanced techniques.