Creating Accessible Forms

Creating Accessible Forms

Online forms are the foundation of communication in the modern age. Without forms, all we'd have would be static text websites with pictures of cats to look at. Sure, it doesn't sound too bad, but without forms, how would we discuss and share cat pictures with all our friends and family? How about shopping online, booking a flight, ordering pizza late at night, or setting up a time to meet on that dating site?

This is why well-written form structure and design is critical to the success of any website or app. Let's take a look at a few areas on how we can increase accessibility when it comes to form design.


Highly visible, clear, and precise form labels are critical to the success of anyone being able to fill out a form. Without a label, no one would know what the intended purpose for each form field would be!

Let's look at a few design considerations when adding labels to our forms.


HTML form fields feature an attribute called placeholder. The value of the attribute places text inside the field and, when someone starts to type, the placeholder text is automatically removed.

The original purpose of this attribute is to place helper or hint text inside the input. However, sometimes placeholders are being used in place of an actual HTML label element. This is problematic for a few reasons:

  • Without a label element associated with the input, some screen readers may not be able to convey the purpose of the input.
  • When someone starts to type and the placeholder text is removed from view, the context of the input may be lost, forcing someone to remove the entered text in order to remember what the input was for.
  • In order to meet color contrast requirements, the default grey text needs to be darkened. In doing so, this may confuse some people into thinking that text has already been entered into the input.

There are more issues with the placeholder attribute, but with these few in mind, it's clear that placeholder should not be used as a replacement for an actual label.

Floating labels

The "floating label" technique seems to be picking up in popularity as of late. Depending on how it's implemented, the basic idea is to use CSS to position the label (or sometimes the placeholder) text over-top of the related input element. When someone starts typing, the label text is "floated" up and placed above the input.

The benefit of this approach is that it saves space in-between inputs when screen real estate is tight. There's also the clear benefit of a label element being associated with its input element for screen readers to convey the purpose of the input.

The downside to this approach is a direct result of its purpose of being; when the label is floated up above the input, the space available typically only allows for less than ideal text size. This could result in difficulty reading the label text for anyone with low-vision. Of course, they could just zoom in on the screen, but why require creating more work for our users?

Then the other concern regards the actual implementation. If the technique ends up using the placeholder attribute instead of an actual label, it would result in issues previously discussed, mainly the lack of conveyed purpose when interacted with a screen reader.

Always-visible labels

The ideal design of form labels when it comes to accessibility is the "always visible" label. Just as it sounds, this technique features a text label that is always present across the whole experience of filling out the form.

The benefits of this approach are quite clear:

  • The text label is in a consistent, predictable position
  • The label is always visible, requiring no cognitive load of having to remember the input purpose
  • Screen readers will convey the purpose of the input when interacted with label text is large with ample white space to be readable

With all this in mind, it's strongly recommended to keep labels visible. Eliminate any guesswork required from your users and allow them to complete the task at hand with ease and confidence.

Visually hidden labels

One more label type we need to discuss is the "visually hidden" label. These often appear in tiny, one-off form inputs such as Search or Language selector components where space is limited.

As we know, form input elements must always include a label in order to share its purpose with the user. What can we do when a design calls for a non-visible label? Let's review how to apply a visually hidden label in two different ways, each method leading to the same outcome in the end.

Note: Voice dictation users may have difficulty finding the correct call-to-action when accessing each control with a visually hidden label. Proceed with caution.

The .visuallyhidden class

Applying the .visuallyhidden class definition (also sometimes called .sr-only) directly to the label element will remove the label for sighted users, but remain available for screen reader users.

<label for="lang-selector" class="visuallyhidden">Select a language</label>
<select id="lang-selector">
  <!-- ... -->

The aria-label approach

Another option is to apply the aria-label attribute directly onto the input element in order to provide a "hidden" label.

<select aria-label="Select a language">
  <!-- ... -->

Just like the .visuallyhidden approach, this will provide a label for the input and, on input focus, the aria-label value will be announced by screen readers.

Required fields

When only specific fields are Required or Optional to fill out a form, it's best to denote this requirement with visual and audible cues. This helps people to know which fields they absolutely need to fill in which, in turn, provides reassurance when filling out the form.

Visual cue

When marking as Required, this is typically done with an asterisk character or an icon placed beside the form label. Whether you use the asterisk or some other form of an icon, what's important here is:

  • The placement of the icon is consistent throughout the form

  • The icon features a contrast ratio high enough to be visible (3.0:1 for non-text elements)

<label for="email">
    <!-- … -->

When marking a field as Optional, placing this context as plain text within the label will suffice.

<label for="email">Email (Optional)</label>

Audible cue

In order to notify a screen reader user of a required field, we can simply add the aria-required attribute to the input element. This will inform the user that this field is required, but does not add anything else in terms of field validation.

<label for="email">Email</label>
<input type="email" id="email" name="email" aria-required="true" />

When a screen reader encounters this input, it may announce something like, "Email, edit text, required."

Additional instructions

In addition to the visual cue for each required field beside the label text, it's also best practice to place instructions at the top, notifying the user of each field type. Something along the lines of:

<!-- When marking as Required… -->
<p aria-hidden="true">* denotes a required field</p>
<!-- When marking as Optional… -->
<p>All fields required unless described otherwise.</p>

With this plain text note, people with cognitive impairments, older generations, or someone new to the internet will have less trouble with understanding which fields are mandatory, and as a result, spend less time and effort on filling out the form.

The required attribute

Now, as a developer you may be thinking, "Why not just use the built-in HTML required attribute? Doesn't this also announce a required field?"

It's true that including the required attribute renders a similar result to using aria-required, notifying the screen reader user of a required field. The difference is using aria-required allows the developer to include custom validation rules.

Avoid native validation

Using custom validation instead of the built-in browser validation is strongly preferred for a few reasons:

  • Greater control over error messaging
  • Messages can be translated into multiple languages
  • Consistent and predictable messaging implementation
  • Messages are not removed from view after a short period of time

For these reasons, it is strongly recommended to implement custom form validation.

WCAG success criteria

This comes back to 3.3.2 Labels or Instructions which states:

"Labels or instructions are provided when content requires user input."


The design of form error messages can be quite delicate. The idea is to bring awareness to the current state of the form without being overwhelming. Unfortunately, it's very easy to cause frustration or grief when displaying error states, especially for those who aren't so tech-savvy and who are already very cautious and on edge from using a computer in the first place.

As designers and developers, it's our responsibility to ease our users into the systems that we create, guiding their way through the path we've set forth before them.

Error messaging

With this in mind, let's consider a design aesthetic which is informal and does its best not to be overbearing at the same time.

Three things to look at in this section include:

  • Error text
  • Input styles
  • Error list

Error text

Error text is critical when conveying an error state. Without this text, people might not realize that the form is in a state in which it cannot be submitted. Sure, we could change the color of the input element, perhaps alter its border or background properties to something recognizable as an error state. However, as we've already seen, relying on color alone to convey meaning is not ideal.

So, how do we ensure the input error state is properly conveyed? A few things we can do are:

  • Output text which is meaningful and does not overwhelm the user with technical jargon (cognitive impairment).
  • Display the text below the related input (low-vision).
  • Ensure text is readable by testing its color contrast.
  • Optionally include an icon to help bring the users attention to the text.

Connecting error text to its input

When someone using a screen reader comes in contact with an input element in an error state, this information needs to be announced alongside its label text. Otherwise, there's a good chance the error context could be lost when navigating through a form.

There are a few ways to accomplish this, but the most robust would be to include the aria-describedby attribute on the input element. Adding this attribute places the error text in the "queue" of announcing the element on keyboard focus.

In order to programmatically make the connection, the aria-describedby value needs to match the id of the error text container.

For example, let's review an input element in an error state.

No connection

<label for="fname">First name</label>
<input type="text" id="fname" name="fname" />
<span id="error-fname" class="error">First name is required</span>

This input features a label and some error text directly below. However, the text is not included with the announcement of the label as there's no programmatic connection being made.

Let's make the connection using aria-describedby, an attribute which is used to help provide more information for the given context.

Using aria-describedby

<label for="fname">First name</label>
<span id="error-fname" class="error">First name is required</span>

With the aria-describedby attribute applied to the input, and its value matching the id of the error text container, the announcement from a screen reader would sound like,

"First name, invalid, edit text. First name is required."

One of the benefits of using aria-describedby specifically is that it adds a short pause in between the label text and the error text, giving it a little bit of a distinction in the announcement.

Error list

Presenting a list of error when a form is in an error state is particularly useful on large forms. This content is made up of a heading, explaining the current state of the form, followed by an unordered link list.

The heading, typically an h2 element, is the primary method of bringing the error state to the users' attention. Sighted users will see the large text and non-visual users will have the keyboard focus be brought to this portion of the screen automatically using focus management; when the heading is presented to the user, send the focus to the heading element using the JavaScript focus() method as well as applying tabindex="-1" to the heading element in order for it to receive focus.

The list portion is made up of a ul element containing links. Each link represents a single error in the form. The link text should match that of the error text output below the related input. When activated, the keyboard focus would switch from the link to the related input, allowing the user to focus on and address the issue at hand.

<h2 class="form-message__title" tabindex="-1">
  Please adjust the following:
    <a href="#fname" class="form-message__link">
      First name is required
  <!-- ... -->

<!-- ... -->

<label for="fname">First name</label>
<span id="error-fname" class="error">First name is required</span>

The ideal positioning of the error list is directly above the form which is for user convenience. For example, if someone using a screen reader were to move forward past the error list, they would be brought to the form with its input elements. Each input would be accompanied by their own error state text which would inform the user on how to adjust the input content to meet its validation requirements.

The error list, in combination with the error text below the input, ensures the user is notified of the current errors which need attention and removes the cognitive load of not having to remember each error which needs fixing; the error message will be available when they arrive at the input element.

WCAG success criteria

This comes back to 3.3.1 Error Identification which states:

"If an input error is automatically detected, the item that is in error is identified and the error is described to the user in text."

Success messaging

Just as important, but sometimes forgotten about, is the success message. When someone completely fills in the form correctly, there should be text set in place to notify the user of the current state.

As with the error list heading, the success message should also be designed to include large, attention-grabbing text. In addition, the keyboard focus should be managed on behalf of the user and set on the success message heading element. With this, the user will be informed of the form submission and can confidently move onto another task.

The autocomplete attribute

The HTML autocomplete helps users to complete filling in forms by using data stored in the browser. This is particularly useful for people with motor disabilities or cognitive impairment who may have difficulties filling out forms online.

For example, a form input asking for someone's email address wound need to include the autocomplete attribute in order to send a hint to browser or other user agents to query for this data:

<label for="email">Email</label>
<input type="email" id="email" name="email" autocomplete="email" ... />

With this in place, users will have a much easier and comfortable user experience when their browser of choice prompts them to enter their email address on their behalf.

Review the section, "W3C HTML 5.12 – Autofill" for all other input types and their corresponding autocomplete attribute values.

Inline validation and the Submit button significance

In modern form design and development, especially common in SPA app demos, there are two trends in particular: inline-validation of form controls after they've been "touched" (essentially when the user moves away from the input) and keeping the Submit button in a disabled state (via disabled attribute) until each validation test has been satisfied.

Inline validation

When it comes to creating highly usable and accessible forms, particularly for people with disabilities, it is recommend not using inline validation. This pattern has the potential to cause the user a bit of confusion or frustration at times. Some examples illustrating this include:

  • When someone using a screen reader is navigating through a form to get a lay-of-the-land overview, trying to read what each field is and the expected data
  • Someone who might want to fill in form fields in a different order
  • When someone leaves a field to go look something up to be certain of what data to place within the form control

These simple actions trigger the error message and the result can be quite irritating

During usability studies, people often grumble or curse out loud when error messages appear before they've even started filling out forms!

Additional accessibility issues appear when the user navigates away from a field; the error message is displayed visually but, typically, not to assistive technology. In order to hear the error message, the user needs to navigate backward through the form. This would be an unexpected required action to hear and understand the error state.

Disabled state

With the Submit button set as disabled by default, only after all of the form validation rules have been satisfied does the control become available for use. Having this pattern in place might lead to a confusing or frustrating user experience; there's no indication as to why the control would be disabled after filling in the form on first pass.

Consequently, disabled controls can present two additional challenges for individuals:

  • People who have low vision might not be able to perceive the button text in the dimmed state.
  • Some assistive technology will skip disabled elements; screen reader users may not get a full picture of what is available in the interface, leading to feelings of uncertainty or self-doubt.

Accounting for accessibility with this pattern

To alleviate some of these potential pain points for users, its recommended to take a slightly different, hybrid approach. Consider making the following changes to your form workflow:

  • When an input is in an error state, output the error message text as described previously. This will serve as a reminder of the error and the expected value when the control is navigated to, as well as any other controls in an error state when the user moves forward through the form content.
  • When the user moves away from the form input, run the validation test. If the test fails, output the error text visually for sighted users but refrain from announcing the new state to screen reader users.
  • When typing in an input which is in an error state, remove any error text; only display error text when focus moves away from the input.
  • Allow full form validation to run on form submission by keeping the Submit button enabled. This will allow the user to easily explore the form and become comfortable with the form structure on the first run through. Even if the form fields have errors, allow users to submit the form on their terms, when they feel comfortable to do so.

With these changes in place, anyone relying on assistive technology or those who require a little more guidance in completing a form will have a clearer understanding of how the form is structured and the current state of the form, as well as if there are any errors and how to address them.

If there are changes to be made on submit, focus will be brought up to the error list. From there, error links will move focus directly to the form input in question. From this point, error messages will be announced when traversing the form, and any changes to the data can easily be made with greater confidence on the part of the user.


Back to blog