القائمة (list)
تحمل مجموعة من عناصر المنتج نفسه (صفوفًا، ومربعات، وبطاقات، وملفات) في عمود واحد، أو سطر واحد، أو أسطر تلتف فتصنع شبكة. يقرؤها الناس، ويفتحونها واحدًا بعد آخر، ويختارون منها، أو يسحبونها إلى ترتيب آخر.
- الفئة
- الاحتواء
- الرموز
comp.list- المستوى
- أساسي
- Web
tanrei-listclock_loader_40 - SwiftUI
TanreiListschedule - Jetpack Compose
TanreiListschedule - Flutter
TanreiListschedule - React Native
Listschedule
مولَّد من docs/status/status.json, specs/list.spec.json
اسحب مربعًا إلى مكان آخر، أو اضغط مقبضه لقائمة من الحركات
القائمة صندوق. ما يعرضه كل عنصر هو محتواك أنت: نص، أو صورة، أو بطاقة، أو عنصر الملف (file item)، أو صف القائمة (list row)، وهو الصف المؤلف من عنوان رئيسي وسطر أو سطرين من النص المساعد وشيء قبلهما وشيء بعدهما، ومنه تتألف معظم القوائم. وتتولى القائمة كل ما حول العناصر: كيف تُرتَّب، والمسافة والخطوط بينها، وما هي بالنسبة إلى التقنيات المساعدة، وكيف ينتقل تركيز لوحة المفاتيح بينها، وأيها مختار، وكيف يضعها الناس في ترتيب آخر.
عمود، وسطر، وشبكة تلتف
عناصر تفتح شيئًا، وعناصر هي روابط، واختيار واحد
للقائمة نوع واحد من العناصر هو list-item، ويحمل كل عنصر محتواك أبناءً له. تضع الخاصية orientation العناصر في عمود (vertical، وهي القيمة الافتراضية) أو في سطر (horizontal)؛ وتسمح wrap لها بالانتقال إلى أسطر تالية فتصنع شبكة؛ وتحدد gap المسافة بينها (none وsmall وmedium وlarge)، وترسم dividers خطًا رفيعًا بينها في قائمة لا تلتف.
العنصر ثابت ما لم تجعله شيئًا آخر. لا يرسم العنصر الثابت شيئًا ولا يضيف مسافة: محتواه هو العنصر، ويمكن أن يحمل عناصر التحكم الخاصة بك. تجعل interactive العنصر زرًا واحدًا، وتجعله href رابطًا واحدًا يعامله المتصفح كأي رابط (يمكن فتحه في علامة تبويب جديدة ونسخ عنوانه؛ وتفتح القيمة target="new" علامة تبويب جديدة وتضع على العنصر علامة open_in_new، وتقول targetLabel ذلك). والقائمة التي قيمة selection فيها single أو multiple هي مربع قائمة، وعناصرها خيارات تُختار بقيمة value. والعنصر التفاعلي أو الرابط أو الخيار يضيف حشوة إلى محتواه، ويرسم كل الحالات، ويُسمّى بـ label أو بنص محتواه إن لم توجد.
<!-- A column of your own boxes, 8 px apart. -->
<tanrei-list gap="small">
<tanrei-list-item><my-card>…</my-card></tanrei-list-item>
<tanrei-list-item><my-card>…</my-card></tanrei-list-item>
</tanrei-list>
<!-- A grid of tiles: a line that wraps. -->
<tanrei-list orientation="horizontal" wrap gap="medium">
<tanrei-list-item><img src="1.jpg" alt="Harbour at dawn" /></tanrei-list-item>
<tanrei-list-item><img src="2.jpg" alt="Market street" /></tanrei-list-item>
</tanrei-list>
<!-- Items that are one button or one link each, with lines between them. A list row draws a headline and its lines. -->
<tanrei-list dividers>
<tanrei-list-item interactive>
<tanrei-list-row leading-icon="wifi" headline="Wi-Fi" supporting-text="Connected"></tanrei-list-row>
</tanrei-list-item>
<tanrei-list-item href="/privacy/">Privacy policy</tanrei-list-item>
<tanrei-list-item href="https://example.com/tanrei" target="new" target-label="opens in a new tab">Source code</tanrei-list-item>
</tanrei-list>
<!-- A listbox: every item is an option, chosen by its value. -->
<tanrei-list selection="single" label="Theme" value="light">
<tanrei-list-item value="light">Light</tanrei-list-item>
<tanrei-list-item value="dark">Dark</tanrei-list-item>
</tanrei-list>
<script type="module">
import '@tanrei/web/components/list';
// The list sets its own value, or values, before it fires change, which carries them.
document.querySelector('[selection="single"]').addEventListener('change', (e) => setTheme(e.detail.value));
</script>import { List, ListItem } from '@tanrei/react';
<List orientation="horizontal" wrap gap="medium">
{photos.map((photo) => (
<ListItem key={photo.id}><img src={photo.src} alt={photo.alt} /></ListItem>
))}
</List>
<List selection="single" label="Theme" value={theme} onChange={(e) => setTheme(e.currentTarget.value)}>
<ListItem value="light">Light</ListItem>
<ListItem value="dark">Dark</ListItem>
</List>المنصات SwiftUI وCompose وFlutter وReact Native متأخرة: تقدّم القائمة السابقة، التي لصفوفها عنوان ونص مساعد وأجزاء في البداية والنهاية، ولا شيء من هذه المراجعة بعد.
عمود يعيد الناس ترتيبه، ولكل عنصر زر حذف خاص بالصفحة
تسمح الخاصية reorderable للناس بوضع العناصر في ترتيب آخر، في عمود أو سطر أو شبكة. يرسم كل عنصر مقبضًا في بدايته، علامةً على أنه يمكن تحريكه. ويمكن سحب العنصر كله لا المقبض وحده: تلتقطه الفأرة ما إن يتحرك بضع وحدات بكسل، والإصبع أو القلم بعد ضغط مطوَّل، حتى يبقى التمرير بالإصبع يمرر الصفحة (ولمسة على المقبض تسحب فورًا). تتنحى العناصر الأخرى عن الطريق، ويتبع العنصر المؤشر فوقها، ويستقر في مكانه حين يُفلَت.
ليس السحب الطريقة الوحيدة أبدًا. المقبض زر: الضغط عليه، أو Enter، يفتح قائمة خيارات بأربع حركات (إلى البداية، ومكانًا واحدًا إلى الأمام، ومكانًا واحدًا إلى الخلف، وإلى النهاية). وبلوحة المفاتيح، يرفع Space على المقبض العنصر، وتنقله مفاتيح الأسهم مكانًا في كل مرة (Home وEnd إلى المكان الأول والأخير)، ويضعه Space أو Enter، ويعيده Escape. وتُعلَن كل خطوة.
الترتيب بياناتك، فلا تغيّره القائمة أبدًا. كل طريقة للتحريك تطلق move مع from، فهرس العنصر، وto، الفهرس الذي يجب أن يأخذه، وكلاهما يُعد من 0. أعد ترتيب عناصرك استجابةً لذلك. حينها ترسم القائمة العناصر وهي تتبادل الأماكن، وتُبقي التركيز على مقبض العنصر المنقول، وتخبر التقنيات المساعدة بمكانه الآن. وإن لم تغيّر شيئًا خلال زمن comp.list.motion.reorder-duration فقد رُفضت الحركة: تعود العناصر ولا يُعلَن شيء.
الكلمات كلماتك: اسم المقبض (dragHandleLabel، مع الموضع {label} الذي تملؤه القائمة باسم العنصر)، ووصفه (dragHandleHint)، والحركات الأربع في القائمة المنبثقة، والجمل الثلاث التي تُعلَن (liftedText وmovedText وcancelledText، بالمواضع {label} و{position} و{count}). اذكرها على القائمة، أو مرة واحدة للمنتج بـ setWords(). وفي السطر، اكتب «التحريك يسارًا» و«التحريك يمينًا» حيث يقول العمود «التحريك لأعلى» و«التحريك لأسفل».
<tanrei-list id="fonts" reorderable label="Typefaces"
drag-handle-label="Reorder {label}"
drag-handle-hint="Space lifts, the arrow keys move, Space drops, Escape cancels; Enter opens the moves"
move-to-start-label="Move to top" move-before-label="Move up"
move-after-label="Move down" move-to-end-label="Move to bottom"
lifted-text="{label} lifted: {position} of {count}"
moved-text="{label}: {position} of {count}"
cancelled-text="{label} returned to {position} of {count}">
<tanrei-list-item label="Inter">Inter</tanrei-list-item>
<tanrei-list-item label="Noto Sans JP">Noto Sans JP</tanrei-list-item>
</tanrei-list>
<script type="module">
import '@tanrei/web/components/list';
const list = document.querySelector('#fonts');
// The list has moved nothing. Put the item where it was asked to stand: from and to count from 0.
list.addEventListener('move', ({ detail: { from, to } }) => {
const items = [...list.children];
list.insertBefore(items[from], items[to > from ? to + 1 : to] ?? null);
});
</script>import { List, ListItem } from '@tanrei/react';
const [fonts, setFonts] = useState(['Inter', 'Noto Sans JP', 'Helvetica Neue']);
// The order is your state. The list finds the moved item again in the order you render.
<List
reorderable
label="Typefaces"
dragHandleLabel="Reorder {label}"
moveToStartLabel="Move to top"
moveBeforeLabel="Move up"
moveAfterLabel="Move down"
moveToEndLabel="Move to bottom"
liftedText="{label} lifted: {position} of {count}"
movedText="{label}: {position} of {count}"
cancelledText="{label} returned to {position} of {count}"
onMove={(e) => {
const { from, to } = e.detail;
setFonts((now) => {
const next = [...now];
next.splice(to, 0, next.splice(from, 1)[0]);
return next;
});
}}
>
{fonts.map((font) => (
<ListItem key={font} label={font}>{font}</ListItem>
))}
</List>البنية
| الجزء | الوصف |
|---|---|
1 list | The box that holds the items. It lays them out in a column, a line or lines that wrap, draws nothing behind them and adds no space around them; it carries the semantics of a list or a listbox and moves keyboard focus among the items. |
2 item | One item: a box around the product's content. A static item draws nothing and adds no space, so its content is the item; an interactive item, a link item or an option pads its content and carries hover, focus and press, and the fill of a selected option. |
3 content | What the product puts in the item: text, a picture, a card, a file item — anything but, in an item that is itself an action, a link or an option, another interactive element. |
4 handle · اختياري | The mark at the start of every item of a reorderable list: an icon-only Button showing the drag glyph. It says the item can be moved, a pointer or a finger may drag the item by it, and pressing it opens the move menu; with the keyboard it lifts the item. The whole item can be dragged, not only the handle. |
5 move-menu · اختياري | The menu the handle opens: a Tanrei Menu with four moves — to the start, one place before, one place after, to the end. The way to reorder that needs neither dragging nor a held key. |
6 gap · اختياري | While an item is dragged, the empty place where it would land if it were dropped now: the other items slide aside to open it. Not drawn. |
7 mark · اختياري | The check at the end of a selected option: the glyph check of the system's icon set. Its box is reserved in every option of a selectable list. |
8 divider · اختياري | A hairline between two neighbouring items of a list that does not wrap, across the list, in the middle of the gap between them. |
9 focus-ring | Gap-then-ring indicator shown for keyboard focus, drawn inside an interactive item, a link item or an option. |
10 announcement · اختياري | The list's one live region: empty at rest, it holds the product's sentence that says an item was lifted, where it now stands, or that it went back. Not drawn. |
الخصائص
| الخاصية | النوع | القيمة الافتراضية | الوصف |
|---|---|---|---|
orientation | vertical | horizontal | "vertical" | The direction the items follow one another: vertical, a column from the top; horizontal, a line from the start edge (the left in a left-to-right layout). |
wrap | boolean | false | Lets the items run onto a further line where the list ends, so that they form a grid: a horizontal list wraps onto a line below where its width ends, a vertical list onto a column toward the end edge where the height the product gives it ends. Off, the items stay on one line or in one column. |
gap | none | small | medium | large | "none" | The space between two neighbouring items, and between two lines of a wrapping list: none, where the items touch, or comp.list.gap.small, medium or large. |
dividers | boolean | false | Draws a hairline between every two neighbouring items. Read only while wrap is false: the lines of a grid are told apart by their gap. |
iconSpace | auto | always | never | "auto" | Whether a List row that is an item's direct child keeps the room of a leading glyph where it draws no leading part, so that the headlines of the list start on one line. auto: a row keeps the room while the row of another item draws a glyph (leadingIcon), so that a list whose rows have no glyph starts their texts at the row's start. always: every such row keeps it. never: only a row that draws a glyph has it. An avatar, a picture or a control in a row's leading slot does not count and keeps no room beside it. The menu's, the tree view's and the navigation drawer's iconSpace, with the same values and default (LST-50). |
density | regular | compact | "regular" | How much room an item takes: the least height of an interactive item, a link item and an option and the padding above and below its content, and the density of every List row that is an item's direct child (LST-51). regular (a 48 item in the default system) is for a product used by touch as well as by pointer; compact (a 32 item, the tree view's compact row) is for a tool used with a pointer, where many items are looked through. For touch an item is never lower than the touch target in either (LST-11). The paddings at the start and the end, the gap and the text style are not changed. A design system may add values. |
selection | none | single | multiple | "none" | Whether people choose items: none for a list that shows or opens things, single for choosing one item, multiple for choosing any number. single and multiple make the list a listbox. |
label | string | "" | The accessible name of the list: what the items are. Required when selection is single or multiple, and when the list is reorderable. |
value | string | null | The value of the selected option while selection is single; null selects none. The list sets it when an option is chosen and emits change; the product may also set it. |
values | strings | | The values of the selected options while selection is multiple: a list of strings (the platforms take a set). An empty list selects none. |
reorderable | boolean | false | Lets people put the items in another order: every item draws a handle, the whole item can be dragged to another place, the handle opens a menu of moves, and the keyboard lifts and carries an item from its handle. The list reports the move that was asked for with the move event and never changes the order itself: the items are the product's data. Read only while selection is none; every item of a reorderable list is static. |
dragHandleLabel | string | "" | The accessible name of an item's handle: the whole name in the product's language with one place, {label}, which the list fills with the item's name ("Reorder {label}", "{label}を並べ替え"), so that the handles of two items are told apart. The product's: Tanrei has no word for it. Empty reads the product's words. Required while reorderable. |
dragHandleHint | string | "" | The description of every handle: how to move an item from it with the keyboard, in the product's words ("Press Space to lift, the arrow keys to move, Space to drop, Escape to cancel; Enter opens the moves"). Empty reads the product's words; with neither, the handle has no description. |
moveToStartLabel | string | "" | The move menu's item that moves the item to the first place ("Move to top", "Move to start"). Empty reads the product's words. Required while reorderable. |
moveBeforeLabel | string | "" | The move menu's item that moves the item one place toward the start ("Move up" in a column, "Move left" in a line). Empty reads the product's words. Required while reorderable. |
moveAfterLabel | string | "" | The move menu's item that moves the item one place toward the end ("Move down", "Move right"). Empty reads the product's words. Required while reorderable. |
moveToEndLabel | string | "" | The move menu's item that moves the item to the last place ("Move to bottom", "Move to end"). Empty reads the product's words. Required while reorderable. |
liftedText | string | "" | The sentence assistive technology is told when the keyboard lifts an item, with three places the list fills: {label} with the item's name, {position} with its place, counted from 1, and {count} with the number of items ("{label} lifted: {position} of {count}"). Empty reads the product's words. Required while reorderable. |
movedText | string | "" | The sentence assistive technology is told when a lifted item reaches another place, and once a move has taken effect, whether it came from the menu, the keyboard or a pointer: {label}, {position} (the place it has, or would have if dropped now, counted from 1) and {count} ("{label}: {position} of {count}"). Empty reads the product's words. Required while reorderable. |
cancelledText | string | "" | The sentence assistive technology is told when a move with the keyboard is cancelled and the item goes back: {label}, {position} (the place it has again) and {count} ("{label} returned to {position} of {count}"). Empty reads the product's words. Required while reorderable. |
الأحداث
| الحدث | الوصف |
|---|---|
change | The user changed the selection; carries value (single) or values (multiple) after the change. |
move | The user asked for an item to stand in another place: by the move menu, by dropping it after carrying it with the keyboard, or by dropping it after dragging it. Carries from, the item's index among the list's items when the move began, and to, the index it is to have once it has moved, both counted from 0 (moving the second item one place before is from 1, to 0; dropping the first item after the third is from 0, to 2). Emitted once per move, never for a move that ends where it began, and never while an item is carried. The product reorders its items; the list has changed nothing. |
العناصر
أنواع العناصر التي يكررها المكوّن. يسرد كل نموذج اختبار عناصره بالترتيب، وتسمّي كل منصة كل نوع بأسلوبها.
list-item · مجموعة — تحتوي عناصر
One item of the list: a box around the product's content. It is static, interactive, a link or, in a selectable list, an option.
| الخاصية | النوع | القيمة الافتراضية | الوصف |
|---|---|---|---|
label | string | "" | The item's name. An interactive item, a link item or an option is named by it in place of its content's text; every item is called by it in the handle's name and in what the list announces about a move. Empty: the headline of a List row that is its content's direct child (LST-49), else the text of the content. |
interactive | boolean | false | Makes the item one pressable action that emits its click (an item that changes a setting or opens something in place). Ignored in a selectable list, where every item is an option, and in a reorderable list, where every item is static. Its content must then hold no interactive element. An item that goes to a page takes href instead and is a link, not a button. |
href | string | "" | The destination: an address as the platform's links take one (a URL; on the web a path or a fragment as well). Not empty, it makes the item one link — a link item — whether interactive is set or not: the link role, activated by pointer, touch or Enter and not by Space, opened by the platform as its own links are, and drawn exactly as an interactive item is. Ignored in a selectable or a reorderable list. Its content must then hold no interactive element. |
target | same | new | "same" | Where the destination opens. same: where the person is. new: a new context — a new tab or window on the web, outside the current screen elsewhere — which the item marks with the glyph open_in_new at its end and says through targetLabel. Ignored without href. |
targetLabel | string | "" | What assistive technology reads after the link's name while target is new ("opens in a new tab"), in the product's language. Required while target is new; ignored otherwise. |
value | string | "" | Identifies the option: the list's value (single) or values (multiple) select the option whose value they hold, and change carries it. Unique within the list; needed for an option, unused by any other item. |
disabled | boolean | false | Prevents activation. The arrow keys still reach it. Ignored for a static item: an item that is not an action, a link or an option has nothing to disable. |
| المنفذ | الوصف |
|---|---|
default | The item's content: the product's own — text, an image, a card, a file item, a List row. In an interactive item, a link item or an option it is presentational and holds no interactive element; in a static item it may hold the product's controls, which keep their own semantics and focus. |
| الحدث | الوصف |
|---|---|
click | The interactive item or the link item was activated by pointer, touch, keyboard or assistive technology. A link item emits it before its destination opens; a product that takes the navigation itself (a client-side router) cancels it (LST-26). |
| المواصفة | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| العنصر | tanrei-list-item | TanreiListItem | TanreiListItem | TanreiListItem | ListItem |
label · الخاصية | label | label | label | semanticLabel | label |
interactive · الخاصية | interactive | action | onClick | onTap | onPress |
href · الخاصية | href | destination | href | href | href |
target · الخاصية | target | target | target | target | target |
targetLabel · الخاصية | targetLabel | targetLabel | targetLabel | targetSemanticLabel | targetLabel |
value · الخاصية | value | tag | value | value | value |
disabled · الخاصية | disabled | disabled | enabled | enabled | disabled |
click · الحدث | click | action | onClick | onTap | onPress |
| ملاحظات | A child element of tanrei-list; its children are its content, in its default slot. Its host has the role listitem or option; an interactive item draws a shadow element with role button and tabindex, as the interactive card does, and fires click; with href the shadow root holds an <a> in its place, inside the host's list item. target is the target attribute with the values same and new (the HTML spellings _self and _blank are read as those two); targetLabel is the target-label attribute. Web-only, forwarded to the anchor and to nothing else: rel (to which new adds noopener) and download. Cancelling click (preventDefault()) cancels the navigation. In a reorderable list the item draws its handle, a tanrei-button, in its shadow root before the slot. | TanreiListItem(label:, action:) { content } with a view builder for the content, tagged with .tag(value); destination: URL in place of action: makes it a SwiftUI Link. | TanreiListItem(label = …, onClick = …) { content } in the list's content lambda; enabled is the inverse flag. | TanreiListItem<T>(value:, onTap:, child:) widgets in the list's children:. | The element type of the list's items array: { key, label?, value?, disabled?, onPress?, href?, content }, where content is an element. |
list-row
A List row as an item's content: the List row component, as it is drawn anywhere else, which the item tells what it is (LST-48) and the list whether it keeps the room of a leading glyph (LST-50).
| الخاصية | النوع | القيمة الافتراضية | الوصف |
|---|---|---|---|
headline | string | "" | The row's name; the item is named by it where it has no label (LST-49). |
overline | string | "" | A short line above the headline. |
supportingText | string | "" | The text under the headline. |
leadingIcon | string | "" | A glyph of the system's icon set at the row's start; a row that draws one makes the other rows keep its room while iconSpace is auto (LST-50). |
trailingText | string | "" | A short value at the row's end. |
trailingIcon | string | "" | A glyph of the system's icon set at the row's very end. |
| المواصفة | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| العنصر | tanrei-list-row | TanreiListRow | TanreiListRow | TanreiListRow | ListRow |
headline · الخاصية | headline | headline | headline | headline | headline |
overline · الخاصية | overline | overline | overline | overline | overline |
supportingText · الخاصية | supportingText | supportingText | supportingText | supportingText | supportingText |
leadingIcon · الخاصية | leadingIcon | leadingIcon | leadingIcon | leadingIcon | leadingIcon |
trailingText · الخاصية | trailingText | trailingText | trailingText | trailingText | trailingText |
trailingIcon · الخاصية | trailingIcon | trailingIcon | trailingIcon | trailingIcon | trailingIcon |
الحالات
السلوك
متطلبات بصيغة EARS. يُختبر تنفيذ كل منصة مقابل هذه المعرّفات. يُعرض نص المتطلبات كما ورد في ملف المواصفة، بالإنجليزية.
| المعرّف | المتطلب |
|---|---|
LST-1 | THE list SHALL lay out its items in the order the product gives them, as far apart as gap names — comp.list.gap.small, comp.list.gap.medium or comp.list.gap.large, and touching while it is none —: WHILE orientation is vertical and wrap is false as one column from the top, each item as wide as the list and as tall as its content; WHILE orientation is horizontal and wrap is false as one line from the start edge, each item as wide as its content and as tall as the tallest item of the line. THE list SHALL never make an item narrower than its content needs or cut it: a line wider than the list goes past the list's end, and the product that wants it scrolled places the list in a scroll view of its own. THE list SHALL draw no fill, no edge and no padding of its own around its items. |
LST-2 | WHILE wrap is true THE list SHALL arrange its items as a grid: WHILE orientation is horizontal, in lines from the start edge that wrap onto a further line below where the list's width ends; WHILE orientation is vertical, in columns from the top that wrap onto a further column toward the end edge where the block size the product gives the list ends, and in one column where it gives none. Each item SHALL keep its content's size, never wider than the list, the items of one line aligned at their start (their tops in a horizontal list); the gap SHALL stand between two items of a line and between two lines alike; enlarged text SHALL make the items wrap sooner, never run past the list's width. Wrapping SHALL change no order: the items are read, focused and counted in the order given, line by line in the reading direction. |
LST-3 | WHILE dividers is true and wrap is false THE list SHALL draw a line of comp.list.divider.thickness in comp.list.divider.color between every two neighbouring items, across the list (horizontal in a column, vertical in a line) and in the middle of the gap between them, inside the space of the gap where there is one and over the meeting edge where gap is none; THE list SHALL draw none before the first item or after the last, SHALL keep its color and full opacity in every state and condition of its neighbours (a disabled item's opacity does not reach it), and SHALL hide it from assistive technology. WHILE wrap is true THE list SHALL draw no divider and, where dividers is true, report it in debug builds. |
LST-4 | THE list 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 list is shown THE list SHALL draw itself for the new direction at once, without a motion of its own, keeping its selection, its scroll position and its focus. |
LST-5 | WHERE the layout direction is right-to-left THE list SHALL mirror its arrangement: a horizontal line, and every line of a wrapping list, begins at the right and runs to the left, a vertical list wraps onto further columns toward the left, and the handle and the mark stand at the item's right and left ends; the arrow keys that point to the start and the end follow (LST-17, LST-33). THE list SHALL leave the direction of the content's text and the mirroring of the content's glyphs to the product: the content is the product's. |
LST-6 | WHEN orientation, gap, selection or an item's target names a value the list does not define THE list SHALL render with the default value, and in debug builds report the unknown value. |
LST-7 | WHILE an item is static — the list's selection is none, the item's interactive is false and its href is empty, or the list is reorderable — THE list SHALL draw nothing of its own around the item's content and add no space to it, so that the item is as large as its content (with its handle in a reorderable list, LST-28), SHALL NOT make the item focusable or emit its click, SHALL draw no hover, focus or press answer on it, SHALL ignore its disabled, and SHALL leave a control in its content its own tokens, semantics and focus stop. |
LST-8 | WHILE an item is interactive, a link item or an option THE list SHALL draw it as one box: its content padded by comp.list.container.padding-block.regular above and below, comp.list.container.padding-inline-start from the start edge and comp.list.container.padding-inline-end from the end edge, the mark of an option comp.list.container.gap after the content (LST-21), at least comp.list.container.height.regular tall and growing with its content and with enlarged text, its corners comp.list.container.shape, and its whole box the item's hit area. THE list SHALL hand the content the content color of the item's condition and state (comp.list.unselected.content.color at rest, and so on) as its foreground and comp.list.content.typescale as its text style, which text the product did not style takes. |
LST-9 | WHILE an interactive item, a link item or an option is hovered, keyboard-focused or pressed THE list SHALL render its container, content color, mark and opacity from the component tokens of its condition in the highest-precedence active state, with precedence pressed over focused over hovered; focus gained by pointer or touch SHALL NOT select the focused state. |
LST-10 | WHEN an interactive item, a link item or an option receives keyboard focus THE list SHALL show the focus ring inside the item's box, so that the list's scrolling ancestor cannot clip it, without animation; focus gained by pointer or touch SHALL NOT show it. |
LST-11 | WHERE an interactive item, a link item or an option is smaller than the minimum target THE list SHALL make its box at least sys.target.touch tall and wide for touch input, in both densities, and for fine pointers at least sys.target.pointer wide and, WHILE density is regular, as tall, WHILE density is compact as tall as its density gives (LST-51), because items that touch cannot extend a hit area over each other; a platform that cannot tell the input type SHALL use sys.target.touch. |
LST-12 | WHEN the list item is pressed by pointer, touch, Enter or Space THE list item SHALL show the pressed state without delay and SHALL keep it visible for at least sys.motion.duration.press-hold, even when the press ends sooner. |
LST-13 | WHILE pressed THE list item SHALL answer as sys.behavior.pressed-feedback names: overlay applies the pressed tokens to the whole container at once; ripple grows the pressed state layer from the point of contact (from the center for keyboard activation); dim applies the pressed opacity to the whole control and draws no state layer; none draws no answer to the press. Under ripple and dim only the container color stays with the state under the press; the content color, mark, opacity and shape still come from the pressed tokens. Under none every part stays with the state under the press. The ripple grows over comp.list.motion.feedback-duration with comp.list.motion.state-easing, fades over comp.list.motion.state-duration with comp.list.motion.state-easing once the press has ended and press-hold has passed, and appears whole where motion is reduced. |
LST-14 | THE list item SHALL ease its container corners by sys.shape.corner-smoothing (0 is a quarter circle, 0.6 the iOS continuous corner; a platform with only the OS curve draws it for any amount above 0) and show its keyboard focus as sys.behavior.focus-style names (ring or glow); a platform that cannot draw a choice SHALL fall back to the first value and state that in its README. |
LST-15 | WHILE an interactive item, a link item or an option is disabled THE list SHALL NOT emit its click or change for it, SHALL expose the disabled state to assistive technology, SHALL render its container, content color and mark from the disabled component tokens of its condition and SHALL apply the disabled opacity to the whole item, its content included; the arrow keys SHALL still reach it, so that people learn it exists, but Space, Enter, pointer and touch SHALL NOT activate it. |
LST-16 | WHEN the colors of an interactive item, a link item or an option change between states THE list SHALL animate them over comp.list.motion.state-duration with comp.list.motion.state-easing, and SHALL change them at once where motion is reduced. |
LST-17 | WHILE the list holds an interactive item, a link item or an option THE list SHALL be one Tab stop — the item that had focus last, or else the first selected option, or else the first enabled item — and SHALL move focus by the arrow keys of its arrangement, without wrapping around: WHILE wrap is false, Down Arrow and Up Arrow to the next and previous item of a vertical list, and the arrow pointing to the end and to the start (Right and Left in a left-to-right layout, the reverse in a right-to-left one) of a horizontal list; WHILE wrap is true, the arrows along the lines to the next and previous item in reading order, and the arrows across them to the item of the next or previous line whose centre is nearest along the line; Home and End to the first and the last item; and to the next item whose name begins with the typed characters when printable characters are typed within one second of each other. Moving focus SHALL NOT change the selection; Space or Enter SHALL activate the focused item, Enter alone a link item (LST-27). Static items SHALL NOT take focus. |
LST-18 | WHILE selection is none THE list SHALL expose the list role, each item as a list item with its position and the list's size, and an interactive item as one element with the button role, or a link item (LST-26) as one element with the link role, inside its list item; WHILE selection is single or multiple THE list SHALL expose the listbox role named by label, multi-selectable while selection is multiple and with its orientation (horizontal while orientation is horizontal and wrap is false), and each item as an option with its selected state, its position and the list's size, and SHALL ignore interactive and href. A reorderable list SHALL be named by label. |
LST-19 | THE list SHALL name an interactive item, a link item or an option by its label and, while label is empty, by the name the product gives the item on the element where it gives one (LST-52) — an author's name is over the content, as the platform's text alternative computation puts it — and otherwise by the text of its content, and SHALL present its content as part of it, not as separate elements; the mark, the dividers and the glyph that marks a new context SHALL be hidden from assistive technology. IF an interactive item, a link item, an option or an item of a reorderable list has no name — an empty label, no name the product gives it and no text in its content — THE list SHALL still render it and in debug builds report that it has no name. |
LST-20 | WHILE an item is interactive, a link item or an option THE item's content SHALL contain no interactive element; a platform that can inspect its content (the web, on every change of it) SHALL report one in debug builds, and a platform that cannot SHALL state the rule in its README and docs page. |
LST-21 | WHILE selection is single or multiple THE list SHALL reserve a box of side comp.list.mark.size at the end of every option, comp.list.container.gap after its content, and, while the option is selected, draw the glyph check of the system's icon set centered in it, in the mark color of the option's condition, with sys.icon.family.selected, sys.icon.fill.selected, sys.icon.weight and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max, and SHALL NOT mirror it when the layout direction is right-to-left; THE list SHALL draw no mark of its own (no path, shape or character of its own for the check). WHEN an option becomes selected or unselected THE list SHALL fade the check in or out without moving or scaling it, and change the option's colors, over comp.list.motion.state-duration with comp.list.motion.state-easing, and SHALL make the change at once where motion is reduced. |
LST-22 | IF the icon set cannot draw the check yet (its font is still loading or failed to load) THE list SHALL draw nothing in the check's place, never the glyph's name as text, SHALL keep the box of side comp.list.mark.size, SHALL still expose the option's selected state, and SHALL show the check once the icon set can draw it, without the fade of LST-21. |
LST-23 | WHEN the user activates an enabled interactive item by pointer, touch, Enter or Space — a link item (LST-26) by pointer, touch or Enter — THE list SHALL emit that item's click exactly once. |
LST-24 | WHEN the user activates an enabled option of a list whose selection is single and whose value is not that option's value THE list SHALL set value to the option's value, so that the option that was selected becomes unselected, and emit change once; WHEN the option is already selected THE list SHALL change nothing and SHALL NOT emit change, because a single choice is never cleared by its own option. WHEN the user activates an enabled option of a list whose selection is multiple THE list SHALL add the option's value to values, or remove it when values holds it, and emit change once. |
LST-25 | THE list SHALL render an option as selected, from the selected condition, WHILE selection is single and value equals the option's value, or selection is multiple and values holds it, and every other option from the unselected condition; THE list SHALL draw the selected option's fill (comp.list.selected.container.color in its state) over whatever the list stands on and under the item's content, and hand the content the selected content color; WHEN value or values names no option THE list SHALL select nothing for that name, and WHEN an option's value is empty or repeats another option's THE list SHALL still render it; in debug builds it SHALL report each of these. |
LST-26 | WHILE selection is none, the list is not reorderable and an item's href is not empty THE list SHALL make the item a link item, whether its interactive is true or false: one element with the link role and not the button role inside its list item, named as LST-19 says, its content held to LST-20; the platform's own link to href (the web's anchor element; SwiftUI's Link; on Compose, Flutter and React Native an element with the platform's link semantics whose address is opened by the platform's opener), so that whatever the platform gives a link — on the web its context menu, opening in a new tab or window, copying and dragging its address, and the address shown before it is followed — is the platform's; and everything this spec says of an interactive item other than its role and its keys — its box, states, focus ring, target, press answer, corners, motion, disabled look and its place in the list's one Tab stop (LST-8 to LST-17) — SHALL hold for a link item from the same tokens, with no look of its own for a link and none for a destination already visited. |
LST-27 | WHEN the user activates a link item by pointer, touch or Enter THE list SHALL emit that item's click exactly once and then open href where target names, unless the product cancelled that click, in which case it SHALL open nothing: the product's own router has taken the navigation. It SHALL show the pressed state as LST-12 and LST-13 say and SHALL NOT hold the navigation back for sys.motion.duration.press-hold. THE list SHALL NOT activate a link item on Space, which on the web scrolls the page as it does on any link, and SHALL NOT show the pressed state for it; WHERE the platform gives its links other keys or gestures (on the web a modified or a middle click that opens a new tab, on Apple platforms a long press that previews) THE list SHALL leave them to the platform. |
LST-28 | WHILE an item is a link item and its target is new THE list SHALL open href in a new context — on the web a new tab or window that is given no handle to the page that opened it; on the other platforms outside the screen the person is on, as the platform's opener decides — and SHALL draw the glyph open_in_new of the system's icon set at the item's end, after the content and comp.list.container.gap from it, at sys.icon.small in the content color of the item's condition and state, through the icon helper, which mirrors it in a right-to-left layout because the icon set marks its name, and hidden from assistive technology. THE list SHALL expose targetLabel as the item's description, and WHILE targetLabel is empty SHALL still render and in debug builds report that a link that opens a new context does not say so. WHILE target is same THE list SHALL draw no mark and SHALL ignore targetLabel. |
LST-29 | WHILE selection is none and an item's href is not empty and its disabled is true THE list SHALL NOT make the item a link that can be followed: it SHALL render the item as LST-15 says, the mark of LST-28 with it, SHALL expose the link role with the disabled state and no destination, SHALL keep it reachable by the arrow keys as LST-15 keeps any disabled item, SHALL NOT emit its click, and SHALL open nothing by any means the platform gives a link (on the web the anchor carries no href, so there is no context menu entry, no drag and no new tab for it). |
LST-30 | WHILE selection is single or multiple THE list SHALL ignore every item's href, target and targetLabel, as it ignores interactive (LST-18), and in debug builds SHALL report an option that has an href: an option holds a state and is not a destination. WHEN an item's target names a value the list does not define THE list SHALL treat it as same, and in debug builds report the unknown value. IF an item's href is only white space, or names a script to run rather than a place to go (the web's javascript: scheme), or cannot be read as an address by the platform, THE list SHALL make the item what its interactive makes it, without a destination, and in the last two cases report it in debug builds. |
LST-31 | WHILE selection is none and reorderable is true THE list SHALL be reorderable: it SHALL render every item as a static item (LST-7) whatever its interactive and href say, SHALL draw a handle in every item (LST-32), SHALL let every item be dragged by the whole of it (LST-36), SHALL open the move menu from a handle (LST-33) and SHALL let the keyboard carry an item from its handle (LST-35). WHILE selection is single or multiple THE list SHALL NOT be reorderable, and where reorderable is true SHALL report it in debug builds: an option is chosen by a press that a drag would take. |
LST-32 | WHILE the list is reorderable THE list SHALL draw in every item, at its start edge and before its content, comp.list.container.gap from it and centred across the item, a handle: an icon-only Tanrei Button of the text variant at Button's small size showing the glyph drag_indicator of the system's icon set, drawn and answering hover, focus and press from Button's tokens, the list's handle in every item the same. THE list SHALL name it by dragHandleLabel with its place {label} filled by the item's name (LST-19), describe it by dragHandleHint, expose it as a button that opens a menu, with its expanded state, and SHALL disable it WHILE the list holds one item, which has nowhere to go — as Button disables a control that stays reachable (focusableWhenDisabled), so that focus that comes to it after the other items were taken away is not lost. The handle SHALL be a Tab stop of its own in every item, before the controls of the item's content. |
LST-33 | WHEN the user activates an enabled handle by pointer, touch, Enter or Down Arrow THE list SHALL open the move menu: one Tanrei Menu, anchored to that handle as a Menu is to its anchor, holding four items in this order — moveToStartLabel with the glyph vertical_align_top, moveBeforeLabel with arrow_upward, moveAfterLabel with arrow_downward and moveToEndLabel with vertical_align_bottom in a vertical list; first_page, arrow_back, arrow_forward and last_page, which the icon set mirrors in a right-to-left layout, in a horizontal one — with focus on its first enabled item. THE list SHALL disable the two items that would leave the first item where it is, and the two that would leave the last item where it is. WHEN the user chooses one THE list SHALL close the menu and emit move once with from the item's index i and to 0, i − 1, i + 1 or the index of the last item; WHEN the menu asks to be closed otherwise (Escape, a press outside, Tab) THE list SHALL close it and move nothing. Focus SHALL return to the handle when the menu closes (LST-43). |
LST-34 | WHEN the user presses Space on an enabled handle THE list SHALL lift its item for the keyboard: it SHALL draw the item dragged (LST-37) where it stands, keep focus on its handle and announce liftedText (LST-44). WHILE an item is lifted by the keyboard THE list SHALL take Space, Enter, Escape, Home, End and the arrow keys of its arrangement (LST-17) on the handle and let none of them reach anything else, an overlay that holds the list included: an arrow key SHALL move the item's place one step — in a list that does not wrap one place toward the start or the end; in a wrapping list one place in reading order along the lines, or into the next or previous line at the place whose centre is nearest along the line — and Home and End to the first and the last place, never past either; the item SHALL move into its new place and its neighbours aside as LST-38 says, and each step that changes the place SHALL announce movedText with the place the item would have if dropped now. |
LST-35 | WHEN the user presses Space or Enter WHILE an item is lifted by the keyboard THE list SHALL drop it: settle it into its place as LST-39 says and, where the place is not the one it was lifted from, emit move exactly once with from the index it had and to the index of its place. WHEN the user presses Escape, or focus leaves the handle, or the window or the app loses focus, or the product changes the list's items, its reorderable or its selection, WHILE an item is lifted by the keyboard THE list SHALL return the item and its neighbours to their places as LST-40 says, emit nothing, and announce cancelledText with the place the item has again — except where the product removed the item. |
LST-36 | WHILE the list is reorderable THE list SHALL let a pointer drag an item by any part of it that is not an interactive element of its content (a button, a link, a field, a control — which keep their presses), the handle included: WHEN the primary button of a mouse is pressed there and the pointer moves past the platform's touch slop (8 points where the platform names none) THE list SHALL lift the item at once; WHEN a touch or a pen presses the handle and moves past the slop THE list SHALL lift the item at once, and the touch SHALL NOT scroll; WHEN a touch or a pen presses anywhere else on the item and stays within the slop for the platform's long-press duration (500 ms where the platform names none) THE list SHALL lift the item then, and the touch SHALL NOT scroll from then on, while a touch that moves past the slop sooner SHALL scroll, select text or do whatever it did before, and lift nothing. A press of another mouse button, a second pointer while an item is carried, and a press on a disabled handle SHALL lift nothing. THE list SHALL keep the platform's own answers to a long press on a reorderable item (a context menu, a text selection, a preview) from appearing, and a press that lifts nothing SHALL do what it would have done. |
LST-37 | WHILE an item is dragged — lifted by a pointer or by the keyboard — THE list SHALL draw it above the other items of the list with its content unchanged, filled with comp.list.dragged.container.color under the content, with the shadow comp.list.dragged.container.elevation and the corners comp.list.container.shape, the fill and the shadow arriving over comp.list.motion.state-duration with comp.list.motion.state-easing, and SHALL leave the gap where it would land. WHILE a pointer drags it THE list SHALL move it with the pointer by the distance the pointer has travelled since the press, in the frame the pointer moves and without a motion of its own — along the list's axis only while wrap is false, in both directions while wrap is true — never past the list's own box, wherever the pointer goes; SHALL draw no item and no handle hovered or pressed; SHALL let nothing of the list be selected as text or be taken by the platform's own drag and drop; SHALL show a fine pointer the platform's cursor for something held (grabbing), and the cursor for something that can be held (grab) over a draggable part of an item at rest; and SHALL emit nothing. |
LST-38 | WHILE an item is dragged THE list SHALL keep the gap where the item would land if it were dropped now and SHALL move every other item whose place that changes, over comp.list.motion.reorder-duration with comp.list.motion.reorder-easing, without fading or scaling it: WHILE wrap is false, WHEN the centre of the dragged item passes the centre of a neighbour, that neighbour SHALL move to the other side of the gap by the dragged item's length and the gap; WHILE wrap is true the dragged item's place SHALL be the place, among those the items had when it was lifted, whose centre is nearest to the dragged item's centre, and every item between its old place and that one SHALL move into the place of its neighbour, so that the grid closes behind the item and opens in front of it. WHILE a pointer is nearer than sys.target.touch to an edge of the list's nearest scrolling ancestor and the list reaches beyond that edge THE list SHALL scroll that ancestor toward it, the faster the nearer, and keep the gap under the dragged item. |
LST-39 | WHEN the user releases an item dragged by a pointer, or drops one lifted by the keyboard (LST-35), THE list SHALL move it into the gap over comp.list.motion.reorder-duration with comp.list.motion.reorder-easing and take the dragged fill and shadow from it over comp.list.motion.state-duration. IF the gap is not where the item stood when it was lifted THE list SHALL emit move exactly once, with from the index the item had and to the index of the gap, both counted from 0, and SHALL keep drawing the items as they were dropped until the product gives that order or comp.list.motion.reorder-duration has passed since the event, whichever comes first, so that a product that reorders in answer to the event shows no frame of the old order; it SHALL then draw the product's order and go on as LST-43 says, and where the product gave no such order the items SHALL go back to their places over the same motion. IF the gap is where the item stood THE list SHALL emit nothing and announce nothing. |
LST-40 | WHILE a pointer drags an item, WHEN the user presses Escape, or the platform cancels the pointer or takes it for a gesture of its own, or the window or the app loses focus, or the product changes the list's items, its reorderable or its selection, THE list SHALL end the drag without a move: it SHALL return the item and its neighbours to the places they had over comp.list.motion.reorder-duration with comp.list.motion.reorder-easing, SHALL emit nothing, SHALL announce nothing and SHALL leave the scroll position where the drag brought it. Escape SHALL be taken by the list while an item is dragged and SHALL NOT reach an overlay that holds the list, so that putting an item back does not also close the dialog it is in. |
LST-41 | WHEN the order of the items of a reorderable list changes — after a move, or because the product changed it — THE list SHALL move every item whose place changed from where it was drawn to its new place over comp.list.motion.reorder-duration with comp.list.motion.reorder-easing, in both directions where the list wraps, without fading or scaling it, the focus ring travelling with the item that holds focus; an item that is new or gone SHALL appear or leave at once, and an item that was just dropped SHALL go on from where it is drawn into its place. |
LST-42 | WHERE motion is reduced THE list SHALL still move an item dragged by a pointer with the pointer, which is the person's own movement and not a motion of the list's, SHALL give a dragged item the dragged fill and shadow at once, SHALL put a neighbour into its new place at once (LST-38), SHALL put an item lifted by the keyboard into its next place at once (LST-34), SHALL put a released or dropped item into the gap or back to its place at once (LST-39, LST-40, LST-35), SHALL draw a new order at once (LST-41) and SHALL scroll its ancestor near an edge as LST-38 says. |
LST-43 | WHEN the product, after a move the list emitted, gives its items in an order in which the moved item stands at the index to, THE list SHALL draw that order (LST-41), SHALL set every item's position anew for assistive technology (LST-18), SHALL announce movedText with the place the item now has (LST-44) and SHALL keep keyboard focus with the moved item: WHILE focus was on its handle, in the move menu, or on the handle of the item carried by the keyboard, on the handle of the item now at to, whether the product moved the item it had or gave a new one for the same content; WHILE focus was elsewhere THE list SHALL NOT move it. IF the product has not given such an order by the time comp.list.motion.reorder-duration has passed since the move was emitted THE list SHALL take the move as refused: it SHALL announce nothing, leave focus on the handle of the item that stands where it stood, and draw the items as the product has them. |
LST-44 | WHILE the list is reorderable THE list SHALL hold one live region of its own, polite, empty at rest and not drawn, and SHALL have nothing else of the list announce a move. THE list SHALL put into it liftedText when the keyboard lifts an item (LST-34), movedText when an item lifted by the keyboard reaches another place (LST-34) and when a move has taken effect (LST-43), and cancelledText when a move with the keyboard is cancelled (LST-35), each with its places filled — {label} by the item's name (LST-19), {position} by its place counted from 1, and {count} by the number of items, the two numbers written as the platform writes a whole number in the language of the list — so that it is announced once, after what is being read; a later sentence SHALL replace what the region holds and SHALL NOT queue behind it. THE list SHALL fill the places as a word with places is filled: each place wherever and however often the product wrote it; any other text between braces, and a brace that opens or closes nothing, left as it is written and in debug builds reported once per page or screen, naming the prop; a brace written twice drawn as one brace; and the name inserted as plain text of its own direction, isolated from the sentence. THE list SHALL announce nothing when a pointer lifts an item or carries it over others: the person using a pointer is watching the item, and assistive technology is told the one thing that happened, where the item now stands. |
LST-45 | WHERE the platform draws with a person's own forced colors (the web's forced-colors mode, and a platform's equivalent where it has one) THE list SHALL draw a dragged item on the platform's background color with an edge sys.border.hairline wide in the platform's highlight color all around it, in place of a fill and a shadow that are not drawn there, SHALL draw the dividers in the platform's text color, and SHALL leave the handle and the move menu to be drawn as a Button and a Menu are there. |
LST-46 | WHEN the product takes away an item of a reorderable list that holds keyboard focus THE list SHALL move focus to the handle of the item that now stands where that item stood, else of the item before it; WHEN the last item is taken away THE list SHALL leave focus to the product, which knows where a person adds an item. THE list SHALL announce nothing for a removal or an addition: what was removed is the product's to say. A control the product places in an item's content — a button that removes the item — keeps its own tokens, semantics and focus stop, after the handle in reading and focus order. |
LST-47 | THE list SHALL have no words of its own: it SHALL take dragHandleLabel, dragHandleHint, moveToStartLabel, moveBeforeLabel, moveAfterLabel, moveToEndLabel, liftedText, movedText and cancelledText from the instance's prop and, WHILE that is empty, from the product's words of the same key, and from nowhere else; WHILE the list is reorderable and one of them other than dragHandleHint is empty in both, it SHALL still render and work, and in debug builds report the missing word once. IF dragHandleLabel has no place {label} THE list SHALL name every handle with it as it is written and report in debug builds that the handles of two items cannot be told apart. |
LST-48 | WHERE an element that fits itself to its item (a List row, list-row) is a direct child of an item THE list SHALL tell it what the item is — whether it is an action, a link or an option, which pads its content (LST-8); whether it draws the handle of a reorderable list before its content (LST-32); whether it is a selected option; whether it is a disabled action, link or option; whether it keeps the room of a leading glyph (LST-50) — before that element is first drawn in the item and again WHEN any of these changes (selection, interactive, href, disabled, the list becoming reorderable or not, or what LST-50 decides), so that the element draws itself to fit the item in every arrangement and mode of the list. An element that is not a direct child of the item SHALL be told nothing and SHALL draw itself as it stands alone. |
LST-49 | WHERE an item's label is empty and its content holds a List row as a direct child THE list SHALL take the item's name from that row's headline in place of the text of its content — for the accessible name of an action, a link or an option, the handle's name, typeahead and every announcement — and SHALL describe an action, a link or an option by the row's overline, supporting text and trailing text, in that order, before the description of a link that opens a new context (LST-28); WHEN the row's texts change THE list SHALL take the new name and description at once. An item with a label keeps it as its name and is still described by the row. |
LST-50 | THE list SHALL decide for all its items together whether a List row that is an item's direct child (LST-48) keeps the room of a leading glyph where it draws no leading part (LRW-16). WHILE iconSpace is auto THE list SHALL tell every such row to keep the room WHERE an item of the list holds a List row whose leading part is a glyph — its leadingIcon, its leading slot empty —, so that the headlines of the rows with a glyph and of the rows without one start on one line, and SHALL tell none to keep it WHERE no item does, so that a list without glyphs starts its texts at the row's start; WHILE iconSpace is always THE list SHALL tell every such row to keep it, whether or not any row draws a glyph; WHILE iconSpace is never THE list SHALL tell none to keep it, so that only a row that draws a glyph has its room. The content of a row's leading slot (an avatar, a picture, a control) SHALL NOT count, because its size is the product's and differs from row to row, so that a room kept for it would line up nothing. The list is one run of items: its rows are decided together whatever its orientation, wrap, gap, dividers, selection and reorderable are. WHEN an item is added, removed or moved, or a row's leadingIcon or the content of its leading slot changes, THE list SHALL decide again; WHEN iconSpace names a value the list does not define THE list SHALL treat it as auto and in debug builds report the unknown value. |
LST-51 | THE list SHALL draw its items at its density. WHILE density is regular an interactive item, a link item and an option SHALL be at least comp.list.container.height.regular tall and padded by comp.list.container.padding-block.regular above and below (LST-8); WHILE density is compact at least comp.list.container.height.compact tall and padded by comp.list.container.padding-block.compact. A static item draws no box of its own and is not changed. THE list SHALL tell a List row that is an item's direct child (LST-48) the list's density, before the row is first drawn in the item and again WHEN density changes, and the row SHALL draw at that density in place of its own (LRW-17), so that the rows of one list are of one density. WHEN density names a value the active design system does not have THE list SHALL draw the default density's items and in debug builds report the unknown value; a density a design system added SHALL be drawn from that density's tokens, as the core values are. WHEN density changes while the list is shown THE list SHALL draw the new items at once, without a motion, keeping focus and the selection. |
LST-52 | WHERE the platform has ARIA attributes (the web), the hosts of the list, of an option and of an item that is neither an action nor a link carry their roles themselves (LST-18), so what the product writes on them is not moved, but for a name: WHILE the list's label, or an option's label or the text of its content, is not empty THE list SHALL hold the product's aria-label and aria-labelledby on that host as their data-aria- twins, so that its own is the whole name although the platform gives aria-labelledby the precedence, and WHEN it is emptied SHALL give them back as they were written; WHILE it is empty a name the product gives so is the name and SHALL NOT be reported as having none in debug builds (LST-18, LST-19); an item that is neither an action nor a link has no name of its own and keeps the product's. WHILE an item is an interactive item or a link item, and WHILE the list is reorderable, the role is carried by the element inside — the action, or the list inside the host (LST-44) — and WHEN the product sets an aria- attribute on that host, by the attribute or by the ARIA property that reflects it, THE list SHALL move it to the data-aria- attribute of the same name, so that the host is no second node with its name, description or state, and SHALL expose it on that element — an id reference by the elements it names in the host's tree or a tree around it, a description after its own — except aria-disabled on an item, which the item owns and which THE list SHALL move, not expose and report in debug builds; its label is the name over a name given so (LST-19). WHEN an item or the list leaves that mode THE list SHALL give back to its host what it moved, as it was written, and WHEN it enters it SHALL move what the host carries. WHEN the product removes one, or its data-aria- twin, or sets the ARIA property that reflects it (ariaLabel, ariaDescribedByElements…) to null, THE list SHALL take it away. |
مطابقة الأسماء بين المنصات
التكافؤ لا يعني واجهات برمجية متطابقة. تعبّر كل منصة عن العقد نفسه بأسلوبها.
| المواصفة | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| المكوّن | tanrei-list | TanreiList | TanreiList | TanreiList | List |
orientation · الخاصية | orientation | axis | orientation | direction | orientation |
wrap · الخاصية | wrap | wraps | wrap | wrap | wrap |
gap · الخاصية | gap | spacing | gap | spacing | gap |
dividers · الخاصية | dividers | dividers | dividers | dividers | dividers |
iconSpace · الخاصية | iconSpace | iconSpace | iconSpace | iconSpace | iconSpace |
density · الخاصية | density | density | density | density | density |
selection · الخاصية | selection | selection | selectionMode | selectionMode | selectionMode |
label · الخاصية | label | label | contentDescription | semanticLabel | accessibilityLabel |
value · الخاصية | value | selection | selectedValue | selected | value |
values · الخاصية | values | selection | selectedValues | selected | values |
reorderable · الخاصية | reorderable | onMove | onMove | onMove | reorderable |
dragHandleLabel · الخاصية | dragHandleLabel | dragHandleLabel | dragHandleLabel | handleSemanticLabel | dragHandleLabel |
dragHandleHint · الخاصية | dragHandleHint | dragHandleHint | dragHandleHint | handleSemanticHint | dragHandleHint |
moveToStartLabel · الخاصية | moveToStartLabel | moveToStartLabel | moveToStartLabel | moveToStartLabel | moveToStartLabel |
moveBeforeLabel · الخاصية | moveBeforeLabel | moveBeforeLabel | moveBeforeLabel | moveBeforeLabel | moveBeforeLabel |
moveAfterLabel · الخاصية | moveAfterLabel | moveAfterLabel | moveAfterLabel | moveAfterLabel | moveAfterLabel |
moveToEndLabel · الخاصية | moveToEndLabel | moveToEndLabel | moveToEndLabel | moveToEndLabel | moveToEndLabel |
liftedText · الخاصية | liftedText | liftedText | liftedText | liftedText | liftedText |
movedText · الخاصية | movedText | movedText | movedText | movedText | movedText |
cancelledText · الخاصية | cancelledText | cancelledText | cancelledText | cancelledText | cancelledText |
change · الحدث | change | selection | onSelectionChange | onSelectionChanged | onSelectionChange |
move · الحدث | move | onMove | onMove | onMove | onMove |
| ملاحظات | tanrei-list holds tanrei-list-item children. It sets its own value or values (an array property; the values attribute is not reflected) before firing change, as a native control does. Focus moves among actionable items by a roving tabindex. Each item is a separate element, so the list sets aria-posinset and aria-setsize on them. Words: the attributes drag-handle-label, drag-handle-hint, move-to-start-label, move-before-label, move-after-label, move-to-end-label, lifted-text, moved-text and cancelled-text, and the same keys of setWords(). move is a CustomEvent whose detail is { from, to }; it bubbles, is not cancelable, and the list moves no element: the product reorders the tanrei-list-item children (or its framework does), and the list sees it as a change of its children. The move menu is one tanrei-menu in the list's shadow root, placed against the handle that opened it. A dragged item and its neighbours are moved by transforms, so the product's elements never change place under it; the item carries touch-action: pan-x pan-y and the handle touch-action: none, and a long press is told from a swipe by a non-passive touchmove listener that is cancelled only once the item is lifted. The live region is one visually hidden role="status" element in the list's shadow root. Before the element is defined (no script) the items are plain children in the order written and nothing can be moved. | Ships the previous revision of the list today — rows with a headline, supporting text, leading and trailing parts and the check, selection, interactive items — and none of this revision is built on this platform yet: TanreiList(axis: .vertical, wraps:, spacing:) { … } laid out by a custom Layout (a flow layout while wrapping); onMove: (from, to) makes it reorderable — the handle with a Menu of the four moves, the whole row draggable through onDrag/onDrop or a long-press gesture, the moves also exposed as accessibility custom actions, and the sentences posted as accessibility announcements. | Ships the previous revision of the list today — rows with a headline, supporting text, leading and trailing parts and the check, selection, interactive items — and none of this revision is built on this platform yet: TanreiList(orientation, wrap, gap) { … } — a Column, a Row or a FlowRow/FlowColumn while wrapping — with onMove: (from: Int, to: Int) -> Unit making it reorderable: the handle opens a DropdownMenu of the four moves, the whole item is dragged after a long press (detectDragGesturesAfterLongPress) and at once from the handle, the moves are also customActions of the item's semantics, and the announcement is a polite live region node. Items need stable keys for the motion of LST-41. | Ships the previous revision of the list today — rows with a headline, supporting text, leading and trailing parts and the check, selection, interactive items — and none of this revision is built on this platform yet: TanreiList(direction: Axis.vertical, wrap:, spacing:, children:) — a Flex or a Wrap — with onMove: (int from, int to) making it reorderable: the handle opens a MenuAnchor of the four moves, the whole item is a LongPressDraggable (a Draggable from the handle), the moves are also semantics custom actions, and the sentences go through SemanticsService.announce. to is the index the item has after the move, not the insertion index of the framework's reorderable list. | Ships the previous revision of the list today — rows with a headline, supporting text, leading and trailing parts and the check, selection, interactive items — and none of this revision is built on this platform yet: <List items orientation wrap gap onMove> — a flex container with flexWrap — the handle opening a menu of the four moves, the whole item dragged after a long press through a gesture responder, the moves also accessibilityActions of the item, and the sentences announced through AccessibilityInfo.announceForAccessibility. |
الرموز
| الرمز | يشير إلى | القيمة المحلولة (فاتح) |
|---|---|---|
--tanrei-comp-list-container-gap | sys.space.space200 | 16px |
--tanrei-comp-list-container-height-compact | comp.tree-view.container.height.compact | 32px |
--tanrei-comp-list-container-height-regular | — | 48px |
--tanrei-comp-list-container-padding-block-compact | — | 4px |
--tanrei-comp-list-container-padding-block-regular | — | 8px |
--tanrei-comp-list-container-padding-inline-end | sys.space.space200 | 16px |
--tanrei-comp-list-container-padding-inline-start | sys.space.space200 | 16px |
--tanrei-comp-list-container-shape | sys.shape.corner.none | 0px |
--tanrei-comp-list-disabled-container-shape | — | 0px |
--tanrei-comp-list-disabled-opacity | — | 0.38 |
--tanrei-comp-list-divider-color | sys.color.outline-variant | |
--tanrei-comp-list-divider-thickness | sys.border.hairline | 1px |
--tanrei-comp-list-dragged-container-color | sys.color.surface-container-high | |
--tanrei-comp-list-dragged-container-elevation | sys.elevation.level3 | 0px 0px 0px 1px rgb(0 0 0 / 0.08), 0px 1px 1px 0px rgb(0 0 0 / 0.04), 0px 4px 8px -4px rgb(0 0 0 / 0.04), 0px 16px 24px -8px rgb(0 0 0 / 0.06) |
--tanrei-comp-list-focused-container-shape | — | 0px |
--tanrei-comp-list-focused-opacity | — | 1 |
--tanrei-comp-list-gap-large | sys.space.space300 | 24px |
--tanrei-comp-list-gap-medium | sys.space.space200 | 16px |
--tanrei-comp-list-gap-small | sys.space.space150 | 12px |
--tanrei-comp-list-hovered-container-shape | — | 0px |
--tanrei-comp-list-hovered-opacity | — | 1 |
--tanrei-comp-list-mark-size | sys.icon.medium | 24px |
--tanrei-comp-list-mark-width | sys.border.thick | 2px |
--tanrei-comp-list-motion-feedback-duration | sys.motion.role.feedback | 450ms |
--tanrei-comp-list-motion-reorder-duration | sys.motion.role.selection.travel | 250ms |
--tanrei-comp-list-motion-reorder-easing | sys.motion.easing.spatial | cubic-bezier(0.2, 0, 0, 1) |
--tanrei-comp-list-motion-state-duration | sys.motion.role.state.large | 250ms |
--tanrei-comp-list-motion-state-easing | sys.motion.easing.state | cubic-bezier(0.2, 0, 0, 1) |
--tanrei-comp-list-pressed-container-shape | — | 0px |
--tanrei-comp-list-pressed-opacity | — | 1 |
--tanrei-comp-list-selected-container-color | sys.color.secondary-container | |
--tanrei-comp-list-selected-content-color | sys.color.on-secondary-container | |
--tanrei-comp-list-selected-disabled-container-color | — | |
--tanrei-comp-list-selected-disabled-content-color | — | |
--tanrei-comp-list-selected-disabled-mark-color | — | |
--tanrei-comp-list-selected-disabled-state-layer-color | — | |
--tanrei-comp-list-selected-focused-container-color | — | |
--tanrei-comp-list-selected-focused-content-color | — | |
--tanrei-comp-list-selected-focused-mark-color | — | |
--tanrei-comp-list-selected-focused-state-layer-color | — | |
--tanrei-comp-list-selected-hovered-container-color | — | |
--tanrei-comp-list-selected-hovered-content-color | — | |
--tanrei-comp-list-selected-hovered-mark-color | — | |
--tanrei-comp-list-selected-hovered-state-layer-color | — | |
--tanrei-comp-list-selected-mark-color | sys.color.primary | |
--tanrei-comp-list-selected-pressed-container-color | — | |
--tanrei-comp-list-selected-pressed-content-color | — | |
--tanrei-comp-list-selected-pressed-mark-color | — | |
--tanrei-comp-list-selected-pressed-state-layer-color | — | |
--tanrei-comp-list-selected-state-layer-color | comp.list.selected.content.color | |
--tanrei-comp-list-unselected-container-color | — | |
--tanrei-comp-list-unselected-content-color | sys.color.on-surface | |
--tanrei-comp-list-unselected-disabled-container-color | — | |
--tanrei-comp-list-unselected-disabled-content-color | — | |
--tanrei-comp-list-unselected-disabled-mark-color | — | |
--tanrei-comp-list-unselected-disabled-state-layer-color | — | |
--tanrei-comp-list-unselected-focused-container-color | — | |
--tanrei-comp-list-unselected-focused-content-color | — | |
--tanrei-comp-list-unselected-focused-mark-color | — | |
--tanrei-comp-list-unselected-focused-state-layer-color | — | |
--tanrei-comp-list-unselected-hovered-container-color | — | |
--tanrei-comp-list-unselected-hovered-content-color | — | |
--tanrei-comp-list-unselected-hovered-mark-color | — | |
--tanrei-comp-list-unselected-hovered-state-layer-color | — | |
--tanrei-comp-list-unselected-mark-color | — | |
--tanrei-comp-list-unselected-pressed-container-color | — | |
--tanrei-comp-list-unselected-pressed-content-color | — | |
--tanrei-comp-list-unselected-pressed-mark-color | — | |
--tanrei-comp-list-unselected-pressed-state-layer-color | — | |
--tanrei-comp-list-unselected-state-layer-color | comp.list.unselected.content.color | |
--tanrei-comp-list-content-typescale | sys.typescale.body | 400 14px/1.5714 |
نماذج الاختبار
عروض مرجعية. تلتقط كل المنصات لقطة لكل نموذج بالمعرّف نفسه حتى يمكن مقارنة النتائج.
density: regular (22)
column
items: 3 column-gap
gap: medium items: 3 dividers
dividers: true items: 3 line
orientation: horizontal gap: small items: 4 line-dividers
orientation: horizontal gap: medium dividers: true items: 3 wrap
orientation: horizontal wrap: true gap: medium items: 7 wrap-rtl
orientation: horizontal wrap: true gap: medium direction: rtl items: 5 interactive
items: 3 links
items: 3 links-rtl
direction: rtl items: 2 single-selected
selection: single label: Theme value: light items: 3 multiple-selected
selection: multiple label: Labels values: ["bug","docs"] items: 3 selected-disabled
selection: single label: Theme value: light items: 2 line-single-selected
orientation: horizontal selection: single label: Size value: m items: 3 reorderable
label: Typefaces reorderable: true dragHandleLabel: Reorder {label} dragHandleHint: Space lifts, arrow keys move, Space drop… moveToStartLabel: Move to top moveBeforeLabel: Move up moveAfterLabel: Move down moveToEndLabel: Move to bottom liftedText: {label} lifted: {position} of {count} movedText: {label}: {position} of {count} cancelledText: {label} returned to {position} of {count… items: 3 reorderable-rtl
label: Typefaces reorderable: true dragHandleLabel: Reorder {label} dragHandleHint: Space lifts, arrow keys move, Space drop… moveToStartLabel: Move to top moveBeforeLabel: Move up moveAfterLabel: Move down moveToEndLabel: Move to bottom liftedText: {label} lifted: {position} of {count} movedText: {label}: {position} of {count} cancelledText: {label} returned to {position} of {count… direction: rtl items: 3 reorderable-gap
gap: small label: Typefaces reorderable: true dragHandleLabel: Reorder {label} dragHandleHint: Space lifts, arrow keys move, Space drop… moveToStartLabel: Move to top moveBeforeLabel: Move up moveAfterLabel: Move down moveToEndLabel: Move to bottom liftedText: {label} lifted: {position} of {count} movedText: {label}: {position} of {count} cancelledText: {label} returned to {position} of {count… items: 3 reorderable-single-item
label: Typefaces reorderable: true dragHandleLabel: Reorder {label} dragHandleHint: Space lifts, arrow keys move, Space drop… moveToStartLabel: Move to top moveBeforeLabel: Move up moveAfterLabel: Move down moveToEndLabel: Move to bottom liftedText: {label} lifted: {position} of {count} movedText: {label}: {position} of {count} cancelledText: {label} returned to {position} of {count… items: 1 reorderable-wrap
orientation: horizontal wrap: true gap: medium label: Photos reorderable: true dragHandleLabel: Reorder {label} dragHandleHint: Space lifts, arrow keys move, Space drop… moveToStartLabel: Move to start moveBeforeLabel: Move left moveAfterLabel: Move right moveToEndLabel: Move to end liftedText: {label} lifted: {position} of {count} movedText: {label}: {position} of {count} cancelledText: {label} returned to {position} of {count… items: 5 icon-space-auto
dividers: true items: 3 icon-space-always
dividers: true iconSpace: always items: 3 icon-space-never
dividers: true iconSpace: never items: 3 density: compact (1)
density-compact
density: compact selection: single label: Network value: wifi items: 3 مولَّد من specs/list.spec.json, tokens/comp/list.tokens.json, design/tokens/
استخدم القائمة لمجموعة من أشياء من نوع واحد يتصفحها الناس أو يفتحونها أو يختارون منها أو يرتبونها: الرسائل، والأشخاص، والملفات، والصور، والإعدادات، وخيارات أداة الاختيار. واستخدم الجدول حين يقارن الناس القيم عبر الأعمدة، وقائمة الخيارات لمجموعة قصيرة من الإجراءات تُفتح من زر.
لا ترسم القائمة شيئًا من عندها: لا خلفية، ولا حشوة حول عناصرها، ولا تمرير. ضعها على سطح، أو في بطاقة، أو في عرض التمرير في شاشتك، وأعطِ كل عنصر المظهر الذي يحتاجه في محتواه.
| النوع | كيف تحصل عليه | ما هو | يُستخدم لـ |
|---|---|---|---|
| ثابت | selection none، وinteractive false، وبلا href |
محتواك كما هو. بلا تركيز، ولا تحويم أو ضغط. | العناصر التي تُقرأ فقط، والعناصر التي تحمل عناصر تحكمها. |
| تفاعلي | interactive |
زر واحد داخل عنصر القائمة. | العناصر التي يغيّر كل منها شيئًا أو يفتحه في مكانه. |
| رابط | href |
رابط واحد داخل عنصر القائمة، يُرسم تمامًا كالعنصر التفاعلي. يتبعه Enter ولا يتبعه Space. | العناصر التي يذهب كل منها إلى صفحة لها عنوانها. |
| خيار | selection single أو multiple |
خيار في مربع قائمة، بحالة تحديد وعلامة اختيار. | الاختيار من العناصر. |
يمكن أن تمزج القائمة بين العناصر الثابتة والتفاعلية والروابط. والقائمة القابلة للاختيار لا تحمل إلا خيارات. والقائمة القابلة لإعادة الترتيب لا تحمل إلا عناصر ثابتة، ولا تكون مربع قائمة أبدًا: فالإجراء والرابط والخيار تُستخدم بضغطة يأخذها السحب.
- استخدم
reorderableحيث يعني الترتيب شيئًا ويعود قراره إلى الشخص: عائلات مجموعة الخطوط، وأقسام الشاشة الرئيسية، وملفات المرفق، وطابور. وللقائمة الطويلة، قدّم بدلًا من ذلك طريقة لفرزها. - أعد ترتيب عناصرك في معالج
move، أو بأسرع ما يعيد إطار العمل الرسم بعده. يمكنك تحريك العناصر الموجودة أو رسم عناصر جديدة: تجد القائمة العنصر المنقول بمكانه واسمه. - أعطِ العنصر
labelحين لا يسمّيه نص محتواه جيدًا: فالاسم جزء من اسم مقبضه، فتتميز مقابض عنصرين، وهو ما تقوله الإعلانات. - اكتب كل جملة كاملة بمواضعها. لا تبني القائمة عبارة من عندها، فترتيب الكلمات هو ترتيب لغتك.
- عنصر تحكم خاص بك داخل عنصر (زر يحذفه) يحتفظ بموضع تركيز خاص به بعد المقبض. سمّه باسم العنصر («حذف Inter»). وحين تحذف العنصر الذي عليه التركيز، تنقل القائمة التركيز إلى مقبض العنصر الذي يأخذ مكانه؛ أما الإعلان عن الحذف فعليك.
| التحديد | القائمة هي | عند اختيار خيار |
|---|---|---|
| لا شيء (افتراضي) | قائمة | لا يُختار شيء. العناصر التفاعلية تطلق click الخاص بها. |
| واحد | مربع قائمة | يحدد الخيار ويلغي تحديد ما كان محددًا. لا يُلغى باختياره مرة أخرى. |
| متعدد | مربع قائمة متعدد التحديد | يشغّل الخيار أو يطفئه. |
مفاتيح الأسهم تنقل التركيز فقط؛ ويتغيّر التحديد حين يضغط الشخص Space أو Enter، أو يلمس العنصر. ويُرسم الخيار المحدد بتعبئة التحديد وعلامة اختيار في نهايته، ومكان العلامة محجوز في كل خيار، فيصطف المحتوى.
حين تحتوي العناصر على صفوف قائمة ولا يكون الرمز في البداية (leadingIcon) إلا لبعض الصفوف، تقرر الخاصية iconSpace من أين تبدأ الصفوف الأخرى نصها:
| iconSpace | صف بلا رمز |
|---|---|
auto (الافتراضي) |
يحتفظ بمساحة الرمز فارغةً ما دام صف آخر في القائمة يرسم رمزًا، لتبدأ كل العناوين الرئيسية على خط واحد. وفي قائمة لا يرسم فيها أي صف رمزًا، تبدأ النصوص من بداية الصف. |
always |
يحتفظ بالمساحة في كل صف، حتى حين لا يرسم أي صف رمزًا. |
never |
لا يحتفظ بأي مساحة: يبدأ نصه من بداية الصف. |
تقرر القائمة لكل صفوفها معًا، في كل ترتيب ووضع. ولا تُحسب الصورة الرمزية أو الصورة أو عنصر التحكم في الفتحة leading للصف، ولا تُترك بجانبها مساحة، لأن حجمها يعود إليك ويختلف من صف إلى آخر. وفي قائمة الخيارات وعرض الشجرة ودرج التنقل الخاصية iconSpace نفسها، بالقيم نفسها والقيمة الافتراضية نفسها.
تحدد density المساحة التي يأخذها العنصر: regular (الافتراضية) أو compact، لأداة تُستخدم بالفأرة وتُتصفح فيها عناصر كثيرة. في القائمة المضغوطة يكون الإجراء أو الرابط أو الخيار ذو النص البسيط بارتفاع 32، ويُرسم كل صف قائمة في عنصر مضغوطًا أيًّا كانت density الخاصة به، فتكون صفوف القائمة الواحدة بكثافة واحدة. باللمس لا يكون العنصر أبدًا أقل من هدف اللمس.
- أعطِ القائمة القابلة للاختيار
labelيقول ما الخيارات، والقائمة القابلة لإعادة الترتيبlabelيقول ما العناصر. - أعطِ العنصر
hrefحين يذهب إلى صفحة، واتركinteractiveللعنصر الذي يعمل في مكانه. - ضع عنوانًا بين قائمتين لصنع أقسام. ليس للقائمة رأس قسم خاص بها.
- لا تضع زرًا أو رابطًا أو مفتاح تبديل أو أي عنصر تحكم آخر داخل عنصر تفاعلي أو عنصر رابط أو خيار. العنصر عنصر واحد، فلا تصل التقنيات المساعدة إلى عنصر التحكم داخله، والضغط على عنصر التحكم يفعّل العنصر أيضًا.
- لا تستخدم الخيار لشيء يُنفَّذ فورًا. مربع القائمة يختار قيمة.
- لا ترسم مقبض سحب أو أزرار تحريك من عندك: ترسم
reorderableالمقبض، وقائمته المنبثقة هي طريقة التحريك التي لا تحتاج إلى سحب.
- الدور
list (listbox while selection is single or multiple)- التسمية
- A selectable list is a listbox named by label; a reorderable list is named by label too. An interactive item, a link item or an option is named by its label or else by its content's text, and its content is presentational. While target is new, a link item is described by its targetLabel. In a reorderable list each handle is a button that opens a menu, named by dragHandleLabel with the item's name and described by dragHandleHint; the move menu's items are named by the four move words. The list's one live region says liftedText, movedText and cancelledText, with the item's name, its place and the number of items.
- الحد الأدنى للهدف
- تستوفي مساحة اللمس الحد الأدنى لحجم الهدف حتى لو كان الحجم المرئي أصغر. minimum 32px · pointer 44px · spacing 8px · touch 48px
لوحة المفاتيح
| المفاتيح | الإجراء |
|---|---|
| Tab / Shift+Tab | Moves focus into the list (to the item focused last, the first selected option or the first enabled item) and out of it. A list of static items is not a tab stop; focus moves through the controls in its items. In a reorderable list every handle is a stop, before the controls of its item. |
| Arrow keys | Move focus to the next or previous item along the list: Up and Down in a column, Left and Right in a line (mirrored in a right-to-left layout). In a wrapping list the arrows along the lines move in reading order and the arrows across them move to the nearest item of the next or previous line. They do not change the selection. |
| Home / End | Moves focus to the first or last item. |
| Printable characters | Moves focus to the next item whose name begins with the characters typed. |
| Space / Enter | Activates the focused item: an interactive item emits its click; an option is selected (single) or switched (multiple). A link item is activated by Enter alone. |
| Enter / Down Arrow (on a handle) | Opens the move menu: Move to start, Move before, Move after, Move to end, in the product's words. Choosing one moves the item; focus returns to its handle. |
| Space (on a handle) | Lifts the item. Then the arrow keys of the arrangement move it one place, Home and End to the first and last place, Space or Enter drop it there, and Escape puts it back. Each step is announced. |
| Escape (while an item is dragged) | Puts the item back where it was. Nothing is moved. |
ملاحظات
- The list is a box: what an item shows is the product's own content, in a slot. The list owns the arrangement (a column, a line, or lines that wrap into a grid, with a gap and dividers), the semantics (list or listbox, positions and size), keyboard focus among actionable items, selection, and reordering. A row with a headline, supporting text, a leading glyph, picture or control and a trailing value, glyph or control is a List row placed in the item as its direct child: the item tells the row what it is (LST-48) and takes its name from the row's headline (LST-49).
- Four kinds of item, chosen by props: static (selection none, interactive false, no href) draws nothing of its own — its content is the item — and may hold the product's controls; interactive is one button inside its list item; a link item (href) is one link inside its list item; an option (selection single or multiple) is chosen. The last three are drawn as a padded box with every state and the minimum target; a list may mix static, interactive and link items, and a selectable list holds only options.
- The list does not scroll or virtualize: the product places it in its own scroll view, or builds it lazily with the platform's lazy list and states the set size where the platform needs it.
- Reordering meets WCAG 2.2 success criterion 2.5.7 (dragging movements) and 2.1.1 (keyboard) without move buttons on every item: the handle is a button whose menu moves the item to the start, one place before, one place after or to the end — single presses for a pointer, touch, a switch, voice control and a screen reader — and with the keyboard it lifts the item, which the arrow keys carry and Space drops, as in the drag and drop patterns of mature systems (Atlassian's and GitHub's).
- The whole item can be dragged, not only the handle: a mouse lifts it after a few points of movement, a touch or a pen after a long press (so that a swipe still scrolls) or at once from the handle. The handle is the visible sign that the item can be moved, and the one part that both a pointer and the keyboard can use.
- The order is the product's data. The list emits move with from and to and draws the items in the order the product then gives; a product that refuses a move changes nothing and the list announces nothing.
- A reorderable list holds static items only, and is never a listbox: an action, a link or an option is taken by a press that a drag would take. What a reorderable item may hold besides its content: the product's own controls, such as a button that removes the item; the list keeps focus in the list when the item with focus is removed (LST-46).
- The four move words and the three sentences are the product's, with places to fill. The product writes the sentence, so the order of the words is its language's; the list fills in a name and two numbers and builds no phrase of its own. In a line, the product says "Move left" and "Move right" (or the reverse in a right-to-left language) where a column says "Move up" and "Move down".
- A wrapping list is still one list in one order: it is read, tabbed through and counted line by line in the reading direction, and a move to another line is a move to another place in that order.
- WCAG 2.5.8 (target size): for touch every interactive item, link item and option is at least the touch target high in both densities (LST-11). The compact density is lower than the pointer target for a fine pointer, by design: it is the density of a tool used with a mouse, it stays above the 24 by 24 the criterion asks, and its items reach across the list's whole width. A product that cannot accept that uses regular.
مولَّد من specs/list.spec.json, tokens/sys/measure.tokens.json
- تُعرض القائمة بلا تحديد قائمةً، وكل عنصر عنصرَ قائمة بموضعه وحجم القائمة. العنصر التفاعلي زر واحد داخل عنصر القائمة، وعنصر الرابط رابط واحد هناك. والقائمة القابلة للاختيار مربع قائمة يسمّيه
label، باتجاهه؛ وكل عنصر خيار بحالة تحديده. - القائمة التي فيها عناصر تفاعلية أو روابط أو خيارات موضع Tab واحد. تنقل مفاتيح الأسهم الخاصة بالترتيب بين العناصر دون التفاف (أعلى وأسفل في العمود، ويسار ويمين في السطر، معكوسة في التخطيط من اليمين إلى اليسار؛ وفي الشبكة عبر الأسطر أيضًا). ويعمل Home وEnd والبحث بالكتابة. ولا تأخذ العناصر الثابتة التركيز؛ وعنصر التحكم داخلها يحتفظ بموضع Tab خاص به.
- في القائمة القابلة لإعادة الترتيب، كل مقبض زر قائمة منبثقة يسمّيه
dragHandleLabelالخاص بك مع اسم العنصر، ويصفهdragHandleHintالخاص بك، وهو موضع Tab مستقل. وتنقل قائمته المنبثقة العنصر بضغطات مفردة، وهذا يلبي متطلب WCAG بأن ما يُفعل بالسحب يمكن فعله دونه (2.5.7). ويرفع Space العنصر للوحة المفاتيح (2.1.1). - منطقة حية واحدة مهذبة تقول متى رفعت لوحة المفاتيح عنصرًا، وأين سيستقر في كل خطوة، وأين يقف بعد أن تأخذ الحركة أثرها، وأنه عاد بعد Escape.
- يتبع العنصر المسحوب المؤشر بتعبئة السحب وظله؛ وتتنحى العناصر الأخرى بحركة إعادة الترتيب. وحيث تُقلَّل الحركة، يتحرك كل شيء فورًا عدا العنصر الذي تحت المؤشر. وفي الألوان القسرية يُحاط العنصر المسحوب بلون التمييز.
- يعيد Escape العنصر المسحوب ولا يغلق معه الحوار الذي فيه القائمة. والضغط المطوَّل على عنصر لا يفتح أي قائمة للمتصفح.
- العنصر التفاعلي أو الرابط أو الخيار لا يقل عن الهدف الأدنى، وتُرسم حلقة التركيز داخله، ويكبر مع النص الأكبر.
- تُبلغ إصدارات التصحيح عن عنصر بلا اسم، وقائمة قابلة للاختيار أو لإعادة الترتيب بلا تسمية، وخيار قيمته فارغة أو مكررة، وتحديد لا يسمّي أي خيار، وعنصر تحكم داخل عنصر تفاعلي أو خيار، وكلمة لم يعطها أحد، و
reorderableعلى قائمة قابلة للاختيار، وقيمةiconSpaceلا تعرّفها القائمة.
الفجوات المعروفة في المنصات:
- تقدّم المنصات SwiftUI وCompose وFlutter وReact Native القائمة السابقة ولا شيء من هذه المراجعة: لا اتجاه، ولا التفاف، ولا مسافة، ولا فتحة للمحتوى، ولا إعادة ترتيب.
- أعطِ القائمة القابلة للاختيار أو لإعادة الترتيب
label. - أعطِ القائمة القابلة لإعادة الترتيب كلماتها بلغة منتجك. بدونها تعمل المقابض بلا اسم ولا يُعلَن شيء.
- أعد ترتيب عناصرك حين يُطلق
move. لم تغيّر القائمة شيئًا. - أعطِ عنصر الرابط الذي قيمة
targetفيهnewتسميةtargetLabel، مثل «يُفتح في علامة تبويب جديدة». - أبقِ العناصر التفاعلية والروابط والخيارات خالية من عناصر التحكم. العنصر الذي يحتاج عنصر تحكم خاصًا به يبقى ثابتًا.
- للبيانات الكثيرة، ابنِ قائمتك الكسولة وصرّح بحجم المجموعة حيث تحتاجه المنصة. لا تمرّر القائمة ولا تُجري المحاكاة الافتراضية.