Create simple, accessible web forms

Oliver Lindberg
net magazine
Published in
9 min readApr 6, 2016



as he explores web standards solutions that will help you create forms that work perfectly for any user

HTML forms are probably among the most complex interfaces a frontend web designer has to create. They involve a bunch of different elements, labels, messaging, validation and errors – it’s a lot to wrap your head around. From a user experience perspective, they are similarly challenging: a poorly designed form can be incredibly frustrating to complete. And some are so poorly thought-out that they actually prohibit you from being able to use them at all.

It doesn’t have to be this way. Web standards provide numerous mechanisms for creating incredibly usable forms simply by being smart about how we write our HTML. Additionally, as HTML is made to be both backward- and forward-compatible, using these tools correctly aligns our efforts with the philosophies of progressive enhancement and universal design.

Start with clear prose

The number one problem most people have with forms is poor messaging. Instructions aren’t clear. Form labels are vague. There is no indication of what is required. These all present challenges to your users. So when you start thinking about a form, think about how it reads.

Start with an introduction. What does a user need to know about this form to get started? Are all of the fields required? How long might it take to fill
out? Will they need to consult any supplementary documents, like their driving license, passport or a utility bill?

Put yourself in your users’ shoes and ask yourself what is crucial to know at the outset, then author your content accordingly. Don’t be pedantic or
patronising. Get to the point and get out of the way. There are plenty of statistics that show a majority of users don’t read instructions because they’re long and unfocused. Keeping it brief can increase the likelihood that content will be read.

Of course, given that most users don’t read instructions, it is doubly important that the labels you use, supplementary instructions you provide,
and overall form organisation be as clear and concise as possible. For example, ‘Your Name’ is a little more explicit (and human-sounding) than simply ‘Name’. If you want to strike a more conversational tone, you could even ask ‘What’s your name?’

The same goes for explanatory text. Keep it clear and concise. On Web Standards Sherpa’s contact form, its email field is joined by text about how the information will be used: “We will only use your email address to respond to your message.” Super helpful (and reassuring) stuff! Text is the foundation of every great user experience on the web. Start there.

Helpful info Web Standards Sherpa’s contact form includes crystal clear
advisory information about how your email address will be used

Always use a label

Labels serve a critical purpose in web forms: they tell someone what you are expecting from them. As I mentioned, the text you use to label a given form control is incredibly important. But once you have that clear label, it’s equally important that you associate it properly with the form control. That’s
where the ‘label’ element comes in.

The ‘label’ element can be associated with a form control in one of two ways: implicitly and explicitly. Implicit association involves wrapping the ‘label’ element around the form control. I like to use that approach with radio and checkbox controls:

<label><input type=”radio” name=”confirm” value=”yes”>

Here the label “Yes” is associated with the radio button implicitly. Explicit association involves using the ‘for’ attribute, which is an ‘id’ reference to the
corresponding form control:

<label for=”name”>Your Name</label>
<input id=”name” …>

The reason I like this approach is that it makes it easy to style radio and checkbox controls independently of other input types, without having
to resort to attribute selection (which doesn’t work in some older browsers):

input { /* styles for normal inputs */ }
label input { /* styles for radios & checkboxes */ }

When you use the ‘label’ element, you get two magical UX improvements for free:

  1. The ‘label’ becomes an interactive element that lets you focus or activate the associated form control
  2. The ‘label’ text will be read out to screen reader users when the field receives focus

Both are hugely beneficial, but the former is especially helpful for increasing the size of the hit target for checkboxes and radio controls for touchbased

Use placeholder text

Sometimes it can be useful to provide users with a hint about the information you are requesting in the field. Most often this takes the form of an example you want to show before the user has entered any content, but that you want to go away when they start typing.

In the past we often resorted to using JavaScript to insert and remove values for the field to accomplish this. However, the HTML5 ‘placeholder’ attribute
provides that functionality natively. To use it, simply supply a value and it will appear in the field when it has no value:

<input placeholder=”Johnny Appleseed” …>

The placeholder remains when the field is focused, and doesn’t disappear until your user starts typing. Note: do not use the ‘placeholder’ attribute in lieu of a ‘label’. You can visibly hide a ‘label’ element (using CSS) if it is superfluous in the design, but the placeholder is not a disappearing label. It is intended to provide example content for the field.

Filling in Huffduffer uses a simple Mad Libs style for its signup form, encouraging users to
complete the sentence. It is simple and clear what is required

Associate helper text

As I mentioned earlier, there are times when we can reduce our users’ frustrations by providing helper or advisory text for a field. We typically display this content next to the field or beneath it in a design. That’s useful to sighted users, but it would be useful to everyone if it could also be read aloud when the field is focused.

This is a perfect place to use the ‘aria-describedby’ attribute, which is part of the ARIA specification (see below for more on this). This attribute
requires a value of one or more ‘id’ references (similar to the ‘for’ attribute), with multiple values separated by spaces:

<label for=”email”>Your Email</label>
<input id=”email” aria-describedby=”email-note” … >
<em id=”email-note” class=”note”>We will only use your email
address to respond to your message.</em>

Highlight required fields

In the past, we’ve relied on symbols like the asterisk ( * ) to indicate that a field is required, but HTML5 gave us a way to do it declaratively, using the ‘required’ attribute. This attribute is a boolean attribute, which
means including it makes the field required:

<input required …>

This would ensure any browser that implements the HTML5 form validation API would not submit the form if there was not a value for this field. Sadly,
at present, screen readers don’t get this information, but there is an ARIA attribute equivalent: ‘aria-required’. Including this attribute with a “true” value will announce that the field is required in the form:

<input required aria-required=”true” …>

Flag up errors

Last but not least, when we encounter a form submission error, we need to provide the details of the error(s), accompanied by quick and easy ways the user can fix them. By default, browsers that implement the HTML5 form validation API will focus the first field with an error and provide some basic error messaging based on the type of error encountered.

It is possible to customise those messages using setCustomValidity(), but I’ll leave that for you to explore on your own. Instead, let’s focus on how we display server-side validation errors. When the server encounters an error with a form submission, it’s a best practice to redirect the user back to the form, pre-populated with the information the user submitted.

The fields with errors should be indicated in some way. It’s common to see the field outlined in red to show that there’s something wrong, but I also
like to include an additional visual indication that does not rely on colour. For example, an ‘x’ in front of the label:

.error label::before { content: “x “; speak: none; }

This uses generated content in CSS to insert the character and tells screen readers (well, future screen readers … it’s not well supported yet) not to read it. Of course, just telling someone a field has an error is not very helpful. We should provide a message indicating why there was an error and/or how to fix it. We already have a pattern for advisory content like this:

<input … aria-describedby=”email-error”>
<em id=”email-error” class=”error-msg”>Your email address
does not appear to be valid. Please double check it.</em>

Easy-peasy. To cap things off, we can also tell assistive technology that the field has an error by adding the ‘aria-invalid’ attribute:

<input … aria-invalid=”true”>

If you want bonus points for awesomeness, you could also aggregate a list of errors at the start of the form, with links to each field with an error:

<div role=”alert”>
<p>There were errors with your form submission:</p>
<li><a href=”#message”>Message</a> is required</li>
<! — errors continue… →

There are two UX benefits to this pattern:

  1. Users have a summary of all errors with quick access to each field
  2. The ‘role’ of “alert” tells screen readers to trigger an audible chime and then start reading the content of this ‘div’ immediately when the page loads
Fixing problems The contact form for Retreats 4 Geeks offers a complete list of errors, with links to the field so users can fix them immediately

Great UX begins with content

By now it should be pretty clear that readability can dramatically improve the usability of an interface. It’s a pretty simple thing, but when we get wrapped up in all of the cool things we can do with CSS and JavaScript, it’s easy to forget.

Hopefully this brief survey of modern HTML forms best practices has given you a handful of simple, repeatable patterns that you can easily integrate into your own work. Your users will certainly thank you for it.

Buttons should be buttons

In the past, web designers have often groaned at the thought of styling buttons. It makes sense: ‘input’ buttons can only have text content (supplied using the ‘value’ attribute) and the ‘button’ element often injects phantom padding that can be tricky to remove.

Unwilling to accept those limitations, many of us began using other elements (‘a‘, ‘div’ and so on) to act as buttons, relying on JavaScript to trigger form submission. There are numerous problems with that approach, however:

The ‘div’ element — and ‘span’ and others — is not seen as interactive by assistive technology, so it is not normally focusable via a keyboard, nor is it exposed as an interactive element via a screen reader

The ‘a’ element is for linking and anchoring within a page, so using it to trigger a form submission can confuse users

If JavaScript is unavailable or stops working for some reason beyond your control — say, if a major ISP blocks jQuery for all of its customers — no one can submit the form

For these reasons (and others), it’s best to let buttons be buttons. I tend to prefer the ‘button’ element as it can contain other elements, but an ‘input’ with a ‘type’ of ‘submit’, ‘reset’ , or ‘button’ is equally nice. And if the phantom padding gets you down, just apply ‘appearance: none’ (and its vendor-prefixed equivalents) in your CSS and everything will be right as rain.

aria-describedby vs aria-labeledby

ARIA (Accessible Rich Internet Applications ) supports two similar concepts: ‘described by’ and ‘labeled by’ (via the ‘aria-describedby’ and ‘aria-labeledby’ attributes, respectively). As they are similar, it can sometimes be difficult to choose the appropriate option. Thankfully, however, forms offer a great way to illustrate the difference:

<label id=”email-label”>Your Email</label>
<input … aria-labeledby=”email” aria-describedby=”email-note”>
<em id=”email-note” class=”note”>We will only use…</em>

In this rewrite of another example from this article, both attributes are in play. The difference in how they end up being used is when they are read out. The ‘aria-labeledby’ reference would be read first (in lieu of any ‘label’ associated with the field, explicitly or implicitly), then information about the field would be read (‘required’ and so on), and finally the ‘aria-describedby’ reference would be read.

This simple example is good to keep in mind when you are considering using these attributes to improve the accessibility of your interfaces.

It’s worth noting that more goes into determining the label and description than just what I’ve shown you here. In fact, the W3C has outlined a decision tree for determining that value of each of these components.

In order of preference, a field’s label is one of the following:

  1. The target of ‘aria-labelledby
  2. The value of the ‘aria-label’ attribute
  3. The associated ‘label’ element
  4. The contents of ‘placeholder’ (because people abuse it)
  5. The field’s ‘title’ attribute

Descriptions come from ‘aria-describedby’, ‘placeholder’, or
‘title’, in that order.

Aaron is a progressive enhancement evangelist and web standards advocate at Microsoft, where he work closely with their browser team.

This article originally appeared in issue 265 of net magazine.



Oliver Lindberg
net magazine

Independent editor and content consultant. Founder and captain of @pixelpioneers. Co-founder and curator of GenerateConf. Former editor of @netmag.