Site Implementation

Accessible Forms: Labels, Errors, Validation

Accessible Forms: Labels, Errors, Validation

Light coloured toolbar against a dark background
Light coloured toolbar against a dark background
Light coloured toolbar against a dark background
Light coloured toolbar against a dark background

Clear labels, sensible validation, and helpful error messages make forms faster for everyone—and they reduce abandonment. Use the patterns below to meet WCAG 2.2 AA, lift conversions, and keep content teams moving. Pair solid form foundations with Adjustable to give visitors personal controls (contrast, text size, motion) that boost completion rates.

Core principles (what “good” looks like)

  • Every input is labelled (programmatically, not just visually).

  • Instructions precede the field; required status is clear.

  • Validation timing is humane (on blur or submit, not on every keystroke).

  • Errors are specific, polite, and linked to fields.

  • Focus moves to the first error; messages are announced to assistive tech.

  • Keyboard access and visible focus everywhere.

Labels & associations

  • Use <label for="id"> for single inputs.

  • Group related controls (radio/checkbox sets) with <fieldset><legend>.

  • Put helper text before the input or associate it via aria-describedby.

Example: single input with helper text

<label for="email">Email address</label>
<p id="email-hint">We’ll send your receipt here.</p>
<input id="email" name="email" type="email" autocomplete="email" aria-describedby="email-hint"

Example: radio group

<fieldset>
  <legend>Preferred contact method</legend>
  <div>
    <input type="radio" id="contact-email" name="contact" value="email">
    <label for="contact-email">Email</label>
  </div>
  <div>
    <input type="radio" id="contact-phone" name="contact" value="phone">
    <label for="contact-phone">Phone</label>
  </div>
</fieldset>

Do not replace labels with placeholders; placeholders disappear on entry and fail contrast in many themes.

Required fields & instructions

  • Indicate required fields in the label (e.g., “Email address (required)”) or provide a legend-level note.

  • Avoid asterisks without explanation.

  • Provide format tips up front (e.g., “UK postcode, e.g., SW1A 1AA”).

Validation timing (be humane)

  • Validate on blur and on submit—don’t block typing.

  • Avoid live, per-keystroke scolding.

  • Prevent submission if invalid, then focus the first error.

Error messages & announcements

  • Be specific and actionable (“Enter a valid email like name@domain.com”).

  • Tie the message to the input with aria-describedby.

  • Add a polite live region for dynamically inserted errors.

Example: error pattern

<form aria-describedby="form-errors" novalidate>
  <div id="form-errors" aria-live="polite"></div>

  <label for="email">Email address</label>
  <span id="email-error" class="error" hidden>Enter a valid email like name@domain.com</span>
  <input id="email" name="email" type="email" autocomplete="email"
         aria-describedby="email-error" aria-invalid="true">
</form>

When you surface the error, remove hidden, set aria-invalid="true", and move focus to the first erroneous field or a summarising error panel that links back to fields.

Autocomplete, input modes & helpful defaults

  • Use autocomplete to speed completion: name, email, tel, address-line1, postal-code, cc-number, etc.

  • On mobile, use input modes: type="email", type="tel", inputmode="numeric" where appropriate.

  • Prefill sane defaults and preserve user input on failed submit.

Keyboard, focus & sequencing

  • Tab order follows visual order; no hidden focus traps.

  • Focus is always visible on inputs, buttons, and custom controls.

  • When modals or inline steppers show form content, trap focus inside and return it on close.

Contrast & hit areas

  • Labels, inputs, and errors meet AA contrast (including placeholder text).

  • Tap targets are comfortable on mobile; spacing prevents accidental taps.

Pattern library (copy/paste)

Accessible inline error (JS snippet)

function showError(input, message) {
  const id = input.id;
  const err = document.getElementById(`${id}-error`);
  if (err) {
    err.textContent = message;
    err.hidden = false;
    input.setAttribute('aria-invalid', 'true');
    input.setAttribute('aria-describedby', `${id}-error`);
  }
}

function clearError(input) {
  const id = input.id;
  const err = document.getElementById(`${id}-error`);
  if (err) {
    err.hidden = true;
    input.removeAttribute('aria-invalid');
    input.removeAttribute('aria-describedby');
  }
}

Error summary linking to fields

<div id="error-summary" aria-live="polite" role="alert">
  <p><strong>There’s a problem</strong></p>
  <ul>
    <li><a href="#email">Enter a valid email address</a></li>
    <li><a href="#postcode">Enter a UK postcode, e.g., SW1A 1AA</a></li>
  </ul>
</div>

Copy-paste checklist (Markdown)

- [ ] Each input has a <label for="…">; groups use <fieldset><legend>

Quick QA routine (10 minutes)

  1. Keyboard pass: Tab through every control; submit an empty form; check focus and messages.

  2. Screen reader skim: Confirm labels read correctly; errors are announced; summary links work.

  3. Mobile check: Input modes, target sizes, and autocorrect behaviour.

  4. Edge cases: Required + optional interplay, postcode/email format, international inputs.

How Adjustable helps

After you fix forms structurally, Adjustable helps more people complete them:

  • Text-size and contrast controls improve readability and reduce mistakes.

  • Reading aids and motion preferences reduce fatigue during long forms.

  • Simple install, visible inclusivity—ideal for Marketing Managers and Website Owners.

Try Adjustable

FAQs

Do placeholders replace labels?
No. Placeholders disappear and aren’t reliably announced. Always provide a visible, programmatic label.

When should validation run?
On blur for a gentle nudge, and on submit to catch everything. Avoid per-keystroke errors.

How do I announce errors?
Associate messages with fields via aria-describedby, set aria-invalid="true", and use a polite live region or an error summary with links.

What about multi-step forms or modals?
Move focus into the step or dialog, trap focus while open, and return it to the trigger on close. Maintain state between steps.

Next steps

Share:

Facebook logo
LinkedIn logo
Instagram logo
X logo
Email icon