ست بطاقات على لوحة التحكم، ست مؤشرات تحميل، ولا تظهر معاً — تظهر واحدة تلو الأخرى كالدومينو، لأن كل بطاقة بدأت التحميل فقط بعد انتهاء التي فوقها. هذا هو الشلال، والوقت الإجمالي للانتظار ليس أبطأ طلب بل مجموع الكل. use() وSuspense يقطعان هذه السلسلة — لكن المثير للاهتمام هو الآلية الداخلية: ماذا يرمي المكوّن المُعلَّق فعلاً، وكيف يصل HTML الذي يصل بترتيب خاطئ إلى مكانه الصحيح.
هناك سبب يجعل التأثير المنظوري بـJavaScript يبدو دائماً غير صحيح قليلاً — منفصلاً بعض الشيء، كأن الطبقات تسبح نصف إطار خلف الصفحة. هذا ليس كوداً رديئاً. السبب هو أن أحداث التمرير تصل إلى JavaScript بعد أن يكون المتصفح قد رسم التمرير بالفعل. حلّ المتصفح هذا مرة واحدة، حين حوّل الترويسات اللاصقة إلى position: sticky. Scroll-driven animations هي نفس الخطوة، مطبّقة على جميع التأثيرات المرتبطة بالتمرير دفعة واحدة.
لعشر سنوات، كان تحريك الصور المصغّرة إلى صور البطل يعني تقنية واحدة: قِس العنصر قبل وبعد، وزيّف الفرق بـtransform، ثم اعرضه. FLIP. كل مكتبة حركات تفعل ذلك، وكل منها تدفع ثمنه بـJavaScript على الخيط الرئيسي في أسوأ لحظة ممكنة. View Transitions API ينقل الحيلة بأكملها إلى المتصفح — ومنذ React 19.2 وNext.js 16، يتكون العقد بأكمله من خاصية واحدة اسمها name.
كتبتُ في مقال المؤثرات الدقيقة (micro-interactions) أن transform وopacity يُحرَّكان على خيط التركيب (compositor thread)، لذا لا ينبغي حتى لصفحة ثقيلة أن تُقطّع حركة زر. هذا لا يزال صحيحًا – مع تحفظ واحد سكت عنه ذلك المقال: إذا كان الخيط الرئيسي محجوبًا بمهمة جافاسكريبت طويلة، فقد لا تحصل الحركة أبدًا على فرصة البدء، لأن معالج النقر نفسه ينتظر في الطابور. مقياس INP هو المقياس الذي يرصد هذه الفجوة بالضبط – ودالة scheduler.yield() واحدة من الوسائل القليلة لسدّها فعليًا.
تُعرض قائمة تضم ألف تعليق ببطء ملحوظ، فتلجأ إلى react-window – وفي مقابل تمرير سلس، تخسر Ctrl+F، وطباعة الصفحة، وجزءًا من تنقّل قارئ الشاشة، لأن العناصر خارج نافذة العرض تتوقف عن الوجود فعليًا في DOM. تحلّ content-visibility المشكلة نفسها بطريقة مختلفة: تبقى العناصر في DOM، ويكتفي المتصفح بتجاهل عمل التخطيط والرسم المكلف لها، إلى أن تقترب من المنطقة المرئية – والمفاجئ أنه قادر على كشفها مؤقتًا عندما تبحث فيها عن نص.
في مقال إتاحة الوصول، كان المودال الذي يُخفى بـ display: none لكنه يبقى موجودًا في شجرة إتاحة الوصول مثالًا على خطأ ناتج عن بناء مودال من الصفر باستخدام عناصر div. السؤال الحقيقي هو: لماذا كان من الأساس يجب بناؤه من الصفر؟ أستعرض ما يقدمه عنصر <dialog> الأصلي وسمة popover مجانًا – الطبقة العليا (top layer)، وفخ التركيز (focus trap)، والإغلاق التلقائي عند النقر خارج العنصر (light dismiss) – وأين تختلف هاتان الآليتان بما يكفي لجعل الخلط بينهما طريقًا مباشرًا نحو واجهة غير قابلة للوصول.