A11y Front-end Roadmap #8 - Forms
Simple Steps for Improving Form Accessibility
Forms are elements that can potentially reach high levels of complexity in certain types of applications. A mindful approach can save you a lot of trouble, though. Here are some key take-aways to avoid some of the major common issues. A word of advice: every time you find a label (form or input), keep in mind that it has to be unique.
1. The <form>
element
Let's start with the <form>
. First things first, consider what your form's main purpose is: if you're making a search form, specify <form role="search">
. Secondly, make sure to identify your form with an aria-labelledby
(pointing to an appropriate heading ID) or with an aria-label
.
2. Inputs and their labels
Each input field must be labeled, have a unique id
, and a unique name. Use the <label>
element, with the for
attribute pointing to the input id
. Labels must always be present, even if visually hidden, for those fields whose meaning can be inferred from the context to avoid redundancy (you have to guarantee, however, their presence and availability to assistive technologies, whether via the visually-hidden technique or via the aria-label
attribute).
Also remember to add a “:” at the end of each label (except for checkboxes and radio buttons, where you should evaluate if appropriate or not).
2.1 Implicit labels
It’s when an <input>
is contained inside a <label>
: in this case there is no need for the “for” attribute, since the relation between the two is direct and unequivocal.
3. Fieldsets
Group related controls with the <fieldset>
tag and use <legend>
as its first child to identify it: basically, a <legend>
is the specificlabel to use with <fieldset>
elements. There's no need to define any other attribute for it. Fieldsets are particularly useful when dealing with groups of checkboxes or radio buttons, but they're not limited to them.
3.1 Example
In the example you can see a <fieldset>
+ <legend>
being applied, and also two kinds of labels, both the explicit and the implicit modes.
<form>
<label for="username">Username:</label>
<input type="text" id="username" name="username" aria-required="true">
<fieldset>
<legend>Preferred Contact Method</legend>
<label>
<input type="radio" name="contact"> Email
</label>
<label>
<input type="radio" name="contact"> Phone
</label>
</fieldset>
<button type="submit">Submit</button>
<button type="reset">Reset</button>
</form>
4. Input attributes
4.1 Field types
For inputs, leverage the HTML5 type
attribute to convey more meaningful semantics and native controls (and even validation): above the most common ones, there are plenty (more than 20!) and some of them have specific attributes to allow evenmore control. For a comprehensive list, check the MDN webdocs page about all the input types (opens in a new tab).
4.2 Required Fields
They should contain the required
attribute, and their label should reinforce this by adding text specifying that the field is required (or an asterisk with an explanatory “Fields with an asterisk are required.” text at the beginning of the form, to avoid repetition).
4.3 Disabled Fields
Disabled fields cannot be operated, and some assistive technologies ignore them, removing them from the focus order. If it’s important for the user to be aware of their presence, you can use the aria-disabled
attribute instead. However, with aria-disabled
, the field could still be operable, so you have to manually disable interactions and visually suggest this state via JS and CSS (lighter color, cursor: default
, and pointer-events: none
). Disabled values are not sent on submit.
4.4 Readonly Fields
Even if not operable, readonly field values are sent on submit.
5. Placeholders
Placeholders are a nice detail to provide hints about what the input value should look like, but they’re not accessible to screen readers. Moreover, they disappear when the control has a value. That means: don’t rely on them: not for your design, nor for accessibility.
6. Instructions
If needed, you can provide detailed and specific instructions for the form as a whole. You can place this text (visible or not) before the <form>
to avoid it being potentially skipped when screen readers switch into “form mode.” It’s also possible to add control-specific instructions, and multiple options are available:
Inside the
<label>
, visible if short, or visually-hidden if long.aria-describedby
in the<input>
element, pointing to the instructions container ID, which may or may not be visually hidden.aria-labelledby
in the<input>
element, pointing to the<label>
ID first and the instructions container ID, which may or may not be visually hidden (thearia-labelledby
attribute can assume the value of a list of IDs, separated by a space).
7. Validation
Client-side validation is undoubtedly a “must” nowadays. We will not discuss here how to validate inputs but we will focus on just one of the different ways to display error messages. Among the various validation error displaying patterns, I’ve decided to pick the one that I find most common: displaying an error message next to the input field. We will discuss more on the different patterns in another article.
Set aside the fact that, by accessible design, we cannot rely only on color changes or the appearance of just an icon. We know that we should clearly display a short but clear message about the cause of the input problem. This message should be placed near the input field to logically connect the two elements. Other design elements indicating that there is an error are obviously fundamental (don’t rely only on color, they say... but don’t rely only on text either, I would suggest). Since this is out of scope for this article, just mind that you will also have to manage your styles as well.