Three jobs, and red text does part of one
An error state has to do three separate things, and conflating them is how forms end up like this. It has to identify which field is wrong, describe what to do about it, and announce that something happened at all. Colour attempts only the first and fails it for anyone who cannot perceive the hue, which is why WCAG 1.4.1 Use of Color exists: colour may reinforce a distinction but may never be the only means of conveying it. So the first change is that every error acquires text, and every red border acquires an icon or a text marker beside the label.
The text also has to say something useful. Success criterion 3.3.1 asks only that the error be identified in text; 3.3.3 Error Suggestion, at level AA, asks that you suggest a correction when you know one. "Invalid date" satisfies neither well. "Enter the date as DD/MM/YYYY, for example 14/03/2026" satisfies both and removes a support ticket.
Associate the message, do not merely place it
Sighted users connect a message to a field by proximity. A screen reader user moving through the form by field does not encounter proximity at all — they hear the field's accessible name, its role, its state, and its description, and a <span> sitting next to the input is in none of those. aria-describedby is what puts it in the description, and it accepts multiple ids so hint text and error text can both be attached.
<label for="email">Email address</label>
<input
id="email"
type="email"
aria-describedby="email-hint email-error"
aria-invalid="true"
/>
<p id="email-hint">We use this for order updates only.</p>
<p id="email-error">Enter an email address in the format name@example.com</p>
Two details in that markup are load-bearing. The label is associated by for, not by wrapping and hoping; a placeholder is not a label and disappears the moment the user types. And aria-describedby references ids that must exist in the DOM at the moment the field receives focus, which means if you render error paragraphs conditionally, the reference is dangling whenever the field is valid. Referencing an absent id is silently ignored, so this fails quietly rather than loudly.
aria-invalid announces, it does not validate
aria-invalid="true" causes assistive technology to announce the field as invalid alongside its name. That is all it does. It applies no constraint, blocks no submission, and adds no styling, though it is a good CSS hook precisely because it is the state of record: styling on [aria-invalid="true"] cannot drift out of sync with what is announced. Remove it, do not set it to "false" and leave it, once the field is corrected.
aria-errormessage exists for exactly this purpose and reads better in the spec, but support across screen readers has been uneven for long enough that aria-describedby remains the reliable choice. If you use aria-errormessage, it is only honoured while aria-invalid is true, which surprises people.
On submit, move focus
The instinct is to put the error summary in a live region and let it be read out. Live regions are the least dependable mechanism in the accessibility toolbox: the container must already exist in the DOM before content is inserted, insertion has to be a genuine mutation rather than a re-render that replaces the node, role="alert" interrupts whatever is currently being spoken, and behaviour still varies by screen reader and browser pairing.
Moving focus is deterministic. Render a summary at the top of the form listing each error as a link to its field, give the container tabindex="-1", and focus it after a failed submit. The user hears the count and the list, can activate a link to jump to a field, and — because focus moved — is now positioned at the top of the form rather than wherever the submit button was. For a single-field error, focusing that field directly is fine and less ceremony.
if (errors.length) {
summaryRef.current.focus();
}
Whatever you do, do not disable the submit button until the form is valid. A disabled button is removed from the tab order and gives no explanation, so a keyboard user reaches the end of the form and finds nothing to do and no reason why. Let the submit happen and report what is wrong.
What a reviewer will cite
Naming criteria precisely is what distinguishes an answer from an opinion. This form fails 1.4.1 Use of Color while colour is the only marker, and 3.3.1 Error Identification if the error is not identified in text. It fails 3.3.3 Error Suggestion where a correction is knowable and not offered, and 1.4.3 Contrast if the red on white does not reach 4.5:1 — which a lot of error red does not.
WCAG 2.2, published in October 2023, adds two more that touch forms directly: 3.3.7 Redundant Entry, which asks you not to make the user re-enter information they already gave in the same process, and 3.3.8 Accessible Authentication, which rules out cognitive puzzles as the only route through a login.
© 2026 Preptima. Originally published at preptima.com (opens in a new tab).