Forms That Everyone Can Use: Accessible Form Design

Glowing accessible web form with focus rings and label connections

Every form on your website is a gate. Someone on the other side wants to give you their email, ask a question, schedule a consultation, or buy something. And if the gate doesn't open for them, they leave. No error message in their inbox. No abandoned cart notification. They just disappear, and you never know they were there.

We audit client sites regularly, and forms are where accessibility failures hit hardest. A missing label that a sighted user never notices makes an entire field invisible to a screen reader. A "creative" placeholder-only input looks clean until someone with a cognitive disability forgets what the field was asking for mid-entry. A custom-styled dropdown that works perfectly with a mouse becomes an impassable wall for anyone using a keyboard. These aren't edge cases. According to the WebAIM Million report, 47.1% of form inputs detected across the top million home pages had missing labels in 2025. Nearly half.

Visible Labels Are Not Optional

We'll say this as directly as we can: placeholder text is not a label. We've had this argument with designers more times than we can count, and we'll keep having it. The "clean look" of a form where the only indication of what a field expects is light grey text inside the input is a usability failure wrapped in an aesthetic choice.

Here's what happens. A user clicks into the email field. The placeholder disappears. Now they're typing into an unmarked box. If they get distracted, tab away, or come back to review their entries before submitting, every filled field is just text with no context. Users with short-term memory difficulties lose track of what goes where. Screen reader users may hear nothing at all if the placeholder wasn't wired up as an accessible name. And users with low vision often can't read placeholder text in the first place because it typically fails the WCAG 1.4.3 minimum contrast ratio of 4.5:1 against white backgrounds.

The fix is the most basic HTML pattern there is, and it has worked since the 1990s:

<label for="email">Email address</label>
<input type="email" id="email" name="email" required>

The for attribute on the <label> matches the id on the <input>. That's the entire mechanism. It tells screen readers which label belongs to which field. It gives users a larger click target because clicking the label focuses the input. It persists visibly while the user types, so they always know what the field is asking for.

WCAG 2.1 Success Criterion 3.3.2 (Labels or Instructions) is a Level A requirement. Level A is the floor, not the ceiling. Every input that expects user data needs a visible label that's programmatically associated with the field. The W3C's Forms Tutorial spells this out with examples for every common input type. There's no ambiguity here.

If your designer insists on a minimal look, there are ways to style labels small, tuck them above the input, or float them on focus. The label can be subtle. It cannot be absent.

Error Messages That Actually Help

A form that says "Please fix the errors below" without specifying which errors, in which fields, or what went wrong is not an error message. It's a shrug.

Baymard Institute's checkout usability research found that users spent up to five minutes trying to fix simple input errors when error messages were vague. Five minutes on a single form field because the site said "Invalid input" instead of "Please enter your email in the format [email protected]." That's not a minor annoyance. That's abandonment waiting to happen.

Good error handling has three parts. The error must be visible. It must be specific. And it must be connected to the field it refers to in the code, not just visually near it.

Here's the pattern we use on every form we build:

<label for="phone">Phone number</label>
<input
  type="tel"
  id="phone"
  name="phone"
  aria-describedby="phone-error"
  aria-invalid="true"
>
<span id="phone-error" class="error-message" role="alert">
  Please enter a 10-digit phone number, e.g. 403-555-1234
</span>

Three things are working here. First, aria-invalid="true" tells screen readers that the field has failed validation. When focus lands on the input, the user hears "invalid" as part of the announcement. Second, aria-describedby="phone-error" connects the error message to the field so the screen reader announces the error text when the user focuses the input. Third, role="alert" on the error message causes screen readers to announce it as soon as it appears in the DOM, even if the user hasn't tabbed to that field yet.

The W3C's ARIA21 technique documents this pattern as the standard approach for meeting WCAG 2.1 Success Criterion 3.3.1 (Error Identification). That criterion requires that errors be identified in text and described to the user. A red border alone doesn't cut it. We covered why relying on colour alone fails in our guide to designing for colour blindness. The same principle applies here: if colour is doing work, something else needs to be doing it too.

One more thing. Set aria-invalid dynamically, not on page load. If every field starts with aria-invalid="true" before the user has typed anything, you've told their screen reader that they've already failed. Validate on blur or on submit, then apply the attribute only to fields that actually have problems.

Good form accessibility isn't about adding ARIA attributes after the fact. It's about choosing the right HTML elements from the start, keeping labels visible, making errors specific, and testing the result without a mouse.

Required Fields: The Asterisk Problem

The red asterisk has been the universal symbol for "required" in web forms for decades. It's so ingrained that most sighted users parse it without conscious thought. But "universal" doesn't extend to everyone. Screen readers don't announce a red asterisk next to a label unless you've explicitly told them what it means.

The minimum viable approach is to add required or aria-required="true" to the input element. Screen readers will announce "required" when the user focuses the field. But you should also do two more things.

First, include a text note at the top of the form explaining the convention: "Fields marked with an asterisk (*) are required." This helps everyone, not just screen reader users. Second, consider the approach Baymard Institute's research recommends: mark both required and optional fields explicitly. Their testing found that only 14% of e-commerce checkout forms do this, yet it consistently reduces form errors and user confusion. When a form has 12 fields and two are optional, marking those two as "(optional)" is clearer than scattering asterisks across the other 10.

<p class="form-instructions">Fields marked with * are required.</p>
<label for="name">
  Full name <abbr title="required" aria-hidden="true">*</abbr>
  <span class="sr-only">(required)</span>
</label>
<input type="text" id="name" name="name" required>
<label for="company">
  Company name <span class="optional">(optional)</span>
</label>
<input type="text" id="company" name="company">

The aria-hidden="true" on the asterisk prevents screen readers from announcing "star" or "asterisk," and the .sr-only span provides screen reader users with the word "required" in plain language. The HTML required attribute on the input handles the programmatic side, triggering both browser validation and screen reader announcements.

Tab Order, Keyboard Access, and Custom Controls

A sighted mouse user fills out a form by clicking fields in whatever order makes sense. A keyboard user fills it out by pressing Tab. If the tab order doesn't match the visual layout, the form becomes disorienting. If a field can't receive focus at all, the form becomes unusable.

WCAG 2.1 Success Criterion 2.4.3 (Focus Order) requires that the tab sequence follow a logical order that preserves meaning and operability. In practice, this means your form's source order in the HTML should match the visual order on screen. CSS Grid and Flexbox make it easy to reorder elements visually without changing the DOM, and that mismatch is exactly where problems start. If you've used order or flex-direction: row-reverse to rearrange fields visually, test the form with a keyboard. Tab through every field and confirm the cursor moves in the order a human would expect.

We covered focus indicators, focus traps, and keyboard testing in detail in our keyboard accessibility guide. Everything in that article applies to forms, and then some. Forms have additional keyboard requirements that catch developers off guard.

Native HTML form controls work with keyboards by default. <input>, <select>, <textarea>, <button> are all focusable, activatable, and announced correctly by screen readers. They've been that way for 25 years. The trouble starts when someone decides the native <select> dropdown is too ugly and replaces it with a <div> styled to look like a dropdown.

We have a strong opinion here: unless you are prepared to rebuild every keyboard interaction, every ARIA role, every screen reader announcement, and every state change that the native element provides for free, do not replace native form controls with custom HTML. The native <select> element is ugly. It's also universally understood by every screen reader, keyboard, switch device, and mobile browser on the planet. Your hand-rolled listbox built from <div> elements is pretty. It also probably fails on at least three of those. The W3C's guidance on custom form controls warns that creating accessible custom widgets requires a deep understanding of ARIA patterns and extensive testing.

If you must build a custom control, the W3C's ARIA Authoring Practices Guide documents the expected keyboard interactions for every common widget pattern. A custom listbox, for instance, needs to support arrow key navigation between options, Home and End keys for jumping to first and last, type-ahead character matching, and Enter or Space for selection. Most custom implementations handle maybe two of those.

Accessible Form Design Checklist

  1. Every input has a visible <label> with a matching for/id pair. No placeholder-only fields.

  2. Error messages are specific ("Enter a valid email" not "Invalid input"), visually visible, and connected to their fields with aria-describedby.

  3. aria-invalid="true" is applied dynamically to fields that fail validation, never on page load.

  4. Required fields are marked with both a visual indicator and required or aria-required="true" on the input. The form includes a text explanation of the convention used.

  5. Tab order matches the visual layout. Test by pressing Tab through every field without touching the mouse.

  6. Native HTML form elements (<input>, <select>, <textarea>, <button>) are used instead of custom-built replacements unless full ARIA support and keyboard interaction has been implemented and tested.

  7. Error summaries appear at the top of the form with links to each problem field, and individual inline errors appear next to the relevant fields.

  8. Form groups use <fieldset> and <legend> for related controls like radio buttons and checkbox groups.

  9. Submit buttons are actual <button> elements, not <div> or <span> elements with click handlers.

  10. The form has been tested with a screen reader (NVDA or VoiceOver) and with keyboard-only navigation.

Mobile Forms and Touch Considerations

Everything above applies on mobile too, but phones introduce their own set of friction points. Touch targets that are too small. Input types that summon the wrong keyboard. Labels that disappear when the virtual keyboard pushes content up.

WCAG 2.2 Success Criterion 2.5.8 (Target Size Minimum) requires interactive elements to be at least 24x24 CSS pixels, with a Level AA target of 44x44 pixels. On a phone, that 44px target isn't generous. It's the bare minimum for a thumb to hit accurately. If your form inputs, checkboxes, or radio buttons are smaller than that, users with motor impairments will struggle to tap the right one.

Using the correct type attribute on inputs makes a measurable difference on mobile. type="email" gives the user a keyboard with the @ symbol readily available. type="tel" shows a numeric keypad. type="url" includes the forward slash and .com shortcut. These aren't cosmetic touches. They reduce input errors because users aren't hunting for special characters on the wrong keyboard layout.

And labels matter even more on mobile than on desktop. The virtual keyboard can consume half the screen. If the label was a placeholder that vanished when the user tapped the field, they now see a flashing cursor in a blank box with a keyboard covering whatever context existed below. Visible labels that sit above the input stay on screen regardless of keyboard state. This is also why we build our own sites with persistent labels as a baseline, not a nice-to-have.

If your website has any form at all, it's worth running through the checklist above. We put together a broader set of tests in our accessibility checklist for business owners, and our piece on why accessibility matters covers the business case for getting this right. But forms are where it gets personal. Either a person can submit their information, or they can't. There's no partial credit. The HTML patterns that make forms accessible have existed for decades. They're well documented, well supported, and none of them make your forms uglier. They just make them work for everyone.


Sources

Get In Touch

Let's talk about your project.

Whether you have a clear scope or just a rough idea, we'd love to hear from you. Tell us what you need and we'll get back within one to two business days.

Not sure where to start? Take our 2-minute assessment and get a personalized recommendation before reaching out.

Rather see our thinking first? Include your current website in the form and we’ll review it before we reply — your response comes back with the three highest-impact things we’d fix, not a sales pitch. If you’d like to walk through them together after, we’re happy to.

Calgary, Alberta, Canada

Response within 1–2 business days

Tell us about your project

The more detail you share, the better we can understand how to help.