packages/ui/README.md
Cherry Studio UI component library for React applications.
npm install @cherrystudio/ui
# peer dependencies
npm install motion react react-dom tailwindcss
The recommended integration style in this repository is to use the package export entry points:
@cherrystudio/ui@cherrystudio/ui/components@cherrystudio/ui/icons@cherrystudio/ui/utils@cherrystudio/ui/styles/tokens.css@cherrystudio/ui/styles/theme.css
Use the full Cherry Studio design system so Tailwind theme tokens resolve to Cherry Studio values.
/* app.css */
@import '@cherrystudio/ui/styles/theme.css';
Characteristics:
bg-primary, bg-red-500, p-4, rounded-lg)rounded-4xl plus rounded-full; smaller Cherry aliases remain available for compatibilityExample:
<Button className="bg-primary text-red-500 p-4 rounded-lg">
</Button>
<div className="rounded-4xs">Tiny radius (0.03125rem)</div>
<div className="rounded-xs">Small radius (0.125rem)</div>
<div className="rounded-md">Medium radius (0.5rem)</div>
<div className="rounded-xl">Large radius (0.875rem)</div>
<div className="rounded-full">Full radius (9999px)</div>
Import only primitives and existing foundation providers, then decide which values your design system exposes.
/* app.css */
@import 'tailwindcss';
@import '@cherrystudio/ui/styles/tokens.css';
/* Re-export only the parts you need */
@theme inline {
--color-primary: var(--cs-brand-500); /* Adopt a Cherry Studio foundation value */
--color-red-500: oklch(...); /* Keep your own red scale */
--radius-lg: 1rem; /* Keep your own radius */
}
Characteristics:
var(--cs-brand-500), var(--cs-red-500))Component consumption after defining the adapter:
<button className="bg-primary text-primary-foreground">Use the adopted primary color</button>
<div className="bg-red-500">
Use the default Tailwind red
</div>
<div className="rounded-lg bg-primary text-primary-foreground" />
src/styles/contract.css is an internal composition layer used by the generated theme.css entry to preserve the
foundation โ runtime input โ Shadcn โ product import order. It is not a public package export or a supported
consumer entry point.
The normative v2 architecture, Shadcn contract, and migration boundary are defined in Design Token System. Official Shadcn variables remain unprefixed; approved Cherry Studio product variables extend the same unprefixed public namespace. Use the Variable Catalog to select a stable role and distinguish runtime API from internal providers and tooling-only historical names.
To avoid mixing value sources, semantic variables, theme mappings, and runtime overrides, use these rules:
--background, --primary, --muted-foreground, and the other variables in shadcn.css are the official Shadcn contract--success and --background-subtle--cs-* variables are internal value providers; a selective-foundation consumer may reference primitive
providers only while defining its own adapter, not from ordinary component styles. --cs-theme-* is the reserved
host-written input subset--color-*, --radius-*, and --font-* are Tailwind adapter output, not another semantic input layer. Only the
adapter owner declares --color-* inside @theme; component CSS, page CSS, and renderer
TypeScript/TSX-authored styles must neither declare nor consume --color-*. Components normally consume generated
radius and typography mappings through Tailwind utilities.--cs-theme-* is a controlled host-written input, not a component-facing semantic role or Tailwind utilityDefault consumption rules:
@cherrystudio/ui/styles/theme.css by defaultbg-background, text-muted-foreground, and bg-success; custom CSS may use the matching official or product variable@cherrystudio/ui/styles/tokens.css--cs-theme-* inputs, not directly to official semantics or derived --color-* variables; renderer-only runtime values stay owner-local under --app-*Use the Shadcn CLI to scaffold or update component source and dependency metadata only. Cherry Studio's authored
theme layers and generator own the shared CSS contract, even though components.json points the CLI at the generated
src/styles/theme.css entry.
src/styles/theme.css; pnpm theme:build is its only writer.shadcn add and place it according to ownership: official semantics in shadcn.css,
Cherry Studio product semantics in product.css and theme-contract.ts, and component-local styles with the
component.pnpm theme:build followed by pnpm theme:check after accepting a component that changes theme requirements.import { Button, Input } from '@cherrystudio/ui'
function App() {
return (
<div>
<Button variant="default" size="default">Click me</Button>
<Input
type="text"
placeholder="Type here"
onChange={(event) => console.log(event.currentTarget.value)}
/>
</div>
)
}
// Components only
import { Button } from '@cherrystudio/ui/components'
// Utilities only
import { DIALOG_CLOSE_DURATION_MS, DIALOG_UNMOUNT_DELAY_MS, toUndefinedIfNull } from '@cherrystudio/ui/utils'
# Install dependencies
pnpm install
# Development mode
pnpm dev
# Build
pnpm build
# Type check
pnpm type:check
# Validate the variable graph, generated adapter, registry, and renderer authored-CSS boundary
pnpm theme:check
# Run tests
pnpm test
Use the package command for all icon generation so ESLint fixes and the repository formatter run after the generated files are updated.
In this command, --type=icons means general UI icons that are not Provider or Model logos.
# Generate general icons, Providers, and Models
pnpm icons:generate
# General icons
pnpm icons:generate --type=icons
# Provider icons, Avatars, barrels, and catalogs
pnpm icons:generate --type=providers
# Model icons, Avatars, barrels, and catalogs
pnpm icons:generate --type=models
| Type | SVG source | Generated output |
|---|---|---|
icons | icons/general/*.svg | General React icon components and their barrel |
providers | icons/providers/{light,dark}/*.svg | Provider light/dark components, metadata, Avatars, barrels, and catalogs |
models | icons/models/{light,dark}/*.svg | Model light/dark components, metadata, Avatars, barrels, and catalogs |
Generation uses a hash cache and skips unchanged SVG files. Use the optional arguments when a narrower or clean regeneration is needed:
# Regenerate one provider and its Avatar/catalog entries
pnpm icons:generate --type=providers --only=opencode
# Regenerate multiple models
pnpm icons:generate --type=models --only=claude,gemini
# Ignore the hash cache and regenerate every provider
pnpm icons:generate --type=providers --force
--type generates all three groups in order: icons, providers, then models.--type=icons|providers|models limits generation to one source and output group.--only=<name[,name]> limits Provider or Model component and Avatar generation to the listed names.--force bypasses the SVG hash cache.Provider and Model generation runs the SVG component stage first and the Avatar/catalog stage second. The posticons:generate lifecycle script fixes the generated icon files with ESLint, then runs the repository formatter once after both stages complete. Internal scripts under scripts/ are still available for pipeline development, but normal usage should go through pnpm icons:generate.
The packages/ui workspace contains both runtime code and development-only assets.
src/dist/ build outputpackage.jsonstories/ and .storybook/scripts/ used for icon and theme generationicons/ source assets used by the generation pipelinedocs/ for migration and reference materialOnly the runtime surface should be treated as consumable package API.
packages/ui keeps Shadcn-compatible data-slot attributes for component-internal styling and its standalone build.
When Cherry Studio consumes the package source, the app's UI-contract generator treats those markers as structural
semantics and emits the corresponding public data-ui part:* tokens without removing the original attributes.
Existing renderer code, application tests, and custom themes that use data-slot continue to work; new selectors can
use the generated semantic layer. The application-level token grammar, stability tiers, maintained anchors, and
selector rules are defined by the
UI Semantic Contract. Explicit roles and maintained part:* tokens
are public selectors; inferred roles are best-effort discovery coordinates.
docs/ # Migration plans and reference docs
src/
โโโ components/
โ โโโ primitives/ # Primitive components
โ โโโ composites/ # Composite components
โ โโโ icons/ # Icon runtime exports and catalogs
โ โโโ index.ts
โโโ hooks/ # React Hooks
โโโ lib/ # Internal utilities
โโโ styles/ # Tokens and theme entry files
โโโ utils/ # Utility functions
โโโ index.ts # Main runtime entry point
scripts/ # Theme and icon generation tooling
stories/ # Storybook stories and sandbox usage
icons/ # Raw icon assets for code generation
All file and directory names under packages/ui/ follow kebab-case (per shadcn CLI convention and project-wide rule ยง4.5 in ../../docs/references/naming-conventions.md). This covers primitives/, composites/, icons/, hooks/, and stories/ alike. Exported identifiers inside files remain PascalCase for components and camelCase for utilities and hooks.
Examples:
button.tsx exports Buttondata-table.tsx exports DataTableerror-boundary/index.tsx exports ErrorBoundaryuse-dnd-reorder.ts exports useDndReorderA button component with multiple variants and sizes.
Props:
variant: default | destructive | outline | secondary | emphasis | ghost | linksize: default | sm | lg | icon | icon-sm | icon-lg | icon-navbarloading, loadingIcon, loadingIconClassName: loading-state controlsasChild: render through Radix SlotThe Shadcn-compatible native input primitive.
Props:
type, value, and event-based onChangearia-invalid for invalid-state stylingclassName for supported layout compositionKeeps drag reordering correct when the rendered list is a filtered subset of the source list.
Reads the active and hovered identifiers from the current dnd-kit context.
Converts null to undefined at API boundaries.
Converts undefined to null at API boundaries.
Duration of the Dialog CSS close animation: 200 ms.
Delay for imperative Dialog hosts before unmounting: 200 ms.
MIT