Skip to main content

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.

OverviewStyleAccessibility

Description

Anatomy

PartRequired?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

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

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

PartTokenValue
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 <ol> element to preserve ordered-list semantics.
  • Each step is an <li>. The active step's <li> should carry aria-current="step".
  • Completed steps rendered as interactive links must use an <a> or <button> element — never a non-interactive div with a click handler — to ensure keyboard and assistive technology support.
  • The connector line between steps is decorative. Render it as a CSS pseudo-element (::after on the <li>) or an aria-hidden span, not as a structural element.
  • Use a data-state attribute (upcoming | active | completed | error | disabled) on each <li> to drive CSS state styling.
  • In JavaScript-driven wizards, update aria-current="step" on the active <li> 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 <nav aria-label="Progress"> to create a landmark that assistive technology users can navigate to directly.

Keyboard interaction

KeyAction
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 <button>).

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 attributeWhen to useExample
nav (landmark) Wrap the entire stepper in <nav aria-label="Progress"> to create a navigable landmark. <nav aria-label="Progress"><ol class="progress-stepper">...</ol></nav>
aria-current="step" Apply to the <li> representing the currently active step. <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> for disabled steps. Do not use the HTML disabled attribute on non-form elements. <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.

Was this page helpful?

Updated 5 October 2026