# Accessible Tabs: aria-selected, Arrow Keys, and the Roving Tabindex

Source: https://a11yshieldhq.com/blog/2026-11-12-accessible-tabs-aria-selected-arrow-keys.html

November 12, 2026

*A tab widget passes when only the selected tab sits in the Tab order, arrow keys move between tabs, and aria-selected marks the active one. Get any of those wrong and a keyboard user is stuck tabbing through every tab to reach the content.*

## The three failures, in the order a keyboard user meets them

Tab to a product page with a tabbed description, reviews, and shipping section. The first thing a keyboard user meets is not the content. It is every tab, one Tab stop each, because the developer left all of them focusable. Then the arrow keys do nothing, so the only way through is Tab, Tab, Tab. Then the screen reader announces nothing about which tab is selected, because aria-selected sits on the panel instead of the tab, or nowhere at all.

That is the state of most tab widgets on Shopify stores, and it is fixable in an afternoon. The W3C ARIA Authoring Practices Guide publishes the tabs pattern in full, and it has not changed in years: one tab in the Tab order, arrow keys to move, aria-selected on the active tab, and the panel tied to its tab with aria-labelledby. The pattern is a checklist, not a design opinion.

## What the pattern actually requires

Four things. First, the tablist has role="tablist" and each tab has role="tab", with its panel carrying role="tabpanel". Second, only the selected tab has tabindex="0"; every other tab gets tabindex="-1". That is the roving tabindex, and it is the whole trick: Tab enters the widget once, and arrow keys do the moving inside it. Third, aria-selected="true" sits on the active tab and "false" on the rest, and it updates the moment selection changes. Fourth, each tab controls its panel through aria-controls, and each panel points back with aria-labelledby. Miss the last one and the screen reader user hears a tab with no content attached to it.

Two details trip up Shopify themes in particular. The tabs are often built from links or buttons that the theme author never gave a role, so the screen reader announces a link that does nothing when activated with Enter. And the panels are often hidden with display:none on load but left in the accessibility tree by a CSS class that only hides visually. A panel that is visually hidden but still announced is worse than a missing panel: the user hears content for a tab they never selected.

## Arrow keys, and the choice nobody documents

The pattern offers two activation modes and the theme has to pick one. In automatic activation, moving focus to a tab selects it immediately. In manual activation, arrow keys move focus and Enter or Space selects. Automatic is the right default for most storefront tabs, because the panels are short and switching is cheap. Manual fits when selecting a tab fires a network request or rebuilds a product view, and you do not want that firing on every arrow press.

The mistake is shipping neither: arrow keys that move focus without selecting, and no Enter handler, so the keyboard user can see the tabs and never open one. Test it with the keyboard alone. If you can reach every tab but cannot open any of them, the widget fails WCAG 2.1.1 Keyboard, the most basic criterion in the standard.

## The test that takes four minutes

Open the page, press Tab until the tablist has focus, and count the Tab stops. One stop to enter the widget is correct. Then press the Right Arrow key. Focus should move to the next tab and, in automatic mode, select it. Press Home and End; they should jump to the first and last tabs. Then check with a screen reader, or with the browser accessibility inspector, that the selected tab announces as selected and that the panel announces its label. If any of that fails, the fix is in the widget's JavaScript, not in the theme's CSS.

Tabs are one of the few widgets where the standard is exact and the fix is small. A storefront that gets tabs right has usually gotten the harder widgets right too, because the same developer who understands roving tabindex understands focus management everywhere else. Start here, and the rest of the audit gets easier. The same audit usually catches [the drag-only sortable list that fails 2.5.7](2026-11-11-dragging-movements-2-5-7-single-pointer.md) and [the help link that moves mid-checkout under 3.2.6](2026-11-11-consistent-help-3-2-6-help-moves-checkout.md).

## Frequently asked questions

### Do all the tabs need to be in the Tab order?

No. Only the selected tab should have tabindex 0. The rest get tabindex -1, and arrow keys move between them. That is the roving tabindex, and it keeps a five-tab widget to one Tab stop instead of five.

### Should arrow keys select a tab or just move focus?

Either, but pick one and document it. Automatic activation selects on focus, which suits short panels. Manual activation waits for Enter or Space, which suits tabs that fire a network request or rebuild the product view. Shipping neither is the failure.

### Where does aria-selected go?

On the tab element itself, the one with role tab. It never goes on the panel. The active tab gets aria-selected true and the rest get false, updated the moment selection changes.

### My tabs are links, not buttons. Does that matter?

Yes. A link promises navigation, and a tab does not navigate. Use a button with role tab, or keep the button and add the tab roles and keyboard handling. A link that opens a panel instead of a page confuses screen readers and fails the link purpose check.
