Poor error messages are one of the most frustrating accessibility failures. A screen reader user submits a form, hears nothing, and has no idea what went wrong or where. Or they hear "Error" with no context. This guide covers how to implement error messages that work for everyone.
Error messages must: (1) identify which field has the error, (2) describe what went wrong, and (3) explain how to fix it.
See how 321 websites scored →
View the 2026 ReportThe error message must be programmatically linked to the field it describes. Use aria-describedby to create this link:
<!-- WRONG: error text near field but not linked -->
<label for="email">Email</label>
<input type="email" id="email">
<p style="color:red">Email is required.</p>
<!-- RIGHT: error linked via aria-describedby -->
<label for="email">Email</label>
<input type="email"
id="email"
aria-describedby="email-error"
aria-invalid="true">
<p id="email-error" class="error-message">
Email address is required.
</p>
When the user tabs to the email field, the screen reader announces: "Email, edit text, Email address is required." The user knows exactly what's wrong before they start typing.
aria-invalid="true" signals to AT that the field value is in an invalid state. Set it when errors are present, remove it when the field is corrected:
// On validation failure
input.setAttribute('aria-invalid', 'true');
input.setAttribute('aria-describedby', 'email-error');
errorEl.textContent = 'Email address is required.';
// On correction
input.removeAttribute('aria-invalid');
input.removeAttribute('aria-describedby');
errorEl.textContent = '';
For forms with multiple fields, also show an error summary at the top of the form. Move focus to it after submission fails so screen reader users are immediately informed.
<!-- Empty on page load -->
<div id="error-summary"
role="alert"
tabindex="-1"
hidden>
<h2>Please fix the following errors:</h2>
<ul id="error-list"></ul>
</div>
function showValidationErrors(errors) {
const summary = document.getElementById('error-summary');
const list = document.getElementById('error-list');
// Build error list with links to each failing field
list.innerHTML = errors.map(({ field, message, id }) =>
`<li><a href="#${id}">${field}: ${message}</a></li>`
).join('');
// Show summary and move focus to it
summary.removeAttribute('hidden');
requestAnimationFrame(() => {
summary.focus(); // role="alert" + tabindex="-1"
});
}
When to show inline validation errors:
// Validate on blur, not on every keystroke
emailInput.addEventListener('blur', () => {
validateEmail(emailInput.value);
});
// Debounced for real-time hints (password strength)
let timer;
passwordInput.addEventListener('input', () => {
clearTimeout(timer);
timer = setTimeout(() => updateStrengthIndicator(passwordInput.value), 500);
});
Red text indicates an error to sighted users. But users who are color blind may not perceive red as different from surrounding text. Always pair color with:
<p id="email-error" class="error-message"> <span aria-hidden="true">⚠ </span> Email address is required. </p>
Accessalyze detects missing aria-describedby, aria-invalid, and form label issues automatically.
Scan Your Forms →← Back to Accessalyze · ← Full Form Accessibility Guide
See real website accessibility scores: Browse 244+ free accessibility audits →
Try it yourself
Enter your website URL to get a free accessibility score.