LEARNING OBJECTIVES โต
- Correctly apply and dynamically update
aria-expanded="true|false"on disclosure controls, navigation drawers, and accordions. - Distinguish between selection (
aria-selected), check states (aria-checked), and toggle press states (aria-pressed). - Implement tri-state checkbox logic using
aria-checked="mixed"alongside JavaScript indeterminate state properties. - Connect reactive CSS styling directly to ARIA state attributes (
button[aria-expanded="true"]) for bulletproof Single Source of Truth architecture.
๐ The Mental Model & Story (Intuitive Foundation)
Think of a luxury airplane cockpit. In front of the pilot are dozens of specialized physical switches and indicators:
- The Foldable Landing Gear Lever (
aria-expanded): The switch indicates whether a massive mechanical subsystem is currently deployed (open) or retracted into the fuselage (closed). - The Radio Frequency Selector (
aria-selected): A dial where one out of five preset channels is actively highlighted and tuned in. - The Cargo Fire Suppression Arming Switch (
aria-checked): A tri-state toggle switch that can be Disarmed (false), Armed (true), or in Partial Diagnostic Test Mode (mixed). - The Cockpit Dome Light Pushbutton (
aria-pressed): A momentary button that physically locks in when turned on and pops out when turned off.
When blind pilots fly in flight simulators using synthetic auditory telemetry, the auditory computer must immediately announce:
- "Landing gear lever, expanded" or "Landing gear lever, collapsed".
- "VHF 1, Tab 1 of 4, selected".
- "Master Cargo Bay Arming, mixed".
- "Night Vision Illumination, toggle button, pressed".
If a web developer opens an accordion dropdown visually with CSS transitions but forgets to toggle aria-expanded="true" in JavaScript, sighted users see the menu open, but the screen reader announces: "Navigation menu, collapsed, button". The user presses Enter expecting to open the menu, but hears nothing, assuming the application is broken.
Technical Deep Dive & Specifications
The Four Major Interactive ARIA States
+---------------------------------------------------------------------------------------------------+
| ARIA INTERACTIVE STATE MATRIX |
+---------------------+-------------------------------+---------------------+-----------------------+
| Attribute | Valid Values | Allowed Roles | Typical UI Widget |
+---------------------+-------------------------------+---------------------+-----------------------+
| aria-expanded | "true" | "false" | button, combobox, | Accordions, Drawers, |
| | | treeitem, menuitem | Navigation Drawers |
+---------------------+-------------------------------+---------------------+-----------------------+
| aria-selected | "true" | "false" | "undefined"| tab, option, | Tab Panels, Listboxes,|
| | | gridcell, row, tree | Data Grids |
+---------------------+-------------------------------+---------------------+-----------------------+
| aria-checked | "true" | "false" | "mixed" | checkbox, radio, | Nested Checkboxes, |
| | | switch, menuitemchk | Permission Trees |
+---------------------+-------------------------------+---------------------+-----------------------+
| aria-pressed | "true" | "false" | "mixed" | button | Toggle buttons (Mute, |
| | | (Toggle buttons) | Bold, Favorite Star) |
+---------------------+-------------------------------+---------------------+-----------------------+
1. aria-expanded: Disclosure & Hierarchical Controls
The aria-expanded state indicates whether the target container controlled by an element is visually displayed or collapsed.
[Button: aria-expanded="false"] ---- Click / Enter ----> [Button: aria-expanded="true"]
| |
v v
[Panel: hidden / display:none] [Panel: visible / display:block]
- Placement Rule:
aria-expandedbelongs on the trigger element (the button initiating the change), never on the collapsible container itself. - Specification Rule: When collapsed, the controlled content must either be removed from the DOM, hidden with
hidden, or styled withdisplay: none.
2. aria-selected vs. aria-checked vs. aria-pressed
Engineers frequently confuse these three states. Here is the strict W3C disambiguation:
STATE DISAMBIGUATION
|
+-----------------------------------+-----------------------------------+
| | |
Is it a TAB / LISTBOX Is it a CHECKBOX / SWITCH Is it a TOGGLE BUTTON
member in a composite widget? with binary/tri-state values? that turns on / off?
| | |
v v v
aria-selected aria-checked aria-pressed
(e.g. Tab 1 of 3) (e.g. [x] Terms) (e.g. [Mute Audio])
3. The Tri-State Checkbox (aria-checked="mixed")
When building parent-child checkbox hierarchies (e.g., selecting all permissions in a category), the parent checkbox has three distinct visual and semantic states:
- Unchecked (
false): Zero child items are selected. - Checked (
true): All child items are selected. - Indeterminate / Mixed (
mixed): Some, but not all, child items are selected.
[ - ] Select All Notifications (aria-checked="mixed", indeterminate=true)
โโโ [X] Email Notifications (aria-checked="true")
โโโ [ ] SMS Notifications (aria-checked="false")
โโโ [X] Push Notifications (aria-checked="true")
Critical DOM API Note: HTML
<input type="checkbox">elements have an IDL propertycheckbox.indeterminate = true. However,indeterminateis a JavaScript property onlyโthere is no HTML attribute<input indeterminate>. For custom ARIA checkboxes (<div role="checkbox">), you must explicitly declarearia-checked="mixed".
๐ป Interactive Code Playground
Starter Code
Line-by-Line Code Breakdown
- Line 51 (
aria-expanded="false"): Declares the initial collapsed state to assistive technologies. - Line 52 (
aria-controls="acc-panel-1"): Explicitly links the trigger button to the content container ID. - Line 58 (
role="region" aria-labelledby="acc-btn-1"): Turns the accordion panel into a landmark labeled by its trigger header. - Line 59 (
hidden): Removes the collapsed panel from visual layout AND the accessibility tree. - Line 94 (
btn.setAttribute('aria-expanded', String(!isExpanded))): Toggles the ARIA state atomically with the visualhiddenattribute. - Line 115 (
parent.indeterminate = true; parent.setAttribute('aria-checked', 'mixed')): Synchronizes both the browser native UI rendering (indeterminate = true) and the accessibility tree (aria-checked="mixed").
Expected Browser Render Output
[Visual Display Initial]
[ What is the W3C AccName algorithm? โผ ] (Collapsed)
[Notification Settings]
[ - ] Select All Notifications (Indeterminate dash icon)
[x] Email Updates
[ ] SMS Text Alerts
[x] Mobile Push Notifications
[Screen Reader Announcement on Focus]
Accordion Button: "What is the W3C AccName algorithm?, Collapsed, Button"
Parent Checkbox: "Select All Notifications, Mixed, Checkbox"๐๏ธ Hands-On Exercise
๐ฏ The Challenge: Build an Accessible Toggle Mute Button & Tabstrip
Instructions:
- Create an audio toggle button using
aria-pressed="false"and the visible text "Mute Microphone". - Add JavaScript logic: When clicked, toggle
aria-pressedbetween"true"and"false", and update the button text to "Microphone Muted" or "Mute Microphone". - Style the button with CSS using the attribute selector
button[aria-pressed="true"]so it turns red with white text. - Create a 2-tab navigation strip (
role="tablist") with two tabs (role="tab"): "General Settings" and "Security Settings". - Set
aria-selected="true"on the active tab andaria-selected="false"on the inactive tab. Ensuretabindex="0"is on the active tab andtabindex="-1"on the inactive tab (Roving Tabindex).
๐ Starter Code Sandbox
โ ๏ธ Common Pitfalls
- Placing
aria-expandedon the Container Instead of the Trigger: Puttingaria-expanded="false"on the<div>being collapsed is invalid ARIA.aria-expandedbelongs strictly on the interactive<button>that controls the state. - Using Non-Boolean Strings: Writing
aria-expanded="expanded"oraria-selected="yes". ARIA states strictly require the string literals"true"or"false"(or"mixed"where supported). - Failing to Manage
tabindexwitharia-selected: In tablists, only the currently selected tab should havetabindex="0". All other tabs must havetabindex="-1"so the user can navigate cleanly via arrow keys without bloating the Tab key sequence.
๐ก Pro Tips
- Zero-Class State Styling: Avoid writing separate CSS helper classes like
.is-activeor.is-open. Use CSS attribute selectors:button[aria-expanded="true"]and.tab[aria-selected="true"]. This forces developers to maintain accessible ARIA states because visual styles will break immediately if ARIA is omitted! - Avoid
aria-expandedon Modal Dialog Triggers: When a button opens a modal<dialog>, do not usearia-expanded. Modals change focus context rather than expanding an inline subtree;aria-haspopup="dialog"is the correct semantic indicator.
๐ Key Takeaways
aria-expandedcommunicates disclosure/accordion state ("true"vs"false") on the trigger control.aria-selectedcommunicates which element is active inside composite widgets (role="tablist",role="listbox",role="tree").aria-checkedsupports tri-state values ("true","false","mixed") for complex permission and file-tree hierarchies.aria-pressedturns standard<button>elements into accessible binary toggle buttons.- Binding CSS rules directly to ARIA attribute selectors ensures visual presentation and accessibility semantics remain perfectly synchronized.
- --