@sarunyu/system-one 2.0.0 → 2.0.2

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 (2) hide show
  1. package/llms.txt +274 -320
  2. package/package.json +1 -1
package/llms.txt CHANGED
@@ -1,6 +1,8 @@
1
1
  # @sarunyu/system-one
2
2
 
3
- React UI component library for AI-powered generation tools (v0, Lovable, Figma Make). Provides production-ready interactive components you design the layouts freely, the library handles the UI primitives. Tailwind CSS v4 + CSS custom properties.
3
+ React UI component library for AI generation tools (Figma Make, v0, Lovable). You compose pages from layout + UI primitives both are in the library. Tailwind CSS v4 + CSS custom properties.
4
+
5
+ ---
4
6
 
5
7
  ## Install
6
8
 
@@ -8,35 +10,36 @@ React UI component library for AI-powered generation tools (v0, Lovable, Figma M
8
10
  npm install @sarunyu/system-one
9
11
  ```
10
12
 
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
- ```
13
+ ## Setup REQUIRED FIRST STEP
19
14
 
20
- Components are pre-annotated with `"use client"`. Safe to import anywhere Server Components, Client Components, pages.
15
+ **You MUST import the stylesheet once at the app root. Without this, components render unstyled and layout will break.**
21
16
 
22
- ### Next.js (Pages Router)
17
+ Pick the entry file that already exists in the project. In order of preference:
23
18
 
24
19
  ```tsx
25
- // pages/_app.tsx
20
+ // Figma Make / Vite / CRA — top of /App.tsx (or /src/App.tsx)
26
21
  import "@sarunyu/system-one/styles.css"
27
- ```
28
22
 
29
- ### Figma Make / Lovable / Vite
23
+ // Next.js App Router top of /app/layout.tsx
24
+ import "@sarunyu/system-one/styles.css"
30
25
 
31
- ```tsx
32
- // src/main.tsx
26
+ // Next.js Pages Router — top of /pages/_app.tsx
33
27
  import "@sarunyu/system-one/styles.css"
34
28
  ```
35
29
 
36
- ## Import
30
+ Rules:
31
+ - Import the stylesheet **exactly once**, as the first import in the entry file.
32
+ - Do **not** re-import it inside individual components.
33
+ - Do **not** add a Tailwind config — the library ships pre-built CSS.
34
+ - If Figma Make complains about a missing import path, place the line at the very top of `App.tsx`.
35
+
36
+ ## Import components
37
37
 
38
38
  ```tsx
39
39
  import {
40
+ // Layout
41
+ Page, PageHeader, Section, Toolbar, Stack, CardGrid,
42
+ // UI
40
43
  Button, Input, TextArea, SearchInput,
41
44
  Dropdown, DropdownMultiple, OptionList,
42
45
  Tag, StatusTag, Chip,
@@ -49,6 +52,199 @@ import {
49
52
 
50
53
  ---
51
54
 
55
+ ## Color rules — read this before touching any className
56
+
57
+ **Never** use Tailwind's literal color utilities for brand or semantic roles. Only use design token CSS variables.
58
+
59
+ | Role | ✅ Use this token | ❌ Never this |
60
+ |---|---|---|
61
+ | Brand / CTA | `var(--primary-action)` | `bg-blue-600`, `text-blue-500` |
62
+ | Hover / active | `var(--primary-action-hover)`, `var(--primary-action-active)` | `hover:bg-blue-700` |
63
+ | Body text | `var(--foreground)` | `text-gray-900` |
64
+ | Secondary text | `var(--muted-foreground)` | `text-gray-500` |
65
+ | Page background | `var(--background)` | `bg-white`, `bg-gray-50` |
66
+ | Card surface | `var(--card)` | `bg-white` |
67
+ | Subtle surface | `var(--muted)` | `bg-gray-100` |
68
+ | Border | `var(--border)` | `border-gray-200` |
69
+ | Subtle divider | `var(--divider)` | `border-gray-100` |
70
+ | Success | `var(--success)` | `text-green-600` |
71
+ | Error / danger | `var(--destructive)` | `text-red-500` |
72
+ | Warning | `var(--accent-orange)` | `text-orange-400` |
73
+
74
+ Usage in className: `className="bg-[var(--card)] text-[var(--foreground)] border-[var(--border)]"`
75
+ Usage in style: `style={{ color: "var(--primary-action)" }}`
76
+
77
+ ---
78
+
79
+ ## Layout — design freely, but color with tokens
80
+
81
+ **Layout is yours to design.** Use any structure, spacing, grid, or visual treatment you want. The two hard rules are:
82
+
83
+ 1. **Colors** — every color must come from a design token (see Color rules above). No `bg-blue-500`, no `text-gray-900`.
84
+ 2. **UI components** — if the element matches a library component, use it (see The One Rule below).
85
+
86
+ Everything else — container widths, grids, flex layouts, padding, hero sections, sidebars, split-pane, masonry, sticky headers, full-bleed backgrounds — is open. Design whatever looks great.
87
+
88
+ ### Layout convenience components (optional)
89
+
90
+ The library exports these if you want quick, consistent scaffolding. Use them when they fit; skip them and write raw Tailwind when they don't.
91
+
92
+ | Component | What it gives you |
93
+ |---|---|
94
+ | `<Page width="lg">` | `max-w-[1200px] mx-auto px-6 py-10 flex flex-col gap-12` |
95
+ | `<PageHeader title actions eyebrow>` | Responsive title + description + right-aligned actions |
96
+ | `<Section title description actions>` | Content group with optional header, `flex flex-col gap-6` |
97
+ | `<Toolbar end>` | Left cluster + right cluster split with `ml-auto` |
98
+ | `<Stack direction gap align justify wrap>` | Flex col/row with typed gap scale |
99
+ | `<CardGrid cols>` | Responsive grid, always `gap-6` |
100
+
101
+ ```tsx
102
+ // Using convenience components
103
+ <Page width="lg">
104
+ <PageHeader title="Events" actions={<Button variant="primary" size="md">Add</Button>} />
105
+ <Section title="All Events">
106
+ <CardGrid cols={3}><Card ... /></CardGrid>
107
+ </Section>
108
+ </Page>
109
+
110
+ // Or go fully custom — both are fine
111
+ <div className="min-h-screen bg-[var(--background)]">
112
+ <div className="relative h-[480px] bg-[var(--primary-action)] flex items-end pb-16 px-12">
113
+ <h1 className="text-5xl font-bold text-[var(--on-primary-action)]">Events</h1>
114
+ </div>
115
+ <div className="max-w-[1400px] mx-auto px-8 -mt-12 grid grid-cols-4 gap-6">
116
+ <Card variant="desktop" ... />
117
+ </div>
118
+ </div>
119
+ ```
120
+
121
+ **Stack gap scale** (if used): `1`=4px · `2`=8px · `3`=12px · `4`=16px · `6`=24px · `8`=32px · `10`=40px · `12`=48px
122
+
123
+ ---
124
+
125
+ ## The One Rule: use library UI components, never recreate them
126
+
127
+ If a UI element matches a component in the list below → import it. Do not rebuild it with raw HTML + Tailwind. Do not add classes that restyle its internal look (colors, border, padding, radius, focus ring). The component already owns those.
128
+
129
+ ```tsx
130
+ // ✅ CORRECT
131
+ <Input placeholder="Email" value={email} onChange={setEmail} />
132
+ <Button variant="primary" size="md">Sign in</Button>
133
+ <Tag text="Active" variant="green" />
134
+
135
+ // ❌ WRONG — never recreate library components
136
+ <input className="border rounded-xl px-4 py-3 w-full" placeholder="Email" />
137
+ <button className="bg-blue-600 text-white px-6 py-2 rounded-lg">Sign in</button>
138
+ <span className="bg-green-100 text-green-700 px-2 py-1 rounded">Active</span>
139
+ ```
140
+
141
+ **If the element is NOT in the library → build it freely with Tailwind + tokens.** The library does NOT provide: modal/dialog, sidebar, table, accordion, tooltip, progress bar, avatar, breadcrumb, slider, toggle/switch, file upload, chart, carousel, skeleton, pagination, toast/banner, stepper, menu. Build these with plain HTML + Tailwind + the design tokens above.
142
+
143
+ ---
144
+
145
+ ## Starter snippets
146
+
147
+ These are minimal correct examples. Layout structure is up to you — the important parts are the component imports and the `var(--...)` tokens.
148
+
149
+ **List page (using convenience components):**
150
+
151
+ ```tsx
152
+ import { Page, PageHeader, Section, Toolbar, CardGrid, Button, SearchInput, TabGroup, Card } from "@sarunyu/system-one"
153
+
154
+ export default function EventsPage() {
155
+ return (
156
+ <div className="min-h-screen bg-[var(--background)] text-[var(--foreground)]">
157
+ <Page width="lg">
158
+ <PageHeader
159
+ title="Events"
160
+ description="Browse and manage your events."
161
+ actions={<Button variant="primary" size="md">New Event</Button>}
162
+ />
163
+ <Section title="All Events">
164
+ <Toolbar end={<SearchInput size="sm" placeholder="Search..." value="" onChange={() => {}} />}>
165
+ <TabGroup items={[{ id: "all", title: "All" }, { id: "mine", title: "Mine" }]} activeId="all" onChange={() => {}} size="md" />
166
+ </Toolbar>
167
+ <CardGrid cols={3}>
168
+ <Card variant="desktop" title="Annual Conference" date="Jun 23, 2024" time="08:30 - 12:00" location="Main Hall" count="150/200" tagStatus="registered" image="/banner.jpg" />
169
+ </CardGrid>
170
+ </Section>
171
+ </Page>
172
+ </div>
173
+ )
174
+ }
175
+ ```
176
+
177
+ **List page (custom layout — equally valid):**
178
+
179
+ ```tsx
180
+ import { Button, SearchInput, TabGroup, Card } from "@sarunyu/system-one"
181
+
182
+ export default function EventsPage() {
183
+ return (
184
+ <div className="min-h-screen bg-[var(--background)]">
185
+ {/* Hero */}
186
+ <div className="bg-[var(--primary-action)] px-12 pt-20 pb-32">
187
+ <h1 className="text-5xl font-bold text-[var(--on-primary-action)]">Events</h1>
188
+ <p className="mt-3 text-lg text-[var(--on-primary-action)] opacity-80">Browse and manage your events.</p>
189
+ </div>
190
+ {/* Content pulled up over hero */}
191
+ <div className="max-w-[1400px] mx-auto px-8 -mt-16">
192
+ <div className="flex items-center justify-between mb-6">
193
+ <TabGroup items={[{ id: "all", title: "All" }, { id: "mine", title: "Mine" }]} activeId="all" onChange={() => {}} size="md" />
194
+ <div className="flex gap-3">
195
+ <SearchInput size="sm" placeholder="Search..." value="" onChange={() => {}} />
196
+ <Button variant="primary" size="md">New Event</Button>
197
+ </div>
198
+ </div>
199
+ <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6">
200
+ <Card variant="desktop" title="Annual Conference" date="Jun 23, 2024" time="08:30 - 12:00" location="Main Hall" count="150/200" tagStatus="registered" image="/banner.jpg" />
201
+ </div>
202
+ </div>
203
+ </div>
204
+ )
205
+ }
206
+ ```
207
+
208
+ **Form page:**
209
+
210
+ ```tsx
211
+ import { Stack, Input, TextArea, DateInput, TimeInput, Dropdown, Button } from "@sarunyu/system-one"
212
+
213
+ <div className="max-w-[480px] mx-auto px-6 py-12 space-y-6">
214
+ <h1 className="text-3xl font-semibold text-[var(--foreground)]">Create Event</h1>
215
+ <Stack gap={5}>
216
+ <Input placeholder="Event Name" value={name} onChange={setName} />
217
+ <TextArea placeholder="Description" value={desc} onChange={setDesc} />
218
+ <div className="grid grid-cols-2 gap-3">
219
+ <DateInput placeholder="Start date" mode="single" value={date} onChange={setDate} />
220
+ <TimeInput placeholder="Start time" value={time} onChange={setTime} />
221
+ </div>
222
+ <Dropdown placeholder="Category" options={cats} value={cat} onChange={setCat} />
223
+ <Button variant="primary" size="md" className="w-full">Create Event</Button>
224
+ </Stack>
225
+ </div>
226
+ ```
227
+
228
+ ---
229
+
230
+ ## Typography scale
231
+
232
+ The library does NOT preset h1–h6 — set them yourself with Tailwind. Always pair with token colors.
233
+
234
+ - Page title (h1): `text-3xl md:text-4xl font-semibold tracking-tight text-[var(--foreground)]`
235
+ - Section title (h2): `text-2xl font-semibold text-[var(--foreground)]`
236
+ - Subsection (h3): `text-lg font-semibold text-[var(--foreground)]`
237
+ - Body: `text-base text-[var(--foreground)]` — never smaller than `text-sm` for reading content
238
+ - Caption / meta: `text-sm text-[var(--muted-foreground)]`
239
+
240
+ ### Radius
241
+
242
+ - Inputs, buttons, small elements: `rounded-md` (6px)
243
+ - Cards, panels: `rounded-xl` (12px) or `rounded-2xl` (16px)
244
+ - Pills / avatars: `rounded-full`
245
+
246
+ ---
247
+
52
248
  ## Components
53
249
 
54
250
  ### Button
@@ -62,17 +258,19 @@ Icon-only sizes: `icon-xs` | `icon-sm` | `icon-md` | `icon-lg` | `icon-xl`
62
258
  <Button variant="outline" size="md" onClick={fn}>Cancel</Button>
63
259
  <Button variant="plain" size="sm">Learn more</Button>
64
260
  <Button variant="primary" size="md" leftIcon={<Icon />}>Add item</Button>
65
- <Button variant="outline" size="md" rightIcon={<Icon />}>Continue</Button>
66
261
  <Button size="icon-md" aria-label="Settings"><GearIcon /></Button>
67
262
  ```
68
263
 
69
264
  Props: `variant`, `size`, `leftIcon`, `rightIcon`, `disabled`, `onClick`, `className`, `children`
70
265
 
71
- ---
266
+ Rules:
267
+ - One `primary` per context. Supporting actions use `outline` or `plain`.
268
+ - For mobile full-width CTAs use `className="w-full"`. Default hug width fits short labels.
269
+ - Do not add classes that change background, border, padding, or radius.
72
270
 
73
271
  ### Input
74
272
 
75
- Floating-label text input with validation states.
273
+ Floating-label text input.
76
274
 
77
275
  ```tsx
78
276
  <Input placeholder="Email" value={email} onChange={setEmail} />
@@ -85,7 +283,10 @@ Floating-label text input with validation states.
85
283
 
86
284
  Props: `placeholder`, `value`, `onChange`, `type`, `unit`, `showCount`, `maxCount`, `forceState` (`"default"` | `"focus"` | `"error"` | `"disabled"`), `errorMessage`, `required`, `className`
87
285
 
88
- ---
286
+ Rules:
287
+ - Keep `placeholder` short — long text breaks the floating label.
288
+ - Never show helper text and `errorMessage` at the same time.
289
+ - Do not override border or background — use `forceState` instead.
89
290
 
90
291
  ### TextArea
91
292
 
@@ -99,12 +300,8 @@ Multi-line input. API mirrors Input.
99
300
 
100
301
  Props: `placeholder`, `value`, `onChange`, `showCount`, `maxCount`, `forceState`, `errorMessage`, `required`, `className`
101
302
 
102
- ---
103
-
104
303
  ### SearchInput
105
304
 
106
- Search field with icon and clear button.
107
-
108
305
  ```tsx
109
306
  <SearchInput placeholder="Search events..." value={q} onChange={setQ} />
110
307
  <SearchInput size="sm" placeholder="Filter by name..." />
@@ -112,11 +309,7 @@ Search field with icon and clear button.
112
309
 
113
310
  Props: `placeholder`, `value`, `onChange`, `size` (`"lg"` | `"sm"`), `className`
114
311
 
115
- ---
116
-
117
- ### Dropdown
118
-
119
- Single-select dropdown.
312
+ ### Dropdown / DropdownMultiple
120
313
 
121
314
  ```tsx
122
315
  <Dropdown
@@ -129,17 +322,7 @@ Single-select dropdown.
129
322
  value={selected}
130
323
  onChange={setSelected}
131
324
  />
132
- ```
133
-
134
- Props: `placeholder`, `options` (`Array<{ label: string, value: string, disabled?: boolean }>`), `value`, `onChange`, `disabled`, `className`
135
-
136
- ---
137
325
 
138
- ### DropdownMultiple
139
-
140
- Multi-select dropdown with checkboxes.
141
-
142
- ```tsx
143
326
  <DropdownMultiple
144
327
  placeholder="Select tags"
145
328
  options={options}
@@ -148,18 +331,32 @@ Multi-select dropdown with checkboxes.
148
331
  />
149
332
  ```
150
333
 
151
- Props: `placeholder`, `options`, `values`, `onChange`, `disabled`, `className`
334
+ Props (single): `placeholder`, `options`, `value`, `onChange`, `disabled`, `className`
335
+ Props (multi): `placeholder`, `options`, `values`, `onChange`, `disabled`, `className`
152
336
 
153
- ---
337
+ Use `Dropdown` for single-select, `DropdownMultiple` for multi-select.
338
+
339
+ ### OptionList
340
+
341
+ Scrollable option list for custom popups.
342
+
343
+ ```tsx
344
+ <OptionList
345
+ options={[{ label: "Item A", value: "a" }, { label: "Item B", value: "b" }]}
346
+ selectedValue={value}
347
+ onSelect={setValue}
348
+ />
349
+
350
+ <OptionList options={options} selectedValues={values} onToggle={toggleValue} />
351
+ ```
154
352
 
155
353
  ### Tag
156
354
 
157
- Compact colored label. Use for categories, statuses, filters.
355
+ Compact colored label for categories/filters.
158
356
 
159
357
  Variants: `blue` | `green` | `yellow` | `red` | `gray` | `lime`
160
358
  Sizes: `large` (default) | `small`
161
-
162
- Color semantics: `green` → positive/active, `red` → error/danger, `yellow` → warning/pending, `blue` → informational, `gray` → neutral/inactive
359
+ Semantics: `green` = positive, `red` = error, `yellow` = warning/pending, `blue` = informational, `gray` = neutral
163
360
 
164
361
  ```tsx
165
362
  <Tag text="Active" variant="green" />
@@ -170,11 +367,11 @@ Color semantics: `green` → positive/active, `red` → error/danger, `yellow`
170
367
 
171
368
  Props: `text`, `variant`, `size`, `close`, `onClose`, `className`
172
369
 
173
- ---
370
+ Rules: all tags in the same group use the same size. Do not override padding/radius.
174
371
 
175
372
  ### StatusTag
176
373
 
177
- Process-flow state indicator with colored dot.
374
+ Workflow state indicator with a colored dot.
178
375
 
179
376
  Types: `stop` | `success` | `hold` | `processing` | `error`
180
377
 
@@ -182,41 +379,36 @@ Types: `stop` | `success` | `hold` | `processing` | `error`
182
379
  <StatusTag type="success" />
183
380
  <StatusTag type="processing" text="In progress" />
184
381
  <StatusTag type="hold" />
185
- <StatusTag type="error" />
186
382
  ```
187
383
 
188
384
  Props: `type`, `text`, `className`
189
385
 
190
- Use `StatusTag` for workflow states. Use `Tag` for categorical labels.
191
-
192
- ---
386
+ Use `StatusTag` for process state; use `Tag` for categorical labels.
193
387
 
194
388
  ### Chip
195
389
 
196
- Toggleable filter/selection chip. Always use in groups of 2+.
390
+ Toggleable filter chip. Always use in groups of 2+.
197
391
 
198
392
  Types: `single` (default) | `multiple`
199
393
  Sizes: `large` | `medium` | `small`
200
394
 
201
395
  ```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")} />
396
+ <div className="flex flex-wrap gap-2">
397
+ <Chip label="All" selected={f === "all"} onClick={() => setF("all")} />
398
+ <Chip label="Active" selected={f === "active"} onClick={() => setF("active")} />
399
+ <Chip label="Archived" selected={f === "archived"} onClick={() => setF("archived")} />
207
400
  </div>
208
401
 
209
- {/* Multi-select */}
210
402
  <Chip label="Design" type="multiple" selected={tags.includes("design")} onClick={() => toggle("design")} />
211
403
  ```
212
404
 
213
405
  Props: `label`, `selected`, `onClick`, `type`, `size`, `disabled`, `className`
214
406
 
215
- ---
407
+ Rules: one size per group. For a single on/off control use a toggle, not a chip.
216
408
 
217
409
  ### TabGroup
218
410
 
219
- Tabbed navigation. Always use `TabGroup`, not bare `Tab`.
411
+ Tabbed navigation. Always use `TabGroup`; never render `Tab` directly.
220
412
 
221
413
  Sizes: `lg` | `md` (default) | `sm`
222
414
 
@@ -234,15 +426,13 @@ Sizes: `lg` | `md` (default) | `sm`
234
426
  />
235
427
  ```
236
428
 
237
- Props: `items` (`Array<{ id: string, title: string, icon?: boolean, notification?: number, disabled?: boolean }>`), `activeId`, `onChange`, `size`, `className`
238
-
239
- ---
429
+ Props: `items` (`{ id, title, icon?, notification?, disabled? }[]`), `activeId`, `onChange`, `size`, `className`
240
430
 
241
431
  ### Card
242
432
 
243
- Responsive event/content card.
433
+ Event/content card with image + metadata.
244
434
 
245
- Variants: `desktop` (308px wide) | `tablet` (224px wide) | `mobile` (163px wide)
435
+ Variants: `desktop` (308px) | `tablet` (224px) | `mobile` (163px)
246
436
  tagStatus: `not-registered` | `registered` | `full`
247
437
 
248
438
  ```tsx
@@ -260,9 +450,7 @@ tagStatus: `not-registered` | `registered` | `full`
260
450
 
261
451
  Props: `variant`, `title`, `date`, `time`, `location`, `count`, `tagStatus`, `image`, `className`
262
452
 
263
- Match variant to viewport: `desktop` on wide layouts, `tablet` / `mobile` on narrow.
264
-
265
- ---
453
+ Match the variant to the viewport `desktop` in wide grids, `tablet`/`mobile` in narrow layouts.
266
454
 
267
455
  ### DateInput
268
456
 
@@ -279,293 +467,59 @@ Variants: `popover` (default) | `modal`
279
467
 
280
468
  Props: `placeholder`, `mode`, `value`, `onChange`, `variant`, `disabled`, `className`
281
469
 
282
- ---
470
+ Format is `DD MMM YYYY` — do not override.
283
471
 
284
472
  ### TimeInput
285
473
 
286
474
  24-hour time picker.
287
475
 
288
- Modes: `single` | `range`
289
-
290
476
  ```tsx
291
477
  <TimeInput placeholder="Start time" value={time} onChange={setTime} />
292
478
  <TimeInput mode="range" value={timeRange} onChange={setTimeRange} />
293
479
  ```
294
480
 
295
- Props: `placeholder`, `mode`, `value`, `onChange`, `disabled`, `className`
481
+ Props: `placeholder`, `mode` (`single` | `range`), `value`, `onChange`, `disabled`, `className`
296
482
 
297
483
  ---
298
484
 
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
485
+ ## Theming
324
486
 
325
487
  All tokens are CSS custom properties. Override after the stylesheet import:
326
488
 
327
489
  ```css
328
490
  :root {
329
- --primary-action: #7c3aed; /* brand color */
491
+ --primary-action: #7c3aed; /* brand color */
330
492
  --primary-action-hover: #6d28d9;
331
493
  --primary-action-active: #5b21b6;
332
- --font-sans: "Inter", sans-serif; /* override library default (Noto Sans Thai) */
333
- --radius: 8px; /* border radius base */
494
+ --font-sans: "Inter", sans-serif; /* default: Noto Sans Thai */
495
+ --radius: 8px;
334
496
  }
335
497
  ```
336
498
 
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
- ---
499
+ Key tokens: `--primary-action`, `--background`, `--foreground`, `--card`, `--muted`, `--muted-foreground`, `--border`, `--divider`, `--destructive`, `--success`, `--accent-orange`, `--font-sans`, `--radius`.
348
500
 
349
- ## Dark Mode
501
+ ### Dark mode
350
502
 
351
- Add `.dark` to `<html>` or any ancestor:
503
+ Add `.dark` to `<html>` (or any ancestor). All components adapt automatically.
352
504
 
353
505
  ```tsx
354
- // Next.js
355
- <html className={isDark ? "dark" : ""}>
356
-
357
- // Vite
358
506
  document.documentElement.classList.toggle("dark", isDark)
359
507
  ```
360
508
 
361
- All components adapt automatically.
362
-
363
- ---
364
-
365
- ## TypeScript
509
+ ### TypeScript
366
510
 
367
511
  ```ts
368
- import type { ButtonVariant, ButtonSize, TagVariant, ChipSize } from "@sarunyu/system-one"
512
+ import type { ButtonVariant, ButtonSize, TagVariant, ChipSize, PageWidth, StackGap } from "@sarunyu/system-one"
369
513
  ```
370
514
 
371
515
  ---
372
516
 
373
- ## Design Guidance
374
-
375
- **You own the layout. The library only provides UI components.** Design page structure, layout, and composition entirely on your own — use your best aesthetic judgment to create beautiful, polished pages. The library has no opinions about layout. It only asks that you use its components (listed below) when the UI element matches one the library provides.
376
-
377
- ### CRITICAL — do NOT recreate library components
378
-
379
- The library's components have their own built-in styles (colors, borders, backgrounds, padding, radius, focus states, hover states). You MUST import and use them as-is. **Never** build your own version of a component that exists in the library. Never add Tailwind classes to override their internal appearance.
380
-
381
- Common mistakes to avoid:
382
-
383
- ```tsx
384
- // ✅ CORRECT — use the library's <Input> component (has floating label, built-in border, focus ring, error state)
385
- import { Input } from "@sarunyu/system-one"
386
- <Input placeholder="Email" value={email} onChange={setEmail} />
387
- <Input placeholder="Password" type="password" value={pw} onChange={setPw} />
388
-
389
- // ❌ WRONG — never build your own input with raw HTML + Tailwind
390
- <input className="border rounded-xl px-4 py-3 w-full" placeholder="Email" />
391
- <input className="border rounded-xl px-4 py-3 w-full" type="password" placeholder="Password" />
392
- ```
393
-
394
- ```tsx
395
- // ✅ CORRECT — use the library's <Tag> component (has built-in colors, padding, border-radius)
396
- import { Tag } from "@sarunyu/system-one"
397
- <Tag text="Popular" variant="green" />
398
- <Tag text="Best Value" variant="blue" />
399
- <Tag text="Error" variant="red" size="small" />
400
-
401
- // ❌ WRONG — never style your own span/div to look like a tag
402
- <span className="text-green-500 bg-green-500/10 px-2 py-1 rounded">Popular</span>
403
- <span className="text-blue-500 border border-blue-500 px-2 py-1 rounded">Best Value</span>
404
- ```
405
-
406
- ```tsx
407
- // ✅ CORRECT — use the library's <Button> component
408
- import { Button } from "@sarunyu/system-one"
409
- <Button variant="primary" size="md">Sign In</Button>
410
- <Button variant="outline" size="md">Cancel</Button>
411
-
412
- // ❌ WRONG — never create buttons with raw HTML + Tailwind
413
- <button className="bg-blue-600 text-white px-6 py-2 rounded-lg">Sign In</button>
414
- ```
415
-
416
- This rule applies to ALL components listed in this document. The components handle their own styling — you only pass props.
417
-
418
- ### Layout — full creative freedom
419
-
420
- Design the layout yourself. Use any technique that produces the best result:
421
-
422
- - Flex, grid, CSS columns, absolute positioning, sticky headers, split panes, sidebars, hero sections, masonry grids, full-bleed backgrounds, overlapping elements — anything goes.
423
- - Choose spacing, padding, max-widths, breakpoints, and visual hierarchy based on what looks best for the specific page. There is no prescribed spacing rhythm or container width.
424
- - Prioritize aesthetics: generous whitespace, clear visual hierarchy, balanced proportions, elegant transitions and micro-interactions.
425
- - Do **not** use the library's layout helper components (`Page`, `PageHeader`, `Section`, `Toolbar`, `CardGrid`, `Stack`). They exist for internal use only. Build your own layouts — they will look better.
426
-
427
- ### Component usage — use ours when we have it, build your own when we don't
428
-
429
- The library provides a specific set of UI components. The rule is simple:
430
-
431
- **If the UI element matches a component below → import it from `@sarunyu/system-one` and use it.** Do not recreate it with raw HTML or Tailwind. Do not add Tailwind classes to restyle it. The component already has all the styling it needs.
432
-
433
- - Buttons → `<Button>` — has built-in variant colors, hover/press states, sizes, icon slots
434
- - Text inputs → `<Input>` — has built-in floating label, border, focus ring, error state
435
- - Textareas → `<TextArea>` — has built-in floating label, border, focus ring, error state
436
- - Search fields → `<SearchInput>` — has built-in search icon, clear button
437
- - Single select → `<Dropdown>` — has built-in dropdown panel, selection state
438
- - Multi select → `<DropdownMultiple>` — has built-in checkboxes, multi-selection
439
- - Option list → `<OptionList>` — has built-in selection highlighting
440
- - Date picker → `<DateInput>` — has built-in calendar popup
441
- - Time picker → `<TimeInput>` — has built-in time selection
442
- - Labels / badges → `<Tag>` — has built-in color variants (blue, green, yellow, red, gray, lime)
443
- - Status indicators → `<StatusTag>` — has built-in colored dot and status colors
444
- - Filter chips → `<Chip>` — has built-in toggle state, active styling
445
- - Tab navigation → `<TabGroup>` — has built-in active tab indicator, sizing
446
- - Event/content cards → `<Card>` — has built-in image, metadata layout
447
-
448
- **If the UI element does NOT match any component above → build it yourself.** Use Tailwind, custom components, or any other approach. Examples of things the library does NOT provide (build these yourself): modals/dialogs, sidebars/navigation, tables, accordions, tooltips, progress bars, avatars, breadcrumbs, sliders, toggles/switches, file uploads, charts, carousels, skeletons/loaders, pagination, alert/notification banners, steppers, menus, etc.
449
-
450
- Pick the correct variant / prop for the semantic role (e.g. `StatusTag type="success"` for success, `Button variant="primary"` once per context). See "Design Rules" below for per-component constraints.
451
-
452
- ### Visual anchors from the library
453
-
454
- The library is intentionally unopinionated about page chrome. It sets one global rule only:
455
-
456
- - `body { font-family: var(--font-sans) }` — the Noto Sans Thai default, so all text inherits a consistent family.
457
-
458
- Everything else — page background, text color, heading sizes, `html` font-size, button/input element defaults — is **yours to own**. The library does not pre-style `<h1>`–`<h6>`; choose the sizes/weights that fit your design.
459
-
460
- Use these tokens so custom-built elements stay visually coherent with the design system. Prefer them over hard-coded Tailwind colors for any brand or semantic role:
461
-
462
- - **Brand / CTA** — `--primary-action` (hover: `--primary-action-hover`, active: `--primary-action-active`). Use for primary buttons, links, active states, key iconography.
463
- - **Text** — body: `--foreground`. Secondary/muted: `--muted-foreground`.
464
- - **Surfaces** — `--background` (page), `--card` (card surface), `--muted` (subtle surface).
465
- - **Semantic** — `--success` (green), `--destructive` (red), `--accent-orange` (warning/accent).
466
- - **Border / divider** — `--border` default, `--divider` for subtle separators.
467
- - **Radius** — `--radius` (default 4px), plus Tailwind `rounded-md` (6px) / `rounded-lg` (8px) / `rounded-xl` (12px) / `rounded-full`.
468
-
469
- Consume tokens via inline style (`style={{ color: "var(--primary-action)" }}`) or arbitrary Tailwind (`className="text-[var(--primary-action)] bg-[var(--card)]"`). Do **not** use `bg-blue-500`, `text-orange-400`, `border-gray-200`, etc. for brand or semantic colors.
470
-
471
- ---
472
-
473
- ## Notes
474
-
475
- - No provider or wrapper needed — just import the stylesheet and use components
476
- - The library ships pre-built CSS — no Tailwind config required in the consuming project
477
- - If the project already uses Tailwind and you see font differences, override `--font-sans` in your CSS
478
- - CSS custom properties can be referenced in Tailwind classes: `bg-[var(--primary-action)]`
479
-
480
- ---
481
-
482
- ## Component Usage Guide
483
-
484
- ### Button
485
-
486
- 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.
487
-
488
- 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.
489
-
490
- Sizes: `base` recommended for Desktop. `xl` recommended for Mobile.
491
-
492
- States: Default, Hover, Press, Disabled.
493
-
494
- ### Input
495
-
496
- Floating Label Input is a single-line or multi-line field with a label that floats inside.
497
-
498
- Types: `Input` — single-line, general text/numeric. `TextArea` — multi-line, for comments or addresses. `Dropdown` — predefined list selection, displays as OptionList.
499
-
500
- States: Default, Focus, Error, Disabled.
501
-
502
- ### Tab
503
-
504
- Tabs allow users to switch between different content sections within the same page.
505
-
506
- Types: Default (text only), Icon (with icon), Notification (with badge).
507
-
508
- States: Default, Active (selected), Disabled.
509
-
510
- Sizes: `lg` (default), `md`, `sm`. Always use the same size within one tab group.
511
-
512
- ### Tag
513
-
514
- Tags display categories, types, or short metadata. Status Tags communicate process state.
515
-
516
- Tag types: Default (text only), Icon (with icon), Remove (dismissible).
517
- Tag states: Default, Hover/Press, Disabled.
518
- Tag sizes: Large, Small.
519
-
520
- Status Tag variants: `success` — process completed. `processing` — in progress. `hold` — temporarily paused. `stop` — stopped. `error` — failed.
521
-
522
- ### Chip
523
-
524
- Chips are toggleable filter/selection elements. Always use in groups of 2+.
525
-
526
- Types: `single` — one selection at a time. `multiple` — multiple selections simultaneously.
527
-
528
- States: Default, Active, Disabled (Default), Disabled (Active).
529
-
530
- Sizes: `large`, `medium`, `small`.
531
-
532
- ---
533
-
534
- ## Design Rules
535
-
536
- ### Button Rules
537
-
538
- - Maximum width is 343px (`max-w-[343px]`). Never remove or override. Do not detach the component.
539
- - Do not manually recreate or modify padding (`py-[10px] px-[16px]`) or border-radius (`rounded-[8px]`).
540
- - Use Primary only once per context. Never place two Primary buttons side-by-side. Use Outline/Secondary for supporting actions.
541
- - Hug width is correct for short labels only. For long labels, use Fill width or set explicit max-w-[343px].
542
-
543
- ### Input Rules
544
-
545
- - Never override input border or background colors manually — use built-in state variants (Default, Focus, Error, Disabled).
546
- - Do not recreate input styles manually or adjust gap/height outside the component's tokens.
547
- - Use Dropdown Tags only for multi-select. For single-select, use standard Dropdown.
548
- - Keep labelText short enough to fit on one line. Long labels break the floating label layout.
549
- - Do not show Helper Text and Error Message simultaneously. Error state replaces helper text.
550
- - Use Option List only when there are multiple selectable values.
551
- - Always follow the system date format (DD MMM YYYY). Do not mix Thai month names with C.E. year or reorder day/month/year.
552
-
553
- ### Tab Rules
554
-
555
- - Do not override padding, gap, border-radius, element order, or colors inside the tab bar.
556
- - All tabs in one group must use the same size prop. Never mix sizes.
557
- - Do not add extra gap or margin between tabs — use built-in spacing tokens only.
558
-
559
- ### Tag Rules
560
-
561
- - Do not override height, padding, or layout of Tag or StatusTag.
562
- - All tags in the same context must use the same size.
563
- - Do not reorder internal elements (icon, label, badge) inside Tag or StatusTag.
564
- - Always use the correct StatusTag variant for its semantic meaning. Never override the color.
517
+ ## Checklist before you render
565
518
 
566
- ### Chip Rules
519
+ 1. Stylesheet imported in the app root entry file? (see Setup)
520
+ 2. Every UI element that matches a library component (Button, Input, Tag, etc.) uses that component — not raw HTML?
521
+ 3. All colors use `var(--...)` tokens, not Tailwind literal color utilities (`bg-blue-500`, `text-gray-900`, etc.)?
522
+ 4. Only one `Button variant="primary"` per context?
523
+ 5. h1 / h2 / h3 use `text-[var(--foreground)]` (or `text-[var(--on-primary-action)]` on dark surfaces)?
567
524
 
568
- - Keep chip labels short and single-purpose. Long labels cause overflow.
569
- - Do not override padding, gap, radius, height, element order, or colors.
570
- - All chips in the same group must share the same size and spacing.
571
- - Use Chip only for groups with 2+ options. For single options, use a toggle or checkbox instead.
525
+ If any answer is "no", fix it before shipping.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sarunyu/system-one",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
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": [