App mode switcher
A segmented control for product modes like Chat, Work and Code. One clipped pill slides between segments, locked modes say why, and the whole control freezes during a run.
On this page
Chat, Work and Code in every size, stretched, icon only, locked and frozen during a run.
Installation
Usage
import { AppModeSwitcher } from "@/components/ui/chatcn/app-mode-switcher"
<AppModeSwitcher value={mode} onValueChange={setMode} />The defaults are Chat, Work and Code. Pass modes for your own, for example Ask, Plan and Agent. A mode with lockedReason stays visible with a lock; picking it calls onLockedSelect so you can show an upgrade prompt.
Props
| Prop | Type | Notes |
|---|---|---|
modes | AppMode[] | { id, label, icon?, description?, disabled?, lockedReason? } |
value, defaultValue, onValueChange | string | Controlled or uncontrolled |
onLockedSelect | (id) => void | A locked mode was picked |
disabled, disabledReason | Freeze it, and say how to unfreeze it | |
size | "sm" | "md" | 24 or 28 px segments, 44 px on touch |
fullWidth | boolean | Segments share the width, for sidebars |
iconOnly | boolean | Labels move into tooltips |
label | string | Name of the radio group. Default Mode |
Keyboard
| Key | Action |
|---|---|
Tab | Focus the selected mode |
←, →, ↑, ↓ | Move and select, mirrored in RTL |
Home, End | First and last mode |
Accessibility
- A
radiogroupofradiobuttons with roving focus. Locked and disabled modes arearia-disabledand keep their tooltip. - A polite live region announces the new mode.
Motion
- The active style is a second copy of the segments clipped to the selected one. Switching animates the
clip-pathover 240 ms on ease-chatcn-in-out: the pill slides and each label turns ink as it passes, with no layout work. - Keyboard changes and reduced motion jump straight to the new mode.