$npx -y skills add mgifford/accessibility-skills --skill aria-live-regionsLoad this skill whenever the project contains dynamic content updates, status messages, alerts, notifications, loading indicators, or any use of aria-live, role="status", role="alert", or role="log". Under no circumstances add or modify live-region markup without applying these r
| 1 | # ARIA Live Regions Accessibility Skill |
| 2 | |
| 3 | > **Canonical source**: `examples/ARIA_LIVE_REGIONS_BEST_PRACTICES.md` in `mgifford/ACCESSIBILITY.md` |
| 4 | > This skill is derived from that file. When in doubt, the example is authoritative. |
| 5 | |
| 6 | Apply these rules when implementing any dynamic content updates announced to |
| 7 | assistive technologies. |
| 8 | **Only load this skill if the project contains dynamic content updates.** |
| 9 | |
| 10 | --- |
| 11 | |
| 12 | ## Core Mandate |
| 13 | |
| 14 | Live regions make dynamic status information available to screen reader |
| 15 | users without moving keyboard focus — they are useful for concise updates |
| 16 | (a result count, a saved confirmation, progress information). **Live regions |
| 17 | are not a general notification system.** They do not replace visible |
| 18 | content, native HTML, focus management, or a control's programmatic state. |
| 19 | Poorly timed or excessive announcements can be missed, duplicated, |
| 20 | reordered, or disruptive. |
| 21 | |
| 22 | Prefer visible status messages — they also help magnification users, |
| 23 | cognitive/learning-disabled users, and anyone who wouldn't otherwise notice |
| 24 | a change elsewhere on the page. Use native HTML and programmatic control |
| 25 | state (e.g., `aria-expanded`) *before* reaching for a live region. |
| 26 | |
| 27 | --- |
| 28 | |
| 29 | ## Severity Scale (this skill) |
| 30 | |
| 31 | | Level | Meaning | |
| 32 | |---|---| |
| 33 | | **Critical** | Dynamic content change conveys no information to AT; user cannot complete task | |
| 34 | | **Serious** | `assertive`/`role="alert"` used for non-urgent updates, interrupting mid-sentence; live region not exposed before the update it must report | |
| 35 | | **Moderate** | Status updates absent; success/failure not announced; announcements too verbose | |
| 36 | | **Minor** | Redundant announcements; live region content duplicates visible text unnecessarily | |
| 37 | |
| 38 | --- |
| 39 | |
| 40 | ## What WCAG 4.1.3 Actually Requires |
| 41 | |
| 42 | WCAG 4.1.3 requires *existing* status messages to be programmatically |
| 43 | determinable so AT can present them without moving focus. It does **not** |
| 44 | require creating a new status message for every dynamic change — an updated |
| 45 | result list is content; a message like "18 results found" is the status |
| 46 | message. A dialog receiving focus is a change of context, not a status message. |
| 47 | |
| 48 | **Choose the mechanism by situation, not by default reflex:** |
| 49 | |
| 50 | | Situation | Primary mechanism | |
| 51 | |---|---| |
| 52 | | A button expands/collapses content | Keep focus on the button; update `aria-expanded` | |
| 53 | | A tab changes panels | Use the tabs pattern and its selected state | |
| 54 | | A modal dialog opens | Move focus into the dialog | |
| 55 | | An SPA changes route | Update the document title; move focus to a logical heading | |
| 56 | | A form reveals several errors | Error summary + focus management; associate each error with its field | |
| 57 | | Save/cart/filter/search completes | Concise visible `role="status"` message | |
| 58 | | An important time-sensitive condition occurs | Visible `role="alert"` message | |
| 59 | | A condition requires an immediate decision | Alert dialog; move focus into it | |
| 60 | | New entries appended to an ordered history | `role="log"` when that semantic matches | |
| 61 | |
| 62 | Live regions can supplement focus management — they should not announce a |
| 63 | focus change that already provides the same information. |
| 64 | |
| 65 | --- |
| 66 | |
| 67 | ## Critical: `role="status"`, `role="alert"`, and `role="log"` |
| 68 | |
| 69 | | Role | Implicit `aria-live` | Implicit `aria-atomic` | Use for | |
| 70 | |---|---|---|---| |
| 71 | | `role="status"` | `polite` | `true` | Advisory info not requiring immediate action | |
| 72 | | `role="alert"` | `assertive` | `true` | Important, usually time-sensitive information | |
| 73 | | `role="log"` | `polite` | — | Ordered sequences where new info is added (chat, activity log) | |
| 74 | |
| 75 | ```html |
| 76 | <p id="cart-status" role="status"></p> |
| 77 | <p id="connection-alert" role="alert"></p> |
| 78 | ``` |
| 79 | |
| 80 | Do not add redundant explicit `aria-live`/`aria-atomic` values on these roles |
| 81 | unless a verified compatibility need justifies it. An alert does not receive |
| 82 | focus and should not require a response to dismiss — **if the user must stop |
| 83 | and respond, use the [ARIA APG alert dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/)** |
| 84 | with full modal dialog keyboard handling instead. Do not use `role="alert"` |
| 85 | for routine validation, success confirmations, or every field error. |
| 86 | |
| 87 | ```html |
| 88 | <div role="log" aria-relevant="additions" aria-atomic="false"> |
| 89 | <p>Jordan uploaded quarterly-report.pdf.</p> |
| 90 | </div> |
| 91 | ``` |
| 92 | |
| 93 | `role="log"` needs an accessible name (via a heading + `aria-labelledby`) and |
| 94 | each entry should be understandable on its own. |
| 95 | |
| 96 | **`aria-live` bare values** (use only when no live-region role fits): |
| 97 | |
| 98 | | Value | Meaning | |
| 99 | |---|---| |
| 100 | | `off` | Default — updates not |