A styled tab selector component that renders a group of toggle buttons for switching between named views or modes, supporting icons, badges, two visual variants, and disabled states.
## Key Components
### `TabSelectorItem`
Configuration object for each tab button:
| Property | Type | Description |
|---|---|---|
| `id` | `string` | Unique identifier |
| `label` | `string?` | Display text (omit for icon-only tabs) |
| `ariaLabel` | `string?` | Required when `label` is omitted |
| `icon` | `ReactNode?` | Leading icon |
| `disabled` | `boolean?` | Disables this specific tab |
| `badge` | `ReactNode?` | Trailing badge element |
### `TabSelectorProps`
| Property | Type | Description |
|---|---|---|
| `value` | `string` | Currently selected tab id |
| `onValueChange` | `(value: string) => void` | Selection change handler |
| `items` | `TabSelectorItem[]` | Tabs to render |
| `variant` | `'primary' \| 'secondary'` | Active tab highlight style (default: `'primary'`) |
| `label` | `string?` | Optional heading above the selector |
| `disabled` | `boolean?` | Disables the entire selector |
| `scrollable` | `boolean?` | Horizontal scrolling + intrinsic-width tabs (for unbounded tab sets) |
| `className` | `string?` | Additional root container classes |
### `TabSelector`
The exported component. Renders a pill-style container with flex buttons — active tab receives accent background (`primary`) or surface background (`secondary`); inactive tabs are transparent with hover state.
## Usage Example
```typescript
import { TabSelector, TabSelectorItem } from './tab-selector'
import { LayoutGrid, List } from 'lucide-react'
const tabs: TabSelectorItem[] = [
{ id: 'grid', label: 'Grid', icon: },
{ id: 'list', label: 'List', icon:
},
{ id: 'archive', label: 'Archive', disabled: true },
]
function ViewToggle() {
const [view, setView] = React.useState('grid')
return (
)
}
```
## `scrollable` (added with the meeting-scheduler work)
Horizontal-scroll mode for unbounded/dynamic tab sets: the row drops `w-full` for `overflow-x-auto`, items drop `flex-1` for `flex-none basis-auto shrink-0` (intrinsic widths must sum past the container or nothing ever overflows). Default layout unchanged.