Loading Developer Playground

Loading ...

Skip to main content
ARIA ATTRIBUTERelationship Attributes

aria-controls

Identifies the element (or elements) whose contents or presence are controlled by the current element. Creates a programmatic relationship between a control and the element it affects.

Value Type
ID Reference (List)
Global Attribute
Yes
Common Use
Tabs, Accordions, Disclosures

Overview

The aria-controls attribute identifies the element (or elements) whose contents or presence are controlled by the element on which the attribute is set. It creates a programmatic relationship that assistive technologies can expose to users.

The value is a space-separated list of one or more ID references pointing to the controlled elements. This is commonly used with buttons that expand/collapse content, tabs that show/hide panels, and other interactive controls.

Note: While aria-controls creates a relationship, not all screen readers actively announce it. It's still valuable for programmatic discovery and should be used alongside aria-expanded for disclosure patterns.

Live Demo: Collapsible Panel & Tabs

Collapsible Panel

aria-expanded="false" aria-controls="demo-panel"

Tab Navigation

This is the Overview panel content. Each tab button has aria-controls pointing to its panel.

aria-controls links the control (button/tab) to the element it affects. This relationship helps assistive technologies understand which content changes when the control is activated.

Code Examples

Basic Usage

<!-- aria-controls identifies the element being controlled -->

<!-- Button controls a collapsible panel -->
<button
  aria-expanded="false"
  aria-controls="details-panel"
  onclick="togglePanel()"
>
  Show Details
</button>

<div id="details-panel" hidden>
  <p>This is the controlled content panel.</p>
</div>

<!-- The aria-controls value matches the id of the controlled element -->

Accordion Pattern

<!-- Accordion with aria-controls -->

<div class="accordion">
  <!-- Accordion item 1 -->
  <h3>
    <button
      aria-expanded="true"
      aria-controls="section1-content"
      id="section1-header"
    >
      Section 1
    </button>
  </h3>
  <div 
    id="section1-content"
    role="region"
    aria-labelledby="section1-header"
  >
    <p>Content for section 1...</p>
  </div>

  <!-- Accordion item 2 -->
  <h3>
    <button
      aria-expanded="false"
      aria-controls="section2-content"
      id="section2-header"
    >
      Section 2
    </button>
  </h3>
  <div 
    id="section2-content"
    role="region"
    aria-labelledby="section2-header"
    hidden
  >
    <p>Content for section 2...</p>
  </div>
</div>

Tab Pattern

<!-- Tabs with aria-controls -->

<div role="tablist" aria-label="Product information">
  <button
    role="tab"
    id="tab-details"
    aria-selected="true"
    aria-controls="panel-details"
    tabindex="0"
  >
    Details
  </button>
  <button
    role="tab"
    id="tab-specs"
    aria-selected="false"
    aria-controls="panel-specs"
    tabindex="-1"
  >
    Specifications
  </button>
  <button
    role="tab"
    id="tab-reviews"
    aria-selected="false"
    aria-controls="panel-reviews"
    tabindex="-1"
  >
    Reviews
  </button>
</div>

<div
  role="tabpanel"
  id="panel-details"
  aria-labelledby="tab-details"
>
  <p>Product details content...</p>
</div>

<div
  role="tabpanel"
  id="panel-specs"
  aria-labelledby="tab-specs"
  hidden
>
  <p>Specifications content...</p>
</div>

<div
  role="tabpanel"
  id="panel-reviews"
  aria-labelledby="tab-reviews"
  hidden
>
  <p>Customer reviews content...</p>
</div>

React Components

// React components with aria-controls
import { useState, useId } from 'react';

// Collapsible Panel Component
function CollapsiblePanel({ title, children }) {
  const [isOpen, setIsOpen] = useState(false);
  const panelId = useId();

  return (
    <div className="collapsible">
      <button
        aria-expanded={isOpen}
        aria-controls={panelId}
        onClick={() => setIsOpen(!isOpen)}
      >
        {title}
        <span aria-hidden="true">{isOpen ? '▼' : '▶'}</span>
      </button>
      
      <div 
        id={panelId}
        hidden={!isOpen}
        role="region"
      >
        {children}
      </div>
    </div>
  );
}

// Tabs Component with aria-controls
function Tabs({ tabs }) {
  const [activeTab, setActiveTab] = useState(0);
  const baseId = useId();

  return (
    <div>
      <div role="tablist">
        {tabs.map((tab, index) => {
          const tabId = `${baseId}-tab-${index}`;
          const panelId = `${baseId}-panel-${index}`;
          
          return (
            <button
              key={index}
              role="tab"
              id={tabId}
              aria-selected={activeTab === index}
              aria-controls={panelId}
              tabIndex={activeTab === index ? 0 : -1}
              onClick={() => setActiveTab(index)}
              onKeyDown={(e) => handleTabKeyboard(e, index)}
            >
              {tab.label}
            </button>
          );
        })}
      </div>
      
      {tabs.map((tab, index) => {
        const tabId = `${baseId}-tab-${index}`;
        const panelId = `${baseId}-panel-${index}`;
        
        return (
          <div
            key={index}
            role="tabpanel"
            id={panelId}
            aria-labelledby={tabId}
            hidden={activeTab !== index}
          >
            {tab.content}
          </div>
        );
      })}
    </div>
  );
}

// Usage
function App() {
  return (
    <>
      <CollapsiblePanel title="More Information">
        <p>This content is revealed when the button is clicked.</p>
      </CollapsiblePanel>
      
      <Tabs 
        tabs={[
          { label: 'Overview', content: <p>Overview content</p> },
          { label: 'Features', content: <p>Features content</p> },
          { label: 'Pricing', content: <p>Pricing content</p> },
        ]}
      />
    </>
  );
}

Related Attributes

Specifications & Resources