How to build accessible forms

The problem

The purpose of each form field should be clear, both for the sake of your sighted users and for those using assistive tech. Web browsers use labels to provide a larger clickable area, making it easier to jump to the right input. And screen readers need labels to describe form controls to users.

Labels in Rails

At first glance, the example below looks OK. The purpose of each field seems to be described anyway. However, this is done with plain text rather than with labels: Product name: <%= f.text_field :product_name %>. Since we don't have any label tags that we've associated with each input, the functionality of this form is limited. Many people would struggle to tick "In stock", as the click area is very small. But with a properly associated label, they'd be able to click both the text and the box to interact with it.

Product name: Description: In stock:

Explicit labels

Rails gives us a handy label method through FormHelper. We can add a label explicitly by placing it either before or after the input tag and giving it a for attribute with the same value as the input's id. This creates a link between the two elements, helping screen readers know which input a label belongs to. If done correctly, clicking the label text should also activate the input - select, check, or focus it.

Let's fix the previous form using explicit labels. Here's the code:

Implicit labels

Alternatively, you can use an implicit label, which wraps the input. But according to W3C screen readers have better support for explicit labels, so keep that in mind.

Hiding labels

All inputs need labels. When you don't want them to be displayed on the page, you can either hide them or attach an aria-label to your inputs to make the form accessible to screen readers. Scott O'Hara talks about how to do this in his blog post Inclusively hidden.

Below I'm using the sr-only class to hide the label from sighted users.

Try it yourself

Using a mouse

Click "Product name", "Description" and "In stock" in the first form on this page. Notice that nothing happens. Now try the second form, where we added explicit labels. Notice that clicking them activates the correct input.

Using VoiceOver

Press cmd + F5 to turn VoiceOver on, then use control + option + right/left arrow to navigate to the first form. For inputs without labels, the screen reader reads "edit text, blank" and "unticked, tick box". Users won't be able to tell what these fields are for. Now navigate to the second form with explicit labels. Here the screen reader announces the purpose of the fields - "Product name, edit text" and "In stock, unticked, tick box". And for the search input with a hidden label, it reads "Type something to search, menu pop-up, edit text". When you're done you can switch VoiceOver off with cmd + F5.

Labels in simple_form

The great thing about simple_form is that it automatically generates labels for you. You can overwrite the default label with your custom text:

The only thing you need to remember is to not disable the labels. If you don't want to display the label, use CSS to hide it:

Form groups

Collections of radio or checkbox inputs need to be wrapped in a fieldset element and provided a legend. A common mistake developers make is adding lots of div wrappers to help with styling. But the legend needs to be a direct child of the fieldset element for this to work. Add any element between those two and you'll get invalid HTML. It's always a good idea to run your code through an HTML validator.

Which color do you prefer?