A row of numbered circles whose ring arcs indicate progress through a multi-step flow.
Restructure — collapse 9 sibling components into one <code>Stepper - Circular</code> with <code>steps</code> and <code>current</code> properties
Step count is a scalar — not a component axis. The current schema has 9 top-level components (2..10 steps) and inside each, 10 symbols of pre-rendered ring arcs. Every step count ships ~N PNGs. Rebuild as a single component: steps: Int (range 2–10) and current: Int (1..steps). Redraw the ring as a stroked SVG arc derived from current / steps so the fill scales with math, not image swaps. Native side maps to a custom EBStepperCircular(current:total:) rendered over an HStack / Row of Circle shapes with .trim(from:to:) or drawArc.
In Context
Stepper - Circular appears at the top of multi-step flows (onboarding, KYC, form wizards) to show position and total count. Consumers pair it with a screen title and step-specific content.
Live Preview
1
2
3
4
Content (proposed)
steps
current
value2 of 4
DS Health
Reusable
Partial
Visually fits any multi-step flow, but the 9-sibling split forces consumers to swap components when step count changes, not just a prop.
Self-contained
Warn
Each step circle's ring arc renders via raster <img>. ~10 PNGs per sibling × 9 siblings ≈ 50+ assets for what should be one SVG arc with two parameters.
Consistent
Fail
Step count modelled as 9 top-level components. Every other scalar in the DS (Badge counter, Progress Bar target) is a property, not a component family. Breaks the naming hierarchy — "Stepper - Circular" is not a component, it's nine.
Composable
Partial
Fixed 45-px circles at 20-px gaps fit most screen widths for 2–6 steps; at 7+ steps the total width exceeds a typical mobile canvas (410+ px). No responsive shrink or wrap behavior.
Behavior
State
iOS
Android
Figma Property
Notes
Completed step
Yes
Yes
ring = full
Step index < current. Full ring fill in brand blue, number in brand blue.
Current step
Yes
Yes
ring = partial arc
Step index = current. Arc fills current / steps of the ring; number in brand blue.
Upcoming step
Yes
Yes
ring = track only
Step index > current. Track-only ring in light blue; number still in brand blue.
Clickable / interactive
N/A
N/A
Not modeled
Some wizards let the user tap a completed step to go back. No pressed / focused state exists today; add if interactive behavior is desired.
Vertical orientation
N/A
N/A
Not modeled
Horizontal only. Vertical steppers (common on narrow screens or list layouts) would need a second orientation mode or a sibling.
Open Issues
Step count is modeled as 9 sibling components instead of a steps prop. Today the family ships as Stepper - Circular - 2 Steps, … - 3 Steps, … … - 10 Steps — 9 top-level components that differ only by hardcoded count. Every other design system exposes one Stepper with steps: Int (or total: Int + current: Int). 9× maintenance, 9× Code Connect mapping, and no path to N=11+ without adding a 10th file. C1 · Layer Structure & Naming
Step ring arcs are pre-rendered raster <img> assets, one per step index. Each number=N symbol's ring is a baked PNG — the progressive fill is achieved by image swap, not by math. Blocks theming (can't retint), breaks at high DPI, and ships dozens of assets the native renderer doesn't need. Should be a stroked SVG arc with strokeDasharray (or Circle().trim(from:to:) on iOS, drawArc on Compose). C6 · Asset & Icon Quality
Variant axis number encodes position, not a property. The inner symbol uses number = 1 | 2 | … | N which both labels the circle (the displayed digit) and implicitly sets the ring-arc raster. Two concerns collapsed into one enum. Should be: index: Int (the number shown) and status: completed | current | upcoming (the visual). C2 · Variant & Property Naming
No native primitive is a 1:1 match — this needs a custom component. SwiftUI has no built-in circular stepper; Material 3 shows only a linear Stepper. Both platforms require a custom EBStepperCircular built from a Row/HStack of Circle/Box shapes with arc-drawn progress. Today's raster baking makes this worse — the dev can't reuse the asset. C4 · Native Mappability
No pressed / focused / disabled state modeled. Wizards often allow tapping a completed step to return to it. With no interaction states in Figma, the developer has to invent hover / pressed treatment and the designer has no reference. C5 · Interaction State Coverage
Code Connect mappings not registered. Blocked until the family collapses to one component and the raster arcs are replaced with vector strokes. Mapping 9 separate siblings would codify the anti-pattern into the tooling. C7 · Code Connect Linkability
Design Recommendations
Collapse all 9 siblings into one Stepper - Circular with steps and current properties. Delete Stepper - Circular - 2 Steps through … - 10 Steps as separate components. Create one Stepper - Circular with steps: 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 (enum) and current: 1 | 2 | … | 10 — or, if Figma supports integer ranges, a numeric property. Variant math drops from 9 top-level components × ~10 step symbols = ~90 variant assets to 1 component with a runtime-computed arc. Native API: EBStepperCircular(current: Int, total: Int). Family
Split the inner symbol's number axis into index (digit shown) and status (ring treatment). Today one enum does both. Separate them: index: Int for the label text and status: completed | current | upcoming for the ring fill. The outer component then computes status per slot from current. Property
Replace raster ring arcs with stroked SVG / vector arcs. Each step circle's ring should be a 2-px stroked circle with stroke-dasharray (or two half-circles via trim). Colors bound to main/stepper/color/bg-track (unfilled) and main/stepper/color/bg (filled). Same visual output, theme-able, resolution-independent, no PNG assets. Asset
Add a completed visual state with a checkmark icon option. Many wizards replace the digit with a check once a step is done. Today there's no variant for this — everything shows a number. Add showCheckOnComplete: Bool (or model it into the status enum as completed-check). State
Add an orientation property for vertical layouts. Long flows (8+ steps) don't fit a phone-width row. Vertical stacks are common for checkout or document review wizards. Add orientation: horizontal | vertical and spec the connector line between circles for both axes. Property
Spec a connector line between circles. Today the 9 sibling frames space circles with a 20-px gap but no visible connector. Most steppers draw a line from the trailing edge of one circle to the leading edge of the next, tinted to match completed (brand) vs upcoming (track) states. Add this to the spec — it's the difference between "a row of circles" and "a stepper". Property
Rename layers step-container → Ring (and the ring arc layer → Arc). "step-container" is a technical label; "Ring" is what a designer or developer searches for. Rename
Build as a custom native component, not a ProgressView wrapper. Neither SwiftUI ProgressView nor Material LinearProgressIndicator visually match this pattern. Ship a dedicated EBStepperCircular: iOS uses an HStack of ZStack { Circle().stroke(track); Circle().trim(from:0,to:progress).stroke(fill); Text(index) }; Android uses a Row of Box(Modifier.size(45.dp)) with Canvas drawing drawArc(sweepAngle = progress * 360f). Composition
Announce "Step X of Y" to screen readers. The component is decorative by default — assistive tech reads only the number. Wrap in a semantic container that announces "Step \(current) of \(total)" (SwiftUI .accessibilityLabel, Compose Modifier.semantics { contentDescription = … }). A11y
Document the canonical composition and retire the sibling names. Update the sticker sheet page to show one Stepper - Circular with property controls; add a migration note pointing Stepper - Circular - N Steps consumers to the new steps prop. Docs
Styles
Stepper - Circular
DESDEV
Row of N 45×45 numbered circles, each with a ring that indicates position through the flow. 9 hardcoded sibling frames today; target is one component with a <code>steps</code> prop.
Circular ring progress indicator with a numeric step label inside.
Role
Token
Default
Active label
stepper/color/label
#005CE5
Active arc
stepper/color/bg
#005CE5
Inactive arc
stepper/color/bg-track
#D2E5FF
Property Mapping
Figma Property
SwiftUI
Compose
Stepper - Circular - N Steps (×9 siblings)
Stepper - Circular (single component)
EBStepperCircular
(implicit in sibling name)
steps: 2…10
total: Int
(implicit in number variant)
current: 1…steps
current: Int
number = 1 | … | N (inner symbol)
index: Int + status: completed | current | upcoming
(derived internally from current)
(horizontal only)
orientation: horizontal | vertical
orientation: Axis = .horizontal
(raster arc)
Stroked SVG / Canvas arc
Circle().trim(from: 0, to: progress).stroke(...)
Accessibility
Requirement
iOS
Android
Progress role
Wrap the row in .accessibilityElement(children: .ignore) and expose a single label. Set .accessibilityValue("Step \(current) of \(total)").
Treat the row as a single semantic node via Modifier.semantics(mergeDescendants = true) with contentDescription = "Step $current of $total".
Value announcement
VoiceOver reads "Step 2 of 4". Avoid announcing each circle individually — it's noisy and position-less.
TalkBack reads the merged label. Update stateDescription when current changes to trigger re-announcement.
Icon-only digits
The digit itself is already semantic. Ensure contrast: #005CE5 on white = 5.3:1 ✓
Same. Digit color passes WCAG AA for non-text graphics.
Interactive steps
If completed steps are tappable, wrap each in a Button with .accessibilityHint("Tap to return to step \(index)"). Pressed state inherits from the button.
Use Modifier.clickable(onClickLabel = "Return to step $index"). Ripple indication appears natively.
Contrast
Ring fill #005CE5 on track #D2E5FF = 3.1:1 — passes 3:1 for non-text graphics (WCAG 1.4.11). OK.
Same ratio.
Criteria Scorecard
ID
Criterion
Status
Notes
C1
Layer Structure & Naming
Requires Rework
Step count is modeled as 9 sibling components instead of a property. Collapse to a single Stepper - Circular. Also rename step-container → Ring.
C2
Variant & Property Naming
Requires Rework
Inner number axis conflates digit label and ring-arc rendering. Split into index + status (and lift current / steps to the top-level component).
C3
Token Coverage
Ready
All colors bound to main/stepper/color/{bg, bg-track, label}. Typography bound to Primary/Headlines/Block. Gap bound to space/space-20.
C4
Native Mappability
Requires Rework
No native primitive matches. Requires a custom EBStepperCircular. Today's raster arcs block direct primitive composition.
C5
Interaction State Coverage
Requires Rework
No pressed / focused / disabled / tappable-completed state modeled. Missing vertical orientation and check-on-complete variants.
C6
Asset & Icon Quality
Requires Rework
Ring arcs are raster <img> — one PNG per step index per sibling component. Replace with stroked SVG / Canvas arcs.
C7
Code Connect Linkability
Not Mapped
Blocked until 9 siblings collapse into one component and raster arcs are replaced with vector strokes.
Variants Inventory (0 total)
Today: 9 sibling components, one per hardcoded step count. Each sibling contains N step symbols (number = 1 … N) with baked raster ring arcs. Sum of step symbols: 2+3+4+5+6+7+8+9+10 = 54 step symbols (+ 9 outer frames = ~63 nodes). Target: 1 component with steps: 2…10 and current: 1…steps as runtime properties; ring arcs drawn by math, not asset.
Verdict: Restructure — Collapse 9 sibling components (Stepper - Circular - 2…10 Steps) into one Stepper - Circular with steps: Int and current: Int properties. Replace raster ring arcs with stroked SVG / Canvas. Open
Schema
C1 — Family structure — 9 top-level components differ only by hardcoded step count. Collapse into a single component with a steps property. Rename step-container layer → Ring. Open
C1
C2 — Property shape — Inner number = 1…N conflates digit label and arc-fill rendering. Split into index (label) + status (ring treatment). Lift current and steps to the parent component. Open
C2
C4 — Native mapping — No native primitive matches. Requires custom EBStepperCircular on both platforms built over HStack/Row of stroked circles. Open
C4
C5 — Missing states — Add pressed / focused for interactive steps, vertical orientation, and check-on-complete option. Open
C5
C6 — Raster arcs — Each step's ring is a pre-baked PNG. Replace with stroked SVG circles bound to main/stepper/color/bg-track and main/stepper/color/bg. Open
C6
C7 — Code Connect — Mappings pending restructure. Mapping 9 separate siblings would codify the anti-pattern. Open