UIDex

الشارة

Badge

role="status" / badgeContent

اسمه كمانبادج، عدّاد الإشعارات، النقطة الحمراء، شارة العدّ، شارة الحالة، عدّاد غير المقروء، notification badge، count badge، dot badge، status badge، unread count، notification dot

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

لو قلت عليه…

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

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

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

الاسم المقروء للجرس: «الإشعارات، 3 إشعارات غير مقروءة»الإشعارات، 3 إشعارات غير مقروءة. الرسائل، 128 رسالة غير مقروءة. الحساب، يوجد نشاط جديد.الشارة نفسها ليست زرّاً، والضغط عليها يمرّ إلى الأيقونة تحتها.
RTLالشارة تنتقل إلى ركن النهاية المنطقية، والرقم معزول بـ dir="ltr"

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

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

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

ابنِ شارة تلتصق بأيقونة أو تبويب. اجعل المرساة زرّاً حقيقياً يحمل position: relative بلا overflow: hidden، واجعل الشارة span مطلق الموضع بـ pointer-events: none، ولا تجعلها زرّاً ولا رابطاً ولا تعطها tabindex. ثبّتها بـ inset-block-start و inset-inline-end بقيم سالبة حتى تنتقل إلى ركن النهاية المنطقية وحدها، ولا تكتب top ولا right، وإن احتجت transform فوقّع الإزاحة بمتغيّر ‎--dir قيمته ‎-1 داخل [dir="rtl"] لأنّ transform لا نظير منطقيّ له. حجّم الكبسولة بـ min-inline-size مع padding-inline بدل عرض ثابت، وأضف font-variant-numeric: tabular-nums. اقطع العدد عند سقف في العرض فقط واترك الرقم الحقيقي في الحالة وفي النصّ المقروء، ولا تعرض كبسولة قيمتها ٠. لفّ الأرقام مع علامة الزائد في عنصر واحد يحمل dir="ltr" حتى لا تنتقل العلامة إلى الجهة الأخرى داخل الصفحة العربية، واشتقّ شكل الأرقام من Intl.NumberFormat بنظام واحد للمنتج كلّه. وفّر شكلين: عدّاد بالرقم ونقطة بلا رقم، وأعطِ النقطة نصّاً مخفيّاً بصريّاً لأنّ span المجرّد لا يقبل aria-label. اكتب الاسم المقروء جملة تحمل المعدود في aria-label على المرساة، واحجب الكبسولة كلّها بـ aria-hidden="true" حتى لا يُنطق العدد مرّتين. وأبقِ منطقة role="status" واحدة في الـ DOM من أوّل رسم يتغيّر نصّها مع العدد، ولا تعتمد على اللون وحده في التفريق بين «جديد» و«خطأ».

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

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

ركن الشارة

ينعكس

الشارة تجلس عند النهاية المنطقية لأعلى المرساة، أي أعلى اليمين في الإنجليزية وأعلى اليسار في العربية. وإزاحتان منطقيّتان سالبتان تكفيان: inset-block-start: -6px مع inset-inline-end: -6px، فتنتقل الجهة وحدها بلا قاعدة خاصّة بـ RTL. أمّا الوصفة الشائعة inset-inline-end: 0 مع transform: translate(50%, -50%) فينعكس نصفها فقط، لأنّ transform لا نظير منطقيّ له، فتدفع translate(50%) الشارة نحو اليمين في الحالتين: خارج المرساة في الإنجليزية وفوق الأيقونة نفسها في العربية، والحلّ توقيع الإزاحة translate(calc(50% * var(--dir)), -50%) مع ضبط ‎--dir: -1 داخل [dir="rtl"].

علامة الزائد في السقف

لا ينعكس

في نصّ مثل 99+ تحمل علامة الزائد صنف bidi المسمّى ES، والقاعدة W4 لا تضمّها إلى العدد إلا إذا وقعت بين رقمين أوروبيين، ولا رقم بعدها هنا. فتبقى محايدة وتأخذ اتجاه الفقرة، فتظهر داخل الصفحة العربية قبل الأرقام لا بعدها. اعزل الأرقام والعلامة معاً في عنصر واحد يحمل السمة dir="ltr"، فالسمة وحدها تجلب unicode-bidi: isolate من ورقة أنماط المتصفّح، بينما direction: ltr في CSS لا تعزل شيئاً وتحتاج unicode-bidi: isolate بجانبها.

نظام الأرقام

لا ينعكس

اختر نظام أرقام واحداً للمنتج كلّه، إمّا العربية الهندية من U+0660 إلى U+0669 وإمّا اللاتينية من U+0030 إلى U+0039، واضبطه في مكان واحد بـ new Intl.NumberFormat("ar", { numberingSystem: "latn" }) بدل ضبطه داخل كلّ مكوّن. والنظامان ليسا نسختين من شيء واحد حتى في bidi: اللاتينية صنفها EN والعربية الهندية صنفها AN، والقاعدة W4 تضمّ علامة الزائد أو الناقص بين رقمين من صنف EN وحده، فتبقى العلامة محايدة بين الأرقام العربية الهندية. وانتبه إلى الخطّ كذلك، فكثير من خطوط الواجهات لا تحوي محارف الأرقام العربية الهندية فيهبط المتصفّح إلى خطّ بديل داخل الشارة وحدها، و font-variant-numeric: tabular-nums لا تحوّل بين النظامين بل تختار بين أشكال الأرقام الموجودة في الخطّ أصلاً.

النقطة وإزاحتها

ينعكس

النقطة دائرة متماثلة، فلا شيء في شكلها ينعكس، ولا يصحّ أن تمرّ عليها قاعدة عامّة مثل ‎[dir="rtl"] .icon { transform: scaleX(-1) }‎. لكنّ إزاحتها شيء آخر: مكتوبة بـ top: -2px مع right: -2px تبقى في الركن العلوي الأيمن الفيزيائي، وهو في العربية ركن البداية، فتقف النقطة في جهة وتقف بقيّة الشارات المكتوبة بخصائص منطقية في الجهة المقابلة داخل الشريط نفسه. اكتب الإزاحة inset-block-start و inset-inline-end، واترك حلقة box-shadow: 0 0 0 2px var(--surface) كما هي لأنّها متماثلة، وأعطِ أيّ clip-path جانبيّ نسخة معكوسة لأنّ clip-path بلا صيغة منطقية.

الشارة بعد تسمية نصّية

ينعكس

حين تجلس الشارة بعد نصّ تبويب لا فوق أيقونة، فهي عنصر في تدفّق السطر لا عنصر مطلق الموضع، وجهتها تأتي من السطر: بعد الكلمة يعني يسارها في العربية. اكتب الفجوة بـ margin-inline-start على الشارة أو gap على الحاوية، ولا تكتبها margin-left لأنّها بعد الانعكاس تلصق الشارة بالكلمة من جهة وتضاعف المسافة من الجهة الأخرى. وأبقِ الشارة بعد التسمية في شجرة الـ DOM في اللغتين واترك direction تتولّى التبديل البصري، أمّا flex-direction: row-reverse فيعكس مرّة ثانية ويجعل ترتيب القراءة مخالفاً لترتيب الشجرة الذي يمشي عليه قارئ الشاشة.

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

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

ARIArole="status"يحمل ضمناً aria-live="polite" و aria-atomic="true"، فيُقرأ نصّ المنطقة كاملاً لا الرقم المتغيّر وحده.
HTMLdir="ltr"السمة تجلب معها unicode-bidi: isolate من ورقة أنماط المتصفّح، فتبقى علامة الزائد ملاصقة لأرقامها.
Material UI<Badge badgeContent={4} max={99} showZero>max هو السقف، و showZero يفرض عرض الصفر، و variant="dot" يسقط الرقم كلّه.
Ant Design<Badge count={5} overflowCount={99} />أنت ديزاين تسمّي السقف overflowCount، وتخفي الشارة عند count={0} ما لم تضبط showZero.
Chakra UI<Badge variant="subtle">شارة تشاكرا وسم نصّي ساكن داخل التدفّق لا عدّاد ملتصق بركن، ومن هنا يبدأ خلط الأسماء.
SwiftUI.badge(3)تُركَّب على صفّ في List أو على تبويب، وتختفي وحدها حين تكون القيمة صفراً.
CSSinset-inline-endالخاصّية المنطقية التي تنقل الشارة إلى الركن الصحيح دون قاعدة خاصّة بـ RTL.

شوف كمان

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