References

Beginner-friendly references for web development, with live, editable examples.

The HTML aria-selected attribute

ARIA Accessibility
In short

Use aria-selected to mark the chosen tab, listbox option, grid cell or row in a widget. A value of true means selected and false means selectable but not chosen. With no attribute, tabs and options default to false, while grid cells and rows default to not selectable.

Overview

The aria-selected state marks which items are selected in a composite widget such as a tab list or grid. The gridcell, option, row and tab roles carry it, and so do columnheader, rowheader and treeitem through inheritance.

Focus can stand in for selection. If no item in a widget is explicitly selected, assistive technology may treat selection as following keyboard focus. Once any item is explicitly selected, the user agent must not convey that implied selection.

In a tab list or listbox with no aria-selected at all, Chrome and Firefox report the focused item as selected. Mark one tab true, and a different tab is no longer reported as selected when it takes focus.

Missing and false are different answers. A false item can be selected but isn't, while the default, undefined, means the element isn't selectable at all. WAI-ARIA 1.2 gives tab and option an implicit false, so those two stay selectable with no attribute.

HTML has no tab element, but a plain list of choices can be a <select>. ARIA in HTML says authors should not use aria-selected on its <option> elements. With both present, HTML-AAM says to expose only the selected attribute's value. Firefox does, and Chrome's tree shows aria-selected instead.

Syntax

<div role="tablist" aria-label="Order details">
  <button role="tab" aria-selected="true">Items</button>
  <button role="tab" aria-selected="false">Shipping</button>
</div>

Values

Value
true | false | undefined

Example

Live example
<p style="margin:0 0 10px">Click a tab, or focus one and press the Left or Right arrow key.</p>
<div role="tablist" aria-label="Order details" style="display:flex;gap:6px">
  <button type="button" role="tab" id="tab-items" aria-selected="true" aria-controls="panel-items" style="font-weight:bold;text-decoration:underline">Items</button>
  <button type="button" role="tab" id="tab-shipping" aria-selected="false" aria-controls="panel-shipping" tabindex="-1">Shipping</button>
  <button type="button" role="tab" id="tab-returns" aria-selected="false" aria-controls="panel-returns" tabindex="-1">Returns</button>
</div>
<div role="tabpanel" id="panel-items" aria-labelledby="tab-items" tabindex="0" style="padding:10px 0">Two notebooks and one pen.</div>
<div role="tabpanel" id="panel-shipping" aria-labelledby="tab-shipping" tabindex="0" style="padding:10px 0" hidden>Arrives in three business days.</div>
<div role="tabpanel" id="panel-returns" aria-labelledby="tab-returns" tabindex="0" style="padding:10px 0" hidden>Free returns within 30 days.</div>
<p role="status" id="state" style="margin:0">Items is selected.</p>
<script>
const tabs = [...document.querySelectorAll('[role="tab"]')];
function select(tab) {
  tabs.forEach(function (item) {
    const chosen = item === tab;
    item.setAttribute('aria-selected', String(chosen));
    item.tabIndex = chosen ? 0 : -1;
    item.style.fontWeight = chosen ? 'bold' : 'normal';
    item.style.textDecoration = chosen ? 'underline' : 'none';
    document.getElementById(item.getAttribute('aria-controls')).hidden = !chosen;
  });
  tab.focus();
  document.getElementById('state').textContent = tab.textContent + ' is selected, so aria-selected is true on it and false on the other two tabs.';
}
tabs.forEach(function (tab, index) {
  tab.addEventListener('click', function () { select(tab); });
  tab.addEventListener('keydown', function (event) {
    const step = event.key === 'ArrowRight' ? 1 : event.key === 'ArrowLeft' ? -1 : 0;
    if (step) select(tabs[(index + step + tabs.length) % tabs.length]);
  });
});
</script>

Best practices

  • Back aria-selected="true" with a visual sign on the tab, because the attribute draws nothing itself.
  • Link each tab to its panel with aria-controls. In a single-selection tab list, each panel should stay hidden until its tab is selected.
  • Keep aria-selected and aria-checked apart in a listbox. Both are valid on option, but avoid using both in one listbox unless each has its own meaning and its own control.
  • To select a whole grid column, set the value on each of its cells, since browsers must not spread it down from a columnheader on their own.

Frequently asked questions

Does aria-selected switch the panel on a tab?
No, switching the panel is your script's job, and aria-selected reports which tab's panel is displayed. Move the true value to the new tab in the same step that swaps the panel.
When should I use aria-current instead of aria-selected?
Use aria-current for the page or item that is current now, and aria-selected for selection inside a widget. Don't let aria-current stand in for aria-selected where they mean the same thing, as on tabs.
Does aria-selected work on a link or a button?
No, the link and button roles don't support aria-selected, so use aria-current on links and aria-pressed on toggle buttons. A button element with role=tab is fine, because its role is then tab.
Do unselected tabs need aria-selected=false?
Yes, inactive tabs should have aria-selected set to false. Even with false as the implicit value, an explicit false keeps browsers from reporting the focused tab as selected when no tab is marked true.
Can several items have aria-selected=true at once?
Yes, several items can carry aria-selected=true once their container sets aria-multiselectable to true. Without that, the widget is declared single-selection, which the browser does not enforce.