步骤条
在产品按固定步骤顺序推进的流程中,显示一个人所处的位置:哪一步是当前步骤,哪些已完成,哪一步有错误,哪些还没到;并让他们按下某一步回到那里。
- 类别
- 导航
- 令牌
comp.stepper- 层级
- 基础
- Web
tanrei-stepperclock_loader_40 - SwiftUI
TanreiStepperradio_button_unchecked - Jetpack Compose
TanreiStepperradio_button_unchecked - Flutter
TanreiStepperradio_button_unchecked - React Native
Stepperradio_button_unchecked
生成自 docs/status/status.json, specs/stepper.spec.json
水平,含一个有错误的步骤
步骤条(stepper)放在一个有位置可言的流程的内容上方或旁边:结账、初始设置、分成几部分填写的申请。一步是当前步骤,它之前的步骤已完成,可能有一步需要这个人处理,其余的还没到。
各个步骤是按顺序排列的 <tanrei-stepper-step> 子元素。value 指明当前步骤。步骤条从不自己改变 value:这个人按下某一步时,它触发带有该步骤值的 select,由你的流程决定是否前往那里,并设置 value。
SwiftUI、Compose、Flutter 和 React Native 尚在计划中:本页开头各平台旁边的名称是规格为它们定下的,这些平台目前还没有提供任何实现。
<tanrei-stepper id="checkout" label="结账进度" value="shipping" pressable="visited"
completed-text="已完成" error-text="有错误" position-text="第 {position} 步,共 {count} 步">
<tanrei-stepper-step value="account" label="账户"></tanrei-stepper-step>
<tanrei-stepper-step value="shipping" label="配送" description="地址和配送方式"></tanrei-stepper-step>
<tanrei-stepper-step value="gift" label="礼品包装" description="可选"></tanrei-stepper-step>
<tanrei-stepper-step value="payment" label="付款"></tanrei-stepper-step>
</tanrei-stepper>
<script type="module">
import '@tanrei/web/components/stepper';
const stepper = document.querySelector('#checkout');
// 按下某一步是请求前往那里。由流程决定,然后再改变 value。
stepper.addEventListener('select', (e) => {
if (goTo(e.detail.value)) stepper.value = e.detail.value;
});
</script>import { Stepper, StepperStep } from '@tanrei/react';
<Stepper
label="结账进度"
value={step}
pressable="visited"
completedText="已完成"
errorText="有错误"
positionText="第 {position} 步,共 {count} 步"
onSelect={(e) => setStep(e.detail.value)}
>
<StepperStep value="account" label="账户" />
<StepperStep value="shipping" label="配送" status={shippingError ? 'error' : 'auto'} />
<StepperStep value="payment" label="付款" />
<StepperStep value="review" label="确认" />
</Stepper>文字由你提供:completed-text 和 error-text 是辅助技术在步骤标签之后朗读的内容,position-text 是紧凑形态显示的那一行,其中有 {position} 和 {count} 两个占位。用 setWords({ stepCompletedText, stepErrorText, stepPositionText }) 可以为所有步骤条只给一次。
垂直,以及紧凑形态
构成
| 部件 | 说明 |
|---|---|
1 root | The ordered list of the flow's steps, named by label: one row of equal columns (horizontal), one column (vertical), or, in the compact form, a bar and two lines of text. It has no fill, no edge and no padding of its own. |
2 step | One item of the list: its marker, its label and, where it has one, its description. A step is in exactly one condition — incomplete, current, complete or error — and, while it can be pressed, in the interaction states. |
3 container | The area of a step that carries hover, focus and press while the step can be pressed: its marker and its text, with the container's padding around them. It has no fill at rest. A step that cannot be pressed draws its container but answers nothing. |
4 marker | What begins a step: a filled or ringed circle holding the step's number, the glyph check (complete) or the glyph priority_high (error). Its fill, ring and content say the step's condition; it is decorative, because the step's condition is also said in words. |
5 label | The step's name, in a few words ("Shipping"). It wraps and is never cut. |
6 description · 可选 | A short second line under the label ("Optional", "2 items"), in a smaller and quieter style. It wraps and is never cut. |
7 connector | The line from a step's marker to the next step's marker: across the column in the horizontal form, down beside the text in the vertical form. Drawn in the complete color after a complete step, so that the way already gone is one line. Decorative. The last step has none. |
8 bar · 可选 | The compact form's row of segments, one per step, filled up to and including the current step. Decorative: the position text says the same in words. |
9 position · 可选 | The compact form's position in the product's words ("Step 2 of 5"), above the current step's label. |
10 focus-ring | The indicator shown for keyboard focus, around a pressable step's container. |
属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
label | string | "" | The accessible name of the stepper ("Checkout progress"), in the product's language. Not drawn. It tells the list of steps from the content it tracks, so it is required. |
value | string | "" | The value of the current step: where the person is. Empty, or a value no step has, makes no step current — before the flow begins, or once it is finished. The stepper never changes it itself: the product moves its flow and sets value. |
layout | horizontal | vertical | compact | "horizontal" | horizontal: the steps side by side in equal columns, above the content they track; where the width it is given cannot hold every step at its least width, the stepper draws the compact form instead (SPR-6). vertical: the steps one under another, beside the content or in a narrow column. compact: the bar and the position in words ("Step 2 of 5") with the current step's label, at any width. |
pressable | none | visited | all | "none" | Which steps the person can press to go to them. none: no step; the stepper only shows where the person is. visited: each step that is complete or has an error and is not current — the steps the person has been to, so that they can go back. all: every step that is not current, for a flow whose steps can be done in any order. A disabled step is never pressable, and the current step never is. The compact form presses nothing. |
completedText | string | "" | The word assistive technology hears after the label of a complete step ("Completed"), in the product's language. Not drawn: the check says it to the eye. The product's: Tanrei has no word for it. Empty reads the product's words. Required while a step is complete. |
errorText | string | "" | The word assistive technology hears after the label of a step with an error ("Has an error"), in the product's language. Not drawn: the glyph says it to the eye. The product's: Tanrei has no word for it. Empty reads the product's words. Required while a step has an error. |
positionText | string | "" | The compact form's position: the whole sentence in the product's language with two places the stepper fills, {position} with the current step's place counted from 1 and {count} with the number of steps ("Step {position} of {count}", "ステップ {position}/{count}"). The product's: Tanrei has no sentence for it. Empty reads the product's words. Required while the compact form is drawn. |
事件
| 事件 | 说明 |
|---|---|
select | The person pressed a step to go to it, by pointer, touch, Enter, Space or assistive technology; carries the step's value. The stepper does not change value: the product goes to the step (or does not) and sets value. |
条目
该组件重复排列的条目种类。测试样例按顺序列出条目;各平台以各自的惯用方式为每个种类命名。
step
One step of the flow: its value, its label, an optional description, its status and whether it can be visited.
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | string | "" | Identifies the step: the stepper's value names the current step by it, and select carries it. Unique within the stepper; required. |
label | string | "" | The step's name, in a few words ("Shipping"). Drawn, and the start of what assistive technology hears for the step. Required. |
description | string | "" | A short second line under the label ("Optional", "2 items"). Empty draws none. |
status | auto | incomplete | complete | error | "auto" | What the product knows of the step. auto: complete when it comes before the current step and incomplete when it comes after it (or when no step is current) — a flow done in order. incomplete and complete: so, wherever the step stands (a step skipped and left for later; a step done ahead in a flow of any order). error: the step needs the person's attention. The current step is drawn current whatever its status, except error, which it keeps. |
disabled | boolean | false | The step cannot be gone to now (it depends on an earlier choice). Only meaningful while pressable is not none: the step is drawn disabled and is not pressable. Ignored on the current step. |
| 规格 | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| 条目 | tanrei-stepper-step | TanreiStepperStep | TanreiStepperStep | TanreiStepperStep | StepperStep |
value · 属性 | value | id | value | value | value |
label · 属性 | label | label | label | label | label |
description · 属性 | description | description | description | description | description |
status · 属性 | status | status | status | status | status |
disabled · 属性 | disabled | disabled | enabled | enabled | disabled |
| 备注 | A child element: <tanrei-stepper-step value="shipping" label="Shipping" description="…" status="error" disabled>, as <option> in a <select>. | TanreiStepperStep(id:, label:, description:, status: .auto, disabled:), an Identifiable value. | A data class; enabled is the inverse flag. | A value class; enabled is the inverse flag. | The element type of the steps array: { value, label, description?, status?, disabled? }. |
状态
行为
以 EARS 形式写成的需求。每个平台的实现都按这些 ID 逐条测试。需求原文取自规格文件,原样以英文呈现。
| ID | 需求 |
|---|---|
SPR-1 | THE stepper SHALL draw each step in exactly one condition: current WHILE value equals the step's value (the first step with that value, SPR-21) and its status is not error; error WHILE its status is error, current or not; complete WHILE its status is complete, or its status is auto and it comes before the current step; incomplete WHILE its status is incomplete, or its status is auto and it comes after the current step or no step is current. WHEN value or a step's status changes THE stepper SHALL draw every step's new condition at once, without a motion of its own beyond the state motion of SPR-17. |
SPR-2 | THE stepper SHALL draw each step's marker as a circle at least comp.stepper.marker.size high and wide, with corners comp.stepper.marker.shape eased by sys.shape.corner-smoothing, filled with the marker color of the step's condition and state (comp.stepper.incomplete.marker.color, comp.stepper.current.marker.color, comp.stepper.complete.marker.color, comp.stepper.error.marker.color at rest) and edged inside its size by a ring comp.stepper.marker.outline.width wide in the marker outline color of the same (comp.stepper.incomplete.marker.outline.color, and so on). An incomplete or current marker SHALL hold the step's number — its place in the flow, counted from 1 — in comp.stepper.marker.label.typescale and the marker content color of its condition, centred, as a left-to-right run whose digits are never reversed; a complete marker SHALL hold the glyph check and an error marker the glyph priority_high instead, in the marker content color of their condition, each in a box comp.stepper.marker.icon.size large through the platform's icon helper with sys.icon.family.selected, sys.icon.fill.selected, the weight comp.stepper.marker.icon.weight and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max, unmirrored in every layout direction; THE stepper SHALL draw no mark of its own. |
SPR-3 | THE stepper SHALL draw each step's label in comp.stepper.label.typescale and the label color of its condition and state, and, WHERE the step has a description, the description comp.stepper.description.gap under the label in comp.stepper.description.typescale and the description color of the same; it SHALL wrap both where their language allows a break and, for a run with no such place that is longer than the line, inside the run, and SHALL NOT cut them, draw an ellipsis or scroll. A change of condition SHALL NOT change the label's text style, so that no step moves when the current step does. |
SPR-4 | WHILE layout is horizontal and the full form is drawn (SPR-6) THE stepper SHALL lay its steps out in one row of columns of equal width, in order, each column a step's container: its marker at the container's start, comp.stepper.container.padding from its edges, and its label and description under the marker, comp.stepper.marker.gap below it, aligned to the start and as wide as the column less its padding; the row SHALL be exactly as wide as the place it stands in and as high as its tallest step, every step's marker on one line. |
SPR-5 | WHILE layout is vertical THE stepper SHALL lay its steps out in one column, in order, each step's container as wide as the stepper: its marker at the container's start, comp.stepper.container.padding from its edges, and its label and description beside the marker, comp.stepper.marker.gap from it, filling the rest of the width; the marker SHALL be centred on the label's first line, and WHERE the marker is taller than that line the stepper SHALL lower the text by half the difference, so that the marker's top is the content's top. |
SPR-6 | WHILE layout is compact, or layout is horizontal and the width the layout gives the stepper is less than the number of steps times comp.stepper.step.min-width, THE stepper SHALL draw the compact form instead of its steps: the bar — one segment per step in a row as wide as the stepper, comp.stepper.bar.gap apart, each comp.stepper.bar.thickness thick with corners comp.stepper.bar.shape, the segments of the current step and every step before it in comp.stepper.bar.filled.color and the others in comp.stepper.bar.color — then, comp.stepper.position.gap under it, the position in comp.stepper.position.typescale and comp.stepper.position.color, and under that the current step's label as SPR-3 draws a current step's, or an error step's where the current step has an error, preceded there by its marker's glyph priority_high at comp.stepper.marker.icon.size in the error label color. It SHALL decide by the width it is given, not by the window's, and SHALL change form at once WHEN that width crosses the threshold, keeping value. WHILE no step is current THE stepper SHALL draw the bar with no segment filled, and neither the position nor a label. |
SPR-7 | THE stepper SHALL fill the position's places as the product's sentence marks them: {position} with the current step's place counted from 1 and {count} with the number of steps, each written as the platform writes a whole number in the language of the element; it SHALL do nothing else to the sentence, and SHALL draw a sentence without {position} as it is written and in debug builds report it. |
SPR-8 | WHILE a step is followed by another step and the full form is drawn THE stepper SHALL draw a connector from that step's marker to the next step's: a straight line comp.stepper.connector.thickness thick, starting comp.stepper.connector.gap after the step's marker and ending comp.stepper.connector.gap before the next step's marker — across the row on the markers' centre line in the horizontal form, down the markers' centre line in the vertical form — in comp.stepper.connector.complete.color WHILE the step is complete and comp.stepper.connector.color otherwise. No container's fill or state layer SHALL cover a connector, and the connector SHALL be hidden from assistive technology. |
SPR-9 | WHILE pressable is visited THE stepper SHALL make pressable each step that is drawn complete or error, is not current and is not disabled; WHILE pressable is all, each step that is not current and is not disabled; WHILE pressable is none, no step; and in the compact form no step, whatever pressable says. A pressable step SHALL be the platform's own button for the whole of its container, in the focus order in the order of the steps; a step that is not pressable SHALL NOT take focus, SHALL NOT answer a pointer, a press or a key, and SHALL draw no interaction state. |
SPR-10 | WHEN the person activates a pressable step by pointer, touch, Enter, Space or assistive technology THE stepper SHALL emit select exactly once with that step's value, and SHALL NOT change value or any step's condition itself: the product goes to the step and sets value, or keeps the person where they are. |
SPR-11 | WHILE pressable is not none and a step's disabled is true and the step is not current THE stepper SHALL draw the step from the disabled tokens of its condition — its label, description, marker content and ring faded as disabled content, its fill kept, SHALL NOT make it pressable, and SHALL expose it as a disabled button, so that a person who reaches it hears that it cannot be gone to now; WHILE pressable is none THE stepper SHALL draw and expose a step whose disabled is true as it draws any other, because nothing is pressed and so nothing is unavailable. WHILE the current step's disabled is true THE stepper SHALL draw it current and in debug builds report it. |
SPR-12 | WHILE a pressable step is hovered, keyboard-focused or pressed THE stepper SHALL render its container, marker, marker outline, marker content, label, description 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. |
SPR-13 | WHEN a pressable step receives keyboard focus THE stepper SHALL show the focus ring around that step's container without animation — the system's focus indicator, sys.focus.offset from the container, sys.focus.outer-width wide in the focus-ring color, with the inner stroke of sys.focus.inner-width where the system has one; focus gained by pointer or touch SHALL NOT show it. |
SPR-14 | WHEN a pressable step is pressed by pointer, touch, Enter or Space THE stepper 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. |
SPR-15 | WHILE pressed THE stepper 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 step 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; marker, label, description, 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.stepper.motion.feedback-duration with comp.stepper.motion.state-easing, fades over comp.stepper.motion.state-duration with comp.stepper.motion.state-easing once the press has ended and press-hold has passed, and appears whole where motion is reduced. |
SPR-16 | THE stepper SHALL ease its containers' corners, comp.stepper.container.shape, 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. |
SPR-17 | WHEN a step changes between interaction states or between conditions THE stepper SHALL change its colors over comp.stepper.motion.state-duration with comp.stepper.motion.state-easing, and at once where motion is reduced; a glyph or a number that replaces another in a marker SHALL be replaced at once. |
SPR-18 | THE stepper SHALL make every step's container at least sys.target.pointer high and wide for fine pointers and sys.target.touch for touch input — a platform that cannot tell the input type SHALL use sys.target.touch — leaving the room it adds below and after the container's content, so that a pressable step's hit area meets the minimum target without reaching over a neighbouring step's, and so that a pressable and a plain step stand alike. |
SPR-19 | THE stepper SHALL be exposed to assistive technology as an ordered list named by label, of as many items as it has steps, each step one item holding, in this order, its label, the product's word for its condition where it has one — completedText for a complete step, errorText for a step with an error, nothing for an incomplete one — and its description; the current step SHALL be exposed as the current step of the flow (the web's aria-current="step"; the platform's selected trait with its position where it has no current state). Markers, numbers, glyphs, connectors and the bar SHALL be hidden: the list gives each step its place, and the words its condition. WHILE the compact form is drawn THE stepper SHALL instead be exposed as one group named by label holding the position and the current step's label, followed by errorText where the current step has an error, so that what is heard is what is drawn. |
SPR-20 | THE stepper SHALL have no words of its own: it SHALL take completedText, errorText and positionText from the instance's prop and, WHILE that is empty, from the product's words of the same keys; WHILE a word that a drawn step needs was given by nobody THE stepper SHALL expose nothing in its place, still draw the step, and record and report the missing word as the product's words do. |
SPR-21 | WHEN value names no step THE stepper SHALL make no step current; WHEN a step's value is empty or repeats another step's THE stepper SHALL still draw it and treat the first step with a value as the one that value names; WHILE label is empty, a step's label is empty, or the stepper holds fewer than two steps, THE stepper SHALL still render; in debug builds it SHALL report each of these, except an empty value, which names the time before or after the flow. |
SPR-22 | WHILE the text size is enlarged THE stepper SHALL grow each label, description, number and position with it and grow each marker with its number, keeping comp.stepper.marker.size as a minimum and every gap and padding as it is, SHALL wrap and never cut text, and SHALL still choose its form by the width it is given (SPR-6). |
SPR-23 | 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 stepper SHALL draw every ring, connector, segment, number, glyph and text in the platform's text color — a pressable step's label in the platform's button text color — the current step's marker filled in the platform's highlight color with its number in the highlight text color, and the filled segments of the bar in the highlight color, and SHALL draw the focus ring in the platform's highlight color; nothing it draws relies on a fill alone. |
SPR-24 | THE stepper 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 stepper is shown THE stepper SHALL draw itself for the new direction at once, without a motion of its own, keeping its focus. |
SPR-25 | WHERE the layout direction is right-to-left THE stepper SHALL mirror its layout by logical start and end — the first step at the right of a horizontal row, the markers at the right of a vertical column, the bar filled from the right — and SHALL keep its reading and focus order first step to last; it SHALL NOT mirror a glyph or reverse a number. |
SPR-26 | THE stepper SHALL lay out each label, description and the position as text of its own direction, isolated from the text around it: it takes the base direction of its first strong character, or the layout direction where it has none, so that a left-to-right name or a number keeps its own order in a right-to-left layout; THE stepper SHALL still place the text and align its lines by the layout direction, not by the text's own. |
SPR-27 | IF the icon set cannot draw a marker's glyph yet (its font is still loading or failed to load) THE stepper SHALL draw nothing in the glyph's place, never the glyph's name as text, keeping the marker's size, fill and ring, and SHALL still expose the step's condition in words; it SHALL show the glyph once the icon set can draw it. |
SPR-28 | WHERE the platform has ARIA attributes (the web), the host carries its list or group role (SPR-19) itself, so what the product writes on it is not moved, but for a name: WHILE label is not empty THE stepper SHALL hold the product's aria-label and aria-labelledby on that host as their data-aria- twins, so that label is its whole name although the platform gives aria-labelledby the precedence, and WHEN label is emptied SHALL give them back as they were written; WHILE label is empty a name the product gives so — aria-label, or aria-labelledby naming an element — SHALL be its name and SHALL NOT be reported as having none in debug builds (SPR-21). WHEN the product removes one, or its data-aria- twin, or sets the ARIA property that reflects it (ariaLabel, ariaLabelledByElements) to null, THE stepper SHALL take it away. |
各平台名称对照
对等并不意味着 API 完全相同。同一份契约,由各平台以自己的惯用方式表达。
| 规格 | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| 组件 | tanrei-stepper | TanreiStepper | TanreiStepper | TanreiStepper | Stepper |
label · 属性 | label | label | contentDescription | semanticLabel | accessibilityLabel |
value · 属性 | value | current | current | current | value |
layout · 属性 | layout | layout | layout | layout | layout |
pressable · 属性 | pressable | pressable | pressable | pressable | pressable |
completedText · 属性 | completedText | completedText | completedText | completedText | completedText |
errorText · 属性 | errorText | errorText | errorText | errorText | errorText |
positionText · 属性 | positionText | positionText | positionText | positionText | positionText |
select · 事件 | select | onSelect | onSelect | onSelect | onSelect |
| 备注 | <tanrei-stepper label="Checkout" value="payment" layout="horizontal" pressable="visited"> holds <tanrei-stepper-step> children, in order. The stepper is a role="list" named by aria-label, each step host a role="listitem" with aria-current="step" on the current one; a pressable step draws a native <button> in its shadow root around its marker and text, a step that is not pressable the same parts in a <div>. The words for a condition are a visually hidden span after the label. The compact form is drawn in the stepper's own shadow root, where the steps are not displayed, and the host's role becomes group. The switch from horizontal to compact reads the host's width with a ResizeObserver against comp.stepper.step.min-width measured from a probe. select is a CustomEvent with detail.value; value is never set by the element. React: Stepper and StepperStep, generated wrappers. Fixtures fill their stage: 600 wide for a horizontal stepper, 360 for a vertical one, 320 for a compact one. | TanreiStepper(_ label:, steps:, current: ID?, layout:, pressable:, onSelect:). Not SwiftUI's Stepper, which increments a value (the number input's steppers are that). The list is an accessibility container; the current step carries the .isSelected trait and its position as its accessibility value, because iOS has no current-step state. The compact switch reads the proposed width (ViewThatFits). | TanreiStepper(steps, current, onSelect, layout, pressable, contentDescription, modifier). A collectionInfo list with collectionItemInfo per step; the current step is selected = true with its position in stateDescription, because Compose has no current-step semantics. The compact switch reads the incoming constraints (BoxWithConstraints). | TanreiStepper(steps:, current:, onSelect:, layout:, pressable:, semanticLabel:), built with the widgets library, without Material's Stepper. Semantics(container: true, label:) around a list; the current step Semantics(selected: true) with its position in its value. The compact switch reads the incoming constraints (LayoutBuilder). | <Stepper steps={StepperStep[]} value layout pressable onSelect accessibilityLabel />. The list has role="list" where the platform maps it, each step role="listitem"; the current step aria-current on the web target and accessibilityState.selected with its position elsewhere. A pressable step is a Pressable with role="button". The compact switch reads onLayout. |
令牌
| 令牌 | 引用 | 解析值(浅色) |
|---|---|---|
--tanrei-comp-stepper-bar-color | sys.color.outline-variant | |
--tanrei-comp-stepper-bar-filled-color | sys.color.primary | |
--tanrei-comp-stepper-bar-gap | sys.space.space50 | 4px |
--tanrei-comp-stepper-bar-shape | sys.shape.corner.full | 9999px |
--tanrei-comp-stepper-bar-thickness | sys.space.space50 | 4px |
--tanrei-comp-stepper-complete-container-color | — | |
--tanrei-comp-stepper-complete-description-color | sys.color.on-surface-variant | |
--tanrei-comp-stepper-complete-disabled-container-color | — | |
--tanrei-comp-stepper-complete-disabled-description-color | — | |
--tanrei-comp-stepper-complete-disabled-label-color | — | |
--tanrei-comp-stepper-complete-disabled-marker-color | — | |
--tanrei-comp-stepper-complete-disabled-marker-content-color | — | |
--tanrei-comp-stepper-complete-disabled-marker-outline-color | — | |
--tanrei-comp-stepper-complete-disabled-state-layer-color | — | |
--tanrei-comp-stepper-complete-focused-container-color | — | |
--tanrei-comp-stepper-complete-focused-description-color | — | |
--tanrei-comp-stepper-complete-focused-label-color | — | |
--tanrei-comp-stepper-complete-focused-marker-color | — | |
--tanrei-comp-stepper-complete-focused-marker-content-color | — | |
--tanrei-comp-stepper-complete-focused-marker-outline-color | — | |
--tanrei-comp-stepper-complete-focused-state-layer-color | — | |
--tanrei-comp-stepper-complete-hovered-container-color | — | |
--tanrei-comp-stepper-complete-hovered-description-color | — | |
--tanrei-comp-stepper-complete-hovered-label-color | — | |
--tanrei-comp-stepper-complete-hovered-marker-color | — | |
--tanrei-comp-stepper-complete-hovered-marker-content-color | — | |
--tanrei-comp-stepper-complete-hovered-marker-outline-color | — | |
--tanrei-comp-stepper-complete-hovered-state-layer-color | — | |
--tanrei-comp-stepper-complete-label-color | sys.color.on-surface | |
--tanrei-comp-stepper-complete-marker-color | sys.color.secondary-container | |
--tanrei-comp-stepper-complete-marker-content-color | sys.color.on-secondary-container | |
--tanrei-comp-stepper-complete-marker-outline-color | sys.color.secondary-container | |
--tanrei-comp-stepper-complete-pressed-container-color | — | |
--tanrei-comp-stepper-complete-pressed-description-color | — | |
--tanrei-comp-stepper-complete-pressed-label-color | — | |
--tanrei-comp-stepper-complete-pressed-marker-color | — | |
--tanrei-comp-stepper-complete-pressed-marker-content-color | — | |
--tanrei-comp-stepper-complete-pressed-marker-outline-color | — | |
--tanrei-comp-stepper-complete-pressed-state-layer-color | — | |
--tanrei-comp-stepper-complete-state-layer-color | comp.stepper.complete.label.color | |
--tanrei-comp-stepper-connector-color | sys.color.outline-variant | |
--tanrei-comp-stepper-connector-complete-color | sys.color.primary | |
--tanrei-comp-stepper-connector-gap | sys.space.space50 | 4px |
--tanrei-comp-stepper-connector-thickness | sys.border.thick | 2px |
--tanrei-comp-stepper-container-padding | sys.space.space100 | 8px |
--tanrei-comp-stepper-container-shape | sys.shape.corner.small | 6px |
--tanrei-comp-stepper-current-container-color | — | |
--tanrei-comp-stepper-current-description-color | sys.color.on-surface-variant | |
--tanrei-comp-stepper-current-disabled-container-color | — | |
--tanrei-comp-stepper-current-disabled-description-color | — | |
--tanrei-comp-stepper-current-disabled-label-color | — | |
--tanrei-comp-stepper-current-disabled-marker-color | — | |
--tanrei-comp-stepper-current-disabled-marker-content-color | — | |
--tanrei-comp-stepper-current-disabled-marker-outline-color | — | |
--tanrei-comp-stepper-current-disabled-state-layer-color | — | |
--tanrei-comp-stepper-current-focused-container-color | — | |
--tanrei-comp-stepper-current-focused-description-color | — | |
--tanrei-comp-stepper-current-focused-label-color | — | |
--tanrei-comp-stepper-current-focused-marker-color | — | |
--tanrei-comp-stepper-current-focused-marker-content-color | — | |
--tanrei-comp-stepper-current-focused-marker-outline-color | — | |
--tanrei-comp-stepper-current-focused-state-layer-color | — | |
--tanrei-comp-stepper-current-hovered-container-color | — | |
--tanrei-comp-stepper-current-hovered-description-color | — | |
--tanrei-comp-stepper-current-hovered-label-color | — | |
--tanrei-comp-stepper-current-hovered-marker-color | — | |
--tanrei-comp-stepper-current-hovered-marker-content-color | — | |
--tanrei-comp-stepper-current-hovered-marker-outline-color | — | |
--tanrei-comp-stepper-current-hovered-state-layer-color | — | |
--tanrei-comp-stepper-current-label-color | sys.color.on-surface | |
--tanrei-comp-stepper-current-marker-color | sys.color.primary | |
--tanrei-comp-stepper-current-marker-content-color | sys.color.on-primary | |
--tanrei-comp-stepper-current-marker-outline-color | sys.color.primary | |
--tanrei-comp-stepper-current-pressed-container-color | — | |
--tanrei-comp-stepper-current-pressed-description-color | — | |
--tanrei-comp-stepper-current-pressed-label-color | — | |
--tanrei-comp-stepper-current-pressed-marker-color | — | |
--tanrei-comp-stepper-current-pressed-marker-content-color | — | |
--tanrei-comp-stepper-current-pressed-marker-outline-color | — | |
--tanrei-comp-stepper-current-pressed-state-layer-color | — | |
--tanrei-comp-stepper-current-state-layer-color | comp.stepper.current.label.color | |
--tanrei-comp-stepper-description-gap | sys.space.space25 | 2px |
--tanrei-comp-stepper-disabled-container-shape | — | 6px |
--tanrei-comp-stepper-disabled-opacity | — | 1 |
--tanrei-comp-stepper-error-container-color | — | |
--tanrei-comp-stepper-error-description-color | sys.color.error | |
--tanrei-comp-stepper-error-disabled-container-color | — | |
--tanrei-comp-stepper-error-disabled-description-color | — | |
--tanrei-comp-stepper-error-disabled-label-color | — | |
--tanrei-comp-stepper-error-disabled-marker-color | — | |
--tanrei-comp-stepper-error-disabled-marker-content-color | — | |
--tanrei-comp-stepper-error-disabled-marker-outline-color | — | |
--tanrei-comp-stepper-error-disabled-state-layer-color | — | |
--tanrei-comp-stepper-error-focused-container-color | — | |
--tanrei-comp-stepper-error-focused-description-color | — | |
--tanrei-comp-stepper-error-focused-label-color | — | |
--tanrei-comp-stepper-error-focused-marker-color | — | |
--tanrei-comp-stepper-error-focused-marker-content-color | — | |
--tanrei-comp-stepper-error-focused-marker-outline-color | — | |
--tanrei-comp-stepper-error-focused-state-layer-color | — | |
--tanrei-comp-stepper-error-hovered-container-color | — | |
--tanrei-comp-stepper-error-hovered-description-color | — | |
--tanrei-comp-stepper-error-hovered-label-color | — | |
--tanrei-comp-stepper-error-hovered-marker-color | — | |
--tanrei-comp-stepper-error-hovered-marker-content-color | — | |
--tanrei-comp-stepper-error-hovered-marker-outline-color | — | |
--tanrei-comp-stepper-error-hovered-state-layer-color | — | |
--tanrei-comp-stepper-error-label-color | sys.color.error | |
--tanrei-comp-stepper-error-marker-color | — | |
--tanrei-comp-stepper-error-marker-content-color | sys.color.error | |
--tanrei-comp-stepper-error-marker-outline-color | sys.color.error | |
--tanrei-comp-stepper-error-pressed-container-color | — | |
--tanrei-comp-stepper-error-pressed-description-color | — | |
--tanrei-comp-stepper-error-pressed-label-color | — | |
--tanrei-comp-stepper-error-pressed-marker-color | — | |
--tanrei-comp-stepper-error-pressed-marker-content-color | — | |
--tanrei-comp-stepper-error-pressed-marker-outline-color | — | |
--tanrei-comp-stepper-error-pressed-state-layer-color | — | |
--tanrei-comp-stepper-error-state-layer-color | comp.stepper.error.label.color | |
--tanrei-comp-stepper-focused-container-shape | — | 6px |
--tanrei-comp-stepper-focused-opacity | — | 1 |
--tanrei-comp-stepper-hovered-container-shape | — | 6px |
--tanrei-comp-stepper-hovered-opacity | — | 1 |
--tanrei-comp-stepper-incomplete-container-color | — | |
--tanrei-comp-stepper-incomplete-description-color | sys.color.on-surface-variant | |
--tanrei-comp-stepper-incomplete-disabled-container-color | — | |
--tanrei-comp-stepper-incomplete-disabled-description-color | — | |
--tanrei-comp-stepper-incomplete-disabled-label-color | — | |
--tanrei-comp-stepper-incomplete-disabled-marker-color | — | |
--tanrei-comp-stepper-incomplete-disabled-marker-content-color | — | |
--tanrei-comp-stepper-incomplete-disabled-marker-outline-color | — | |
--tanrei-comp-stepper-incomplete-disabled-state-layer-color | — | |
--tanrei-comp-stepper-incomplete-focused-container-color | — | |
--tanrei-comp-stepper-incomplete-focused-description-color | — | |
--tanrei-comp-stepper-incomplete-focused-label-color | — | |
--tanrei-comp-stepper-incomplete-focused-marker-color | — | |
--tanrei-comp-stepper-incomplete-focused-marker-content-color | — | |
--tanrei-comp-stepper-incomplete-focused-marker-outline-color | — | |
--tanrei-comp-stepper-incomplete-focused-state-layer-color | — | |
--tanrei-comp-stepper-incomplete-hovered-container-color | — | |
--tanrei-comp-stepper-incomplete-hovered-description-color | — | |
--tanrei-comp-stepper-incomplete-hovered-label-color | — | |
--tanrei-comp-stepper-incomplete-hovered-marker-color | — | |
--tanrei-comp-stepper-incomplete-hovered-marker-content-color | — | |
--tanrei-comp-stepper-incomplete-hovered-marker-outline-color | — | |
--tanrei-comp-stepper-incomplete-hovered-state-layer-color | — | |
--tanrei-comp-stepper-incomplete-label-color | sys.color.on-surface-variant | |
--tanrei-comp-stepper-incomplete-marker-color | — | |
--tanrei-comp-stepper-incomplete-marker-content-color | sys.color.on-surface-variant | |
--tanrei-comp-stepper-incomplete-marker-outline-color | sys.color.outline | |
--tanrei-comp-stepper-incomplete-pressed-container-color | — | |
--tanrei-comp-stepper-incomplete-pressed-description-color | — | |
--tanrei-comp-stepper-incomplete-pressed-label-color | — | |
--tanrei-comp-stepper-incomplete-pressed-marker-color | — | |
--tanrei-comp-stepper-incomplete-pressed-marker-content-color | — | |
--tanrei-comp-stepper-incomplete-pressed-marker-outline-color | — | |
--tanrei-comp-stepper-incomplete-pressed-state-layer-color | — | |
--tanrei-comp-stepper-incomplete-state-layer-color | comp.stepper.incomplete.label.color | |
--tanrei-comp-stepper-marker-gap | sys.space.space100 | 8px |
--tanrei-comp-stepper-marker-icon-size | sys.icon.extra-small | 16px |
--tanrei-comp-stepper-marker-icon-weight | sys.icon.mark.weight | 500 |
--tanrei-comp-stepper-marker-outline-width | sys.border.thick | 2px |
--tanrei-comp-stepper-marker-shape | sys.shape.corner.full | 9999px |
--tanrei-comp-stepper-marker-size | — | 24px |
--tanrei-comp-stepper-motion-feedback-duration | sys.motion.role.feedback | 450ms |
--tanrei-comp-stepper-motion-state-duration | sys.motion.role.state.large | 250ms |
--tanrei-comp-stepper-motion-state-easing | sys.motion.easing.state | cubic-bezier(0.2, 0, 0, 1) |
--tanrei-comp-stepper-position-color | sys.color.on-surface-variant | |
--tanrei-comp-stepper-position-gap | sys.space.space100 | 8px |
--tanrei-comp-stepper-pressed-container-shape | — | 6px |
--tanrei-comp-stepper-pressed-opacity | — | 1 |
--tanrei-comp-stepper-step-min-width | — | 120px |
--tanrei-comp-stepper-description-typescale | sys.typescale.body-small | 400 13px/1.5385 |
--tanrei-comp-stepper-label-typescale | sys.typescale.label | 500 14px/1.4286 |
--tanrei-comp-stepper-marker-label-typescale | sys.typescale.label-small | 500 12px/1.3333 |
--tanrei-comp-stepper-position-typescale | sys.typescale.label-small | 500 12px/1.3333 |
测试样例
作为基准的渲染结果。所有平台都以相同的 ID 为每个样例截取快照,以便相互比较。
horizontal
label: Checkout value: payment completedText: Completed errorText: Has an error items: 4 horizontal-descriptions
label: Checkout value: shipping completedText: Completed errorText: Has an error items: 4 horizontal-error
label: Checkout value: payment completedText: Completed errorText: Has an error items: 4 current-error
label: Checkout value: payment completedText: Completed errorText: Has an error items: 4 pressable-visited
label: Checkout value: payment pressable: visited completedText: Completed errorText: Has an error items: 4 pressable-disabled
label: Set-up value: profile pressable: all completedText: Completed errorText: Has an error items: 4 finished
label: Checkout completedText: Completed errorText: Has an error items: 3 long-labels
label: Application value: documents completedText: Completed errorText: Has an error items: 3 vertical
label: Set-up value: profile layout: vertical completedText: Completed errorText: Has an error items: 4 vertical-error
label: Set-up value: team layout: vertical pressable: visited completedText: Completed errorText: Has an error items: 4 compact
label: Checkout value: shipping layout: compact completedText: Completed errorText: Has an error positionText: Step {position} of {count} items: 5 compact-error
label: Checkout value: payment layout: compact completedText: Completed errorText: Has an error positionText: Step {position} of {count} items: 4 horizontal-rtl
label: الدفع value: payment completedText: مكتمل errorText: به خطأ direction: rtl items: 4 compact-rtl
label: الدفع value: shipping layout: compact completedText: مكتمل errorText: به خطأ positionText: الخطوة {position} من {count} direction: rtl items: 4 生成自 specs/stepper.spec.json, tokens/comp/stepper.tokens.json, design/tokens/
当一个人要按产品设定的顺序走完三到七步的流程,并且知道自己在哪里、还剩多少能帮助他们完成时,使用步骤条:结账、分成几部分的注册、一份申请。把它放在步骤内容的上方(水平),或者作为一列放在内容旁边(垂直)。
以下情况不要使用步骤条:
- 读者自己逐步完成的操作说明。那是步骤:一套没有位置概念的编号流程,它的标记只是普通的圆环,从不显示进度。
- 只有两步的流程,或者短到位置说明不了任何东西的流程。一个说明本页用途的标题就够了。
- 一长串可以按任意顺序完成的独立任务。那是任务列表:每个任务一行,带上它的状态,每一行都是一个链接。
- 代替流程本身的导航。请保留“返回”和“继续”按钮。步骤条说明这个人在哪里,它不是大多数人用来移动的方式。
| 状态 | 含义 | 绘制方式 |
|---|---|---|
| 当前 | 这个人所在的位置。 | 填充的标记,内有步骤编号。 |
| 已完成 | 已经完成。默认是当前步骤之前的每一步。 | 带对勾的标记。 |
| 错误 | 需要这个人处理,无论它在什么位置。 | 圆环加优先级字形,标签使用错误颜色。 |
| 未完成 | 还没到。默认是当前步骤之后的每一步。 | 圆环,内有步骤编号。 |
步骤的 status 默认为 auto:当前步骤之前为已完成,之后为未完成。当你的流程掌握更多信息时再设置它:需要修正的步骤用 error,不按顺序完成的步骤用 complete,被跳过但仍需完成的步骤用 incomplete。每种状态都靠标记的形状来区分,而不只靠颜色。
流程结束后,把 value 留空,并把每一步都标为 complete。
pressable="visited"让这个人可以按下自己去过的步骤(已完成的或有错误的),回到那里。大多数流程需要这一项。pressable="all"让他们按下除当前步骤之外的任何一步,适用于各步骤可以按任意顺序完成的流程。pressable="none"(默认)只显示位置。
和大多数系统一样,可按下的步骤在悬停或获得焦点之前,看起来与不可按下的步骤相同。如果你的用户需要更明显的提示,请在步骤条附近写明(“选择一个步骤进行修改”)。当前步骤永远不可按下。还不能前往的步骤(它取决于更早的某个选择)设为 disabled:它会以淡化的样子绘制,并被朗读为不可用。
当水平步骤条的宽度容不下每一步的最小宽度时,它会自动变为紧凑形态:一条分段的进度条,填充到当前步骤;用你的文字写出的位置(“第 2 步,共 5 步”);以及当前步骤的标签。layout="compact" 在任何宽度下都要求这种形态。紧凑形态不可按下:流程的“返回”按钮承担这项工作。layout="vertical" 则把每一步排成一列,全部保持可见。
- 用这个人要做的事或要提供的东西为步骤命名,一两个词即可:账户、配送、付款、确认。
- 从头到尾保持步骤数量不变。如果某一步只在部分情况下适用,就显示它并跳过它,而不是中途加进来。
- 把错误放在这个人能修正它的那一步上,并附上一句简短的说明,指出哪里有问题。
- 不要把步骤条作为向前推进的唯一方式。
- 不要在标签里写编号。标记会显示编号,列表会朗读它。
- 不要把步骤条用于页面的各个部分或用作标签页。那些是标签页或目录。
- 角色
list- 标注
- An ordered list named by label ("Checkout progress"). Each step is an item read as its label, then its condition in the product's words (completedText, errorText), then its description; the current step is exposed as the current step (aria-current="step"). The list gives each step its place ("3 of 4"), so the number in the marker is not read again. In the compact form the stepper is a group named by label that reads the position and the current step's label, as drawn.
- 最小目标
- 即使外观尺寸更小,点击区域也满足最小目标尺寸。 minimum 32px · pointer 44px · spacing 8px · touch 48px
键盘操作
| 按键 | 动作 |
|---|---|
| Tab / Shift+Tab | Moves focus to the next or previous pressable step, in the order of the steps; a step that cannot be pressed is not a stop. |
| Enter / Space | Presses the focused step: the stepper emits select with its value, and the product goes there. |
备注
- The stepper is an ordered list with the current item marked, as in the W3C's tutorial on forms in several steps: a step's place comes from the list, its condition from words the product gives, and the current step from aria-current="step". It is not a landmark: it belongs to the flow's content, and a page has one.
- Condition is never said by color alone (WCAG 1.4.1): the current marker is filled and holds the number, a complete marker holds a check, an error marker a different glyph, an incomplete marker is a ring; each is also said in words to assistive technology.
- Contrast in the default system: the ring of an incomplete step is outline, held to 3:1 on the surface; the current marker's primary fill is held to 3:1, and its number on-primary to 4.5:1 on it; the complete marker's secondary-container fill is not held to 3:1 by itself, and does not need to be, because its check (on-secondary-container, 4.5:1) identifies it. Labels are on-surface or on-surface-variant (4.5:1), the error label error (4.5:1). The connector and the bar are decorative.
- Pressing a step only asks to go there (select): the product decides, and moves value. A step that cannot be pressed is plain text and not a stop; a disabled step of a pressable stepper is a disabled button, so that a person hears it cannot be gone to now.
- The compact form draws no pressable step: a person goes back with the flow's own Back control. What is drawn is what is heard: the position and the current step.
- How a state looks is a value decision of the active design system; the stepper reads the state's tokens and never computes the effect itself.
- Not the steps of a procedure: a set of instructions to read, with no position, is Steps, whose markers are hairline rings with no fill, no glyph and no state.
生成自 specs/stepper.spec.json, tokens/sys/measure.tokens.json
- 步骤条是一个有序列表,以它的
label命名。每一步是一个列表项,依次读出它的标签、表示它状态的文字(completed-text、error-text),然后是它的描述。列表会给出每一步的位置(“第 3 项,共 4 项”),所以标记里的编号不会再读一遍。 - 当前步骤作为当前步骤提供给辅助技术(
aria-current="step")。 - 可按下的步骤是原生按钮,也是一个 Tab 停靠点,顺序与步骤顺序一致。Enter 和 Space 会按下它。不可按下的步骤是普通文本,不是停靠点。可按下的步骤条中被禁用的步骤是一个禁用的按钮。
- 标记、字形、连接线和进度条都对辅助技术隐藏。
- 各种状态既靠颜色也靠形状区分:填充的标记、对勾、优先级字形、圆环。
- 在紧凑形态下,步骤条是一个以其标签命名的分组,朗读的就是画出来的内容:位置和当前步骤的标签;当前步骤有错误时,还有表示错误的文字。
- 标签和描述会换行,绝不截断。文字放大时一切都随之变大,步骤条仍然根据自己得到的宽度变为紧凑形态。
- 在强制颜色模式下,当前步骤的标记采用高亮颜色,其余一切采用文本颜色。
- 缺少标签、值不对应任何步骤、步骤值为空或重复、步骤没有标签、步骤条少于两步、当前步骤被标为禁用、位置句子里没有
{position},以及缺少某项文字,都会在调试构建中报告。
- 给步骤条一个
label,说明它跟踪的是哪个流程(“结账进度”)。 - 用你的语言提供
completed-text、error-text和position-text,或者用setWords()只给一次。 - 保留流程本身的“返回”和“继续”按钮,并在步骤改变时把焦点移到新步骤的标题上,让屏幕阅读器用户听到自己在哪里。
- 当某一步有错误时,也要在该步骤的内容里显示错误本身。步骤条只说明这一步需要处理。