Page announces dynamic content changes via aria-live regions with appropriate politeness setting
Why it matters
Single-page applications update content dynamically—cart counts change, search results replace prior results, toast notifications appear, status messages resolve—all without a page reload. Screen readers only announce DOM changes inside aria-live regions; DOM changes elsewhere are silent. WCAG 2.2 SC 4.1.3 (Level AA) requires status messages to be programmatically determinable without receiving focus, and SC 1.3.1 (Level A) requires that information conveyed through presentation be available to AT. Section 508 2018 Refresh 502.3.14 requires applications to notify AT of relevant UI events. A cart update, form submission success, or real-time filter that silently changes the DOM shuts screen reader users out of the live application state.
Severity rationale
Medium because missing `aria-live` regions cause screen reader users to miss status messages and dynamic content changes, degrading usability of dynamic interactions without preventing access to static content.
Remediation
Wrap every dynamic status message in an aria-live region. For non-urgent updates (cart count, filter results), use aria-live="polite". For urgent alerts (errors, session warnings), use aria-live="assertive":
// In a shared Announcer component — mount once at root
const [politeMessage, setPoliteMessage] = useState('');
export function useAnnounce() {
return (msg: string) => {
setPoliteMessage(''); // Clear first to re-trigger announcement
requestAnimationFrame(() => setPoliteMessage(msg));
};
}
<div
aria-live="polite"
aria-atomic="true"
className="sr-only"
>
{politeMessage}
</div>
In your cart or filter component:
const announce = useAnnounce();
const addToCart = (item: Item) => {
addItem(item);
announce(`${item.name} added to cart. ${cartCount + 1} items total.`);
};
Avoid aria-live="assertive" for routine updates—it interrupts whatever the screen reader is currently reading.
Detection
- ID:
aria-live - Severity:
medium - What to look for: Count all relevant instances and enumerate each. Check for dynamic content updates (items added to cart, search results loaded, notifications, status updates). Verify that changes are announced to screen readers via
aria-liveregions with appropriate politeness levels (polite,assertive, or impliedoff). - Pass criteria: Dynamic content updates are announced via
aria-live="polite"oraria-live="assertive"as appropriate. Updates are clear and timely. - Fail criteria: Dynamic content updates do not use
aria-live, causing screen reader users to miss important announcements. - Skip (N/A) when: No dynamic content updates exist.
- Detail on fail: Example:
"When a user adds a product to the cart, the page updates the cart count and shows a toast notification visually. No aria-live region announces this to screen reader users. Search results update dynamically when filters are changed, but changes are not announced." - Remediation: Wrap dynamic content with
aria-live:const [message, setMessage] = useState(''); const addToCart = () => { // Add product setMessage('Product added to cart'); setTimeout(() => setMessage(''), 5000); }; return ( <> <button onClick={addToCart}>Add to Cart</button> <div aria-live="polite" aria-atomic="true" className="sr-only"> {message} </div> </> );
External references
- wcag:2.2 · 4.1.3 — Status Messages
- wcag:2.2 · 1.3.1 — Info and Relationships
- section-508:2018-refresh · 502.3.14 — Event Notification — Dynamic Content
Taxons
History
- 2026-04-18·v1.0.0·Initial import from gov-section-508·automated