# Tabs

> Slug: `tabs` · Category: Navigation

## When to use
- Switching between mutually exclusive panels of related content within a single page (no URL change).
- Organizing 2–6 sections of related data so users can compare or browse without scrolling.

## When NOT to use
- For section-level navigation that changes the URL — use `Subnavigation` (links).
- For sequential flows like checkout — use `StepIndicator`.
- For a single yes/no choice — use `Toggle` or radios.
- For more than ~6 items — consider `MenuDropdown` or a different layout.

## Variants & decision tree
`Tabs` is data-driven. Pass a `tabs` array of `{ id, label?, icon?, iconA11yLabel?, content }` items. The component handles tablist + panels and manages roving focus internally.

- **Label-only** → set `label`.
- **Icon-only** → omit `label`, set `icon` + `iconA11yLabel` (a11y label is required).
- **Icon + label** → set both. The `iconA11yLabel` is composed into the tab's `aria-label`.

Use `defaultActiveTab` for uncontrolled or `activeTab` + `onTabChange` for controlled mode. Always provide `ariaLabel` describing the tablist.

## States
- Active tab is the panel currently shown.
- Disabled tabs are not exposed by Arrow's tab data shape — gate panels via your own logic if needed.
- `inverse` swaps to dark-background styling.
- Force-state props (`hover`, `focused`) per tab for visual testing.

## Props
| Prop | Type | Default | Description |
|---|---|---|---|
| `tabs` | `TabItem[]` | — | Required tabs/panels data. |
| `id` | `string` | — | ID for the tablist (auto-derives `tabId`/`panelId`). |
| `defaultActiveTab` | `string` | first tab | Uncontrolled active tab id. |
| `activeTab` | `string` | — | Controlled active tab id. |
| `onTabChange` | `(id: string) => void` | — | Called when active tab changes. |
| `ariaLabel` | `string` | — | Accessible label for the tablist. |
| `inverse` | `boolean` | inherited | Dark-background styling. |

## Usage examples
```tsx
<Tabs
  ariaLabel="Account sections"
  defaultActiveTab="overview"
  onTabChange={(id) => track('tab', id)}
  tabs={[
    { id: 'overview', label: 'Overview', content: <Overview /> },
    { id: 'billing',  label: 'Billing',  content: <Billing /> },
    { id: 'settings', label: 'Settings', content: <Settings /> },
  ]}
/>
```

## Composition
Tabs are a flagship pattern; data-driven only — `Tabs.Tab` / compound children do **not** exist. Each panel can hold any layout: Cards, Forms, Tables, even nested `Accordion`s.

## Accessibility
- Renders `role="tablist"` + `role="tab"` + `role="tabpanel"` with linked `id` / `aria-controls` / `aria-labelledby`.
- Roving tabindex: only the active tab is tabbable; ArrowLeft/Right cycle, Home/End jump (managed by `useRovingTabIndex`).
- Provide `ariaLabel` describing the tablist's purpose.
- For icon-only tabs, set `iconA11yLabel` — without it the tab has no name.

## Do / Don't
- Do pass content as `ReactNode` in each item's `content`.
- Do use `Subnavigation` when each section has its own URL.
- Don't write `<Tabs.Tab>` or `<Tabs.Panel>` — the API is `tabs={[…]}`.
- Don't put a form spanning multiple tabs unless you persist values across switches.
