Back to articles

Creating Accessible Web Forms

Boost Usability and Compliance with Practical WCAG and ARIA Techniques

June 25, 2025
An illustration of an accessible web form with proper labels, error messages, and focus indicators, highlighting key accessibility principles.
accessibility
web development
8 min read

As another installment of this series and a continuation of my previous article on Building Accessible Web Applications , I’m writing this one that focuses on forms.

Designing forms that are accessible is crucial for ensuring that all users, including those with various disabilities, can complete them efficiently and effectively. Making forms accessible, just like the rest of the website, creates an inclusive environment that accommodates diverse needs (visual, auditory, motor, cognitive, etc.).

So without spending any more effort on writing an obligatory introduction, I’m going to get right into [what i think are] essential practices for accessible form design, with code examples and best practices for both plain HTML/JavaScript and modern frameworks like React.

note

Recent guidelines like WCAG 2.2 (released Oct 2023) introduce new criteria (e.g. Focus Not Obscured and Minimum Target Size) that improve keyboard and touch usability, while WCAG 3 is still in the works with a working draft posted at W3.org

Label and Group Form Elements

Every form control needs a clear, visible label. Use the <label> element with a for attribute (or htmlFor in React/JSX) to associate text with inputs.

For example:

<label for="username">Username:</label>
<input type="text" id="username" name="username" required>

This explicitly links the label to the input, so screen readers announce “Username” when the field gains focus.

important

Do not rely on placeholder text alone as a label – placeholder hints disappear as users type and are often not read by screen readers. Always provide a visible label (you can supplement it with aria-label or aria-labelledby if hiding it visually).

Group related fields logically using <fieldset> and <legend> so users (and assistive technologies) understand the context of the group.

For instance:

<fieldset>
  <legend>Name</legend>
  <label for="first-name">First Name:</label>
  <input type="text" id="first-name" name="first-name" required>
  <label for="last-name">Last Name:</label>
  <input type="text" id="last-name" name="last-name" required>
</fieldset>

This adds semantic structure – the legend “Name” applies to both first and last name – making the form more navigable.

In React, remember to use htmlFor like this:

<label htmlFor="first-name">First Name:</label>
tip

If you must use a non-semantic element for a control, assign an appropriate ARIA role (e.g. role="button") so assistive tech identifies it.

Provide Clear Error Messages and Instructions

When validation fails, give specific, contextual feedback near the affected fields. Avoid vague alerts like “Submission failed.” Instead, say what went wrong.

For example:

<label for="email">Email:</label>
<input type="email" id="email" name="email" required aria-describedby="email-error">
<span id="email-error" class="error-message" role="alert" aria-live="assertive"></span>

Here, aria-describedby="email-error" associates the input with an error message element. Setting role="alert" or aria-live="assertive" ensures screen readers announce the error as soon as it appears. For example,

<div aria-live="assertive">Please enter a valid email address.</div>

would be read immediately by assistive technology.

In JavaScript validation, mark invalid fields with aria-invalid="true". This indicates to screen readers that the field’s value is unacceptable. For instance, once we detect an invalid email, we might do: emailInput.setAttribute("aria-invalid", "true"), and update the error text. This way, screen readers like JAWS/NVDA will announce the field as “invalid entry”.

Another thing we can do is that above the form, we can include overall instructions or summaries if helpful (e.g. “All fields are required.”) to guide users.

For multi-step or long forms, consider an error summary at the top linking to each field with errors – announcing only the summary avoids overwhelming users with multiple alerts.

In short: use inline, descriptive messages, link them to inputs with ARIA, and ensure they are announced (e.g. via aria-live) to all users.

Use Semantic and Appropriate HTML Elements

Prefer semantic HTML inputs and controls. HTML5 input types trigger optimal keyboards and validation.

Example: Use type="email" for emails and type="tel" for phone numbers. A numeric keypad often appears for <input type="tel"> on mobile, improving input speed. Also consider the inputmode attribute if needed (for instance, inputmode="numeric" to hint a number keypad).

For clickable actions, use <button> or <a> with role="button" rather than a <div>, so users know they can activate them. For example:

<button type="submit">Submit</button>

If you must create a custom control, assign proper ARIA roles or states (e.g. role="checkbox", aria-checked, etc.) so assistive tech can handle it.

You can also use HTML attributes like required to trigger native browser validation and announce missing fields.

important

Remember that label text, placeholder hints, and instructions should all have sufficient contrast (WCAG 1.4.3 requires at least 4.5:1) to be legible.

bonus

React Considerations React fully supports all ARIA attributes in JSX, using the same hyphen-case names as in HTML. For example:

<label htmlFor="name">Name:</label>
<input id="name" type="text" aria-label="Full Name" name="name" />

Notice we use htmlFor instead of for. When managing focus or dynamic content in React, use refs (useRef) and effect hooks to set focus or update attributes programmatically. Also consider libraries like React Aria (Adobe’s Spectrum toolkit) which provide accessible UI components out-of-the-box.

Ensure Keyboard Navigation and Focus

All form controls must be reachable and operable via keyboard alone (Tab, Shift+Tab, Enter, Space). Users should be able to move focus through fields in a logical order that matches the visual layout. And I don’t know if it needs to be said but - never trap focus in a field. Basically avoid preventing Tab from leaving a text field. Also provide visible focus indicators (the outline or border around a focused element) so users know where they are.

tip

WCAG 2.2’s Focus Not Obscured criterion requires that this focus indicator remain at least partially visible (not hidden behind sticky headers or out of view).

In CSS, avoid removing the focus outline without replacing it; if you do customize it, make sure it meets contrast requirements.

You can test this with a keyboard: ensure pressing Enter or Space activates buttons, links, or checkboxes. Don’t manually reorder the tab sequence with tabindex (this often confuses users) – instead, structure your HTML in the same order you want users to navigate.

For complex components (e.g. custom dropdowns, dialogs), you can manage focus with scripts (e.g. move focus into a modal when it opens, and return focus to a logical place when it closes).

bonus

Providing “Skip to Content” links or using landmark roles (like <main>, <nav>, etc.) also helps keyboard users bypass repetitive page elements.

You might remember this as a small link text on top of the page from the web. I used to see it more often than we do now.

Make Forms Mobile and Touch Friendly

Okay so this one’s simple.

  • Make sure the forms are responsive (e.g., media queries, flexbox/grids).
  • Make sure buttons are big enough (have enough padding between the text and border).
  • Make sure anything else that can be touched is big enough. (checkboxes, links, etc)
  • Set appropriate input types to make it easier and faster for the user to input (type="tel", type="email", type="number", etc.) by triggering the appropriate virtual keyboard.
important

WCAG AA now recommends a minimum of 24×24 CSS pixels for tappable controls (the older WCAG 2.1 AAA recommendation was 44×44px for critical targets)

In summary, design with a “fat finger” in mind: larger controls, sufficient spacing, and responsive sizing.

Also, avoid using autofocus which can force the keyboard to pop up unexpectedly. If you have ever come across that - it is frustrating af.

Leverage ARIA for Dynamic Interactions

Semantic HTML should be the first choice, but ARIA can help where native elements aren’t enough.

  • Live Regions: As mentioned, aria-live (and role="alert") can announce dynamic messages (like errors or success notes) to assistive tech. Set it to "assertive" for urgent messages, or "polite" for less-critical updates.
  • Aria Labels: If a field has no visible label (e.g. an icon-only button), use aria-label="Description" or link to a hidden <label> with aria-labelledby.
  • ARIA States: Use aria-required="true" on required fields to indicate necessity (though required is usually sufficient), and aria-invalid="true" on fields when validation fails.
  • Avoid Unsupported ARIA: Note that aria-errormessage (to point to an error message) exists but lacks widespread support; it’s safer to stick with aria-describedby as shown earlier.
  • ARIA Roles: Explicitly set roles for complex widgets. For instance, if you use a <div> as a button, do <div role="button" tabindex="0">. For completeness, you can wrap the form in <form role="form"> and sections in <section role="group">, but native elements are preferred.

Each of these techniques enhances the experience for users with assistive technology, but always test carefully to ensure they work as expected.

Final Thoughts

And for wrapping up, I would say embrace the opportunity to implement these best practices in your next project. By prioritizing accessibility in forms, we empower all users to interact with our application, fostering a more inclusive web. Well-designed accessible forms can lead to higher completion rates and better satisfaction for everyone.

Cheers! 🍻

bonus
  • Accessibility Guidelines (WCAG): The Web Content Accessibility Guidelines provide comprehensive standards. WCAG 2.2 (latest as of 2025) adds new criteria focusing on keyboard focus and touch targets. Familiarize yourself with WCAG AA requirements (and relevant AAA criteria for additional best practices) to build compliant forms.
  • Learning Resources: Read up on inclusive design principles. Search for recent articles, books, or courses on web accessibility. The WAI (W3C) site, WebAIM, A11y Project, and conferences like CSUN are great sources. Cover topics like semantic HTML, ARIA, and user testing with people with disabilities.
  • Tools and Libraries: Use accessibility evaluation tools like WAVE, axe DevTools, or Accessibility Insights to catch issues early. Browser dev tools often have an “accessibility” pane now. For React, consider libraries like React Aria or Reach UI, which offer accessible components. These handle many details (keyboard handling, focus management) for you.
  • Testing with Users: Regularly test your forms via keyboard-only navigation and with screen readers (NVDA, JAWS, VoiceOver, etc.). Also, involve people with disabilities in testing when possible; their feedback is invaluable. Check form behavior on various devices and browsers.

Continue Reading

Discover more insights and stories that you might be interested in.