a11y: Tabs — tabs and tabpanels are not programmatically associated; other APG tabs-pattern deviations
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 140
- Forks
- 100
- Avg merge
- 1h 3m
- Merged PRs (30d)
- 30
Description
Context
Found during the 2026 Axim-commissioned accessibility audit of Paragon, while documenting Tabs against the APG tabs pattern.
Current behavior (from src/Tabs/__snapshots__/Tabs.test.jsx.snap and React-Bootstrap 1.6.5)
- No tab↔panel association. Tab panels render
role="tabpanel"witharia-labelledby={null}, and tabs renderrole="tab"with noaria-controls(noidlinkage in either direction). - No roving tabindex. Every tab is an
<a href="#">and remains in the page tab sequence; the APG pattern expects a single tab stop for the tablist with arrow-key navigation inside. Arrow keys do work (React-BootstrapAbstractNav: Left/Up previous, Right/Down next, selection follows focus), but Home/End are unsupported and Space does not activate (anchor semantics). - Overflow "More..." control. The responsive overflow renders a
<button aria-haspopup="true">nested inside an<a role="tab">that has noaria-selected— nested interactive elements, and atabthat isn't a tab. Hidden tabs are exposed as dropdown items rather than tabs.
Impact
- (1) Screen reader users landing in a panel get no announcement of which tab labels it; some AT navigation shortcuts between tab and panel don't work. WCAG 1.3.1 Info and Relationships, 4.1.2 Name, Role, Value.
- (2) Extra tab stops slow keyboard traversal of pages with many tabs; missing Space activation is inconsistent with the announced role.
- (3) The nested-interactive structure produces unpredictable focus/announcement behavior in the overflow case.
Suggested direction
Generate stable ids per tab/panel and wire aria-controls/aria-labelledby (fixes 1 without behavior change); consider roving tabindex and Home/End as a follow-up; rework the overflow control so the "More..." toggle is not inside a role="tab" element.
References
- APG tabs pattern: https://www.w3.org/WAI/ARIA/apg/patterns/tabs/
- Related ACT rules: ARIA required context role, ARIA required owned elements, Element with role attribute has required states and properties
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the current behavior captured in src/Tabs/snapshots/Tabs.test.jsx.snap and the React-Bootstrap AbstractNav keyboard behavior. Implement stable tab/panel ids with aria-controls and aria-labelledby, then address the roving-tabindex and overflow-control deviations described in the issue. Done means the Tabs markup follows the referenced APG relationships without nested interactive controls.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, react
- Domain
- accessibility, frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100