Tab Bar
TabBar is a headless tab list container that provides the ARIA tablist role and built-in keyboard navigation for tab interfaces. It manages arrow key navigation between tab elements, allowing users to cycle through tabs with Left/Right arrow keys, and jump to the first or last tab with Home/End keys.
Use TabBar when building a tabbed interface where users switch between different views or content panels. The component wraps tab buttons in a <div> with role="tablist" and handles keyboard interactions automatically. Consumer provides tab buttons as children (each with role="tab" and aria-selected) and is responsible for managing selection state and associating tab panels.
Implementation Notes
- Renders a
<div>element withrole="tablist"for ARIA semantics - Uses two-way
thisbinding to capture a reference to the tablist element for DOM queries - Queries all child elements with
[role='tab']to build the list of navigable tabs - Implements keyboard navigation via an
onkeydownhandler:- ArrowRight moves focus to the next tab (wraps to first)
- ArrowLeft moves focus to the previous tab (wraps to last)
- Home moves focus to the first tab
- End moves focus to the last tab
tablistRef)aria-selected state on tab buttons and displaying corresponding tab panelsrestProps onto the div for consumer customizationProps
label: string (required) -- accessible label for the tab list, applied viaaria-labelchildren: slot (required) -- tab button elements to render inside the tablist...restProps: unknown -- additional attributes spread onto the<div>element
Usage
<TabBar label="Medication information">
<TabBarButton selected controls="panel-about">About this medicine</TabBarButton>
<TabBarButton controls="panel-dosage">Dosage</TabBarButton>
<TabBarButton controls="panel-side-effects">Side effects</TabBarButton>
<TabBarButton controls="panel-interactions">Interactions</TabBarButton>
</TabBar>
Keyboard Interactions
- Arrow Right: Moves focus to the next tab; wraps from the last tab to the first
- Arrow Left: Moves focus to the previous tab; wraps from the first tab to the last
- Home: Moves focus to the first tab
- End: Moves focus to the last tab
- Tab: Moves focus into and out of the tab list (only one tab in the list is in the tab order at a time)
ARIA
role="tablist"-- identifies the container as a list of tabsaria-label={label}-- provides an accessible name for the tab list so screen readers announce its purpose- Child tabs should have
role="tab"andaria-selectedattributes (managed by consumer) - Associated tab panels should have
role="tabpanel"witharia-labelledbypointing to the corresponding tab (managed by consumer)
When to Use
- Use to let users navigate between related sections of content without leaving the page
- Use when content naturally divides into distinct categories that users will access one at a time
- Use to reduce page length by showing only the relevant section
- Use when there are 2-6 sections -- enough to justify tabs, few enough to remain scannable
When Not to Use
- Do not use when users need to compare information across sections -- show all content on one page or use a Table
- Do not use with only one section -- show the content directly without tabs
- Do not use for sequential steps or progress -- use TaskList or a multi-page form with BackLink
- Do not use for navigation between different pages -- use NavigationMenu or BreadcrumbNav instead
Headless
This headless component renders a <div> with role="tablist", aria-label, and built-in arrow key navigation (Left/Right to cycle tabs, Home/End to jump to first/last). The consumer provides tab button children, manages selection state, associates tab panels, and applies all visual styling.
Styles
The consumer provides all CSS styling. The component renders with a .tab-bar class for targeting. No default styles are included — this is a fully headless component.
Testing
- Verify the component renders a
<div>element with classtab-bar - Verify role="tablist"` -- identifies the container as a list of tabs
- Verify aria-label={label}` -- provides an accessible name for the tab list so screen readers announce its purpose
- Verify Child tabs should have
role="tab"andaria-selectedattributes (managed by consumer) - Verify Associated tab panels should have
role="tabpanel"witharia-labelledbypointing to the corresponding tab (managed by consumer) - Verify keyboard interactions work correctly
- Verify pass-through attributes are applied
Advice
- Designers: Clearly indicate the active tab with a visual treatment such as an underline, background color, or border. Keep tab labels short and descriptive.
- Developers: Pair each TabBarButton with a corresponding
role="tabpanel"element linked viaaria-controlsandaria-labelledby. Manage thearia-selectedstate on child tab buttons from the consumer.
Composition
TabBar uses the Bar/BarButton composition pattern. Place TabBarButton components as children inside TabBar. The TabBar provides the role="tablist" container with keyboard navigation, while each TabBarButton provides role="tab" with selection state and panel association.
Related components
tab-bar-button— one tab button in a tab group
References
- WAI-ARIA Tabs Pattern: https://www.w3.org/WAI/ARIA/apg/patterns/tabs/
- WAI-ARIA Authoring Practices - Tabs Example: https://www.w3.org/WAI/ARIA/apg/patterns/tabs/examples/tabs-automatic/