@create-ui/cli 0.5.4 → 0.5.5
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/dist/{chunk-EWAP55CF.js → chunk-5YX4Z2U3.js} +3 -3
- package/dist/{chunk-EWAP55CF.js.map → chunk-5YX4Z2U3.js.map} +1 -1
- package/dist/{chunk-UVIUVCLG.js → chunk-EF7MDUJN.js} +3 -3
- package/dist/{chunk-UVIUVCLG.js.map → chunk-EF7MDUJN.js.map} +1 -1
- package/dist/{chunk-MK3CCMH4.js → chunk-Z73MEB7I.js} +3 -3
- package/dist/{chunk-MK3CCMH4.js.map → chunk-Z73MEB7I.js.map} +1 -1
- package/dist/icons/index.d.ts +3 -3
- package/dist/icons/index.js +1 -1
- package/dist/icons/index.js.map +1 -1
- package/dist/index.d.ts +360 -360
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/registry/index.d.ts +2 -2
- package/dist/registry/index.js +1 -1
- package/dist/schema/index.d.ts +715 -715
- package/dist/skills/createui/SKILL.md +54 -53
- package/dist/skills/createui/agents/openai.yml +1 -1
- package/dist/skills/createui/cli.md +25 -93
- package/dist/skills/createui/customization.md +48 -31
- package/dist/skills/createui/evals/evals.json +12 -11
- package/dist/skills/createui/mcp.md +6 -32
- package/dist/skills/createui/rules/composition.md +112 -101
- package/dist/skills/createui/rules/forms.md +31 -24
- package/dist/skills/createui/rules/icons.md +41 -25
- package/dist/skills/createui/rules/styling.md +5 -9
- package/dist/utils/index.js +1 -1
- package/package.json +1 -1
- package/dist/skills/createui/contributing.md +0 -213
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
# Create UI MCP Server
|
|
2
2
|
|
|
3
|
-
The Create UI MCP server lets AI assistants browse, search, and install components from
|
|
3
|
+
The Create UI MCP server lets AI assistants browse, search, and install components from the `@createui` registry through the Model Context Protocol.
|
|
4
4
|
|
|
5
5
|
## Contents
|
|
6
6
|
|
|
7
7
|
- [Setup](#setup)
|
|
8
8
|
- [Available Tools](#available-tools)
|
|
9
|
-
- [Configuring Registries](#configuring-registries)
|
|
10
9
|
- [Usage Examples](#usage-examples)
|
|
11
10
|
|
|
12
11
|
## Setup
|
|
@@ -33,14 +32,14 @@ Supported clients and the config path each one writes:
|
|
|
33
32
|
| `codex` | `.codex/config.toml` |
|
|
34
33
|
| `opencode` | `opencode.json` |
|
|
35
34
|
|
|
36
|
-
The generated config always runs `npx
|
|
35
|
+
The generated config always runs `npx @create-ui/cli mcp` (the package is `@create-ui/cli`; there is no bare `createui` npm package). For example, the `claude` client writes:
|
|
37
36
|
|
|
38
37
|
```json
|
|
39
38
|
{
|
|
40
39
|
"mcpServers": {
|
|
41
40
|
"createui": {
|
|
42
41
|
"command": "npx",
|
|
43
|
-
"args": ["
|
|
42
|
+
"args": ["@create-ui/cli", "mcp"]
|
|
44
43
|
}
|
|
45
44
|
}
|
|
46
45
|
}
|
|
@@ -48,7 +47,7 @@ The generated config always runs `npx createui@latest mcp`. For example, the `cl
|
|
|
48
47
|
|
|
49
48
|
## Available Tools
|
|
50
49
|
|
|
51
|
-
The MCP server exposes seven tools
|
|
50
|
+
The MCP server exposes seven tools. **`@createui` is the only registry** — it is built in and needs no configuration, and there are no other registries to add. Wherever a tool takes a `registries` array, pass `["@createui"]`.
|
|
52
51
|
|
|
53
52
|
### get_project_registries
|
|
54
53
|
|
|
@@ -80,7 +79,7 @@ Searches for items across registries by query string.
|
|
|
80
79
|
Returns detailed information about specific items.
|
|
81
80
|
|
|
82
81
|
**Inputs:**
|
|
83
|
-
- `items` (string[]) — e.g. `@createui
|
|
82
|
+
- `items` (string[]) — bare item names, e.g. `button` (the MCP server also accepts a `@createui/` prefix and strips it)
|
|
84
83
|
|
|
85
84
|
### get_item_examples_from_registries
|
|
86
85
|
|
|
@@ -105,31 +104,6 @@ Returns a checklist to verify after adding components.
|
|
|
105
104
|
|
|
106
105
|
> **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
106
|
|
|
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
107
|
## Usage Examples
|
|
134
108
|
|
|
135
109
|
Browse the Create UI registry:
|
|
@@ -147,5 +121,5 @@ Search for a date picker in @createui
|
|
|
147
121
|
Install a component:
|
|
148
122
|
|
|
149
123
|
```
|
|
150
|
-
Add the button and
|
|
124
|
+
Add the button and select components from @createui
|
|
151
125
|
```
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
# Component Composition
|
|
2
2
|
|
|
3
|
-
How Create UI components fit together. Compose primitives — never reroll a `<select>`, a custom callout, or a hand-styled loading button when a component already exists. Every component name below is in the
|
|
3
|
+
How Create UI components fit together. Compose primitives — never reroll a `<select>`, a custom callout, or a hand-styled loading button when a component already exists. Every component name below is in the `@createui` registry; add any of them with `npx @create-ui/cli add <name>`.
|
|
4
4
|
|
|
5
5
|
## Contents
|
|
6
6
|
|
|
7
7
|
- Items always inside their Group component
|
|
8
|
-
- Callouts use
|
|
9
|
-
-
|
|
10
|
-
- Toast notifications use sonner
|
|
8
|
+
- Callouts use InlineAlert
|
|
9
|
+
- Toasts use the Toast component
|
|
11
10
|
- Choosing between overlay components
|
|
12
|
-
- Dialog, Sheet, and Drawer always need a Title
|
|
13
|
-
- Card structure
|
|
14
11
|
- Button has a `loading` prop — never hand-build a spinner button
|
|
15
|
-
-
|
|
12
|
+
- Tabbed navigation uses TabMenu
|
|
16
13
|
- Avatar always needs AvatarFallback
|
|
17
14
|
- Use existing components instead of custom markup
|
|
18
15
|
|
|
@@ -49,15 +46,13 @@ This applies to every group-based component:
|
|
|
49
46
|
|------|-------|
|
|
50
47
|
| `SelectItem`, `SelectLabel` | `SelectGroup` |
|
|
51
48
|
| `DropdownMenuItem`, `DropdownMenuLabel` | `DropdownMenuGroup` |
|
|
52
|
-
| `MenubarItem`, `MenubarLabel` | `MenubarGroup` |
|
|
53
|
-
| `ContextMenuItem`, `ContextMenuLabel` | `ContextMenuGroup` |
|
|
54
49
|
| `CommandItem` | `CommandGroup` |
|
|
55
50
|
|
|
56
51
|
---
|
|
57
52
|
|
|
58
|
-
## Callouts use
|
|
53
|
+
## Callouts use InlineAlert
|
|
59
54
|
|
|
60
|
-
Use `
|
|
55
|
+
Use `InlineAlert` for callouts. Don't hand-roll a styled `<div>` — and don't look for a shadcn-style generic alert (or a page-banner) component; `InlineAlert` is the callout primitive.
|
|
61
56
|
|
|
62
57
|
**Incorrect:**
|
|
63
58
|
|
|
@@ -71,108 +66,118 @@ Use `Alert` for inline callouts. Set the tone with the `variant` prop (`default`
|
|
|
71
66
|
**Correct:**
|
|
72
67
|
|
|
73
68
|
```tsx
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
69
|
+
import { RiErrorWarningFill } from "@create-ui/assets/icons"
|
|
70
|
+
import { Button } from "@/components/ui/button"
|
|
71
|
+
import {
|
|
72
|
+
InlineAlert,
|
|
73
|
+
InlineAlertActions,
|
|
74
|
+
InlineAlertContent,
|
|
75
|
+
InlineAlertDescription,
|
|
76
|
+
InlineAlertHeading,
|
|
77
|
+
InlineAlertIcon,
|
|
78
|
+
InlineAlertTitle,
|
|
79
|
+
} from "@/components/ui/inline-alert"
|
|
80
|
+
|
|
81
|
+
<InlineAlert variant="danger">
|
|
82
|
+
<InlineAlertIcon>
|
|
83
|
+
<RiErrorWarningFill />
|
|
84
|
+
</InlineAlertIcon>
|
|
85
|
+
<InlineAlertContent>
|
|
86
|
+
<InlineAlertHeading>
|
|
87
|
+
<InlineAlertTitle>Payment failed</InlineAlertTitle>
|
|
88
|
+
<InlineAlertDescription>Update your card to continue.</InlineAlertDescription>
|
|
89
|
+
</InlineAlertHeading>
|
|
90
|
+
<InlineAlertActions>
|
|
91
|
+
<Button variant="danger" size="md">Update card</Button>
|
|
92
|
+
</InlineAlertActions>
|
|
93
|
+
</InlineAlertContent>
|
|
94
|
+
</InlineAlert>
|
|
81
95
|
```
|
|
82
96
|
|
|
83
|
-
|
|
97
|
+
`InlineAlert` takes `variant` (`primary` | `neutral` | `danger` | `success` | `warning` | `info` | `away`) and `appearance` (`default` | `solid` | `soft` | `outline`). For a dismissible callout, add `<InlineAlertClose />` as a direct child and handle `onDismiss` on the root. For a full-width page banner, place an `InlineAlert` in a full-width container — there is no separate banner component.
|
|
84
98
|
|
|
85
99
|
---
|
|
86
100
|
|
|
87
|
-
##
|
|
101
|
+
## Toasts use the Toast component
|
|
88
102
|
|
|
89
|
-
|
|
103
|
+
Toasts are the registry's own `toast` component — **not `sonner`**. There is no `toast()` function to import; compose the `Toast` parts and render it from your notification state.
|
|
90
104
|
|
|
91
|
-
|
|
92
|
-
import { RiFolderLine } from "lucide-react" // or your project's iconLibrary
|
|
93
|
-
|
|
94
|
-
<Empty>
|
|
95
|
-
<EmptyHeader>
|
|
96
|
-
<EmptyMedia variant="icon">
|
|
97
|
-
<RiFolderLine />
|
|
98
|
-
</EmptyMedia>
|
|
99
|
-
<EmptyTitle>No projects yet</EmptyTitle>
|
|
100
|
-
<EmptyDescription>Get started by creating a new project.</EmptyDescription>
|
|
101
|
-
</EmptyHeader>
|
|
102
|
-
<EmptyContent>
|
|
103
|
-
<Button>Create Project</Button>
|
|
104
|
-
</EmptyContent>
|
|
105
|
-
</Empty>
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
---
|
|
109
|
-
|
|
110
|
-
## Toast notifications use sonner
|
|
111
|
-
|
|
112
|
-
Toasts come from `sonner`. Import the `toast` function directly — don't build a custom toast component.
|
|
105
|
+
**Incorrect:**
|
|
113
106
|
|
|
114
107
|
```tsx
|
|
115
108
|
import { toast } from "sonner"
|
|
116
109
|
|
|
117
|
-
toast.success("
|
|
118
|
-
toast.error("Something went wrong.")
|
|
119
|
-
toast("File deleted.", {
|
|
120
|
-
action: { label: "Undo", onClick: () => undoDelete() },
|
|
121
|
-
})
|
|
110
|
+
toast.success("Draft saved.")
|
|
122
111
|
```
|
|
123
112
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
## Choosing between overlay components
|
|
127
|
-
|
|
128
|
-
Pick the overlay that matches the interaction — they all exist in the registry.
|
|
129
|
-
|
|
130
|
-
| Use case | Component |
|
|
131
|
-
|----------|-----------|
|
|
132
|
-
| Focused task that requires input | `Dialog` |
|
|
133
|
-
| Destructive action confirmation | `AlertDialog` |
|
|
134
|
-
| Side panel with details or filters | `Sheet` |
|
|
135
|
-
| Mobile-first bottom panel | `Drawer` |
|
|
136
|
-
| Quick info on hover | `HoverCard` |
|
|
137
|
-
| Small contextual content on click | `Popover` |
|
|
113
|
+
**Correct:**
|
|
138
114
|
|
|
139
|
-
|
|
115
|
+
```tsx
|
|
116
|
+
import { RiCheckboxCircleFill } from "@create-ui/assets/icons"
|
|
117
|
+
import {
|
|
118
|
+
Toast,
|
|
119
|
+
ToastAction,
|
|
120
|
+
ToastBody,
|
|
121
|
+
ToastContent,
|
|
122
|
+
ToastDescription,
|
|
123
|
+
ToastIcon,
|
|
124
|
+
ToastTitle,
|
|
125
|
+
} from "@/components/ui/toast"
|
|
126
|
+
|
|
127
|
+
<Toast variant="success" appearance="solid">
|
|
128
|
+
<ToastBody>
|
|
129
|
+
<ToastIcon>
|
|
130
|
+
<RiCheckboxCircleFill />
|
|
131
|
+
</ToastIcon>
|
|
132
|
+
<ToastContent>
|
|
133
|
+
<ToastTitle>Draft saved</ToastTitle>
|
|
134
|
+
<ToastDescription>Your changes are synced to the cloud.</ToastDescription>
|
|
135
|
+
</ToastContent>
|
|
136
|
+
</ToastBody>
|
|
137
|
+
<ToastAction>Undo</ToastAction>
|
|
138
|
+
</Toast>
|
|
139
|
+
```
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
`Toast` takes `variant` (`primary` | `neutral` | `danger` | `success` | `warning` | `info` | `away`) and `appearance` (`solid` | `soft` | `outline` | `default`), plus an `onDismiss` callback. Add `<ToastClose />` for an explicit close affordance and `<ToastProgress />` for an auto-dismiss countdown bar.
|
|
142
142
|
|
|
143
|
-
|
|
143
|
+
There is no provider, queue, or stacking system — you own the notification state and the placement. Render active toasts from your state into a fixed container:
|
|
144
144
|
|
|
145
145
|
```tsx
|
|
146
|
-
<
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
146
|
+
const [toasts, setToasts] = React.useState<AppToast[]>([])
|
|
147
|
+
|
|
148
|
+
<div className="fixed right-4 bottom-4 flex flex-col gap-2">
|
|
149
|
+
{toasts.map((t) => (
|
|
150
|
+
<Toast
|
|
151
|
+
key={t.id}
|
|
152
|
+
variant={t.variant}
|
|
153
|
+
appearance="solid"
|
|
154
|
+
onDismiss={() => setToasts((all) => all.filter((x) => x.id !== t.id))}
|
|
155
|
+
>
|
|
156
|
+
<ToastBody>
|
|
157
|
+
<ToastContent>
|
|
158
|
+
<ToastTitle>{t.title}</ToastTitle>
|
|
159
|
+
</ToastContent>
|
|
160
|
+
</ToastBody>
|
|
161
|
+
<ToastClose />
|
|
162
|
+
</Toast>
|
|
163
|
+
))}
|
|
164
|
+
</div>
|
|
153
165
|
```
|
|
154
166
|
|
|
155
167
|
---
|
|
156
168
|
|
|
157
|
-
##
|
|
169
|
+
## Choosing between overlay components
|
|
158
170
|
|
|
159
|
-
|
|
171
|
+
Pick the overlay that matches the interaction — these are the overlays that exist.
|
|
160
172
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
</CardHeader>
|
|
170
|
-
<CardContent>...</CardContent>
|
|
171
|
-
<CardFooter>
|
|
172
|
-
<Button>Invite</Button>
|
|
173
|
-
</CardFooter>
|
|
174
|
-
</Card>
|
|
175
|
-
```
|
|
173
|
+
| Use case | Component |
|
|
174
|
+
|----------|-----------|
|
|
175
|
+
| Short hint on hover | `Tooltip` |
|
|
176
|
+
| "What is this?" helper next to a label | `InfoTooltip` |
|
|
177
|
+
| Action menu on a trigger | `DropdownMenu` |
|
|
178
|
+
| Command palette / quick switcher | `Command` (inline) / `CommandDialog` (modal) |
|
|
179
|
+
|
|
180
|
+
There is **no dialog, popover, sheet, drawer, alert-dialog, or hover-card component**. The only modal surface is `CommandDialog` (shipped with `command`). For other modal or contextual-panel needs, don't invent a lookalike from raw markup — surface the flow inline (e.g. an expanding section, a dedicated route, or an `InlineAlert` confirmation) or ask the user before hand-rolling an overlay.
|
|
176
181
|
|
|
177
182
|
---
|
|
178
183
|
|
|
@@ -198,7 +203,7 @@ Compose `Card` from its parts — don't dump everything into `CardContent`. `Car
|
|
|
198
203
|
For icons, use the `leadingIcon` / `trailingIcon` props (or `iconOnly` for an icon-only button) — never wrap raw `<svg>` children or add sizing classes; the component sizes the icon per `size`.
|
|
199
204
|
|
|
200
205
|
```tsx
|
|
201
|
-
import { RiSearchLine } from "
|
|
206
|
+
import { RiSearchLine } from "@create-ui/assets/icons"
|
|
202
207
|
|
|
203
208
|
<Button leadingIcon={<RiSearchLine />}>Search</Button>
|
|
204
209
|
<Button iconOnly aria-label="Search" leadingIcon={<RiSearchLine />} />
|
|
@@ -208,20 +213,25 @@ Remember the Button API: `variant` is `primary | neutral-solid | neutral-light |
|
|
|
208
213
|
|
|
209
214
|
---
|
|
210
215
|
|
|
211
|
-
##
|
|
216
|
+
## Tabbed navigation uses TabMenu
|
|
212
217
|
|
|
213
|
-
|
|
218
|
+
Tabs are the `tab-menu` component — there is no shadcn-style tabs / tabs-list / tabs-trigger set. `TabMenu` wraps `TabMenuItem`s and owns the selection (`defaultValue`, or controlled `value` / `onValueChange`). It renders the menu only — render the active panel yourself from the value; there is no content component.
|
|
214
219
|
|
|
215
220
|
```tsx
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
<
|
|
222
|
-
|
|
221
|
+
import { TabMenu, TabMenuItem } from "@/components/ui/tab-menu"
|
|
222
|
+
|
|
223
|
+
const [tab, setTab] = React.useState("overview")
|
|
224
|
+
|
|
225
|
+
<TabMenu variant="horizontal-line" indicator="bottom" value={tab} onValueChange={setTab}>
|
|
226
|
+
<TabMenuItem value="overview" label="Overview" />
|
|
227
|
+
<TabMenuItem value="billing" label="Billing" />
|
|
228
|
+
<TabMenuItem value="settings" label="Settings" />
|
|
229
|
+
</TabMenu>
|
|
230
|
+
{tab === "overview" && <OverviewPanel />}
|
|
223
231
|
```
|
|
224
232
|
|
|
233
|
+
`variant` is `vertical-button` (default) | `vertical-line` | `horizontal-line` | `horizontal-button`; `size` is `sm` | `md` | `lg`. `TabMenuItem` takes `label`, `leadingIcon` / `trailingIcon`, `disabled`, and `asChild` for link tabs.
|
|
234
|
+
|
|
225
235
|
---
|
|
226
236
|
|
|
227
237
|
## Avatar always needs AvatarFallback
|
|
@@ -244,6 +254,7 @@ If a primitive already covers the job, use it — don't reach for raw elements o
|
|
|
244
254
|
| Instead of | Use |
|
|
245
255
|
|---|---|
|
|
246
256
|
| `<hr>` or `<div className="border-t">` | `<Separator />` |
|
|
247
|
-
| `<div className="animate-pulse">` with styled divs | `<Skeleton className="h-4 w-3/4" />` |
|
|
248
257
|
| `<span className="rounded-full bg-green-100 …">` | `<Badge variant="success">Active</Badge>` |
|
|
249
|
-
| A status dot built from a styled `<span>` | `<StatusBadge variant="success"
|
|
258
|
+
| A status dot built from a styled `<span>` | `<StatusBadge variant="success" />` (it renders the dot only — put the label next to it). `variant`: `primary`, `danger`, `success`, `warning`, `info`, `highlighted`, `away`, `verified`, `cyan`, `lime`, `neutral`, `white` — note `danger`, not `error` |
|
|
259
|
+
| A removable tag built from `<span>` + `<button>` | `<Chip onClose={…}>…</Chip>` |
|
|
260
|
+
| A hand-rolled `animate-spin` loading indicator | `<Spinner />` |
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
- Choosing the right form control
|
|
7
7
|
- InputGroup requires InputGroupControl/InputGroupTextarea
|
|
8
8
|
- Buttons inside inputs use InputGroup + InputGroupButton
|
|
9
|
-
- Option sets (2–7 choices) use
|
|
9
|
+
- Option sets (2–7 choices) use SegmentedControl
|
|
10
10
|
- Dropdowns use Select
|
|
11
11
|
- FieldSet + FieldLegend for grouping related fields
|
|
12
12
|
- Field validation and disabled states
|
|
@@ -59,6 +59,8 @@ import { Input } from "@/components/ui/input"
|
|
|
59
59
|
|
|
60
60
|
Use `FieldLabel className="sr-only"` for a visually hidden but accessible label.
|
|
61
61
|
|
|
62
|
+
Beyond the parts above, `field.tsx` also ships `FieldTitle` (a label for non-labelable controls, connect via `aria-labelledby`), `FieldContent` (wraps the control + description in horizontal layouts), `FieldFooter` (a footer row, e.g. helper + counter), `FieldHelper` (small helper text), and `FieldSeparator` (a divider between fields).
|
|
63
|
+
|
|
62
64
|
---
|
|
63
65
|
|
|
64
66
|
## Choosing the right form control
|
|
@@ -70,14 +72,17 @@ Every control below exists in the registry. Pick by intent:
|
|
|
70
72
|
| Single-line text | `Input` |
|
|
71
73
|
| Multi-line text | `Textarea` |
|
|
72
74
|
| Dropdown of predefined options | `Select` |
|
|
73
|
-
| Searchable dropdown | `Combobox` |
|
|
74
|
-
| Native HTML select (no JS) | `native-select` |
|
|
75
75
|
| Boolean in a settings row | `Switch` |
|
|
76
76
|
| Boolean in a form | `Checkbox` |
|
|
77
77
|
| One choice from a few options | `RadioGroup` |
|
|
78
|
-
| One choice across 2–7 visible options | `
|
|
78
|
+
| One choice across 2–7 visible options | `SegmentedControl` |
|
|
79
|
+
| Several related on/off options | `CheckboxGroup` / `SwitchGroup` |
|
|
79
80
|
| Verification / OTP code | `InputOTP` |
|
|
80
|
-
| Numeric value with step controls | `
|
|
81
|
+
| Numeric value with step controls | `InputStepper` |
|
|
82
|
+
| Date | `DateInput` |
|
|
83
|
+
| Phone number | `PhoneInput` |
|
|
84
|
+
| Card details | `CreditCardInput` |
|
|
85
|
+
| Password with a strength meter | `PasswordStrength` |
|
|
81
86
|
|
|
82
87
|
---
|
|
83
88
|
|
|
@@ -134,7 +139,7 @@ Never absolutely-position a `Button` over an `Input`. Compose `InputGroup` + `In
|
|
|
134
139
|
|
|
135
140
|
```tsx
|
|
136
141
|
import { InputGroup, InputGroupControl, InputGroupButton } from "@/components/ui/input-group"
|
|
137
|
-
import { RiSearchLine } from "
|
|
142
|
+
import { RiSearchLine } from "@create-ui/assets/icons"
|
|
138
143
|
|
|
139
144
|
<InputGroup>
|
|
140
145
|
<InputGroupControl placeholder="Search…" />
|
|
@@ -156,9 +161,9 @@ import { InputGroup, InputGroupControl, InputGroupAddon } from "@/components/ui/
|
|
|
156
161
|
|
|
157
162
|
---
|
|
158
163
|
|
|
159
|
-
## Option sets (2–7 choices) use
|
|
164
|
+
## Option sets (2–7 choices) use SegmentedControl
|
|
160
165
|
|
|
161
|
-
For a small set of mutually-exclusive
|
|
166
|
+
For a small set of mutually-exclusive choices, use `SegmentedControl` + `SegmentedControlItem`. Don't hand-roll a row of `Button`s with manual active state.
|
|
162
167
|
|
|
163
168
|
**Incorrect:**
|
|
164
169
|
|
|
@@ -181,28 +186,30 @@ const [selected, setSelected] = useState("daily")
|
|
|
181
186
|
**Correct:**
|
|
182
187
|
|
|
183
188
|
```tsx
|
|
184
|
-
import {
|
|
189
|
+
import { SegmentedControl, SegmentedControlItem } from "@/components/ui/segmented-control"
|
|
185
190
|
|
|
186
|
-
<
|
|
187
|
-
<
|
|
188
|
-
<
|
|
189
|
-
<
|
|
190
|
-
</
|
|
191
|
+
<SegmentedControl defaultValue="daily">
|
|
192
|
+
<SegmentedControlItem value="daily">Daily</SegmentedControlItem>
|
|
193
|
+
<SegmentedControlItem value="weekly">Weekly</SegmentedControlItem>
|
|
194
|
+
<SegmentedControlItem value="monthly">Monthly</SegmentedControlItem>
|
|
195
|
+
</SegmentedControl>
|
|
191
196
|
```
|
|
192
197
|
|
|
193
|
-
|
|
198
|
+
`SegmentedControl` is single-select: the value props are `value` / `defaultValue` / `onValueChange` (a string — there is no `type="multiple"`). Style it with `variant` (`primary` | `neutral`) and `appearance` (`flat` | `grouped`); items take `leadingIcon`. When more than one option can be active at once, that's not a segmented control — use `CheckboxGroup` (or `SwitchGroup`) instead.
|
|
199
|
+
|
|
200
|
+
Wrap a labelled segmented control in a `Field` and connect them with `aria-labelledby`:
|
|
194
201
|
|
|
195
202
|
```tsx
|
|
196
203
|
import { Field, FieldTitle } from "@/components/ui/field"
|
|
197
|
-
import {
|
|
204
|
+
import { SegmentedControl, SegmentedControlItem } from "@/components/ui/segmented-control"
|
|
198
205
|
|
|
199
206
|
<Field orientation="horizontal">
|
|
200
207
|
<FieldTitle id="theme-label">Theme</FieldTitle>
|
|
201
|
-
<
|
|
202
|
-
<
|
|
203
|
-
<
|
|
204
|
-
<
|
|
205
|
-
</
|
|
208
|
+
<SegmentedControl defaultValue="system" aria-labelledby="theme-label">
|
|
209
|
+
<SegmentedControlItem value="light">Light</SegmentedControlItem>
|
|
210
|
+
<SegmentedControlItem value="dark">Dark</SegmentedControlItem>
|
|
211
|
+
<SegmentedControlItem value="system">System</SegmentedControlItem>
|
|
212
|
+
</SegmentedControl>
|
|
206
213
|
</Field>
|
|
207
214
|
```
|
|
208
215
|
|
|
@@ -237,7 +244,7 @@ import {
|
|
|
237
244
|
</Field>
|
|
238
245
|
```
|
|
239
246
|
|
|
240
|
-
`Select` accepts `size` (`"xs" | "sm" | "md"`), `invalid`, `disabled`, and `loading`. When it's not inside a `Field`, set these on the `Select` itself.
|
|
247
|
+
`Select` accepts `size` (`"xs" | "sm" | "md"`), `invalid`, `disabled`, and `loading`. When it's not inside a `Field`, set these on the `Select` itself.
|
|
241
248
|
|
|
242
249
|
---
|
|
243
250
|
|
|
@@ -272,7 +279,7 @@ import { Checkbox } from "@/components/ui/checkbox"
|
|
|
272
279
|
</FieldSet>
|
|
273
280
|
```
|
|
274
281
|
|
|
275
|
-
For a single-choice group, swap the checkboxes for a `RadioGroup` inside the same `FieldSet`.
|
|
282
|
+
For a single-choice group, swap the checkboxes for a `RadioGroup` inside the same `FieldSet`. When the group is one logical control, reach for the grouped-control primitives directly: `CheckboxGroup`, `SwitchGroup`, and `RadioGroup`.
|
|
276
283
|
|
|
277
284
|
---
|
|
278
285
|
|
|
@@ -298,4 +305,4 @@ import { Input } from "@/components/ui/input"
|
|
|
298
305
|
</Field>
|
|
299
306
|
```
|
|
300
307
|
|
|
301
|
-
This pattern works for every control: `Input`, `Textarea`, `Select`, `Checkbox`, `RadioGroup`, `Switch`,
|
|
308
|
+
This pattern works for every control: `Input`, `Textarea`, `Select`, `Checkbox`, `RadioGroup`, `Switch`, and `InputOTP`.
|