On this page
A form is accessible only when people can correct it, not merely when its fields have labels. Someone submitting a benefits application needs to know which answer failed, why it failed, and how to return to that answer without losing the rest of the application. Treat validation as a recovery workflow with specific states and predictable navigation.
Define useful instructions before writing validation
Start by listing what each field requires and when users need to know it. A password policy belongs before submission; an unavailable appointment can only be confirmed after checking availability. Separate input instructions from errors so people are not forced to discover requirements through failure.
Use the word counter to compare alternative instructions and remove unnecessary wording. It does not measure accessibility or comprehension. Its useful role is highlighting sprawling messages that might hide the corrective action. Prefer a concrete instruction such as Enter a date in day, month, year order over a generic Invalid input message.
The WAI forms tutorial explains the relationship between labels, grouping, instructions, and notifications. Make these relationships part of the component specification rather than adding them after the visual design has been approved.
Connect labels, hints, and errors programmatically
Every control needs an accessible name. A visible label associated through matching for and id attributes usually provides the strongest foundation because it benefits screen readers, pointer users, and speech input together. Placeholder text is not an adequate replacement: it disappears during entry and often has poor contrast.
Use descriptive relationships for supplemental information. When an error exists, connect it to the relevant control and expose the invalid state. Keep identifiers unique, including when several instances of the same field component appear on one page.
<label for="contact-email">Email address</label>
<p id="email-hint">We send the receipt to this address.</p>
<input id="contact-email" name="email" type="email"
autocomplete="email" required aria-invalid="true"
aria-describedby="email-hint email-error">
<p id="email-error">Enter an email address, such as name@example.com.</p>This represents an existing error, not the initial untouched state. Do not mark every empty required field invalid when the page first opens. Use aria-invalid when validation has actually identified a problem, and remove or update it after correction. Keep server-side validation even if the client provides immediate feedback; browser code can be bypassed or interrupted.
Choose validation timing that supports completion
Validating on every keystroke can announce errors while a person is still entering a perfectly reasonable partial answer. A date field containing the first two digits is incomplete, not necessarily wrong. Start with submission validation, then selectively add earlier checks where they provide clear value.
For expensive checks, such as username availability, consider checking after the user leaves a field or explicitly requests verification. Preserve the entered value while waiting. If the request fails, distinguish a network problem from an invalid answer; otherwise people may keep changing correct information to satisfy a broken service.
Native validation is useful but needs testing across supported browsers and languages. Custom validation gives more control over presentation, but also makes your team responsible for error identification, focus behavior, accessible relationships, and message timing. Do not disable browser validation merely because its popup looks different from the design system.
The MDN form validation guide covers the constraint validation APIs and the distinction between browser and server responsibilities. If you use novalidate, document exactly which custom behavior replaces the browser's feedback and how it is tested.
Build an error summary with working destinations
After an unsuccessful submission, present a concise error summary near the start of the form. Give it a heading, explain that submission did not complete, and provide links to affected fields. For a long application, this lets users understand the scope of the problem before navigating through several sections.
<section tabindex="-1" id="form-errors"
aria-labelledby="form-errors-title">
<h2 id="form-errors-title">Check two answers</h2>
<ul>
<li><a href="#contact-email">Enter an email address</a></li>
<li><a href="#start-date">Choose a start date</a></li>
</ul>
</section>Move focus to this summary after rendering it when that is the chosen submission pattern. Verify that activating each link brings the corresponding control into view and makes it practical to edit. For composite fields, the destination may need to be the first relevant input rather than a decorative group wrapper.
Avoid making the same summary an assertive live region while also focusing it unless testing demonstrates a need. Duplicate announcements can make a simple failure sound like several failures. Inline errors should remain available when users revisit fields, even after the initial announcement has finished.
Test recovery, not just the error appearance
Submit an empty form, an almost-correct form, and a form with a server-side failure. In each case, check whether values remain intact, messages identify the actual problem, and users can reach the correct answer without guessing. Test radio groups, checkboxes, file uploads, and repeated address sections separately.
Check the visual error state with the contrast checker. Red alone does not identify a failure for everyone, so include text and a structural cue as well. If an icon is decorative, prevent it from cluttering the field's accessible name. If an error mentions a format, include an example that matches the real accepted format.
Use keyboard-only and screen-reader passes to verify recovery paths. Listen to the field name, current value, hint, and error together. Long descriptions that sound reasonable in isolation can become exhausting when repeated after every focus movement. Keep permanent hints stable and error messages specific.
The WCAG error suggestion guidance distinguishes identifying an error from helping resolve it. For payments and other consequential submissions, also review confirmation and reversal requirements. Keep shared patterns in your accessibility documentation so teams do not invent incompatible recovery behavior for each form.
Walk through a delayed server rejection
Use a realistic appointment form with contact details and a selected time. Submit valid-looking answers, then have the test server report that the selected appointment has just been taken. The message should explain that availability changed, preserve the contact details, and offer a practical route to choose another time. Calling the whole submission invalid would obscure which answer needs attention.
Test asynchronous validation with deliberately reordered responses. If a person changes an email address while an earlier availability check is pending, the old result must not mark the new value invalid. Associate results with the value or request that produced them, and discard stale responses. Keep any checking status concise and avoid repeatedly interrupting screen-reader output while the person types.
Finish by correcting the appointment and submitting again using only the keyboard. Confirm that obsolete error text and invalid states disappear, that the error count matches the remaining problems, and that success is perceivable without relying on a color change. Include a second submission after a network timeout: recovery should not require retyping the whole form or accidentally create duplicate bookings. Document this scenario as an acceptance test rather than only storing a screenshot of red fields.
Key takeaways
- Explain requirements before users encounter validation failures.
- Associate labels, hints, and current errors with each control.
- Validate at moments that help completion rather than interrupt typing.
- Provide a focused summary with reliable links to affected answers.
- Test value preservation, announcements, and successful correction end to end.
Reach designers & developers at work
Put your product next to the tools people use every day. One sponsor per placement, no tracking pixels.
Become a sponsor