Skip to content

JavaScript Tutorial Forms and Validation

Accessible Form Validation

Lesson 129 of 258 2 min read By Deepak Paswan Updated Oct 4, 2026

Validation is only useful if everyone can understand it. People who use screen readers or only the keyboard need the same information that sighted mouse users get from a red border.

A checklist

  1. Every field has a visible <label>, connected with for and id.
  2. Error messages are real text, near the field. Do not rely on color alone.
  3. Link the message to the field with aria-describedby, so screen readers read it with the field.
  4. Mark the field invalid with aria-invalid="true".
  5. Announce errors: a summary with role="alert", or aria-live, so they are spoken when they appear.
  6. Move focus to the first invalid field (or the summary) after a failed submit.
  7. Say how to fix it, not only what is wrong.
  8. Mark required fields in text too (for example “required”), not only with a star colour.

Putting it together

index.html
HTML
<style>
  .field { margin-bottom: 10px; }
  .error { color: #b00020; font-size: 14px; margin: 2px 0 0; }
  input[aria-invalid="true"] { border: 2px solid #b00020; }
</style>
<form id="f" novalidate>
  <div role="alert" id="summary" tabindex="-1"></div>
  <div class="field">
    <label for="email">Email (required)</label><br>
    <input id="email" type="email" aria-describedby="email-help email-error" required>
    <div id="email-help" style="font-size:13px;color:#555">We only use it to send your receipt.</div>
    <div id="email-error" class="error"></div>
  </div>
  <button>Send</button>
</form>
<script>
  const form = document.querySelector("#f");
  const email = document.querySelector("#email");
  const error = document.querySelector("#email-error");
  const summary = document.querySelector("#summary");

  form.addEventListener("submit", event => {
    event.preventDefault();
    const bad = !email.validity.valid;

    email.setAttribute("aria-invalid", String(bad));
    error.textContent = bad ? "Enter an email address like name@example.com." : "";
    summary.textContent = bad ? "There is 1 problem with the form." : "";

    if (bad) email.focus();
  });
</script>

Why each piece matters

  • aria-describedby makes a screen reader read the help text and the error after the field name.
  • aria-invalid="true" tells assistive technology the value is not valid.
  • role="alert" makes the summary spoken as soon as its text changes.
  • Focusing the first bad field saves a keyboard user from tabbing through the whole form.

Placeholders are not labels

A placeholder disappears when typing and often has low contrast. Use a real label.

Do not move focus on every keystroke

Validating while the user types and jumping around is disorienting. Move focus only after a submit attempt.

Test it

Try your form with the keyboard only (Tab, Shift+Tab, Enter), and with a screen reader if you can. If you cannot complete the form without a mouse, fix that first.

info
Related reading
Accessible Form Styling in the CSS course covers the visual side, and Dynamic Content Announcements explains aria-live in more depth.