هناك سبب يجعل التأثير المنظوري بـ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() واحدة من الوسائل القليلة لسدّها فعليًا.
يستجيب زر «إعجاب» فورًا، يمتلئ القلب، يرتفع العداد – قبل أن يجيب الخادم أصلًا. هذا ليس وهم السرعة الذي تحدثنا عنه في مقال التفاعلات الدقيقة، بل شيء أبعد من ذلك: تعرض الواجهة حالة لم توجد بعد، وتفترض أنها ستوجد بعد لحظات. أستعرض كيفية بناء هذا بأمان – مع تراجع حقيقي عن الكذبة إذا لم يتحقق الافتراض، ولماذا يُعد الجمع اليدوي بين setState وcatch نسخة أسوأ مما يقدمه useOptimistic.
متجران إلكترونيان، المنتج نفسه، وزمن استجابة الخادم نفسه تمامًا – ومع ذلك يبدو أحدهما أسرع وأكثر جدارة بالثقة. الفارق يكمن في التفاعلات الدقيقة: بضع مئات من المللي ثانية من الحركة تقرر ما إذا كان المستخدم سيثق بما يراه على الشاشة أم لا. أفكك هذه الآلية من جذورها – نموذج المُحفّز والتغذية الراجعة، وما يحدث فعليًا داخل محرك عرض المتصفح، وقاعدة `prefers-reduced-motion`.
CSS Houdini ليست تقنية واحدة، بل مجموعة من المواصفات في مراحل نضج مختلفة تماماً. تعرف على أي جزء منها صالح اليوم للاستخدام في الإنتاج، وكيف يعمل @property وpaint worklet فعلياً، ولماذا لا يزال الباقي مجرد تجربة.