UIDex

الخطّ الفاصل

Divider

<hr> / role="separator"

اسمه كمانديفايدر، سيبراتور، فاصل، خطّ أفقي، فاصل القائمة، حدّ فاصل، separator، horizontal rule، hr، menu separator، thematic break، hairline

الخطّ الفاصل خطّ رفيع يرسم الحدّ بين كتلتين في الواجهة، والخطوط الثلاثة التي تبدو متطابقة على الشاشة تحمل ثلاثة معانٍ مختلفة تماماً في الكود. أوّلها ‎<hr>‎، وهو انقطاع موضوعي في المحتوى، أي أن ما بعده كلام في شأن آخر، ودوره الضمني separator فيصل إلى شجرة الوصول ويُعلَن بلا سطر واحد من ARIA. وثانيها ‎role="separator"‎ داخل ‎role="menu"‎ أو ‎role="toolbar"‎، يفصل مجموعة من عناصر التحكّم عن التي تليها ولا يقول شيئاً عن المحتوى، وثالثها الخطّ الزخرفي الموضوع للإيقاع البصري وحده، وواجبه أن يختفي من شجرة الوصول تماماً. وهو ليس حدّ CSS: الحدّ خاصّية على صندوق ولا يُنشئ عقدة في الشجرة، فلا يُعلَن مهما بدا ثقيلاً على الشاشة. وليس عنواناً، فالعنوان يسمّي القسم الذي يليه ويظهر في قائمة العناوين التي يقفز بينها قارئ الشاشة، أمّا الفاصل فيقول إن شيئاً تغيّر هنا دون أن يقول ما هو. ومتى قبل الخطّ التركيز وصار المستخدم يسحبه، خرج من باب البنية وصار مقبض تقسيم يحمل aria-valuenow. وسؤالان يحسمان أمر أيّ خطّ أمامك: هل يحتاج عقدة في شجرة الوصول، وهل يأخذ التركيز.

لو قلت عليه…

«الخط الرفيع اللي بيفصل بين حاجتين في الصفحة»«الشرطة الطويلة اللي بتقسم الكارت نصّين»«الخط اللي في نصّه كلمة «أو»»«الفاصل اللي بيقسم خيارات المنيو لمجموعات»«الخط الواقف الصغير اللي بين الزراير»

العيّنة الحيّة

تفاعل مع الديمو. كل الأجزاء حقيقية ومرقّمة.

تقرير-الربع.pdf

↑↓ تتخطّى الفاصل


RTLالخطّ المُزاح يبدأ بعد الأيقونة في الاتجاهين، بلا margin-left

تشريح العنصر: كل جزء واسمه

مرّر على أي سطر ليتحدّد مكانه في الديمو فوق.

برومبت البناء

ابنِ فواصل لواجهة عربية، وقرّر لكل خطّ أي نوع هو قبل أن ترسمه. استعمل ‎<hr>‎ حين يكون ما بعده كلاماً في موضوع آخر واتركه بلا ARIA لأن دوره الضمني separator يكفي، لكن صفّر ‎border‎ ثم ارسم ‎border-block-start‎ واحداً لأن الشكل الافتراضي صندوق بحدود inset لا خطّ. وداخل ‎role="menu"‎ أو ‎role="toolbar"‎ استعمل عنصراً بـ ‎role="separator"‎ لفصل المجموعات، واحرص أن يبقى خارج ترتيب التركيز، وأن تقفز فوقه أسهم لوحة المفاتيح، وألّا يُحسب في ‎aria-setsize‎ و ‎aria-posinset‎. وكل خطّ زخرفي أخفِه بـ ‎aria-hidden="true"‎، أو استغنِ عن العنصر ذاته وارسمه بـ ‎border-block-end‎ على الصفّ. وللفاصل المُعنون: حاوية بـ ‎role="separator"‎ فيها خطّان بـ ‎aria-hidden‎ حول الكلمة، مع ‎aria-label‎ بنفس الكلمة لأن الدور يأخذ اسمه من المؤلّف لا من محتواه، ولا تولّد الكلمة بـ ‎content‎ في CSS. وأي خطّ رأسي أعطه ‎aria-orientation="vertical"‎ صراحةً لأن الافتراضي أفقي. واستخدم الخصائص المنطقية في كل مكان: ‎margin-inline‎ للإزاحة، و ‎border-inline-start‎ للخطّ الرأسي بين مجموعتين، و ‎inset-inline-start‎ بدل ‎left‎، ولا تكتب ‎margin-left‎ ولا ‎border-right‎ إطلاقاً. وأي تدرّج تلاشٍ اكتب له قاعدة صريحة داخل ‎[dir="rtl"]‎ لأن ‎linear-gradient‎ لا يقبل كلمات منطقية. ولو كان الفاصل قابلاً للسحب فأعطه ‎tabindex="0"‎ مع ‎aria-valuenow‎ و ‎aria-valuemin‎ و ‎aria-valuemax‎، واضرب إزاحة المؤشّر في إشارة اتجاه قبل تحديث القيمة.

سلوكه في الاتجاه من اليمين لليسار

الجزء ده حصري عندنا.

الخطّ المُزاح

ينعكس

الخطّ الذي يبدأ بعد أيقونة الصفّ يجب أن يتبع الأيقونة عند انعكاس الصفحة. اكتب ‎margin-inline-start: 56px‎ لا ‎margin-left: 56px‎، وإلا بقيت الإزاحة على اليسار في العربية بينما انتقلت الأيقونة إلى اليمين، فيبدو الخطّ مقطوعاً من الطرف الخطأ. أمّا الإزاحة المتساوية من الجهتين فقيمة واحدة ‎margin-inline: 16px‎ ولا تحتاج انعكاساً لأنها متماثلة أصلاً. ولاحظ أن المتصفّح نفسه يكتب هوامش ‎<hr>‎ الافتراضية بخصائص منطقية (‎margin-inline: auto‎)، فالكود الفيزيائي هنا يتراجع عن الافتراضي بدل أن يبني عليه.

الفاصل المُعنون

لا ينعكس

الفاصل الذي تتوسّطه كلمة لا يحتاج انعكاساً إذا بُني بصفّ flex فيه ‎flex: 1‎ على الخطّين المحيطين بالكلمة: المحور المضمّن يرتّب نفسه، والتوسيط متماثل في الاتجاهين. والفاصل الذي تبدأ كلمته من أول السطر يُضبط بـ ‎text-align: start‎ أو بترتيب عناصر flex، ولا يُكتب له ‎text-align: left‎ أبداً. البناء الوحيد الذي ينكسر هنا هو الذي يرسم الخطّين بـ ‎::before‎ و ‎::after‎ مثبّتين بـ ‎left: 0‎ و ‎right: 0‎، فحوّلهما إلى ‎inset-inline-start‎ و ‎inset-inline-end‎.

الخطّ الرأسي بين الأزرار

ينعكس

الخطّ الرأسي الذي يفصل مجموعتي أزرار في شريط أدوات يُرسم عادةً كحدّ على المجموعة التالية. اكتب ‎.group + .group { border-inline-start: 1px solid var(--line) }‎: المُحدِّد ‎+‎ يتبع ترتيب DOM، والحافّة inline-start تتبع اتجاه القراءة، والاثنان يتّفقان في العربية كما يتّفقان في الإنجليزية. أمّا ‎border-left‎ فينتقل بعد الانعكاس إلى الحافّة الخارجية للمجموعة الأخيرة، فيظهر خطّ زائد على طرف الشريط ويختفي الخطّ الذي كان بين المجموعتين. وإن رسمت الفاصل عنصراً مستقلاً بعرض ‎1px‎ داخل صفّ flex فهو محايد اتجاهياً ولا يحتاج أي تعديل.

التدرّج المتلاشي

ينعكس

الخطّ الذي يتلاشى نحو نهايته يُرسم بـ ‎linear-gradient‎، والتدرّجات لا تعرف الكلمات المنطقية: القيم المتاحة ‎to right‎ و ‎to left‎ وزوايا بالدرجات، ولا وجود لـ ‎to inline-end‎. فالخطّ المكتوب ‎to right‎ يتلاشى عكس اتجاه القراءة في العربية. عالجه بقاعدة صريحة ‎[dir="rtl"] .fade { background-image: linear-gradient(to left, …) }‎، أو بتدرّج متماثل يخفت من الطرفين معاً فلا يبقى للاتجاه أثر. والقيد نفسه ينطبق حرفياً على ‎mask-image‎ إذا أخفيت به طرف الخطّ.

الفاصل القابل للسحب

ينعكس

الفاصل الذي يأخذ التركيز ويحرّكه المستخدم يتحوّل من عنصر بنيوي إلى أداة تحكّم، فيصير ‎aria-valuenow‎ مطلوباً عليه، بينما القيمتان الضمنيتان لـ ‎aria-valuemin‎ و ‎aria-valuemax‎ هما ٠ و ١٠٠. وأحداث المؤشّر تسلّمك إزاحة فيزيائية بحتة: ‎clientX‎ يزيد نحو اليمين مهما كان اتجاه الصفحة، فالسحب يميناً في العربية يجب أن يصغّر اللوح الأول لا أن يكبّره. اضرب الفرق في إشارة تقرأها من ‎getComputedStyle(el).direction === "rtl" ? -1 : 1‎ قبل تحديث القيمة، وطبّق القلب نفسه على ‎ArrowLeft‎ و ‎ArrowRight‎ حتى يظلّ المقبض يتحرّك بصرياً في اتجاه السهم المضغوط. واجعل ‎aria-valuenow‎ يصف نسبة اللوح عند البداية المنطقية لا اللوح الأيسر.

أسماؤه في الكود

كل سطر هو كلمة مكتبة واحدة عن نفس الشيء. خد السطر اللي بيكلّم مشروعك.

HTML<hr>انقطاع موضوعي في المحتوى، بدور separator ضمني وعنصر فارغ لا يقبل أبناء.
ARIArole="separator"دور بنيوي ما دام غير قابل للتركيز، ويأخذ اسمه من aria-label لا من نصّه الظاهر.
Radix UI<Separator.Root orientation="vertical" />يكتب Radix خاصّية aria-orientation في الحالة الرأسية وحدها، لأن الأفقي هو افتراضي الدور أصلاً.
ARIArole="separator" + aria-valuenowحين يقبل الفاصل التركيز والسحب يصير مقبض تقسيم بقيمة يقرأها المستخدم ويغيّرها.
CSSborder-inline-startالخاصّية المنطقية التي تُبقي الخطّ الرأسي بين المجموعتين بعد انعكاس الصفحة.
shadcn/ui<Separator decorative={false} />الغلاف يضبط decorative على true افتراضياً فيخرج العنصر بـ role="none"، ولا يُعلَن أي فاصل ما لم تمرّر false.
Material UI<Divider variant="inset" />الإزاحة خيار جاهز في المكوّن، وهي التي تميّز فاصل الصفوف من فاصل الأقسام.

شوف كمان

آخر تحديث · 2026-08-19