cooterlabs 1.0.2 → 2.0.0
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/README.md +55 -11
- package/bin/index.js +327 -22
- package/package.json +12 -4
- package/templates/action-sheet/action-sheet.tsx +113 -0
- package/templates/alert/alert.tsx +96 -0
- package/templates/avatar/avatar.tsx +21 -4
- package/templates/badge/badge.tsx +106 -15
- package/templates/breadcrumb/breadcrumb.tsx +113 -0
- package/templates/button/button.tsx +40 -2
- package/templates/button-group/button-group.tsx +93 -0
- package/templates/card/card.tsx +31 -19
- package/templates/checkbox/checkbox.tsx +4 -3
- package/templates/context-menu/context-menu.tsx +177 -0
- package/templates/dialog/dialog.tsx +127 -0
- package/templates/dropdown/dropdown.tsx +180 -0
- package/templates/input/input.tsx +30 -3
- package/templates/label/label.tsx +2 -2
- package/templates/list/list.tsx +96 -0
- package/templates/loader/loader.tsx +38 -0
- package/templates/navbar-bottom/navbar-bottom.tsx +76 -0
- package/templates/navbar-top/navbar-top.tsx +84 -0
- package/templates/page-control/page-control.tsx +55 -0
- package/templates/pagination/pagination.tsx +109 -0
- package/templates/progress/progress.tsx +69 -0
- package/templates/radio/radio.tsx +41 -0
- package/templates/stepper/stepper.tsx +93 -0
- package/templates/tabs/tabs.tsx +57 -0
- package/templates/textarea/textarea.tsx +1 -1
- package/templates/toggle/toggle.tsx +27 -0
- package/templates/tooltip/tooltip.tsx +37 -0
package/README.md
CHANGED
|
@@ -26,7 +26,23 @@ To swiftly set up a cohesive Tailwind theme and properly configure your base CSS
|
|
|
26
26
|
npx cooterlabs init
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
This will give you a list of carefully curated designer themes
|
|
29
|
+
This will give you a list of carefully curated designer themes to apply global default variables (background, foreground, primary borders, etc.) out-of-the-box.
|
|
30
|
+
|
|
31
|
+
The default theme is **`aeon`** — the complete Aeon Product Design System as a Tailwind CSS v4 `@theme` block. It ships:
|
|
32
|
+
|
|
33
|
+
- **Semantic colors**: `primary` (deep teal `#0d3435`), `secondary`, `muted`, `accent`, `destructive`, `border`, `ring`, plus `success` / `info` / `warning` / `error` — each with a full 50–900 primitive scale (`primary-50`…`primary-900`, `grey-*`, `success-*`, …).
|
|
34
|
+
- **Text sizes as Tailwind CSS variables**: the full Aeon type ramp is exposed as `--text-*` theme variables, giving you utilities like `text-h1`…`text-h5` (headings), `text-s1`/`text-s2` (subtitles), `text-b1`…`text-b4` (body), `text-c1`…`text-c3` (captions), `text-label`, and button sizes `text-btn-giant`…`text-btn-tiny` — each with the correct size, line-height, and weight baked in.
|
|
35
|
+
- **Radius scale**: `rounded-xxs` (4px) through `rounded-xl` (24px).
|
|
36
|
+
- **Elevation**: `shadow-100` through `shadow-800`.
|
|
37
|
+
- **Font**: Inter.
|
|
38
|
+
|
|
39
|
+
To apply it non-interactively:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx cooterlabs init aeon
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Legacy `zinc` and `slate` themes (Tailwind v3 / shadcn HSL format) remain available.
|
|
30
46
|
|
|
31
47
|
### Adding Components
|
|
32
48
|
|
|
@@ -56,16 +72,44 @@ npx cooterlabs add --all
|
|
|
56
72
|
|
|
57
73
|
## Available Components
|
|
58
74
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
- `
|
|
63
|
-
- `button` -
|
|
64
|
-
- `
|
|
65
|
-
- `
|
|
66
|
-
- `
|
|
67
|
-
- `
|
|
68
|
-
- `
|
|
75
|
+
The full Aeon Product Design System, scaffolded as owned code:
|
|
76
|
+
|
|
77
|
+
**Form controls**
|
|
78
|
+
- `button` - Filled / outline / clear styles in five sizes (giant → tiny), icon-only mode, Radix `Slot` support.
|
|
79
|
+
- `button-group` - Segmented control matching the button size ramp.
|
|
80
|
+
- `input` - Aeon-styled HTML `<input />` with error and disabled states.
|
|
81
|
+
- `textarea` - Matching multiline input.
|
|
82
|
+
- `label` - Accessible Radix-backed `<label />`.
|
|
83
|
+
- `checkbox` - Radix checkbox with indeterminate support.
|
|
84
|
+
- `radio` - Radix radio group.
|
|
85
|
+
- `toggle` - Radix switch.
|
|
86
|
+
|
|
87
|
+
**Feedback**
|
|
88
|
+
- `alert` - Filled and soft appearances across default / success / info / warning / error severities.
|
|
89
|
+
- `badge` - Status pills plus dismissible `Chip`.
|
|
90
|
+
- `avatar` - Radix avatar with the Aeon size ramp.
|
|
91
|
+
- `progress` - Radix progress bar.
|
|
92
|
+
- `loader` - CSS spinner in five sizes.
|
|
93
|
+
- `tooltip` - Radix dark tooltip.
|
|
94
|
+
|
|
95
|
+
**Overlays**
|
|
96
|
+
- `dialog` - Radix dialog (Pop-Up).
|
|
97
|
+
- `action-sheet` - Mobile bottom sheet built on Radix dialog.
|
|
98
|
+
- `context-menu` - Radix context menu.
|
|
99
|
+
- `dropdown` - Radix dropdown menu.
|
|
100
|
+
- `list` - Mobile list rows with leading/trailing slots.
|
|
101
|
+
|
|
102
|
+
**Navigation**
|
|
103
|
+
- `tabs` - Radix tabs.
|
|
104
|
+
- `breadcrumb` - Accessible breadcrumb trail.
|
|
105
|
+
- `pagination` - Page navigation.
|
|
106
|
+
- `page-control` - Mobile page-indicator dots.
|
|
107
|
+
- `stepper` - Horizontal step indicator.
|
|
108
|
+
- `navbar-top` - Mobile top app bar.
|
|
109
|
+
- `navbar-bottom` - Mobile bottom tab bar.
|
|
110
|
+
|
|
111
|
+
**Layout**
|
|
112
|
+
- `card` - Header, footer, body, title, and description wrappers.
|
|
69
113
|
|
|
70
114
|
## How it works under-the-hood
|
|
71
115
|
|
package/bin/index.js
CHANGED
|
@@ -14,6 +14,116 @@ const __dirname = path.dirname(__filename);
|
|
|
14
14
|
const cwd = process.cwd();
|
|
15
15
|
|
|
16
16
|
const REGISTRY = {
|
|
17
|
+
alert: {
|
|
18
|
+
name: "Alert",
|
|
19
|
+
dependencies: ["lucide-react", "clsx", "tailwind-merge"],
|
|
20
|
+
dev_dependencies: [],
|
|
21
|
+
},
|
|
22
|
+
"action-sheet": {
|
|
23
|
+
name: "ActionSheet",
|
|
24
|
+
dependencies: ["@radix-ui/react-dialog", "clsx", "tailwind-merge"],
|
|
25
|
+
dev_dependencies: [],
|
|
26
|
+
},
|
|
27
|
+
breadcrumb: {
|
|
28
|
+
name: "Breadcrumb",
|
|
29
|
+
dependencies: ["lucide-react", "clsx", "tailwind-merge"],
|
|
30
|
+
dev_dependencies: [],
|
|
31
|
+
},
|
|
32
|
+
"button-group": {
|
|
33
|
+
name: "ButtonGroup",
|
|
34
|
+
dependencies: ["clsx", "tailwind-merge"],
|
|
35
|
+
dev_dependencies: [],
|
|
36
|
+
},
|
|
37
|
+
"context-menu": {
|
|
38
|
+
name: "ContextMenu",
|
|
39
|
+
dependencies: [
|
|
40
|
+
"@radix-ui/react-context-menu",
|
|
41
|
+
"lucide-react",
|
|
42
|
+
"clsx",
|
|
43
|
+
"tailwind-merge",
|
|
44
|
+
],
|
|
45
|
+
dev_dependencies: [],
|
|
46
|
+
},
|
|
47
|
+
dialog: {
|
|
48
|
+
name: "Dialog",
|
|
49
|
+
dependencies: [
|
|
50
|
+
"@radix-ui/react-dialog",
|
|
51
|
+
"lucide-react",
|
|
52
|
+
"clsx",
|
|
53
|
+
"tailwind-merge",
|
|
54
|
+
],
|
|
55
|
+
dev_dependencies: [],
|
|
56
|
+
},
|
|
57
|
+
dropdown: {
|
|
58
|
+
name: "Dropdown",
|
|
59
|
+
dependencies: [
|
|
60
|
+
"@radix-ui/react-dropdown-menu",
|
|
61
|
+
"lucide-react",
|
|
62
|
+
"clsx",
|
|
63
|
+
"tailwind-merge",
|
|
64
|
+
],
|
|
65
|
+
dev_dependencies: [],
|
|
66
|
+
},
|
|
67
|
+
list: {
|
|
68
|
+
name: "List",
|
|
69
|
+
dependencies: ["lucide-react", "clsx", "tailwind-merge"],
|
|
70
|
+
dev_dependencies: [],
|
|
71
|
+
},
|
|
72
|
+
loader: {
|
|
73
|
+
name: "Loader",
|
|
74
|
+
dependencies: ["clsx", "tailwind-merge"],
|
|
75
|
+
dev_dependencies: [],
|
|
76
|
+
},
|
|
77
|
+
"navbar-bottom": {
|
|
78
|
+
name: "NavbarBottom",
|
|
79
|
+
dependencies: ["lucide-react", "clsx", "tailwind-merge"],
|
|
80
|
+
dev_dependencies: [],
|
|
81
|
+
},
|
|
82
|
+
"navbar-top": {
|
|
83
|
+
name: "NavbarTop",
|
|
84
|
+
dependencies: ["lucide-react", "clsx", "tailwind-merge"],
|
|
85
|
+
dev_dependencies: [],
|
|
86
|
+
},
|
|
87
|
+
"page-control": {
|
|
88
|
+
name: "PageControl",
|
|
89
|
+
dependencies: ["clsx", "tailwind-merge"],
|
|
90
|
+
dev_dependencies: [],
|
|
91
|
+
},
|
|
92
|
+
pagination: {
|
|
93
|
+
name: "Pagination",
|
|
94
|
+
dependencies: ["lucide-react", "clsx", "tailwind-merge"],
|
|
95
|
+
dev_dependencies: [],
|
|
96
|
+
},
|
|
97
|
+
progress: {
|
|
98
|
+
name: "Progress",
|
|
99
|
+
dependencies: ["@radix-ui/react-progress", "clsx", "tailwind-merge"],
|
|
100
|
+
dev_dependencies: [],
|
|
101
|
+
},
|
|
102
|
+
radio: {
|
|
103
|
+
name: "Radio",
|
|
104
|
+
dependencies: ["@radix-ui/react-radio-group", "clsx", "tailwind-merge"],
|
|
105
|
+
dev_dependencies: [],
|
|
106
|
+
},
|
|
107
|
+
stepper: {
|
|
108
|
+
name: "Stepper",
|
|
109
|
+
dependencies: ["lucide-react", "clsx", "tailwind-merge"],
|
|
110
|
+
dev_dependencies: [],
|
|
111
|
+
},
|
|
112
|
+
tabs: {
|
|
113
|
+
name: "Tabs",
|
|
114
|
+
dependencies: ["@radix-ui/react-tabs", "clsx", "tailwind-merge"],
|
|
115
|
+
dev_dependencies: [],
|
|
116
|
+
},
|
|
117
|
+
toggle: {
|
|
118
|
+
name: "Toggle",
|
|
119
|
+
dependencies: ["@radix-ui/react-switch", "clsx", "tailwind-merge"],
|
|
120
|
+
dev_dependencies: [],
|
|
121
|
+
},
|
|
122
|
+
tooltip: {
|
|
123
|
+
name: "Tooltip",
|
|
124
|
+
dependencies: ["@radix-ui/react-tooltip", "clsx", "tailwind-merge"],
|
|
125
|
+
dev_dependencies: [],
|
|
126
|
+
},
|
|
17
127
|
button: {
|
|
18
128
|
name: "Button",
|
|
19
129
|
dependencies: [
|
|
@@ -56,12 +166,192 @@ const REGISTRY = {
|
|
|
56
166
|
},
|
|
57
167
|
checkbox: {
|
|
58
168
|
name: "Checkbox",
|
|
59
|
-
dependencies: [
|
|
169
|
+
dependencies: [
|
|
170
|
+
"@radix-ui/react-checkbox",
|
|
171
|
+
"lucide-react",
|
|
172
|
+
"clsx",
|
|
173
|
+
"tailwind-merge",
|
|
174
|
+
],
|
|
60
175
|
dev_dependencies: [],
|
|
61
176
|
},
|
|
62
177
|
};
|
|
63
178
|
|
|
64
179
|
const THEMES = {
|
|
180
|
+
aeon: `@theme static {
|
|
181
|
+
/* ——— Aeon Product Design System ——— */
|
|
182
|
+
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
|
183
|
+
|
|
184
|
+
/* Semantic colors */
|
|
185
|
+
--color-background: #ffffff;
|
|
186
|
+
--color-foreground: #1c222a;
|
|
187
|
+
--color-card: #ffffff;
|
|
188
|
+
--color-card-foreground: #1c222a;
|
|
189
|
+
--color-popover: #ffffff;
|
|
190
|
+
--color-popover-foreground: #1c222a;
|
|
191
|
+
--color-primary: #0d3435;
|
|
192
|
+
--color-primary-foreground: #ffffff;
|
|
193
|
+
--color-secondary: #f4f5f6;
|
|
194
|
+
--color-secondary-foreground: #1c222a;
|
|
195
|
+
--color-muted: #e7e9ec;
|
|
196
|
+
--color-muted-foreground: #475569;
|
|
197
|
+
--color-accent: #f1f3f3;
|
|
198
|
+
--color-accent-foreground: #0a2b2b;
|
|
199
|
+
--color-destructive: #821717;
|
|
200
|
+
--color-destructive-foreground: #ffffff;
|
|
201
|
+
--color-border: #cbcfd5;
|
|
202
|
+
--color-input: #cbcfd5;
|
|
203
|
+
--color-ring: #0d3435;
|
|
204
|
+
--color-success: #14532d;
|
|
205
|
+
--color-success-foreground: #ffffff;
|
|
206
|
+
--color-info: #075985;
|
|
207
|
+
--color-info-foreground: #ffffff;
|
|
208
|
+
--color-warning: #713f12;
|
|
209
|
+
--color-warning-foreground: #ffffff;
|
|
210
|
+
--color-error: #991b1b;
|
|
211
|
+
--color-error-foreground: #ffffff;
|
|
212
|
+
|
|
213
|
+
/* Primitive scales */
|
|
214
|
+
--color-primary-50: #f1f3f3;
|
|
215
|
+
--color-primary-100: #e0e6e6;
|
|
216
|
+
--color-primary-200: #bcc9c9;
|
|
217
|
+
--color-primary-300: #93a8a8;
|
|
218
|
+
--color-primary-400: #577778;
|
|
219
|
+
--color-primary-500: #0f3d3e;
|
|
220
|
+
--color-primary-600: #0d3435;
|
|
221
|
+
--color-primary-700: #0a2b2b;
|
|
222
|
+
--color-primary-800: #082222;
|
|
223
|
+
--color-primary-900: #061819;
|
|
224
|
+
--color-grey-50: #f4f5f6;
|
|
225
|
+
--color-grey-100: #e7e9ec;
|
|
226
|
+
--color-grey-200: #cbcfd5;
|
|
227
|
+
--color-grey-300: #acb2bc;
|
|
228
|
+
--color-grey-400: #7e8896;
|
|
229
|
+
--color-grey-500: #475569;
|
|
230
|
+
--color-grey-600: #3c4859;
|
|
231
|
+
--color-grey-700: #323c4a;
|
|
232
|
+
--color-grey-800: #272f3a;
|
|
233
|
+
--color-grey-900: #1c222a;
|
|
234
|
+
--color-success-50: #f1f5f2;
|
|
235
|
+
--color-success-100: #e0e9e4;
|
|
236
|
+
--color-success-200: #bdcfc4;
|
|
237
|
+
--color-success-300: #95b2a0;
|
|
238
|
+
--color-success-400: #5a876c;
|
|
239
|
+
--color-success-500: #14532d;
|
|
240
|
+
--color-success-600: #114726;
|
|
241
|
+
--color-success-700: #0e3a20;
|
|
242
|
+
--color-success-800: #0b2e19;
|
|
243
|
+
--color-success-900: #082112;
|
|
244
|
+
--color-error-50: #f9f1f1;
|
|
245
|
+
--color-error-100: #f2e1e1;
|
|
246
|
+
--color-error-200: #e2bfbf;
|
|
247
|
+
--color-error-300: #d19898;
|
|
248
|
+
--color-error-400: #b85f5f;
|
|
249
|
+
--color-error-500: #991b1b;
|
|
250
|
+
--color-error-600: #821717;
|
|
251
|
+
--color-error-700: #6b1313;
|
|
252
|
+
--color-error-800: #540f0f;
|
|
253
|
+
--color-error-900: #3d0b0b;
|
|
254
|
+
--color-warning-50: #f6f3f1;
|
|
255
|
+
--color-warning-100: #ede6e0;
|
|
256
|
+
--color-warning-200: #d7c9bd;
|
|
257
|
+
--color-warning-300: #bfa994;
|
|
258
|
+
--color-warning-400: #9c7959;
|
|
259
|
+
--color-warning-500: #713f12;
|
|
260
|
+
--color-warning-600: #60360f;
|
|
261
|
+
--color-warning-700: #4f2c0d;
|
|
262
|
+
--color-warning-800: #3e230a;
|
|
263
|
+
--color-warning-900: #2d1907;
|
|
264
|
+
--color-info-50: #f0f5f8;
|
|
265
|
+
--color-info-100: #dfe9ef;
|
|
266
|
+
--color-info-200: #bad1dd;
|
|
267
|
+
--color-info-300: #8fb4c8;
|
|
268
|
+
--color-info-400: #518baa;
|
|
269
|
+
--color-info-500: #075985;
|
|
270
|
+
--color-info-600: #064c71;
|
|
271
|
+
--color-info-700: #053e5d;
|
|
272
|
+
--color-info-800: #04314a;
|
|
273
|
+
--color-info-900: #032435;
|
|
274
|
+
|
|
275
|
+
/* Typography — Aeon text ramp (text-h1 … text-c3, text-label, text-btn-*) */
|
|
276
|
+
--text-h1: 3rem;
|
|
277
|
+
--text-h1--line-height: 3.625rem;
|
|
278
|
+
--text-h1--font-weight: 600;
|
|
279
|
+
--text-h2: 2.5rem;
|
|
280
|
+
--text-h2--line-height: 3rem;
|
|
281
|
+
--text-h2--font-weight: 600;
|
|
282
|
+
--text-h3: 2rem;
|
|
283
|
+
--text-h3--line-height: 2.375rem;
|
|
284
|
+
--text-h3--font-weight: 600;
|
|
285
|
+
--text-h4: 1.75rem;
|
|
286
|
+
--text-h4--line-height: 2.125rem;
|
|
287
|
+
--text-h4--font-weight: 600;
|
|
288
|
+
--text-h5: 1.5rem;
|
|
289
|
+
--text-h5--line-height: 1.75rem;
|
|
290
|
+
--text-h5--font-weight: 600;
|
|
291
|
+
--text-s1: 1.125rem;
|
|
292
|
+
--text-s1--line-height: 1.75rem;
|
|
293
|
+
--text-s1--font-weight: 600;
|
|
294
|
+
--text-s2: 1rem;
|
|
295
|
+
--text-s2--line-height: 1.5rem;
|
|
296
|
+
--text-s2--font-weight: 600;
|
|
297
|
+
--text-b1: 1rem;
|
|
298
|
+
--text-b1--line-height: 1.5rem;
|
|
299
|
+
--text-b1--font-weight: 400;
|
|
300
|
+
--text-b2: 1rem;
|
|
301
|
+
--text-b2--line-height: 1.5rem;
|
|
302
|
+
--text-b2--font-weight: 500;
|
|
303
|
+
--text-b3: 0.875rem;
|
|
304
|
+
--text-b3--line-height: 1.25rem;
|
|
305
|
+
--text-b3--font-weight: 400;
|
|
306
|
+
--text-b4: 0.875rem;
|
|
307
|
+
--text-b4--line-height: 1.25rem;
|
|
308
|
+
--text-b4--font-weight: 500;
|
|
309
|
+
--text-c1: 0.75rem;
|
|
310
|
+
--text-c1--line-height: 1rem;
|
|
311
|
+
--text-c1--font-weight: 400;
|
|
312
|
+
--text-c2: 0.75rem;
|
|
313
|
+
--text-c2--line-height: 1rem;
|
|
314
|
+
--text-c2--font-weight: 500;
|
|
315
|
+
--text-c3: 0.625rem;
|
|
316
|
+
--text-c3--line-height: 0.875rem;
|
|
317
|
+
--text-c3--font-weight: 500;
|
|
318
|
+
--text-label: 0.75rem;
|
|
319
|
+
--text-label--line-height: 1rem;
|
|
320
|
+
--text-label--font-weight: 500;
|
|
321
|
+
--text-btn-giant: 1.125rem;
|
|
322
|
+
--text-btn-giant--line-height: 1.5rem;
|
|
323
|
+
--text-btn-giant--font-weight: 600;
|
|
324
|
+
--text-btn-large: 1rem;
|
|
325
|
+
--text-btn-large--line-height: 1.25rem;
|
|
326
|
+
--text-btn-large--font-weight: 600;
|
|
327
|
+
--text-btn-medium: 0.875rem;
|
|
328
|
+
--text-btn-medium--line-height: 1rem;
|
|
329
|
+
--text-btn-medium--font-weight: 600;
|
|
330
|
+
--text-btn-small: 0.75rem;
|
|
331
|
+
--text-btn-small--line-height: 1rem;
|
|
332
|
+
--text-btn-small--font-weight: 600;
|
|
333
|
+
--text-btn-tiny: 0.625rem;
|
|
334
|
+
--text-btn-tiny--line-height: 0.75rem;
|
|
335
|
+
--text-btn-tiny--font-weight: 600;
|
|
336
|
+
|
|
337
|
+
/* Radius scale — rounded-xxs … rounded-xl */
|
|
338
|
+
--radius-xxs: 0.25rem;
|
|
339
|
+
--radius-xs: 0.5rem;
|
|
340
|
+
--radius-sm: 0.75rem;
|
|
341
|
+
--radius-md: 1rem;
|
|
342
|
+
--radius-lg: 1.25rem;
|
|
343
|
+
--radius-xl: 1.5rem;
|
|
344
|
+
|
|
345
|
+
/* Elevation — shadow-100 … shadow-800 */
|
|
346
|
+
--shadow-100: 0 4px 4px -2px #13192714, 0 2px 4px -2px #1319271f;
|
|
347
|
+
--shadow-200: 0 8px 8px -4px #13192714, 0 4px 6px -4px #1319271f;
|
|
348
|
+
--shadow-300: 0 8px 16px -6px #13192714, 0 6px 8px -6px #1319271f;
|
|
349
|
+
--shadow-400: 0 8px 24px -4px #13192714, 0 6px 12px -6px #1319271f;
|
|
350
|
+
--shadow-500: 0 10px 32px -4px #1319271a, 0 6px 14px -6px #1319271f;
|
|
351
|
+
--shadow-600: 0 12px 42px -4px #1319271f, 0 8px 18px -6px #1319271f;
|
|
352
|
+
--shadow-700: 0 14px 64px -4px #1319271f, 0 8px 22px -6px #1319271f;
|
|
353
|
+
--shadow-800: 0 18px 88px -4px #13192724, 0 8px 28px -6px #1319271f;
|
|
354
|
+
}`,
|
|
65
355
|
zinc: `:root {
|
|
66
356
|
--background: 0 0% 100%;
|
|
67
357
|
--foreground: 240 10% 3.9%;
|
|
@@ -231,19 +521,29 @@ const main = async () => {
|
|
|
231
521
|
}
|
|
232
522
|
|
|
233
523
|
if (command === "init") {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
524
|
+
let theme = componentName; // support `cooterlabs init aeon`
|
|
525
|
+
if (!THEMES[theme]) {
|
|
526
|
+
const response = await prompts({
|
|
527
|
+
type: "select",
|
|
528
|
+
name: "theme",
|
|
529
|
+
message: "Which theme would you like to use?",
|
|
530
|
+
choices: Object.keys(THEMES).map((t) => ({
|
|
531
|
+
title: t === "aeon" ? "aeon (default)" : t,
|
|
532
|
+
value: t,
|
|
533
|
+
})),
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
if (!response.theme) {
|
|
537
|
+
console.log(chalk.red("❌ Theme selection cancelled."));
|
|
538
|
+
process.exit(1);
|
|
539
|
+
}
|
|
540
|
+
theme = response.theme;
|
|
244
541
|
}
|
|
245
542
|
|
|
246
|
-
|
|
543
|
+
// aeon is a Tailwind v4 @theme block appended at top level;
|
|
544
|
+
// legacy themes are :root vars wrapped in @layer base.
|
|
545
|
+
const isV4Theme = theme === "aeon";
|
|
546
|
+
const themeCSS = THEMES[theme];
|
|
247
547
|
let cssPath = path.join(cwd, "src", "index.css");
|
|
248
548
|
|
|
249
549
|
if (env.framework === "next") {
|
|
@@ -277,24 +577,29 @@ const main = async () => {
|
|
|
277
577
|
cssContent.includes("@tailwind base") ||
|
|
278
578
|
cssContent.includes('@import "tailwindcss');
|
|
279
579
|
if (!hasTailwindDirectives) {
|
|
280
|
-
cssContent =
|
|
580
|
+
cssContent = isV4Theme
|
|
581
|
+
? `@import "tailwindcss";\n\n${cssContent}`
|
|
582
|
+
: `@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n${cssContent}`;
|
|
281
583
|
}
|
|
282
584
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
585
|
+
const themeBlock = isV4Theme
|
|
586
|
+
? `\n${themeCSS}\n`
|
|
587
|
+
: `\n@layer base {\n${themeCSS}\n}\n`;
|
|
588
|
+
|
|
589
|
+
const hasExistingVars = isV4Theme
|
|
590
|
+
? cssContent.includes("@theme")
|
|
591
|
+
: cssContent.includes(":root {") || cssContent.includes("--background:");
|
|
592
|
+
|
|
593
|
+
if (hasExistingVars) {
|
|
287
594
|
console.log(
|
|
288
595
|
chalk.yellow(
|
|
289
|
-
`⚠️
|
|
596
|
+
`⚠️ Theme variables may already exist in ${cssPath}. Please append them manually if needed:\n${themeBlock}`,
|
|
290
597
|
),
|
|
291
598
|
);
|
|
292
599
|
} else {
|
|
293
|
-
cssContent = `${cssContent}
|
|
600
|
+
cssContent = `${cssContent}${themeBlock}`;
|
|
294
601
|
fs.writeFileSync(cssPath, cssContent);
|
|
295
|
-
console.log(
|
|
296
|
-
chalk.green(`✅ Theme '${response.theme}' initialized in ${cssPath}`),
|
|
297
|
-
);
|
|
602
|
+
console.log(chalk.green(`✅ Theme '${theme}' initialized in ${cssPath}`));
|
|
298
603
|
}
|
|
299
604
|
} else if (command === "add") {
|
|
300
605
|
let componentsToAdd = [];
|
package/package.json
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cooterlabs",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Component library CLI for CooterLabs apps",
|
|
5
5
|
"files": [
|
|
6
6
|
"bin",
|
|
7
7
|
"templates"
|
|
8
8
|
],
|
|
9
|
-
"main": "index.js",
|
|
10
9
|
"scripts": {
|
|
11
10
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
11
|
},
|
|
13
12
|
"bin": {
|
|
14
|
-
"cooterlabs": "
|
|
13
|
+
"cooterlabs": "bin/index.js"
|
|
15
14
|
},
|
|
16
|
-
"keywords": [
|
|
15
|
+
"keywords": [
|
|
16
|
+
"aeon",
|
|
17
|
+
"react",
|
|
18
|
+
"typescript",
|
|
19
|
+
"cli",
|
|
20
|
+
"component-library"
|
|
21
|
+
],
|
|
17
22
|
"author": "",
|
|
18
23
|
"license": "ISC",
|
|
19
24
|
"type": "module",
|
|
@@ -25,5 +30,8 @@
|
|
|
25
30
|
"prettier": "^3.8.1",
|
|
26
31
|
"prompts": "^2.4.2",
|
|
27
32
|
"tailwind-merge": "^3.5.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/react": "^19.2.14"
|
|
28
36
|
}
|
|
29
37
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
|
3
|
+
import { clsx, type ClassValue } from "clsx"
|
|
4
|
+
import { twMerge } from "tailwind-merge"
|
|
5
|
+
|
|
6
|
+
export function cn(...inputs: ClassValue[]) {
|
|
7
|
+
return twMerge(clsx(inputs))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const ActionSheet = DialogPrimitive.Root
|
|
11
|
+
|
|
12
|
+
const ActionSheetTrigger = DialogPrimitive.Trigger
|
|
13
|
+
|
|
14
|
+
const ActionSheetPortal = DialogPrimitive.Portal
|
|
15
|
+
|
|
16
|
+
const ActionSheetOverlay = React.forwardRef<
|
|
17
|
+
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
|
18
|
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
|
19
|
+
>(({ className, ...props }, ref) => (
|
|
20
|
+
<DialogPrimitive.Overlay
|
|
21
|
+
ref={ref}
|
|
22
|
+
className={cn(
|
|
23
|
+
"fixed inset-0 z-50 bg-black/50 transition-opacity data-[state=closed]:opacity-0",
|
|
24
|
+
className
|
|
25
|
+
)}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
))
|
|
29
|
+
ActionSheetOverlay.displayName = "ActionSheetOverlay"
|
|
30
|
+
|
|
31
|
+
const ActionSheetContent = React.forwardRef<
|
|
32
|
+
React.ElementRef<typeof DialogPrimitive.Content>,
|
|
33
|
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
|
34
|
+
>(({ className, children, ...props }, ref) => (
|
|
35
|
+
<ActionSheetPortal>
|
|
36
|
+
<ActionSheetOverlay />
|
|
37
|
+
<DialogPrimitive.Content
|
|
38
|
+
ref={ref}
|
|
39
|
+
className={cn(
|
|
40
|
+
"fixed inset-x-0 bottom-0 z-50 flex w-full flex-col bg-background rounded-t-lg px-4 pb-4 pt-2 transition-transform data-[state=closed]:translate-y-full",
|
|
41
|
+
className
|
|
42
|
+
)}
|
|
43
|
+
{...props}
|
|
44
|
+
>
|
|
45
|
+
<div className="mx-auto mb-2 h-1 w-10 rounded-full bg-grey-200" />
|
|
46
|
+
{children}
|
|
47
|
+
</DialogPrimitive.Content>
|
|
48
|
+
</ActionSheetPortal>
|
|
49
|
+
))
|
|
50
|
+
ActionSheetContent.displayName = "ActionSheetContent"
|
|
51
|
+
|
|
52
|
+
const ActionSheetHeader = ({
|
|
53
|
+
className,
|
|
54
|
+
...props
|
|
55
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
56
|
+
<div
|
|
57
|
+
className={cn(
|
|
58
|
+
"flex flex-col gap-1 px-4 py-3 text-center text-c1 text-muted-foreground",
|
|
59
|
+
className
|
|
60
|
+
)}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
)
|
|
64
|
+
ActionSheetHeader.displayName = "ActionSheetHeader"
|
|
65
|
+
|
|
66
|
+
export interface ActionSheetItemProps
|
|
67
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
68
|
+
variant?: "default" | "destructive"
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const ActionSheetItem = React.forwardRef<
|
|
72
|
+
HTMLButtonElement,
|
|
73
|
+
ActionSheetItemProps
|
|
74
|
+
>(({ className, variant = "default", ...props }, ref) => (
|
|
75
|
+
<button
|
|
76
|
+
ref={ref}
|
|
77
|
+
className={cn(
|
|
78
|
+
"flex h-12 w-full items-center justify-center gap-2 text-b2 transition-colors hover:bg-secondary active:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:size-5 [&_svg]:shrink-0",
|
|
79
|
+
variant === "destructive" ? "text-error" : "text-foreground",
|
|
80
|
+
className
|
|
81
|
+
)}
|
|
82
|
+
{...props}
|
|
83
|
+
/>
|
|
84
|
+
))
|
|
85
|
+
ActionSheetItem.displayName = "ActionSheetItem"
|
|
86
|
+
|
|
87
|
+
const ActionSheetCancel = React.forwardRef<
|
|
88
|
+
HTMLButtonElement,
|
|
89
|
+
React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
90
|
+
>(({ className, ...props }, ref) => (
|
|
91
|
+
<DialogPrimitive.Close asChild>
|
|
92
|
+
<button
|
|
93
|
+
ref={ref}
|
|
94
|
+
className={cn(
|
|
95
|
+
"mt-2 flex h-12 w-full items-center justify-center rounded-xs border border-border bg-background text-b2 text-foreground transition-colors hover:bg-secondary active:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
96
|
+
className
|
|
97
|
+
)}
|
|
98
|
+
{...props}
|
|
99
|
+
/>
|
|
100
|
+
</DialogPrimitive.Close>
|
|
101
|
+
))
|
|
102
|
+
ActionSheetCancel.displayName = "ActionSheetCancel"
|
|
103
|
+
|
|
104
|
+
export {
|
|
105
|
+
ActionSheet,
|
|
106
|
+
ActionSheetTrigger,
|
|
107
|
+
ActionSheetPortal,
|
|
108
|
+
ActionSheetOverlay,
|
|
109
|
+
ActionSheetContent,
|
|
110
|
+
ActionSheetHeader,
|
|
111
|
+
ActionSheetItem,
|
|
112
|
+
ActionSheetCancel,
|
|
113
|
+
}
|