UIDex

مربّع الاختيار

Checkbox

<input type="checkbox">

اسمه كمانتشيك بوكس، خانة الاختيار، صندوق الاختيار، مربّع التحديد، مربّع صح، check box، tick box، tri-state checkbox، indeterminate checkbox، select all checkbox

مربّع الاختيار حقل ثنائي داخل نموذج: محدَّد أو غير محدَّد، وقيمته لا تصل إلى الخادم إلا لحظة إرسال النموذج. ولهذا مكانه الخيارات المستقلّة التي يجوز أن يُؤخذ منها أكثر من واحد، أو ألّا يُؤخذ منها شيء. وهو ليس مفتاح تبديل (switch)، فالمفتاح يطبّق أثره في لحظة الضغط بلا زرّ حفظ، بينما يبقى المربّع مسوّدة يملك المستخدم أن يعدل عنها قبل الإرسال. وهو ليس زرّ راديو (radio)، فالراديو يفرض خياراً واحداً من مجموعة متنافية، أمّا مربّعات المجموعة الواحدة فكل واحد منها مستقلّ عن جاره تماماً، حتى إنّ سمة required على مربّع تعني «هذا المربّع بعينه يجب تحديده» ولا تعني «حدِّد واحداً من المجموعة». وله شكل ثالث اسمه indeterminate يعرض شرطة بدل علامة الصح، لكنه حالة عرض لا قيمة ثالثة: لا يغيّر checked ولا يغيّر ما يُرسَل مع النموذج.

لو قلت عليه…

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

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

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

تنبيهات الحساب

مسطرة المسافة تبدّل المربّع، و Enter يرسل النموذج.

RTLالمربّع عند بداية السطر، والتفريع يزيح بـ padding-inline-start

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

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

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

ابنِ مجموعة مربّعات اختيار داخل نموذج. اجعل كل مربّع <input type="checkbox"> حقيقياً له name و value صريحان، ولا تبنِه من <div> إلا إذا كنت مستعدّاً لكتابة role="checkbox" و tabindex="0" و aria-checked ومعالج keydown لمسطرة المسافة مع preventDefault يمنع تمرير الصفحة. اربط كل تسمية بـ for/id واجعل الصفّ كله مساحة ضغط لا تقلّ عن 24 بكسل في كل بُعد، ولا تضع رابطاً ولا زرّاً داخل التسمية. اجمع المربّعات المتعلّقة بسؤال واحد في <fieldset> واجعل <legend> أول عنصر فيه. أضف مربّع «تحديد الكل» واحسب حالته من الأبناء: محدَّد إن اكتملوا، و indeterminate إن كان بعضهم فقط، وأعِد الخاصّية إلى false صراحةً عند اكتمال العدد أو خلوّه. اضبط indeterminate من JavaScript على مرجع العنصر بعد الرسم لأنها خاصّية DOM لا سمة HTML، واستعمل aria-checked="mixed" إن كان المربّع مخصّصاً. ارسم الشكل بـ appearance: none ولا تُخفِ الحقل بـ display: none، وأعطِ المربّع حلقة تركيز خاصّة به على ‎:focus-visible‎ بـ outline مع outline-offset، ولا تكتب outline: none بلا بديل ظاهر. والزم الخصائص المنطقية في كل موضع: gap للمسافة بين المربّع والنصّ، و padding-inline-start لتفريع الشجرة، و border-inline-start لخطّها الرأسي، و inset-inline-start لأي عمود ملتصق، ولا تكتب margin-right ولا padding-left ولا left. ولا تعكس علامة الصح حين تنقلب الصفحة. واحسب على الخادم أن المربّع غير المحدَّد لا يُرسل شيئاً، فغياب المفتاح معناه false.

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

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

موضع المربّع في السطر

ينعكس

المربّع يجلس عند بداية السطر المنطقية، أي على اليمين في العربية، وترتيب DOM الصحيح (المربّع ثم النصّ) يعطيك ذلك في الاتجاهين بلا قاعدة إضافية واحدة. باعد بينهما بـ display: flex مع gap، فـ margin-right على المربّع تفتح الفجوة من الجهة الخطأ بعد الانعكاس. ولا تُضف flex-direction: row-reverse داخل [dir="rtl"] ظنّاً أنك تصحّح الاتجاه: المحور المضمّن انعكس مرّة بالفعل، والقلب الثاني يجرّ المربّع إلى اليسار ويجعل الترتيب البصري مخالفاً لترتيب التنقّل بـ Tab.

علامة الصح والشرطة

لا ينعكس

رمز الصح لا ينقلب مع اتجاه الصفحة: خاصّية Bidi_Mirrored للمحرف ✓ قيمتها No، ورسم المتصفّح الأصلي للمربّع لا يتغيّر في RTL أصلاً. لذلك تكسر القاعدة الشاملة [dir="rtl"] svg { transform: scaleX(-1) } العلامة التي رسمتها بيدك وتتركها مقلوبة. أمّا الشرطة الوسيطة فمتماثلة حول محورها الرأسي، فلا يظهر عليها أثر القلب إطلاقاً، وهذا سبب مرور الخطأ في المراجعة وظهوره متأخّراً على حالة التحديد وحدها.

تدرّج الشجرة المتفرّعة

ينعكس

إزاحة الأبناء في شجرة المربّعات تُكتب padding-inline-start لا padding-left، فتتدرّج من اليمين في العربية ومن اليسار في الإنجليزية. والقائمة <ul> تفعل ذلك وحدها لأن ورقة أنماط المتصفّح تمنحها padding-inline-start: 40px، والخطأ الشائع هو إلغاء ذلك بـ padding-left: 1.5rem فيعود التفريع إلى الجهة الخطأ. ولو رسمت الخطّ الرأسي الذي يربط الأبناء فهو border-inline-start لا border-left.

حلقة التركيز

لا ينعكس

الحلقة المرسومة بـ outline محايدة تجاه الاتجاه: outline تحيط الجهات الأربع، و outline-offset يبعدها عنها جميعاً بالقدر نفسه، فلا شيء هنا يحتاج قلباً. الخلل في الحلقات المصنوعة يدوياً: box-shadow: -3px 0 0 var(--ring) لا مقابل منطقي لها أصلاً، و‎::before‎ مثبّت بـ left: -4px يبقى على اليسار بعد الانعكاس فيقع فوق النصّ بدل أن يطوّق المربّع، وكان inset-inline-start: -4px هو الذي يتبع الاتجاه. والجهة التي تُقصّ منها الحلقة تتبدّل معها، فإن كان على الصفّ overflow: hidden أو حشو أقلّ من سُمك الحلقة قُصَّت عند البداية المنطقية حيث صار المربّع، فاترك padding-inline-start يتّسع لها.

عمود التحديد في الجدول

ينعكس

عمود المربّعات في الجداول هو أول الأعمدة، أي أقصى اليمين في العربية، وترتيب الأعمدة ينعكس من تلقائه لأن الجدول يتبع direction. لكن تثبيت العمود بـ position: sticky مع left: 0 يتركه لاصقاً بالحافّة اليسرى بعد الانعكاس فيسبح فوق باقي الأعمدة بدل أن يثبت عند أوّلها، والصحيح inset-inline-start: 0. والفاصل الذي يميّز هذا العمود border-inline-end لا border-right.

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

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

HTML<input type="checkbox" name="…" value="…">المربّع غير المحدَّد لا يُرسل مفتاحه إطلاقاً، وبلا value تصل كل المحدَّدات بالقيمة "on".
HTMLinput.indeterminate = trueخاصّية بلا سمة HTML مقابلة، فلا سبيل إليها إلا من JavaScript بعد الرسم.
ARIArole="checkbox" + aria-checked="mixed"الطريق الوحيد لإعلان الحالة الثالثة على مربّع مخصّص؛ المربّع الأصلي يعلنها وحده.
CSSappearance: noneتُسقط رسم المتصفّح وحده، فيصير الإطار والتعبئة والعلامة من مسؤوليتك أنت.
HTML<fieldset> + <legend>الاسم المقروء للمجموعة، ويؤخذ من أوّل legend داخل <fieldset> دون سواه.
Radix UI<Checkbox.Root checked="indeterminate">الحالة الثالثة قيمة نصّية داخل خاصّية checked نفسها، و Checkbox.Indicator يظهر معها ومع true.
Material UI<Checkbox indeterminate />خاصّية مستقلّة ترسم الشرطة، وتعلو على checked في العرض دون أن تغيّر قيمته.

شوف كمان

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