- Web
tanrei-listclock_loader_40 - SwiftUI
TanreiListschedule - Jetpack Compose
TanreiListschedule - Flutter
TanreiListschedule - React Native
Listschedule
生成自 docs/status/status.json, specs/list.spec.json
把磁贴拖到别处,或按下它的手柄打开移动菜单
列表是一个容器。每一项显示的是产品自己的内容:文本、图片、卡片、文件项,或列表行——由标题、一两行辅助文本以及其前后的内容组成的一行,大多数列表都由这样的行构成。列表负责项周围的一切:项如何排列、项之间的间距和分隔线、它们对辅助技术意味着什么、键盘焦点如何在项之间移动、哪些项被选中,以及如何调整顺序。
一列、一行和折行的网格
打开某物的项、作为链接的项,以及单选
列表只有一种项 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 形式写成的需求。每个平台的实现都按这些 ID 逐条测试。需求原文取自规格文件,原样以英文呈现。
| ID | 需求 |
|---|---|
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. |
各平台名称对照
对等并不意味着 API 完全相同。同一份契约,由各平台以自己的惯用方式表达。
| 规格 | 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 |
测试样例
作为基准的渲染结果。所有平台都以相同的 ID 为每个样例截取快照,以便相互比较。
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,例如“在新标签页中打开”。 - 不要在交互项、链接和选项中放入控件。需要自己控件的项保持静态。
- 数据量大时,自行构建延迟加载的列表,并在平台需要时说明项数。列表不滚动,也不虚拟化。