كتبتُ في مقال المؤثرات الدقيقة (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) – وأين تختلف هاتان الآليتان بما يكفي لجعل الخلط بينهما طريقًا مباشرًا نحو واجهة غير قابلة للوصول.
يستجيب زر «إعجاب» فورًا، يمتلئ القلب، يرتفع العداد – قبل أن يجيب الخادم أصلًا. هذا ليس وهم السرعة الذي تحدثنا عنه في مقال التفاعلات الدقيقة، بل شيء أبعد من ذلك: تعرض الواجهة حالة لم توجد بعد، وتفترض أنها ستوجد بعد لحظات. أستعرض كيفية بناء هذا بأمان – مع تراجع حقيقي عن الكذبة إذا لم يتحقق الافتراض، ولماذا يُعد الجمع اليدوي بين setState وcatch نسخة أسوأ مما يقدمه useOptimistic.
تخسر الفئة المساعدة (utility class) .mt-4 أمام القاعدة .card .card__header .card__title، رغم أنه من المنطقي أن تفوز لأنها أُضيفت لاحقًا. هذا ليس عطلًا – إنها الخصوصية (specificity) تقوم بالضبط بما صُمِّمت من أجله. يُدخل @layer محورًا جديدًا كليًا لحل التعارضات، يعمل قبل الخصوصية، لا معها – وله مزلق واحد غير بديهي لدرجة أن جزءًا من التوثيق المتاح على الإنترنت نفسه يقع فيه.
يبدو التوجيه "use server" وكأنه مجرد اختصار نحوي لاستدعاء fetch إلى مسار API – ولهذا بالتحديد تتعامل معظم التطبيقات معه وكأنه استدعاء دالة عادية. هذا خطأ له عواقب محددة: كل دالة تحمل علامة "use server" تتحول إلى نقطة نهاية عامة (endpoint) مكشوفة على الشبكة، والنموذج المبني على هذه الآلية يعمل حتى قبل أن يتمكن جافاسكريبت من التحميل أصلًا. أستعرض ما يحدث فعليًا خلف هذا التوجيه، وأين تكمن المزالق التي لا تظهر للوهلة الأولى في التوثيق.
يُفترض أن يتوهج حقل النموذج بالأحمر عندما يكون الإدخال بداخله غير صحيح. أمر بسيط – إلا أن CSS لم يستطع طوال 25 عامًا أن يسأل عنصرًا عمّا يجري داخله، بل العكس فقط. أستعرض كيف تعكس :has() هذا الاتجاه، وكيف تختلف عن أدوات الربط (combinators) المعتادة، وأين تكلّف هذه القوة الجديدة الأداء فعليًا.
تبدو بطاقة المنتج نفسها رائعة في الشبكة (grid) وتنكسر في الشريط الجانبي الضيق – رغم اختيار استعلامات الوسائط (media query) بعناية. المشكلة ليست في كودك، بل في السؤال الذي تطرحه استعلامات الوسائط: عن عرض الشاشة، لا عن المساحة التي حصل عليها المكوّن فعليًا. أستعرض كيف تنقل استعلامات الحاوية (container queries) هذا السؤال إلى حيث كان ينبغي أن يُطرح من البداية، وما الذي يحدث فعليًا خلف الكواليس عندما يصبح عنصر ما «حاوية استعلام».
HTML الصحيح ليس سوى نقطة البداية. اكتشف كيف تعمل شجرة إتاحة الوصول فعليًا، ولماذا تضر ARIA أكثر مما تنفع في أحيان كثيرة، وابنِ معي أكورديون حقيقيًا، ومنطقة حية (live region)، وإدارة تركيز في Next.js.
متجران إلكترونيان، المنتج نفسه، وزمن استجابة الخادم نفسه تمامًا – ومع ذلك يبدو أحدهما أسرع وأكثر جدارة بالثقة. الفارق يكمن في التفاعلات الدقيقة: بضع مئات من المللي ثانية من الحركة تقرر ما إذا كان المستخدم سيثق بما يراه على الشاشة أم لا. أفكك هذه الآلية من جذورها – نموذج المُحفّز والتغذية الراجعة، وما يحدث فعليًا داخل محرك عرض المتصفح، وقاعدة `prefers-reduced-motion`.
CSS Houdini ليست تقنية واحدة، بل مجموعة من المواصفات في مراحل نضج مختلفة تماماً. تعرف على أي جزء منها صالح اليوم للاستخدام في الإنتاج، وكيف يعمل @property وpaint worklet فعلياً، ولماذا لا يزال الباقي مجرد تجربة.