What makes an error message accessible?
An error message has to reach the person, point at the right field and say how to fix the problem. On the starting page it does only part of that. A sighted person sees red text under the email box and a red border. A screen reader user hears nothing when the message appears, and when they Tab back to the field they hear “Email, edit text, required” with no hint that anything is wrong.
WCAG 2.2 sets out the pieces. 3.3.1 Error Identification asks that the field in error is identified and the error described in text. 3.3.3 Error Suggestion asks that you suggest a fix when you know one. 4.1.3 Status Messages asks that messages which appear without a focus change can be announced. 1.4.1 Use of Color rules out red as the only signal. The work comes down to three attributes and a few lines of script.
How do you mark a field as invalid?
Set aria-invalid="true" on the field when, and only when, its value is wrong. A screen reader then adds “invalid entry” or similar to what it says for the field. The attribute is a state, so scripts change it: set it after a failed check and remove it, or set it to false, once the value is fixed.
Don’t put aria-invalid="true" in the starting HTML. An empty form that announces every field as invalid before anyone has typed is confusing and discouraging. The browser does not set aria-invalid for you when its own validation fails either, so a custom error flow has to manage it in script.
Keep the visual signal in step. If the border turns red, the attribute should be true; if the attribute is cleared, the border should go back. A CSS rule such as input[aria-invalid="true"] { border-color: #b3261e; } ties the two together so they can’t drift apart.
email.setAttribute('aria-invalid', 'true'); // after a failed check
email.removeAttribute('aria-invalid'); // once it is fixedHow is the message tied to the field?
Give the message an id and point the field’s aria-describedby at it. The message becomes the field’s description: a screen reader reads the name, the role and states, then the description, as in “Email, edit text, required, invalid entry, Enter an email address, like rider@example.com”. The link holds wherever the message sits in the layout, and the person hears it every time they come back to the field.
A field can have more than one description. List several ids, such as a format hint and the error, aria-describedby="email-hint email-error", and they are read in that order. When the message element is empty, there is nothing to read, so it is safe to leave the attribute in place all the time.
ARIA also has aria-errormessage, which is read only while the field is invalid. Screen reader support for it has improved but is still patchier than for aria-describedby, so aria-describedby remains the safer choice in 2026.
<input id="email" type="email" required aria-describedby="email-error">
<p id="email-error" class="error" role="alert"></p>

How do you make the message announced?
A screen reader reads what has focus, plus anything that changes inside a live region. Put role="alert" on the message container, and when the script fills it with text, the screen reader speaks it straight away. role="alert" is assertive: it interrupts whatever is being read, which suits an error the person needs before going further.
The live region must already be in the page, empty, before the text arrives. Screen readers watch regions that exist; one created at the same moment as its message is often missed. That is why the demo keeps an empty <p id="email-error"> in the HTML and only changes its text. A repeated identical message may not be announced again either, so clear the text before setting it when the same error can happen twice.
Use alerts sparingly. One alert for a failed submit is helpful. An alert on every key press, while someone is still typing, is noise. For a quiet success message, such as “Booked. Check your email for the time.”, role="status" is polite and waits for a pause.
Where should focus go after a failed submit?
Move focus to where the person needs to act. With one error, focus the field itself, as the solution does with email.focus(). The screen reader reads the field with its invalid state and its message, and the person can type the fix at once. With several errors, show an error summary at the top of the form and move focus to it, or put it in a role="alert" region as the Break It page does. Revealing a hidden role="alert" element is a common exception to the rule below about live regions already being present: browsers raise an alert event when it appears, and most screen readers speak it.
A good summary has a heading such as “There is a problem with your enquiry” and one link per error, each pointing at its field with href="#phone". Selecting a link moves focus to the field. People who use a screen reader or magnification get an overview first, and everyone can jump straight to each fix. Leave the messages next to the fields as well; the summary is a map, and the inline messages still explain each fix.
<div id="summary" role="alert" hidden>
<h2>There is a problem with your enquiry</h2>
<p><a href="#phone">The phone number needs fixing</a></p>
</div>What should an error message say?
Say what is wrong and how to fix it, in the words people used to fill in the form. “Invalid input” fails on both counts: it doesn’t say which input or what a valid one looks like. “Enter a phone number with at least 10 digits, like 01632 960 123” does both, which is what 3.3.3 Error Suggestion asks.
Avoid blaming language and codes like “Error 422”. Avoid messages that only repeat the label, such as “Phone number is required” when the field is plainly empty; “Enter your phone number so we can call you about the cake” gives a reason too. Keep the example values realistic and obviously fake. And don’t clear the person’s other answers when one field fails, since retyping a whole form is a real barrier for people with motor or memory difficulties.
How do you test error handling?
Submit the form empty, then with one wrong value, then with everything correct. After each try, open the **Screen reader** view. The bad field should read “invalid entry” followed by the message; a fixed field should read normally again. The **Checks** view flags a field marked invalid with no linked message as error-not-linked.
Watch the announcements too. The checks on this page record what live regions say after each action, and a real screen reader will speak the alert in a similar way, though wording and timing vary. Tab back into the field afterwards and listen for the description again. Finally, try the form at 200 percent zoom: messages that appear far from their fields, or in a summary off screen, are easy to miss for people who magnify.
What do AI tools get wrong with errors?
Generated validation code often turns the border red and inserts a message with no aria-invalid, no aria-describedby and no live region, exactly like the starting page. Some create the alert element at the moment of the error, which many screen readers miss, or set aria-invalid="true" on every field when the page loads. Others write vague messages like “Invalid value”. Check for the three attributes, check that aria-invalid is removed when the error is fixed, and read each message aloud to see whether it tells you what to do.
Field notes
- Set
aria-invalid="true"only while a field's value is wrong. - Point the field's
aria-describedbyat the messageidto tie them together. - Use
role="alert"so a screen reader reads the message when it appears. - A good error says what is wrong and how to fix it.
Easy to get wrong
A red class and nearby text are only visual. The attributes put the error into the accessibility tree and announce it.
Many screen readers only announce changes inside live regions that already exist, so a region added with its message is often missed.
Fields announced as invalid before anyone types are confusing and make the whole form sound broken.
A message should say what is wrong and how to fix it. Vague text fails 3.3.3 Error Suggestion when the fix is known.
Questions people ask
Why does the script listen for clicks on the button?
When someone presses Enter in a text field, the browser fires a click on the form’s first submit button before sending the form. One click listener on that button therefore catches mouse, touch, Space, Enter on the button and Enter in a field. A submit listener on the <form> works just as well on a normal page.
Should errors appear while people type?
Usually not. Checking as people type interrupts them with errors for values they haven’t finished. Check when they leave a field or submit the form, and clear the error as soon as the value becomes valid.
Is role="alert" the same as aria-live="assertive"?
Nearly. role="alert" implies aria-live="assertive" and aria-atomic="true", so the whole message is read at once. The live regions lesson covers the differences in detail.
Do I need both an error summary and inline messages?
For a short form with one or two fields, inline messages and moving focus to the first error are enough. For longer forms, a summary at the top with links to each field helps people see how many problems there are.
Does the browser’s built-in validation set aria-invalid?
No. Native validation shows its own message and focuses the field, but it does not add aria-invalid to the HTML. Screen readers do report the native invalid state in some browsers, which is one more reason to test with a real one.