Loading Developer Playground

Loading ...

Skip to main content
ARIA ATTRIBUTEWidget Attributes

aria-selected

Indicates the current "selected" state of various widgets. Used for tabs, options in listboxes, grid cells, and tree items to communicate selection status to assistive technologies.

Value Type
true | false | undefined
Common Use
Tabs, Listboxes, Grids
Applies To
tab, option, gridcell

Overview

The aria-selected attribute indicates the current "selected" state of elements like tabs, options, grid cells, rows, and tree items. Screen readers announce this state to help users understand which items are currently selected.

This attribute can have three states: true (selected), false (not selected but selectable), or undefined (absence of attribute means not selectable).

Important Distinction

aria-selected="false" means "selectable but not currently selected". If an element is NOT selectable, omit the attribute entirely rather than setting it to false.

Live Demo: Selection Patterns

Tab List

This is the description panel content.

Tab 1: aria-selected="true"

Listbox

Select a color:
  • Red
  • Blue
  • Green
  • Yellow
  • Purple

Selected: None

Calendar Grid

Su
Mo
Tu
We
Th
Fr
Sa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

Selected date: None

Screen reader: For tabs: "Description, tab, selected, 1 of 3". For listbox: "Blue, selected, 2 of 5". For grid: "15, selected".

Attribute Values

true

The element is currently selected. For tabs, this means the tab's panel is visible. For options, this means the option is the current selection.

Screen reader: "[Label], selected"
false

The element is selectable but not currently selected. This implies the element CAN be selected. Use this for other options in a listbox, non-active tabs, etc.

Screen reader: "[Label]" (no "selected" announcement)
undefined(attribute absent)

The element is NOT selectable. Only omit the attribute when selection is not a feature for that element. If all items in a list are potentially selectable, use false for unselected items.

Note: Don't set aria-selected="false" on items that can never be selected.

Code Examples

Tab List

<!-- Basic aria-selected usage -->

<!-- Tab list with selected tab -->
<div role="tablist" aria-label="Product tabs">
  <button 
    role="tab" 
    aria-selected="true"
    aria-controls="panel-1"
    id="tab-1"
  >
    Description
  </button>
  <button 
    role="tab" 
    aria-selected="false"
    aria-controls="panel-2"
    id="tab-2"
    tabindex="-1"
  >
    Reviews
  </button>
  <button 
    role="tab" 
    aria-selected="false"
    aria-controls="panel-3"
    id="tab-3"
    tabindex="-1"
  >
    Shipping
  </button>
</div>

<!-- Tab panels -->
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
  Description content...
</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
  Reviews content...
</div>

Listbox Options

<!-- Listbox with selectable options -->
<label id="color-label">Choose a color:</label>
<ul 
  role="listbox" 
  aria-labelledby="color-label"
  aria-activedescendant="option-blue"
  tabindex="0"
>
  <li 
    role="option" 
    id="option-red"
    aria-selected="false"
  >
    Red
  </li>
  <li 
    role="option" 
    id="option-blue"
    aria-selected="true"
  >
    Blue
  </li>
  <li 
    role="option" 
    id="option-green"
    aria-selected="false"
  >
    Green
  </li>
</ul>

<!-- Screen reader: "Blue, selected, 2 of 3" -->

Grid Selection

<!-- Grid with selectable cells -->
<table role="grid" aria-label="Monthly calendar">
  <thead>
    <tr>
      <th role="columnheader">Sun</th>
      <th role="columnheader">Mon</th>
      <!-- ... -->
    </tr>
  </thead>
  <tbody>
    <tr role="row">
      <td role="gridcell" aria-selected="false" tabindex="-1">1</td>
      <td role="gridcell" aria-selected="false" tabindex="-1">2</td>
      <td role="gridcell" aria-selected="true" tabindex="0">3</td>
      <!-- Selected date -->
      <td role="gridcell" aria-selected="false" tabindex="-1">4</td>
    </tr>
  </tbody>
</table>

<!-- For range selection (calendar date range) -->
<td 
  role="gridcell" 
  aria-selected="true"
  aria-label="March 15, start of selection"
>
  15
</td>

Tree Items

<!-- Tree with selectable items -->
<ul role="tree" aria-label="File browser">
  <li 
    role="treeitem" 
    aria-selected="false"
    aria-expanded="true"
  >
    Documents
    <ul role="group">
      <li role="treeitem" aria-selected="true">
        report.pdf
      </li>
      <li role="treeitem" aria-selected="false">
        notes.txt
      </li>
    </ul>
  </li>
  <li 
    role="treeitem" 
    aria-selected="false"
    aria-expanded="false"
  >
    Images
  </li>
</ul>

<!-- Note: aria-selected indicates selection for actions
     like delete, move, copy - NOT expansion state -->

React Components

// React Tabs with aria-selected
import { useState, useRef, useEffect } from 'react';

function Tabs({ tabs, defaultTab = 0 }) {
  const [selectedIndex, setSelectedIndex] = useState(defaultTab);
  const tabRefs = useRef([]);
  
  const handleKeyDown = (e, index) => {
    let newIndex = index;
    
    switch (e.key) {
      case 'ArrowRight':
        newIndex = (index + 1) % tabs.length;
        break;
      case 'ArrowLeft':
        newIndex = (index - 1 + tabs.length) % tabs.length;
        break;
      case 'Home':
        newIndex = 0;
        break;
      case 'End':
        newIndex = tabs.length - 1;
        break;
      default:
        return;
    }
    
    e.preventDefault();
    setSelectedIndex(newIndex);
    tabRefs.current[newIndex]?.focus();
  };
  
  return (
    <div className="tabs">
      <div role="tablist" aria-label="Content sections">
        {tabs.map((tab, index) => (
          <button
            key={tab.id}
            ref={el => tabRefs.current[index] = el}
            role="tab"
            id={`tab-${tab.id}`}
            aria-selected={index === selectedIndex}
            aria-controls={`panel-${tab.id}`}
            tabIndex={index === selectedIndex ? 0 : -1}
            onClick={() => setSelectedIndex(index)}
            onKeyDown={(e) => handleKeyDown(e, index)}
            className={index === selectedIndex ? 'selected' : ''}
          >
            {tab.label}
          </button>
        ))}
      </div>
      
      {tabs.map((tab, index) => (
        <div
          key={tab.id}
          role="tabpanel"
          id={`panel-${tab.id}`}
          aria-labelledby={`tab-${tab.id}`}
          hidden={index !== selectedIndex}
          tabIndex={0}
        >
          {tab.content}
        </div>
      ))}
    </div>
  );
}

// Selectable Listbox
function Listbox({ items, label, onChange }) {
  const [selectedId, setSelectedId] = useState(null);
  const listRef = useRef(null);
  
  const handleSelect = (id) => {
    setSelectedId(id);
    onChange?.(id);
  };
  
  const handleKeyDown = (e) => {
    const currentIndex = items.findIndex(i => i.id === selectedId);
    let newIndex = currentIndex;
    
    switch (e.key) {
      case 'ArrowDown':
        newIndex = Math.min(currentIndex + 1, items.length - 1);
        break;
      case 'ArrowUp':
        newIndex = Math.max(currentIndex - 1, 0);
        break;
      case 'Home':
        newIndex = 0;
        break;
      case 'End':
        newIndex = items.length - 1;
        break;
      default:
        return;
    }
    
    e.preventDefault();
    handleSelect(items[newIndex].id);
  };
  
  return (
    <div>
      <label id="listbox-label">{label}</label>
      <ul
        ref={listRef}
        role="listbox"
        aria-labelledby="listbox-label"
        aria-activedescendant={selectedId ? `option-${selectedId}` : undefined}
        tabIndex={0}
        onKeyDown={handleKeyDown}
      >
        {items.map(item => (
          <li
            key={item.id}
            role="option"
            id={`option-${item.id}`}
            aria-selected={item.id === selectedId}
            onClick={() => handleSelect(item.id)}
            className={item.id === selectedId ? 'selected' : ''}
          >
            {item.label}
          </li>
        ))}
      </ul>
    </div>
  );
}

Best Practices

For tabs, only one tab should have aria-selected="true" at a time

Set tabindex="-1" on non-selected tabs and tabindex="0" on selected tab

Provide clear visual indication of selected state

Use aria-selected with appropriate roles: tab, option, gridcell, row, treeitem

For multi-select, use with aria-multiselectable on the container

×

Don't use aria-selected on elements that cannot be selected

×

Don't confuse aria-selected with aria-current (for navigation)

×

Don't use aria-selected for checkboxes—use aria-checked instead

×

Don't forget to update tabindex when selection changes (for tabs)

Common Use Cases

Tab panels and tab navigation
Listbox/dropdown options
Calendar date selection
Data grid cell selection
File tree selection
Carousel slide indicators
Sidebar navigation items
Multi-select lists

Related Attributes

Specifications & Resources