sdk/packages/ui/ADOPTION.md
@cline/ui adoption primerThis guide is for Cline engineering teams that want a web application to share the Cline visual language and agent-chat presentation without copying desktop styles or adopting desktop product structure.
@cline/ui has two opt-in layers:
The theme provides:
The first component surface provides:
Each application continues to own:
This boundary gives Cline products a shared visual and interaction language
without turning @cline/ui into a second agent runtime.
@cline/ui is configured for public npm publication with its own version and
manual release workflow. Check availability with npm view @cline/ui version;
an E404 means the first release is still pending. The API is pre-stable, so
production consumers should pin exact versions and review compatibility notes
when updating.
Desktop is the first production-shaped consumer of both the theme and shared chat primitives. Storybook is the reference catalog for isolated component states. Hub and other agent interfaces are candidates for the next adoption pass once their runtime and Markdown adapters are mapped explicitly.
| Goal | Import | Tailwind required | React required |
|---|---|---|---|
| Use only light/dark CSS variables | @cline/ui/theme/tokens.css | No | No |
| Render shared approval, welcome, search, or status UI | @cline/ui/theme/tokens.css, @cline/ui/components.css, and @cline/ui | No | React 18.3 or 19 |
| Use tokens through Tailwind utilities | tokens.css then theme.css | Tailwind v4 | No |
| Use the complete theme and shared base behavior | @cline/ui/theme/index.css | Tailwind v4 | No |
| Compose shared agent-chat presentation | @cline/ui/components/agent-chat plus its CSS | No, if tokens are mapped in plain CSS | React 18.3 or 19 |
The package exports base.css separately for consumers that want its global,
Markdown, scrollbar, selection, cursor, and native color-scheme behavior.
There is no @cline/ui/theme shorthand. Use the explicit paths documented here
so dependencies remain visible.
Add the workspace dependency:
{
"dependencies": {
"@cline/ui": "workspace:*"
}
}
Run the repository's normal package installation workflow after updating the manifest and lockfile.
After the initial release is available, install the latest production UI
release. The --exact flag records the resolved version instead of a range:
bun add --exact @cline/ui
The package is ESM. Its React entry point targets browser applications. Install only the prerequisites for the layer being adopted:
# Required for React components
bun add react@^19 react-dom@^19
# Required for the documented Tailwind-backed theme and Cline fonts
bun add @fontsource-variable/schibsted-grotesk @fontsource/azeret-mono
bun add --dev tailwindcss
Applications already on React 18.3 can retain that compatible version. Tokens-only consumers do not need React or Tailwind.
Commit the consuming repository's lockfile so builds continue using the same resolved version. Use the package manager's update command when the team intentionally wants to move to a newer release:
bun update @cline/ui
For deliberate previews, UI releases can publish an unstable next npm tag:
bun add --exact @cline/ui@next
Do not use next for production applications. UI versions move independently
from the runtime SDK packages.
Import fonts and Tailwind before the complete theme:
@import "@fontsource-variable/schibsted-grotesk";
@import "@fontsource/azeret-mono/latin.css";
@import "tailwindcss";
@import "@cline/ui/theme/index.css";
This supplies:
color-schemeApplication-specific CSS should follow these imports.
Use this when the application wants the shared tokens and utilities but already owns document, Markdown, scrollbar, or cursor behavior:
@import "@fontsource-variable/schibsted-grotesk";
@import "@fontsource/azeret-mono/latin.css";
@import "tailwindcss";
@import "@cline/ui/theme/tokens.css";
@import "@cline/ui/theme/theme.css";
If the application later opts into shared base behavior, import
@cline/ui/theme/base.css after theme.css.
Applications without Tailwind can import only the variables:
@import "@cline/ui/theme/tokens.css";
Token-only consumers must provide:
color-scheme, if desiredFor native controls that should follow the selected theme:
:root {
color-scheme: light;
}
.dark {
color-scheme: dark;
}
With the complete Tailwind theme, import the component styles afterward:
@import "@cline/ui/theme/index.css";
@import "@cline/ui/components/agent-chat.css";
Without Tailwind, import the framework-neutral tokens and component styles, then apply the shared font family at an app or chat root (tokens define font values but do not apply document typography):
@import "@cline/ui/theme/tokens.css";
@import "@cline/ui/components/agent-chat.css";
.agent-chat-root {
font-family: var(--font-sans);
}
Then compose the presentation around the consuming application's own data:
import type { ReactNode } from "react";
import {
type AgentMessageRole,
Conversation,
ConversationContent,
ConversationScrollButton,
ConversationViewport,
Message,
MessageActions,
MessageAction,
MessageContent,
Reasoning,
ReasoningContent,
ReasoningTrigger,
ToolActivity,
ToolActivityContent,
ToolActivityTrigger,
} from "@cline/ui/components/agent-chat";
type ProductMessage = {
id: string;
role: "human" | "agent" | "system" | "error";
content: string;
reasoning?: string;
isStreaming?: boolean;
};
const roleMap: Record<ProductMessage["role"], AgentMessageRole> = {
human: "user",
agent: "assistant",
system: "system",
error: "error",
};
type AgentTranscriptProps = {
conversationId: string;
messages: ProductMessage[];
onCopy: (message: ProductMessage) => void;
renderMarkdown: (content: string) => ReactNode;
};
export function AgentTranscript({
conversationId,
messages,
onCopy,
renderMarkdown,
}: AgentTranscriptProps) {
return (
<Conversation
className="agent-chat-root"
key={conversationId}
style={{ height: "32rem" }}
>
<ConversationViewport aria-label="Agent conversation">
<ConversationContent>
{messages.map((message) => (
<Message from={roleMap[message.role]} key={message.id}>
<MessageContent>
{message.reasoning ? (
<Reasoning isStreaming={message.isStreaming}>
<ReasoningTrigger />
<ReasoningContent>
{renderMarkdown(message.reasoning)}
</ReasoningContent>
</Reasoning>
) : null}
{renderMarkdown(message.content)}
</MessageContent>
<MessageActions>
<MessageAction label="Copy message" onClick={() => onCopy(message)}>
Copy
</MessageAction>
</MessageActions>
</Message>
))}
<ToolActivity expandable>
<ToolActivityTrigger
label="Edited 2 files"
additions={24}
deletions={8}
status="success"
/>
<ToolActivityContent>Normalized tool details</ToolActivityContent>
</ToolActivity>
</ConversationContent>
</ConversationViewport>
<ConversationScrollButton />
</Conversation>
);
}
The explicit height keeps this standalone example scrollable. In a real shell,
an equivalent bounded flex layout works too: every ancestor in the height chain
must allow shrinking (commonly min-height: 0) and the conversation must fill
the available height.
The example intentionally injects a consumer-owned renderMarkdown. Different
products currently have different Streamdown plugins, syntax-highlighting
budgets, link-confirmation behavior, and image policies. The React key resets
conversation-local state when the active session changes. The shared package
standardizes the surrounding presentation without silently changing those
security and product decisions.
Map runtime roles and tool states at the consumer boundary. Do not make the UI
package depend on @cline/core, the Vercel AI SDK, desktop schemas, or transport
events.
From the Cline repository root:
bun -F @cline/ui storybook
Open http://localhost:6006. The toolbar switches light/dark mode and offers
representative chat and mobile viewports. Stories cover:
In the repository's agent sandbox, bind to a forwarded host and unused port:
bun -F @cline/ui storybook -- --host 0.0.0.0 --port 3490 --exact-port
Build the production Storybook bundle with:
bun -F @cline/ui build-storybook
Storybook is the isolated component reference. Real application builds remain the integration test for runtime adapters and product CSS.
The catalog currently runs from a Cline monorepo checkout. Story sources and configuration are not included in the npm package, and the catalog is not hosted yet.
Product components should use semantic tokens:
.card {
color: var(--card-foreground);
background: var(--card);
border-color: var(--border);
}
.primary-action {
color: var(--primary-foreground);
background: var(--primary);
}
Use the small --brand-* palette and --primary-emphasis for branded artwork
or deliberate emphasis. Normal controls should prefer semantic tokens so they
continue to work across light, dark, and future theme layers.
Import the package first, then override standard semantic values:
@import "@cline/ui/theme/index.css";
:root {
--primary: /* product-specific value */;
}
.dark {
--primary: /* dark product-specific value */;
}
Do not copy tokens.css or component CSS into the consuming application.
Explicit overrides make product differences reviewable and allow future
package upgrades.
Keep the following outside @cline/ui:
#__next, viewport locking, and shell layoutThe package should standardize repeated visual and interaction language, not erase product boundaries.
workspace:* or a pinned npm version.agent-chat.css when using the React primitives.Conversation React key..dark behavior.Until the package has a stable version, contract changes should:
tokens.css framework-neutralRemoving or changing the meaning of a semantic token or component prop should eventually be treated as a breaking change. Additive tokens, props, and entry points can be introduced compatibly.
The npm package solves cross-repository distribution. The remaining work is to validate and stabilize the public contract.
Recommended sequence:
Likely follow-up components should be driven by repeated needs. Approval cards, follow-up questions, attachments, and prompt composers are candidates, but their current product contracts should be compared before standardizing them.