Architecture
Runtime ownership, skin layering, customization paths, and registry derivation.
Runtime and source ownership
The host application owns model calls, transport, streaming, persistence, branching, and tool execution. Control UI owns the installed component behavior, markup, local UI state, and stable anatomy. Provider-specific usage examples compose native runtime messages directly at that boundary without introducing a normalized Control UI message model.
Interactive previews are host-app integration surfaces, so they can mount a provider's official client and a server-side mock runtime. They render the provider's native messages directly; they do not add a Control UI message format or convert one provider's shape into another. Provider code never enters installable components, blocks, hooks, or utilities.
import { ChatMessage, ChatMessageAvatar, ChatMessageBody, ChatMessageContent, ChatMessageHeader, ChatMessageRow,} from "@/components/control-ui/chat-message";export function AssistantMessage({ children }: { children: ReactNode }) { return ( <ChatMessage from="assistant"> <ChatMessageRow> <ChatMessageAvatar>AI</ChatMessageAvatar> <ChatMessageBody> <ChatMessageHeader>Assistant</ChatMessageHeader> <ChatMessageContent>{children}</ChatMessageContent> </ChatMessageBody> </ChatMessageRow> </ChatMessage> );}Skins over one component tree
Every skin owns the same three files over one component source. Its theme.css explicitly resolves the complete token
contract for light and dark modes; it never inherits another skin's values. Component recipes declare registered,
typed custom-property knobs for every visual decision. Scoped skin.css re-values those knobs and owns CSS-only work:
pseudo-elements, keyframes, native chrome, relational selectors, and uniform semantic families. skin.config.tsx
contains only typed design-system behavior choices and optional adornments. Resolution order is component recipe, skin
knob values, CSS-only skin rules, then caller className.
- skin.config.tsxtyped slots · DS choices · adornments
- skin.csspseudo-elements · keyframes · descendant families
- theme.csstoken values scoped by data-skin
{ id: "refined" }. Every installed pack supplies this file; no provider or wrapper is required.The knob cascade
The whole styling system rests on four CSS rules, each verifiable in the shipped files.
@layer components behind :where(), so any
selector you write anywhere outranks the library's paint.@property { inherits: true }, and each family declares its defaults only
at its root parts. Re-value a knob on any ancestor — the skin root, a data-skin boundary, a free data-*
attribute, an inline style — and every part beneath inherits it.@layer components, any unlayered stylesheet wins against them even at
zero specificity. That is the deliberate exit for overrides — and it means a broad unlayered reset in your app
silently repaints Control UI parts too. Keep global resets in a layer.The compiled styles set the browser floor: registered custom properties and relative color syntax — 274
oklch(from …) declarations across the recipes — require Safari 16.4, Chrome 119, and Firefox 128. One rich-tooltip
highlight uses currentColor inside relative color, which resolves only from Chrome 131, Safari 18, and Firefox 133;
below that the highlight declaration drops and the tooltip renders without it.
Stable anatomy without runtime metadata
Every public part emits three attributes, and only one of them is the selector key. data-control-family names the
family whose knobs paint the part — key on it. data-slot names the part inside its component. data-control-ui
names the component itself, for devtools, adornments and root extensions. Families that several components share add
data-<family>-kind to say which member a rule means, and data-<family>-part to name the part's role in the family.
data-skin belongs on the skin boundary, including portal positioners. It is not repeated on every component.
/* One exact part in one skin. */[data-skin="rig"] :where([data-slot="root"][data-control-family="chat-composer"]) { ... }/* One member of a shared family: toasts, not every popup. */[data-skin="rig"] :where([data-slot="root"][data-popup-kind="toast"][data-control-family="popup"]) { ... }/* Every interactive Control UI root, without touching host application controls. */[data-skin="rig"] [data-control="true"][data-control-family] { ... }The generated skin-contract.json is the equivalent of a defineAnatomy catalog, but it is built from the component
source instead of becoming a browser-side object. It lists scopes, parts, states, adornments, semantic families, and
registry ownership in one agent-readable artifact. Item API responses separate ownScopes from installedScopes, so
an agent sees both the requested component and the anatomy brought by its dependency closure.
Keep the active skin sparse
skin.config.tsx is one shared ES module containing only behavior choices and optional adornments. Component rendering
performs no runtime class lookup: recipes read registered custom properties directly from CSS. Visual breadth therefore
adds no active JavaScript, and unused component recipes remain outside an install unless the component manifest declares
them.
Bundle tests cap every shipped config at 4 kB gzip. Refined and Flat remain nearly empty; richer packs pay only for their behavior and adornment config, not once per rendered component.
Choose the smallest customization surface
Start at the top and stop at the first rung that expresses the change. Each step downward owns more behavior and is harder to undo.
- 1Tokentheme.cssChange a named value
- 2VariantpropTwo values coexist in one app
- 3DS choiceControlUiSkinOne decision for the design system
- 4Slotskin.configReact to existing variants
- 5Pack CSSskin.cssPseudo-elements, keyframes, families
- 6Global utilityeffects.cssReusable token-driven effect
- 7Extensionoptional itemInstallable behavior or anchored effect
- 8Edit sourceowned fileRestructure the installed anatomy
Typed vocabularies stay closed. variant, tone, and every other union name what the library paints, not what one brand
needs. A look the vocabulary does not name is stamped at the call site as a free data-* attribute — every part forwards
unknown props to the DOM — and painted by re-valuing knobs under it, without a new union member or a forked component
source. Shipped skin packs key only on emitted anatomy, so the free attribute belongs in the application's own CSS, which
already outranks both the zero-specificity recipe and the skin.
/* Application CSS. The call site renders <Button data-campaign="launch" />. */[data-slot="root"][data-control-family="button"][data-campaign="launch"] { --cui-button-bg: var(--brand-launch); --cui-button-hover-bg: var(--brand-launch-hover);}Registry source of truth
The docs catalog and real source imports define file ownership, dependencies, and each transitive install closure. Source manifests, public payloads, previews, API metadata, and agent documentation are generated views; validation rejects drift between them.
The current anatomy contract is version 7. Version 6 removed runtime class and paint maps: component visuals now flow through
registered CSS knob contracts, while skin config retains only behavior and adornments. Version 7 publishes every registered
--cui-* knob (syntax and recipe default included) in the generated contract. This pre-release registry uses a
clean contract cutover: reinstall core, affected components or blocks, and the selected skin together.