aria-expanded
Indicates whether a grouping element owned or controlled by this element is expanded or collapsed. Essential for accordions, dropdown menus, tree views, and disclosure widgets.
Overview
The aria-expanded attribute indicates whether the element, or another grouping element it controls, is currently expanded or collapsed. This attribute is used on buttons or other triggers that show/hide content.
Screen readers announce the expanded/collapsed state to users, helping them understand when content is available or hidden. The attribute should be updated dynamically via JavaScript when the user interacts with the control.
When to Use aria-expanded
Use aria-expanded on the element that triggers the expand/collapse action (usually a button), not on the content that expands. Pair it with aria-controls to reference the controlled element's ID.
Live Demo: aria-expanded in Action
Accordion Example
aria-expanded indicates whether a grouping element owned or controlled by this element is expanded or collapsed. It's essential for creating accessible accordions, menus, and disclosure widgets.
Button has aria-expanded="false"
Dropdown Menu Example
Button has aria-expanded="false" and aria-haspopup="menu"
Tree View Example
Treeitem has aria-expanded="false"
Screen reader announcement: When focusing on an expandable button, screen readers announce the current state. For example: "What is aria-expanded?, collapsed, button" or "Actions, expanded, pop-up menu, button".
Attribute Values
trueIndicates that the grouping element controlled by this element is expanded and visible. The expandable content is currently shown.
falseIndicates that the grouping element controlled by this element is collapsed and hidden. The expandable content is currently not visible.
undefined (no attribute)When omitted, the element does not own or control an expandable grouping element. The element has no expand/collapse functionality.
Code Examples
Accordion Pattern
<!-- Accordion Pattern -->
<div class="accordion">
<h3>
<button
aria-expanded="false"
aria-controls="panel-1"
id="accordion-header-1"
>
Section 1
<svg aria-hidden="true"><!-- chevron icon --></svg>
</button>
</h3>
<div
id="panel-1"
role="region"
aria-labelledby="accordion-header-1"
hidden
>
<p>Content for section 1...</p>
</div>
</div>
<!-- When expanded, update to aria-expanded="true" and remove hidden -->Dropdown Menu
<!-- Dropdown Menu Button -->
<div class="dropdown">
<button
aria-expanded="false"
aria-haspopup="menu"
aria-controls="dropdown-menu"
>
Options
<svg aria-hidden="true"><!-- dropdown arrow --></svg>
</button>
<ul
id="dropdown-menu"
role="menu"
hidden
>
<li role="menuitem"><button>Edit</button></li>
<li role="menuitem"><button>Duplicate</button></li>
<li role="menuitem"><button>Delete</button></li>
</ul>
</div>
<!-- JavaScript toggles aria-expanded and hidden attribute -->Disclosure Widget
<!-- Disclosure Widget (Show/Hide) -->
<div class="disclosure">
<button
aria-expanded="false"
aria-controls="more-info"
>
Show more details
</button>
<div id="more-info" hidden>
<p>Additional information that can be revealed...</p>
<p>This content is hidden until the user clicks the button.</p>
</div>
</div>
<!-- Native HTML alternative: <details> and <summary> elements -->
<details>
<summary>Show more details</summary>
<p>Content is natively expandable without JavaScript!</p>
</details>Tree View
<!-- Tree View Pattern -->
<ul role="tree" aria-label="File explorer">
<li role="treeitem" aria-expanded="false">
<span>๐ Documents</span>
<ul role="group" hidden>
<li role="treeitem">๐ Resume.pdf</li>
<li role="treeitem">๐ Cover Letter.docx</li>
</ul>
</li>
<li role="treeitem" aria-expanded="true">
<span>๐ Projects</span>
<ul role="group">
<li role="treeitem" aria-expanded="false">
<span>๐ Website</span>
<ul role="group" hidden>
<li role="treeitem">๐ index.html</li>
<li role="treeitem">๐ styles.css</li>
</ul>
</li>
</ul>
</li>
</ul>React Examples
// React Accordion Component
import { useState } from 'react';
function Accordion({ items }) {
const [openIndex, setOpenIndex] = useState(null);
return (
<div className="accordion">
{items.map((item, index) => {
const isOpen = openIndex === index;
const headerId = `accordion-header-${index}`;
const panelId = `accordion-panel-${index}`;
return (
<div key={index} className="accordion-item">
<h3>
<button
id={headerId}
aria-expanded={isOpen}
aria-controls={panelId}
onClick={() => setOpenIndex(isOpen ? null : index)}
className="accordion-trigger"
>
{item.title}
<ChevronIcon
aria-hidden="true"
className={isOpen ? 'rotate-180' : ''}
/>
</button>
</h3>
<div
id={panelId}
role="region"
aria-labelledby={headerId}
hidden={!isOpen}
className="accordion-panel"
>
{item.content}
</div>
</div>
);
})}
</div>
);
}
// Dropdown Menu Component
function DropdownMenu({ label, items }) {
const [isOpen, setIsOpen] = useState(false);
const menuRef = useRef(null);
// Close on outside click
useEffect(() => {
const handleClickOutside = (e) => {
if (menuRef.current && !menuRef.current.contains(e.target)) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
return (
<div ref={menuRef} className="dropdown">
<button
aria-expanded={isOpen}
aria-haspopup="menu"
onClick={() => setIsOpen(!isOpen)}
>
{label}
</button>
{isOpen && (
<ul role="menu">
{items.map((item, i) => (
<li key={i} role="menuitem">
<button onClick={() => {
item.onClick();
setIsOpen(false);
}}>
{item.label}
</button>
</li>
))}
</ul>
)}
</div>
);
}Best Practices
Place aria-expanded on the button or trigger element, not on the expandable content
Use aria-controls to reference the ID of the element being expanded/collapsed
Update aria-expanded dynamically when the user toggles the content
Pair with proper keyboard support (Enter/Space to toggle)
Use the hidden attribute or display:none on collapsed content to hide it from all users
Consider using native HTML elements like <details> and <summary> when appropriate
Don't use aria-expanded on elements that don't control expandable content
Don't forget to update the attribute when state changes - it must stay in sync
Don't put aria-expanded on the content panel - it goes on the trigger
Don't use aria-expanded="undefined" - simply omit the attribute instead
Common Use Cases
Accessibility Notes
Screen Reader Announcements
Screen readers announce the expanded state when users focus on the trigger element. For example: "Menu, expanded, button" or "Section 1, collapsed, button". This helps users understand whether activating the control will show or hide content.
Keyboard Support Requirements
Expandable elements should be keyboard accessible. Buttons automatically support Enter and Space keys. For other elements with role="button", ensure you handle both keypress events. For tree views, implement arrow key navigation.
Using aria-controls
While aria-controls has limited screen reader support, it's still a best practice to include it. It creates a programmatic relationship between the trigger and the content, which some assistive technologies can leverage.

