Współczesne strony internetowe walczą o uwagę coraz bardziej spektakularną oprawą wizualną. Sekcje hero z efektem paralaksy rozciągają się głęboko w polu widzenia. Karuzele przewijają się w nieskończoność. Wideo w tle automatycznie odtwarza się za formularzami zapisu. W portfolio projektanta takie wzorce wyglądają dopracowanie i angażująco. Dla części Twoich odbiorców są one jednak fizycznym wyzwalaczem.

Osoby z zaburzeniami błędnika doświadczają nudności, zawrotów głowy lub migren w kontakcie z dużymi obszarami ciągłego ruchu. Aby ich chronić, każdy główny system operacyjny zawiera obecnie ustawienie dostępności „Reduce Motion”. Przeglądarki internetowe udostępniają tę preferencję poprzez media query prefers-reduced-motion. Programiści React, którzy chcą w czysty sposób ją obsłużyć, mogą skorzystać z hooka useReducedMotion z biblioteki @reactuses/core. Odczytuje on preferencję systemu operacyjnego do wartości typu boolean, aktualizuje ją na żywo, jeśli użytkownik zmieni zdanie, i obsługuje renderowanie po stronie serwera bez powodowania błędów.

Dlaczego własnoręczne rozwiązania zawodzą

Możesz samodzielnie zapytać o tę preferencję za pomocą window.matchMedia('(prefers-reduced-motion: reduce)'). Wielu programistów tak robi, co często prowadzi do subtelnych błędów.

Po pierwsze, łatwo zapomnieć o nasłuchiwaniu zmian (change listener). Początkowe zapytanie wykonuje się tylko raz w momencie montowania komponentu. Jeśli użytkownik otworzy Twoją aplikację, a następnie przełączy opcję „Reduce Motion” w ustawieniach systemu, ponieważ karuzela wywołuje u niego mdłości, Twój komponent nigdy nie otrzyma aktualizacji. Obiekt media query obsługuje addEventListener, ale poprawne podpięcie go, usunięcie przy odmontowywaniu (unmount) oraz tworzenie polyfilli dla przestarzałej składni addListener to powtarzalny kod (boilerplate), który sprzyja błędom typu „kopiuj-wklej”.

Po drugie, window nie istnieje podczas SSR. Jeśli Twoja aplikacja renderuje się na serwerze, naiwne wywołanie matchMedia wyrzuci błąd referencji (reference error) i przerwie renderowanie. Kończysz wtedy owijając sprawdzenie w warunki typeof window !== 'undefined', zgadując wartość domyślną i licząc na to, że proces hydratacji po stronie klienta przebiegnie pomyślnie.

Po trzecie, powtarzanie tej logiki w każdym animowanym komponencie zamienia się w dług technologiczny. Jeden współpracownik napisze nasłuchiwanie w jeden sposób, inny całkowicie je pominie, a trzeci wpisze na sztywno domyślną wartość sprzyjającą ruchowi. Hook pozwala scentralizować ten chaos.

useReducedMotion rozwiązuje wszystkie trzy problemy. Zwraca prostą wartość boolean. Bezpiecznie wykonuje operację typu no-op na serwerze. Automatycznie podłącza i czyści nasłuchiwanie.

Trzy sposoby na wdrożenie tego w praktyce

Gdy masz już wartość boolean, potrzebujesz strategii działania na jej podstawie. Oto trzy wzorce, które można skalować od pojedynczego komponentu aż po całą aplikację.

1. Kontroluj klasy CSS, aby uniknąć zbędnej pracy

Najbardziej bezpośrednim podejściem jest zapobieganie uruchamianiu ciężkich pętli animacji JavaScript. Wyobraź sobie, że masz komponent obrazu z efektem paralaksy, który normalnie nasłuchuje zdarzeń przewijania (scroll) i przesuwa warstwy wewnątrz pętli requestAnimationFrame. Zamiast uruchamiać tę logikę, a następnie tłumić efekt wizualny, najpierw sprawdź useReducedMotion.

Jeśli hook zwróci true, wyrenderuj komponent z klasą wariantu statycznego i pomiń useEffect, który wiąże nasłuchiwanie zdarzeń przewijania. Przeglądarka wykonuje mniej pracy. Użytkownicy wrażliwi na ruch widzą nieruchomy obraz. Wszyscy inni widzą przesuwające się warstwy. Ponieważ kod animacji nigdy się nie inicjalizuje, oszczędzasz zasoby procesora i baterię.

2. Przekazuj go bezpośrednio do bibliotek animacji

Jeśli używasz biblioteki takiej jak Framer Motion, hook można bezpośrednio wpiąć w definicje Twoich propsów. Komponenty Motion przyjmują obiekty konfiguracyjne dla wariantów, przejść (transitions) i gestów. Możesz użyć wartości boolean z useReducedMotion, aby warunkowo ustawić duration: 0 w przejściach, lub