Tabs
An independent React adapter where Ark UI provides the parts and Zag maintains selection, roving tabindex, ARIA relationships, and keyboard behavior.
Design
Functional preview
Running with the independent Ark UI adapter and Zag behavior.
Anatomy
Panel content goes here.
4.ds-tabs) — container with role="tablist".2 Tab (
.ds-tab) — individual tab button with role="tab".3 Active indicator — bottom border on the active tab.
4 Tab panel — content area with
role="tabpanel".
Default tabs
One tab is active, the others are in their default state. Each tab controls a corresponding panel.
This is the Overview panel content. It provides a summary of your account status and recent activity.
Settings panel for profile, notifications preferences, and account security options.
Activity panel listing recent logins, changes, and audit events.
With disabled tab
A disabled tab cannot be interacted with and is skipped during keyboard navigation.
General settings for your account: display name, language, and timezone preferences.
Multiple tabs
Tabs scroll horizontally when they overflow the container.
Dashboard panel showing project metrics, recent commits, and team activity overview.
Token mapping
| Property | Token | CSS variable |
|---|---|---|
| text (inactive) | component.tabs.label.color.default | --ds-tabs-label-color-default |
| text (hover) | component.tabs.label.color.hover | --ds-tabs-label-color-hover |
| text (active) | component.tabs.label.color.active | --ds-tabs-label-color-active |
| text (disabled) | component.tabs.label.color.disabled | --ds-tabs-label-color-disabled |
| active indicator | component.tabs.indicator.fill.active | --ds-tabs-indicator-fill-active |
| divider | semantic.overlay.default | --ds-overlay-default |
| list border | component.tabs.list.border-width.default | --ds-tabs-list-border-width-default |
| item padding | component.tabs.item.{padding-x,padding-top}.default | --ds-tabs-item-padding-*-default |
| gap to indicator | component.tabs.item.indicator-gap.default | --ds-tabs-item-indicator-gap-default |
| indicator height | component.tabs.indicator.height.default | --ds-tabs-indicator-height-default |
| label typography | component.tabs.label.{font-size,line-height,font-weight,letter-spacing}.default | --ds-tabs-label-*-default |
| panel padding | component.tabs.panel.padding-y.default | --ds-tabs-panel-padding-y-default |
| focus ring | component.focus-ring.{color.default,width} | --ds-focus-ring-color-default, --ds-focus-ring-width |
| focus ring radius | component.tabs.focus-ring.radius.default | --ds-tabs-focus-ring-radius-default |
Usage
When to use
Best practices
Content guidelines
| Rule | Example |
|---|---|
| Use nouns as labels | "Overview", "Settings", "Activity" — not "View overview" or "Go to settings" |
| Same hierarchy level | All tabs should represent parallel categories of the same content |
| No icons without text | Always include a text label — icons alone are ambiguous in tab context |
| Keep labels short (1-2 words) | "Pull Requests" — not "View all pull requests in this project" |
Related
When to choose this output
Choose this adapter in projects that adopted Ark UI and Zag as their behavioral architecture. Do not import it into the shadcn/Base UI output.
Implementation
- Status
- Beta
- Distribution
- Source adapter
Adapter source
packages/react/src/ark/tabs.jsx
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from '@tis/react/ark/tabs'Accessibility
Keyboard interaction
| Key | Action |
|---|---|
Tab | Moves focus into the tab list, landing on the active tab |
Arrow Left / Arrow Right | Moves to previous / next tab (auto-activates) |
Home | Moves to the first tab |
End | Moves to the last tab |
Tab (from list) | Moves focus into the active panel content |
Disabled tabs are skipped during arrow key navigation. Only the active tab is in the Tab sequence (tabindex="0"); all others have tabindex="-1".
Accessibility
| WCAG criterion | Requirement | Status |
|---|---|---|
| 4.1.2 Name, Role, Value (A) | role="tablist", role="tab", role="tabpanel". aria-selected on tabs. aria-controls / aria-labelledby linking tabs to panels. | ✓ |
| 2.1.1 Keyboard (A) | Arrow keys navigate between tabs. Tab enters and exits the tab list. | ✓ |
| 2.4.11 Focus Appearance (AA) | Focus ring visible on the active tab when focused. | ✓ |
role="tablist" — on the tab list container.role="tab" — on each tab button.role="tabpanel" — on each content panel.aria-selected="true|false" — indicates the active tab.aria-controls — on each tab, pointing to the corresponding panel ID.aria-labelledby — on each panel, pointing to the corresponding tab ID.aria-disabled="true" — on disabled tabs.
Output responsibility
Preserve the Ark/Zag semantics and validate Escape, outside interaction, ARIA relationships, and focus return.
Validation evidence
Covered by its independent Storybook output plus browser, responsive, keyboard, Axe, and bundle checks.