Accessible business forms

Wire field errors to the controls they describe

Keep labels, hints and validation messages connected in the document. Then handle focus and submission state so users can recover without losing their answers.

In this article

Start with a stable field contract

Give each control a stable identifier and a visible label. Connect persistent instructions and any error message through the control's accessible description. Use grouping semantics for related radio buttons or checkboxes.

This small HTML example illustrates the relationship after server validation rejects an email address. It is not a complete form implementation.

HTML example
<label for="contact-email">Work email</label>
<p id="contact-email-hint">We will use this to reply to your request.</p>
<input id="contact-email" name="email" type="email"
  autocomplete="email" aria-invalid="true"
  aria-describedby="contact-email-hint contact-email-error" />
<p id="contact-email-error">Enter an email address in the form name@example.com.</p>

When the field becomes valid, update the invalid state and remove the obsolete error association. Keep the useful hint. Do not leave hidden stale errors attached to a control after correction.

Return structured validation results

Have the server return field-specific errors and a separate form-level error when necessary. Map them to stable field names rather than displaying an unstructured exception string.

Use a summary after failed submission with links that move to the relevant controls. Set focus to the summary or another deliberate recovery target appropriate to the form. Test the resulting announcement instead of stacking several live regions that repeat the same message.

Retain entered values across the failure. For file inputs, explain any browser limitation and preserve uploaded references where the workflow safely supports it.

Keep asynchronous states distinct

Represent idle, submitting, validation failure, uncertain network outcome and confirmed success separately. A generic loading boolean cannot explain all of them.

Guard repeated activation on the client and enforce the operation's duplicate-handling contract on the server. Keep the action label readable while work is pending and provide a visible recovery step when it cannot finish.

Verify the component in context

Place the form inside the actual page shell with navigation, footer and mobile keyboard behaviour. Check that sticky elements do not obscure the focused control or summary.

Exercise both client and server validation. A polished client-only path can conceal a broken response mapping that appears only when the server rejects a real submission.

Primary sources

W3C WAI: forms tutorialW3C WAI: user notifications

References checked 11 September 2026.