आधुनिक वेबसाइट्स अधिक भव्य व्हिज्युअल्सद्वारे लक्ष वेधून घेण्याचा प्रयत्न करतात. पॅरलॅक्स हिरोज (Parallax heroes) व्ह्यूपोर्टमध्ये खोलवर पसरलेले असतात. कॅरोसेल्स (Carousels) सतत स्क्रोल होत राहतात. साइनअप फॉर्मच्या मागे बॅकग्राउंड व्हिडिओ आपोआप प्ले होतात. डिझाइन पोर्टफोलिओमध्ये हे पॅटर्न पॉलिश आणि आकर्षक वाटतात. परंतु, तुमच्या प्रेक्षकांच्या एका भागासाठी, हे एक शारीरिक त्रासदायक घटक (physical trigger) ठरू शकतात.

वेस्टिबुलर डिसऑर्डर (vestibular disorders) असलेल्या लोकांना सततच्या हालचालींमुळे मळमळणे, चक्कर येणे किंवा मायग्रेनचा त्रास होऊ शकतो. त्यांचे संरक्षण करण्यासाठी, आता प्रत्येक प्रमुख ऑपरेटिंग सिस्टममध्ये "Reduce Motion" ही ॲक्सेसिबिलिटी सेटिंग समाविष्ट आहे. वेब ब्राउझर्स prefers-reduced-motion मीडिया क्वेरीद्वारे ही पसंती दर्शवतात. जे React डेव्हलपर्स हे व्यवस्थित राबवू इच्छितात, ते @reactuses/core मधील useReducedMotion हुक वापरू शकतात. हा हुक OS ची पसंती एका boolean मध्ये वाचतो, वापरकर्त्याने बदल केल्यास तो रिअल-टाइममध्ये अपडेट होतो आणि सर्व्हर-साइड रेंडरिंग (SSR) दरम्यान क्रॅश न होता हाताळतो.

स्वतः तयार केलेले उपाय (Handmade Solutions) का अपयशी ठरतात

तुम्ही window.matchMedia('(prefers-reduced-motion: reduce)') वापरून स्वतः ही पसंती तपासू शकता. अनेक डेव्हलपर्स असेच करतात, आणि यामुळे अनेकदा सूक्ष्म बग्स (subtle bugs) निर्माण होतात.

पहिले म्हणजे, तुम्ही 'change listener' विसरू शकता. जेव्हा कंपोनंट माउंट (mount) होतो, तेव्हा सुरुवातीची क्वेरी एकदाच चालते. जर वापरकर्त्याने तुमचे ॲप उघडले आणि कॅरोसेलमुळे त्यांना त्रास होत असल्याने सिस्टम सेटिंग्जमध्ये 'Reduce Motion' चालू केले, तर तुमच्या कंपोनंटला त्या अपडेटची माहिती मिळत नाही. मीडिया क्वेरी ऑब्जेक्ट addEventListener ला सपोर्ट करतो, परंतु ते योग्यरित्या जोडणे, अनमाउंट (unmount) करताना ते काढून टाकणे आणि जुन्या addListener सिंटॅक्सला पॉलीफिल (polyfill) करणे ही अशी प्रक्रिया आहे जिथे कॉपी-पेस्टच्या चुका होण्याची शक्यता जास्त असते.

दुसरे म्हणजे, SSR दरम्यान window अस्तित्वात नसते. जर तुमचे ॲप सर्व्हरवर रेंडर होत असेल, तर साधी matchMedia कॉल केल्यास 'reference error' येतो आणि रेंडरिंग थांबते. परिणामी, तुम्हाला typeof window !== 'undefined' सारख्या गार्ड्सचा वापर करावा लागतो, डिफॉल्ट व्हॅल्यूचा अंदाज घ्यावा लागतो आणि क्लायंट हायड्रेशन (client hydration) मॅच होईल अशी आशा करावी लागते.

तिसरे म्हणजे, प्रत्येक ॲनिमेटेड कंपोनंटमध्ये ही लॉजिक पुन्हा पुन्हा लिहिणे हे मेंटेनन्सचे ओझे (maintenance debt) वाढवते. एखादा टीममेट लिसनर एका पद्धतीने लिहितो, दुसरा तो पूर्णपणे वगळतो, आणि तिसरा मोशनला प्राधान्य देणारा डिफॉल्ट कोड हार्ड-कोड करतो. एक 'hook' या गोंधळाला एका ठिकाणी केंद्रित करतो.

useReducedMotion या तिन्ही समस्या सोडवते. हे एक साधे boolean रिटर्न करते. हे सर्व्हरवर सुरक्षितपणे 'no-op' राहते. हे लिसनर आपोआप जोडते आणि काढून टाकते.

याचा वापर करण्याचे तीन मार्ग

एकदा का तुमच्याकडे boolean मिळाले की, त्यावर आधारित कृती करण्यासाठी तुम्हाला एका रणनीतीची गरज असते. येथे तीन पॅटर्न दिले आहेत जे एका सिंगल कंपोनंटपासून संपूर्ण ॲप्लिकेशनपर्यंत वापरता येतात.

1. अनावश्यक काम टाळण्यासाठी CSS क्लासेस नियंत्रित करा

सर्वात थेट मार्ग म्हणजे जड JavaScript ॲनिमेशन लूप्स सुरूच होऊ न देणे. समजा तुमच्याकडे एक पॅरलॅक्स इमेज कंपोनंट आहे जो सामान्यतः स्क्रोल इव्हेंट्सवर लक्ष ठेवतो आणि requestAnimationFrame लूपमध्ये लेयर्स ट्रान्सलेट करतो. ती लॉजिक चालवून नंतर व्हिज्युअल रिझल्ट थांबवण्याऐवजी, प्रथम useReducedMotion तपासा.

जर हुक true रिटर्न करत असेल, तर कंपोनंटला स्टॅटिक व्हेरिएंट क्लाससह रेंडर करा आणि स्क्रोल लिसनर्स बांधणारा useEffect वगळा. यामुळे ब्राउझरला कमी काम करावे लागते. ज्या वापरकर्त्यांना हालचालींची संवेदनशीलता आहे त्यांना स्थिर प्रतिमा दिसते, तर इतरांना हलणारे लेयर्स दिसतात. ॲनिमेशन कोड कधी सुरूच होत नसल्यामुळे, तुम्ही CPU आणि बॅटरीची बचत देखील करता.

2. थेट ॲनिमेशन लायब्ररीजमध्ये वापरा

जर तुम्ही Framer Motion सारखी लायब्ररी वापरत असाल, तर हा हुक तुमच्या 'prop definitions' मध्ये थेट वापरता येतो. मोशन कंपोनंट्स व्हेरिएंट्स, ट्रान्झिशन्स आणि जेस्चर्ससाठी कॉन्फिगरेशन ऑब्जेक्ट्स स्वीकारतात. तुम्ही useReducedMotion मधून मिळालेल्या boolean चा वापर करून ट्रान्झिशन्सवर कंडिशनल duration: 0 सेट करू शकता, किंवा