# Control UI — extended agent reference > 100+ open-source React components for Tailwind and shadcn/ui: pickers, trees, editors, app blocks, and AI chat, with swappable skins. Source you own. ## How to use this registry - MCP: `https://control-ui.dev/api/mcp` speaks streamable HTTP Model Context Protocol with `search_registry`, `get_registry_item` and `list_registry`, no authentication; the card is at `https://control-ui.dev/.well-known/mcp/server-card.json`. - Markdown: request any canonical documentation page with `Accept: text/markdown` to read it as markdown; browsers still get HTML. - Discover: `GET https://control-ui.dev/api/registry` (or the static `https://control-ui.dev/r/agent-index.json`) → the agent-friendly index. - shadcn catalog: `https://control-ui.dev/r/registry.json` → the official registry schema used by shadcn tooling. - Read one item: `GET https://control-ui.dev/api/registry/` → source files, install command, and deps. - Search: `GET https://control-ui.dev/api/registry/search?q=`. - Skin anatomy: `https://control-ui.dev/r/contract/index.json` lists every paint family; read `https://control-ui.dev/r/contract/.json` for the family you restyle, its `.contrast.json` sibling for what paints behind it, and `GET https://control-ui.dev/api/registry/` for one item's slice. Do not guess selectors. - Component knobs: every component paints through registered `--cui--*` CSS custom properties (e.g. `--cui-button-radius`). A family slice carries its knobs with syntax and recipe defaults; restyle per instance via the typed `style` prop or from a skin, never by overriding Tailwind classes. - Install: run the item's `install` command (`npx shadcn@latest add `). Installed files are yours to own and edit. - Skinning: core has neutral defaults. Consumer CSS owns the theme; optional SkinProvider configs and registry presets add scoped behavior and adornments. - Setting up a repository from scratch: `https://control-ui.dev/r/setup-prompt.md` is the full procedure, including the wiring steps that fail silently when skipped. - Working where Control UI is already installed: `https://control-ui.dev/r/control-ui-skill.md` is the installable skill carrying the token contract and the working rules (mirrored at `skills/control-ui/SKILL.md`). ## Contrast (check this before shipping a skin) - Every painted knob with what the browser actually paints behind it, one file per paint family: `https://control-ui.dev/r/contract/.contrast.json`, harvested from rendered components and verified against the browser. `https://control-ui.dev/r/contract/index.json` names the families and how many probes each has. - A probe's `anatomy` indexes into the file's `anatomies`: the paint stack under the part, back to front, its ancestors but also a sibling indicator that slides beneath it. The last node is the part itself. - A part's contrast is decided by the knob it paints from **and** by every knob in that stack, never by theme tokens alone: re-valuing a surface knob changes the contrast of text you did not touch. - For each probe: rebuild `anatomy` as nested elements, resolve `knobs.text` on the last one, composite the stack's fills behind it, and clear `rendersText ? 4.5 : 3`:1. - `state: true` means the paint waits on an interaction, so force `knobs.fill` onto the part instead of expecting it to paint itself. - A knob you leave alone still paints — it keeps its recipe default and still has to clear the ratio under your surfaces. - The family's `/r/contract/.json` slice lists `uncovered`: the knobs no documented route renders. Nothing measured them, so treat them as unverified rather than passing. - After importing a theme, run `node /scripts/control-ui-doctor.mjs --contrast`: it resolves every required pair that theme tokens alone decide, offline, and exits non-zero on a failure. Pairs a recipe paints are only measured by the rendered audit at `/theme-accessibility`. ## Catalog ### Components **Actions** — Trigger a command, directly or from a menu of commands. - `button` (Button) — Accessible action button with size, variant, and semantic tone support. — `npx shadcn@latest add https://control-ui.dev/r/button.json` - `dropdown-menu` (DropdownMenu) — Dropdown menu for actions, resources, labels, separators, and submenus. — `npx shadcn@latest add https://control-ui.dev/r/dropdown-menu.json` - `context-menu` (Context Menu) — Pointer-positioned right-click and long-press menu with nested actions and selection controls. — `npx shadcn@latest add https://control-ui.dev/r/context-menu.json` - `command` (Command) — Command palette with token-matched dialog, input, and result rows. — `npx shadcn@latest add https://control-ui.dev/r/command.json` - `menubar` (Menubar) — Desktop command bar with nested menus, shortcuts, and separators. — `npx shadcn@latest add https://control-ui.dev/r/menubar.json` - `button-group` (Button group) — Joined button group for toolbars, split actions, and segmented controls. — `npx shadcn@latest add https://control-ui.dev/r/button-group.json` - `trigger-menu` (Trigger Menu) **[beta]** — Caret-anchored command or mention menu for text editors. — `npx shadcn@latest add https://control-ui.dev/r/trigger-menu.json` - `toolbar` (Toolbar) — Roving-focus toolbar for editor controls and compact actions. — `npx shadcn@latest add https://control-ui.dev/r/toolbar.json` **Forms** — Capture a value from the user — typed, picked, toggled, or uploaded — plus the scaffolding that labels and validates it. - `slider` (Slider) — Single-value range control with branded and plain treatments. — `npx shadcn@latest add https://control-ui.dev/r/slider.json` - `channel-strip` (ChannelStrip) — Console mixer strips — title, fader, level meter with dB scale, value, and controls — whose rows line up across a Mixer even when a strip leaves a part out. — `npx shadcn@latest add https://control-ui.dev/r/channel-strip.json` - `select` (Select) — Single-choice picker with a token-matched trigger and floating list. — `npx shadcn@latest add https://control-ui.dev/r/select.json` - `toggle` (Toggle) — Pressed-state button and toggle group built on the Button surface. — `npx shadcn@latest add https://control-ui.dev/r/toggle.json` - `switch` (Switch) — On/off control with token-driven track, thumb, and press motion. — `npx shadcn@latest add https://control-ui.dev/r/switch.json` - `input` (Input) — Text field primitive sized and styled to match other controls. — `npx shadcn@latest add https://control-ui.dev/r/input.json` - `input-group` (Input group) — Joined input wrapper for addons, icons, and a shared focus indicator. — `npx shadcn@latest add https://control-ui.dev/r/input-group.json` - `dropzone` (Dropzone) **[beta]** — Composable file intake with validation, managed selection, and drag-activated overlays. — `npx shadcn@latest add https://control-ui.dev/r/dropzone.json` - `phone-input` (Phone input) — International phone field with country search, E.164 values, and Zod validation helpers. — `npx shadcn@latest add https://control-ui.dev/r/phone-input.json` - `checkbox` (Checkbox) — Single checkbox control with checked and indeterminate states. — `npx shadcn@latest add https://control-ui.dev/r/checkbox.json` - `radio-group` (Radio group) — Single-choice radio set for plans, filters, and option lists. — `npx shadcn@latest add https://control-ui.dev/r/radio-group.json` - `field` (Field) — Form field wrapper for labels, descriptions, errors, and validity state. — `npx shadcn@latest add https://control-ui.dev/r/field.json` - `form` (Form) — Form wrapper that coordinates field validation and returned errors. — `npx shadcn@latest add https://control-ui.dev/r/form.json` - `native-select` (Native select) — Native select control styled to match the Control UI control family. — `npx shadcn@latest add https://control-ui.dev/r/native-select.json` - `textarea` (Textarea) — Multiline text field with CSS-first auto-growth. — `npx shadcn@latest add https://control-ui.dev/r/textarea.json` - `input-otp` (Input OTP) — One-time-code field with grouped, focus-aware slots. — `npx shadcn@latest add https://control-ui.dev/r/input-otp.json` - `combobox` (Combobox) — Searchable single-select with input and floating option list. — `npx shadcn@latest add https://control-ui.dev/r/combobox.json` - `checkbox-group` (Checkbox Group) — Multi-select checkbox set with shared state and select-all support. — `npx shadcn@latest add https://control-ui.dev/r/checkbox-group.json` - `autocomplete` (Autocomplete) — Free-text input with search-as-you-type suggestions. — `npx shadcn@latest add https://control-ui.dev/r/autocomplete.json` - `number-field` (Number Field) — Numeric input with stepper buttons and optional drag-to-change behavior. — `npx shadcn@latest add https://control-ui.dev/r/number-field.json` - `color-picker` (Color Picker) **[beta]** — Color input with picker UI, formats, presets, contrast helpers, and a gradient editor with draggable stops. — `npx shadcn@latest add https://control-ui.dev/r/color-picker.json` - `emoji-picker` (Emoji Picker) **[beta]** — Searchable, virtualized emoji grid with skin-aware cells, an active emoji footer, and an emoji-or-icon tabbed variant. — `npx shadcn@latest add https://control-ui.dev/r/emoji-picker.json` - `icon-picker` (Icon Picker) **[beta]** — Searchable icon grid and color swatches using an app-supplied icon catalog. — `npx shadcn@latest add https://control-ui.dev/r/icon-picker.json` - `calendar` (Calendar) — Date selection grid built on react-day-picker and themed through tokens. — `npx shadcn@latest add https://control-ui.dev/r/calendar.json` **Overlays** — A container whose only job is to float above the page. Anything floating that runs a command or captures a value is filed by that job instead. - `dialog` (Dialog) — Modal dialog for focused tasks, confirmations, and custom panels. — `npx shadcn@latest add https://control-ui.dev/r/dialog.json` - `popover` (Popover) — Anchored floating panel for inline settings and contextual content. — `npx shadcn@latest add https://control-ui.dev/r/popover.json` - `tooltip` (Tooltip) — Hover or focus hint popup with Base UI positioning and Control UI tokens. — `npx shadcn@latest add https://control-ui.dev/r/tooltip.json` - `rich-tooltip` (Rich tooltip) — Persistent anchored tip for onboarding tours, walkthroughs, and new-feature announcements — dismissible, with optional media and step counter. — `npx shadcn@latest add https://control-ui.dev/r/rich-tooltip.json` - `drawer` (Drawer) — Swipeable edge panel for mobile sheets and off-canvas surfaces. — `npx shadcn@latest add https://control-ui.dev/r/drawer.json` - `responsive-dialog` (Responsive dialog) — Modal dialog on desktop that becomes a swipeable bottom drawer on mobile. — `npx shadcn@latest add https://control-ui.dev/r/responsive-dialog.json` - `hover-card` (Hover card) — Hover or focus preview panel for profiles, links, and contextual details. — `npx shadcn@latest add https://control-ui.dev/r/hover-card.json` - `alert-dialog` (Alert dialog) — Modal confirmation dialog for destructive or blocking decisions. — `npx shadcn@latest add https://control-ui.dev/r/alert-dialog.json` **Navigation** — Move the user between views, steps, or pages. - `tabs` (Tabs) — Segmented and browser-style navigation with a stable active indicator. — `npx shadcn@latest add https://control-ui.dev/r/tabs.json` - `table-of-contents` (Table of contents) — Sticky in-page navigation with scroll-spy range highlighting. — `npx shadcn@latest add https://control-ui.dev/r/table-of-contents.json` - `stepper` (Stepper) — Static and interactive workflow steps with horizontal and vertical layouts. — `npx shadcn@latest add https://control-ui.dev/r/stepper.json` - `navigation-menu` (Navigation menu) — Site navigation menu with a shared animated viewport. — `npx shadcn@latest add https://control-ui.dev/r/navigation-menu.json` - `pagination` (Pagination) — Page navigation for long lists and result sets. — `npx shadcn@latest add https://control-ui.dev/r/pagination.json` - `breadcrumb` (Breadcrumb) — Location trail with links, the current page, and collapsed levels. — `npx shadcn@latest add https://control-ui.dev/r/breadcrumb.json` **Feedback** — Tell the user what just happened, what is loading, or what is missing. - `skeleton` (Skeleton) **[beta]** — Loading placeholders with seamless shimmer, pulse, and static variants. Match their dimensions to the incoming content. — `npx shadcn@latest add https://control-ui.dev/r/skeleton.json` - `toast` (Toast) — Transient notifications with a callable toast API and single Toaster mount. — `npx shadcn@latest add https://control-ui.dev/r/toast.json` - `progress` (Progress) — Determinate task progress with optional label and value rows. — `npx shadcn@latest add https://control-ui.dev/r/progress.json` - `alert` (Alert) **[beta]** — Inline status panel for callouts, errors, and notices. — `npx shadcn@latest add https://control-ui.dev/r/alert.json` - `empty` (Empty) **[beta]** — Empty-state layout for blank lists, zero results, and new workspaces. — `npx shadcn@latest add https://control-ui.dev/r/empty.json` - `live-status` (Live status) — Always-mounted polite status region that announces changes to assistive technology. — `npx shadcn@latest add https://control-ui.dev/r/live-status.json` - `spinner` (Spinner) — Accessible loading indicator for pending buttons, panels, and inline waits. — `npx shadcn@latest add https://control-ui.dev/r/spinner.json` - `swap` (Swap) — Animated hand-off between the states of a button or label: copied, pending, done. — `npx shadcn@latest add https://control-ui.dev/r/swap.json` **Layout** — Content-agnostic structure: regions, panels, wrappers, scrolling, resizing, and disclosure. It never knows what is inside. - `collapsible` (Collapsible) — Accessible disclosure primitive with measured open and close motion. — `npx shadcn@latest add https://control-ui.dev/r/collapsible.json` - `sidebar` (Sidebar) **[beta]** — Responsive app sidebar with resizing, offcanvas and icon collapse, mobile navigation, and nested menus. — `npx shadcn@latest add https://control-ui.dev/r/sidebar.json` - `app-shell` (App shell) **[beta]** — The application container: persistent navigation and header around changing page content, with one shared scroll policy. — `npx shadcn@latest add https://control-ui.dev/r/app-shell.json` - `page-layout` (Page layout) **[beta]** — Page shell that owns the scroll container, the shared measure, and the title and actions row. — `npx shadcn@latest add https://control-ui.dev/r/page-layout.json` - `scroll-area` (Scroll area) — Scroll container with overlay or space-reserving scrollbars, edge fades, and optional progressive blur. — `npx shadcn@latest add https://control-ui.dev/r/scroll-area.json` - `accordion` (Accordion) — Stacked disclosure rows with measured panel animation. — `npx shadcn@latest add https://control-ui.dev/r/accordion.json` - `card` (Card) — Content surface for panels, tiles, and settings groups. — `npx shadcn@latest add https://control-ui.dev/r/card.json` - `aspect-ratio` (Aspect ratio) — Holds a ratio computed at runtime. Ratios known at build time need no component: aspect-video, aspect-square, aspect-[4/3]. — `npx shadcn@latest add https://control-ui.dev/r/aspect-ratio.json` - `dockable-panel` (Dockable Panel) **[experimental]** — Non-modal workspace panel that moves between two explicit edge slots with a mobile Drawer fallback. — `npx shadcn@latest add https://control-ui.dev/r/dockable-panel.json` - `infinite-canvas` (Infinite Canvas) **[experimental]** — Pan-and-zoom spatial workspace for arranging content without fixed bounds. — `npx shadcn@latest add https://control-ui.dev/r/infinite-canvas.json` - `flow` (Flow) **[experimental]** — Node graph editor parts that skin React Flow with Control UI tokens. — `npx shadcn@latest add https://control-ui.dev/r/flow.json` - `resize-handle` (Resize Handle) **[experimental]** — Edge and corner grip for resizing a positioned box; the host owns the drag math. — `npx shadcn@latest add https://control-ui.dev/r/resize-handle.json` - `morphing-panel` (Morphing Panel) **[experimental]** — A compact control that expands into settings, workspace navigation, or an inline confirmation, with stable alignment and keyboard dismissal. — `npx shadcn@latest add https://control-ui.dev/r/morphing-panel.json` - `resizable` (Resizable) **[beta]** — Accessible resizable panel groups and split layouts with keyboard support. — `npx shadcn@latest add https://control-ui.dev/r/resizable.json` **Display** — Render a data model you hand it: records, hierarchies, sequences, and the chips that label them. - `timeline` (Timeline) — Static chronological events with independent status, connectors, descriptions, and metadata. — `npx shadcn@latest add https://control-ui.dev/r/timeline.json` - `kbd` (Kbd) — Keyboard shortcut chip and chord group. — `npx shadcn@latest add https://control-ui.dev/r/kbd.json` - `avatar` (Avatar) — Profile image with initials fallback and composable overlapping groups. — `npx shadcn@latest add https://control-ui.dev/r/avatar.json` - `badge` (Badge) — Compact status, label, or count chip. — `npx shadcn@latest add https://control-ui.dev/r/badge.json` - `table` (Table) — Responsive data table for lists, comparisons, and structured records. — `npx shadcn@latest add https://control-ui.dev/r/table.json` - `item` (Item) **[beta]** — List row with media, content, and trailing actions. — `npx shadcn@latest add https://control-ui.dev/r/item.json` - `separator` (Separator) — Horizontal or vertical hairline divider. — `npx shadcn@latest add https://control-ui.dev/r/separator.json` - `meter` (Meter) — Static range meter for quota, storage, score, or usage values. — `npx shadcn@latest add https://control-ui.dev/r/meter.json` - `level-meter` (LevelMeter) — Live audio level in dBFS with warning and clip zones and a peak hold, painted without React renders. — `npx shadcn@latest add https://control-ui.dev/r/level-meter.json` - `chart` (Chart) — Gradient-first charts on TanStack Charts: themed host, presets that return real definitions, and the full grammar. — `npx shadcn@latest add https://control-ui.dev/r/chart.json` - `tree` (Tree) — Accessible tree view with roving keyboard navigation, single/multi selection, and animated disclosure. — `npx shadcn@latest add https://control-ui.dev/r/tree.json` **Content** — Render a document you authored or generated, from prose to code to email. - `typography` (Typography) — Heading and Text over the token-driven type scale: level is the document outline, size is the rung, tone is the color role, className stays layout-only. A bare text-* rung is for HTML React doesn't render as components, such as email HTML; MDX maps its tags to Heading and Text. — `npx shadcn@latest add https://control-ui.dev/r/typography.json` - `code` (Code) — Shared code surface: Shiki-highlighted lines, gutter, clean copy, editable content, and virtualization for large files. — `npx shadcn@latest add https://control-ui.dev/r/code.json` - `code-diff` (Code Diff) — Unified or split diff from a git patch or a before/after pair, with word-level intra-line highlighting. — `npx shadcn@latest add https://control-ui.dev/r/code-diff.json` - `markdown` (Markdown) — Rendered agent markdown (GFM) whose code fences compose Code, and diff fences compose CodeDiff. — `npx shadcn@latest add https://control-ui.dev/r/markdown.json` **Effects** — Shared visual behavior other components already use internally, published so you can apply it to your own markup. - `track-highlight` (Track highlight) — Shared fluid hover, keyboard-focus, and selection highlight for custom lists and groups, with CSS anchors and a measured fallback. — `npx shadcn@latest add https://control-ui.dev/r/track-highlight.json` - `progressive-blur` (Progressive blur) — CSS-driven backdrop blur that grows toward an edge, with masked layers and interruptible staggered transitions. — `npx shadcn@latest add https://control-ui.dev/r/progressive-blur.json` ### Kits (pre-assembled sets of components) **Forms** — Capture a value from the user — typed, picked, toggled, or uploaded — plus the scaffolding that labels and validates it. - `filter-bar` (FilterBar) — Build editable field, operator, and value filters with a button or inline search. — `npx shadcn@latest add https://control-ui.dev/r/filter-bar.json` **Feedback** — Tell the user what just happened, what is loading, or what is missing. - `dynamic-notification` (DynamicNotification) **[experimental]** — Dynamic Island-style AI notification pill with a thinking state that morphs into a reply bubble — token-driven surface, WebGL-enhanced backdrop blur, or real refractive liquid glass. — `npx shadcn@latest add https://control-ui.dev/r/dynamic-notification.json` **Content** — Render a document you authored or generated, from prose to code to email. - `email` (Email) **[beta]** — React Email compositions with Control UI colors and typography: contained or plain surfaces, column layouts, themed markdown and code, and a footer with social icons, address, and unsubscribe parts. — `npx shadcn@latest add https://control-ui.dev/r/email.json` **Chat & agents** — Everything that composes an AI conversation: the transcript, the composer and the surfaces that replace it, and what the agent is doing while you wait. - `chat-message` (ChatMessage) — Composable chat message with stable inline editing, bubble or flat layout, grouped bursts, reactions, and replies. — `npx shadcn@latest add https://control-ui.dev/r/chat-message.json` - `chat-composer` (ChatComposer) — Message composer with controlled text, Enter or ⌘ Enter submit, caret text insertion, and trigger-menu support. — `npx shadcn@latest add https://control-ui.dev/r/chat-composer.json` - `chat-composer-attachment` (ChatComposerAttachment) — Composer attachment rail with file previews, upload progress, and removal. — `npx shadcn@latest add https://control-ui.dev/r/chat-composer-attachment.json` - `user-ask` (UserAsk) — Keyboard-first agent question panel that temporarily replaces the chat composer inside its container. — `npx shadcn@latest add https://control-ui.dev/r/user-ask.json` - `tool-approval` (ToolApproval) — Inline approve or deny card for an agent action, with risk, a preview of what will run, and keyboard shortcuts. — `npx shadcn@latest add https://control-ui.dev/r/tool-approval.json` - `task-list` (TaskList) — Floating agent task progress pill above the composer that expands into the full task list. — `npx shadcn@latest add https://control-ui.dev/r/task-list.json` - `audio-device-select` (AudioDeviceSelect) — Composable audio input selector with permission, loading, default-device, and disconnected states. — `npx shadcn@latest add https://control-ui.dev/r/audio-device-select.json` - `audio-recorder` (AudioRecorder) — Voice recorder with realtime waveform, duration, cancel, and submit controls. — `npx shadcn@latest add https://control-ui.dev/r/audio-recorder.json` - `audio-visualizer` (AudioVisualizer) **[beta]** — Audio visualization with waveform, line envelope, and frequency bar alternatives. Choose the implementation that fits your audio data. — `npx shadcn@latest add https://control-ui.dev/r/audio-visualizer.json` - `activity` (Activity) — Shared static and collapsible activity anatomy with bounded, scrollable detail content. — `npx shadcn@latest add https://control-ui.dev/r/activity.json` - `transcript-divider` (TranscriptDivider) — Toned run-boundary separator for transcripts: steering, interruptions, and condensed context. — `npx shadcn@latest add https://control-ui.dev/r/transcript-divider.json` - `context` (Context) **[beta]** — Compact context-window usage with an automatically derived token graph and anchored detail inspector. — `npx shadcn@latest add https://control-ui.dev/r/context.json` - `inline-citation` (InlineCitation) — Inline multi-source citation with a keyboard-accessible preview and source navigation. — `npx shadcn@latest add https://control-ui.dev/r/inline-citation.json` - `source-badge` (SourceBadge) — Linked source badge with an automatic same-origin favicon, derived hostname, and resilient fallback. — `npx shadcn@latest add https://control-ui.dev/r/source-badge.json` - `action-bar` (ActionBar) — Reusable hover actions for message and response controls. — `npx shadcn@latest add https://control-ui.dev/r/action-bar.json` - `inline-attachment` (InlineAttachment) **[beta]** — Inline file and media previews for chat turns. — `npx shadcn@latest add https://control-ui.dev/r/inline-attachment.json` - `markdown-editor` (MarkdownEditor) **[beta]** — Compose Markdown with draggable blocks, slash commands, people mentions, formatting, and application-owned image uploads. — `npx shadcn@latest add https://control-ui.dev/r/markdown-editor.json` - `markdown-block` (MarkdownBlock) — Assistant markdown output rendered to prose, with a header and copy-source action. — `npx shadcn@latest add https://control-ui.dev/r/markdown-block.json` - `chat-layout` (ChatLayout) — A scrolling conversation with a pinned header, message turns, and a floating composer dock. — `npx shadcn@latest add https://control-ui.dev/r/chat-layout.json` - `thread-rail` (ThreadRail) — Conversation minimap for scanning and jumping between chat turns. — `npx shadcn@latest add https://control-ui.dev/r/thread-rail.json` ### Templates (pre-composed recipes) - `templates` (Templates) — Start from complete workspace templates or focused interaction patterns, then own and adapt the installed source. - `chat` (Chat) — Controlled chat shell that composes rendered turns and a provider-owned composer. — `npx shadcn@latest add https://control-ui.dev/r/chat-block.json` - `theme-toggle` (Theme toggle) — Controlled theme controls with a three-value switch, binary switch, cycle button, and dropdown. — `npx shadcn@latest add https://control-ui.dev/r/theme-toggle-block.json` - `discussion` (Discussion) **[beta]** — Comment thread with ⌘ Enter posting, nested replies, inline editing, and hover actions. — `npx shadcn@latest add https://control-ui.dev/r/discussion-block.json` - `coding-agent` (Coding agent) **[beta]** — Desktop coding workspace with project tasks, a focused conversation, and a persistent controlled composer. — `npx shadcn@latest add https://control-ui.dev/r/coding-agent-block.json` - `agent-run` (Agent run) **[beta]** — One autonomous agent run: its plan step by step, the tool calls behind each step, approvals it waits on, and what it produced. — `npx shadcn@latest add https://control-ui.dev/r/agent-run-block.json` - `settings` (Settings) — Searchable multi-page settings shell with responsive navigation and accessible control groups. — `npx shadcn@latest add https://control-ui.dev/r/settings-block.json` - `team-chat` (Team chat) **[beta]** — Slack-style workspace with channels, unread badges, presence, reactions, and a side thread for replies. — `npx shadcn@latest add https://control-ui.dev/r/team-chat-block.json` - `file-explorer` (File explorer) **[beta]** — Finder-inspired file browser with locations, search, resizable columns, breadcrumbs, and an item preview. — `npx shadcn@latest add https://control-ui.dev/r/file-explorer-block.json` - `design-canvas` (Design canvas) **[beta]** — Figma-style editor with layers, an infinite canvas, a floating tool bar, and scrubbable property inputs. — `npx shadcn@latest add https://control-ui.dev/r/design-canvas-block.json` ### Extensions - `control-effects` (ControlEffects) — CSS-driven control effects (top-shine, ripple, hover-circle) that follow every control app-wide through the emitted anatomy — portalled surfaces included. — `npx shadcn@latest add https://control-ui.dev/r/control-effects.json` - `view-transition` (viewTransition) — Interrupt-safe driver for the browser View Transitions API: page transitions, shared-element morphs, and a reduced-motion fallback for both. — `npx shadcn@latest add https://control-ui.dev/r/view-transition.json` - `send-aurora` (SendAurora) — Anchored ChatComposer extension: a blurred aurora backdrop that sweeps up once per sent message — activated from skin.config via the chat-composer:send-layer anchor. — `npx shadcn@latest add https://control-ui.dev/r/send-aurora.json` ### Skins - `skins` (Skinning Control UI) — Author complete token-driven Control UI skins with slots, adornments, motion controls, and one shared component source. - `mastra` (Mastra) — Mastra Playground UI: Mona Sans and Commit Mono, compact pill controls, neutral inverse actions, quiet navigation, inset badges, and layered surfaces. — `npx shadcn@latest add https://control-ui.dev/r/skin-mastra.json --overwrite` - `refined` (Refined) — Craft-inspired neutral surfaces, Inter typography, pill controls, and quiet layered shadows. — `npx shadcn@latest add https://control-ui.dev/r/skin-refined.json --overwrite` - `xp` (Windows XP) — Windows XP Luna with compact system typography, glossy controls, blue window frames, and Explorer task panes. — `npx shadcn@latest add https://control-ui.dev/r/skin-xp.json --overwrite` - `windows-98` (Windows 98) — Classic Windows 98 with bitmap system typography, raised gray controls, recessed fields, navy title bars, and Explorer tree navigation. Fixed to its original light palette. — `npx shadcn@latest add https://control-ui.dev/r/skin-windows-98.json --overwrite` - `liquid-metal` (Liquid metal) — Polished metal skin with a WebGL shader control surface. — `npx shadcn@latest add https://control-ui.dev/r/skin-liquid-metal.json --overwrite` - `rig` (Rig) — Brutalist skin with coral accents, squared corners, and dense typography. — `npx shadcn@latest add https://control-ui.dev/r/skin-rig.json --overwrite` - `sketch` (Sketch) — Hand-drawn pencil outlines, Excalifont handwriting, and neutral paper surfaces. A clean sketchbook for prototypes and everyday interfaces. — `npx shadcn@latest add https://control-ui.dev/r/skin-sketch.json --overwrite` - `none` (No skin) — Control UI defaults: neutral colors, square corners, and no shadows. No skin required. - `modern-apple` (macOS) — macOS 27 Liquid Glass: SF Pro at 13px, 24px regular controls, accent-filled selection, frosted glass menus and popovers, and an edge-attached sidebar. — `npx shadcn@latest add https://control-ui.dev/r/skin-modern-apple.json --overwrite` - `cuicui` (Cuicui) — Cuicui-inspired shell skin with fixed grain, a docked w-80 sidebar, a neutral main container, and the send-aurora anchored extension on ChatComposer (skin.config fills the chat-composer:send-layer anchor). — `npx shadcn@latest add https://control-ui.dev/r/skin-cuicui.json --overwrite` - `linear` (Linear) — Linear's measured design language: indigo on a cool neutral ramp, a 12/13px chrome band at weight 450/500, pill buttons with half-pixel rims, 8px fields on 12px panels, and 32px menu rows inset in a 6px gutter. — `npx shadcn@latest add https://control-ui.dev/r/skin-linear.json --overwrite` ### Hooks - `use-chat-message` (useChatMessage) — Typed role, density, and lifecycle state for ChatMessage. - `use-chat-composer` (useChatComposer) — Controlled composer text and submit state. - `use-user-ask` (useUserAsk) — Question/option registration, selection, freeform text, and keyboard flow for UserAsk. - `use-audio-recorder` (useAudioRecorder) — Browser microphone recording state, waveform levels, and Blob completion. - `use-copy-to-clipboard` (useCopyToClipboard) — Copy-to-clipboard behavior with success state and fallback support. ### Utils - `cn` (cn) — Tailwind class-name merge helper. - `skin` (skin) — Skin slot and adornment resolvers for the Control UI library. - `serialize` (serialize) — Bridge a rich editor doc to plain text (and structured mentions) and back — independent of any one component. ### Guides - `create` (Create app) — Scaffold a Next.js app with every Control UI component installed as source you own, or hand the whole install to the coding agent already open in your project. - `get-started` (Install) — One command installs every component, kit, and template as source you own, with neutral light and dark defaults. The CSS entry is the only wiring you write. - `theming` (Theme it) — Pick the pack, re-value the token contract in one artifact, and check the contrast the components actually paint. - `build-a-screen` (Build a screen) — Render provider-owned messages with plain props and children. No intermediary message schema, no runtime coupling. - `create-a-skin` (Create a skin) — Re-value the token contract over an installed pack, or own a full pack of three files, then reach the component knobs beneath. - `foundations` (Foundations) — Every foundation of the design system, rendered live in the active skin: color, type, surfaces, elevation, radius, sizing, motion, and focus. - `theme-editor` (Theme editor) — Edit theme tokens category by category and watch real components repaint as you go. - `theme-accessibility` (Theme accessibility) — Audit canonical theme colors plus rendered popup, badge, and active-tab states, then run the same checks from the CLI. - `update` (Update & diff) — Updating is a reinstall: the update manifest refreshes every installed source and never touches the three skin-owned files. - `setup-prompt` (Setup prompt) — One prompt that makes the agent already open in your project read the repository, install from the registry, wire the CSS, run the doctor, and design the theme with you. - `agent-skill` (Agent skill) — The skill installed with every pack: the token contract and the working rules, refreshed by every update, so later sessions build without the prompt. - `agent-surface` (Machine docs) — Inspect and install registry items through HTTP, MCP, shadcn manifests, markdown negotiation, and machine-readable docs. - `overview` (Why Control UI) — An owned-source registry of components, kits, complete templates, and swappable skins. - `architecture` (Architecture) — Runtime ownership, skin layering, customization paths, and registry derivation. - `lock-in` (Lock-in) — What you own at each layer, what stays proprietary, and what leaving costs — measured, not promised. - `shadcn-compatibility` (shadcn compatibility) — shadcn registry, token, and ownership conventions without writing to components/ui. - `control-ui-vs-shadcn-ui` (Control UI vs shadcn/ui) — Both ship open-source React source through the shadcn CLI. The difference starts after install: a typed knob contract, skins that re-value it wholesale, and every supported skin mode audited against WCAG AA on every commit. - `best-react-component-libraries-for-ai-interfaces` (Best React component libraries for AI interfaces) — Eight production options compared by ownership model, theming system, and agent-specific surfaces — from shadcn/ui and AI Elements to MUI to Control UI. - `components-missing-from-shadcn-ui` (Components missing from shadcn/ui) — Color picker, tree view, file explorer, phone input, code diff: React components shadcn/ui does not ship, installed with the shadcn CLI. - `reference` (Reference) — Theme guides, the maintenance workflow, and the reasoning behind Control UI — everything beyond the install path. ## Practice rules (how to build well) ### CSS-first - **CSS-first interactivity** — Keep interaction declarative, server-renderable, and JavaScript-free by expressing it in CSS the browser already resolves — reserve React state for logic that is genuinely stateful or asynchronous. - do: Reveal, size, or restyle from a sibling/parent/child's state with `:has()`, `:focus-within`, and sibling combinators (Tailwind `has-*`, `group-has-*`, `peer-has-*`, `not-*`) instead of tracking hover, open, or selection in `useState` + `onMouseEnter`/`onFocus` handlers. Adapt layout to available width with container queries (`@container` + `@md:`/`@max-md:`) rather than a `ResizeObserver` measuring the element in JS; use `@container style(--x: y)` or a `data-*` attribute variant to branch on a token instead of reading a CSS variable in JS. Let native elements own open state, dismissal, and the top layer: the `popover` attribute + Popover API (Baseline widely available) and `` replace hand-written outside-click, Escape, focus-trap, and z-index code; `
`/`::details-content` gives a semantic, JS-free disclosure for tool activity and reasoning surfaces. Style form and choice state natively: `:user-valid`/`:user-invalid` and `:in-range`/`:out-of-range` (Tailwind `user-invalid:`, `in-range:`) drive validation UI, and `peer-checked:`/`has-checked:` + `accent-color` drive custom switches and checkboxes without mirroring the value into React. - avoid: `useState` + `onMouseEnter`/`onMouseLeave`/`onFocus`/`onBlur` (or `useEffect` listeners) whose only job is toggling a class a selector could match. `ResizeObserver`/`getBoundingClientRect` used only to pick a responsive layout a container query already expresses, or `getComputedStyle` read in JS to decide a className. Reimplementing dropdowns, tooltips, menus, and modals from raw divs when the native popover/dialog primitives already own open state, light-dismiss, and focus. - **CSS-first motion & sizing** — Move sizing and motion off the main thread and out of effects, keeping every duration on the theme's motion tokens and every cutting-edge property behind a working fallback. - do: Auto-grow inputs with `field-sizing: content` (Tailwind `field-sizing-content`, bounded by `min-h`/`max-h`) instead of a ref copying `scrollHeight` back into a height; fade scroll edges with a `mask-image` gradient driven by overflow attributes, not a scroll listener. Animate enter/exit with `@starting-style` + `transition-behavior: allow-discrete` (Tailwind `starting:` + `transition-discrete`) rather than mount/unmount timers or an animation library, and keep every duration/easing on the theme's `--duration-*`/`--ease-*` tokens so the reduce-motion kill-switch flattens them. Animate to intrinsic size with the `grid-template-rows: 0fr → 1fr` collapse (widely supported) as the base, and progressively enhance to `interpolate-size: allow-keywords`/`calc-size(auto)` behind `@supports`, instead of measuring the panel and animating a pixel height. Guard Limited features — anchor positioning (`anchor()`/`position-try`), scroll-driven animations (`animation-timeline: scroll()`/`view()`), style queries, and `if()` — behind `@supports`, keeping the JS path (Floating UI, `IntersectionObserver`) as the fallback so an unsupported browser still gets a correct, static result. - avoid: Measuring `scrollHeight`/`getBoundingClientRect` on every keystroke, resize, or scroll to set a style a container query, `field-sizing`, or scroll-driven animation already reaches. JS enter/exit libraries for a fade or slide that `@starting-style` plus a token transition expresses in a few lines. Shipping an experimental CSS feature (anchor positioning, `if()`, `interpolate-size`, scroll-driven timelines) as a hard dependency with no `@supports` fallback, or hardcoding millisecond durations that bypass the motion tokens and the reduce-motion kill-switch. ### React code quality - **Derive, do not duplicate** — Compute values from the source already in scope instead of asking callers to pass both source and derivative. - do: Derive itemCount, isEmpty, selected item, filtered count, and booleans from the data already passed in. Keep a single owner for each value; callers should not synchronize pairs such as items plus itemCount. When state mirrors props, check whether the value should be derived during render instead. - avoid: Props such as itemCount next to items, isEmpty next to a list, or selectedName next to selectedId and entities. Effects whose only job is to keep duplicated state synchronized. - **Remount state boundaries** — When an upstream identity changes, remount the stateful branch so initializers naturally receive fresh data. - do: Lift the discriminant, such as selected id or route param, above the stateful form or editor. Render a skeleton or keyed branch while new data loads so stale state cannot flash. Use a key as the narrow fallback when there is no async boundary to restructure around. - avoid: useEffect blocks that call several setters to reset a form after props change. Rendering an editor once with the previous entity and correcting it after an effect runs. - **Explicit names instead of comments** — When code needs explanation, rename or extract until the names carry it; reserve comments for constraints the code cannot express, written as short as they can be and still land. - do: Rename variables, functions, and components until the call site reads as the sentence the comment would have said. Extract a named function, variable, or component when a block needs a heading comment; the name replaces the heading. Name booleans and predicates for what they assert, such as isEmpty or hasSkinAdornment, not for how they compute. Keep the rare justified comment to one line stating a why the code cannot show: a constraint, a workaround, or an external contract. Write that line telegraphically — a fragment naming the constraint beats a grammatical sentence explaining it; drop articles, narration, and hedging before dropping the fact. Treat a comment that wants a paragraph as a missing name, a missing extraction, or a docs page; move the rationale to the docs surface that already documents the item, and leave the code line self-explaining. Prefer a `@link`-style pointer to the canonical file or contract over restating that file's reasoning at every call site. - avoid: Comments that restate the code, narrate the next line, or describe what a well-named extraction would say. Multi-line prose blocks — JSX `{/* … */}` essays especially — arguing a class list, layout choice, or design intent the markup already states. Generic or abbreviated names such as data, tmp, or handleThing patched over with an explanatory comment. Section-divider comments inside a function instead of extracting named units, and decorative banner rules such as box-drawing separators. Keeping a stale comment in sync with code instead of deleting it once the name carries the intent. Repeating the same rationale in several files instead of stating it once where the fact is owned. - **One responsibility per file** — Keep each domain component or hook responsible for one behavior so review, testing, and reuse stay local. - do: Extract a hook when data fetching and filtering can be named independently. Extract child components for list rendering, detail panels, and edit forms. Let the page/container compose hooks and children; composition is a valid single responsibility. - avoid: Files where comment headers divide unrelated responsibilities inside one component. Components with unrelated useState or useQuery clusters that always re-render together. - **Keep input and output APIs narrow** — Keep each component, hook, function, or utility API focused on one cohesive responsibility. - do: Treat unrelated clusters of props, arguments, return values, and handlers as evidence that the unit needs to split. Compose focused hooks and components at a page or container instead of hiding their composition in a mega-hook. Return only values owned by the responsibility; group values only when they form one meaningful concept. Judge cohesion from caller usage and reasons to change, not from an arbitrary parameter limit. Keep semantic and render inputs explicit: use named props, or narrow the children prop when its content type is the component contract. - avoid: APIs that span unrelated concerns such as querying, pagination, selection, permissions, and editing. Wrapping the same unrelated values in one options object and calling the API narrow. Pass-through mega-hooks that expose one large bag of internal state and handlers. Runtime type checks on ReactNode props such as children or label that recover semantic data or silently switch behavior; keep typeof guards at unknown and external boundaries. - **Keep context necessary, narrow, and stable** — Keep context ownership explicit and prevent broad or unstable provider values from invalidating unrelated consumers. - do: Use context when a root owns state or a semantic mode needed by user-composed descendants that props cannot directly reach. Keep each context value cohesive; prefer a scalar for one fact, and consider splitting when disjoint consumers need values that change at materially different rates. Stabilize provider object or function identities when unrelated renders would recreate a semantically unchanged value; treat memoization as an optional optimization, not a blanket rule. Destructure the fields used at the narrowest consumer so its dependencies are visible; keep the whole value for nullable-context validation, cohesive object passing, or when most fields are used. - avoid: Context for values needed only by a directly controlled child, or context added only to avoid passing a small cohesive prop. Blanket useMemo or useCallback around every provider value, or splitting every field into a separate context when consumers need them together. Claims that destructuring a context value reduces re-renders; useContext subscribes to the complete provider value. Destructuring a nullable context before the custom hook verifies that its provider exists. - **Do not query the DOM in React** — Never use querySelector or querySelectorAll in React code; use refs, props, state, and context to preserve ownership. - do: Attach refs where elements are created and pass them through React-owned boundaries. Register dynamic element collections with callback refs and a ref-backed Map. Carry selection and identity through props, state, or context instead of rediscovering them from rendered attributes. Refactor existing DOM searches instead of suppressing or wrapping them. - avoid: querySelector and querySelectorAll in React source. Class-name or data-attribute selectors used to recover React-owned elements or state. Effects that scan rendered descendants instead of maintaining refs as elements mount and unmount. Lint exceptions, wrapper helpers, or selector utilities that hide DOM queries. - **Real stack tests** — Make a green test prove the real UI path, not a mocked copy of the implementation. - do: Render through the real provider stack when the component depends on cache, router, or client behavior. Mock network responses at the boundary and use production response types where available. Assert behavior visible to the user rather than duplicating implementation class strings or hook internals. - avoid: vi.mock for local hooks, services, auth gates, or SDK calls that the component is meant to exercise. Bespoke test-only data shapes that drift from production response types. - **Test rendered output, not class names** — Make style tests fail on a user-visible regression, not on an implementation-detail refactor. - do: Assert computed styles when CSS behavior is available in the DOM test runtime. Assert real geometry for layout and overflow, including dimensions, scrollWidth, and clientWidth. Use browser validation for pseudo-elements, media and container queries, hover or focus rendering, and layout-engine behavior. Assert class strings only when a class builder, class forwarding, or a public slot/classNames API is the contract under test. - avoid: Assertions that grep rendered HTML, source files, snapshots, className, or classList for Tailwind utilities to prove appearance. Calling a value computed when the test merely maps a class token to an expected CSS value. Keeping a class-name-only test when the current test runtime cannot observe the claimed behavior. ### Architecture - **Cohesive folders** — Make each folder express one nameable intent so a reader can predict its contents without opening files, and promote the hidden grouping into a folder when loose files accumulate. - do: Treat roughly seven loose files in one folder as the review threshold: beyond it, find the implicit groups — shared responsibility, shared consumer, or shared lifecycle — and promote each into its own folder. Read a shared filename prefix across several files as a folder that was never created; promote the prefix into a directory and drop it from the filenames. Name every folder after its single responsibility; a file you cannot place without hesitation reveals a grouping that does not exist yet. Keep only framework-reserved files at conventional roots, such as route files, entrypoints, and global config; everything else lives in a named group. Colocate what changes together: a unit's component, styles, hooks, tests, and helpers sit in that unit's folder rather than sorted by file kind at the top level. - avoid: Junk-drawer folders such as utils, helpers, misc, common, or shared that accept anything and grow without bound. Generic catch-all filenames such as utils.ts, helpers.ts, types.ts, or constants.ts at broad scope; scope them to the unit they serve or name the actual content. Disambiguating with filename prefixes instead of directories once several files share the same prefix. Overcorrecting into single-file folders or deep nesting; a folder earns its existence by grouping several related files. - **Single source of truth** — Give every fact one authoritative definition and derive, generate, or validate every other representation from it. - do: Name the canonical owner for each fact, such as a schema, manifest, config definition, or state owner, and keep dependency direction downstream from it. Derive types, ids, registries, routes, navigation, docs metadata, defaults, view values, and structural test fixtures instead of restating the same fields or allowed values. Treat generated manifests, mirrored fixtures, public mirrors, and serialized projections as read-only outputs; regenerate them reproducibly and fail validation when they drift. When direct derivation is impossible, isolate one adapter or synchronization boundary with an explicit owner, lifecycle, and invalidation rule. - avoid: Parallel hand-maintained enums, lists, defaults, route maps, docs tables, config values, fixtures, or UI state for the same fact. Two writable peers kept aligned by effects, listeners, copy steps, or bidirectional synchronization without an authoritative owner. Editing generated output or copied fixtures directly instead of changing the canonical source and regenerating. A god manifest that owns unrelated concepts; single source of truth means one owner per fact, not one file for the whole system. ### UI / Tailwind - **Use existing components first** — Preserve visual consistency and accessibility by reusing the closest existing component contract. - do: Search existing Control UI components, kits, hooks, and examples before adding a new surface. Use variants or composition slots when the existing component already owns the behavior. Create a new component only when the required behavior or anatomy is genuinely missing. - avoid: Duplicating a button, menu, select, dialog, sidebar, or scroll behavior with raw divs. Adding a component to a shared design-system folder without an explicit shared ownership reason. - **Token discipline** — Keep the visual language themeable and predictable by styling through approved tokens. - do: Prefer existing color, spacing, radius, shadow, and typography tokens. Introduce local CSS variables only when a component needs a named internal scale. Keep arbitrary values exceptional and constrained to precise dimensions when no token fits. - avoid: Changing shared Tailwind tokens as part of a narrow component task. Sprinkling arbitrary color, shadow, spacing, or radius values across product code. - **ClassName boundaries** — Keep component contracts meaningful and avoid style drift from consumer overrides. - do: Compose className values with `cn()`, never template literals. On the docs website, keep shared-component className values to caller-owned layout and placement such as width constraints, flex or grid alignment, positioning, overflow, and responsive visibility. Choose props, variants, sizes, tones, colors, slots, and composition for component-owned presentation. If the required treatment is missing, add a meaningful component variant instead of styling one consumer into a new variant. Use Tailwind v4 trailing important syntax when needed: `fixed!`, `bottom-0!`, `translate-x-0!`. - avoid: Passing long className strings into shared components to bypass their variants. Overriding control height, padding, radius, color, border, background, typography, shadow, focus treatment, or motion from a docs-site consumer. Using older leading important utilities such as `!fixed` in Tailwind v4 code. - **Tailwind v4 CSS configuration** — Keep Tailwind configuration CSS-first so tokens, utilities, variants, sources, and `@apply` references stay in the stylesheet the build actually reads. - do: Check the pinned `tailwindcss` version before relying on a recent utility or directive; at the time this rule was added, the docs app resolved Tailwind 4.3.2. Use `@import "tailwindcss"`, `@theme`, `@utility`, `@custom-variant`, `@source`, `@variant`, and `@reference` for new configuration. Put utility-generating tokens in `@theme`; use plain CSS variables for runtime-only values that should not generate classes. Use `@theme inline` when a token value references another variable so generated utilities resolve at the declaration site. - avoid: `@tailwind base/components/utilities`, new `tailwind.config.ts` work, JS plugins, `addVariant`, content arrays, or safelist config for new v4 code. The v3 `theme()` function or `resolveConfig` for runtime styling decisions; use CSS variables, `--alpha(...)`, and `--spacing(...)`. - **Tailwind v4 migration syntax** — Avoid silent visual regressions from v3 names that still compile but mean something different in Tailwind v4. - do: Use v4 scale names such as `shadow-xs`, `drop-shadow-xs`, `blur-xs`, `rounded-xs`, `outline-hidden`, and explicit `ring-3` where the old default mattered. Use slash opacity and v4 gradient names: `bg-black/50`, `text-white/50`, and `bg-linear-to-r`. Use trailing important syntax such as `bg-red-500!`, plus `shrink-*` and `grow-*` instead of `flex-shrink-*` and `flex-grow-*`. Use v4 CSS-variable shorthand and type hints: `bg-(--row-bg)`, `text-(color:--fg)`, and `text-(length:--size)`. Use underscores inside arbitrary list values, such as `grid-cols-[max-content_auto]`. - avoid: Keeping old classes such as `bg-opacity-*`, `text-opacity-*`, `bg-gradient-to-*`, prefix important utilities, `bg-[--var]`, or comma-separated arbitrary grid values. Assuming a bare v3 size still has the same rendered weight, radius, shadow, blur, ring, or outline behavior in v4. - **First-class utilities before custom CSS** — Keep styling composable, token-aware, and isolated by letting Tailwind own utility state and generated CSS whenever it supports the effect. - do: Before writing an arbitrary property or custom CSS, check the pinned Tailwind v4 utility family and use it when it expresses the same behavior. Use generated spacing and sizing utilities such as `min-w-100`, `w-150`, `size-6`, `mt-17`, `grid-cols-15`, `h-dvh`, `w-dvw`, `h-lh`, and container max-width utilities when they fit. Use boolean data variants such as `data-current:opacity-100`; keep bracketed values for real values like `data-[state=open]:opacity-100`. Keep class strings complete and statically detectable by mapping props to full class names, for example `{ success: "bg-positive1" }[tone]`. Use a first-class utility with an arbitrary value when only the value is custom, such as `max-h-[calc(100dvh-3rem)]`, `bg-size-[18px_18px]`, or `transform-[translateX(var(--x))]`. For scroll-edge fades, compose overflow-state variants with `mask-l-from-*`, `mask-r-from-*`, `mask-t-from-*`, and `mask-b-from-*`; Tailwind supplies mask intersection and non-inheriting internal state. Reserve arbitrary properties for unsupported CSS such as `[writing-mode:vertical-rl]`; use `mask-type-luminance` instead of `[mask-type:luminance]`. - avoid: Reimplementing supported behavior with arbitrary properties or custom utility state; `[mask-image:linear-gradient(...)]` plus manual gradient stops and composite variables is the ScrollArea smell. Building Tailwind class fragments dynamically, such as concatenating `bg-`, a tone, and `-500`, because the compiler cannot reliably detect them. - **Variant-first styling** — Express styling reactions with variants the compiler understands instead of duplicating browser state in CSS or JavaScript. - do: Use pseudo, ARIA, and data variants such as `active:`, `focus-visible:`, `aria-expanded:`, `data-[state=open]:`, and boolean `data-current:`. Use relationship variants such as `group-*`, `peer-*`, `has-*`, `in-*`, `*:`, and `**:` before adding parent-state selectors or event handlers. Use capability and structural variants such as `pointer-coarse:`, `pointer-fine:`, `first:`, `last:`, `odd:`, `nth-*`, and `not-*`. Use built-in mask and container-query utilities such as `mask-b-from-80%`, `mask-t-from-*`, `@container`, `@sm:`, and `@max-md:`. - avoid: Handwritten pseudo-state CSS or `style` props when a Tailwind variant directly models the state. React hover, focus, or child-presence state whose only purpose is toggling a style a selector variant can express. - **Tailwind v4 capabilities** — Use Tailwind's native v4 coverage for sizing, wrapping, validation, enter motion, masks, shadows, color scheme, and 3D transforms before adding custom code. - do: Use `field-sizing-content` for auto-growing fields, bounded by `min-h-*` and `max-h-*`. Use `wrap-anywhere` or `wrap-break-word` for long content and `items-center-safe` or `justify-center-safe` when centered overflow should fall back safely. Use `user-valid:` and `user-invalid:` for validation states after user interaction. Use `starting:` with `transition-discrete` for enter transitions and popover/display transitions. Use `text-shadow-*`, `mask-t-from-*`, `mask-b-to-*`, `scheme-dark`, `rotate-x-*`, and `perspective-*` when those utilities fit the effect. - avoid: Measuring fields in JavaScript, adding `min-w-0`-style hacks before trying wrapping utilities, or sniffing user agents for coarse pointer behavior. Custom one-off CSS for effects that Tailwind v4 already exposes as token-aware utilities. - **Tailwind v4 behavior & motion** — Prevent subtle interaction and visual bugs that come from assuming Tailwind v3 runtime behavior still applies. - do: Transition individual transform properties with utilities such as `transition-[opacity,scale]`; reset transforms with `scale-none`, `rotate-none`, and `translate-none`. Treat `hover:` as hover-capable only and keep required functionality available on touch devices. Set explicit border, divide, ring, and outline colors when the color matters because v4 defaults these to `currentColor`. Stack variants left to right, for example `*:first:pt-0`, and prefer flex or grid `gap-*` over `space-x-*` or `space-y-*`. Gate decorative animation with `motion-safe:` and `motion-reduce:`, and put reusable animations in `--animate-*` tokens with matching `@keyframes`. - avoid: `transition-[opacity,transform]` when the animated property is now `scale`, `rotate`, or `translate`. Required actions that are only discoverable or usable through hover. Hardcoded local animation durations that bypass motion tokens and reduced-motion behavior. ### UX - **State continuity** — Make each state feel like the same workflow instead of a bolt-on fallback. - do: Name the user action available in each state, especially empty and error states. Keep layout dimensions stable between loading, loaded, and empty states. Use progressive disclosure for diagnostics so the primary next step stays clear. - avoid: Generic empty states that describe the app instead of moving the workflow forward. Spinners or skeletons that resize the page when real content arrives. - **Provenance without noise** — Make agent UI explain where output came from without turning every row into a diagnostics panel. - do: Use compact badges, muted labels, or hover details for source and runtime metadata. Put high-confidence primary content first; keep provenance adjacent but secondary. Escalate provenance visually only when it changes what the user should do next. - avoid: Large always-visible panels for metadata that is only occasionally useful. Hiding critical source or ownership details behind a generic info icon. - **Dense but scannable** — Fit operational agent workflows on screen while preserving scan paths, grouping, and clear affordances. - do: Use concise labels, small status chips, and predictable alignment for repeated rows. Reserve large type and decorative space for actual page-level orientation. Group controls by workflow stage instead of visual novelty. - avoid: Card-heavy layouts where every small piece of information becomes a separate panel. Oversized headings or explanatory text inside compact tools and sidebars. - **Say it once** — Make every visible sentence earn its space by adding a decision, constraint, consequence, or recovery path. - do: Trust clear labels, icons, toggles, and placement to explain standard interactions. Before adding helper text, name the new information it adds beyond the label and control. Reserve descriptions for unusual behavior, irreversible actions, hidden constraints, permissions, delays, or failure recovery. Use progressive disclosure for secondary context so frequent users do not scan past repeated explanations. - avoid: Label-description pairs that restate the same action, such as "Change theme" plus "This changes the theme." Always-visible paragraphs under every input, toggle, tab, or card because generated UI looks more complete with copy. Explaining standard control mechanics instead of naming the user outcome. Empty states that describe the product when they should give the next useful action or reason. ### Control UI registry - **Skin authoring** — Make every pack self-contained and composable with typed CSS knobs, complete theme tokens, scoped CSS, and no runtime visual class resolver. - do: Start every visual change at the lowest rung of the customization ladder (/architecture#customization-ladder): a contract token in theme.css first; then a DS-level `ControlUiSkin` field (`sidebarLayout`, `sidebarWidth`, `indicators`, `motion`, `colorScheme`, `effects`) when the whole app should flip at once; then a registered component knob re-valued in skin.css; then CSS-only rules or an extension. Recipe CSS owns every component visual default and paints only through registered `---*` custom properties. Variants, tones, states, and anatomy selectors re-value knobs instead of repeating paint declarations. Skin CSS re-values recipe knobs under literal `[data-skin=""] :where([data-control-family=""][data-slot=""])` selectors. Keep paint in the recipe so one override survives every component state. Every public knob appears once in the family contract array, has a typed `@property`, and receives a recipe default on the element that reads it. Derive the component's `KnobStyle` type from that array and expose it through `style?: CSSProperties & KnobStyle`. theme.css resolves every contract token in BOTH light and dark, either through a shared declaration or explicit mode declarations; skin-local formulas are allowed, but missing values are not. Packs never open their own `@theme` block — core owns bindings, neutral token defaults, and invariant mechanics. Scope every skin under `[data-skin=""]` and stamp exactly one valid id on the application root. The id is the design system's name, lets several packs compile in one build, and is re-asserted on portalled surfaces. Pass the config to SkinProvider so portals receive the same id. Without a provider, components use core defaults and application root tokens. Variables the pack invents (`---*`) are declared in skin.css beside the rules that read them, scoped `[data-skin=""]` — never added to theme.css and never leaked unscoped. Use descendant rules for coordinated anatomy and semantic families, but change recipe-painted properties by re-valuing their knobs rather than competing with the recipe declaration. When a visual decision has no knob, add the typed property, recipe default, contract entry, and skin override together. Per-instance knob values travel through the typed `style` prop and remain last in the cascade. A pack's behavioral fx activates through nested `adornments` on the component's named anchor (e.g. `adornments: { "chat-composer": { "send-layer": (ctx) => } }`) — the referenced extension lives in a separate "use client" file the pack ships, the config is imported by a client provider wrapper, and the manifest lists the extension files as additional targets. Run the pack test suite from apps/docs (`bun test`): knob completeness, typed registrations, fixture parity, theme coverage, and popup surface consistency are enforced requirements. - avoid: `!important` or the Tailwind `!` suffix to beat a recipe-painted property — add or re-value the corresponding knob instead. Painting a recipe-owned background, border, radius, shadow, color, typography, or motion property directly in skin.css; parallel paint declarations drift as component states evolve. Unregistered component custom properties, knobs missing from the contract array, or contract entries without a recipe default. Bare host-element selectors (`button`, `input`, `code`) or un-layered rules — they leak past the component anatomy and outlive the skin's scope. `animation: none` for calm variants — the motion kill-switch collapses `--duration-*` to 0ms so `animationend` cleanup still fires; disable durations through tokens, never the animation itself. Hardcoding geometry the token contract already parameterizes; prefer the shared token or a component knob so editor controls and skins stay live. - **Extension authoring** — Every fx lands on exactly one mechanism, costs nothing when absent, and keeps its a11y/perf guarantees in the component-owned wrapper instead of hand-rolled placement. - do: Sort the fx first (/architecture#extension-contract): persistent state → plain CSS on the emitted data-state/data-slot anatomy, no mechanism; transient skin-owned event → an anchored extension; per-instance decoration → call-site composition; cross-cutting uniformity → a root extension; behavior needing internals that many skins want → an integrated behavior (lazy + `ControlUiSkin` field); everything else → rung 8. An anchor is ONE position contract owned by the component: it ships the positioned wrapper (aria-hidden, pointer-events-none, `contain: paint`, clipping) rendered only when `skinAdornment(...)` resolves, and the extension only supplies visuals — never a portal, never its own positioning. Anchor ctx carries plain render-time values (a render prop, not React Context); state that only feeds an anchor is guarded by `hasSkinAdornment(...)` so unfilled apps pay no extra update (the `trackSends` precedent in use-chat-composer). One-shot replay is structural: key the layer on a ctx counter and play a mount-time keyframe/@starting-style that parks invisible; every duration rides `--duration-*` so `data-motion="reduced"` collapses it for free. Theme the extension through its own component-local tokens with baseline defaults in its CSS (`--aurora-*`), re-valuable by a pack and overridable inline via a typed prop — never hardcoded colors. Root extensions attach by semantic family (`[data-control-ui][data-control="true"]`), mount once above their targets (a route segment counts), delegate events, and park when their skin is inactive (liquid-metal's gated observer). Ship every extension as its own registry item (`registry:style` or component+CSS) so the install boundary IS the cost boundary; a pack that activates it lists the same files in its manifest so both install paths stay byte-identical (validate-registry allowlist). Components without an anchor carry the breadcrumb comment with the canonical slug (`{/* fx anchor candidate: ":" */}`) — documentation and promotion path, never an activation channel. - avoid: CustomEvent buses or context providers to notify a component-scoped extension — the anchored render prop already delivers typed values with no indirection. Portals for anchored layers: the component's own stacking context is the contract; a portal escapes the wrapper's a11y/hit-test/containment guarantees. Baking an fx into a component because one design wants it — that ships bytes to every install; anchor it and let skin.config decide. Presenting the breadcrumb comment as wiring users must fill, or activating extensions anywhere but skin.config (anchored) / an explicit layout mount (root). - **Registry-first DX** — Keep the shadcn registry as the install source of truth while avoiding copied demo-only code. - do: Every installable file is referenced by a registry manifest. Docs snippets are read from real source files. Generated fixtures are checked by validation scripts. - avoid: Maintaining raw code strings in docs. Publishing a package API before the registry contract is stable. - **Skin completeness** — Make every skin a complete visual layer for the public Control UI component set. - do: Each skin implements the same documented component files. Each skin's theme.css explicitly resolves the complete token contract in light and dark without inheriting Refined or core values. Skins can replace UI dependencies when the visual behavior needs more than classes. Adapters never import skin internals. - avoid: Partial skins that only work on one block. Variant matrices for theme-level differences. - **Control UI composition** — Prefer explicit React composition so advanced blocks remain readable after installation. - do: Blocks compose documented top-level components. Actions are children, not data arrays, when the user may customize layout. Reusable behavior lives in hooks or context, not in theme code. - avoid: Component factories in installed skin files. Duplicated behavior between component source and skin packs. - **Runtime-agnostic UI** — Keep Control UI core components independent from model runners, message stores, and transport lifecycles. - do: Core components render from plain props and children without requiring a runtime provider. Context is only used for local UI anatomy or behavior, such as role, density, open state, copy value, or submit state. Provider-specific code lives in usage examples or host-app mapping functions, not installed core files. Blocks compose UI components and callbacks without owning model calls, streaming transport, persistence, branching, or tool execution. - avoid: AssistantRuntimeProvider-style APIs that make UI components depend on one runtime shape. Importing Mastra, AI SDK, LangChain, or custom runner packages from skins or core components. Hiding message/thread/run lifecycle inside visual components. Installing runner adapters as the default path for rendering the UI. - **Compound components DX** — Prefer anatomy-based compound components over large prop bags for customizable Control UI surfaces. - do: Expose meaningful parts such as Root, Header, Content, Actions, Media, Title, and Submit when users may customize layout. Keep data or state props only where they drive behavior, such as from, state, value, onSubmit, or code. Type each part with ComponentProps<> for its rendered element or base UI component. Forward remaining native props with ...props and let users customize each part with className. - avoid: Adding new visual props when a child component slot would be clearer. Centralized classNames slot objects for components that can expose real parts. Data arrays for actions or toolbar items when users should compose children directly. - **Context vs props** — Keep compound-component context to the cases props cannot cover, and put semantic intent in the value rather than precomputed classes. - do: Add a context only when both hold: the Root owns something parts need (state, or a mode that also styles the Root), and consumers place those parts in JSX you do not control, so a prop cannot reach them. Store the smallest semantic value — modes, flags, callbacks — and let each part derive its own classes; do not freeze precomputed className strings in the value. Let a per-part prop override the context default (`maxHeight ?? context.maxHeight`), so context is the cascade and props are the escape hatch. - avoid: Creating a context for parts you fully control or that share nothing with the Root — pass props instead. Freezing precomputed Tailwind strings in the context value, which couples every part to the Root and blocks skins from varying by attribute.