DEV Community

Matthew Dillon
Matthew Dillon

Posted on • Originally published at cleara11y.net on

How to Make Your Forms Accessible: Complete Guide

Why Form Accessibility Matters

Forms are how users interact with your website - signing up, checking out, contacting you, searching. If your forms aren't accessible, you're locking out users who:

  • Use screen readers
  • Navigate with keyboards only
  • Have motor impairments
  • Use voice control software

Inaccessible forms are also one of the most common reasons for ADA lawsuits. The good news? Most form accessibility issues are easy to fix.

The Fundamentals

Every Input Needs a Label

This is the most common form accessibility failure. Every input must have a programmatically associated label.

<!-- Bad: No label association -->
<span>Email</span>
<input type="email">

<!-- Bad: Placeholder is not a label -->
<input type="email" placeholder="Email">

<!-- Good: Label with for/id association -->
<label for="email">Email</label>
<input type="email" id="email">

<!-- Also good: Wrapping label -->
<label>
  Email
  <input type="email">
</label>

Enter fullscreen mode Exit fullscreen mode

Screen readers announce the label when users focus on the input. Without it, users have no idea what information to enter.

Use Proper Input Types

HTML5 input types provide built-in accessibility benefits:

<input type="email"> <!-- Email keyboard on mobile, validation -->
<input type="tel"> <!-- Phone keyboard on mobile -->
<input type="url"> <!-- URL keyboard, validation -->
<input type="number"> <!-- Number keyboard, increment controls -->
<input type="date"> <!-- Native date picker -->
<input type="search"> <!-- Search semantics, clear button -->

Enter fullscreen mode Exit fullscreen mode

Add Autocomplete Attributes

Help users fill forms faster and reduce errors:

<input type="text" autocomplete="name">
<input type="email" autocomplete="email">
<input type="tel" autocomplete="tel">
<input type="text" autocomplete="street-address">
<input type="text" autocomplete="postal-code">
<input type="text" autocomplete="cc-number">

Enter fullscreen mode Exit fullscreen mode

This is also a WCAG 2.1 Level AA requirement (Success Criterion 1.3.5).

Required Fields

Mark Required Fields Clearly

Don't rely on color alone to indicate required fields:

<!-- Good: Visual indicator + aria-required -->
<label for="name">
  Name <span aria-hidden="true">*</span>
  <span class="sr-only">(required)</span>
</label>
<input type="text" id="name" aria-required="true" required>

<!-- Include instructions -->
<p class="form-instructions">Fields marked with * are required</p>

Enter fullscreen mode Exit fullscreen mode

Use the required Attribute

The HTML required attribute provides native validation:

<input type="email" id="email" required>

Enter fullscreen mode Exit fullscreen mode

Error Handling

Poor error handling is frustrating for everyone and impossible for screen reader users. Here's how to do it right:

Identify Errors Clearly

<!-- Associate error with input using aria-describedby -->
<label for="email">Email</label>
<input
  type="email"
  id="email"
  aria-invalid="true"
  aria-describedby="email-error"
>
<span id="email-error" class="error">
  Please enter a valid email address
</span>

Enter fullscreen mode Exit fullscreen mode

Announce Errors to Screen Readers

Use aria-live regions to announce errors as they appear:

<div aria-live="polite" class="error-summary">
  <!-- Errors inserted here will be announced -->
</div>

Enter fullscreen mode Exit fullscreen mode

Provide an Error Summary

For long forms, provide a summary at the top:

<div role="alert" class="error-summary">
  <h2>There were 2 errors with your submission</h2>
  <ul>
    <li><a href="#email">Email: Please enter a valid email</a></li>
    <li><a href="#password">Password: Must be at least 8 characters</a></li>
  </ul>
</div>

Enter fullscreen mode Exit fullscreen mode

Be Specific About What's Wrong

<!-- Bad: Vague error -->
<span class="error">Invalid input</span>

<!-- Good: Specific and helpful -->
<span class="error">Password must be at least 8 characters and include a number</span>

Enter fullscreen mode Exit fullscreen mode

Form Layout and Grouping

Group Related Fields

Use fieldset and legend for related inputs:

<fieldset>
  <legend>Shipping Address</legend>

  <label for="street">Street Address</label>
  <input type="text" id="street" autocomplete="street-address">

  <label for="city">City</label>
  <input type="text" id="city" autocomplete="address-level2">

  <label for="zip">ZIP Code</label>
  <input type="text" id="zip" autocomplete="postal-code">
</fieldset>

Enter fullscreen mode Exit fullscreen mode

Radio Buttons and Checkboxes

Always group these with fieldset:

<fieldset>
  <legend>Preferred contact method</legend>

  <input type="radio" id="contact-email" name="contact" value="email">
  <label for="contact-email">Email</label>

  <input type="radio" id="contact-phone" name="contact" value="phone">
  <label for="contact-phone">Phone</label>

  <input type="radio" id="contact-mail" name="contact" value="mail">
  <label for="contact-mail">Mail</label>
</fieldset>

Enter fullscreen mode Exit fullscreen mode

Accessible Form Patterns

Search Forms

<form role="search">
  <label for="search" class="sr-only">Search</label>
  <input type="search" id="search" placeholder="Search...">
  <button type="submit">
    <span class="sr-only">Submit search</span>
    <svg aria-hidden="true"><!-- search icon --></svg>
  </button>
</form>

Enter fullscreen mode Exit fullscreen mode

Login Forms

<form>
  <h1>Sign In</h1>

  <label for="username">Email or Username</label>
  <input type="text" id="username" autocomplete="username" required>

  <label for="password">Password</label>
  <input type="password" id="password" autocomplete="current-password" required>

  <button type="submit">Sign In</button>

  <a href="/forgot-password">Forgot password?</a>
</form>

Enter fullscreen mode Exit fullscreen mode

Multi-Step Forms

<!-- Indicate progress -->
<nav aria-label="Form progress">
  <ol>
    <li aria-current="step">1. Personal Info</li>
    <li>2. Shipping</li>
    <li>3. Payment</li>
  </ol>
</nav>

<!-- Each step should have a heading -->
<h2>Step 1: Personal Information</h2>

Enter fullscreen mode Exit fullscreen mode

Testing Your Forms

Keyboard Testing

  1. Tab through every field - is the order logical?
  2. Can you select options in dropdowns with arrow keys?
  3. Can you submit the form with Enter?
  4. Can you cancel/close modals with Escape?

Screen Reader Testing

  1. Are all labels announced?
  2. Are required fields identified?
  3. Are errors announced when they appear?
  4. Is the error summary read when the form is submitted?

Automated Testing

Use ClearA11y to scan your forms for:

  • Missing labels
  • Empty buttons
  • Missing form field names
  • ARIA misuse

Quick Reference Checklist

  • Every input has a visible, associated label
  • Required fields are marked (not just with color)
  • Input types match the expected data
  • Autocomplete attributes are set
  • Errors are specific and associated with fields
  • Error summary links to problem fields
  • Related fields are grouped with fieldset/legend
  • Form can be completed with keyboard only
  • Focus order is logical
  • Submit button has clear text

Scan Your Forms Now

Don't guess whether your forms are accessible. Run a free scan with ClearA11y and get a complete report with AI-powered fix suggestions you can copy and paste directly into your code.

Top comments (0)