@create-ui/cli 0.1.0-beta.1 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +1 -1
- package/README.md +194 -24
- package/dist/{chunk-UPXNWTZZ.js → chunk-EWAP55CF.js} +9 -9
- package/dist/chunk-EWAP55CF.js.map +1 -0
- package/dist/chunk-MK3CCMH4.js +3 -0
- package/dist/{chunk-RJOEUUDA.js.map → chunk-MK3CCMH4.js.map} +1 -1
- package/dist/{chunk-HRI6QVOR.js → chunk-UVIUVCLG.js} +6 -6
- package/dist/chunk-UVIUVCLG.js.map +1 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.js +61 -59
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/registry/index.js +1 -1
- package/dist/skills/createui/SKILL.md +212 -0
- package/dist/skills/createui/agents/openai.yml +5 -0
- package/dist/skills/createui/assets/createui-small.png +0 -0
- package/dist/skills/createui/assets/createui.png +0 -0
- package/dist/skills/createui/cli.md +309 -0
- package/dist/skills/createui/contributing.md +213 -0
- package/dist/skills/createui/customization.md +284 -0
- package/dist/skills/createui/evals/evals.json +47 -0
- package/dist/skills/createui/mcp.md +151 -0
- package/dist/skills/createui/rules/composition.md +249 -0
- package/dist/skills/createui/rules/forms.md +301 -0
- package/dist/skills/createui/rules/icons.md +130 -0
- package/dist/skills/createui/rules/styling.md +253 -0
- package/dist/utils/index.js +1 -1
- package/package.json +4 -3
- package/dist/chunk-HRI6QVOR.js.map +0 -1
- package/dist/chunk-RJOEUUDA.js +0 -3
- package/dist/chunk-UPXNWTZZ.js.map +0 -1
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Contributing to the Create UI Monorepo
|
|
2
|
+
|
|
3
|
+
This guide is for contributors working **inside** the Create UI monorepo — adding or editing
|
|
4
|
+
registry components, the CLI, or the documentation site. If you only want to consume Create UI in
|
|
5
|
+
your own app, see [`cli.md`](./cli.md) instead.
|
|
6
|
+
|
|
7
|
+
## Contents
|
|
8
|
+
|
|
9
|
+
- [Monorepo layout](#monorepo-layout)
|
|
10
|
+
- [Dev, build & quality commands](#dev-build--quality-commands)
|
|
11
|
+
- [Tests](#tests)
|
|
12
|
+
- [The registry](#the-registry)
|
|
13
|
+
- [Authoring a component](#authoring-a-component)
|
|
14
|
+
- [Scaffolding & validation](#scaffolding--validation)
|
|
15
|
+
- [Testing the CLI locally](#testing-the-cli-locally)
|
|
16
|
+
- [Website: three-layer architecture](#website-three-layer-architecture)
|
|
17
|
+
- [Component rules recap](#component-rules-recap)
|
|
18
|
+
- [Commit convention](#commit-convention)
|
|
19
|
+
|
|
20
|
+
## Monorepo layout
|
|
21
|
+
|
|
22
|
+
Create UI is a **pnpm 9.0.6 + Turbo** monorepo with two workspaces:
|
|
23
|
+
|
|
24
|
+
| Workspace | What it is | Stack |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| `apps/v4` | Documentation site & component showcase | Next.js 16 (App Router), React 19, Tailwind CSS v4 |
|
|
27
|
+
| `packages/createui` | The `createui` CLI published to npm | Commander.js, tsup (ESM-only) |
|
|
28
|
+
|
|
29
|
+
The site is also where the **registry** lives (`apps/v4/registry/`). The CLI reads that registry —
|
|
30
|
+
in the monorepo it points at the local dev server (`http://localhost:4000/r`).
|
|
31
|
+
|
|
32
|
+
## Dev, build & quality commands
|
|
33
|
+
|
|
34
|
+
Run all commands from the repo root. Package manager is **pnpm** — do not use npm or yarn.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Develop
|
|
38
|
+
pnpm dev # everything, in parallel via Turbo
|
|
39
|
+
pnpm v4:dev # docs/showcase site only (port 4000)
|
|
40
|
+
pnpm createui:dev # CLI only, in watch mode
|
|
41
|
+
|
|
42
|
+
# Build
|
|
43
|
+
pnpm build # all workspaces
|
|
44
|
+
pnpm v4:build # site only
|
|
45
|
+
pnpm createui:build # CLI only
|
|
46
|
+
|
|
47
|
+
# Quality
|
|
48
|
+
pnpm check # lint + typecheck + format:check (run this before opening a PR)
|
|
49
|
+
pnpm lint # ESLint
|
|
50
|
+
pnpm lint:fix # ESLint with --fix
|
|
51
|
+
pnpm typecheck # TypeScript
|
|
52
|
+
pnpm format:write # Prettier write
|
|
53
|
+
pnpm format:check # Prettier check
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Tests
|
|
57
|
+
|
|
58
|
+
The test suite needs the v4 dev server running; `pnpm test` handles that for you.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pnpm test # starts the v4 dev server, then runs Vitest
|
|
62
|
+
pnpm test:dev # runs Vitest against an already-running server (no startup)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## The registry
|
|
66
|
+
|
|
67
|
+
Every UI component is defined under `apps/v4/registry/` and served to users through the CLI.
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
apps/v4/registry/
|
|
71
|
+
ui/ core UI components (button, select, dialog, …)
|
|
72
|
+
examples/ demos shown in the docs
|
|
73
|
+
blocks/ larger composed blocks
|
|
74
|
+
hooks/ custom React hooks
|
|
75
|
+
lib/ utilities (e.g. cn())
|
|
76
|
+
internal/ internal-only components (not exposed to users)
|
|
77
|
+
components/ shared registry components
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Each subdirectory has a `_registry.ts` that exports a metadata array, and every array is aggregated
|
|
81
|
+
in `apps/v4/registry/registry.ts`.
|
|
82
|
+
|
|
83
|
+
In-repo registry entries **omit `content`** — `pnpm registry:build` injects each file's source when
|
|
84
|
+
it generates `apps/v4/public/r/<name>.json`. Real entry shapes:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
// apps/v4/registry/ui/_registry.ts
|
|
88
|
+
{
|
|
89
|
+
name: "button",
|
|
90
|
+
type: "registry:ui",
|
|
91
|
+
dependencies: ["class-variance-authority", "radix-ui"],
|
|
92
|
+
files: [{ path: "ui/button.tsx", type: "registry:ui" }],
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
{
|
|
96
|
+
name: "select",
|
|
97
|
+
type: "registry:ui",
|
|
98
|
+
registryDependencies: ["field", "dropdown-menu"],
|
|
99
|
+
files: [{ path: "ui/select.tsx", type: "registry:ui" }],
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The `type` values, file objects (`{ path, type, content?, target? }`), and item fields are defined by
|
|
104
|
+
the registry-item schema (`$schema: https://createui.co/schema/registry-item.json`). Use
|
|
105
|
+
`dependencies` for npm packages and `registryDependencies` for other registry items the component
|
|
106
|
+
needs.
|
|
107
|
+
|
|
108
|
+
## Authoring a component
|
|
109
|
+
|
|
110
|
+
1. Create `apps/v4/registry/ui/<name>.tsx`:
|
|
111
|
+
- use **`cva`** for variants and **`cn()`** (from `@/registry/lib/utils`) to merge classes
|
|
112
|
+
- put `data-slot="<name>"` on the root element
|
|
113
|
+
- support polymorphism with **`asChild`** via the Radix `Slot`
|
|
114
|
+
- type props as `React.ComponentProps<"el"> & VariantProps<typeof variants> & { … }`
|
|
115
|
+
- use a **named export** (`export { Name }`)
|
|
116
|
+
2. Add an entry to `apps/v4/registry/ui/_registry.ts` (shape above).
|
|
117
|
+
3. Add a demo under `apps/v4/registry/examples/<name>-demo.tsx` and register it in
|
|
118
|
+
`apps/v4/registry/examples/_registry.ts`.
|
|
119
|
+
4. Run **`pnpm registry:build`** to rebuild the registry (it also runs `lint:fix` + format and
|
|
120
|
+
regenerates the `public/r/*.json` files).
|
|
121
|
+
|
|
122
|
+
> Filenames are **kebab-case**; exports are PascalCase named exports. There is **no** `pnpm tokens:build` —
|
|
123
|
+
> tokens live in `apps/v4/styles/globals.css` and are edited directly.
|
|
124
|
+
|
|
125
|
+
## Scaffolding & validation
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
pnpm scaffold <name> # create a component + example + registry entry
|
|
129
|
+
pnpm scaffold <name> --existing # generate an example from an existing component (parses CVA)
|
|
130
|
+
pnpm scaffold <name> --dry-run # preview output without writing files
|
|
131
|
+
pnpm scaffold <name> --force # overwrite existing files
|
|
132
|
+
|
|
133
|
+
pnpm validate:registries # validate every _registry.ts against the schema
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Testing the CLI locally
|
|
137
|
+
|
|
138
|
+
The root `pnpm createui` script runs the **local CLI build** against `http://localhost:4000/r`, so
|
|
139
|
+
start the site first:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
pnpm v4:dev # serves the local registry at http://localhost:4000/r
|
|
143
|
+
pnpm createui add -c ~/path/to/app # run the local CLI against a target app
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`-c` (`--cwd`) points at the project you want to add components to.
|
|
147
|
+
|
|
148
|
+
## Website: three-layer architecture
|
|
149
|
+
|
|
150
|
+
Site work follows a strict three-layer model:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
registry/ui/ primitives — SACROSANCT, never edited by site work
|
|
154
|
+
↓ composed by
|
|
155
|
+
components/site/ shared composites (reused across ≥2 route groups)
|
|
156
|
+
↓ composed by
|
|
157
|
+
app/(group)/_components/ route-local composites (used by one route group)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
A new composite belongs in `components/site/` only if it is reused across **two or more** route
|
|
161
|
+
groups; otherwise it lives in the route group's `_components/`. If a primitive is missing, **ask
|
|
162
|
+
first** — never edit `registry/ui/` to make a page work.
|
|
163
|
+
|
|
164
|
+
The authoritative website guides live in **`mds/website/`** — start at
|
|
165
|
+
[`mds/website/BRIEF.md`](../../mds/website/BRIEF.md) (it is written in Turkish). Consult those guides
|
|
166
|
+
for any website task; do not duplicate them here.
|
|
167
|
+
|
|
168
|
+
## Component rules recap
|
|
169
|
+
|
|
170
|
+
These conventions keep every component consistent (see [`rules/styling.md`](./rules/styling.md),
|
|
171
|
+
[`rules/composition.md`](./rules/composition.md), and [`rules/icons.md`](./rules/icons.md) for the
|
|
172
|
+
full rules):
|
|
173
|
+
|
|
174
|
+
- **`data-slot`** on the root element (and on meaningful sub-parts) for semantic styling hooks.
|
|
175
|
+
- **`cn()`** for all class merging — never string-concatenate classes.
|
|
176
|
+
- **`cva`** for variant-based styling; hoist the variants object out of the component.
|
|
177
|
+
- **Semantic tokens** for color: `bg-primary-base`, `text-error-base`, `bg-weak`, `text-body` —
|
|
178
|
+
never `bg-background` / `text-foreground`-style names.
|
|
179
|
+
- **Radix UI** primitives for accessibility; polymorphism via **`asChild`** + Radix `Slot` only.
|
|
180
|
+
- **Focus is `outline-*`**, never `ring-*` (e.g. `focus-visible:outline-primary-700`).
|
|
181
|
+
- Intersection prop types: `React.ComponentProps<"button"> & VariantProps<…> & { … }`.
|
|
182
|
+
- **kebab-case** filenames, **named** exports.
|
|
183
|
+
|
|
184
|
+
A correct root element looks like:
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
// Incorrect — ring focus, shadcn-style token, anonymous default export
|
|
188
|
+
<button className={`${base} focus-visible:ring-2 bg-primary`}>{children}</button>
|
|
189
|
+
|
|
190
|
+
// Correct — data-slot, cn(), outline focus, semantic token
|
|
191
|
+
<Comp
|
|
192
|
+
data-slot="button"
|
|
193
|
+
className={cn(buttonVariants({ variant, appearance, size, className }))}
|
|
194
|
+
{...props}
|
|
195
|
+
/>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
When you reference a component in an example, **read its source** at
|
|
199
|
+
`apps/v4/registry/ui/<name>.tsx` first — never guess props. For instance, `Button` takes
|
|
200
|
+
`variant` (`primary` | `neutral-solid` | `neutral-light` | `danger` | `success` | `inverse-solid` |
|
|
201
|
+
`inverse-light`), `appearance` (`solid` | `outline` | `ghost` | `soft`), `size`, `shape`, `loading`,
|
|
202
|
+
`iconOnly`, `leadingIcon`, `trailingIcon`, and `asChild` — there is no `variant="outline"` or
|
|
203
|
+
`variant="destructive"`.
|
|
204
|
+
|
|
205
|
+
## Commit convention
|
|
206
|
+
|
|
207
|
+
Conventional commits: `category(scope): message`.
|
|
208
|
+
|
|
209
|
+
Categories: `feat`, `fix`, `refactor`, `docs`, `build`, `test`, `ci`, `chore`.
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
feat(components): add loading prop to the button component
|
|
213
|
+
```
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# Customization & Theming
|
|
2
|
+
|
|
3
|
+
Create UI is **one styling system with many themes**. Components reference semantic CSS variable tokens; change the tokens (or swap the theme) and every component follows. Values are **HEX**, never OKLCH.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- How it works (CSS variables → Tailwind utilities → components)
|
|
8
|
+
- Token families (surfaces, text, primary scale, status, shadows, focus, radius)
|
|
9
|
+
- shadcn → Create UI token map (for migrants)
|
|
10
|
+
- Semantic spacing & typography tokens
|
|
11
|
+
- Theming (primary themes, neutral themes, font variants)
|
|
12
|
+
- Dark mode setup
|
|
13
|
+
- Adding a custom color / changing radius
|
|
14
|
+
- Customizing components (props, className, variants, wrappers)
|
|
15
|
+
- Checking for updates
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## How It Works
|
|
20
|
+
|
|
21
|
+
1. CSS variables are defined in `:root` (light) and overridden under `.dark` (dark mode), as **HEX** values.
|
|
22
|
+
2. Tailwind v4 auto-generates utilities from those `--color-*` / `--spacing-*` / `--radius-*` variables (`bg-weakest`, `text-body`, `gap-component-sm`, …).
|
|
23
|
+
3. Components consume those utilities — change a variable, or swap the active theme, and every component that references it updates.
|
|
24
|
+
|
|
25
|
+
There is no OKLCH, no `--primary`/`--muted` convention, and no parallel style/base stack. One system, layered themes.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Token Families
|
|
30
|
+
|
|
31
|
+
Create UI uses **semantic** token names. Backgrounds, borders, and text share a weak→strong scale; status colors and the primary scale have their own families.
|
|
32
|
+
|
|
33
|
+
### Surfaces
|
|
34
|
+
|
|
35
|
+
| Utility | Purpose |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| `bg-weakest` … `bg-strongest` | Neutral surface scale (`weakest`, `weak`, `light`, `medium`, `heavy`, `strong`, `strongest`) |
|
|
38
|
+
| `bg-static` | Page/card surface that swaps with theme (white in light, black in dark) |
|
|
39
|
+
| `bg-static-white` / `bg-static-black` | Surfaces that never swap |
|
|
40
|
+
| `border-weakest` … `border-strongest` | Border scale (use `border-light` / `border-medium` for inputs and dividers) |
|
|
41
|
+
|
|
42
|
+
### Text
|
|
43
|
+
|
|
44
|
+
| Utility | Purpose |
|
|
45
|
+
| --- | --- |
|
|
46
|
+
| `text-body` | Default body text |
|
|
47
|
+
| `text-placeholder` | Hints, secondary, muted text |
|
|
48
|
+
| `text-disabled` | Disabled text |
|
|
49
|
+
| `text-strongest` | High-contrast headings |
|
|
50
|
+
| `text-static` / `text-static-white` | Text that never swaps with theme |
|
|
51
|
+
|
|
52
|
+
### Primary scale
|
|
53
|
+
|
|
54
|
+
`bg-primary-50` … `bg-primary-950`, plus `bg-primary-base` (= `bg-primary-500`), `text-primary-base`, and the alphas `bg-primary-alpha-16` / `bg-primary-alpha-48` (subtle/hover tints). Focus uses `outline-primary-500` / `outline-primary-700`.
|
|
55
|
+
|
|
56
|
+
### Status families
|
|
57
|
+
|
|
58
|
+
Each of `error`, `success`, `warning`, `info` has a `-base` plus a `-weakest` … `-strongest` scale, e.g. `bg-error-base`, `text-error-base`, `outline-error-strongest`, `bg-success-base`, `bg-info-base`. **Prefer the semantic status tokens** in your own code. The shipped Button implements its danger/success variants with the raw `red-*` / `green-*` scales (e.g. `bg-red-600`, `bg-red-alpha-16`) — that is the implementation detail, not a pattern you need to copy.
|
|
59
|
+
|
|
60
|
+
### Shadows
|
|
61
|
+
|
|
62
|
+
Neutral elevation: `shadow-neutral-{2xs,xs,sm,md,lg,…}`. Component state shadows: `shadow-component-{primary,neutral,error,success,info,inverted}-{default,hover,focused}`. Text shadows: `text-shadow-{2xs,xs,…}`.
|
|
63
|
+
|
|
64
|
+
### Focus
|
|
65
|
+
|
|
66
|
+
Focus is an **outline**, never a ring. The pattern is a transparent base outline that becomes visible on focus:
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
"outline-2 outline-transparent focus-visible:outline-primary-700"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Use `outline-primary-700` (or `outline-strongest` / `outline-strong`) for focus. Never use `ring-*`.
|
|
73
|
+
|
|
74
|
+
### Radius
|
|
75
|
+
|
|
76
|
+
`--radius-none/xs/sm/md/lg/xl/2xl/3xl/4xl/5xl/full` back the `rounded-sm`/`rounded-md`/`rounded-lg`/`rounded-xl`/`rounded-2xl` utilities. Radius is set per component (e.g. Button maps each size to a radius via compound variants).
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## shadcn → Create UI Token Map
|
|
81
|
+
|
|
82
|
+
If you are migrating markup written against shadcn token names, translate them:
|
|
83
|
+
|
|
84
|
+
| shadcn | Create UI |
|
|
85
|
+
| --- | --- |
|
|
86
|
+
| `bg-background` | `bg-static` |
|
|
87
|
+
| `bg-card` | `bg-static` (or `bg-weakest` for subtle surfaces) |
|
|
88
|
+
| `text-foreground` | `text-body` |
|
|
89
|
+
| `text-muted-foreground` | `text-placeholder` |
|
|
90
|
+
| `bg-primary` | `bg-primary-base` (or `bg-primary-500`) |
|
|
91
|
+
| `text-primary-foreground` | `text-white` (on solid primary) |
|
|
92
|
+
| `border-border` / `border-input` | `border-light` or `border-medium` |
|
|
93
|
+
| `bg-muted` | `bg-weak` |
|
|
94
|
+
| `bg-destructive` | `bg-error-base` |
|
|
95
|
+
| `text-destructive` | `text-error-base` |
|
|
96
|
+
| `bg-accent` | `bg-primary-alpha-16` |
|
|
97
|
+
| `ring-ring` (focus) | `outline-primary-700` (primary) / `outline-strong` (neutral) |
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Semantic Spacing & Typography Tokens
|
|
102
|
+
|
|
103
|
+
Tailwind v4 also auto-generates spacing and typography utilities from `--spacing-*` and the typography tokens. These **mirror Figma's semantic variables**.
|
|
104
|
+
|
|
105
|
+
### Spacing
|
|
106
|
+
|
|
107
|
+
| Family | Utilities |
|
|
108
|
+
| --- | --- |
|
|
109
|
+
| component | `gap-component-none`, `gap-component-xs`, `gap-component-sm`, `p-component-md`, `gap-component-lg`, `p-component-xl`, `px-component-none` |
|
|
110
|
+
| layout | `gap-layout-xs`, `p-layout-sm`, `gap-layout-md`, `p-layout-lg`, `p-layout-xl` |
|
|
111
|
+
|
|
112
|
+
**Figma-token rule:** use a semantic spacing class **only when Figma references a semantic token** (e.g. `var(--component/sm,8px)` → `gap-component-sm`). When Figma shows a static value (`space-space-4`), use the standard Tailwind class (`gap-4`). Prefer `px-component-none` over `px-0` when it is an intentional "no padding" override of a token.
|
|
113
|
+
|
|
114
|
+
### Typography
|
|
115
|
+
|
|
116
|
+
`text-display-{lg,xl}`, `text-heading-h1` … `text-heading-h6`, `text-body-{xs,sm,md,lg,xl}`, `text-paragraph-{xs…xl}`, `text-ui-{xs,sm,md,lg,xl}`, `text-ui-overline-*`, `text-ui-caption-*`, `text-numeric-{xs…xl}`.
|
|
117
|
+
|
|
118
|
+
Spacing and typography tokens **auto-scale across breakpoints** — never add `md:` / `xl:` prefixes to `gap-component-sm`, `text-heading-h2`, and the like.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Theming
|
|
123
|
+
|
|
124
|
+
One system, many themes. A theme is a swappable **token set** (a `registry:theme` item carrying a `primary` 50…950 palette), layered on the single foundation — it is not a style/base split and not a preset color.
|
|
125
|
+
|
|
126
|
+
- **Primary themes (8):** `indigo` (default), `blue`, `lime`, `green`, `red`, `orange`, `yellow`, `cyan`
|
|
127
|
+
- **Neutral themes (5):** `gray` (default), `slate`, `zinc`, `base`, `stone`
|
|
128
|
+
- **Font variants:** `v1` (Geist + JetBrains Mono)
|
|
129
|
+
|
|
130
|
+
Pick a theme at init, interactively or via flags:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
npx @create-ui/cli init --theme blue --neutral slate --font-variant v1
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`init` applies the choice by adding the matching registry items (`theme-<primary>`, `neutral-<neutral>`, `font-variant-<v>`). There is **no `apply` command** and **no preset codes** — to change theme later, re-run `init` with new flags or edit the token variables in your global CSS.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Dark Mode
|
|
141
|
+
|
|
142
|
+
Toggle the `.dark` class on the root element; tokens swap automatically. In Next.js, use `next-themes`:
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
import { ThemeProvider } from "next-themes"
|
|
146
|
+
|
|
147
|
+
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
|
148
|
+
{children}
|
|
149
|
+
</ThemeProvider>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Because every color is a token that already has a `.dark` override, **never write manual `dark:` color overrides** on components — let the tokens do the work.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Adding a Custom Color / Changing Radius
|
|
157
|
+
|
|
158
|
+
Edit the project's global CSS file — the `tailwind.css` path reported by `createui info` (typically `app/globals.css`). **Never create a new CSS file** for this.
|
|
159
|
+
|
|
160
|
+
Define the HEX scale under `:root` (and `.dark`), then expose it to Tailwind in the same file:
|
|
161
|
+
|
|
162
|
+
```css
|
|
163
|
+
/* app/globals.css */
|
|
164
|
+
:root {
|
|
165
|
+
--color-brand-50: #eef6ff;
|
|
166
|
+
--color-brand-500: #2f7bff;
|
|
167
|
+
--color-brand-900: #14315f;
|
|
168
|
+
--radius-lg: 0.75rem; /* override radius */
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.dark {
|
|
172
|
+
--color-brand-500: #5a96ff;
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Tailwind v4 auto-generates the utilities (`bg-brand-500`, `text-brand-900`, …) from those `--color-*` variables:
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
<div className="bg-brand-50 text-brand-900">Brand surface</div>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
To restyle existing components, override the semantic tokens themselves (e.g. redefine the `primary` scale or a status family) rather than adding new ones. (There is no token build step to run — editing the CSS is the whole change.)
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Customizing Components
|
|
187
|
+
|
|
188
|
+
See also: [rules/styling.md](./rules/styling.md) for Incorrect/Correct examples.
|
|
189
|
+
|
|
190
|
+
Prefer these approaches, in order.
|
|
191
|
+
|
|
192
|
+
### 1. Built-in props
|
|
193
|
+
|
|
194
|
+
Reach for the component's own API first. For example, `Button` exposes `variant`, `appearance`, `size`, `shape`, `loading`, `iconOnly`, `leadingIcon`, `trailingIcon`, and `asChild`:
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
import { Button } from "@/components/ui/button"
|
|
198
|
+
|
|
199
|
+
<Button appearance="outline" size="sm">Save</Button>
|
|
200
|
+
<Button variant="danger">Delete</Button>
|
|
201
|
+
<Button loading>Saving…</Button>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### 2. Tailwind classes via `className`
|
|
205
|
+
|
|
206
|
+
Use `className` for layout and spacing, not for re-coloring the variant:
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
import { cn } from "@/lib/utils"
|
|
210
|
+
import { Card } from "@/components/ui/card"
|
|
211
|
+
|
|
212
|
+
<Card className={cn("mx-auto max-w-md")}>...</Card>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### 3. Add a CVA variant in the source
|
|
216
|
+
|
|
217
|
+
If you need a new look, add it to the component's `cva` config in the source, using real Create UI tokens:
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
// components/ui/button.tsx — add to the buttonVariants config
|
|
221
|
+
warning: "bg-warning-base text-static-white hover:bg-warning-strong",
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### 4. Wrapper components
|
|
225
|
+
|
|
226
|
+
Compose Create UI primitives into higher-level components. Verify each primitive's props (`Button` has `variant="danger"`, not `variant="destructive"`):
|
|
227
|
+
|
|
228
|
+
```tsx
|
|
229
|
+
import { Button } from "@/components/ui/button"
|
|
230
|
+
import {
|
|
231
|
+
Dialog,
|
|
232
|
+
DialogClose,
|
|
233
|
+
DialogContent,
|
|
234
|
+
DialogDescription,
|
|
235
|
+
DialogFooter,
|
|
236
|
+
DialogHeader,
|
|
237
|
+
DialogTitle,
|
|
238
|
+
DialogTrigger,
|
|
239
|
+
} from "@/components/ui/dialog"
|
|
240
|
+
|
|
241
|
+
export function ConfirmDialog({ title, description, onConfirm, children }) {
|
|
242
|
+
return (
|
|
243
|
+
<Dialog>
|
|
244
|
+
<DialogTrigger asChild>{children}</DialogTrigger>
|
|
245
|
+
<DialogContent>
|
|
246
|
+
<DialogHeader>
|
|
247
|
+
<DialogTitle>{title}</DialogTitle>
|
|
248
|
+
<DialogDescription>{description}</DialogDescription>
|
|
249
|
+
</DialogHeader>
|
|
250
|
+
<DialogFooter>
|
|
251
|
+
<DialogClose asChild>
|
|
252
|
+
<Button appearance="outline">Cancel</Button>
|
|
253
|
+
</DialogClose>
|
|
254
|
+
<Button variant="danger" onClick={onConfirm}>
|
|
255
|
+
Confirm
|
|
256
|
+
</Button>
|
|
257
|
+
</DialogFooter>
|
|
258
|
+
</DialogContent>
|
|
259
|
+
</Dialog>
|
|
260
|
+
)
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Checking for Updates
|
|
267
|
+
|
|
268
|
+
To see which installed components differ from the registry, use the `diff` command:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
# List components that have updates available.
|
|
272
|
+
npx @create-ui/cli diff
|
|
273
|
+
|
|
274
|
+
# Show what changed upstream for one component.
|
|
275
|
+
npx @create-ui/cli diff button
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
When you re-add a component, pass `--overwrite` to replace local files:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
npx @create-ui/cli add button --overwrite
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
See [Updating Components in SKILL.md](./SKILL.md#updating-components) for the full workflow.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "createui",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"prompt": "I'm building a Next.js app with Create UI (lucide icons). Create a settings form component with fields for: full name, email address, and notification preferences (email, SMS, push notifications as on/off toggles). Add validation states for required fields.",
|
|
7
|
+
"expected_output": "A React component using FieldGroup, Field, Switch, data-invalid/aria-invalid validation, gap-* spacing, and Create UI semantic tokens.",
|
|
8
|
+
"files": [],
|
|
9
|
+
"expectations": [
|
|
10
|
+
"Uses FieldGroup and Field components for form layout instead of a raw div with space-y",
|
|
11
|
+
"Uses Switch for independent on/off notification toggles (not looping Button with manual active state)",
|
|
12
|
+
"Uses data-invalid on Field and aria-invalid on the input control for validation states",
|
|
13
|
+
"Uses gap-* (e.g. gap-4, gap-6) instead of space-y-* or space-x-* for spacing",
|
|
14
|
+
"Uses Create UI semantic tokens (e.g. bg-static, text-body, text-placeholder, text-error-base) — never shadcn tokens like bg-background/text-muted-foreground or raw colors like bg-red-500",
|
|
15
|
+
"No manual dark: color overrides (tokens swap via the .dark class)"
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": 2,
|
|
20
|
+
"prompt": "Create a dialog component for editing a user profile. It should have the user's avatar at the top, input fields for name and bio, and Save/Cancel buttons with appropriate icons. Using Create UI with lucide icons.",
|
|
21
|
+
"expected_output": "A React component with DialogTitle, Avatar+AvatarFallback, Button with leadingIcon/trailingIcon props, no icon sizing classes, asChild for custom triggers.",
|
|
22
|
+
"files": [],
|
|
23
|
+
"expectations": [
|
|
24
|
+
"Includes DialogTitle for accessibility (visible or with an sr-only class)",
|
|
25
|
+
"Avatar component includes AvatarFallback",
|
|
26
|
+
"Icons on buttons are passed via the leadingIcon/trailingIcon props (never a data-icon attribute, which does not exist in Create UI)",
|
|
27
|
+
"No sizing classes on icons inside components (no size-4, w-4, h-4, etc.) — the component sizes icons via CVA",
|
|
28
|
+
"Uses the Button loading prop for the pending Save state instead of manually composing a Spinner",
|
|
29
|
+
"Uses asChild for custom triggers (Radix Slot)"
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": 3,
|
|
34
|
+
"prompt": "Create a dashboard component that shows 4 stat cards in a grid. Each card has a title, large number, a percentage-change badge, and a loading skeleton state. Using Create UI with lucide icons.",
|
|
35
|
+
"expected_output": "A React component with full Card composition, Skeleton for loading, a Badge/StatusBadge for the change, Create UI semantic tokens, and gap-* spacing.",
|
|
36
|
+
"files": [],
|
|
37
|
+
"expectations": [
|
|
38
|
+
"Uses full Card composition with CardHeader, CardTitle, CardContent (not dumping everything into CardContent)",
|
|
39
|
+
"Uses the Skeleton component for loading placeholders instead of custom animate-pulse divs",
|
|
40
|
+
"Uses a Badge or StatusBadge for the percentage change instead of custom styled spans",
|
|
41
|
+
"Uses Create UI semantic tokens (e.g. text-body, text-strongest, text-success-base) instead of shadcn tokens or raw colors like bg-green-500",
|
|
42
|
+
"Uses gap-* instead of space-y-* or space-x-* for spacing",
|
|
43
|
+
"Uses size-* when width and height are equal instead of separate w-* h-*"
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Create UI MCP Server
|
|
2
|
+
|
|
3
|
+
The Create UI MCP server lets AI assistants browse, search, and install components from registries through the Model Context Protocol.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- [Setup](#setup)
|
|
8
|
+
- [Available Tools](#available-tools)
|
|
9
|
+
- [Configuring Registries](#configuring-registries)
|
|
10
|
+
- [Usage Examples](#usage-examples)
|
|
11
|
+
|
|
12
|
+
## Setup
|
|
13
|
+
|
|
14
|
+
Start the MCP stdio server with:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx @create-ui/cli mcp
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
To write the configuration file for your MCP client, run:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx @create-ui/cli mcp init --client claude
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Supported clients and the config path each one writes:
|
|
27
|
+
|
|
28
|
+
| Client | Config path |
|
|
29
|
+
| --- | --- |
|
|
30
|
+
| `claude` | `.mcp.json` |
|
|
31
|
+
| `cursor` | `.cursor/mcp.json` |
|
|
32
|
+
| `vscode` | `.vscode/mcp.json` |
|
|
33
|
+
| `codex` | `.codex/config.toml` |
|
|
34
|
+
| `opencode` | `opencode.json` |
|
|
35
|
+
|
|
36
|
+
The generated config always runs `npx createui@latest mcp`. For example, the `claude` client writes:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"mcpServers": {
|
|
41
|
+
"createui": {
|
|
42
|
+
"command": "npx",
|
|
43
|
+
"args": ["createui@latest", "mcp"]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Available Tools
|
|
50
|
+
|
|
51
|
+
The MCP server exposes seven tools:
|
|
52
|
+
|
|
53
|
+
### get_project_registries
|
|
54
|
+
|
|
55
|
+
Returns the registries configured in the current project's `components.json`.
|
|
56
|
+
|
|
57
|
+
**Inputs:** none
|
|
58
|
+
|
|
59
|
+
### list_items_in_registries
|
|
60
|
+
|
|
61
|
+
Lists all items available in the specified registries.
|
|
62
|
+
|
|
63
|
+
**Inputs:**
|
|
64
|
+
- `registries` (string[]) — registry namespaces to query
|
|
65
|
+
- `limit` (number, optional)
|
|
66
|
+
- `offset` (number, optional)
|
|
67
|
+
|
|
68
|
+
### search_items_in_registries
|
|
69
|
+
|
|
70
|
+
Searches for items across registries by query string.
|
|
71
|
+
|
|
72
|
+
**Inputs:**
|
|
73
|
+
- `registries` (string[])
|
|
74
|
+
- `query` (string)
|
|
75
|
+
- `limit` (number, optional)
|
|
76
|
+
- `offset` (number, optional)
|
|
77
|
+
|
|
78
|
+
### view_items_in_registries
|
|
79
|
+
|
|
80
|
+
Returns detailed information about specific items.
|
|
81
|
+
|
|
82
|
+
**Inputs:**
|
|
83
|
+
- `items` (string[]) — e.g. `@createui/button`
|
|
84
|
+
|
|
85
|
+
### get_item_examples_from_registries
|
|
86
|
+
|
|
87
|
+
Finds example and demo code for items.
|
|
88
|
+
|
|
89
|
+
**Inputs:**
|
|
90
|
+
- `registries` (string[])
|
|
91
|
+
- `query` (string)
|
|
92
|
+
|
|
93
|
+
### get_add_command_for_items
|
|
94
|
+
|
|
95
|
+
Returns the `add` command for the specified items.
|
|
96
|
+
|
|
97
|
+
**Inputs:**
|
|
98
|
+
- `items` (string[])
|
|
99
|
+
|
|
100
|
+
### get_audit_checklist
|
|
101
|
+
|
|
102
|
+
Returns a checklist to verify after adding components.
|
|
103
|
+
|
|
104
|
+
**Inputs:** none
|
|
105
|
+
|
|
106
|
+
> **Tip:** The MCP server has no equivalent for inspecting project configuration. To print the resolved aliases, framework, and Tailwind setup, run the `info` command directly: `npx @create-ui/cli info`.
|
|
107
|
+
|
|
108
|
+
## Configuring Registries
|
|
109
|
+
|
|
110
|
+
Configure additional registries under the `registries` field in `components.json`:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"registries": {
|
|
115
|
+
"@acme": "https://acme.com/r/{name}.json",
|
|
116
|
+
"@private": {
|
|
117
|
+
"url": "https://api.company.com/r/{name}.json",
|
|
118
|
+
"headers": {
|
|
119
|
+
"Authorization": "Bearer ${MY_TOKEN}"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
- Registry namespaces must start with `@`.
|
|
127
|
+
- A registry can be a plain URL string (`@acme`) or an object with `url`, `headers`, and `params` (`@private`).
|
|
128
|
+
- URL templates contain a `{name}` placeholder that is replaced with the item name.
|
|
129
|
+
- `${VAR}` expands from environment variables, which is useful for tokens and secrets.
|
|
130
|
+
|
|
131
|
+
The `@createui` registry is always built-in and available without any configuration.
|
|
132
|
+
|
|
133
|
+
## Usage Examples
|
|
134
|
+
|
|
135
|
+
Browse the Create UI registry:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
Show me all available components in @createui
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Search for a component:
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
Search for a date picker in @createui
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Install a component:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
Add the button and card components from @createui
|
|
151
|
+
```
|