الدرج الجانبي
Drawer / Side Panelrole="dialog" / inset-inline-start
اسمه كماندرور، سايد بانل، اللوحة الجانبية المنزلقة، قائمة جانبية منزلقة، درج التنقّل، ورقة جانبية، side panel، side sheet، navigation drawer، slide-out panel، off-canvas menu، flyout panel
الدرج الجانبي سطح مؤقّت ينزلق من إحدى حواف المنطقة المرئية، يلتصق بها ويمتدّ على طولها، ثم ينسحب ويختفي حين يُغلق. والصفة التي تعرّفه هي اقتران الارتكاز على حافّة ببقاء السياق خلفه مرئياً: المستخدم ما زال يرى الصفحة التي جاء منها، ولهذا صار المكان الطبيعي للتنقّل والفلاتر ولوحات التفاصيل. وهو ليس نافذة حوارية (modal)، فالنافذة تتوسّط المنطقة المرئية ولا تنتمي إلى حافّة، وهي قائمة أصلاً لتنتزع منك قراراً قبل أن تكمل. وليس شريطاً جانبياً (sidebar)، لأن الشريط جزء دائم من تخطيط الصفحة يقاسمها المساحة ويزيح المحتوى بدل أن يعلوه، ولا يُغلق بـ Escape ولا بالنقر خارجه لأنه لا يُغلق أصلاً. ولا يُخلط بالورقة السفلية (bottom sheet): تلك نظيره على الموبايل، مرتكزة على الحافّة السفلية وتتحرّك على المحور الكتلي لا المحور المضمّن. وانتبه أن الحَجْب ليس جزءاً من التعريف: الدرج قد يكون حاجباً بطبقة تعتيم وحصر تركيز وقفل تمرير، وقد يكون غير حاجب فيبقى ما خلفه قابلاً للنقر وللوصول بـ Tab، فالحجب محور مستقلّ تماماً عن الارتكاز على الحافّة. والاختبار العملي: إن لم يستطع المستخدم أن يغلقه ويكمل ما كان فيه، فما بنيتَه نافذة حوارية في هيئة درج.
لو قلت عليه…
العيّنة الحيّة
تفاعل مع الديمو. كل الأجزاء حقيقية ومرقّمة.
التنقّل
اسحبه نحو حافّته، أو Esc
تشريح العنصر: كل جزء واسمه
مرّر على أي سطر ليتحدّد مكانه في الديمو فوق.
برومبت البناء
سلوكه في الاتجاه من اليمين لليسار
الجزء ده حصري عندنا.
الحافّة المرتكِزة نفسها تنقلب
ينعكسهذا هو الانعكاس الذي ينساه الجميع: ليس محتوى الدرج ما ينقلب، بل الحافّة التي يرتكز عليها ويخرج منها. فدرج التنقّل الذي يخرج من يسار الشاشة في الإنجليزية يجب أن يخرج من يمينها في العربية، لأنه مربوط ببداية القراءة لا باليسار الفيزيائي. اكتبه inset-inline-start: 0 مع inset-block: 0 و inline-size صريح، واحذف left و width تماماً، وحوّل الفاصل بين اللوح والمحتوى إلى border-inline-end بدل border-right. والاستثناء المشروع الوحيد درج مرتبط بمعنى ثابت لا بالقراءة، كلوحة تفاصيل تفتح دائماً عند نهاية جدول، وحتى هذا يُكتب inset-inline-end لا right.
حركة الانزلاق تحتاج متغيّر اتجاه
ينعكسلا يوجد نظير منطقي لـ translateX: دالة الإزاحة تعمل على المحور الفيزيائي X مهما كان اتجاه الصفحة، ولا شيء اسمه translate-inline في المواصفة. لذا translateX(-100%) الثابت يجعل الدرج في العربية يبدأ خارج الشاشة من الجهة المقابلة لحافّته ويعبر عرضها كلّه قبل أن يستقرّ. عرّف --dir: 1 على الجذر و --dir: -1 داخل [dir="rtl"]، ثم اكتب transform: translateX(calc(-100% * var(--dir))). البديل تحريك inset-inline-start من -320px إلى صفر وهو ينعكس مجّاناً، لكنه يجبر المتصفّح على إعادة التخطيط في كل إطار بينما transform يُنفَّذ على المُركِّب.
اتجاه السحب للإغلاق ينعكس
ينعكسإحداثيات المؤشّر واللمس فيزيائية دائماً: clientX يزداد نحو يمين الشاشة في العربية والإنجليزية على السواء، والمتصفّح لا يعكسها لك في RTL. لذا شرط مثل if (deltaX < -60) close() خطأ صريح لا مسألة ذوق: في العربية يجلس الدرج المرتكز على البداية عند يمين الشاشة، فالسحبة التي تُبعده نحو حافّته تتّجه يميناً وتُعطي deltaX موجباً لا يحقّق الشرط أبداً فيرتدّ اللوح إلى مكانه، بينما الحركة الوحيدة التي تحقّقه هي السحب يساراً بعيداً عن الحافّة نحو داخل الصفحة، فينغلق الدرج بجرّه فوق المحتوى: عكس ما تفعله يد المستخدم تماماً. اقرأ الاتجاه وقت الحدث عبر const sign = getComputedStyle(panel).direction === "rtl" ? -1 : 1، واضرب فيه الفرق قبل أي مقارنة. ونفس الإصلاح يلزم حدّ السرعة velocityX ومفتاحي ArrowLeft و ArrowRight إن أضفتهما.
زرّ الإغلاق والظلّ
ينعكسالاثنان ينتقلان معاً إلى الجهة المقابلة. زرّ × يقف عند النهاية المنطقية لرأس اللوح: اجعله آخر ابن في صفّ flex مع justify-content: space-between فينضبط وحده، أو ثبّته بـ inset-inline-end بدل right. أمّا الظلّ فمصيدة أهدأ: box-shadow ليس له صيغة منطقية في CSS إطلاقاً، وقيمة مثل 8px 0 24px تُلقي الظلّ بعد الانعكاس خارج حافّة الشاشة حيث يقصّه المتصفّح، فيفقد اللوح ارتفاعه فوق المحتوى الذي يُفترض أنه يعلوه. اكتب الإزاحة الأفقية calc(8px * var(--dir))، أو صفّرها واكتفِ بالتمويه وحده فهو متماثل بطبعه. والرمز × متماثل حول محوره الرأسي فلا يُقلب، بينما سهم الطيّ « أيقونة اتجاهية تنعكس معه.
ما لا ينعكس: المحور الكتلي والحجب
لا ينعكساتجاه الصفحة يحكم المحور المضمّن وحده. فالدرج المرتكز على حافّة كتلية، كالحافّة السفلية، لا ينتقل بين اللغتين أبداً: inset-block-end: 0 يبقى الأسفل نفسه، ومحور سحبه deltaY يبقى بنفس الإشارة، فلا تضف إليه متغيّر اتجاه من الأساس. والطبقة الحاجبة محايدة كذلك: inset: 0 يغطّي كل شيء وليس فيه ما ينقلب. وقفل التمرير خلف الدرج محايد هو الآخر، فـ overflow: hidden و overscroll-behavior: contain لا علاقة لهما بالاتجاه، وفيه شيء واحد ينقلب: موضع شريط التمرير المختفي يقع عند الحافّة المنطقية النهائية، أي على اليسار في صفحة dir="rtl"، فعوّضه بـ scrollbar-gutter: stable لا بـ padding-right ثابت.
أسماؤه في الكود
كل سطر هو كلمة مكتبة واحدة عن نفس الشيء. خد السطر اللي بيكلّم مشروعك.
شوف كمان
آخر تحديث · 2026-08-19