Skip to Content

Overview

Tabs organize content into selectable panels. The most common mistake is building tabs from <a> links or unsemantic <div> elements. These miss the required ARIA roles, states, and keyboard behavior that make tabs work for screen reader and keyboard users.

WCAG Criteria:

Key requirements:

  • The tab container needs role="tablist"
  • Each tab needs role="tab", aria-selected, and aria-controls pointing to its panel
  • Active tab: tabindex="0", inactive tabs: tabindex="-1"
  • Left/Right arrow keys move focus between tabs
  • Each panel needs role="tabpanel" and aria-labelledby pointing back to its tab
  • Only the active panel is visible

Tab Widget

ARIA Tab Widget vs. Link-Based Tabs

Inaccessible
<!-- Links used as tabs — no role="tab", no aria-selected --> <div class="tab-list"> <a href="#" class="tab active" onclick="showTab(0)">Account</a> <a href="#" class="tab" onclick="showTab(1)">Security</a> <a href="#" class="tab" onclick="showTab(2)">Billing</a> </div> <div class="tab-panel active"> Manage your account details and email address. </div> <div class="tab-panel" style="display:none"> Update your password and configure two-factor authentication. </div> <div class="tab-panel" style="display:none"> View invoices and manage your payment methods. </div>
Live Preview
Manage your account details and email address.
Accessible
<div role="tablist" aria-label="Settings"> <button role="tab" id="tab-account" aria-selected="true" aria-controls="panel-account" tabindex="0"> Account </button> <button role="tab" id="tab-security" aria-selected="false" aria-controls="panel-security" tabindex="-1"> Security </button> <button role="tab" id="tab-billing" aria-selected="false" aria-controls="panel-billing" tabindex="-1"> Billing </button> </div> <div role="tabpanel" id="panel-account" aria-labelledby="tab-account"> Manage your account details and email address. </div> <div role="tabpanel" id="panel-security" aria-labelledby="tab-security" hidden> Update your password and configure two-factor authentication. </div> <div role="tabpanel" id="panel-billing" aria-labelledby="tab-billing" hidden> View invoices and manage your payment methods. </div>
Live Preview
Manage your account details and email address.

What’s wrong with link-based tabs?

  • <a> elements have role link, not tab — screen readers won’t identify the widget as a tab list
  • No aria-selected means the user can’t tell which tab is active
  • Arrow key navigation (the expected pattern for tab widgets) does not work
  • Screen reader users navigating by landmarks or roles won’t find a tab widget

What the screen reader announces:

VersionAnnouncement on focus
Inaccessible (link)“Account, link” — no tab context
Accessible (tab)“Account, tab, 1 of 3, selected, Settings tab list”
Inactive tab”Security, tab, 2 of 3, Settings tab list”

Resources