Multi-Step Forms / Wizards
Step indicators, focus management between steps, and WCAG 2.2's Redundant Entry requirement.
Overview
Multi-step forms (wizards) break long forms into manageable chunks, but they introduce serious accessibility problems when step transitions are visual-only. A sighted user sees the step indicator change and the new fields appear, but a screen reader user may be stranded at the top of the page with no idea that the form has moved to step 2. The step indicator itself is often a set of styled spans with no semantic meaning — screen readers cannot determine which step is current. WCAG 2.2 introduced 3.3.7 Redundant Entry, which requires that information previously entered by the user must be auto-populated or available for selection if needed again in a later step.
WCAG Criteria:
- 3.3.7 Redundant Entry — information previously entered must not be requested again in a later step unless re-entry is essential
- 2.4.2 Page Titled — every document needs a title that describes its topic or purpose
- 3.3.1 Error Identification — identify input errors in text
Key requirements:
- Use an
<ol>for the step indicator so screen readers convey the sequence and total number of steps - Mark the current step with
aria-current="step"so assistive technology can identify it programmatically - On step transition, move focus to the new step’s heading (with
tabindex="-1") so screen readers announce the new context - Validate the current step’s fields before advancing — do not let the user skip to step 2 with empty required fields
- Preserve values when users move Back so they can review and edit them; this is essential usability, but is not by itself the test in 3.3.7
- When a later step asks for information already supplied in the same process, auto-populate it or make it selectable unless an exception applies (WCAG 3.3.7)
- Provide Back and Next buttons so users can navigate between steps without losing data
Wizard Steps
Silent Step Change vs. Managed Focus Wizard
Step 1: Personal Info
View inaccessible code
<!-- Step indicator with no semantic structure or current-step indication -->
<div style="display: flex; gap: 8px;">
<span>1</span>
<span>2</span>
</div>
<!-- Step 1 -->
<div id="step1">
<p>Step 1: Personal Info</p>
<label>Name</label>
<input type="text" />
<label>Email</label>
<input type="email" />
<button onclick="
document.getElementById('step1').style.display = 'none';
document.getElementById('step2').style.display = 'block';
">Next</button>
</div>
<!-- Step 2 — focus stays at page top, no Back button -->
<div id="step2" style="display: none;">
<p>Step 2: Address</p>
<label>Street</label>
<input type="text" />
<label>City</label>
<input type="text" />
<button>Submit</button>
</div>Step 1 of 2: Personal Info
View accessible code
<!-- Semantic step indicator with aria-current -->
<nav aria-label="Form progress">
<ol>
<li aria-current="step">1</li>
<li>2</li>
</ol>
</nav>
<!-- Step 1 — heading receives focus on Back navigation -->
<div id="step1">
<h3 id="heading1" tabindex="-1">Step 1 of 2: Personal Info</h3>
<label for="name">Name</label>
<input id="name" type="text" required />
<label for="email">Email</label>
<input id="email" type="email" required />
<div id="error" role="alert"></div>
<button onclick="
if (!name.value || !email.value) {
error.textContent = 'Please fill in all required fields.';
return;
}
step1.style.display = 'none';
step2.style.display = 'block';
heading2.focus();
">Next</button>
</div>
<!-- Step 2 — heading receives focus on Next navigation -->
<div id="step2" style="display: none;">
<h3 id="heading2" tabindex="-1">Step 2 of 2: Address</h3>
<label for="street">Street</label>
<input id="street" type="text" />
<label for="city">City</label>
<input id="city" type="text" />
<button onclick="
step2.style.display = 'none';
step1.style.display = 'block';
heading1.focus();
">Back</button>
<button>Submit</button>
</div>What’s wrong with the silent step change?
- The step indicator uses unsemantic
<span>elements — screen readers have no way to determine the total number of steps or which step is current - No
aria-current="step"— the visual highlight on the active step number is invisible to assistive technology - When the user clicks “Next”, focus stays wherever it was — a screen reader user has no indication that new form fields have appeared
- No validation before advancing — the user can proceed with empty required fields and encounter errors later
- No “Back” button — users cannot return to a previous step to review or correct their input
- Previously entered data may be lost when navigating between steps, forcing avoidable recovery work
Programmatic behavior to verify:
| Action | Inaccessible version | Accessible version |
|---|---|---|
| Reading step indicator | Two unlabelled numbers with no current state | A labelled navigation list whose active item exposes aria-current="step" |
| Activating Next | Focus remains on a control that has been hidden | Focus moves to the newly visible step heading |
| Activating Back | No Back control | Previous values remain available and focus moves to the restored step heading |
| Empty fields on Next | The step changes without validation | A persistent empty alert is populated with the validation message |
Exact spoken output varies by screen reader, browser, settings, and language.
Why aria-current="step" matters:
aria-current="step"is specifically designed for step indicators in wizards — it tells screen readers “this is the step the user is currently on”- Without it, a screen reader user navigating the step indicator hears a list of numbers with no way to know which is active
- The value
"step"(not"true"or"page") is the semantically correct token for multi-step processes
Why focus management is critical:
- When content changes dynamically, screen readers continue reading from the current cursor position
- If focus is not moved to the new step heading, the user may not realize the form has changed at all
tabindex="-1"on the heading allows programmatic focus without adding the heading to the natural tab order
Key Teaching Points
| Technique | Purpose | Where to apply |
|---|---|---|
<ol> for step indicator | Convey sequence and total step count | On the step indicator container |
aria-current="step" | Identify the current step programmatically | On the active <li> in the step indicator |
tabindex="-1" + .focus() | Move focus to new step on transition | On each step’s heading element |
role="alert" | Announce validation errors immediately | On the error message container |
| Validate before advancing | Prevent skipping required fields | In the Next button’s click handler |
| Preserve entered data | Let users review and correct earlier steps | Retain input values when navigating Back |
| Reuse information | Meet WCAG 3.3.7 when the same information is requested again | Pre-fill or offer previously entered addresses, contact details, or other data |
Redundant Entry is about repeated requests
Keeping a field value when the user goes Back is good state management. WCAG 3.3.7 applies when the process asks for the same information again, such as requesting a billing address and then a shipping address. Offer “Same as billing address,” pre-fill the later fields, or let the user select the prior address. Re-entry can still be required when it is essential, needed for security, or the previous information is no longer valid.
Page title and step heading are different
The visible step heading identifies the current section and is a useful focus target. WCAG 2.4.2 applies to the document’s <title>. If each step is a separate page, give each document a descriptive title such as “Address — Checkout.” If steps update one document in place, changing the document title can help orientation and history, but it is not replaced by an <h3>.