Multi-step forms
Designing multi-step forms
It’s best practice to divide a long form into a stepped series of smaller forms with validation at each step. Multi-step forms can be less daunting and easier to understand and correct than a long form. Multi-step forms can be designed across pages, or on the same page.
Follow these best practices for multi-step forms:
- Split the form up according to its logical groups of controls (e.g., contact information and questionnaire).
- Validate the current step before exposing the next.
- Label optional steps and enable users to skip them.
- Ensure keyboard focus moves smoothly from step to step, backwards and forwards. At a new step, set the focus preferably on the next relevant form element, or relevant heading or container (a heading or container would need the
tabindex="-1"
attribute to programmatically receive focus). - Avoid a time limit to fill out the form. If a limit is required, enable the user to adjust or extend the time limit.
- If the steps are across pages, repeat overall instructions on every page.
- Indicate progress in the form (“Step x of y”).
Indicating progress in a multi-step form
Each step should note the user’s progress. Approaches 1 and 2 apply only to multi-page forms.
Approach 1: Using the page title
Add a note indicating progress (e.g., “Step 2 of 4:”) to the beginning of the <title>
element, before the name of the step or any error notification. The <title>
element is the first thing declared on page load to screen reader users and so provides immediate feedback.
HTML
Code begins
<title>Step 2 of 4: Contact information – Survey X – Company Z</title>
Code ends
Approach 2: Using the main heading
Add a note indicating progress (e.g., “(Step 2 of 4)” to the end of the page’s main heading, making the information prominent and easily discoverable by visual scan.
HTML
Code begins
<h1>Contact information (Step 2 of 4)</h1>
Code ends
Approach 3: Using the HTML5 progress element
You can use a HTML5 <progress>
element to inform users of their progress, as the first element in each step.
Example begins
SurveyExample ends
HTML
Code begins
Survey <progress max="7" value="1">(Step 1 of 7)</progress>
Code ends
Some operating systems animate the <progress>
element, which violates WCAG’s Success Criterion 2.2.2 Pause, Stop, Hide. The animation can be stopped by using custom styling with browser-specific CSS as shown below:
CSS
Code begins
/* Microsoft IE */
progress {
color: #036;
}
/* Apple Safari and Google Chrome */
progress::-webkit-progress-bar {
background-color: #036;
}
/* Mozilla Firefox */
progress::-moz-progress-bar {
background-color: #036;
}
Code ends
Approach 4: Using a step-by-step indicator
A step-by-step indicator can help users orient themselves in a stepped process. This example uses an ordered list with each step being a list item. Visually hidden text, off screen but still encountered by assistive tech, indicates the current and completed status of steps (the WET class .wb-inv
visually hides the text). Previous steps are linked so users can review them. Any data already entered in the current step should be saved.
Example begins
- Completed: Billing Address
- Current: Shipping Address
- Review Order
- Payment
- Finish Purchase
Example ends
HTML
Code begins
<div class="tlwrapper">
<ol class="timeline">
<li class="timeline-past">
<span class="wb-inv">Completed: </span>
<a href="billing.html">Billing Address</a>
</li>
<li class="timeline-current">
<span class="wb-inv">Current: </span>
<span>Shipping Address</span>
</li>
<li><span>Review Order</span></li>
<li><span>Payment</span></li>
<li><span>Finish Purchase</span></li>
</ol>
</div>
Code ends
View CSS
Code begins
.wb-inv {
clip: rect(1px,1px,1px,1px);
height: 1px;
margin: 0;
overflow: hidden;
position: absolute;
width: 1px;
}
.box-content {
font-size: .7em;
overflow: auto;
padding: .5em;
}
.tlwrapper {
display: table;
width: 100%;
}
.timeline {
display: table-row;
counter-reset: timeline;
}
.timeline li.timeline-past {
background-color: #ccc;
}
.timeline li:first-child {
padding-left: 0;
}
.timeline li {
display: table-cell;
width: 20%;
counter-increment: timeline;
list-style: none;
text-align: center;
padding: .25em .5em;
overflow: hidden;
position: relative;
background-color: #fff;
padding-left: 25px;
white-space: nowrap;
}
.timeline li.timeline-current > span,
.timeline li.timeline-current a {
color: #036;
font-weight: bold;
}
.timeline li a, .timeline li > span {
z-index: 100;
position: relative;
display: block;
color: #555;
}
.timeline li.timeline-current > span:before {
color: #fff;
background-color: #036;
border-color: #036;
}
.timeline li a:before,
.timeline li > span:before {
display: inline-block;
color: #555;
content: counter(timeline);
background-color: transparent;
border: 3px solid #555;
margin-right: .25em;
border-radius: 5px;
padding: 0 .25em;
}
.timeline li:after {
left: 0;
top: 50%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
border-color: rgba(151, 204, 237, 0);
border-left-color: #ccc;
border-width: 20px;
margin-top: -20px;
}
Code ends
Alternative approach: Using aria-current="step"
This alternative replaces the visually-hidden text "Current" with an aria-current="step"
attribute applied to the parent <li>
element. Screen readers declare "current step" in addition to the list item text. User agent support for aria-current when applied to a non-focusable element like <li>
(as opposed to controls) is an open question. Visually-hidden text, on the other hand, is universally supported.
HTML
Code begins
[…]
<li class="timeline-current" aria-current="step">
<span>Shipping Address</span>
</li>
[…]
Code ends
Good example: The WET steps form
The WET steps form (example) is a same-page multi-step form. The Steps form documentation describes the input markup requirements.
The WET steps form does not yet support optional steps with a “skip” button.
You define each step within a <fieldset>
element and name it with a nested <legend>
element. You place the step's form elements in a <div>
following the <legend>
.
HTML
Code begins
<fieldset>
<legend>Contact Information</legend>
<div>(Form elements go here, hidden when inactive.)</div>
</fieldset>
<fieldset>
<legend>Questionnaire</legend>
<div>(Form elements go here, hidden when inactive.)</div>
</fieldset>
Code ends
JavaScript hides the <div>
when the step is unselected. JavaScript also adds navigation buttons “next” and “previous”, which trigger validation on click.
The HTML markup for Approaches 3 and 4 is from the Web Accessibility Initiative (WAI) document: Multi-Page Forms in the Forms Concepts (WAI) tutorial of the Web Accessibility Tutorials. Eric Eggert and Shadi Abou-Zahra. Copyright © 2019 W3C® (MIT, ERCIM, Keio). Updated 27 July 2019 (first published September 2014).
Related WCAG resources
Related WCAG resources
Success criteria
Techniques
- G133: Providing a checkbox on the first page of a multipart form that allows users to ask for longer session time limit or no session time limit
- G180: Providing the user with a means to set the time limit to 10 times the default time limit
- G198: Providing a way for the user to turn the time limit off
- H71: Providing a description for groups of form controls using fieldset and legend elements
- SCR1: Allowing the user to extend the default time limit
- SCR16: Providing a script that warns the user a time limit is about to expire