Error messages are crucial for helping users understand and fix problems when interacting with forms and applications. Accessible error messages ensure that all users, including those using assistive technologies, can understand what went wrong and how to fix it.

Why Accessible Error Messages Matter

When users encounter errors, they need clear information about what went wrong and how to fix it. Poor error messages can frustrate users and prevent them from completing tasks. Good error messages ensure that:

  • Screen readers announce errors immediately
  • Users understand exactly what needs to be fixed
  • Error messages are associated with the relevant form fields
  • Users get helpful suggestions for resolving issues

Basic Error Message Structure

The foundation of accessible error messages starts with proper HTML structure:

<div class="form-group">
  <label for="email">Email address</label>
  <input type="email" id="email" name="email" aria-describedby="email-error" />
  <div id="email-error" class="error-message" role="alert" aria-live="polite">
    Please enter a valid email address
  </div>
</div>

Key Points:

  • Use aria-describedby to associate error with the field
  • Use role="alert" for immediate announcements
  • Use aria-live="polite" for non-intrusive updates

Inline Error Messages

For errors that appear next to form fields:

<div class="form-group">
  <label for="password">Password</label>
  <input 
    type="password" 
    id="password" 
    name="password" 
    aria-describedby="password-error"
    aria-invalid="true"
  />
  <div id="password-error" class="error-message" role="alert">
    Password must be at least 8 characters long
  </div>
</div>

Form-Level Error Messages

For errors that affect the entire form:

<form>
  <div class="form-errors" role="alert" aria-live="polite">
    <h2>Please fix the following errors:</h2>
    <ul>
      <li><a href="#email">Email address is required</a></li>
      <li><a href="#password">Password is too short</a></li>
    </ul>
  </div>
  
  <div class="form-group">
    <label for="email">Email address</label>
    <input type="email" id="email" name="email" />
  </div>
  
  <div class="form-group">
    <label for="password">Password</label>
    <input type="password" id="password" name="password" />
  </div>
</form>

Real-time Validation

For validation that happens as users type:

<div class="form-group">
  <label for="username">Username</label>
  <input 
    type="text" 
    id="username" 
    name="username" 
    aria-describedby="username-error username-help"
  />
  <div id="username-error" class="error-message" role="alert" aria-live="polite"></div>
  <div id="username-help" class="help-text">
    Username must be 3-20 characters long
  </div>
</div>

CSS for Error Messages

Proper styling ensures error messages are visually accessible:

.error-message {
  color: #d32f2f;
  font-size: 0.875rem;
  margin-top: 0.25rem;
  padding: 0.5rem;
  border-left: 3px solid #d32f2f;
  background-color: #ffebee;
  border-radius: 0 4px 4px 0;
}

.error-message::before {
  content: "⚠ ";
  font-weight: bold;
}

/* Error state for form fields */
input[aria-invalid="true"] {
  border-color: #d32f2f;
  box-shadow: 0 0 0 2px rgba(211, 47, 47, 0.2);
}

/* Focus state for error fields */
input[aria-invalid="true"]:focus {
  border-color: #d32f2f;
  box-shadow: 0 0 0 2px rgba(211, 47, 47, 0.4);
}

/* Form-level errors */
.form-errors {
  background-color: #ffebee;
  border: 1px solid #d32f2f;
  border-radius: 4px;
  padding: 1rem;
  margin-bottom: 1rem;
}

.form-errors h2 {
  color: #d32f2f;
  font-size: 1rem;
  margin: 0 0 0.5rem 0;
}

.form-errors ul {
  margin: 0;
  padding-left: 1.5rem;
}

.form-errors a {
  color: #d32f2f;
  text-decoration: underline;
}

.form-errors a:hover,
.form-errors a:focus {
  color: #b71c1c;
  text-decoration-thickness: 2px;
}

JavaScript for Dynamic Error Messages

function showError(fieldId, message) {
  const field = document.getElementById(fieldId);
  const errorId = `${fieldId}-error`;
  let errorElement = document.getElementById(errorId);
  
  // Create error element if it doesn't exist
  if (!errorElement) {
    errorElement = document.createElement('div');
    errorElement.id = errorId;
    errorElement.className = 'error-message';
    errorElement.setAttribute('role', 'alert');
    errorElement.setAttribute('aria-live', 'polite');
    field.parentNode.appendChild(errorElement);
  }
  
  // Update error message
  errorElement.textContent = message;
  field.setAttribute('aria-invalid', 'true');
  field.setAttribute('aria-describedby', errorId);
}

function clearError(fieldId) {
  const field = document.getElementById(fieldId);
  const errorId = `${fieldId}-error`;
  const errorElement = document.getElementById(errorId);
  
  if (errorElement) {
    errorElement.remove();
  }
  
  field.removeAttribute('aria-invalid');
  field.removeAttribute('aria-describedby');
}

// Example usage
document.getElementById('email').addEventListener('blur', function() {
  const email = this.value;
  if (!email.includes('@')) {
    showError('email', 'Please enter a valid email address');
  } else {
    clearError('email');
  }
});

Common Error Message Mistakes

❌ Bad: Generic error messages

<div class="error">Error occurred</div>

Problems:

  • Doesn’t tell users what went wrong
  • No guidance on how to fix it

❌ Bad: Missing ARIA attributes

<input type="email" id="email" />
<div class="error">Invalid email</div>

Problems:

  • Screen readers won’t associate error with field
  • Users don’t know which field has the error

❌ Bad: Using only color to indicate errors

<input type="text" style="border-color: red;" />

Problems:

  • Colorblind users may not see the error
  • No text explanation of the problem

❌ Bad: Error messages that disappear too quickly

setTimeout(() => {
  errorElement.style.display = 'none';
}, 2000); // Too fast!

Problems:

  • Screen readers may not announce the error
  • Users don’t have time to read the message

Error Message Guidelines

Be Specific

<!-- Good -->
<div class="error">Password must be at least 8 characters long</div>

<!-- Bad -->
<div class="error">Invalid password</div>

Be Helpful

<!-- Good -->
<div class="error">Email address is already registered. Try logging in instead.</div>

<!-- Bad -->
<div class="error">Email already exists</div>

Use Clear Language

<!-- Good -->
<div class="error">Please enter your phone number in the format: 555-123-4567</div>

<!-- Bad -->
<div class="error">Invalid format</div>

Testing Error Messages

  1. Screen Reader Testing: Use NVDA, JAWS, or VoiceOver
  2. Keyboard Navigation: Ensure error links are keyboard accessible
  3. Color Contrast: Verify error text meets WCAG contrast requirements
  4. Focus Management: Test that focus moves appropriately when errors appear

Advanced Patterns

<div class="error-summary" role="alert" aria-live="polite">
  <h2>There are 3 errors on this page</h2>
  <ul>
    <li><a href="#email" onclick="focusField('email')">Email address is required</a></li>
    <li><a href="#password" onclick="focusField('password')">Password must be at least 8 characters</a></li>
    <li><a href="#confirm" onclick="focusField('confirm')">Passwords do not match</a></li>
  </ul>
</div>

Success Messages

<div class="success-message" role="status" aria-live="polite">
  Your profile has been updated successfully!
</div>

Loading States

<div class="loading-message" role="status" aria-live="polite">
  <span class="spinner" aria-hidden="true"></span>
  Validating email address...
</div>

Remember: Good error messages are specific, helpful, and accessible to all users. They should clearly explain what went wrong and provide guidance on how to fix it.