Building Validation That Everyone Can Use

Every form field we build brings the same hidden question: when something goes wrong, will every user actually know it? Accessible validation isn't a single feature you bolt on at the end—it's a set of small decisions made while structuring the field, announcing its requirements, and surfacing its errors. The line between a usable form and an inaccessible one is often drawn in those details.

Before touching validation logic, it's worth separating two ideas that get mixed up constantly. Usability is about how easy an action is to perform—writing clearer labels, ordering questions logically, making error recovery feel natural. Accessibility is about whether the content can be reached at all by someone using assistive technology. A perfectly usable form that a screen reader user can't interpret isn't accessible; an accessible form that's awkward to navigate isn't usable. Both have to hold.

The Foundations of Accessible Forms

Two fundamentals underpin every accessible form, even before validation is considered:

  • Keyboard navigation. A person who doesn't use a mouse must be able to reach every control with the Tab key. That starts with a visible, compliant focus indicator on each interactive element.
  • Accessible names. Every field needs a label that assistive technology can associate with it. Screen readers announce the field's accessible name so the user knows what they're interacting with.

Screen readers are not a monolith. NVDA pairs best with Firefox, while VoiceOver works most reliably with Safari, and their behavior differs in shortcuts and announcements. That variation doesn't excuse building without shared fundamentals—those are supported across the board. What it does mean is that we lean on well-established patterns rather than experiments.

Describing The Field

The label is the first instruction a user gets. When the label isn't enough, a description fills the gap. Sighted users see that description; screen reader users need it announced when they focus the input. That connection is made with aria-describedby, which takes an id pointing at the element holding the description text.

Screen readers announce the field's accessible name, pause, then read the description. For an address field with a description that reads "Remember to include the door and apartment," VoiceOver says roughly: "Your address, input [pause] Remember to include the door and apartment."

aria-describedby is for supplementary info. The accessible name—normally the <label>, or aria-labelledby or aria-label as alternatives—is the critical hook that identifies the field. Descriptions come after, with a pause that some screen readers let users lengthen or even turn off entirely.

When Instructions Get Complicated

There's a future aria-description attribute, but it isn't supported across screen readers yet, so aria-describedby remains the working tool. And even that tool has limits: aria-describedby announces the connected content as plain text. Any semantics inside—lists, links, headings—are flattened into a single stream of words.

For a field whose help text includes a list of requirements or a link to more details, pulling all of that into aria-describedby makes for a wall of unparsed text. A better pattern: keep a short SR-only hint in the description association, and let the full instructions exist as separate focusable content. The hint can read "Includes a maximum of 80 characters. Read the field instructions." The user then decides whether to move to the full text themselves.

To expose that hint to screen readers without creating duplicate announcements while tabbing through the page, the hidden attribute is the right tool. Hiding the element from the accessibility tree prevents a second reading as the user moves to the next field, but the node's content remains usable as input.

The takeaway: point aria-describedby at a short pointer to the fine print—never at the full document of instructions.

Marking The Field As Required

The visual cue for a required field is ubiquitous: the red asterisk. Sighted users recognise it instantly. Screen reader users get "Address, star, edit text"—at best. Some readers ignore the symbol entirely depending on their verbosity settings.

A common remediation is to hide the asterisk from assistive technology using aria-hidden="true". The screenshot of a form shows the visual asterisk is gone from the accessibility tree, so VoiceOver simply reads "Address, edit text." But removing it visually leaves nothing semantic in place.

The alternative is the native required attribute, which provides the semantics ("Address, required, edit text") and also alters browser behavior. When a required field is empty, the browser blocks submission with a native tooltip and focuses the offending field.

That default validation is frequently unusable: it collides with a site's design system, may vanish before a user can parse it, and its presentation can't be styled or controlled. When you need custom validation, keep the requirement semantics but ditch the browser's behavior by switching from required to aria-required.

Here is how the two attributes divide the work:

  • required adds semantics, native validation on submit, focus to the first invalid field, and a default visual marker in some browsers.
  • aria-required only adds semantics to the accessibility tree.

ARIA attributes never restyle or manage focus or behavior—they only extend semantics. That is the core reminder when a form needs both accessible meaning and custom presentation.

One neat hack to keep the native required attribute inside a form that validates custom: add novalidate to the <form> element, which disables the browser's validation while keeping semantics in place. But aria-required keeps the requirement logic in the field component rather than depending on the parent form—often the cleaner isolation.

Marking Invalid Fields Clearly

Relying solely on color to indicate an invalid field fails WCAG 1.4.1 Use of Color, since how people perceive color varies widely. A more inclusive approach pairs the color with an icon and, critically, a visible text message that explains why the field is invalid.

Error messages themselves are a usability concern. They should state what went wrong and, where possible, how to fix it, without technical jargon. For accessibility, the invalid state needs to be announced to screen readers. This is done with the ARIA attribute aria-invalid="true" on the field, which the screen reader will announce when the input is focused. To also surface the error text, use the aria-describedby attribute to point to the error message element.

When a field has both a description and an error message, you can pass multiple IDs to aria-describedby. This ensures the screen reader announces both elements' contents in the order they are listed. However, note that dynamically changing the IDs or content of these referenced elements will not trigger a re-announcement while the input is focused.

Though a dedicated ARIA attribute, aria-errormessage, exists, it lacks broad screen reader support. Its relative, aria-describedby, remains the more reliable choice. Sites such as A11Ysupport track support for ARIA features across screen readers and can help verify compatibility.

Since content added or changed dynamically via JavaScript will not be announced automatically, a technique called Live Regions is required. The aria-live attribute turns an element into a region that screen readers will actively monitor for changes.

When Validation Happens

The static examples above need to be applied dynamically in real forms. Three common validation patterns exist: instant (on every value change), afterward (on blur), and submit (on form submission). Any of these can be made accessible with the right implementation.

Instant Validation

In this pattern, the field is checked on every keystroke, and the error becomes visible immediately. To ensure the screen reader announces the error right away, the error element must be a Live Region using aria-live="assertive". Without it, the message goes unheard unless the user manually navigates to it. While an input is valid, the aria-invalid attribute can be removed from the DOM or set to "false". Also note that altering aria-describedby IDs while an input is focused won't trigger an announcement; the input will require a re-focus. This pattern can feel annoying if the error appears before the user finishes typing.

Afterward Validation

The error appears only when the user leaves the field. As with instant validation, an aria-live region is necessary so the error is read before the user moves on to the next field. One preference is to trigger the on-blur validation only if the input’s value changed. This avoids firing an error when a screen reader or keyboard user is traversing fields to survey the form, or when a sighted user accidentally clicks a field while scrolling.

Submit Validation

How errors appear after form submission often depends on the form’s length. In long forms, an error summary is useful, generally placed right before the submit button so it's in view. The summary should be concise, telling the user how many fields are invalid. While you could also show inline errors on all invalid fields, attaching aria-live to each one risks the screen reader announcing every error at once, which is jarring and potentially incorrect since some screen readers announce only the first region they encounter. Consequently, the aria-live is placed solely on the summary. Rather than adding live regions to the visual list, it’s better to direct announces to a hidden summary with the .sr-only class. A separate visual summary element can then update without cluttering the screen reader experience.

Short forms, like logins, often skip the visual error summary. An effective alternative is to place an invisible error summary with .sr-only for assistive tech, or automatically focus the single invalid field via HTMLElement.focus(). Focusing the field inherently triggers the screen reader to announce the state and its error message due to aria-describedby; no supplementary aria-live region is needed to force the announcement.

Regardless of placement (top of form, before submit button), focus behavior (whether to move focus to the summary or the first invalid field), or whether the summary includes links for each error, all these patterns meet accessible standards if well implemented and clearly understandable.

Testing Validations

Automated scans do not provide sufficient assurance for interactive elements; they typically detect only 20-25% of accessible issues, an even smaller slice for highly interactive content. Writing unit tests can verify that ARIA attributes are correctly positioned, but that level of detail doesn't prove real-world screen reader consistency.

Accessibility relies heavily on manual testing. Since individual experiences differ, the most effective strategy is a mixture of audits: automated tools, unit testing, manual interactions, and personalized testing. A prime practice is familiarizing yourself with a screen reader while building new interactive components to understand how your work operates and to develop better web creators overall.

Field Considerations And Trade-offs

Avoid auto-focusing the first invalid input by default, since this might interrupt users while they're reviewing errors. Let people scan the summary and navigate to the relevant field themselves.

Similarly, resist the urge to wrap every related element inside the <label> tag. Over-eager wrapping confuses assistive tech, can create conflicts with interactive controls inside the label, and complicates automated tests, such as those in the Testing Library suite. Keeping each label focused and isolated provides finer control over the announce order and organization of information.

Disabling the submit button to prevent invalid submissions undermines the user’s understanding. A disabled button offers no feedback on why a form cannot be submitted and imposes unnecessary cognitive strain. A better path is allowing submission and showing a clear error message explaining what was wrong. In cases where a disabled state is mandatory, fashion it to be inclusive so all users can perceive and interact with it.

Strong user experience offers options. Just as buildings provide both stairs and lifts, adaptable web patterns support diverse needs and preferences. Instead of enforcing a single solution, look for ways to offer accessible alternatives that complement each other. No single experience can serve everyone — but creating well-rounded patterns increases the chances that most users find their own path.

Validation Is More Than ARIA

Field validation remains one of those areas where HTML alone doesn’t cover modern web patterns, so ARIA is often the missing piece. The attributes that matter most for accessible form feedback are few but powerful:

  • aria-required signals that a field is mandatory.
  • aria-invalid flags the field as errored.
  • aria-describedby ties the input to its instructions and error text.
  • aria-live announces errors that appear after user interaction.

Accessibility, however, is about people rather than markup. Beyond these attributes, check your color contrast, icon clarity, field instructions, and the validation logic itself. A form that fails to submit should still be usable by everyone.

WCAG Input Assistance

The practices above map directly to the WCAG success criteria under “3.3 Input Assistance”:

Working through these guidelines makes one thing clear: accessibility extends beyond strict compliance. WCAG is called “guidelines” rather than “rules” intentionally. They give you a strong foundation, but if your user research suggests a deviation, don’t hesitate to question the norm and design outside it. Guidelines evolve when practitioners do.

Acknowledgments

Thanks to Ben Myers, Kitty Giraudel, and Manuel Matuzović for the technical review.