# Segmented Mode Picker

Segmented Mode Picker [#segmented-mode-picker]

The **Segmented Mode Picker** is a pill toggle for 2–4 mutually-exclusive named modes — OCR content-type, summary length, translation formality, and the like. It renders as a classic segmented control: a muted track with the active segment raised on a `bg-background&#x60; chip. It ships a typed &#x2A;*`TabBar`** variant: an underline tab bar with `disabledTabs` gating (e.g. an "Inspect" tab disabled until a model is selected).

Both are fully controlled and purely presentational.

**When to use it:** switch between a small fixed set of options that change how a task runs (length, format, mode).

Preview [#preview]

```tsx
'use client';

import { useState } from 'react';
import { SegmentedModePicker, TabBar } from '@/components/segmented-mode-picker';

/**
 * Demo for the SegmentedModePicker + TabBar, used by the docs live preview.
 * Shows the pill toggle and the typed tab variant (with a disabled tab). Pure
 * UI — no model download.
 */
export default function SegmentedModePickerDemo() {
  const [length, setLength] = useState<'short' | 'medium' | 'long'>('medium');
  const [tab, setTab] = useState<'browse' | 'inspect'>('browse');

  return (
    <div className="flex flex-col gap-6">
      <SegmentedModePicker
        aria-label="Summary length"
        items={[
          { id: 'short', label: 'Short' },
          { id: 'medium', label: 'Medium' },
          { id: 'long', label: 'Long' },
        ]}
        selectedId={length}
        onSelect={setLength}
      />
      <TabBar
        aria-label="Explorer mode"
        tabs={[
          { id: 'browse', label: 'Browse' },
          { id: 'inspect', label: 'Inspect (select a model first)' },
        ]}
        activeId={tab}
        onSelect={setTab}
        disabledTabs={['inspect']}
      />
    </div>
  );
}
```

Installation [#installation]

```bash
npx shadcn@latest add @localmode/ui/input-controls/segmented-mode-picker
```

Dependencies [#dependencies]

* **Data source:** renders the controlled `items` / `selectedId` you pass and emits `onSelect` — works with any backend. Recommended LocalMode producer: feed the selected mode into any task hook, e.g. `useSummarize` length (optional).
* `clsx` + `tailwind-merge` — via the shared `cn()` util (installed automatically as a registry dependency)

Files installed [#files-installed]

* `segmented-mode-picker.tsx` — both `SegmentedModePicker` and `TabBar`
* `lib/utils.ts` — the `cn()` helper (if not already present)

Props [#props]

**SegmentedModePicker**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `items` | `array` | — | **Required.** The 2–4 mutually-exclusive modes to render. |
| `selectedId` | `TId` | — | **Required.** Currently selected mode id. |
| `onSelect` | `function` | — | **Required.** Fired with the selected mode id when the user activates a mode. |
| `accent` | `string` | `"bg-background text-foreground shadow-sm"` | Classes applied to the active segment. Defaults to a raised segmented-control look (`bg-background text-foreground shadow-sm`) that reads clearly on the muted track in both light and dark themes. |
| `aria-label` | `string` | — | Accessible label for the group. |

`TabBar` [#tabbar]

**TabBar**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `tabs` | `array` | — | **Required.** The tabs to render. |
| `activeId` | `TId` | — | **Required.** Currently active tab id. |
| `onSelect` | `function` | — | **Required.** Fired with the tab id when a (non-disabled) tab is activated. |
| `disabledTabs` | `array` | — | Ids of tabs that are present but not selectable. |
| `aria-label` | `string` | — | Accessible label for the tablist. |

Examples [#examples]

Summary length picker [#summary-length-picker]

```tsx
import { useState } from 'react';
import { SegmentedModePicker } from '@/components/segmented-mode-picker';

export function LengthPicker() {
  const [mode, setMode] = useState<'short' | 'medium' | 'long'>('medium');
  return (
    <SegmentedModePicker
      items={[
        { id: 'short', label: 'Short' },
        { id: 'medium', label: 'Medium' },
        { id: 'long', label: 'Long' },
      ]}
      selectedId={mode}
      onSelect={setMode}
    />
  );
}
```

Typed `TabBar` with a disabled tab [#typed-tabbar-with-a-disabled-tab]

```tsx
<TabBar
  tabs={[
    { id: 'browse', label: 'Browse' },
    { id: 'inspect', label: 'Inspect' },
  ]}
  activeId={tab}
  onSelect={setTab}
  disabledTabs={['inspect']}
/>
```

Customization [#customization]

The active segment is raised on a `bg-background text-foreground shadow-sm` chip over the muted track by default; pass an `accent` class string to restyle it. Both components are generic over the id type, so `onSelect` is typed to your union. Everything is theme-driven via shadcn/ui CSS variables — edit the copied file to change spacing, sizing, or the underline treatment.

Accessibility [#accessibility]

The segmented control is a `role="radiogroup"` of `role="radio"` segments that announce their selected state via `aria-checked`; the `TabBar` variant is a `role="tablist"` of `role="tab"` items. The segment row `flex-wrap`s and its labels no longer `truncate`, so a long mode/VAD label wraps inside its segment (multi-line) instead of clipping at narrow widths (down to 375px). Selection is fully keyboard-operable and `getByRole('radio', { name })` resolves each segment.