Creating Accessible Web Forms
Boost Usability and Compliance with Practical WCAG and ARIA Techniques

Remember that time you tried to fill out a form on your phone, and the email keyboard didn’t pop up?
Or when you pressed submit, and the error message was somewhere in Paraguay while you’re still staring at the submit button wondering what went wrong?
Yeah, we’ve all been there. And if you’re building forms, you’re either the hero who saves people from this digital hell or… well, the architect of said hell. No pressure.
quoteThis is part 2 of my Web Accessibility series, and today we’re diving deep into forms. Because honestly? Forms are where accessibility goes to die if you’re not careful.
Look, I get it. You’ve got deadlines, your PM is breathing down your neck, and “just make it work” sounds way easier than “make it work for everyone.”
But here’s the thing — making forms accessible isn’t just about checking compliance boxes: It’s about being a decent human who builds stuff that everyone can use.
bonusTL;DR (for my fellow skimmers):
- Labels aren’t optional. Period.
- Error messages should be specific, not cryptic prophecies
- Keyboard navigation or GTFO
- ARIA is your friend, but HTML should be your BFF
- Test with actual humans, including those using assistive tech
noteWCAG 2.2 was released in October 2023 and introduces important new criteria like Focus Not Obscured (Criterion 2.4.11) and Minimum Target Size (Criterion 2.5.8) that improve keyboard and touch usability. These are now part of the AA conformance level. WCAG 3 is still in early working draft stage.
Label and Group Form Elements
Here’s the deal: every form control needs a clear, visible label. Not a maybe-label, not a “placeholder-that-disappears-when-you-start-typing” label. A real label.
Back when I was mentoring students at the coding club in college, one of the most common mistakes I saw was developers treating placeholders as labels. “But it looks cleaner!” they’d say. Sure, until someone with a screen reader tries to use your form and has no idea what goes in that field.
Use the <label> element with a for attribute (or htmlFor in React/JSX) to associate text with inputs.
<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.
importantI just want to repeat myself here.
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-labeloraria-labelledbyif 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>tipIf 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
Think of error messages like GPS directions. “Turn right” is useless if you don’t know where to turn right. “In 500 feet, turn right on Main Street” is helpful.
“Submission failed” is like your GPS saying “Error occurred” and leaving you stranded. Don’t be that GPS.
One mistake I see all the time: vague error messages at the top of the form while users are left guessing which field is actually wrong. 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.
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.
tipIn 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 and NVDA will announce the field as “invalid entry”.
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
tipHot Take from Someone Who’s Built Too Many Custom Dropdowns
That fancy custom dropdown you built? It probably has accessibility issues. That animated checkbox? Keyboard users aren’t fans.
Sometimes the boring
<input>is the hero we need, not the one we deserve.
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.
importantRemember that label text, placeholder hints, and instructions should all have sufficient contrast. WCAG 2.2 Criterion 1.4.3 requires at least 4.5:1 contrast ratio for normal text to be legible.
bonusReact 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
htmlForinstead offor. When managing focus or dynamic content in React, use refs (useRef) and effect hooks to set focus or update attributes programmatically.
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.
Never trap focus in a field—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.
noteWCAG 2.2’s Focus Not Obscured criterion (2.4.11) requires that the 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).
bonusProviding “Skip to Content” links or using landmark roles (like
<main>,<nav>, etc.) helps keyboard users bypass repetitive page elements. You might remember seeing these as small links at the top of pages more often in the early 2010s web—they’re still valuable, just often visually hidden until focused.
Make Forms Mobile and Touch Friendly
Here’s what matters for mobile:
- Responsive forms: Use media queries, flexbox, or CSS Grid to ensure forms work on all screen sizes.
- Large enough touch targets: Buttons need sufficient padding, not just for aesthetics but for usability.
- Touchable controls: Checkboxes, radio buttons, and links all need adequate size.
- Appropriate input types: Use
type="tel",type="email",type="number", etc. to trigger the right virtual keyboard.
importantWCAG 2.2’s Target Size criterion (2.5.8) recommends a minimum of 24×24 CSS pixels for tappable controls at the AA level. 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. Trust me, it’s frustrating when you’re just trying to read the page and suddenly the keyboard’s in your face.
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(androle="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>witharia-labelledby. - ARIA States: Use
aria-required="true"on required fields to indicate necessity (thoughrequiredis usually sufficient), andaria-invalid="true"on fields when validation fails. - Avoid Unsupported ARIA: Note that
aria-errormessage(to point to an error message) exists but has limited browser support as of 2025; it’s safer to stick witharia-describedbyas 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.
The Bottom Line (because we all skip to the end anyway..)
Accessible forms aren’t just about checking compliance boxes (though your legal team will thank you). They’re about being a decent human who builds stuff that everyone can use.
Next time you build a form, think about the person using it on a cracked phone screen on a bumpy bus. Or someone navigating with just a keyboard because their mouse broke. Or your future self trying to fill out a form at 2 AM when you can barely keep your eyes open.
The truth is, accessible forms have higher completion rates. They’re better for everyone, not just users with disabilities. It’s not extra work—it’s good work.
Now go forth and build forms that don’t suck. The internet will thank you. 🚀
P.S. If you’re still using placeholder text as labels, we need to talk.
bonusAdditional Resources:
- WCAG Guidelines: The WCAG 2.2 Quick Reference is your go-to resource for comprehensive accessibility standards. Familiarize yourself with AA requirements (and AAA for additional best practices).
- Learning Resources: WebAIM and The A11y Project provide excellent guides on accessible design patterns and best practices.
- Browser Support: Use Can I Use to check browser compatibility for newer HTML and ARIA features.
- Testing: Test your forms with keyboard-only navigation and screen readers. Involve people with disabilities in testing when possible—their feedback is invaluable.




