When designing and coding a form, it is crucial to incorporate proper HTML tags. Without them, a screen reader won’t be able to inform a non-sighted user about the contents of the webpage. The task might seem obvious, but there are few things one needs to keep in mind not to exclude some users from using the website you’re developing.
Always use labels
You should remember that each input needs a label that describes the purpose of the input field. There are two ways of connecting the label to input:
- Implicit association – the label wraps the input. In this case, you do not need to add the for attribute to the label
<input name=”username” id=”username” />
- Explicit association – they are connected through a for attribute on the label, which must be the same as the input’s id.
<input name=”username” id=”username” />
Beside the markup, which is responsible for the logic, it is also important to put elements in a specific order so that they don’t confuse the user. According to the WCAG rules, all inputs of type text, password, or select need a label before the field, while inputs of type checkbox or radio should have the label after the input.
Mark required fields
It is popular to use an asterisk to indicate a required field, by putting it after the name of the field such, e.g. “Username *”. This is, however, not the best practice, as the screen reader will read it as “Username star”. This does not provide the same information as it does for sighted users. There are several solutions to this issue:
- Explicitly say “Username (required)”.
- Add information that required fields are marked with an asterisk.
- Add the ‘required’ attribute to the input field. Personally, I do not like this solution as it affects the UI on validation and the message provided by the browser does not imply which field contains wrong input. On top of that, it is not read by the screen reader.
- Add ‘aria-required=”true”’ to the input field. This will announce to the end user that they need to fill this field out.
- Switch the legend: associate the asterisk with “optional” for more clarity and less noise. You can also add this information after a field: “Date of birth (optional)”.
Rethink your validation
Every form needs validation. It’s important to remember that non-sighted users also need to be informed about what potential errors they could have in their form. Using only color to indicate an error is not a best practice – it may also affect colorblind people. If you intend to add a red border, you should look into contrast checks – they need to be at least AA.
In my opinion, the best option is to include errors in forms, as there are a lot of types of mistakes that a user could make.
In order to inform the user about the error, an aria-invalid=”true” should be added to the input. This goes nicely with “aria-describedby” including the id of the paragraph with the error message. The paragraph should include an “alert” role so that it reads out the error’s content, interrupting the flow of the page.
If we write our project in React, the username input could then look like this:
Aria-live attributes inform screen readers about the announcement’s priority. Its default value is “off”, however we could also insert “polite” and “assertive”. The difference between the two is that “assertive” will interrupt the general flow of the reading in order to announce something, while “polite” will politely wait.
It’s also important not to overuse aria-live as it creates a lot of noise for screen reader users.
In this case, it was not used on the div as aria-live=”assertive” is implicit for role=”alert”.
However, if we have two errors that we would like to have read in a specific order, we can add role=”alert” and aria-live=”polite” to the second div, so that the screen reader reads it out after the first error.
You can get additional UX points for moving the user back to the field that has an error message on validation. This will save them time tabbing back to the specific input.
If you have a bigger form, where you could have more than two errors, it might be a good idea to create a component that lists all those errors. Including links to specific fields so that users can tab to them easily is also encouraged.
If you’d like to play around with the demo a bit, here’s the link