Christian Heilmann

A CSS only “click to animate gif” solution

Thursday, July 16th, 2020 at 9:23 pm

Here’s a quick experiment in pure CSS how to cover a GIF with a play button instead of playing it. You can see it in this codepen or also play with it on GitHub. The GitHub code repository is also available.


See the Pen
CSS only "click to show GIF"
by Christian Heilmann (@codepo8)
on CodePen.


Animated GIFs are fun, but they can also be annoying as hell. To some people, they can even be a health issue. They can distract people, confuse them when reading is a problem to start with. They can even cause nausea and – in the worst case scenario – seizures.

On a more technical, but also inclusivity and accessibility issue, they also tend to be huge in file size. And – unlike videos – they don’t stream.

That’s why Operating Systems come with a way to tell software that the user doesn’t want to see any animations. On MacOS, for example, you can enable the “Reduce motion” settings and apps will not swoosh and pop any longer.

MacOS Settings with reduced motion enabled

On the web, this triggers a media query you can react to called prefers-reduced-motion.

Now, the best way to do this has been discussed by Brad Frost and Chris Coyier in detail back in June 2019. Using the picture element and a media attribute on the source element makes sure that the browser shows a fallback and doesn’t even load the big GIF. A simple way to have a “click to play” is also to use a video element and an MP4 instead of GIFs. This sounds more complex, but it does result in smaller files and the user has more control. Also, videos stream, GIFs do not.

Anyways, back to my little experiment here. I wanted to have a GIF covered by a play button and to show the GIF when the user clicks or tabs to it and presses space. And I didn’t want to use any JavaScript. So here is what I came up with:

The HTML is a label surrounding a checkbox and an image

<label class="click-to-gif" title="click/hit space to show gif">
<input type="checkbox">
<img src="https://media.giphy.com/media/XV74ZvGRXcZdS/source.gif"
alt="Captain America saying he can do this all day - animated"
width="500" height="180">
</label>

This gives me a few things for free:

  • The checkbox is keyboard enabled – you can tab to it and check/uncheck it by pressing space
  • The label means that you can click anywhere to check/uncheck the checkbox

The first bit is to make the image display as a block to avoid gaps and to hide the checkbox by positioning it off-screen:

.click-to-gif img {display: block;}
 
.click-to-gif input[type=checkbox] {
  position: absolute;
  left: -100vw;
}

We set the label to block to enable the user to click anywhere and we float it to the left to wrap it around the whole image.
We set a background colour and an inline SVG (this is the play icon, abbreviated here).

label.click-to-gif {
  display: block;
  float: left;
  background: DimGrey no-repeat center center;
  background-image: url('data:image/svg+xml;
   ... 
  fill="LightGrey"/>') ;
}

On hover or focus-within, we change the colours to tell the user that this is interactive. Focus-within is a great addition to CSS as it triggers a change on a parent element when any child element gets focus. That way we can change the look of the label when the checkbox state changes.

.click-to-gif:hover, .click-to-gif:focus-within {
  background: DarkSlateGrey no-repeat center center;
  background-image: url('data:image/svg+xml; 
  ...    
  fill="MediumSeaGreen"/></svg>');
}

When the checkbox isn’t checked, we hide the image by setting its opacity to 0. When the checkbox is checked, we show it. As the above design is all happening in the background, the image will then cover it.

.click-to-gif input[type=checkbox] + img {
  opacity: 0;
}
.click-to-gif input[type=checkbox]:checked + img {
  opacity: 1;
}

And that’s all. If you don’t want this functionality to be the default, but only apply when the user has set his operating system to reduced motion, you can wrap the CSS in a media query:

.click-to-gif img {display: block;}
.click-to-gif input[type=checkbox] {
  position: absolute;
  left: -100vw;
}
 
@media (prefers-reduced-motion: reduce) {
  label.click-to-gif {
    display: block;
    float: left;
    background: DimGrey no-repeat center center;
    background-image: url('data:image/svg+xml;
    ... 
    fill="LightGrey"/>') ;
  }
  .click-to-gif:hover, .click-to-gif:focus-within {
    background: DarkSlateGrey no-repeat center center;
    background-image: url('data:image/svg+xml; 
    ...    
    fill="MediumSeaGreen"/></svg>');
  }
  .click-to-gif input[type=checkbox] + img {
    opacity: 0;
  }
  .click-to-gif input[type=checkbox]:checked + img {
    opacity: 1;
  }
}

If you want to test this without resetting your OS the whole time, you can use the Reduced Motion Simulation in Chromium Devtools as shown in the following video.

To toggle the reduced motion setting, press CMD/Ctrl+Shift+P and type “motion”. You can also find the setting as a drop-down in the rendering pane.

Share on Mastodon (needs instance)

Share on Twitter

My other work: