Loading Developer Playground

Loading ...

Skip to main content
Share:
ARIA ATTRIBUTEโ€ขWidget Attributes

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.

Value Type
true | false | undefined
Common Use
Accordions, Menus
Related
aria-controls

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

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

true

Indicates that the grouping element controlled by this element is expanded and visible. The expandable content is currently shown.

Example: An accordion panel is open, a dropdown menu is visible
false

Indicates that the grouping element controlled by this element is collapsed and hidden. The expandable content is currently not visible.

Example: An accordion panel is closed, a dropdown menu is hidden
undefined (no attribute)

When omitted, the element does not own or control an expandable grouping element. The element has no expand/collapse functionality.

Use case: Non-expandable buttons, static content

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

Accordions and FAQ sections
Dropdown navigation menus
Collapsible sidebars
Tree views and file explorers
Disclosure widgets (show more/less)
Modal and dialog triggers
Expandable table rows
Mobile hamburger menus

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.

Related Attributes

Specifications & Resources