Accessible Forms: The Fixes That Matter Most
Forms are where accessibility problems cost the most, because a visitor who cannot complete a form cannot buy, book or contact you. The encouraging part is that most failures come from a short list of causes, and the fixes are mostly plain HTML. This article collects the changes we would make first when reviewing a contact form, a checkout step or a newsletter signup.
Use the native element before inventing one
A styled div that looks like an input is not an input. Screen readers may not announce it as one, clicking its text does not focus anything, its value is not submitted by default, and mobile devices cannot show the right on-screen keyboard for it. Native controls bring all of that for free. The same logic applies to buttons: a real button is focusable and operable with Enter and Space out of the box.
If you truly must build a custom widget, MDN’s keyboard guidance gives the checklist: it must be reachable with Tab, it must respond to keyboard events as well as pointer events, and it must show a visible focus style. A tabindex="0" places an element in the natural order; values above zero should be avoided because they pull focus out of the reading order and confuse people.
<!-- Prefer this -->
<button type="button" class="save">Save</button>
<!-- Only if you cannot: it needs role, tabindex and key handling -->
<div role="button" tabindex="0" class="save">Save</div>
Label every control, and put hints where they can be linked
Each input needs an associated label. Matching the label’s for to the input’s id does two things at once: assistive technology announces the label, and clicking the label focuses the field, enlarging the target. For format rules and help text, place the hint next to the field and connect it with aria-describedby.
<label for="pw">Password</label>
<input id="pw" name="pw" type="password"
minlength="8" required
aria-describedby="pw-hint">
<p id="pw-hint">Use at least eight characters.</p>
Smashing Magazine’s validation guide advises keeping the label separate from hints and errors rather than wrapping everything in the label element, since that makes announcements noisier and tests harder. Also use the correct type and inputmode so phones present the right keyboard.
Make errors perceivable, specific and linked
A red border alone fails people with colour-vision differences. Pair colour with an icon and, above all, text that says what is wrong and how to fix it, in plain language. Then link that text to the field and mark the field as invalid:
<label for="email">Email</label>
<input id="email" name="email" type="email"
aria-invalid="true"
aria-describedby="email-error">
<p id="email-error">Enter an address like [email protected].</p>
The guide recommends aria-describedby over aria-errormessage for now, because support for the latter among screen readers is weak. It also points out that if an error text changes while the field already has focus, the new text is not re-announced until the field is focused again.
Decide how errors are announced on submit
The two guides we read differ in detail but agree on the principle: do not flood the screen reader. For a long form, show a summary near the submit button, announced once, and keep individual inline messages quiet. For a short form such as a login, moving focus to the first invalid field works well, because its linked error is read as soon as it receives focus. Whichever pattern you pick, avoid making many aria-live regions fire at the same time, since usually only the first is reliably heard.
Also be wary of disabling the submit button until everything is valid. Visitors may not understand why it is inactive. Letting them press it and then explaining what needs attention is kinder and clearer.
Keep focus visible
Never remove the outline without replacing it. The :focus-visible selector lets you show a strong indicator for keyboard users without adding it on every mouse click.
:where(a, button, input, select, textarea):focus-visible {
outline: 3px solid #005fcc;
outline-offset: 2px;
}
Make sure the visual order matches the DOM order. If CSS reorders fields on screen, keyboard and screen reader users meet them in a different sequence from sighted users.
Test with more than a scanner
Automated checkers are a good first pass, but Smashing’s article estimates they catch only about 20 to 25 percent of issues and miss most interactive behaviour. web.dev suggests a short manual routine instead: complete the form using only the keyboard, run it with a screen reader such as NVDA on Windows or VoiceOver on Mac, and zoom the page to 200 percent to see what breaks. Doing this once per template catches most of what scanners miss.
If you would like help auditing a theme or store checkout against these points, see our services; accessible markup is also something we aim for in our themes.