How we made animations in Typeform more accessible

Roberto Espejo
Typeform's Engineering Blog
5 min readApr 19, 2023
Photo by Ahmad Odeh on Unsplash

Introduction and motivation

Animation is a powerful tool that can positively affect user interface (UI) design. Animations can help guide the user through the UI by using motion and direction to bring the interface to life and give meaning to changes on the page. By using animations, designers can create more engaging and interactive interfaces that are easier to understand and use. Animations can also help to provide visual feedback to the user, such as indicating that a button has been clicked or that an action is being processed. Additionally, animations can make interfaces more memorable, improving the user experience. By using animations strategically, designers can create visually appealing and functional interfaces, making them more intuitive and enjoyable for users.

However, animations are a delicate thing. Not every device connecting to the internet can render animations, or at least not as smoothly as designed. Your user experience might suffer when running a low-end device or even a better device in low-power mode. Many things can also affect your animations: reader mode might disable your animations altogether, or the viewport might be zoomed in.

When zoomed in, if your animation is not using relative units, there’s a good chance that parts of the animation would be left out of the viewport. It leaves it incomplete and creates confusion for the user, who has some apparently random flickering bits on their screen.

Besides, websites with a lot of motion can impact the device battery or even cause more data to be used (e.g. auto-playing videos); Not everyone likes decorative animations or transitions. They can annoy or distract some users as they navigate a site. Some users might suffer from vestibular disorders and outright experience motion sickness or even seizures when faced with some of those animations: flashing ads, parallax scrolling, zooming effects, etc.

Reducing motion in the web

First, we must remember something when honoring the users’ reduced motion preferences. Reduce doesn’t mean remove. While we should try to reduce motion, we shouldn’t go crazy and remove all animations from our sites. Instead, we should slow down and simplify the animations so they are still visually appealing and accessible simultaneously. For example, a slide-up could be changed into a subtle fade-in as long as the component is still meaningful with a different animation. Of course — we need to keep the context in mind. We should consider styling for reduced motion on a case-by-case basis.

That’s why operating systems started enabling users with accessibility settings to specify a preference for reduced motion. The screenshot below displays the Accessibility settings panel in System Preferences in MacOS Monterey. This setting applies to all OS animations (changing desktops, apps starting in the dock…). Apps themselves can read from this setting and should honor it by disabling all the unnecessary animations when the user has explicitly requested reduced motion.

Reduce motion setting on Mac OS Monterey
Accessibility settings panel in System Preferences in MacOS Monterey

Here’s an example of how the desktop switching animation looks on Mac OS, with and without reduced motion turned on.

Screen recording displaying Mac OS’s animation with reduced motion turned off
With reduced motion setting off (default setting)
Screen recording displaying Mac OS’s animation with reduced motion turned on
With reduced motion enabled, notice how the large sliding screen is replaced with a subtle fade between the windows.

prefers-reduced-motion media query

In the browser, the user preference media query prefers-reduced-motion (part of the Media Queries Level 5 specification) detects if the user has set an operating system preference to minimize the amount of animation. Detecting this setting lets you design a motion-reduced variant of your site for users who have expressed this preference. It’s widely supported by all major browsers, but keep in mind that it also depends if the OS as to whether it is supported.

It can have two potential values: reduce or no-preference. The first indicates that the user has set the OS accessibility setting to indicate that all non-essential movement should be practically removed. The latter indicates that the user has not set this preference at an OS level.

The following CSS snippet shows how to apply our slideUp animation to an element only when the user has not turned on this setting.

@media (prefers-reduced-motion: no-preference) {
.my-element {
animation: slideUp 1200ms;
}
}

Again, this is just an example. Instead of no animation at all, you could apply a different, more subtle one or one with less motion.

Centralizing your logic

If you’re using components or a design system, you’ll likely have an Animation component that wraps the components you want to animate. Having a clear stance on how animation is used (or not) in your design system can help ensure your brand/site uses animation consistently while helping your team work faster. This would help you save many changes throughout your code whenever you want to implement or change anything regarding your animations.

If your application is using React, you can use hooks as a mechanism to share this logic. Here’s how you can make your components aware of the user motion preferences using the prefers-reduced-motion media query and a simple React hook.

import { useEffect, useState } from 'react'

const usePrefersReducedMotion = () => {
const [prefersReducedMotion, setPrefersReducedMotion] = useState(false)
useEffect(() => {
const mediaQueryList = window.matchMedia('(prefers-reduced-motion: no-preference)')
// Set the true initial value for the client
setPrefersReducedMotion(!window.matchMedia('(prefers-reduced-motion: no-preference)').matches)
// Register event listener
const handleChange = (event) => {
setPrefersReducedMotion(!event.matches)
}
// Safari < 14 support
try {
mediaQueryList.addEventListener('change', handleChange)
} catch {
mediaQueryList.addListener(handleChange)
}
return () => {
try {
mediaQueryList.removeEventListener('change', handleChange)
} catch {
mediaQueryList.removeListener(handleChange)
}
}
}, [])
return prefersReducedMotion
}

If you’re using class components, you can always wrap your components with a HOC using this hook and re-use the same logic.

So how does this look in Typeform?

We tweaked some animations to reduce motion and respect user settings. This is just an example; there is plenty more to find out around the product!

Reduced motion preference has not been set
The user prefers reduced motion. Notice subtle changes like the logo not sliding out while loading, and blocks are not sliding in and out of the screen when navigating different questions

Conclusions

Respecting user preferences is key to providing a great user experience on the web, especially for preferences related to accessibility. Browsers are exposing more and more features to enable web developers to do so. This means we now have more tools to thoughtfully approach how we design and implement animations and provide alternative versions for those with vestibular disorders.

Roberto Espejo

Expert Frontend Engineer @ Typeform

--

--

Roberto Espejo
Typeform's Engineering Blog
0 Followers

Expert Frontend Engineer (E4) @ Typeform