ADVERTISEMENT
Google AdSense Slot (headerLeaderboard)

Set your Publisher ID in src/config/ads.ts to display live ads.

How-To: 50+ Real-World Developer Recipes• 6 min read

How To: Accessible Tabbed Interfaces

Build keyboard-navigable, accessible tabbed interfaces following the WAI-ARIA tabs design pattern.

The WAI-ARIA Tabs Design Pattern

Accessible tabbed widgets require specific semantic roles and states so screen reader users understand the component hierarchy:

role="tablist": Wraps the collection of tab buttons.
role="tab": Applied to each individual tab button, with aria-selected="true|false" and aria-controls="panel-id".
role="tabpanel": Applied to the container displaying the active panel content, with aria-labelledby="tab-id".
JavaScript Tab Controller
function setupTabs() {
  const tabs = document.querySelectorAll('[role="tab"]');
  const panels = document.querySelectorAll('[role="tabpanel"]');

  tabs.forEach(tab => {
    tab.addEventListener('click', () => {
      // 1. Deactivate all tabs
      tabs.forEach(t => {
        t.setAttribute('aria-selected', 'false');
        t.classList.remove('active');
      });

      // 2. Hide all panels
      panels.forEach(p => p.hidden = true);

      // 3. Activate clicked tab
      tab.setAttribute('aria-selected', 'true');
      tab.classList.add('active');

      // 4. Reveal controlled panel
      const targetPanel = document.getElementById(tab.getAttribute('aria-controls'));
      if (targetPanel) targetPanel.hidden = false;
    });
  });
}
ADVERTISEMENT
Google AdSense Slot (inContentBanner)

Set your Publisher ID in src/config/ads.ts to display live ads.

Test Yourself With Exercise

+15 XP

Which ARIA attribute indicates whether a tab button is currently active and selected?