Skip to main content

Accessibility in Practice: Animated Content

Design
Front-end Development
Accessibility

There are many internet users that are prone to dizziness or even seizures when viewing things like video, GIFs, parallax scrolling effects, and animated entrances of content. People in this user group have usually changed settings on their OS to disable extra animation effects, but in many cases, this does not extend to the web. So what do they do about it? In several cases, they install browser extensions that claim to disable animated effects, which can cause breaking issues to your site if the extension doesn't correctly remediate the issues at hand. Gross. Plus, even when it does correctly disable the animation, it results in a sub-par site change because we didn't plan for non-animated versions of the design.

Prefers Reduced Motion

How can we combat the powers of user OS and browser settings? A little thing called prefers-reduced-motion. Prefers reduced motion is a media query that's been gaining support steadily over the past couple years, and is what we can utilize to honor user preference for motion while maintaining control of how that happens. By using prefers-reduced-motion: reduced, we can have control over how animation is disabled for this user group, along with honoring the user OS setting and not requiring a browser extension.

Usage In Stylesheets

When implementing this media query in our styles, it's better to add animations via the media query being set to no-preference, rather than having the animation be the default and overriding it within a media query set to reduce. This not only usually results in fewer lines of code and better performance, it also gets us in the mindset that we are meant to build something of quality that works for everyone, and animations are nice to have for those users who don't care about animation, rather than feeling like we're forced to pull back on site design and animations to serve a smaller group of people. Here's a basic example.

// bouncing animation
@keyframes bounceUpDown {
  0% {
    position: relative;
    top: 0;
  }
  50% {
    position: relative;
    top: -3px;
  }
  100% {
    position: relative;
    top: 0;
  }
}

// animation applied to class within prefers reduced motion media query of "no-preference"
@media (prefers-reduced-motion: no-preference) {
  .element-class {
    animation-name: bounceUpDown 1s infinite;
  }
}

There are a several different ways we can utilize this media query, but we're going to outline two of them here, one for background video and one for animated GIFs.

Usage in Javascript

You can also look for the prefers-reduced-motion setting in javascript to set conditional behaviors. Background video is a common design choice and desire for organizations to increase visual interest for their websites above the fold. Unfortunately, it's common practice to have these autoplaying on a loop, with no way to pause or disable them. We crafted a solution that allows users to pause the video, along with using a static image in cases where the user prefers reduced motion.

First, we have our background video banner HTML, which sets up our video controls, poster image and background video, from Vimeo in this example:

<div class="banner-video banner-background">
  <video loop="" muted="" id="banner-video-source" poster="/img/video-banner-img.png" style="background-image: url('/img/video-banner-img.png');">
    <source src="https://player.vimeo.com/external/[VIDEO-ID].hd.mp4?s=[]&amp;amp;profile_id=175" type="video/mp4">
  </video>
</div>

<div class="banner-video-controls">
  <button aria-label="Play" id="play-button" class="hide-button">
    <span>
      <i aria-hidden="true" class="fas fa-play"></i>
    </span>
    <span class="text">
      Play</span>
  </button>
  <button aria-label="Pause" id="pause-button">
    <span>
      <i aria-hidden="true" class="fas fa-pause"></i>
    </span>
    <span class="text">
      Pause</span>
  </button>
</div>

Here's the JS that builds out our play/pause button functionality and determines whether to display the static or video banner based on motion settings. We're utilizing window.matchMedia to look for prefers-reduced-motion to set our conditions:

Drupal.behaviors.videoBanner = {
    attach: function (context) {
      $(context).find('#banner-video-source').once('videoBanner').each(function () {
        var video = document.getElementById('banner-video-source');
        var playButton = document.getElementById('play-button');
        var pauseButton = document.getElementById('pause-button');

        if (!(window.matchMedia('(prefers-reduced-motion)').matches)) {
          video.play();
          playButton.classList.add('hide-button');
        }
        else {
          pauseButton.classList.add('hide-button');
        }

        playButton.addEventListener('click', function () {
          if (video.paused === true) {
            video.play();
            // hide and show/enable/disable of buttons with focus change
            playButton.classList.add('hide-button');
            pauseButton.classList.remove('hide-button');
            pauseButton.focus();
          }
        });
        pauseButton.addEventListener('click', function () {
          if (video.paused === false) {
            video.pause();
            // hide and show/enable/disable of buttons with focus change
            pauseButton.classList.add('hide-button');
            playButton.classList.remove('hide-button');
            playButton.focus();
          }
        });
      });
    }
  };

With this in place, a user that prefers reduced motion will only see the static image and never need to bother pausing the video at all. For those that have no preference, they will at least have the ability to pause the video if need be.

Usage in HTML

We're also able to look at this setting within a <picture> element and present what we want based on user preference. Take a look at this example: 

<picture>
          <source srcset="/images/animated-img.gif" media="(prefers-reduced-motion: no-preference)"></source>
          <img srcset="/images/static-img.png" alt="accurate, concise description of image contents">
</picture>

Here you can see that the default is actually the static image, mentioned above as being the best way set animated vs. non-animated content. Within Drupal, we can have a media type of GIF that allows for a GIF upload and a jpg/png upload, and format the media display in a twig template to resemble this picture element.

Takeaway

Whether you're designing a site or building it out from mockups provided by a third party, consider both static and animated versions of the content. Will it look good both ways? Does the static version stand on its own as a polished, inclusive layout, or does it pale in comparison to the animated version? Make sure you have a solid static version, then add the animations as the cherry on top for those that have no preference for motion. This mindset also moves us in the direction of inclusivity for everyone, rather than looking at static content as unwanted concessions necessary for certain user groups.