UIDex

الشريط المقسّم

Segmented Control

role="radiogroup"

اسمه كمانسيجمنتد كنترول، المتحكّم المقسّم، مفتاح مقسّم، أزرار ملتحمة، شريط اختيار مضغوط، تبويبات مصغّرة، segmented button، segmented picker، toggle group، joined button group، UISegmentedControl، pill switcher

الشريط المقسّم صفّ من الخيارات المتلاصقة داخل إطار واحد، لا يُختار منها إلا واحد، وتنتقل خلفية التحديد إلى الخيار الجديد فور الضغط عليه. وهو في جوهره حقل يختار قيمة، ولهذا بنيته الدلالية بنية مجموعة أزرار اختيار: role="radiogroup" على الحاوية، و role="radio" مع aria-checked على كل قسم. وهو ليس تبويبات، فالتبويب يحمل role="tab" و aria-selected ويشير بـ aria-controls إلى لوح محتوى يظهر ويختفي معه، أمّا الشريط المقسّم فقد لا يكون تحته لوح أصلاً، وقد ينتهي أثره كله عند إعادة ترتيب جدول معروض أو تغيير المدى الزمني لرسم بياني. وشريط الأدوات (toolbar) شيء آخر: أزراره إجراءات مستقلّة، لا حالة تحديد بينها، ولا شيء يمنع المستخدم من ضغط اثنين منها في ثانية واحدة. وليس مجموعة أزرار (button group)، فتلك أزرار جمعها الشكل وحده وكل واحد منها يفعل شيئاً مختلفاً، بينما أقسام الشريط المقسّم قيم متنافية لحقل واحد يعرف أيّها المختار الآن. والفيصل عند المراجعة: إن كان تحت الصفّ لوح محتوى كامل له عنوانه الخاص فهذه تبويبات، وإن كانت الحصيلة قيمة واحدة تُقرأ من متغيّر أو تُرسل مع النموذج فهذا شريط مقسّم.

لو قلت عليه…

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

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

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

نطاق التقرير

يعرض آخر ٧ أيامname="range" value="week"
RTLأول خيار عند اليمين، والسهم الأيسر هو «التالي»، جرّبه بلوحة المفاتيح

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

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

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

ابنِ شريطاً مقسّماً (segmented control) يختار مدى زمنياً من أربعة خيارات. اجعل الحاوية عنصراً بـ role="radiogroup" واسم مقروء عبر aria-labelledby، وضع داخلها <input type="radio"> واحداً لكل قسم تتقاسم كلها نفس قيمة name، مخفية بالقصّ (sr-only) لا بـ display: none، حتى يأتي التوقّف الواحد لـ Tab واختيار الأسهم من المتصفّح بلا JavaScript. لفّ كل مدخل بـ <label for> يملأ خليّته كلها. أضف معالج keydown واحداً يقرأ getComputedStyle(el).direction ويبدّل ArrowLeft بـ ArrowRight تحت rtl مع preventDefault، لأن WebKit لا يجري هذا التبديل للمدخلات الأصلية. وزّع العرض بـ grid-template-columns: repeat(4, minmax(0, 1fr)) حتى لا يتغيّر عرض أي قسم مع تغيّر الاختيار. ضع خلف القسم المختار كبسولة واحدة تتحرّك بـ transform وحده: إمّا إرساء بـ left: 0 الفيزيائية مع قياس offsetLeft، وإمّا إرساء بـ inset-inline-start: 0 مع translateX(calc(var(--i) * 100% * var(--dir))) وضبط ‎--dir إلى ‎-1 داخل [dir="rtl"]، ولا تخلط بين الطريقتين. ألغِ انتقال الكبسولة تحت prefers-reduced-motion: reduce دون إخفائها. ارسم فاصلاً بعرض بكسل بين كل قسمين غير مختارين وأخفِه بـ opacity أو بلون شفّاف لا بإزالة الحدّ. دوّر طرفي الشريط بـ border-start-start-radius و border-end-start-radius على القسم الأول ونظيرتيهما end على الأخير، واستعمل الخصائص المنطقية في كل الحشو والهوامش (padding-inline و margin-inline)، ولا تكتب left أو right إلا في إرساء الكبسولة إن اخترت الطريقة الفيزيائية. أضف حلقة تركيز على القسم عبر ‎:focus-visible للمدخل المخفي واجعلها فوق الكبسولة في ترتيب z. واحصر عدد الأقسام بين اثنين وخمسة.

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

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

ترتيب الأقسام

ينعكس

ترتيب DOM لا يتغيّر، والذي ينعكس هو المحور المضمّن، فيقع الخيار الأول عند يمين الشريط في العربية والأخير عند يساره. وهذا يحدث وحده في صفّ flex أو grid، فلا تضف flex-direction: row-reverse ولا خاصية order داخل [dir="rtl"]، لأن الانعكاس الثاني يعيد الترتيب البصري إلى ما كان عليه ويجعل ما تراه العين مخالفاً لما يمشي عليه Tab. والشيء الوحيد الذي يبقى عليك عكسه بيدك هو الرسم الذي يحمل اتجاهاً بذاته: أيقونات محاذاة النصّ خطوط متراصّة تلتصق بجهة واحدة، و«محاذاة إلى البداية» في العربية تلتصق باليمين، فاقلبها بـ transform: scaleX(-1) داخل [dir="rtl"] أو استعمل ملفّ SVG ثانياً.

إزاحة المؤشّر

ينعكس

transform خاصية فيزيائية لا مكافئ منطقي لها في CSS. والخطأ الشائع أن تُرسي الكبسولة بـ inset-inline-start: 0 ثم تزيحها بـ translateX(calc(var(--i) * 100%)): نقطة الإرساء انتقلت إلى يمين الشريط في العربية بينما بقيت الإزاحة تدفع يميناً، فتطير الكبسولة خارج الشريط عند أول اختيار. اضرب الإزاحة في إشارة اتجاه: translateX(calc(var(--i) * 100% * var(--dir))) مع ضبط ‎--dir إلى ‎-1 داخل [dir="rtl"]. والبديل الذي يعمل في الاتجاهين بلا متغيّر أن تُرسي الكبسولة بـ left: 0 الفيزيائية وتقيس موضع القسم بـ offsetLeft، فتكون الخاصيتان فيزيائيتين معاً. الكسر يقع حين تخلط منطقية بفيزيائية.

زوايا الطرفين

ينعكس

الشريط مدوّر من طرفيه فقط وأقسامه الوسطى قائمة الزوايا. و rounded-l و rounded-r، أي border-top-left-radius وأخواتها، تثبّت التدوير على اليسار واليمين الفيزيائيين، فيصير القسم الأول في العربية مربّع الزاوية من الخارج مدوّرها من الداخل. استخدم border-start-start-radius و border-end-start-radius على القسم الأول، و border-start-end-radius و border-end-end-radius على الأخير؛ الكلمة الأولى تسمّي المحور الكتلي والثانية المحور المضمّن، وفي Tailwind هما rounded-s-* و rounded-e-*. ويبقى ‎:first-child هو المحدّد الصحيح، لأنه يتبع ترتيب DOM وهو نفسه ترتيب البداية المنطقية.

الأسهم

ينعكس

في مجموعة الاختيار ينقل السهم التركيز ويغيّر القيمة في الضغطة نفسها، ومعنى «التالي» في العربية هو ArrowLeft. ولا تعتمد على المحرّك في تبديل المفتاحين: Chromium يقرأ اتجاه المدخل المحسوب فيبدّل بين ArrowLeft و ArrowRight تحت rtl، أمّا WebKit فيبقى ArrowRight عنده «التالي» مهما كان الاتجاه، والترميز نفسه يمشي إلى الخلف في Safari. اقرأ الاتجاه بـ getComputedStyle(el).direction داخل معالج keydown وبدّل المفتاحين هناك مع preventDefault، سواء كتبت المجموعة بيدك أو رقّعت مدخلات radio أصلية. أمّا ArrowUp و ArrowDown فلا يتبادلان أبداً، لأن المحور الكتلي لا ينعكس مع العربية.

التسميات اللاتينية

لا ينعكس

تسميات هذا العنصر قصيرة، وكثير منها لاتيني خالص: PNG و SVG، أو 1D و 1W، أو USD. الكلمة اللاتينية وحدها تُعرض سليمة، لكن أي محرف محايد ملاصق لها يأخذ اتجاه الفقرة وينتقل إلى الطرف الآخر من التسمية، فتظهر «+20%» داخل شريط عربي على الشاشة هكذا: «20%+». اعزل كل تسمية بذاتها بـ dir="ltr" على عنصرها أو بـ <bdi>، ولا تعالج الأمر بوضع dir="ltr" على الشريط كله وإلا انقلب ترتيب الأقسام معه.

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

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

ARIArole="radiogroup" + role="radio"الزوج الذي يجعل الشريط اختياراً لقيمة لا تبويبات؛ الحالة في aria-checked لا في aria-selected.
HTML<input type="radio" name="range">الاسم المشترك وحده يعطيك التوقّف الواحد لـ Tab واختيار الأسهم بلا سطر برمجي؛ أمّا تبديل الأسهم في RTL فتختلف فيه المحرّكات.
Ant Design<Segmented options={[…]} />تسمّيه Ant Design و Mantine و Radix Themes بالاسم نفسه، وتبني Ant Design كل قسم على مدخل radio مخفي داخل <label>.
Material UI<ToggleButtonGroup exclusive>بدون exclusive تبقى الأزرار قابلة للضغط معاً ويضيع معنى الاختيار الواحد.
Radix UI<RadioGroup.Root orientation="horizontal" dir="rtl">الدور والتنقّل بالأسهم جاهزان، و dir هي ما يبدّل معنى ArrowLeft و ArrowRight؛ الشكل الملتحم يبقى عليك.
SwiftUIPicker(…).pickerStyle(.segmented)نفس العنصر اسمه UISegmentedControl في UIKit و NSSegmentedControl في AppKit، وأبل تصنّفه منتقي قيمة لا نظام تبويبات.
CSSborder-start-start-radiusالزاوية المنطقية التي تدوّر الطرف الصحيح من الشريط بعد انعكاس الصفحة.

شوف كمان

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