@sarunyu/system-one 1.1.0 → 1.1.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.
Files changed (3) hide show
  1. package/LICENSE +21 -0
  2. package/llms.txt +471 -0
  3. package/package.json +3 -2
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Saranyu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/llms.txt ADDED
@@ -0,0 +1,471 @@
1
+ # @sarunyu/system-one
2
+
3
+ React component library. Tailwind CSS v4 + CSS custom properties. 13 production-ready components. Built for AI-powered generation tools (v0, Lovable, Figma Make).
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @sarunyu/system-one
9
+ ```
10
+
11
+ ## Setup by Platform
12
+
13
+ ### v0 / Next.js (App Router)
14
+
15
+ ```tsx
16
+ // app/layout.tsx
17
+ import "@sarunyu/system-one/styles.css"
18
+ ```
19
+
20
+ Components are pre-annotated with `"use client"`. Safe to import anywhere — Server Components, Client Components, pages.
21
+
22
+ ### Next.js (Pages Router)
23
+
24
+ ```tsx
25
+ // pages/_app.tsx
26
+ import "@sarunyu/system-one/styles.css"
27
+ ```
28
+
29
+ ### Figma Make / Lovable / Vite
30
+
31
+ ```tsx
32
+ // src/main.tsx
33
+ import "@sarunyu/system-one/styles.css"
34
+ ```
35
+
36
+ ## Import
37
+
38
+ ```tsx
39
+ import {
40
+ Button, Input, TextArea, SearchInput,
41
+ Dropdown, DropdownMultiple, OptionList,
42
+ Tag, StatusTag, Chip,
43
+ Tab, TabGroup,
44
+ Card,
45
+ DateInput, TimeInput,
46
+ cn
47
+ } from "@sarunyu/system-one"
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Components
53
+
54
+ ### Button
55
+
56
+ Variants: `primary` | `outline` | `plain` | `outline-black` | `plain-black`
57
+ Label sizes: `xs` | `sm` | `md` | `lg` | `xl`
58
+ Icon-only sizes: `icon-xs` | `icon-sm` | `icon-md` | `icon-lg` | `icon-xl`
59
+
60
+ ```tsx
61
+ <Button variant="primary" size="md">Submit</Button>
62
+ <Button variant="outline" size="md" onClick={fn}>Cancel</Button>
63
+ <Button variant="plain" size="sm">Learn more</Button>
64
+ <Button variant="primary" size="md" leftIcon={<Icon />}>Add item</Button>
65
+ <Button variant="outline" size="md" rightIcon={<Icon />}>Continue</Button>
66
+ <Button size="icon-md" aria-label="Settings"><GearIcon /></Button>
67
+ ```
68
+
69
+ Props: `variant`, `size`, `leftIcon`, `rightIcon`, `disabled`, `onClick`, `className`, `children`
70
+
71
+ ---
72
+
73
+ ### Input
74
+
75
+ Floating-label text input with validation states.
76
+
77
+ ```tsx
78
+ <Input placeholder="Email" value={email} onChange={setEmail} />
79
+ <Input placeholder="Amount" unit="THB" />
80
+ <Input placeholder="Password" type="password" />
81
+ <Input placeholder="Bio" showCount maxCount={160} />
82
+ <Input forceState="error" errorMessage="This field is required" placeholder="Email" />
83
+ <Input forceState="disabled" placeholder="Read only" />
84
+ ```
85
+
86
+ Props: `placeholder`, `value`, `onChange`, `type`, `unit`, `showCount`, `maxCount`, `forceState` (`"default"` | `"focus"` | `"error"` | `"disabled"`), `errorMessage`, `required`, `className`
87
+
88
+ ---
89
+
90
+ ### TextArea
91
+
92
+ Multi-line input. API mirrors Input.
93
+
94
+ ```tsx
95
+ <TextArea placeholder="Description" value={text} onChange={setText} />
96
+ <TextArea placeholder="Tweet" showCount maxCount={280} />
97
+ <TextArea forceState="error" errorMessage="Required" placeholder="Notes" />
98
+ ```
99
+
100
+ Props: `placeholder`, `value`, `onChange`, `showCount`, `maxCount`, `forceState`, `errorMessage`, `required`, `className`
101
+
102
+ ---
103
+
104
+ ### SearchInput
105
+
106
+ Search field with icon and clear button.
107
+
108
+ ```tsx
109
+ <SearchInput placeholder="Search events..." value={q} onChange={setQ} />
110
+ <SearchInput size="sm" placeholder="Filter by name..." />
111
+ ```
112
+
113
+ Props: `placeholder`, `value`, `onChange`, `size` (`"lg"` | `"sm"`), `className`
114
+
115
+ ---
116
+
117
+ ### Dropdown
118
+
119
+ Single-select dropdown.
120
+
121
+ ```tsx
122
+ <Dropdown
123
+ placeholder="Select category"
124
+ options={[
125
+ { label: "Option A", value: "a" },
126
+ { label: "Option B", value: "b" },
127
+ { label: "Disabled", value: "c", disabled: true },
128
+ ]}
129
+ value={selected}
130
+ onChange={setSelected}
131
+ />
132
+ ```
133
+
134
+ Props: `placeholder`, `options` (`Array<{ label: string, value: string, disabled?: boolean }>`), `value`, `onChange`, `disabled`, `className`
135
+
136
+ ---
137
+
138
+ ### DropdownMultiple
139
+
140
+ Multi-select dropdown with checkboxes.
141
+
142
+ ```tsx
143
+ <DropdownMultiple
144
+ placeholder="Select tags"
145
+ options={options}
146
+ values={selected}
147
+ onChange={setSelected}
148
+ />
149
+ ```
150
+
151
+ Props: `placeholder`, `options`, `values`, `onChange`, `disabled`, `className`
152
+
153
+ ---
154
+
155
+ ### Tag
156
+
157
+ Compact colored label. Use for categories, statuses, filters.
158
+
159
+ Variants: `blue` | `green` | `yellow` | `red` | `gray` | `lime`
160
+ Sizes: `large` (default) | `small`
161
+
162
+ Color semantics: `green` → positive/active, `red` → error/danger, `yellow` → warning/pending, `blue` → informational, `gray` → neutral/inactive
163
+
164
+ ```tsx
165
+ <Tag text="Active" variant="green" />
166
+ <Tag text="Draft" variant="yellow" />
167
+ <Tag text="Error" variant="red" size="small" />
168
+ <Tag text="Filter" close onClose={fn} />
169
+ ```
170
+
171
+ Props: `text`, `variant`, `size`, `close`, `onClose`, `className`
172
+
173
+ ---
174
+
175
+ ### StatusTag
176
+
177
+ Process-flow state indicator with colored dot.
178
+
179
+ Types: `stop` | `success` | `hold` | `processing` | `error`
180
+
181
+ ```tsx
182
+ <StatusTag type="success" />
183
+ <StatusTag type="processing" text="In progress" />
184
+ <StatusTag type="hold" />
185
+ <StatusTag type="error" />
186
+ ```
187
+
188
+ Props: `type`, `text`, `className`
189
+
190
+ Use `StatusTag` for workflow states. Use `Tag` for categorical labels.
191
+
192
+ ---
193
+
194
+ ### Chip
195
+
196
+ Toggleable filter/selection chip. Always use in groups of 2+.
197
+
198
+ Types: `single` (default) | `multiple`
199
+ Sizes: `large` | `medium` | `small`
200
+
201
+ ```tsx
202
+ {/* Single-select group */}
203
+ <div className="flex gap-2">
204
+ <Chip label="All" selected={filter === "all"} onClick={() => setFilter("all")} />
205
+ <Chip label="Active" selected={filter === "active"} onClick={() => setFilter("active")} />
206
+ <Chip label="Archived" selected={filter === "archived"} onClick={() => setFilter("archived")} />
207
+ </div>
208
+
209
+ {/* Multi-select */}
210
+ <Chip label="Design" type="multiple" selected={tags.includes("design")} onClick={() => toggle("design")} />
211
+ ```
212
+
213
+ Props: `label`, `selected`, `onClick`, `type`, `size`, `disabled`, `className`
214
+
215
+ ---
216
+
217
+ ### TabGroup
218
+
219
+ Tabbed navigation. Always use `TabGroup`, not bare `Tab`.
220
+
221
+ Sizes: `lg` | `md` (default) | `sm`
222
+
223
+ ```tsx
224
+ <TabGroup
225
+ items={[
226
+ { id: "overview", title: "Overview" },
227
+ { id: "details", title: "Details", icon: true },
228
+ { id: "history", title: "History", notification: 3 },
229
+ { id: "settings", title: "Settings", disabled: true },
230
+ ]}
231
+ activeId={activeTab}
232
+ onChange={setActiveTab}
233
+ size="md"
234
+ />
235
+ ```
236
+
237
+ Props: `items` (`Array<{ id: string, title: string, icon?: boolean, notification?: number, disabled?: boolean }>`), `activeId`, `onChange`, `size`, `className`
238
+
239
+ ---
240
+
241
+ ### Card
242
+
243
+ Responsive event/content card.
244
+
245
+ Variants: `desktop` (308px wide) | `tablet` (224px wide) | `mobile` (163px wide)
246
+ tagStatus: `not-registered` | `registered` | `full`
247
+
248
+ ```tsx
249
+ <Card
250
+ variant="desktop"
251
+ title="Annual Conference 2024"
252
+ date="Jun 23, 2024"
253
+ time="08:30 - 12:00"
254
+ location="Main Hall, Floor 7"
255
+ count="150/200"
256
+ tagStatus="registered"
257
+ image="/banner.jpg"
258
+ />
259
+ ```
260
+
261
+ Props: `variant`, `title`, `date`, `time`, `location`, `count`, `tagStatus`, `image`, `className`
262
+
263
+ Match variant to viewport: `desktop` on wide layouts, `tablet` / `mobile` on narrow.
264
+
265
+ ---
266
+
267
+ ### DateInput
268
+
269
+ Calendar date picker.
270
+
271
+ Modes: `single` | `range` | `multiple`
272
+ Variants: `popover` (default) | `modal`
273
+
274
+ ```tsx
275
+ <DateInput placeholder="Select date" mode="single" value={date} onChange={setDate} />
276
+ <DateInput mode="range" value={range} onChange={setRange} />
277
+ <DateInput mode="multiple" value={dates} onChange={setDates} />
278
+ ```
279
+
280
+ Props: `placeholder`, `mode`, `value`, `onChange`, `variant`, `disabled`, `className`
281
+
282
+ ---
283
+
284
+ ### TimeInput
285
+
286
+ 24-hour time picker.
287
+
288
+ Modes: `single` | `range`
289
+
290
+ ```tsx
291
+ <TimeInput placeholder="Start time" value={time} onChange={setTime} />
292
+ <TimeInput mode="range" value={timeRange} onChange={setTimeRange} />
293
+ ```
294
+
295
+ Props: `placeholder`, `mode`, `value`, `onChange`, `disabled`, `className`
296
+
297
+ ---
298
+
299
+ ### OptionList
300
+
301
+ Scrollable option list for custom dropdowns.
302
+
303
+ ```tsx
304
+ {/* Single-select */}
305
+ <OptionList
306
+ options={[{ label: "Item A", value: "a" }, { label: "Item B", value: "b" }]}
307
+ selectedValue={value}
308
+ onSelect={setValue}
309
+ />
310
+
311
+ {/* Multi-select */}
312
+ <OptionList
313
+ options={options}
314
+ selectedValues={values}
315
+ onToggle={toggleValue}
316
+ />
317
+ ```
318
+
319
+ Props: `options` (`Array<{ label: string, value: string, disabled?: boolean }>`), `selectedValue`, `onSelect`, `selectedValues`, `onToggle`, `className`
320
+
321
+ ---
322
+
323
+ ## Design Tokens
324
+
325
+ All tokens are CSS custom properties. Override after the stylesheet import:
326
+
327
+ ```css
328
+ :root {
329
+ --primary-action: #7c3aed; /* brand color */
330
+ --primary-action-hover: #6d28d9;
331
+ --primary-action-active: #5b21b6;
332
+ --font-sans: "Inter", sans-serif; /* override library default (Noto Sans Thai) */
333
+ --radius: 8px; /* border radius base */
334
+ }
335
+ ```
336
+
337
+ Key tokens:
338
+ - `--primary-action` — brand color (primary buttons, links, active states)
339
+ - `--background` / `--foreground` — page surface and text
340
+ - `--border` — default border color
341
+ - `--muted-foreground` — placeholder and secondary text
342
+ - `--destructive` — error/danger red
343
+ - `--success` — success green
344
+ - `--font-sans` — font family (default: Noto Sans Thai)
345
+ - `--radius` — base border radius (default: 4px)
346
+
347
+ ---
348
+
349
+ ## Dark Mode
350
+
351
+ Add `.dark` to `<html>` or any ancestor:
352
+
353
+ ```tsx
354
+ // Next.js
355
+ <html className={isDark ? "dark" : ""}>
356
+
357
+ // Vite
358
+ document.documentElement.classList.toggle("dark", isDark)
359
+ ```
360
+
361
+ All components adapt automatically.
362
+
363
+ ---
364
+
365
+ ## TypeScript
366
+
367
+ ```ts
368
+ import type { ButtonVariant, ButtonSize, TagVariant, ChipSize } from "@sarunyu/system-one"
369
+ ```
370
+
371
+ ---
372
+
373
+ ## Notes
374
+
375
+ - No provider or wrapper needed — just import the stylesheet and use components
376
+ - The library ships pre-built CSS — no Tailwind config required in the consuming project
377
+ - If the project already uses Tailwind and you see font differences, override `--font-sans` in your CSS
378
+ - CSS custom properties can be referenced in Tailwind classes: `bg-[var(--primary-action)]`
379
+
380
+ ---
381
+
382
+ ## Component Usage Guide
383
+
384
+ ### Button
385
+
386
+ Buttons are interactive elements that allow users to click or tap to perform actions — such as submitting a form, saving data, navigating to another page, or opening a popup.
387
+
388
+ Types: `primary` — main CTA, use once per context. `outline` — secondary action, supports primary without affecting main flow. `plain` — no background or border, low-priority actions. `secondary` (outline-black) — same as outline but black, use when blue is not appropriate.
389
+
390
+ Sizes: `base` recommended for Desktop. `xl` recommended for Mobile.
391
+
392
+ States: Default, Hover, Press, Disabled.
393
+
394
+ ### Input
395
+
396
+ Floating Label Input is a single-line or multi-line field with a label that floats inside.
397
+
398
+ Types: `Input` — single-line, general text/numeric. `TextArea` — multi-line, for comments or addresses. `Dropdown` — predefined list selection, displays as OptionList.
399
+
400
+ States: Default, Focus, Error, Disabled.
401
+
402
+ ### Tab
403
+
404
+ Tabs allow users to switch between different content sections within the same page.
405
+
406
+ Types: Default (text only), Icon (with icon), Notification (with badge).
407
+
408
+ States: Default, Active (selected), Disabled.
409
+
410
+ Sizes: `lg` (default), `md`, `sm`. Always use the same size within one tab group.
411
+
412
+ ### Tag
413
+
414
+ Tags display categories, types, or short metadata. Status Tags communicate process state.
415
+
416
+ Tag types: Default (text only), Icon (with icon), Remove (dismissible).
417
+ Tag states: Default, Hover/Press, Disabled.
418
+ Tag sizes: Large, Small.
419
+
420
+ Status Tag variants: `success` — process completed. `processing` — in progress. `hold` — temporarily paused. `stop` — stopped. `error` — failed.
421
+
422
+ ### Chip
423
+
424
+ Chips are toggleable filter/selection elements. Always use in groups of 2+.
425
+
426
+ Types: `single` — one selection at a time. `multiple` — multiple selections simultaneously.
427
+
428
+ States: Default, Active, Disabled (Default), Disabled (Active).
429
+
430
+ Sizes: `large`, `medium`, `small`.
431
+
432
+ ---
433
+
434
+ ## Design Rules
435
+
436
+ ### Button Rules
437
+
438
+ - Maximum width is 343px (`max-w-[343px]`). Never remove or override. Do not detach the component.
439
+ - Do not manually recreate or modify padding (`py-[10px] px-[16px]`) or border-radius (`rounded-[8px]`).
440
+ - Use Primary only once per context. Never place two Primary buttons side-by-side. Use Outline/Secondary for supporting actions.
441
+ - Hug width is correct for short labels only. For long labels, use Fill width or set explicit max-w-[343px].
442
+
443
+ ### Input Rules
444
+
445
+ - Never override input border or background colors manually — use built-in state variants (Default, Focus, Error, Disabled).
446
+ - Do not recreate input styles manually or adjust gap/height outside the component's tokens.
447
+ - Use Dropdown Tags only for multi-select. For single-select, use standard Dropdown.
448
+ - Keep labelText short enough to fit on one line. Long labels break the floating label layout.
449
+ - Do not show Helper Text and Error Message simultaneously. Error state replaces helper text.
450
+ - Use Option List only when there are multiple selectable values.
451
+ - Always follow the system date format (DD MMM YYYY). Do not mix Thai month names with C.E. year or reorder day/month/year.
452
+
453
+ ### Tab Rules
454
+
455
+ - Do not override padding, gap, border-radius, element order, or colors inside the tab bar.
456
+ - All tabs in one group must use the same size prop. Never mix sizes.
457
+ - Do not add extra gap or margin between tabs — use built-in spacing tokens only.
458
+
459
+ ### Tag Rules
460
+
461
+ - Do not override height, padding, or layout of Tag or StatusTag.
462
+ - All tags in the same context must use the same size.
463
+ - Do not reorder internal elements (icon, label, badge) inside Tag or StatusTag.
464
+ - Always use the correct StatusTag variant for its semantic meaning. Never override the color.
465
+
466
+ ### Chip Rules
467
+
468
+ - Keep chip labels short and single-purpose. Long labels cause overflow.
469
+ - Do not override padding, gap, radius, height, element order, or colors.
470
+ - All chips in the same group must share the same size and spacing.
471
+ - Use Chip only for groups with 2+ options. For single options, use a toggle or checkbox instead.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sarunyu/system-one",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "type": "module",
5
5
  "description": "A production-ready React design system built for AI-powered web generation tools (Figma Make, Lovable, V0). Tailwind CSS v4 + CSS custom properties for full theming support.",
6
6
  "keywords": [
@@ -38,7 +38,8 @@
38
38
  "*.css"
39
39
  ],
40
40
  "files": [
41
- "dist"
41
+ "dist",
42
+ "llms.txt"
42
43
  ],
43
44
  "scripts": {
44
45
  "dev": "vite",