Tabs
Tabbed content navigation with keyboard support and ARIA roles.
<arc-tabs> Overview
Tabs organize related content into separate panels that share the same page space. Only one panel is visible at a time, letting users switch context without navigating away. This makes Tabs ideal for grouping settings pages, documentation sections, or dashboard views where horizontal real estate is limited.
The component follows the WAI-ARIA Tabs pattern out of the box. Each tab button carries role="tab" and its corresponding panel carries role="tabpanel", linked via aria-controls and aria-labelledby. Focus management uses a roving tabindex so arrow keys move between tabs while Tab moves focus out of the tab list entirely, matching the behavior users expect from native OS tab controls.
Switching tabs moves one indicator between them — the underline in the default variant, the ground behind the selection in pills and in vertical bars — and the incoming panel rises into place rather than cutting. Both are CSS, both stop under prefers-reduced-motion, and a bar holding more tabs than room scrolls the selection into view without moving the page around it. Panels that are not active are removed from the accessibility tree and hidden with display: none so screen readers never encounter stale content.
Guidelines
When to use
- Use Tabs when content sections are closely related and users need to compare or switch between them frequently
- Keep tab labels short — one or two words — so the entire tab bar fits without scrolling
- Set a sensible default `selected` index (usually 0) so the component is never empty on first render
- Provide meaningful panel content for every tab; avoid empty or placeholder panels in production
- Use the disabled state for tabs that are temporarily unavailable rather than hiding them entirely
When not to use
- Do not use Tabs for sequential steps — use a Stepper component instead
- Avoid nesting Tabs inside Tabs; the double tab bar creates confusion for keyboard and screen-reader users
- Do not place critical actions (like a Save button) inside a non-default tab where users may never see them
- Avoid more than five or six tabs in a single group — consider a dropdown or sidebar navigation for larger sets
- Do not rely on tab order to imply a workflow; tabs should be independently meaningful
Features
- Arrow-key keyboard navigation between tabs (left/right for horizontal, up/down for vertical)
- Automatic WAI-ARIA roles: tab, tablist, and tabpanel with proper `aria-controls` linking
- One indicator that travels between tabs, and a panel that rises in behind it
- Disabled tab support — individual tabs can be non-interactive while remaining visible
- Programmatic selected index via the `selected` property
- Roving tabindex so only the active tab participates in the page Tab order
- Supports rich HTML content inside each panel, not just plain text
Preview
Usage
Layout and styling work without JavaScript via the HTML/CSS versions. Interactive features like events and state management require the Web Component or a framework wrapper.
<arc-tabs>
<arc-tab label="Overview">
ARC UI is a framework-agnostic component library built on web standards.
</arc-tab>
<arc-tab label="Features">
Keyboard navigation, ARIA roles, smooth transitions, and lazy rendering.
</arc-tab>
<arc-tab label="Changelog">
v2.4.0 — Disabled tab support and improved focus-visible ring styling.
</arc-tab>
</arc-tabs> import { Tabs, Tab } from '@arclux/arc-ui-react';
export default function Example() {
return (
<Tabs>
<Tab label="Overview">
ARC UI is a framework-agnostic component library built on web standards.
</Tab>
<Tab label="Features">
Keyboard navigation, ARIA roles, smooth transitions, and lazy rendering.
</Tab>
<Tab label="Changelog">
v2.4.0 — Disabled tab support and improved focus-visible ring styling.
</Tab>
</Tabs>
);
} <script setup>
import { Tabs, Tab } from '@arclux/arc-ui-vue';
</script>
<template>
<Tabs>
<Tab label="Overview">
ARC UI is a framework-agnostic component library built on web standards.
</Tab>
<Tab label="Features">
Keyboard navigation, ARIA roles, smooth transitions, and lazy rendering.
</Tab>
<Tab label="Changelog">
v2.4.0 — Disabled tab support and improved focus-visible ring styling.
</Tab>
</Tabs>
</template> <script>
import { Tabs, Tab } from '@arclux/arc-ui-svelte';
</script>
<Tabs>
<Tab label="Overview">
ARC UI is a framework-agnostic component library built on web standards.
</Tab>
<Tab label="Features">
Keyboard navigation, ARIA roles, smooth transitions, and lazy rendering.
</Tab>
<Tab label="Changelog">
v2.4.0 — Disabled tab support and improved focus-visible ring styling.
</Tab>
</Tabs> import { Component } from '@angular/core';
import { Tabs, Tab } from '@arclux/arc-ui-angular';
@Component({
imports: [Tabs, Tab],
template: `
<arc-tabs>
<arc-tab label="Overview">
ARC UI is a framework-agnostic component library built on web standards.
</arc-tab>
<arc-tab label="Features">
Keyboard navigation, ARIA roles, smooth transitions, and lazy rendering.
</arc-tab>
<arc-tab label="Changelog">
v2.4.0 — Disabled tab support and improved focus-visible ring styling.
</arc-tab>
</arc-tabs>
`,
})
export class TabsDemoComponent {} import { Tabs, Tab } from '@arclux/arc-ui-solid';
export default function TabsDemo() {
return (
<Tabs>
<Tab label="Overview">
ARC UI is a framework-agnostic component library built on web standards.
</Tab>
<Tab label="Features">
Keyboard navigation, ARIA roles, smooth transitions, and lazy rendering.
</Tab>
<Tab label="Changelog">
v2.4.0 — Disabled tab support and improved focus-visible ring styling.
</Tab>
</Tabs>
);
} import { Tabs, Tab } from '@arclux/arc-ui-preact';
export default function TabsDemo() {
return (
<Tabs>
<Tab label="Overview">
ARC UI is a framework-agnostic component library built on web standards.
</Tab>
<Tab label="Features">
Keyboard navigation, ARIA roles, smooth transitions, and lazy rendering.
</Tab>
<Tab label="Changelog">
v2.4.0 — Disabled tab support and improved focus-visible ring styling.
</Tab>
</Tabs>
);
} <arc-tabs>
<arc-tab label="Overview">
ARC UI is a framework-agnostic component library built on web standards.
</arc-tab>
<arc-tab label="Features">
Keyboard navigation, ARIA roles, smooth transitions, and lazy rendering.
</arc-tab>
<arc-tab label="Changelog">
v2.4.0 — Disabled tab support and improved focus-visible ring styling.
</arc-tab>
</arc-tabs> API
-
selectednumber0 - Zero-based index of the currently active tab. Changing this value programmatically switches the visible panel and updates ARIA attributes. Out-of-range values are clamped to the nearest valid index.
-
align'start' | 'center' | 'end''start' - Aligns the tab list. Options: 'start', 'center', 'end'.
-
variant'underline' | 'pills''underline' - Visual style of the tabs. Options: 'underline', 'pills'.
-
orientation'horizontal' | 'vertical''horizontal' - Layout direction of the tab list. Use 'vertical' to place tabs in a sidebar column with the panel to the right. Arrow-key navigation automatically switches to up/down in vertical mode.
Events
-
arc-change - Fired when the active tab changes
Tab
<arc-tab> An individual tab panel within a Tabs group. Each Tab renders a button in the tab bar and owns its associated content panel. Use this sub-component when you need fine-grained control over individual tab behavior, such as disabling a specific tab or attaching per-tab event listeners.
-
labelstring'' - Text displayed on the tab button. Keep labels concise — one or two words — to prevent the tab bar from overflowing.
-
disabledbooleanfalse - When true, the tab button is dimmed, is skipped by the arrow keys and cannot be selected by click. A disabled tab that is already selected stays visible — disabling is not a way to hide a panel.
See Also
- Accordion Expandable content sections with smooth height animations. Ideal for FAQs, settings panels, and any UI that benefits from progressive disclosure.
- Segmented Control A radio-group-style toggle bar that renders slotted arc-option elements as a row of mutually exclusive buttons with an active highlight.