الشارة
تضع نقطة أو عددًا صغيرًا على أيقونة أو عنصر تنقل أو صورة رمزية للدلالة على أن شيئًا ينتظر.
- الفئة
- التواصل
- الرموز
comp.badge- المستوى
- أساسي
- Web
tanrei-badgeschedule - SwiftUI
TanreiBadgeschedule - Jetpack Compose
TanreiBadgeschedule - Flutter
TanreiBadgeschedule - React Native
Badgeschedule
مولَّد من docs/status/status.json, specs/badge.spec.json
نقطة وعدد
<!-- بلا عدد: النقطة. والنقطة تحتاج إلى تسمية. -->
<tanrei-badge label="رسائل جديدة">
<tanrei-button variant="text" label="المحادثة"><span slot="icon">chat</span></tanrei-button>
</tanrei-badge>
<!-- القيمة 0 لا ترسم شيئًا؛ والعدد الذي يتجاوز max (999) يظهر 999+. -->
<tanrei-badge count="3" label="3 غير مقروءة">
<tanrei-button variant="text" label="الوارد"><span slot="icon">inbox</span></tanrei-button>
</tanrei-badge>
<script type="module">
import '@tanrei/web/components/badge';
import '@tanrei/web/components/button';
const badge = document.querySelector('tanrei-badge[count]');
badge.count = unread;
badge.label = `${unread} غير مقروءة`;
</script>import { Badge, Button } from '@tanrei/react';
<Badge label="رسائل جديدة">
<Button variant="text" label="المحادثة" onClick={openChat}><span slot="icon">chat</span></Button>
</Badge>
<Badge count={unread} label={`${unread} غير مقروءة`}>
<Button variant="text" label="الوارد" onClick={openInbox}><span slot="icon">inbox</span></Button>
</Badge>import Tanrei
// القيمة nil هي النقطة.
TanreiBadge(count: nil, label: "رسائل جديدة") {
TanreiIconButton("المحادثة", action: openChat) { Image(systemName: "bubble.left") }
}
TanreiBadge(count: unread, label: "\(unread) غير مقروءة") {
TanreiIconButton("الوارد", action: openInbox) { Image(systemName: "tray") }
}
// الشيء نفسه في صورة مُعدِّل على أي عرض.
Image(systemName: "bell").accessibilityLabel("الإشعارات").tanreiBadge(1200, label: "أكثر من 999 غير مقروءة")import io.github.chnotchy.tanrei.components.TanreiBadge
import io.github.chnotchy.tanrei.components.TanreiIconButton
import io.github.chnotchy.tanrei.primitives.TanreiText
// القيمة null هي النقطة.
TanreiBadge(count = null, contentDescription = "رسائل جديدة") {
TanreiIconButton(onClick = ::openChat, contentDescription = "المحادثة") { TanreiText("chat") }
}
TanreiBadge(count = unread, contentDescription = "$unread غير مقروءة") {
TanreiIconButton(onClick = ::openInbox, contentDescription = "الوارد") { TanreiText("inbox") }
}import 'package:tanrei/tanrei.dart';
// بلا عدد: النقطة.
TanreiBadge(
semanticLabel: 'رسائل جديدة',
child: TanreiIconButton(onPressed: openChat, semanticLabel: 'المحادثة', icon: const Icon(AppSymbols.chat)),
);
TanreiBadge(
count: unread,
semanticLabel: '$unread غير مقروءة',
child: TanreiIconButton(onPressed: openInbox, semanticLabel: 'الوارد', icon: const Icon(AppSymbols.inbox)),
);import { Badge, IconButton } from '@tanrei/react-native';
<Badge label="رسائل جديدة">
<IconButton accessibilityLabel="المحادثة" onPress={openChat}>chat</IconButton>
</Badge>
<Badge count={unread} label={`${unread} غير مقروءة`}>
<IconButton accessibilityLabel="الوارد" onPress={openInbox}>inbox</IconButton>
</Badge>الأعداد والحد الأقصى
البنية
البنية
count-single كما ترسمه حزمة الويب.| الجزء | الوصف |
|---|---|
1 container | The pill that holds the count: at least as wide as it is high, so a single digit sits in a circle. |
2 dot · اختياري | The container's form when there is no count: a small circle with no label. |
3 label · اختياري | The count, or the capped count followed by a plus sign. |
4 outline | A ring outside the dot or the pill that separates the badge from what it overlaps. |
القياسات
القياسات
| الطول | الرمز | القيمة (px) |
|---|---|---|
container padding-inline | tanrei.comp.badge.container.padding-inline | 4 |
container height | tanrei.comp.badge.container.height | 16 |
count-single كما ترسمه حزمة الويب.الخصائص
| الخاصية | النوع | القيمة الافتراضية | الوصف |
|---|---|---|---|
count | number | null | How many things are waiting. Null draws the dot (something is new, the number is unknown or unimportant); 0 draws nothing; above 0 draws the number. |
max | number | 999 | The largest count shown as it is; a larger one shows as max followed by a plus sign (999+). |
label | string | "" | What assistive technology reads after the anchor's own name ("3 unread"). With no label, the displayed count is read; a dot needs one. |
المنافذ
| المنفذ | الوصف |
|---|---|
default | The anchor the badge sits on, which it needs: an icon, an icon-only Button, a navigation item or an avatar. Its first element is the anchor; a badge with no element to sit on draws nothing (BDG-16). In fixtures the anchor is the glyph of the system's icon set that the fixture's content names, drawn at the icon size sys.icon.medium in the color on-surface-variant; its accessible name is that glyph name ("mail"), and the badge adds the fixture's label to it as its value ("mail, 3 unread"). |
الحالات
السلوك
متطلبات بصيغة EARS. يُختبر تنفيذ كل منصة مقابل هذه المعرّفات. يُعرض نص المتطلبات كما ورد في ملف المواصفة، بالإنجليزية.
| المعرّف | المتطلب |
|---|---|
BDG-1 | WHILE count is null THE badge SHALL draw a circle of diameter comp.badge.dot.size in comp.badge.container.color with no label, its center comp.badge.dot.offset inside the anchor's top-end corner on both axes; the dot is a circle whatever corners the container has. |
BDG-2 | WHILE count is above 0 THE badge SHALL draw a container at least comp.badge.container.height high and at least as wide as it is high, with comp.badge.container.padding-inline on each side of its label and corners of comp.badge.container.shape, filled with comp.badge.container.color, and the label centered in comp.badge.label.typescale and comp.badge.label.color. |
BDG-3 | WHILE count is above 0 THE badge SHALL place the center of the container's leading circle (a square of the container's drawn height at its start) comp.badge.container.offset inside the anchor's top-end corner on both axes, so that a longer count grows toward the end, away from the anchor. |
BDG-4 | WHEN count is greater than max THE badge SHALL show max followed by a plus sign (999+ with the default max) instead of count, and SHALL otherwise show count in full. |
BDG-5 | WHILE count is 0 THE badge SHALL draw nothing and expose nothing to assistive technology, and its anchor SHALL render and be announced as it would without a badge. |
BDG-6 | THE badge SHALL draw comp.badge.outline.width of comp.badge.outline.color around the dot or the container, outside its edge, without changing the badge's size or position. |
BDG-7 | THE badge SHALL NOT change its anchor's size or position, SHALL NOT take space in the layout (it may extend past the anchor's bounds), and SHALL NOT take pointer, touch or keyboard input: input over the badge reaches the anchor. |
BDG-8 | THE badge SHALL hide its drawn dot and number from assistive technology and SHALL add label to its anchor's accessible description, so that the anchor is announced with its own name followed by label; WHILE label is empty and count is above 0 THE badge SHALL add the displayed text instead (for example 999+). |
BDG-9 | WHILE count is null and label is empty THE badge SHALL still render, and in debug builds report that the dot tells assistive technology nothing. |
BDG-10 | WHEN count or label changes THE badge SHALL update its anchor's accessible description and SHALL NOT announce the change by itself. |
BDG-11 | WHERE the layout direction is right-to-left THE badge SHALL take the anchor's top-left corner as its top-end corner and grow toward the left, and SHALL isolate its label from the surrounding direction so that a capped count keeps its plus sign after the digits. |
BDG-12 | THE badge SHALL format count and max with the current locale's digits and no grouping separator, and SHALL draw digits with tabular figures where the font has them, so that a changing count does not shift its neighbours. |
BDG-13 | WHILE the text size is enlarged THE badge SHALL grow its container with its label, keep the center of the leading circle — a square of the grown container's height — at its placement, and keep the dot at comp.badge.dot.size. |
BDG-14 | IF count is negative or not a number THE badge SHALL render as for 0; IF count is not a whole number THE badge SHALL use it rounded down; IF max is below 1 THE badge SHALL use its default of 999; in each case debug builds SHALL report the value. |
BDG-15 | THE badge SHALL take its layout direction from where it is used — the direction of the nearest ancestor that sets one, and the platform's own where none does — and SHALL have no direction setting of its own; WHEN that direction changes while the badge is shown THE badge SHALL draw itself for the new direction at once, without a motion of its own, keeping what it shows and its state. |
BDG-16 | IF the badge has no anchor THE badge SHALL draw nothing, take no space and expose nothing to assistive technology, whatever its count and label, and in debug builds report that a badge needs an anchor; WHEN an anchor is given to it THE badge SHALL draw itself on that anchor (BDG-1, BDG-3) and describe it (BDG-8) at once, and WHEN its anchor is taken away it SHALL give the anchor back its own description and draw nothing again. |
مطابقة الأسماء بين المنصات
التكافؤ لا يعني واجهات برمجية متطابقة. تعبّر كل منصة عن العقد نفسه بأسلوبها.
| المواصفة | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| المكوّن | tanrei-badge | TanreiBadge | TanreiBadge | TanreiBadge | Badge |
count · الخاصية | count | count | count | count | count |
max · الخاصية | max | max | max | max | max |
label · الخاصية | label | label | contentDescription | semanticLabel | label |
| ملاحظات | Wraps its anchor in the default slot. No count attribute is the dot. The badge sets aria-description on the slotted anchor and keeps its own shadow content aria-hidden. | TanreiBadge(count: Int?, max:, label:) { anchor }, with a .tanreiBadge(_:max:label:) modifier for the same view (SwiftUI's own .badge works only in lists and tab bars). count: nil is the dot. | One composable plays Material's BadgedBox and Badge together: TanreiBadge(count: Int?, max, contentDescription) { anchor }. count = null is the dot. | As Flutter's Badge.count, with the anchor as child; count: null is the dot (Flutter's plain Badge). | The anchor is children. label is appended to the anchor's accessibilityValue.text, not set as the badge's own accessibilityLabel: the badge itself is not an accessibility element. |
الرموز
| الرمز | يشير إلى | القيمة المحلولة (فاتح) |
|---|---|---|
--tanrei-comp-badge-container-color | sys.color.primary | |
--tanrei-comp-badge-container-height | — | 16px |
--tanrei-comp-badge-container-offset | sys.space.space0 | 0px |
--tanrei-comp-badge-container-padding-inline | sys.space.space50 | 4px |
--tanrei-comp-badge-container-shape | sys.shape.corner.full | 9999px |
--tanrei-comp-badge-dot-offset | sys.space.space25 | 2px |
--tanrei-comp-badge-dot-size | — | 6px |
--tanrei-comp-badge-label-color | sys.color.on-primary | |
--tanrei-comp-badge-outline-color | sys.color.surface | |
--tanrei-comp-badge-outline-width | sys.border.thick | 2px |
--tanrei-comp-badge-label-typescale | sys.typescale.label-small | 500 12px/1.3333 |
نماذج الاختبار
عروض مرجعية. تلتقط كل المنصات لقطة لكل نموذج بالمعرّف نفسه حتى يمكن مقارنة النتائج.
dot
label: New messages count-single
count: 3 label: 3 unread count-double
count: 42 label: 42 unread count-overflow
count: 1200 label: More than 999 unread count-custom-max
count: 120 max: 99 label: More than 99 unread count-zero
count: 0 label: No unread messages مولَّد من specs/badge.spec.json, tokens/comp/badge.tokens.json, design/tokens/
استخدم الشارة لتبيّن أن شيئًا ينتظر خلف أيقونة أو عنصر تنقل أو صورة رمزية: رسائل غير مقروءة، نشاط جديد، تحديثات تنتظر التثبيت. فهي تلفت النظر إلى الموضع الذي يستطيع الشخص أن يتصرف فيه. ليس للشارة كلمة ولا مكان خاص بها في التخطيط، ولذلك ليست تسمية حالة. للكلمة التي تسمّي حالة أو فئة، مثل «جديد» أو «تجريبي» أو «Pro»، استخدم الوسم.
يُفرَّق بين الثلاثة بما يفعله كل منها:
| المكوّن | ما هو | أين يقف | هل يستقبل إدخالًا |
|---|---|---|---|
| الشارة | نقطة أو عدد ملحق بعنصر آخر. | على زاوية عنصر الارتكاز، خارج التخطيط. | لا. الإدخال يصل إلى عنصر الارتكاز. |
| الرقاقة | عنصر تحكم. | داخل التخطيط. | نعم: تُضغط أو تُبدَّل أو تُزال، ولها تركيز وحالات. |
| الوسم | تسمية ثابتة. | داخل التخطيط، ضمن السطر. | أبدًا. |
- إن كان رقمًا أو نقطة فوق شيء آخر فهو شارة.
- إن كان يُضغط أو يُحدَّد أو يُزال فهو رقاقة. وإن كان يقود إلى مكان ما فهو رابط.
- إن كان كلمة لا تقول إلا ما هو الشيء أو ما حالته فهو وسم.
والعدد الذي يقف وحده في صف، كعدد النتائج بجانب عنوان، ليس شارة هو الآخر: للشارة دائمًا عنصر ارتكاز، والشارة التي لا تجد ما تستقر عليه لا ترسم شيئًا. اكتب هذا العدد نصًّا يُقرأ في موضعه مع سائر كلمات الصف: في نهاية عنصر من درج التنقل هو trailingText لذلك العنصر. وحيث يسمّي حالة، استخدم وسمًا.
تتبع الهيئة قيمة count، فلا طراز (variant) يُختار.
| الهيئة | count |
يُستخدم لـ |
|---|---|---|
| نقطة | لا شيء (null) | شيء جديد، والعدد مجهول أو لا يهم. |
| عدد | أكبر من 0 | العدد يساعد الشخص على أن يقرر هل ينظر الآن: رسائل غير مقروءة، طلبات معلّقة. |
| لا شيء | 0 | لا شيء ينتظر. اربط الشارة بالعدد مباشرة؛ ولا حاجة إلى شرط حولها. |
العدد الذي يتجاوز max يظهر في الصورة max+ (أي 999+ افتراضيًّا)، فلا تزيد الشارة أبدًا على أربعة محارف. اخفض max (إلى 99 مثلًا) حيث يكون عنصر الارتكاز صغيرًا أو حيث تفقد القيمة الدقيقة أهميتها أبكر.
- أعطِ كل شارة تسمية تُقرأ جيدًا بعد اسم عنصر الارتكاز: «3 غير مقروءة» بعد «الوارد». والنقطة أحوج ما تكون إليها، لأنها من دونها لا تقول شيئًا لقارئ الشاشة.
- اترك فسحة حول عنصر الارتكاز. تقع الشارة على زاويته العليا من جهة النهاية وقد تتجاوز حدوده؛ ولا تحرّكه ولا تغيّر حجمه أبدًا.
- أزِل الشارة حين يرى الشخص ما تشير إليه، ليبقى لها معنى.
- لا تضع شارة على كل عنصر. حين يُعلَّم كل شيء لا يبرز شيء.
- لا تضع شارة على تسمية زر ولا في نص متصل. مكانها على أيقونة أو عنصر تنقل أو صورة رمزية. والكلمة التي بجانب تسمية أو في سطر من النص وسم.
- لا تستخدم لون الشارة لتقول أي نوع من الأشياء ينتظر، ولا مدى استعجاله. قل ذلك في التسمية، أو في المحتوى الذي تشير إليه.
- لا تجعل الشارة تفاعلية. اللمسات والنقرات عليها تصل إلى عنصر الارتكاز، وهناك يكون الإجراء.
- الدور
none- التسمية
- The badge has no role or name of its own. label (or, without one, the displayed count) becomes part of the anchor's accessible description, so the anchor is announced as, for example, "Inbox, button, 3 unread". The drawn number is hidden, so it is never read twice.
- الحد الأدنى للهدف
- تستوفي مساحة اللمس الحد الأدنى لحجم الهدف حتى لو كان الحجم المرئي أصغر. minimum 32px · pointer 44px · spacing 8px · touch 48px
لوحة المفاتيح
| المفاتيح | الإجراء |
|---|
ملاحظات
- Not interactive: the badge takes no input (BDG-7), so the minimum target is the anchor's and the badge never shrinks or covers it for input.
- A dot conveys its meaning by color and position only, so a dot without a label fails for screen reader users (BDG-9); give it one ("new messages").
- Where the count must be announced when it changes (a live chat), the product uses a live region; the badge does not announce by itself (BDG-10).
- A badge marks an anchor and never stands alone in a line or a row (BDG-16). A count that stands by itself, such as the count at the end of a navigation drawer's row, a tree view's row or a menu item, is text, read in its place as the row's own words: that component's trailing text. A word that names a state is a tag.
- How the description reaches the anchor is each platform's idiom: the web sets it on the slotted anchor (an ID reference cannot cross the shadow root), Apple platforms append it to the anchor's accessibility value, Compose merges it into the anchor's semantics as a state description, Flutter adds it to the anchor's semantics value, React Native to its accessibility value.
مولَّد من specs/badge.spec.json, tokens/sys/measure.tokens.json
- النقطة والرقم المرسومان مخفيّان عن التقنيات المساعدة، فلا يُقرأ العدد مرتين أبدًا.
- تصير التسمية (أو، في غيابها، العدد المعروض مثل
999+) جزءًا من عنصر الارتكاز، فيُعلَن العنصر مرة واحدة بهما معًا: «الوارد، زر، 3 غير مقروءة». ولكل منصة أسلوبها:aria-descriptionعلى عنصر الارتكاز في الويب، وقيمة إمكانية الوصول (accessibility value) على منصات Apple و Flutter و React Native، ووصف الحالة (state description) على Compose. - العدد 0 لا يضيف شيئًا: يُعلَن عنصر الارتكاز كما لو لم تكن عليه شارة.
- الشارة بلا عنصر ارتكاز لا ترسم شيئًا ولا تشغل حيّزًا ولا تقول شيئًا، ويُبلَّغ عن ذلك في بنيات التنقيح (debug). وإذا أُعطيت عنصر ارتكاز لاحقًا رُسمت عليه في الحال.
- تغيّر العدد أو التسمية يحدّث ما يقوله عنصر الارتكاز، من دون إعلان يقاطع الشخص.
- لا تستقبل إدخالًا ولا تشغل حيّزًا، فتبقى مساحة اللمس لعنصر الارتكاز كاملة.
- النقطة بلا تسمية، والعدد السالب، وقيمة
maxالأقل من 1 يُبلَّغ عنها في بنيات التنقيح (debug).
- اكتب تسمية تُكمل اسم عنصر الارتكاز ولا تكرّره: «3 غير مقروءة»، لا «في الوارد 3 رسائل غير مقروءة».
- اجعل عنصر الارتكاز عنصرًا واحدًا له اسم خاص به، كزر أيقونة له تسمية. على Compose، الأيقونة المجرّدة التي ليست داخل عنصر يدمج فروعه تحتاج إلى عنصر كهذا حولها ليُقرأ الاسم والتسمية معًا.
- إن وجب إعلان التغيّر لحظة حدوثه، كما في محادثة حيّة، فأعلنه من منتجك بمنطقة حيّة (live region). الشارة لا تُعلِن.