Test live region announcements
Status messages must be programmatically determined so they can be presented to the user by assistive technologies without receiving focus.
Dynamic content updates
Toast Notifications:
Form Status:
Shopping Cart:
What gets announced
✕ (Silence)
Visual-only update, screen reader misses it
✕ (Silence)
User doesn't know if save succeeded
✕ (Silence)
User unsure if item was added
When to use each
Waits for user to finish current task before announcing. Use for non-critical updates.
Toasts, form validation, search resultsInterrupts immediately. Use sparingly for time-sensitive information.
Errors, alerts, timer warningsImplicit aria-live="polite". Semantic way to mark status messages.
Loading states, progress, confirmationsTesting hints
import AxeBuilder from '@axe-core/playwright';
// Check for status message accessibility
const results = await new AxeBuilder({ page })
.withRules(['aria-live-region-text'])
.analyze();
// Verify live region exists
const liveRegion = page.locator('[aria-live]');
await expect(liveRegion).toBeAttached();
// Trigger update and verify content changes
await page.click('[data-testid="trigger-notification"]');
await expect(liveRegion).toContainText('saved');// Find all live regions
const liveRegions = await page.$$('[aria-live], [role="status"], [role="alert"]');
for (const region of liveRegions) {
const live = await region.getAttribute('aria-live');
const role = await region.getAttribute('role');
const atomic = await region.getAttribute('aria-atomic');
console.log({
live,
role,
atomic,
text: await region.textContent()
});
}
// Verify dynamic content is in live region
await page.click('[data-testid="save-button"]');
const status = page.locator('[role="status"]');
await expect(status).toHaveText(/saved|error/i);Automation hints
page.locator('[aria-live]')page.getByRole('status')