web/mobile uses shadcn/ui on Tailwind v4 with CSS variables. No antd, ever.
Installing registry components
- Prefer the shared primitive first: if
@agenta/ui/uialready exports it (Button,Input,Select,Dialog,Sheet,DropdownMenu, ...), import it from there. Do not install a local copy of a component the kit already has —buttonin particular lives ONLY in@agenta/ui(Nova preset on the sharedcontrol-*scale), so there is one button. - Use
Input,SkeletonBlockandSheetfrom@agenta/ui/ui. A localsrc/components/ui/sheet.tsxstill backsChannelsPanelSheetonly; do not import it anywhere else, and do not add other local duplicates of kit components. - Otherwise install via the CLI from
web/mobile/:pnpm dlx shadcn@latest add <component>. - Components land in
src/components/ui/(aliases incomponents.json). They are owned code: you may adapt them, but keep diffs minimal and expressed in semantic tokens so upstream refreshes stay cheap. - The CLI adds any peer deps (e.g.
@radix-ui/react-slot) toweb/mobile/package.json— commit the manifest andweb/pnpm-lock.yamlchanges together with the component. - Never copy component source from the shadcn website by hand; the CLI resolves the Tailwind v4 variant correctly.
- Installing a shadcn component that references a NEW token (e.g.
bg-sidebar,chart-*) requires extending VARS inscripts/generate-shadcn-tokens.ts+ the@theme inlinemap inglobals.cssfirst — Tailwind v4 silently generates nothing for unmapped tokens.
Theming — the token bridge
- shadcn variables (
--background,--primary, ...) are NOT hand-maintained. They are generated intosrc/styles/theme.generated.cssfromweb/oss/src/styles/theme/palette.tsbyscripts/generate-shadcn-tokens.ts. The script also readscontrolScale.tsfrom the same folder. That theme folder is the one part ofweb/ossstill in use: edit it for color changes, but touch nothing else inweb/oss. - To change a color: edit
palette.ts(if the design-system value is wrong) or the ROLE MAP in the script (if the mapping is wrong), then runpnpm --filter @agenta/mobile generate:tokensand commit the regenerated CSS. - Never edit
theme.generated.cssdirectly; never introduce raw hex values in components — if a needed role is missing, extend the bridge. - Dark mode is the
.darkclass on<html>(@custom-variant darkinglobals.css), set pre-paint by the_document.tsxinit script from the sharedagenta-themelocalStorage key. Both themes must be checked for every new surface.
Extending components
- Wrap, don't fork: feature-specific variants live in
src/features/*as thin wrappers overcomponents/ui/*primitives (cva variants where appropriate). - Use the
cnutil from@/lib/utilsfor all class merging.
Chat UI
- There are no Vercel AI Elements in the app and no
src/components/ai-elements/. Chat lives insrc/features/chat/(ChatScreen,Composer,LiveConversation,TranscriptTurns, ...), built on@agenta/chat(state,model,hooks,components, ... subpaths) and the Lexical-based@agenta/ui/rich-chat-input. Markdown streams throughstreamdown. - Behavior comes from
@agenta/chathooks — never re-implement orchestration inside a rendered component. - Icons: both
@phosphor-icons/react(most files) andlucide-react(older files and shadcn registry output) are in use. Match the surrounding feature.