Progress Stepper
Displays a user's current position within a multi-step process. Use in wizards or guided workflows to communicate progress. Don't use for processes with more than 7 steps.
FigmaDescription
Renders a horizontal or vertical sequence of labelled steps, highlighting the active step and visually distinguishing completed steps from upcoming ones.
Multi-step forms and wizards — for example, the stakeholder onboarding wizard in Open Point, or the consultation submission flow in Social Point.
Government workflows often span multiple screens of structured data entry. A persistent progress indicator reduces abandonment by showing users how much work remains and confirming what they have already completed.
Anatomy
| Part | Required? | Notes |
|---|---|---|
| Step indicator (circle) | Required | Numbered circle that marks each step. Shows the step number when upcoming, a check icon when complete, and a filled active style on the current step. |
| Step connector (line) | Required | Horizontal or vertical line linking adjacent step indicators. Uses --op-color-border-default for upcoming segments and --op-color-interactive-default for completed segments. |
| Step label | Recommended | Short text directly beneath or beside the step indicator. Describes the step's purpose (e.g. 'Contact details', 'Review'). Omit only when space is critically constrained and step numbers alone are sufficient. |
| Step sub-label | Optional | Secondary line of text below the step label. Use for brief clarification (e.g. 'Required fields only'). Keep to one line. |
| Step error indicator | Optional | Replaces the step circle icon with an error icon and applies --op-color-status-error when a completed step contains a validation error that requires the user to return to it. |
Variants
Horizontal
Steps flow left-to-right across the page
Default layout. Use when there are 2–5 steps and sufficient horizontal space (desktop and tablet viewports).
Vertical
Steps stack top-to-bottom
Use on narrow viewports, in side panels, or when step labels are long. Also appropriate when there are 6–7 steps and the horizontal layout would become crowded.
Compact (no labels)
Only numbered circles and connecting lines with no text labels
Use sparingly when space is extremely limited and the surrounding UI provides sufficient context. Always include a descriptive aria-label on each step indicator.
States
| State | Behaviour |
|---|---|
| Upcoming | Step circle displays the step number. Circle border uses --op-color-border-default; number text uses --op-color-text-secondary. Connector line uses --op-color-border-default. |
| Active | Step circle is filled with --op-color-interactive-default; number text uses --op-color-text-on-interactive. Label text weight increases to semibold. Connector line to the left/above uses --op-color-interactive-default. |
| Completed | Step circle filled with --op-color-interactive-default; displays a checkmark icon instead of a number. Connector lines on both sides use --op-color-interactive-default. Label text uses --op-color-text-secondary. |
| Error | Replaces the checkmark with an error icon. Circle border and icon use --op-color-status-error. Label text uses --op-color-status-error. Indicates the user must revisit this step before proceeding. |
| Disabled | Applies when a step cannot be accessed until a preceding step is completed. Circle border and label use --op-color-text-disabled. The step must not be keyboard-focusable in this state. |
| Clickable (completed step) | Completed steps may be made interactive to allow non-linear navigation. Apply a hover underline to the label and a pointer cursor. Focus ring: 3px solid --op-color-interactive-focus, offset 2px. |
Usage guidelines
When to use
- Multi-step form wizards where each step is a distinct page or panel (e.g. creating a new stakeholder record in Open Point across Contact, Organisation, and Tags steps).
- Guided submission flows where users must complete steps sequentially before submitting (e.g. a public consultation response in Social Point).
- Onboarding sequences where orientation through a fixed set of screens is required.
When not to use
- Processes with more than 7 steps — consider splitting into sub-workflows or using a different navigation pattern such as a sidebar task list.
- Simple single-screen forms — a progress stepper adds visual noise without benefit. Use a standard form layout instead.
- Non-linear processes where the user may complete steps in any order — use a checklist or task list component instead.
- Progress through a continuous value (e.g. a file upload percentage) — use a progress bar instead.
Do / Don't
Do
Keep step labels concise — two to three words per step (e.g. 'Your details', 'Preferences', 'Review').
Don't
Don't write sentence-length labels. Long labels break horizontal layouts and are harder to scan.
Do
Allow completed steps to be clicked so users can review or correct earlier input without losing later progress.
Don't
Don't reset all subsequent steps when a user edits an earlier one unless the data genuinely depends on it.
Do
Persist the stepper at the top of each step screen so users always know where they are.
Don't
Don't hide or remove the stepper between steps — users need continuous orientation in government forms.
Do
Validate each step before advancing and surface errors on the relevant step indicator if the user returns.
Don't
Don't surface all validation errors only on the final review step — this forces unnecessary back-navigation.
Layout & Spacing
Horizontal layout:
- Stepper container: padding-block --op-space-16 (16px)
- Step indicator circle: 32x32px; minimum touch target wrapper 44x44px via padding
- Gap between indicator and label: --op-space-8 (8px)
- Connector line height: 2px; horizontally centred between circles
- Minimum gap between step columns: --op-space-24 (24px)
Vertical layout:
- Step indicator circle: 32x32px
- Connector line width: 2px; vertically centred between circles
- Minimum gap between indicator centres: --op-space-40 (40px)
- Label left-offset from indicator: --op-space-12 (12px)
Tokens
| Part | Token | Value |
|---|---|---|
| Step indicator — upcoming (border) | --op-color-border-default | 1px border on the circle in upcoming state |
| Step indicator — active/completed (fill) | --op-color-interactive-default | Green-400; filled circle for active and completed states |
| Step indicator — error (border and icon) | --op-color-status-error | Replaces interactive colour in error state |
| Step indicator — disabled | --op-color-text-disabled | Applied to circle border and label text when step is inaccessible |
| Step connector — completed segment | --op-color-interactive-default | Matches the completed indicator fill |
| Step connector — upcoming segment | --op-color-border-default | Neutral line for steps not yet reached |
| Step label — active | --op-color-text-primary | Semibold weight; --op-text-sm |
| Step label — upcoming / completed | --op-color-text-secondary | Regular weight; --op-text-sm |
| Step label — error | --op-color-status-error | Applied to label text in error state |
| Focus ring | --op-color-interactive-focus | 3px solid outline; offset 2px; applied to clickable completed steps |
| Check icon (completed) and step number (active) | --op-color-text-on-interactive | White icon/number on filled green circle |
Engineering notes
- The progress stepper is not currently available as a wa-* Web Awesome component. Implement as a custom HTML/CSS component using an
- element to preserve ordered-list semantics.
- Each step is an
- . The active step's
- should carry aria-current="step".
- Completed steps rendered as interactive links must use an or
- The connector line between steps is decorative. Render it as a CSS pseudo-element (::after on the
- ) or an aria-hidden span, not as a structural element.
- Use a data-state attribute (upcoming | active | completed | error | disabled) on each
- to drive CSS state styling.
- In JavaScript-driven wizards, update aria-current="step" on the active
- and remove it from all others when the step changes. Announce step changes using a visually hidden live region (aria-live="polite") placed outside the stepper.
- Wrap the stepper in
Keyboard interaction
| Key | Action |
|---|---|
| Tab | Moves focus to the next clickable (completed) step indicator or label. Upcoming and disabled steps are not focusable. |
| Shift + Tab | Moves focus to the previous clickable step. |
| Enter | Activates a focused completed step, navigating the user back to that step. |
| Space | Activates a focused completed step (same as Enter when the interactive element is a |
Why it matters
Government forms frequently require citizens or staff to enter sensitive information across multiple screens. Clear, accessible progress indication reduces errors and frustration — particularly for users relying on screen readers, keyboard navigation, or high-contrast displays. Agencies using Open Point and Social Point must meet WCAG 2.1 AA as a baseline requirement.
Focus
Focus is managed by the surrounding wizard controller, not the stepper itself. When a step change occurs (e.g. the user clicks Next), focus should be moved programmatically to the heading or first interactive element of the new step panel — not to the stepper component. Within the stepper, only completed interactive steps receive focus via Tab. The focus ring must be 3px solid --op-color-interactive-focus with a 2px offset, always visible. Do not suppress the focus ring with outline: none.
ARIA
| Role or attribute | When to use | Example |
|---|---|---|
nav (landmark) | Wrap the entire stepper in | <nav aria-label="Progress"><ol class="progress-stepper">...</ol></nav> |
aria-current="step" | Apply to the | <li data-state="active" aria-current="step"> |
aria-label (on step link/button) | Provide a descriptive label for each interactive step that includes the step number, name, and state so screen reader users understand what they are activating. | <a href="#step-1" aria-label="Step 1 of 4: Contact details (completed)">...</a> |
aria-disabled="true" | Apply to the | <li data-state="disabled" aria-disabled="true"> |
aria-live="polite" | Place a visually hidden live region outside the stepper. Update its text content when the active step changes so screen reader users hear a step-change announcement. | <p class="sr-only" aria-live="polite" aria-atomic="true">Step 2 of 4: Organisation details</p> |
aria-hidden="true" | Apply to decorative icons (check marks, connector lines) so screen readers do not announce them. | <svg aria-hidden="true" focusable="false">{/* checkmark */}</svg> |
Contrast
Active/completed step circle (--op-color-interactive-default, green-400) with white icon/number: Must meet 3:1 contrast ratio for UI components (WCAG 1.4.11 Non-text Contrast). Verify the resolved green-400 value against white before implementation.
Step label text (--op-color-text-primary on --op-color-bg-primary): Must meet 4.5:1 for normal text (WCAG 1.4.3 Contrast Minimum).
Error state (--op-color-status-error on --op-color-bg-primary): Must meet 4.5:1 for the error label text and 3:1 for the error icon/circle border.
Disabled state (--op-color-text-disabled): Exempt from contrast requirements per WCAG 1.4.3 exception for disabled components. However, test with Windows High Contrast / forced-colours mode to ensure disabled steps remain distinguishable from interactive ones.
Touch targets
Each clickable step indicator must meet the 44x44px minimum touch target. Achieve this by applying padding to the indicator's interactive wrapper element rather than enlarging the visible 32px circle. This preserves the visual design while meeting the touch target requirement.
Things to avoid
- Do not use colour alone to communicate step state — always pair with an icon or label change.
- Do not make upcoming or disabled steps focusable — this creates keyboard navigation dead ends.
- Do not move focus to the stepper when a step changes — move focus to the new step's heading or first interactive element instead.
- Do not omit aria-current="step" — screen reader users rely on it to identify the active step.
- Do not use div or span elements for interactive completed steps — use a or button so keyboard and assistive technology support is native.