Form part 1 - Structure your form
Associated themes:- Web
- Intermediate
- Component
Publication date
Update of the article from
Introduction #
For a form to be accessible to all users, a few rules must be respected during development.
In this first part, we will show how to build your form. In the second part, we will cover form submission and validation.
We will build a registration form to demonstrate the accessibility considerations.
In this example, we used Boosted library. This allows you to obtain forms whose design complies with the Orange charter.
Label form fields #
Labels should describe the role of each field on the form. In order for all users to have access to the same information, it is important that these labels are correctly associated with their form fields. To achieve this, there are several techniques.
The label element #
The preferred solution is to use the label element. It is best supported by assistive technologies. There are two ways to use this label element:
- explicitly link a
labelto a field by using the for attribute that matches the field'sid(best solution), - implicitly wrap the field with the
labelelement.
Example #
Example of explicitly linked form labels:
Sample code:
<form id="formulaire" class="border border-secondary p-3 my-2">
<div class="mb-2">
<label for="email" class="form-label">Email</label>
<input type="text" class="form-control" id="email"/>
</div>
<div class="mb-2">
<label for="name" class="form-label">Last Name</label>
<input type="text" class="form-control" id="name"/>
</div>
<div class="mb-2">
<label for="firstname" class="form-label">First Name</label>
<input type="text" class="form-control" id="firstname"/>
</div>
</form>
Hide labels in an accessible way #
In some cases, it may be useful to hide the label visually. Note: labels can be hidden only if the field function is sufficiently clear and understandable in its context: for example, a search field next to a magnifying glass icon.
Even though the label is visually hidden, it should still be accessible to assistive technologies.
This method uses a CSS class (for example, the visually-hidden class from Bootstrap/Boosted) to hide content accessibly. Using accessible hiding keeps the element visually hidden while remaining available to assistive technologies (e.g. spoken by screen readers). Avoid using display:none or visibility:hidden, because these also hide content from screen readers.
See the example at accessible masking example for more information.
Example #
For example, we can use accessible hiding for a search field, if a button with the search label, or a magnifying glass image, is next to the field. Thus, the field label is visually hidden to avoid redundancy.
Sample code:
<label for="search" class="visually-hidden">Search:</label>
<input type="text" name="search" id="search">
<button type="submit">Search</button>
ARIA attributes #
It is also possible to use the aria-label and aria-labelledby attributes to label form fields. Support is generally good but varies by assistive technology and browser version, so test with your target assistive technologies (NVDA, JAWS, VoiceOver, TalkBack, etc.) and browsers.
- The
aria-labelledbyattribute is used to specify theidof an element present in the code that will be used to label the field. - The
aria-labelattribute allows you to directly specify a label in the form of a character string. Please note that the information will not be given visually.
A possible example:
<h2 id="id-search">Rechercher</h2>
<input type="text" name="search" aria-labelledby="id-search">
<button type="submit" class="icon-search" aria-label="Search"></button>
The title attribute #
The title attribute can provide a tooltip but is not a reliable substitute for a label. It will also trigger the display of a tooltip when hovering over the element with the mouse, which can help users with cognitive impairments and novice users. In addition, title is often not exposed to keyboard-only users and not consistently read by screen readers.
Never use placeholder as the only label; placeholders are not a replacement for labels and are not reliably read or persistent:
- the
placeholdertext that displays in the field is usually not high enough contrast; - it is erased when entering the content in the control (causing difficulties for users with cognitive impairments);
- the
placeholderis not always read by assistive technology; - it makes corrections difficult in case of error if there is no label displayed;
On the other hand, the placeholder can serve as a guide, an aid to fill in the field without this information being absolutely necessary (for example, proposing a valid expected value). Use placeholders for such purposes only (as examples), not as labels.
Associating related controls #
When necessary, group fields of the same type. This makes the form easier to understand.
Most of the time, we group our radio buttons, or our checkboxes, in order to associate a header with these elements.
To group them, we use the fieldset element, which will have as its first child the legend element that will serve as the header for our grouped fields.
Example #
In our registration form, we can add the gender of our user. For this, we will implement radio buttons, and group them with the header Returning customer?
Sample code:
<div class="col-md-8">
<form id="formulaire3" class="border border-secondary p-3 my-2">
[...]
<fieldset>
<legend>Returning customer?</legend>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="inlineRadioOptions" id="yes" value="yes">
<label class="form-check-label" for="yes">Yes</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="inlineRadioOptions" id="no" value="no">
<label class="form-check-label" for="no">No</label>
</div>
</fieldset>
</form>
</div>
Specify the expected type or format #
To help the user, it is also important to specify the expected type or format when necessary. For example, indicate the expected input format for a date of birth when needed (e.g. dd/mm/yyyy).
To inform the user, one can:
- provide instructions in the label
- use the
aria-labelledbyoraria-describedbyattribute
Example #
For our registration form, we are going to add a password field, specifying the format that we want.
When adding a password field, it is also important to allow the possibility of displaying or hiding the password. This helps users with motor, attention, or cognitive impairments avoid input errors.
Sample code:
<div class="col-md-8">
<form id="formulaire4" class="border border-secondary p-3 my-2">
<label for="password" class="form-label">Password</label>
<div class="mb-2 input-group">
<input type="password" class="form-control" id="password" aria-describedby="passwordHelpBlock"/>
<span class="input-group-text">
<button type="button" class="btn btn-icon btn-no-outline btn-sm" id="password_visibility" title="Show password" >
<svg aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
[...]
</svg>
</button>
</span>
</div>
<p id="passwordHelpBlock" class="form-text mb-0">
Your password must contain at least 6 characters.
</p>
</form>
</div>
Complete example #
The complete example with all the elements that we have reviewed. In the second part, we will see how to validate the form and manage error messages.
For the rest of the exercise and to complete our registration form, we have added fields for the address (address, city, zip code).
The final code:
<div class="col-md-8">
<form id="formulaire_final" class="border border-secondary p-3 my-2">
<div class="mb-2">
<label for="email_final" class="form-label">Email</label>
<input type="text" class="form-control" id="email_final"/>
</div>
<label for="password_final" class="form-label">Password</label>
<div class="mb-2 input-group">
<input type="password" class="form-control" id="password_final" aria-describedby="passwordHelpBlock_final"/>
<span class="input-group-text">
<button type="button" class="btn btn-icon btn-no-outline btn-sm" id="password_visibility_final" title="Show password" >
<svg aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 1000 1000"></svg>
</button>
</span>
</div>
<p id="passwordHelpBlock_final" class="form-text mb-0">
Your password must contain at least 6 characters.
</p>
<div class="mb-2">
<label for="name_final" class="form-label">Last Name</label>
<input type="text" class="form-control" id="name_final"/>
</div>
<div class="mb-2">
<label for="firstname_final" class="form-label">First Name</label>
<input type="text" class="form-control" id="firstname_final"/>
</div>
<fieldset>
<legend>Returning customer?</legend>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="inlineRadioOptions_final" id="yes_final" value="yes">
<label class="form-check-label" for="yes_final">Yes</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="inlineRadioOptions_final" id="no_final" value="no">
<label class="form-check-label" for="no_final">No</label>
</div>
</fieldset>
<div class="mb-2">
<label for="adresse_final" class="form-label">Address</label>
<input type="text" class="form-control" id="adresse_final"/>
</div>
<div class="mb-2">
<label for="adresse2_final" class="form-label">Additional address</label>
<input type="text" class="form-control" id="adresse2_final"/>
</div>
<div class="mb-2">
<label for="ville_final" class="form-label">City</label>
<input type="text" class="form-control" id="ville_final"/>
</div>
<div class="mb-2">
<label for="cp_final" class="form-label">Zip Code</label>
<input type="text" class="form-control" id="cp_final"/>
</div>
</form>
</div>