Skip to main content

Accessible Animated GIF Alternatives

By Tyler Sticka

Published on July 21st, 2023

Topics
A robotic dinosaur labeled 'GIF', with a play button representing playback control, and a word balloon representing alternative text

In my previous article, I explored a few different alternatives to the animated GIF with regard to performance, compatibility and feature parity. I found many promising formats, but one big potential hiccup: The two strongest contenders required separate HTML elements, img for AVIF or video for WebM. This raises a new question: Which HTML element is more accessible for the display of GIF-like animated clips?

For the purposes of this article, I’m defining a “GIF-like animated clip” as a short, silent, non-vector video or animated image file that plays inline with content. They often loop and usually play automatically.

True accessibility depends on real people interacting with real experiences. But as an imperfect substitute, we can base our requirements on portions of the Web Content Accessibility Guidelines (WCAG). Specifically, the element we choose should…

  1. Allow the viewer to control its playback
  2. Provide a text alternative
  3. Prevent motion where preferred

Let’s step through each requirement to build our accessible GIF-like animation pattern.

For any moving, blinking or scrolling information that (1) starts automatically, (2) lasts more than five seconds, and (3) is presented in parallel with other content, there is a mechanism for the user to pause, stop, or hide it unless the movement, blinking, or scrolling is part of an activity where it is essential

Understanding SC 2.2.2: Pause, Stop, Hide (Level A)

Visitors may control the playback of video elements via a context menu or visible controls when the controls attribute is present:

<video controls autoplay loop muted playsinline
  src="clip.mp4"></video>Code language: HTML, XML (xml)

The img element does not support playback controls. There are JavaScript libraries that attempt to remedy this, but they work by replacing the img with a canvas, which disqualifies them for the purposes of this article. iOS 17 and macOS 14 “Sonoma” add some native playback settings, but browser support is limited to Safari, and the UX is a little clunky.

Provide text alternatives for any non-text content so that it can be changed into other forms people need, such as large print, braille, speech, symbols or simpler language.

Understanding SC 1.1: Text Alternatives

We’ve already ruled out img for its lack of playback controls, but video lacks an alt attribute, and its support for captions won’t apply to silent videos. Thankfully, we can use aria-label instead (as long as the controls attribute is also present):

<video controls autoplay loop muted playsinline
  aria-label="text alternative goes here"
  src="clip.mp4"></video>
Code language: HTML, XML (xml)

But some visitors may rely on translation services, which sometimes overlook aria-label attributes. No problem, let’s use aria-labelledby and a separate descriptive element:

<video controls autoplay loop muted playsinline
  aria-labelledby="video-label"
  src="clip.mp4"></video>
<div id="video-label" aria-hidden="true">
  text alternative goes here
</div>
Code language: HTML, XML (xml)

Definitely not as simple as the img element’s alt attribute, but not too unreasonable.

Some users experience distraction or nausea from animated content. For example, if scrolling a page causes elements to move (other than the essential movement associated with scrolling) it can trigger vestibular disorders.

Technique C39: Using the CSS reduce-motion query to prevent motion

The simplest way to support reduced motion preferences is to include controls but not autoplay. The viewer may choose to initiate the motion whenever they like:

<video controls loop muted playsinline
  aria-labelledby="video-label"
  src="clip.mp4"></video>
<div id="video-label" aria-hidden="true">
  text alternative goes here
</div>
Code language: HTML, XML (xml)

If losing automatic playback is too large a departure from typical GIF behavior, we can use JavaScript to restore it based on the visitor’s preference:

// Save a reduced motion media query
const reduceMotionQuery = window.matchMedia(
  '(prefers-reduced-motion: reduce)'
);

// Loop through all video elements and toggle behavior
function gifLikeVideos(enable) {
  const videos = document.querySelectorAll(
    'video[loop][muted][playsinline]'
  );
  [...videos].forEach((element) => {
    if (enable) {
      element.play();
    } else {
      element.pause();
    }
  });
}

// Toggle all videos based on the media query state
function onMotionChange({matches} = {}) {
  gifLikeVideos(!matches);
}

// Listen for media query changes
reduceMotionQuery.addEventListener('change', onMotionChange);

// Set initial state
onMotionChange(reduceMotionQuery);Code language: JavaScript (javascript)

Here’s our final pattern in action (apologies again to Jason):

It fulfills all three of our initial criteria:

  1. Visitors can control its playback.
  2. There is a text alternative that isn’t obscured from translation services.
  3. It does not autoplay until the viewer’s reduced motion preference is confirmed, and responds dynamically to that preference.

While the video element has room for improvement, its native playback controls make video formats the most viable animated GIF alternatives when it comes to accessibility.

Big thanks to my teammates Scott Vandehey and Gerardo Rodriguez for their technical review, and to Adrian Roselli for pointing me toward a helpful discussion in the A11y Slack.