A compact row of steps that shows how far a multi-step flow has progressed.
Step 2 of 6, Pick up, in progress
A compact row of steps that shows how far a multi-step flow has progressed.
Step 2 of 6, Pick up, in progress
ProgressStepsCompact shows where the user is in a flow made of ordered steps — a courier delivery: go to the sender, pick up, go to the recipient, drop off. Each step is a small squircle whose look follows its status, and a thin line joins neighbouring steps — a Divider by default, or a ProgressBar that shows the progress between them. It shows at most four steps; a longer flow ends in an arrow that opens the full list.
Step 1 of 2, Go to sender, in progress
Step 1 of 3, Go to sender, in progress
Step 1 of 4, Go to sender, in progress
Step 1 of 6, Go to sender, in progress
The row has three states — Standard, Skeleton and Disabled. Each step shows its status through the Squircle's own states; MoreButton presses and takes focus.
Step 3 of 4, Go to recipient, in progress. Step 2, Pick up, cancelled
Loading
Loading
Loading
.squircle--skeleton: --skeleton-on-white fill, content hidden, the DS shimmer --pattern-shimmer.Static: four placeholders, whatever the row shows once loaded — 2, 3, 4 steps or 4 + MoreButton; it does not track the real number of StepItems. No MoreButton, no Counter. A matching shape is a custom-base job (Usage).
.progress-steps-compact--skeleton, data-ds-state="skeleton"; the phrase reads "Loading" — no aria-busy: the phrase is the live region, and a busy one holds its own announcement back.When the steps land, the host swaps the placeholders for the steps and writes the position into the same phrase — announced once.Reduced motion: the shimmer resolves to none at the token; the fill alone shows the state.
Step 2 of 6, Pick up, in progress
Step 2 of 6, Pick up, in progress
Step 2 of 6, Pick up, in progress, 60 percent
.squircle--disabled: --surface-on-white with --text-and-icon-disabled — in place of its status.The statuses are not drawn; the phrase still speaks the position.
aria-disabled="true" — it stays reachable, the press and the tap are refused; the label drops the count ("Show all steps").
.progress-steps-compact--disabled and data-ds-state="disabled". Not staged in Figma — an owner ruling.
Step 1 of 6, Go to sender, in progress
:focus-visible only. Tab to the arrow in the Animation demo to see it.
A full-width row of steps joined by connectors — Dividers by default, or ProgressBars — and, when the flow has more steps than fit, a trailing arrow with a counter.
Step 2 of 6, Pick up, in progress
chevron-right — opens the full list of steps.Steps = More only.
One size. The row fills its host — 360 in the Figma set — and the connectors take what the steps leave; at most four steps, the fifth position is MoreButton. Paddings and gaps stay fixed (SP 100 %); the squircles cap at 130 %.
Step 1 of 2, Go to sender, in progress
Step 1 of 3, Go to sender, in progress
Step 1 of 4, Go to sender, in progress
Step 1 of 6, Go to sender, in progress
flex: 1 1 0 — the free width, down to zero.At a 360 host: 92 with three steps, ≈ 45 with four, 22 with More.
.progress-steps-compact--steps-2, only with two steps.
Step 1 of 6, Go to sender, in progress
Step 1 of 6, Go to sender, in progress
Step 2 of 6, Pick up, in progress, 60 percent
Step 2 of 6, Pick up, in progress, 60 percent
.progress-steps-compact__divider. It keeps its colour in every state.ProgressBar — the second option, for a flow that shows progress between steps: .progress-steps-compact__progress with its fill. One kind per row.
transform with --transforming-state-default, zeroed under reduced motion.
Step 1 of 6, Go to sender, in progress
Step 1 of 6, Go to sender, in progress
Step 1 of 6, Go to sender, in progress
person-courier (current), info-outline, courier-walk, courier, then MoreButton "+2".
courier-walk mirrors; the other icons do not.
.squircle--text) or an icon (the bare .squircle--s, the SVG in .squircle__slot). One content type per row.The icon is a 24 glyph, var(--sp-s24), from icons/outlined/; it takes the squircle's content colour, so the status colours it like a number.
courier-walk) carries .progress-steps-compact__icon--rtl and mirrors under :dir(rtl), like the chevron.
Step 1 of 3, Go to sender, in progress
Step 1 of 6, Go to sender, in progress
Step 1 of 6, Go to sender, in progress
Step 1 of 6, Go to sender, in progress
Step 1 of 2, Go to sender, in progress
Step 1 of 2, Go to sender, in progress
width: 100%); the connectors absorb the extra.
.progress-steps-compact--align-center · .progress-steps-compact--align-end.It places the row in its host when a max width leaves room, and the two-step pair inside the row. Start and end are logical — they swap in RTL.
Step 1 of 2, Go to sender, in progress
Step 1 of 6, Go to sender, in progress
dir="rtl".
Step 1 of 8, Go to sender, in progress
:active:not([aria-disabled="true"]). Standard ↔ Disabled swaps the squircle states, no transition.
transform (scaleX from the start edge) with --transforming-state-default — Transforming/State/Default, the Progress Bar moment. Switch the live demo to ProgressBar: it fills the bar behind each passed step.The Divider is static — no motion.
Use ProgressStepsCompact where only the position in a flow matters — a screen header, a card, the top of a bottom sheet. To describe each step — names, addresses, people — use the full list, ProgressStepList. Not for a continuous value such as a download (a Progress Bar), not as a stepper the user taps through, not for a single step or a flow with no order.
Go to sender 10:15
Skyline Shopping Center
Step 1 of 6, Go to sender, in progress
Drop off
50 Nauryzbay Batyr St, Esentai Business Center
Step 4 of 4, Drop off, in progress
Go to recipient
The pickup was cancelled
Step 3 of 4, Go to recipient, in progress. Step 2, Pick up, cancelled
Step 1 of 6, Go to sender, in progress
Step 2 of 6, Order details, in progress
Step 3 of 4, Go to recipient, in progress
Loading
Loading
Step 3 of 6, Go to recipient, in progress
Step 6 of 10, Drop off, in progress
Step 5 of 6, Go to recipient, in progress
Step 2 of 6, Pick up, in progress
chevron-left — the passed steps 1–2 behind it, steps 3–6 drawn.
38824:5842). The structure stays — steps, connectors, the phrase; a team re-points what the main preset fixes, including the step count and MoreButton.An instance on the custom base carries data-ds-preset="custom".
.progress-steps-compact__step; an IconContainer step adds .progress-steps-compact__step--icon-container and takes its status colour through its own color.The status still needs a visible difference besides colour where it matters, and the phrase still speaks it.
.progress-steps-compact--progress-{style}, style = inverse · always-light · brand · accent · error · warning (Primary is the bare root). Inverse and AlwaysLight sit on dark surfaces, Brand on dark only (the colour rule).
__steps sits in .progress-steps-compact__scroller, which scrolls sideways; each connector takes --progress-steps-compact-connector-length, unset var(--sp-100-s24). No scrollbar; the host scrolls the current step into view.The scroller is focusable — tabindex="0", role="group", aria-label="Steps, scrollable" — so a keyboard can scroll the strip; its focus ring is an var(--sp-s2) outline in --text-and-icon-primary. The phrase still speaks the position.
__steps, at the row's start (example 8, chevron-left) — or be replaced by another DS component (example 9, a Button S Secondary).Whatever opens the full list stays a native button outside __steps with data-ds-action="open-all-steps" and data-ds-target="more-button", a label that says what it opens, and its own touch-target contract — a Button S is 40 and relies on the separation around it, which the row's padding gives. The phrase stays the first child, so the position is read before the button.
One element for the row, one button for the arrow. The squircles, numbers, icons, connectors and the Counter are not announced on their own.
Step 2 of 6, Pick up, in progress
clearAndSetSemantics with the contentDescription and a polite liveRegion — whether the first appearance is spoken is verified on a device. VoiceOver: children: .ignore, .staticText; a change is a queued announcement (.accessibilitySpeechQueueAnnouncement) — a plain one interrupts.Web: a .visually-hidden paragraph with role="status" stays in the DOM and only its text changes, so a change is announced once and the first render is not; the drawn steps are aria-hidden; no aria-label on the generic root.
Role.Button; VoiceOver .button, hint "Opens the full list of steps."The trap: the drawn box is 40, and growing it to 48 with minimumInteractiveComponentSize or a 48 .frame changes the layout — the row grows to 72. The touch region expands around the box instead; on the web a transparent ::before inset 4 outside it.
:focus-visible only. Order: the row, then MoreButton, in LTR and RTL — the reading order.Custom base, Scroll layout: the steps stay hidden, so .progress-steps-compact__scroller is the keyboard stop — tabindex="0", role="group", aria-label="Steps, scrollable", the arrow keys scroll it (WCAG 2.1.1); the same var(--sp-s2) ring in --text-and-icon-primary, Highlight in forced colours. The order follows the DOM: the phrase, then the scroller and MoreButton as drawn.
GrayText, the fill the text colour; a disabled MoreButton and the skeleton placeholders outline in GrayText; focus stays Highlight.
liveRegion as the Standard row — only its contentDescription changes when the steps land. VoiceOver: one element labelled "Loading"; the arrival is a queued announcement.Web: no aria-busy — the phrase is the live region, and a busy ancestor would hold its announcement back.
aria-disabled="true" keeps the native button in the tab order, but does not stop its Enter / Space click — the handler returns early while it is set.