A form error gives no way to recover
A red border appears after submission, but the field has no explanation.
The problem
People see that something went wrong, but not what to change.
What happens The border turns red. Nothing else. Read moreRead less
Submit an email form with an invalid value. The page stays in place and the field border turns red. The value is preserved, but there is no message telling the user what to change.
Why it fails Only color marks the error. Read moreRead less
Only color identifies the failure. The input has no linked error message, and returning to it with a screen reader provides no correction guidance.
Who is affected Anyone who misses the color, or the format. Read moreRead less
People who cannot perceive the color change may not know submission failed. Others can see the error but still do not know the required format.
What automation and AI miss The label passes. The failed state goes untested. Read moreRead less
Static checks may see a valid label and miss the failed-submission state. AI can add an alert, but repeated announcements or focus movement on every keystroke can make recovery harder. Exercise the full correction cycle.
Try it
Submit it empty and find what to fix.
Submit the form empty, then listen or look for what went wrong.
A recreated demo. The broken version is intentionally inaccessible.
The fix
Say what went wrong in words, next to the field, and lead people to it.
- 1Link each message to its field with
aria-describedbyandaria-invalid. - 2After a failed submission, move focus to the error summary or the field.
- 3Keep what people typed, and validate again on the server.
<input name="email" placeholder="Email" class="invalid"><label for="email">Email address (required)</label><p id="email-hint">Use the address where you want to receive updates.</p><input id="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 [email protected].</p>html
<section id="error-summary" tabindex="-1" aria-labelledby="error-title"> <h2 id="error-title">There is a problem</h2> <ul><li><a href="#email">Enter a valid email address</a></li></ul></section>js
// Run after rendering the unsuccessful submission response.document.getElementById('error-summary')?.focus();Why this works
The placeholder disappears during typing, and the class alone communicates no accessible error message. A border color cannot explain the required correction.
This is the rendered state after validation has found an error.
Set aria-invalid="true" when an error has been detected. Once corrected, remove that attribute or set it to false, remove the old message, and remove its ID from aria-describedby. Keep the hint association.
Do not mark untouched fields invalid on page load. Keep the error text visible next to the field, not only in a tooltip, and make sure it is more than a color: an icon or the word "Error" helps people who cannot see the red.
Make a failed submission easy to navigate
For a form with several errors, put a summary before the fields. Render it after an unsuccessful submission and move focus to it once it exists. Its links take the reader directly to affected controls.
For a form with a single field, moving focus to that field is enough. Do not move focus on every keystroke. Avoid combining forced focus with several assertive live announcements: repeating the same error can make the form harder to understand.
Preserve the user's work
Validate again on the server. Return useful field errors while preserving appropriate entered values. Escape values before inserting them into HTML. Never refill a password or other sensitive field merely to preserve a form.
When submission succeeds, replace the error state with a clear confirmation. If the confirmation updates the current page without moving focus, an appropriate status region can announce the result.
Verify the fix
6 checks, no mouse.
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the shared form component changes.
Limits
This is a starting pattern, not a guarantee for every application. The snippets omit application-specific data handling. Test the complete journey with your target assistive technology.
This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.
References: www.w3.org
Keep this fixed as your code changes.
Turn guides like this into rules your coding agents and CI follow, then have the result retested with a keyboard and screen readers.