Logo with initials
Click me x5 ↓

How to use prefers-reduced-motion in SvelteKit

How to use prefers-reduced-motion in SvelteKit

I love animations, but I also don’t want to force them onto my visitors if they don’t want them. Browsers give us the prefers-reduced-motion media query to check the user’s preferences.

CSS

For CSS animations in Svelkit, there’s no problem, you can use the media query directly.

.animation {
  transition: none;
}

@media (prefers-reduced-motion: no-preference) {
  .animation {
    transition: transform 0.4s ease-out;
  }
}

JavaScript

In JavaScript, you can check the value of the media query using window.matchMedia. The problem is, with Sveltekit, the window object is not accessible during server-side rendering (SSR).

You check if the window object is avaible using the browser variable that Sveltekit exports from $app/env.

Here’s a function that I use for my animations

import { browser } from '$app/env';

// returns true _only_ if the user has ticked the box
const getReducedMotion = () => {
  if (!browser) return;

  const QUERY = '(prefers-reduced-motion: no-preference)';
  const mediaQueryList = window.matchMedia(QUERY);
  return !mediaQueryList.matches;
};

export default getReducedMotion;

Then, where I want to animate:

let updateRotation = () => {
  const isReducedMotion = getReducedMotion();

  if (isReducedMotion) return;

  // TODO wrap value?
  rotation = (y / innerHeight) * 360;
};