Accessible Sites With Less Motion
Some people dislike having decorative animations and motions in the screen. Such animations might trigger discomfort for those people. To tackle this issue operating system allow users to set reduced motion on their devices. For exmaple on Windows 10 we can turn off in settings - ‘show animations in windows’. Or in IOS we can go to motion settings and toggle the ‘Reduce Motion’ Switch.
So how we respect this option in the browser?
We can use the prefers-reduce-motion css media query to detect if a user has enabled reduced motion setting.
@media (prefers-reduced-motion) {
// rest of css...
}Lets see an example. If you turn reduced motion on you should see a different animation with no movement.
If you are using Chrome your can turn this setting on using devtools Ctrl+Shift+P and search
Emulate CSS prefer-reduced-motion.
.button {
background-color: blueviolet;
color: white;
border: none;
font-size: 1.5rem;
width: 200px;
height: 60px;
border-radius: 8px;
animation: pulse 1s linear infinite both;
}
@keyframes pulse {
0% {
transform: scale(1);
}
25% {
transform: scale(1.1);
}
50% {
transform: scale(1);
}
75% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
@keyframes transparency {
0% {
opacity: 1;
}
50% {
opacity: 0.2;
}
100% {
opacity: 1;
}
}
@media (prefers-reduced-motion) {
.button {
animation: transparency 3s linear infinite both;
}
}But wait what about Javascript Animations?
We can use the window.matchMedia method to detect it.
Let’s see Another Example.
document.addEventListener('DOMContentLoaded', () => {
const button = document.getElementById('button') as HTMLButtonElement;
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
let animation: Animation | null = null;
function animateButton() {
animation = button.animate(
[
{ transform: 'scale(1)' },
{ transform: 'scale(0.8)'},
{ transform: 'scale(1)' }
],
{
duration: 1000,
easing: 'ease-in-out',
fill: 'both',
iterations: Infinity
}
);
}
if (!mediaQuery.matches) {
animateButton();
}
mediaQuery.addEventListener('change', (e) => {
if (mediaQuery.matches) {
animation?.cancel()
animation = null;
}
else {
animateButton()
}
})
})Sometimes canceling the animation might no give you the expected result you want, for example you may want the element to be in the end state of the animation. In this case instead of canceling the animation you could set the animation duration to be zero.