Tips on making a web site accessible part 2

Karen Payne - Apr 24 '23 - - Dev Community

Using the CSS reduce-motion query to prevent motion

Some users experience distraction or nausea from animated content.

Note
The impact of animation on people with vestibular disorders can be quite severe. Triggered reactions include nausea, migraine headaches, and potentially needing bed rest to recover.

Vestibular Disorder

  • People with vestibular disorders need control over movement triggered by interactions. Non-essential movement can trigger vestibular disorder reactions. Vestibular (inner ear) disorder reactions include distraction, dizziness, headaches and nausea.
  • Persona Quote: "Stop that extra movement! You are making me so dizzy I cannot concentrate. Now I have to turn off my computer and go lie down."

Information above is from WCAG 2.1 Techniques

Dealing with reduce-motion

  • Assume the visitor has set prefers-reduced-motion in the operating system. This is done with the Index page so if the visitor is affected they may suffer.
  • Be pro-active, in Index1 page code asserts for prefers-reduced-motion and if set displays an image rather than a animated image which provides the same message as Index page.
  • Provide a method for the user to set prefers-reduced-motion, in this case in local storage which is done in SettingsPage and used in Index2 page.

From wwwroot/js/Application.js, provides the following.

Method Description
setMotion store user reduce motion perference in local storage
getMotion retrieve users reduce setting perference from local storage
supportsLocalStorage determines if we can use local storage
prefersReducedMotion determines reduce motion is set locally
var $Application = $Application || {};
$Application = function () {
    const motionKey = 'useReduceMotion';
    var supportsLocalStorage = function () {
        return typeof (Storage) !== "undefined";
    };
    var setMotion = function (value) {
        if (!supportsLocalStorage()) {
            return false;
        }
        localStorage.setItem(motionKey, value);
        return true;
    };

    var getMotion = function () {
        if (localStorage.getItem(motionKey) === null) {
            localStorage.setItem(motionKey, false);
        }

        return localStorage.getItem(motionKey).toLowerCase() === 'true';

    };

    var prefersReducedMotion = function () {
        const results = window.matchMedia('(prefers-reduced-motion: reduce)');
        // Check if the media query matches or is not available.
        return !results || results.matches;
    }

    return {
        setMotion: setMotion,
        getMotion: getMotion,
        prefersReducedMotion: prefersReducedMotion
    };
}();
Enter fullscreen mode Exit fullscreen mode

Add the script file to your project.

See SettingsPage.cshtml for storing reduce-motion to local storage.

<div class="container" id="main-content">
    <div class="form-check form-switch">
        <input class="form-check-input" type="checkbox" id="useMotion">
        <label class="form-check-label" for="useMotion">Use motion</label>
    </div>
</div>

@section Scripts
{
    <script>
        const identifier = 'useMotion';
        document.addEventListener("DOMContentLoaded", () => {
            document.getElementById(identifier).checked = $Application.getMotion();
        });

        document.getElementById(identifier).addEventListener("click", function () {
            $Application.setMotion(document.getElementById(identifier).checked);
        });
    </script>
}
Enter fullscreen mode Exit fullscreen mode

Index2

Assert setting in local storage, if $Application.getMotion() returns true, use an image rather than animation else if false if return show animation.

<div class="position-absolute top-50 start-50 translate-middle">
    <div class="animation1">Sale today</div>
    <div id="noAnimate" style="display: none">
        <div class="card" style="width: 21rem;">
            <img src="images/none.png" class="card-img-top">
            <div class="card-body">
                <h5 class="card-title">Information</h5>
                <p class="card-text">Detected <mark>prefers-reduced-motion: reduce</mark></p>
            </div>
        </div>        
    </div>
</div>

@section Scripts
 {
    <script>
        document.addEventListener("DOMContentLoaded", () => {
            if ($Application.getMotion()) {
                document.getElementById('noAnimate').style.display = 'inline';
                document.getElementsByClassName('animation1')[0].style.display = 'none';
            }
        });

    </script>
}
Enter fullscreen mode Exit fullscreen mode

Source code

Clone the following GitHub repository then use ReduceMotionExample project to try out what has been presented.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .