WCAG Compliance Patterns
When to Use
Apply these patterns to every AJAX implementation. Accessibility is not optional — WCAG 2.1 Level AA is the standard for Drupal sites.
Decision
| If you need... | Use... | WCAG Criterion |
|---|---|---|
| Screen reader announcements | AnnounceCommand or MessageCommand | 4.1.3 |
| Focus management after updates | FocusFirstCommand | 2.4.3 |
| Keyboard AJAX triggers | keypress: TRUE in #ajax |
2.1.1 |
| Live region updates | aria-live attributes |
1.3.1 |
| Loading indicators | Accessible progress messages | 2.2.1 |
Pattern
use Drupal\Core\Ajax\AnnounceCommand;
use Drupal\Core\Ajax\FocusFirstCommand;
public function ajaxCallback(array &$form, FormStateInterface $form_state) {
$response = new AjaxResponse();
// 1. Update content
$response->addCommand(new ReplaceCommand('#results', $updated_content));
// 2. Announce change to screen readers
$response->addCommand(new AnnounceCommand(
'Results updated with 5 new items',
'polite' // 'assertive' only for errors
));
// 3. Manage focus for keyboard users
$response->addCommand(new FocusFirstCommand('#results'));
return $response;
}
// Keyboard-accessible AJAX trigger
$form['trigger'] = [
'#type' => 'button',
'#value' => t('Load More'),
'#ajax' => [
'callback' => '::loadMore',
'wrapper' => 'results',
'keypress' => TRUE, // Enables ENTER/SPACE triggering
'progress' => [
'type' => 'throbber',
'message' => t('Loading more results...'), // Read by screen readers
],
],
];
// ARIA live region for dynamic content
$form['results'] = [
'#type' => 'container',
'#attributes' => [
'id' => 'results',
'aria-live' => 'polite',
'aria-atomic' => 'true',
],
];
Reference: core/lib/Drupal/Core/Ajax/AnnounceCommand.php, core/lib/Drupal/Core/Ajax/FocusFirstCommand.php
Common Mistakes
- Wrong: Not announcing AJAX updates → Right: WCAG 4.1.3 violation; screen reader users don't know content changed
- Wrong: Missing
keypress: TRUEon AJAX buttons → Right: WCAG 2.1.1 violation; keyboard users can't trigger AJAX - Wrong: Not managing focus after updates → Right: Use FocusFirstCommand or InvokeCommand('focus')
- Wrong: Using 'assertive' aria-live unnecessarily → Right: Reserve for errors; 'polite' for most content updates
- Wrong: Not testing with screen readers → Right: Test with NVDA (Windows), JAWS (Windows), or VoiceOver (Mac)