golden:webgl §7 · 4 checks
ما الذي ينبغي أن يفعله تقليل الحركة بواجهة canvas؟
جوابان يُشحنان كثيرًا وكلاهما خطأ. الأوّل يترك الواجهة تعمل، لأن استعلام وسائط في ملف أنماط لا يبلغ شيئًا يرسمه زمن تشغيل JavaScript داخل سياق معالج الرسوميات (GPU) — التفضيل مضبوط، والصفحة تبدو كما هي، والزائر الذي طلب حركة أقلّ يحصل عليها كاملة كما كانت. والثاني يزيل الواجهة، فيبادل صورة متحرّكة بفجوة، ويأخذ معه الانطباع الأوّل للصفحة كلّه.
الجواب هنا إطار واحد كامل، ثم لا شيء. تحت reduce تُرسَم الصورة الشخصية والظلّ النقطي (halftone) والوضعية المرساة جميعًا؛ ولا يُرسَم اختلاف المنظور بالمؤشّر ولا الأثر السائل خلفه؛ ولا يُحتفَظ بأي اشتراك في الإطارات. ويُقرَّر ذلك في شرطٍ واحد داخل زمن تشغيل الواجهة، حيث التفضيل واحد من أربعة أسباب مستقلّة للتوقّف، وفرع else هو ما يجعل النتيجة حالة مستقرّة لا حالة فارغة: ألغِ الاشتراك في أطوار الإطار، وأغلِق المؤشّر، ثم ارسم مرّة أخيرة إن كانت الواجهة ما تزال داخل بوّابتها.
وبقيّة هذه المقالة هي ذلك القرار معمَّمًا على تسعة أنظمة فرعية، والعيوب الثلاثة التي لزمت لضبطه، والأرقام التي تحت الأربعة أدناه.
- 61 فحص بوّابة يذكر التفضيل من أصل 817 عبر 17 كشفًا
- 7 لوحة canvas ما تزال تحمل صورة واجهة WebGL واحدة، وحقل نقاط واحد، و5 أسهم Rive
- 0 مشترك في الإطارات محفوظ عند كل واحدة من 12 محطّة تمرير
- 16 كتلة reduce في ملفات الأنماط موزّعة على 17 من أصل 42 ملفًا
src/styles/base/reset.css:38-49
مفتاح الإيقاف صحيح، وهو يبلغ أقلّ ممّا تظنّ
المقتطف أدناه مشحون هنا كما هو، في طبقة التتالي الأولى، ولا شيء في هذه المقالة حجّة ضدّه. هو الأرضية. وما يستحقّ أن يُعرَف هو لماذا يقع في الطبقة الأولى، وما الذي لا يستطيع بلوغه بالبرهان.
أسبقية طبقات التتالي تنعكس مع الإعلانات المهمّة: الإعلان العادي في طبقة لاحقة هو الذي يفوز، والإعلان المهمّ في طبقة سابقة هو الذي يفوز. وترتيب الطبقات هو reset ثم tokens ثم base ثم layout ثم components ثم sections ثم utilities ثم overrides — فهذه أقوى قاعدة مهمّة في الملف كلّه، ويؤكّد اختبار وحدة نصفَي ذلك: الطبقة صفر هي reset وفيها سطر المدّة، والطبقة الأخيرة لا تحمل أي إعلان مهمّ البتّة.
/* src/styles/base/reset.css — طبقة التتالي الأولى. */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
/* استثناء موثَّق: تفضيل المستخدم يعلو على التصميم */
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* For important declarations the layer order REVERSES: the declaration
in the earliest layer wins (CSS Cascade 5, section 6.1). Nothing in a
later layer can out-rank this at any specificity — and the correct
escape hatch is therefore a NORMAL declaration in the last layer.
Reaching for important there makes the rule weaker, not stronger. */
السطران اللذان لا يتعلّقان بالمدّة يحملان حِملًا. فـ0.01ms بدل 0s تُبقي حدثَي transitionend وanimationend، فتظلّ آلة الحالة التي تنتظر انتهاء انتقال تتقدّم؛ وanimation-iteration-count: 1 تمنع حركة معلَنة infinite من أن تصير ارتعاشًا سريعًا جدًّا عند 0.01ms، وهو نقيض ما طُلب. ويسجّل Chris Coyier، الذي شاع المقتطف على يده، في المنشور نفسه أنه لا يعمل إلا حيث تكون الحركة كلّها من CSS، وأنه مع التأثيرات التي تقودها JavaScript قد يجعل الحركات سريعة ومُدوِّخة.
ولوضع المفتاح أوّلًا كلفة صادقة، وهذا المستودع يسجّلها بدل أن يلتفّ حولها. طبقة التحميل مُواصَفة لتجيب shortened، عند 0.3 ث — وذلك غير قابل للبلوغ من أي طبقة لاحقة، لأن الإعلان العادي يخسر أمام المهمّ، والإعلان المهمّ في طبقة لاحقة أضعف منه بعد. فتحت reduce تكون العلامة مستقرّة لا مرسومة، والانبثاق لحظيّ. إنه تراجع متعمَّد عن المرجع، تُرك في مكانه لأن علوّ تفضيل المستخدم على التصميم هو النتيجة الصحيحة.
واستنتاج واحد مكانه هنا لا مكان قياس. ثلاث قواعد في طبقات لاحقة في هذا المستودع تضبط transition-duration: 0.01s تحت reduce، وبحكم قاعدة التتالي التي تذكرها الفقرة أعلاه، هي إعلانات عادية يهيمن عليها إعلان reset المهمّ. ولا تقرأ أي بوّابة مدّة انتقال محسوبة تحت reduce في أي مكان من العُدّة، فذلك يتبع المواصفة لا المتصفّح — وهو تحديدًا نوع القاعدة التي يراكمها مستودع ولا ينتبه أبدًا إلى أنها خاملة.
home-hero.css:329-353
المدّة هي الرافعة الخطأ — السؤال هو الإطار الذي تحطّ عليه
تحمل الواجهة رسمًا سلكيًّا يرسم نفسه ويمحو نفسه في دورة من 18 ثانية. اطوِ تلك الدورة إلى 0.01ms يحطّ كل خطّ على أي إطار مفتاحي صادف وجوده هناك، وهو في هذه الحركة الإطار الممحو: فالقاعدة الشاملة، مطبَّقة تطبيقًا صحيحًا وفاعلة تمامًا كما تقول، تترك لوحًا فارغًا. ولا شيء في المدّة قابل للتفاوض أيضًا، لأن ذلك الإعلان المهمّ هو الأقوى في الملف.
خاصّة واحدة تهزمها، واحدة لا غير. animation-name: none تُخرج الحركة من اللعب كلّيًّا، ولا تستطيع مدّة أن تُحيي حركة بلا اسم.
/* src/styles/sections/home-hero.css — the hero wireframe.
The reset layer would collapse the 18 s cycle to 0.01 ms and land
every stroke on the ERASED keyframe, leaving an empty board. */
@media (prefers-reduced-motion: reduce) {
.wfx-e {
animation-name: none;
stroke-dashoffset: 0;
}
.wfx-g2,
.wfx-g3,
.wfx-g4 {
display: none;
}
}
/* تُترَك مجموعة واحدة مرسومة بالكامل، وهذا ما وُجدت اللوحة من أجله. */
والفخّ نفسه يوقع إشعار الاتجاه، حيث تحرّك ثلاثة عناصر هاتفًا ينتصب وقوسًا يُرسَم خلفه. إطارها الأوّل هو الجهاز مضطجعًا بلا قوس — الوضعية الوحيدة التي لا تحمل معلومة — فتُوقِف كتلة reduce الثلاثة عند الحالة النهائية بدل ذلك: الهاتف منتصبًا، والعلامة منتصبة معه، والقوس مرسومًا بالكامل ورأسه مضاء. هذا هو end-state في مفردات الأجوبة الأربعة، والتعليل هو الحالة العامّة: المعلومة وجهة لا رحلة، فالقارئ الذي طلب حركة أقلّ يفقد الإيماءة ويحتفظ بالمعنى.
والحالة الثالثة أصغر وأسهل في الإفلات. مؤشّر الكتابة في لوحة الشيفرة يومض بقاعدة keyframes لا يطالب بها متحكّم الطباعة أبدًا، فمع سلسلة الطباعة التي لا تبدأ أصلًا كان الوميض ليصير الشيء الوحيد الذي ما يزال يتحرّك في الصفحة. ويُسكَت بالاسم في كتلة reduce الخاصّة بالقسم، للسبب نفسه الذي يُسكَت به الرسم السلكي.
motion-architecture.md:451-460
أربعة أجوبة، ولماذا يحتاج runs إلى تبرير مكتوب
التفضيل ليس قيمة منطقية، والمواصفة لا تصفه كذلك. تعرّفه Media Queries Level 5 بأنه كشف رغبة في حركة أقلّ على الصفحة، ويصف MDN واجب المؤلّف بأنه واجهة تُزيل الرسوم المتحرّكة القائمة على الحركة أو تقلّلها أو تستبدلها — ثلاث استجابات مسموحة لا واحدة. وهذا هو السند الخارجي لأي تصنيف، وهذا الموقع يعلن واحدًا من أربعة أجوبة لكل تأثير متحرّك.
| الجواب | ما الذي يعنيه | أين يُستعمل هنا |
|---|---|---|
| off | لا تبدأ أبدًا. يُرسَم العنصر في حالته النهائية المستقرّة | تحوّل أيقونة القائمة، وسحب الشريط باللمس، واللوحة التي تتعقّب المؤشّر |
| end-state | تُتخطّى الحركة ويُطبَّق إطارها الأخير فورًا | واجهة WebGL، وأسهم Rive الخمسة، وحقل النقاط، وكل ظهور دخول |
| shortened | تعمل، بمدّة مخفَّضة | مسمّاة لانبثاق التحميل، ومسجَّلة بأنها غير قابلة للبلوغ من طبقة لاحقة |
| runs | تعمل بلا تغيير، وتتطلّب تبريرًا مكتوبًا | مشهد واحد لا غير في الموقع كلّه: فصل لون body |
والإعلان ليس عُرفًا في المراجعة. لا يمكن تسجيل تأثير في زمن تشغيل الحركة بلا دالّة reset ودالّة dispose، ويُطلق التسجيل TypeError إن غابت إحداهما — والتأثير الذي جوابه الأمين لا شيء يكتب دالّة فارغة مع تعليق يقول لماذا. الصمت انهيار لا افتراض، وهذا ما يحوّل قاعدة معمارية إلى شيء يستطيع البناء أن يمسك به.
والـ runs الوحيد في الموقع تلاشٍ متقاطع للّون مربوط بموضع التمرير، وهو يستحقّ الاستثناء بقياس لا برأي: لا شيء ينزاح ولا يدور ولا يتغيّر حجمه، فلا حركة تُقلَّل. وقد قيس إطفاؤه فكسر الصفحة عند 390 بكسل، حيث يطوي الملف المخفَّض غلاف الحلقة إلى نافذة عرض واحدة، فيصير مصدر تقدّم الفصل صفرًا، وتُبقي الخلفية rgb(237, 245, 243) طوال الفصل كلّه. والبوّابة المُودَعة تؤكّد الشرط المعاكس، والحلقة مثبَّتة والمسافة صفر: يجب أن تظلّ خلفية body المحسوبة تُعتِم متجاوزةً قناة حمراء قدرها 120 عند رزمة الشهادات.
7 canvases · 3 runtimes · 15 consumers
نظامًا فنظامًا، ما الذي يفعله reduce فعلًا
قراءة تفضيل واحدة تبلغ خمس عشرة وحدة زمن تشغيل، وكلٌّ منها تُستقِرّ سطحًا مختلفًا. والجدول هو المرجع: الجواب، وممّ تتألّف الحالة المستقرّة، والفحص المُودَع الذي كان ليسقط لو لم تكن كذلك.
| النظام الفرعي | الجواب | ممّ تتألّف الحالة المستقرّة | فحوص مُودَعة |
|---|---|---|---|
| واجهة WebGL، بـ Three.js | end-state | إطار واحد كامل عند فارق زمني صفر؛ أكثر من نصف اللوحة مضاء؛ لا مؤشّر مشترك | 5 |
| أسهم Rive، بـ Canvas2D | end-state | عشر خطوات، كلٌّ منها جزء من ستّين من الثانية، تُطبَّق وتُرسَم مرّة واحدة | 3 |
| حقل النقاط، بـ Canvas2D | end-state | التبعثر الساكن وكل نقطة مأخوذة، وحلقة المؤشّر مركونة خارج الحقل | 5 |
| ظهورات GSAP | off | غير مثبَّتة أصلًا: النص بلا قصّ، والماسحات غائبة، وكل شيء معلَّم بأنه ظهر | 3 |
| تمشيط ScrollTrigger | off | وضعية القسم المكتوبة — الحلقة مجمَّدة عند سالب 20 درجة من ملف الأنماط | 6 |
| تنعيم Lenis | off | لا يُنشَأ أصلًا: تمرير أصليّ، فوريّ، وكل دالّة ما تزال تعمل | 1 |
| رزمة الشهادات | off | ست بطاقات كاملة في عمود عادي، والشهادة أخيرة، بلا زرّ إيقاف | 7 |
صفّان يستحقّان التوسيع، لأنهما يُظهران ما تكلّفه حالة مستقرّة كي تُبنى. جواب Rive معلَن كبيانات على العقد لا مقرَّرًا في فرع شرطي — عقد الأسهم المشحون يحمل settle — والاستقرار يعني تقديم آلة الحالة عشر مرّات، كل مرّة جزءًا من ستّين من الثانية، ثم الرسم مرّة واحدة. والحدّ ثابت لا حلقة تنتظر أن تكفّ الآلة عن التغيّر، لأن آلة حالة دائرية لا تبلغ ذلك الشرط أبدًا. وعشر خطوات تكفي لانتقال خمول بين حالتين كي يحطّ.
ولحقل النقاط وضع سكون مخصّص، وهو قطعًا ليس الإطار صفر. في التشغيل العادي يعيد ثلث النقاط أخذ متّجه انحرافه في كل إطار ويعيد الباقي استعمال الأخير، وذلك في المظهر المشحون لا مُلحَق به — فالإطار صفر ثلث صورة. ووضع السكون يأخذ كل نقطة ويركن حلقة المؤشّر بعيدًا خارج الحقل، فيكون إطار تقليل الحركة هو التبعثر الساكن لا كتلة مؤشّر مجمّدة. والبوّابة تفحص الملء لا الحلقة: العدد المرسوم نفسه ومتوسّط ألفا نفسه بفارق 900 مللي ثانية، والحقل ما يزال في المجموعة النشطة، لأن مفتاح تفضيل يفصل الحقل يأخذ معه قراءة حبر الترويسة.
pinTime(1.5) against pinTime(9)
عيب نصف الإطار، والسؤال الذي أمسك به
كان إطار الواجهة المستقرّ خاطئًا شهورًا وبدا صحيحًا طوال ذلك الوقت. وعُثر عليه بمطالبة الإطار بأن يكون حتميًّا لا بالنظر إليه، وهذا هو الجزء القابل للنقل من هذه القصّة: على لوحة canvas، أسئلة الصحّة التي تستطيع الإجابة عنها بالعين هي الأسئلة المُجاب عنها سلفًا.
- التنفيذ الذي يبدو صحيحًا تحت reduce، ألغِ الاشتراك في أطوار الإطار ثم استدعِ طور الكتابة مرّة واحدة. يرسم، ولوحة canvas ليست فارغة، ولقطة منها مقنعة.
- العيب الكامن الكتابة تركّب مع ما تركه آخر طور حساب. وحيث لم يعمل حساب قطّ، يكون ذلك قيمة uniform للزمن تساوي 0 ونسيج ضوضاء خلفية فارغًا — فكان الإطار المستقرّ ينقصه حقل الظلّ النقطي نفسه، وهو معظم ما تبدو عليه الواجهة.
- السؤال الذي أظهره طلبت العُدّة الحتمية لا الجمال. عادت لقطتان عند زمنين مثبَّتين 1.5 ث و9 ث متطابقتين بايتًا ببايت، والزمن المثبَّت الذي لا يغيّر شيئًا معناه قيمة uniform لا تصل إلى المظلِّل (shader).
- الإصلاح شغّل الحساب ثم الكتابة، عند فارق زمني صفر. كل مُخمِّد يحتفظ بقيمته، لأن التخميد عبر زمن معدوم يعيد القيمة التي بدأ بها، وساعة الزمن المنقضي لا تتقدّم. إنه الإطار نفسه مكتملًا لا الإطار التالي.
- البوّابة التي تجعل الادّعاء قابلًا للبرهان ثبّت 3 ثوانٍ، وارسم مرّة، ثم أكّد أن قيمة uniform للزمن في المشهد تقرأ 3. والتثبيت أوّلًا شرط لازم: تحت reduce يكون الزمن غير المثبَّت 0 بحقّ، فقراءته هناك لا تبرهن شيئًا في أي اتّجاه. ونصف الإطار يتركها عند 0 ونسيج الضوضاء فارغًا.
- اقرأ البكسلات، ولا تلتقط لقطة أبدًا مخزن الرسم غير محفوظ، فمحتويات canvas غير معرَّفة بمجرّد أن يأخذها المركِّب، ولقطة العنصر رمية عملة. والرسم وقراءة البكسلات في الدورة نفسها هما الالتقاط الأمين الوحيد.
والكشف حول ذلك الإصلاح يؤكّد أربعة أشياء وليس فيها صورة واحدة: أكثر من نصف اللوحة مضاء، والإطار إطار كامل، ولا إطار محفوظ ولا مؤشّر مشترك، وعدد الإطارات لم يتغيّر بعد 1,200 مللي ثانية. والأخير يهمّ أكثر ممّا يبدو — فالمُصيِّر الذي يواصل النبض بهدوء من تلقاء نفسه يجتاز كل فحص ساكن يمكنك كتابته عن الصورة.
قراءة البكسلات بدل مقارنة اللقطات هي الانضباط نفسه الذي تحتاجه لوحة canvas في اختبار الانحدار، وله أرقامه هنا: أرضية الضجيج في لقطة canvas.
home-journey.css:569-605
النصف الذي لا تبلغه أي قاعدة مدّة: التخطيط والحالة
بلغت ثلاثة أقسام من هذا المستودع النتيجة نفسها مستقلًّا بعضها عن بعض، بكلماتها هي: المفتاح لا يستطيع نقض التخطيط، ولا يستطيع نقض الحالة. اللوحة اللاصقة تظلّ تثبّت. والتحويل الذي كتبه متحكّم يظلّ قائمًا. والخاصّية المخصّصة المُمشَّطة إلى قيمة في منتصف التمرير تحتفظ بآخر ما كُتب فيها. ولا واحد من هذه حركةٌ وقت قراءة التفضيل، ولا واحد منها له مدّة تُطوى.
وشريط الرحلة هو حيث كلّف ذلك قدرًا قابلًا للقياس من المحتوى. هو نظام أفقي — أربع بطاقات على مسار عرضه 4,052 بكسل داخل نافذة عرض 1,440 بكسل — والشيء الوحيد الذي يجلب الثلاثة الأخرى إلى المشهد هو تحويل مُمشَّط. وإطفاء ذلك التحويل عند reduce صحيح، لأن انزلاقًا جانبيًّا يقوده التمرير هو بالضبط الحركة التي يدور حولها التفضيل؛ لكن إطفاءه وحده قصّ ثلاثة أرباع القسم خلف الفيض الأفقي للصفحة. وقبل وجود قاعدة التخطيط، قيس المستند فخرج 14,884 بكسل بدل 18,709، والـ3,825 المفقودة كانت الفصول التي لم يكن أحد يستطيع بلوغها.
والمستند الأقصر تحت reduce صحيح، والبوّابة تقول ذلك صراحةً: قياس الصفحة المخفَّضة على طول الصفحة المتحرّكة سيكون تأكيدًا أن تقليل الحركة لا يغيّر شيئًا، وهو نقيض المقصد. وما يجب أن يصحّ هو ألّا يضيع محتوى، فيجمع الفحص كل لوحة في قسم الرحلة ويشترط أن يكون لكلٍّ منها مساحة، وأن تكون معروضة ومرئية، وأن تقع داخل النطاق الأفقي للمستند نفسه. والإصلاح يعطي reduce العرض نفسه الذي يحصل عليه الهاتف: يصير المسار عمودًا.
والنصف المتعلّق بالحالة يُنتج فشلًا أسوأ من بطاقة مفقودة. في شريط المشاريع، تفكّ كتلة reduce التصاق المنصّة، وتعيد الفيض الأفقي والالتقاط عند التمرير، وتضبط تحويل المسار على none، وتعيد touch-action إلى auto — كل ذلك حتى لا تترك سمةٌ قديمة، خلّفها تفضيل تغيّر في منتصف الجلسة قبل أن يفكّ زمن التشغيل بناءه، القارئَ داخل تثبيت بطول ثماني شاشات لا يستطيع التمرير خارجه. وإعادة touch-action ليست زينة: نصّ السحب لا يُثبَّت أبدًا تحت reduce، فقيمة pan-y موروثة كانت لتمنع الإيماءة الوحيدة التي وُجد البديل ليسمح بها.
والموقع الذي حلّ هذا محلّه كانت فيه أدقّ نسخة من هذا الصنف. انبثاق الانتقال بين الصفحات فيه يقصر من 0.68 ث إلى 0.3 ث تحت reduce، بينما يبقى انتقال الظهور المقترن به عند 0.68 ث، فلمدّة 380 مللي ثانية تقريبًا بعد أن تبدو الطبقة قد اختفت، كانت نقرات زائر تقليل الحركة تقع على طبقة غير مرئية تملأ نافذة العرض. والتفضيل المطبَّق جزئيًّا يفكّ تزامن توقيتين لم يكونا صحيحين إلا معًا، ولا يظهر ذلك في لقطة ولا لتدقيق آلي.
والشريط الذي تحميه تلك القاعدتان هو فهرس المشاريع، والأعمال التي خلفه هي المواقع المفاهيمية العشرة.
hero-swipe-lock.js:34-41
ما الذي يجب ألّا يطفئه reduce
الاختبار ليس هل يتحرّك. قفل التمرير لا يحرّك شيئًا، وإطفاؤه تحت reduce يكسر إمكانية تنقّل طلبها الزائر صراحةً؛ والتثبيت لا يحرّك شيئًا وقت قراءة التفضيل، وتركه في مكانه يحبس القارئ. ورسم ذلك الخطّ لكل آلية على حدة، داخل مكوّن واحد إن كان الشقّ يقع هناك، هو معظم العمل.
- قفل التمرير
- ليس نظام حركة، فلا يطفئه reduce — فقفل التمرير إمكانية تنقّل وقد طلبها الزائر صراحةً. أمّا تبديلا التسمية إلى جانبه فحركة، وينطويان إلى تبديل لحظيّ. والقفل نفسه محسوب بالمرجعيات لدى مالك التمرير، فيستطيع هذا الزرّ والقائمة أن يحمل كلٌّ منهما واحدًا بلا أن يحرّر قفل الآخر.
- التثبيت
- تخطيط، فعلى reduce أن ينقضه لا أن يوقف تحريكه. وفحص سهولة الوصول المُودَع ينقل تأكيدًا لم يجرِه الموقع القديم قطّ: تحت reduce، والقائمة مفتوحة، لا يغيّر 1,200 بكسل من عجلة الفأرة ولا ضغطة End ولا ضغطة PageDown موضعَ التمرير، وEscape يحرّره ويدع الصفحة تبلغ قاعها.
- التمرير الناعم
- وضع مختلف لا وضع معطَّل. في الموقع الذي حلّ هذا محلّه، قيس الخيار البديهي — إيقاف المنعِّم — فترك الصفحة غير قابلة للتمرير إطلاقًا: 0 بكسل من العجلة، و0 بكسل من End. ولهذا كانت رقعته تستطلع حتى 600 إطار بحثًا عن متغيّر عامّ ثم تُتلف النسخة بدلًا من ذلك. أمّا هنا فلا يُنشَأ المنعِّم أصلًا تحت reduce، والتمرير أصليّ وكامل.
- زرّ الإيقاف
- غائب لا خامل. رزمة الشهادات توزّع ست بطاقات على إيقاع 2,200 مللي ثانية، بتوزيع مدّته 600 مللي ثانية ودورة 13.2 ث، وتحت reduce لا تُنصَب أصلًا — فالزرّ الذي كان سيوقفها يُزال لا يُعطَّل. والعنصر الحاضر الذي لا يفعل شيئًا أسوأ من غيابه: يأخذ محطّة تنقّل واسمًا مقروءًا وضغطة.
- تلاشي اللون المتقاطع
- التأثير الوحيد في هذا الموقع الذي يعلن runs. لا شيء ينزاح ولا يدور ولا يتغيّر حجمه، والحجّة مقيسة لا مُدَّعاة — فمع إطفاء التأثير عند 390 بكسل صارت مسافة الفصل نفسها صفرًا، وحملت الصفحة لونًا مسطّحًا واحدًا بطولها كلّه. وتقليل الحركة مفعَّل افتراضيًّا في حصّة كبيرة من الهواتف، وهذا افتراض عمل لهذا المستودع لا رقم قاسه بنفسه.
ولحالة التمرير نسخة في المكتبة أيضًا، وقد تحرّكت. النسخة المثبَّتة هنا، 1.1.20، لا تحتوي أي إشارة إلى الاستعلام البتّة — فكان عدم إنشائها أصلًا هو الجواب الوحيد المتاح. أمّا Lenis الحالي فيشحن respectReducedMotion مفعَّلًا افتراضيًّا ويأخذ موقفًا ثالثًا: أبقِ النسخة تعمل مع إجبار استيفائها على واحد إلى واحد، كي ينجو تزامن WebGL مع DOM، واجعل عمليات التمرير البرمجية لحظية. ثلاثة أجوبة قابلة للدفاع عن تفضيل واحد، وهي حجّة التصنيف مصغَّرة.
وهل يستحقّ المنعِّم وجوده أصلًا سؤال قياس منفصل، وهو Lenis مقابل التمرير الأصليّ، مقيسًا.
ولجواب القفزة اللحظية في الروابط المرساة عواقبه على التركيز والتاريخ — ما الذي يكسره رابط مرسًى بتمرير ناعم.
axe-core 4.11.0 · 144 scans · 2026-08-24
كيف تبرهن ذلك، والـ707 التي صارت 5
الادّعاء على مستوى الصفحة كلّها تأكيدان يُجرَيان في المرور نفسه، وهما الأطروحة في صيغة قابلة للتنفيذ. مع ضبط التفضيل، لا يحتفظ أي نظام فرعي بإطار — عدّاد المشتركين الأحياء في ساعة الإطارات صفر، والواجهة غير مشتركة، ولا نسخة Rive تعمل، وزمن تشغيل الجسيمات مركون. وكل سطح ثنائي الأبعاد ما يزال عليه صورة مستقرّة: لا واحدة من لوحات canvas السبع تعود بصفر بكسل مرسوم.
والرحلة المزدوجة الحيّة هي النصف الأصعب، لأن التفضيل قد يتغيّر في منتصف الجلسة. تشغيل reduce يفكّ 26 من أصل 41 من ScrollTriggers وكل فاصل تثبيت معها، وهنا تذهب الـ3,203 بكسل من ارتفاع المستند؛ وإطفاؤه يعيد بناءها، بلا تأثير مسرَّب ولا مكرَّر عبر الرحلة، وبلا شيء متروك مقصوصًا في أي اتّجاه. أمّا إزاحة التمرير فلا تنجو منها — 9,645 بكسل، ثم 8,004، ثم 9,716 — وذلك الانحراف البالغ 71 بكسل ينقل ثلاثة ظهورات أخرى تحت الطيّة إلى مجموعة ما لم يُطلَق بعد.
| الكشف | الفحوص فيه | التي تذكر التفضيل |
|---|---|---|
| certificate | 123 | 7 |
| webgl | 72 | 5 |
| projects | 52 | 6 |
| rive | 48 | 3 |
| particles | 38 | 5 |
| rest | 20 | 7 |
| accessibility | 20 | 9 |
| الكشوف الـ17 كلّها | 817 | 61 |
وهذا ليس ادّعاءً بلاغيًّا. رقم axe-core المنشور لهذا الموقع — 5 مخالفات، axe-core 4.11.0، و72 وثيقة بعرضين لمجموع 144 فحصًا، قيست في 24 أغسطس 2026 — يحمل شرطًا، والشرط يحمل حِملًا: prefers-reduced-motion: reduce، بعد تمرير كل وثيقة من طرفها إلى طرفها. وبدونه، أبلغت الأداة نفسها على الموقع نفسه عن 707 عقدة تباين لونيّ خطيرة عبر 60 مسارًا. والجولتان تغطّيان عددًا مختلفًا من المسارات، فهما الأداة نفسها قبل شرط مصحَّح وبعده، لا مقارنة مضبوطة.
وكل واحدة من تلك الـ707 كانت عنصرًا أُمسك به في منتصف ظهوره. يركّب axe العنصر شبه الشفّاف على ما خلفه، فيُقرَأ لون على لون شبه مطابق، ويقيس عنوان يتلاشى داخلًا 1.02:1. ولم يُعفِ التصحيح شيئًا: نجا منه عيبان حقيقيان فأُصلحا — بطاقة دراسة حالة تطبع عند 4.36:1 عبر 612 عقدة وفي وثائقها الـ33 كلّها، وعلامة مائية في صفحة 404 عند 1.25:1 — والخمس الباقية عنصر واحد على ورق الشهادة، عند 1.33:1 حيث ينطبق 3:1، مسموح به بالاسم وبالمحدِّد عبر ثلاثة مسارات وعرضين.
والموقع الذي حلّ هذا محلّه هو المثال المضادّ لهذا كلّه، وأرقامه أدلّة على ذلك البناء القديم لا على هذا. تمشيط للاستعلام عبر محرّكه يعيد صفرًا. تحت reduce عملت نبضة GL فيه 51 مرّة في ثلاث ثوانٍ مقابل 52 بدونه، وبقي خطّه الزمني العامّ في GSAP عند مقياس زمن 1 عبر ظهوراته الـ836 كلّها، وعملت ScrollTriggers الـ61 فيه بلا تغيير، ونوع واحد لا غير من لوحات Rive توقّف — أيقونة القائمة في شريط التنقّل، 70 منها في الموقع كلّه — بينما واصلت 520 غيرها اللعب. أمّا المعالجة التي وُجدت فعلًا فكانت رقعة من 106 أسطر تستطلع متغيّرًا عامًّا، و24 كتلة في ملفات الأنماط بسياستين متنافرتين. ليس أن أحدًا لم يحاول، بل أن التفضيل أُجيب حيث كانت الإجابة سهلة، وتُرك بلا جواب حيث كانت الحركة فعلًا.
ونصف تلك القصّة المتعلّق بالتباين مقالة قائمة بذاتها — لماذا تكون جولة axe على موقع متحرّك زائفة الإيجاب في معظمها.