Respect reduced motion preference for animated GIFs in WordPress
Tags: wordpress, a11y, css, php
Published: 2023-10-18 16:48:08

We should respect reduced motion preferences on every website, but while we have full control of a custom website, WordPress projects often use existing plugins for image optimization. They can optimize everything ... except for animated GIFs.

<img src="https://media.tenor.com/2jvzfQQxDFcAAAAC/think-smart.gif" alt="animated smart gesture meme pointing a finger at the head" width="220" height="142">

DEV adds a stop button to this animated GIF, but WordPress doesn't.

## WordPress + plugins < DEV

Using WordPress plugins can save time and possible errors compared to writing custom code from scratch. Website owners can use the existing UI to upload arbitrary pictures, crop and resize, let the plugin generate optimized files in the background and enjoy what-you-see-is-what-you-get editing.

A WordPress plugin like [TinyPNG](https://wordpress.org/plugins/tiny-compress-images/) will add markup to offer alternative image versions like **webp vs. jpg**. But popular image optimizing plugins **don't care about GIF** ... yet? The GIF format [should have been replaced by PNG](https://www.w3.org/QA/Tips/png-gif) [or webp](https://web.dev/articles/serve-images-webp) for years, but there are still a lot of animated GIFs around, causing different kinds of problems beyond annoying some of your website visitors.

## Why does it matter?

- file size: an animated GIF can easily be several MB
- no respect for reduced motion preference
- missing stop option

**Most popular GIF animations play as endless loops, although they don't need to.** There are possible options to stop it after playing the animation.

Current web browsers don't offer options to pause or prevent animations, at least not without special plugins. Users can set a reduced motion preference option, but that doesn't change much until we actively care about reduced motion in our website setup.

WordPress has been quite strict and critical about SVG uploads by default, but it allows users to upload GIF images without showing any warning at all.

## Accessibility: "Dizzy Users Close Browsers"

[Dizzy Users Close Browsers](https://beyondtellerrand.com/events/berlin-2023/speakers/piccia-neri#talk), so we better respect the [pause, stop, or hide principle](https://www.w3.org/WAI/WCAG21/Understanding/pause-stop-hide.html).

{% youtube https://www.youtube.com/watch?v=KYnvaOxEsN0 %}

In this talk, [Piccia Neri](https://piccianeri.com/) shares how she enjoyed GIF animations but found out they made her feel dizzy, and how we can improve our websites' accessibility without giving up playful details in web design. (That's just one topic of her talk, so take your time to watch it, if you didn't see her at [beyond Tellerrand conference in Berlin](https://beyondtellerrand.com/events/berlin-2023/) this year).

## Frontend markup, backend logic, or both

We could use a [source set](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/srcset) to offer an alternative still image version without animation and modern, more efficient, animation formats. 

### Accept and vary on the server side

If we want to offer different image formats depending on browser support, we can add backend logic to send different images based on the [Accept HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept) and adding a `vary: accept` header to our response.

Here is a switch for GIF vs. (animated) webp based on browser capabilities. We can request this script instead of the original image.

```php
<?php // image.php
header('Status: 200 OK');
header('Vary: Accept');
$request_headers = getallheaders();
if (
  (request_headers["Accept"] &&
   is_string($request_headers["Accept"]) &&
   str_contains($request_headers["Accept"], 'webp')
  ) || (
    $request_headers["accept"] && 
  is_string($request_headers["accept"]) &&
  str_contains($request_headers["accept"], 'webp')
 )
) {
  serve_webp();
} else {
  serve_gif();
}
```

```html
<img src="image.php">
```

### Picture / Source / Image

Without adding backend code, we can add more frontend markup wrapping a [picture element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture) around our image markup. CSS-tricks shared a good example and explanation how to use [GIFS and prefers-reduced-motion](https://css-tricks.com/gifs-and-prefers-reduced-motion/):

```html
<picture>
  <source srcset="img/waterfall.avif" type="image/avif">
  <source srcset="img/waterfall.webp" type="image/webp"> 
  <img src="img/waterfall.jpg" alt="a waterfall">
</picture>
```

Currently, we can't generate the aforementioned markup without generating an alternative image that does not move. There is no option for website owners to upload alternative versions of one media item yet.

### Best practice for a default WordPress setup?

WordPress should automatically generate an alternative that takes the first frame of the animation and exports it as a static image. But it doesn't.

I opened a support issue to [add static fallback for animated images if a user prefers reduced motion](https://wordpress.org/support/topic/add-static-fallback-for-animated-images-if-user-prefers-reduced-motion/) in [TinyPNG](https://wordpress.org/plugins/tiny-compress-images/).

As a workaround of a workaround, I could use `add_filter( 'post_thumbnail_html'` and add such a CSS class name manually. To do it properly, I would have to open each image file stream and check each GIF, webp or avif for an animation signature. More pragmatically, I currently check for a `.gif` string anywhere inside the thumbnail section which of only works because TinyPNG leaves animated GIFs untouched and does not make animated webp’s out of theme.

As a more pragmatic, quick and simple workaround, I decided to hide the GIF for users that prefer reduced motion. No image, no animation.

![Can’t have animated images if there are no images meme.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ad1juxhpstiwrccs4l2k.png)

You can’t have animated images if there are no images.

Or in CSS:

```css
@media (prefers-reduced-motion) {
  .has-gif img {
    visibility: hidden;
  }
}
```

I needed to add the `has-gif` class in our markup.

```php
<figure class="news__post__image<?PHP 
if (str_contains(get_the_post_thumbnail(),'.gif"')) { 
 echo ' news__post__image--has-gif'; 
} ?>">
  <?php the_post_thumbnail() ?>
```

## What did I do here?

I added a [figure element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure) and called [the_post_thumbnail](https://developer.wordpress.org/reference/functions/the_post_thumbnail/) function to let WordPress core and the image optimization plugins do their work.

## Conclusion

Another pragmatic solution for a problem that shouldn't exist in the first place. GIFs are bad! But sometimes they're fun. That's why people use them.

That's why we need to deal with reality and still make our websites work properly regarding accessibility and performance optimization.