Skip to content
ShellApp mode switcherMenu

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.

Chat, Work and Code in every size, stretched, icon only, locked and frozen during a run.

Installation

$ pnpm dlx shadcn@latest add https://chatcn.dev/r/app-mode-switcher.json

Usage

tsx
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

PropTypeNotes
modesAppMode[]{ id, label, icon?, description?, disabled?, lockedReason? }
value, defaultValue, onValueChangestringControlled or uncontrolled
onLockedSelect(id) => voidA locked mode was picked
disabled, disabledReasonFreeze it, and say how to unfreeze it
size"sm" | "md"24 or 28 px segments, 44 px on touch
fullWidthbooleanSegments share the width, for sidebars
iconOnlybooleanLabels move into tooltips
labelstringName of the radio group. Default Mode

Keyboard

KeyAction
TabFocus the selected mode
←, →, ↑, ↓Move and select, mirrored in RTL
Home, EndFirst and last mode

Accessibility

  • A radiogroup of radio buttons with roving focus. Locked and disabled modes are aria-disabled and 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-path over 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.