@lucasvu/scope-ui 0.1.0 → 0.1.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.
@@ -0,0 +1,1039 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/ai-manifest.ts
8
+ var uiAiManifest = {
9
+ packageName: "@lucasvu/scope-ui",
10
+ styleImport: "@lucasvu/scope-ui/styles.css",
11
+ rules: [
12
+ "Import the stylesheet once at the app entry before rendering any component.",
13
+ "If the project declares an approved theme preset in AGENTS.md or ui-theme.css, stay inside that preset and do not invent a second palette.",
14
+ "Collect the shell + screen brief first: layout preset, workspace label, route url, sidebar items, active sidebar item, page title, tabs/actions, and the page-specific content schema.",
15
+ "If the project ships a generated layout preset file, keep the shell aligned with it instead of inventing a new page frame.",
16
+ "Prefer the canonical component for each intent instead of mixing legacy MainFe components.",
17
+ "Use Input/Textarea/Select label props directly for simple fields; use Field only to wrap custom controls or grouped content.",
18
+ "Use Select for small fixed option lists, SearchableSelect for larger local lists, Combobox for type-and-pick flows, and AsyncCombobox for remote search.",
19
+ 'Always provide a stable rowKey to DataTable and use sortMode="server" when sorting happens on the backend.',
20
+ "Prefer Card as the outer layout section and keep alerts, stats, and tables inside CardContent when building dashboards or forms.",
21
+ "Do not import MainFe components unless the target explicitly asks for legacy main-fe styling."
22
+ ],
23
+ components: [
24
+ {
25
+ name: "Primary action",
26
+ importName: "Button",
27
+ purpose: "Render the main clickable action.",
28
+ useWhen: [
29
+ "Submitting a form",
30
+ "Triggering create/save/confirm flows",
31
+ "Need loading or block width support"
32
+ ],
33
+ avoidWhen: [
34
+ "Pure navigation text links without button semantics"
35
+ ],
36
+ props: [
37
+ { name: "variant", type: "'default' | 'secondary' | 'ghost' | 'outline' | 'destructive' | 'link' | 'confirm' | 'create'", description: "Chooses the visual emphasis." },
38
+ { name: "size", type: "'sm' | 'md' | 'lg' | 'icon'", description: "Chooses the button size." },
39
+ { name: "block", type: "boolean", description: "Expands the button to full width." },
40
+ { name: "loading", type: "boolean", description: "Shows a spinner and disables interaction." }
41
+ ],
42
+ example: "<Button variant='create' block>Save changes</Button>",
43
+ related: ["Alert", "Card"]
44
+ },
45
+ {
46
+ name: "Single-line text input",
47
+ importName: "Input",
48
+ purpose: "Capture short text, email, password, number-like strings, or search text.",
49
+ useWhen: [
50
+ "A single text value is needed",
51
+ "You need label/helper/error handling built in",
52
+ "You need prefix, suffix, or password toggle support"
53
+ ],
54
+ props: [
55
+ { name: "label", type: "ReactNode", description: "Field label rendered above the control." },
56
+ { name: "helperText", type: "ReactNode", description: "Supplemental guidance shown below." },
57
+ { name: "errorMessage", type: "ReactNode", description: "Validation message shown below in error state." },
58
+ { name: "prefix", type: "ReactNode", description: "Visual prefix inside the field." },
59
+ { name: "suffix", type: "ReactNode", description: "Visual suffix inside the field." }
60
+ ],
61
+ example: "<Input label='Email' type='email' placeholder='you@example.com' />",
62
+ related: ["Textarea", "NumericInput", "Field"]
63
+ },
64
+ {
65
+ name: "Multiline text input",
66
+ importName: "Textarea",
67
+ purpose: "Capture longer free-form content.",
68
+ useWhen: [
69
+ "The user needs multiple lines of text",
70
+ "A description, note, or comment field is needed"
71
+ ],
72
+ props: [
73
+ { name: "label", type: "ReactNode", description: "Field label rendered above the textarea." },
74
+ { name: "rows", type: "number", description: "Controls initial textarea height." },
75
+ { name: "helperText", type: "ReactNode", description: "Supplemental guidance shown below." },
76
+ { name: "errorMessage", type: "ReactNode", description: "Validation message shown below in error state." }
77
+ ],
78
+ example: "<Textarea label='Description' rows={5} placeholder='Write the details...' />",
79
+ related: ["Input", "Field"]
80
+ },
81
+ {
82
+ name: "Single select",
83
+ importName: "Select",
84
+ purpose: "Pick one value from a small fixed option set.",
85
+ useWhen: [
86
+ "Fewer options and no search is required",
87
+ "A simple dropdown is enough"
88
+ ],
89
+ props: [
90
+ { name: "options", type: "SelectOption[]", required: true, description: "List of selectable options." },
91
+ { name: "label", type: "ReactNode", description: "Field label rendered above the trigger." },
92
+ { name: "placeholder", type: "string", description: "Placeholder shown when no value is selected." },
93
+ { name: "onChange", type: "(value) => void", description: "Returns the original option value type." }
94
+ ],
95
+ example: "<Select label='Plan' options={[{ label: 'Starter', value: 'starter' }]} />",
96
+ related: ["SearchableSelect", "Combobox"]
97
+ },
98
+ {
99
+ name: "Searchable local select",
100
+ importName: "SearchableSelect",
101
+ purpose: "Pick one value from a larger in-memory list with search.",
102
+ useWhen: [
103
+ "More than a handful of options exist",
104
+ "Local filtering is enough"
105
+ ],
106
+ props: [
107
+ { name: "options", type: "SearchableSelectOption[]", required: true, description: "List of selectable options." },
108
+ { name: "searchPlaceholder", type: "string", description: "Placeholder inside the search field." },
109
+ { name: "onSearch", type: "(query) => void", description: "Receives the local search query." }
110
+ ],
111
+ example: "<SearchableSelect label='Country' searchPlaceholder='Search country' options={countries} />",
112
+ related: ["Select", "Combobox", "AsyncCombobox"]
113
+ },
114
+ {
115
+ name: "Type and pick",
116
+ importName: "Combobox",
117
+ purpose: "Let the user type and choose from a filtered list.",
118
+ useWhen: [
119
+ "A command-palette-like input is needed",
120
+ "The typed text should stay visible in the input"
121
+ ],
122
+ props: [
123
+ { name: "options", type: "ComboboxOption[]", required: true, description: "List of options to filter." },
124
+ { name: "value", type: "string | number", description: "Selected value." },
125
+ { name: "onChange", type: "(value) => void", description: "Selected value callback." }
126
+ ],
127
+ example: "<Combobox label='Assignee' options={users} value={assigneeId} onChange={setAssigneeId} />",
128
+ related: ["SearchableSelect", "AsyncCombobox"]
129
+ },
130
+ {
131
+ name: "Remote search combobox",
132
+ importName: "AsyncCombobox",
133
+ purpose: "Search and pick from server-backed data.",
134
+ useWhen: [
135
+ "Options are loaded from an API",
136
+ "Infinite scroll or async fetching is needed"
137
+ ],
138
+ props: [
139
+ { name: "options", type: "ComboboxOption[]", required: true, description: "Currently loaded options." },
140
+ { name: "loading", type: "boolean", description: "Shows async loading state." },
141
+ { name: "onSearch", type: "(query) => void", required: true, description: "Triggers remote search." },
142
+ { name: "onLoadMore", type: "() => void", description: "Loads more options when scrolling near the end." }
143
+ ],
144
+ example: "<AsyncCombobox label='User' options={options} loading={loading} onSearch={searchUsers} />",
145
+ related: ["Combobox", "SearchableSelect"]
146
+ },
147
+ {
148
+ name: "Multiple selection",
149
+ importName: "MultiSelect",
150
+ purpose: "Choose many values from a searchable list.",
151
+ useWhen: [
152
+ "The user can pick multiple tags, users, or filters"
153
+ ],
154
+ props: [
155
+ { name: "options", type: "MultiSelectOption[]", required: true, description: "List of selectable options." },
156
+ { name: "value", type: "Array<string | number>", description: "Selected values." },
157
+ { name: "onChange", type: "(values) => void", description: "Selected values callback." }
158
+ ],
159
+ example: "<MultiSelect label='Tags' options={tagOptions} value={tags} onChange={setTags} />",
160
+ related: ["Select", "SearchableSelect"]
161
+ },
162
+ {
163
+ name: "Structured field wrapper",
164
+ importName: "Field",
165
+ purpose: "Add a shared label/helper/error wrapper around custom content.",
166
+ useWhen: [
167
+ "Wrapping a custom control that does not expose label props",
168
+ "Grouping multiple controls under one heading"
169
+ ],
170
+ avoidWhen: [
171
+ "A built-in form control already renders its own label and helper text"
172
+ ],
173
+ props: [
174
+ { name: "label", type: "ReactNode", description: "Field label rendered above children." },
175
+ { name: "helperText", type: "ReactNode", description: "Helper text below children." },
176
+ { name: "errorMessage", type: "ReactNode", description: "Error text below children." },
177
+ { name: "htmlFor", type: "string", description: "Associates the label with the wrapped control id." }
178
+ ],
179
+ example: "<Field label='Schedule' htmlFor='range'><DateRangePicker id='range' /></Field>",
180
+ related: ["Input", "Textarea", "Select"]
181
+ },
182
+ {
183
+ name: "Data table",
184
+ importName: "DataTable",
185
+ purpose: "Render sortable tabular data with optional selection, actions, loading, and pagination.",
186
+ useWhen: [
187
+ "Displaying records in rows and columns",
188
+ "Sort, pagination, selection, or row actions are required"
189
+ ],
190
+ props: [
191
+ { name: "columns", type: "DataTableColumn<T>[]", required: true, description: "Column definitions." },
192
+ { name: "data", type: "T[]", required: true, description: "Rows to render." },
193
+ { name: "rowKey", type: "keyof T | ((record: T) => string | number)", required: true, description: "Stable key for each row." },
194
+ { name: "pagination", type: "DataTablePagination", description: "Enables pagination controls." },
195
+ { name: "sortMode", type: "'client' | 'server'", description: "Choose client or backend sorting." }
196
+ ],
197
+ example: "<DataTable rowKey='id' columns={columns} data={rows} pagination={pagination} />",
198
+ related: ["Card", "Pagination", "Loading"]
199
+ },
200
+ {
201
+ name: "Status message",
202
+ importName: "Alert",
203
+ purpose: "Communicate neutral, success, warning, or danger states.",
204
+ useWhen: [
205
+ "You need a visible status or validation banner"
206
+ ],
207
+ props: [
208
+ { name: "tone", type: "'neutral' | 'info' | 'success' | 'warning' | 'danger'", description: "Semantic tone mapping." },
209
+ { name: "title", type: "ReactNode", description: "Alert headline." },
210
+ { name: "description", type: "ReactNode", description: "Supporting message." }
211
+ ],
212
+ example: "<Alert tone='warning' title='Missing information' description='Please fill all required fields.' />",
213
+ related: ["Loading", "Card"]
214
+ },
215
+ {
216
+ name: "Layout section",
217
+ importName: "Card",
218
+ purpose: "Wrap a coherent chunk of UI in a bordered surface.",
219
+ useWhen: [
220
+ "Building forms, dashboards, settings sections, or summary panels"
221
+ ],
222
+ props: [
223
+ { name: "className", type: "string", description: "Adds layout-specific spacing or width." }
224
+ ],
225
+ example: "<Card><CardHeader><CardTitle>Account</CardTitle></CardHeader><CardContent>...</CardContent></Card>",
226
+ related: ["CardHeader", "CardTitle", "CardContent", "CardFooter"]
227
+ }
228
+ ]
229
+ };
230
+
231
+ // src/screen-blueprint.ts
232
+ var uiScreenBriefFields = [
233
+ {
234
+ id: "layoutPreset",
235
+ label: "Layout preset",
236
+ description: "Shared shell preset id that locks the sidebar/topbar/page-card structure.",
237
+ required: false,
238
+ appliesTo: ["all"],
239
+ example: "workspace-admin-v1"
240
+ },
241
+ {
242
+ id: "workspaceLabel",
243
+ label: "Workspace label",
244
+ description: "Short label shown in the sidebar or workspace topbar.",
245
+ required: false,
246
+ appliesTo: ["all"],
247
+ example: "Workspace"
248
+ },
249
+ {
250
+ id: "timezoneLabel",
251
+ label: "Timezone/meta label",
252
+ description: "Short meta label shown in the top-right shell area, such as a timezone or environment.",
253
+ required: false,
254
+ appliesTo: ["all"],
255
+ example: "UTC+07:00"
256
+ },
257
+ {
258
+ id: "pageKind",
259
+ label: "Page kind",
260
+ description: "Choose the base recipe for the screen.",
261
+ required: true,
262
+ appliesTo: ["all"],
263
+ example: "list"
264
+ },
265
+ {
266
+ id: "routeUrl",
267
+ label: "Route URL",
268
+ description: "Final route for the screen or feature entry.",
269
+ required: true,
270
+ appliesTo: ["all"],
271
+ example: "/admin/users"
272
+ },
273
+ {
274
+ id: "sidebarItems",
275
+ label: "Sidebar items",
276
+ description: "Sidebar entries with at least id, title, and href so the shell stays consistent.",
277
+ required: true,
278
+ appliesTo: ["all"],
279
+ example: '[{ id: "users", title: "Users", href: "/admin/users" }]'
280
+ },
281
+ {
282
+ id: "activeSidebarItemId",
283
+ label: "Active sidebar item id",
284
+ description: "The sidebar item that should appear active on this screen.",
285
+ required: true,
286
+ appliesTo: ["all"],
287
+ example: "users"
288
+ },
289
+ {
290
+ id: "breadcrumbs",
291
+ label: "Breadcrumbs",
292
+ description: "Ordered breadcrumb items for the page header.",
293
+ required: false,
294
+ appliesTo: ["all"],
295
+ example: '[{ label: "Admin", href: "/admin" }, { label: "Users", current: true }]'
296
+ },
297
+ {
298
+ id: "pageTitle",
299
+ label: "Page title",
300
+ description: "Main page heading shown in the content header.",
301
+ required: true,
302
+ appliesTo: ["all"],
303
+ example: "Users"
304
+ },
305
+ {
306
+ id: "pageSubtitle",
307
+ label: "Page subtitle",
308
+ description: "Short supporting text under the title.",
309
+ required: false,
310
+ appliesTo: ["all"],
311
+ example: "Manage internal user accounts and permissions."
312
+ },
313
+ {
314
+ id: "pageTabs",
315
+ label: "Page tabs",
316
+ description: "Optional segmented tabs rendered under the intro card.",
317
+ required: false,
318
+ appliesTo: ["all"],
319
+ example: '[{ value: "overview", label: "Overview" }, { value: "coupons", label: "Coupons" }]'
320
+ },
321
+ {
322
+ id: "activePageTab",
323
+ label: "Active page tab",
324
+ description: "The selected segmented tab value when tabs are present.",
325
+ required: false,
326
+ appliesTo: ["all"],
327
+ example: "coupons"
328
+ },
329
+ {
330
+ id: "primaryAction",
331
+ label: "Primary action",
332
+ description: "The highest-emphasis action shown in the page header.",
333
+ required: false,
334
+ appliesTo: ["all"],
335
+ example: '{ label: "Create user", href: "/admin/users/create" }'
336
+ },
337
+ {
338
+ id: "secondaryActions",
339
+ label: "Secondary actions",
340
+ description: "Additional header actions such as export, refresh, or cancel.",
341
+ required: false,
342
+ appliesTo: ["all"],
343
+ example: '[{ label: "Export" }, { label: "Refresh" }]'
344
+ },
345
+ {
346
+ id: "summaryStats",
347
+ label: "Summary stats",
348
+ description: "Top-line numbers for dashboard or overview screens.",
349
+ required: false,
350
+ appliesTo: ["dashboard", "detail"],
351
+ example: '[{ label: "Active users", value: "1,204", trend: "up", delta: "+12%" }]'
352
+ },
353
+ {
354
+ id: "filters",
355
+ label: "Filters",
356
+ description: "Filter bar controls shown above list or dashboard content.",
357
+ required: false,
358
+ appliesTo: ["list", "dashboard"],
359
+ example: '[{ type: "search", label: "Search user" }, { type: "select", label: "Role" }]'
360
+ },
361
+ {
362
+ id: "searchPlaceholder",
363
+ label: "Search placeholder",
364
+ description: "Placeholder text for the main toolbar search input when the shell includes one.",
365
+ required: false,
366
+ appliesTo: ["list", "dashboard"],
367
+ example: "Search by coupon code"
368
+ },
369
+ {
370
+ id: "tableColumns",
371
+ label: "Table columns",
372
+ description: "Columns for a list table, including label and render intent.",
373
+ required: false,
374
+ appliesTo: ["list", "detail", "dashboard"],
375
+ example: '[{ key: "name", title: "Name" }, { key: "role", title: "Role" }]'
376
+ },
377
+ {
378
+ id: "rowActions",
379
+ label: "Row actions",
380
+ description: "Per-row actions for table screens.",
381
+ required: false,
382
+ appliesTo: ["list", "detail"],
383
+ example: '[{ label: "Edit" }, { label: "Reset password" }]'
384
+ },
385
+ {
386
+ id: "formSections",
387
+ label: "Form sections",
388
+ description: "Grouped form sections and the fields inside each section.",
389
+ required: false,
390
+ appliesTo: ["form"],
391
+ example: '[{ title: "Basic info", fields: ["name", "email", "role"] }]'
392
+ },
393
+ {
394
+ id: "fields",
395
+ label: "Fields",
396
+ description: "Detailed field list with type, label, required state, and helper text.",
397
+ required: false,
398
+ appliesTo: ["form", "detail"],
399
+ example: '[{ name: "email", type: "input", label: "Email", required: true }]'
400
+ },
401
+ {
402
+ id: "detailSections",
403
+ label: "Detail sections",
404
+ description: "Cards or sections for detail pages such as profile, activity, permissions, or related records.",
405
+ required: false,
406
+ appliesTo: ["detail"],
407
+ example: '[{ title: "Profile" }, { title: "Recent activity" }]'
408
+ },
409
+ {
410
+ id: "emptyState",
411
+ label: "Empty state",
412
+ description: "Fallback title, description, and action when there is no data.",
413
+ required: false,
414
+ appliesTo: ["list", "dashboard", "detail"],
415
+ example: '{ title: "No users yet", description: "Create the first user to get started." }'
416
+ },
417
+ {
418
+ id: "permissions",
419
+ label: "Permissions",
420
+ description: "Permission keys that affect sidebar visibility or action availability.",
421
+ required: false,
422
+ appliesTo: ["all"],
423
+ example: '["user.read", "user.write"]'
424
+ }
425
+ ];
426
+ var uiScreenBlueprint = {
427
+ pageKinds: [
428
+ {
429
+ id: "list",
430
+ label: "List page",
431
+ frame: [
432
+ "Sidebar shell",
433
+ "Workspace topbar",
434
+ "Page intro card with Breadcrumb and PageTitle",
435
+ "Optional segmented tabs",
436
+ "Toolbar card",
437
+ "Optional stats",
438
+ "DataTable card"
439
+ ]
440
+ },
441
+ {
442
+ id: "form",
443
+ label: "Form page",
444
+ frame: [
445
+ "Sidebar shell",
446
+ "Workspace topbar",
447
+ "Page intro card with Breadcrumb and PageTitle",
448
+ "Optional segmented tabs",
449
+ "Header actions",
450
+ "Sectioned form cards",
451
+ "Bottom action row"
452
+ ]
453
+ },
454
+ {
455
+ id: "detail",
456
+ label: "Detail page",
457
+ frame: [
458
+ "Sidebar shell",
459
+ "Workspace topbar",
460
+ "Page intro card with Breadcrumb and PageTitle",
461
+ "Optional segmented tabs",
462
+ "PageTitle with status/actions",
463
+ "Summary stats or metadata card",
464
+ "Detail cards and related tables"
465
+ ]
466
+ },
467
+ {
468
+ id: "dashboard",
469
+ label: "Dashboard page",
470
+ frame: [
471
+ "Sidebar shell",
472
+ "Workspace topbar",
473
+ "Page intro card with Breadcrumb and PageTitle",
474
+ "Optional segmented tabs",
475
+ "Toolbar card with actions",
476
+ "Stat cards row",
477
+ "Main insight cards and tables"
478
+ ]
479
+ }
480
+ ],
481
+ workflow: [
482
+ "Collect the shell + screen brief before coding. Do not invent layout presets, workspace labels, sidebar items, route urls, tabs, filters, fields, or table columns if the brief is missing.",
483
+ "Use Sidebar plus the workspace topbar and intro card to lock the shell before building the page body.",
484
+ "Choose only canonical root exports from @lucasvu/scope-ui.",
485
+ "Use Card and ui-grid utilities to create the frame first, then place controls and data components inside.",
486
+ "Keep all palette, radius, surface, and shadow decisions in the shared ui-theme.css preset file.",
487
+ "End with a consistency check against the selected preset and the screen brief."
488
+ ],
489
+ briefFields: uiScreenBriefFields
490
+ };
491
+
492
+ // src/theme-contract.ts
493
+ var uiThemePresets = [
494
+ {
495
+ id: "ocean",
496
+ label: "Ocean Glass",
497
+ description: "Blue-cyan preset with clean glass surfaces. Matches the current default visual language.",
498
+ recommendedFor: [
499
+ "Admin dashboards",
500
+ "SaaS CRUD screens",
501
+ "Projects that want the existing package look"
502
+ ],
503
+ tokens: {
504
+ light: {
505
+ "--tw-background": "0 0% 100%",
506
+ "--tw-foreground": "222.2 47.4% 11.2%",
507
+ "--tw-primary": "221.2 83.2% 53.3%",
508
+ "--tw-accent": "199 89% 48%",
509
+ "--tw-success": "142.1 76.2% 36.3%",
510
+ "--tw-destructive": "0 84.2% 60.2%",
511
+ "--tw-border": "214.3 31.8% 91.4%",
512
+ "--radius": "0.75rem",
513
+ "--surface": "rgba(255, 255, 255, 0.92)",
514
+ "--surface-strong": "rgba(241, 245, 249, 0.96)",
515
+ "--grey": "rgba(248, 250, 252, 0.96)",
516
+ "--grey-strong": "rgba(241, 245, 249, 0.98)",
517
+ "--shadow-sm": "0 10px 28px -18px rgba(15, 23, 42, 0.22)",
518
+ "--shadow": "0 24px 60px -28px rgba(15, 23, 42, 0.3)",
519
+ "--primary-grad-from": "199 89% 48%",
520
+ "--primary-grad-to": "221.2 83.2% 53.3%"
521
+ },
522
+ dark: {
523
+ "--tw-background": "222.2 84% 4.9%",
524
+ "--tw-foreground": "210 40% 98%",
525
+ "--tw-primary": "217.2 91.2% 59.8%",
526
+ "--tw-accent": "199 89% 48%",
527
+ "--tw-success": "142.1 70.6% 45.3%",
528
+ "--tw-destructive": "0 62.8% 30.6%",
529
+ "--tw-border": "217.2 32.6% 17.5%",
530
+ "--radius": "0.75rem",
531
+ "--surface": "rgba(15, 23, 42, 0.78)",
532
+ "--surface-strong": "rgba(30, 41, 59, 0.92)",
533
+ "--grey": "rgba(30, 41, 59, 0.88)",
534
+ "--grey-strong": "rgba(51, 65, 85, 0.92)",
535
+ "--shadow-sm": "0 16px 32px -18px rgba(2, 6, 23, 0.48)",
536
+ "--shadow": "0 28px 80px -32px rgba(2, 6, 23, 0.7)",
537
+ "--primary-grad-from": "198.6 88.7% 48.4%",
538
+ "--primary-grad-to": "221.2 83.2% 53.3%"
539
+ }
540
+ }
541
+ },
542
+ {
543
+ id: "sunset",
544
+ label: "Sunset Ember",
545
+ description: "Warm orange-coral preset with softer cream surfaces for branded landing or growth products.",
546
+ recommendedFor: [
547
+ "Growth products",
548
+ "Commerce backoffices",
549
+ "Projects that want a warmer visual tone"
550
+ ],
551
+ tokens: {
552
+ light: {
553
+ "--tw-background": "30 100% 98%",
554
+ "--tw-foreground": "20 24% 14%",
555
+ "--tw-primary": "14 90% 56%",
556
+ "--tw-accent": "29 100% 58%",
557
+ "--tw-success": "145 63% 38%",
558
+ "--tw-destructive": "0 78% 58%",
559
+ "--tw-border": "24 45% 89%",
560
+ "--radius": "0.85rem",
561
+ "--surface": "rgba(255, 247, 240, 0.92)",
562
+ "--surface-strong": "rgba(255, 237, 223, 0.96)",
563
+ "--grey": "rgba(255, 243, 231, 0.96)",
564
+ "--grey-strong": "rgba(255, 232, 214, 0.98)",
565
+ "--shadow-sm": "0 12px 30px -18px rgba(194, 65, 12, 0.18)",
566
+ "--shadow": "0 28px 64px -30px rgba(154, 52, 18, 0.24)",
567
+ "--primary-grad-from": "29 100% 58%",
568
+ "--primary-grad-to": "14 90% 56%"
569
+ },
570
+ dark: {
571
+ "--tw-background": "20 24% 8%",
572
+ "--tw-foreground": "40 33% 96%",
573
+ "--tw-primary": "18 100% 62%",
574
+ "--tw-accent": "35 100% 58%",
575
+ "--tw-success": "145 60% 47%",
576
+ "--tw-destructive": "0 73% 52%",
577
+ "--tw-border": "18 24% 22%",
578
+ "--radius": "0.85rem",
579
+ "--surface": "rgba(41, 24, 18, 0.84)",
580
+ "--surface-strong": "rgba(59, 34, 24, 0.9)",
581
+ "--grey": "rgba(70, 42, 29, 0.88)",
582
+ "--grey-strong": "rgba(92, 54, 38, 0.9)",
583
+ "--shadow-sm": "0 18px 36px -20px rgba(67, 20, 7, 0.48)",
584
+ "--shadow": "0 30px 84px -34px rgba(67, 20, 7, 0.62)",
585
+ "--primary-grad-from": "35 100% 58%",
586
+ "--primary-grad-to": "18 100% 62%"
587
+ }
588
+ }
589
+ },
590
+ {
591
+ id: "forest",
592
+ label: "Forest Mist",
593
+ description: "Emerald-teal preset with soft botanical surfaces for calmer productivity products.",
594
+ recommendedFor: [
595
+ "Operations tools",
596
+ "Internal platforms",
597
+ "Products that want a calmer green tone"
598
+ ],
599
+ tokens: {
600
+ light: {
601
+ "--tw-background": "138 40% 98%",
602
+ "--tw-foreground": "160 25% 14%",
603
+ "--tw-primary": "158 64% 40%",
604
+ "--tw-accent": "173 58% 44%",
605
+ "--tw-success": "145 63% 36%",
606
+ "--tw-destructive": "0 78% 58%",
607
+ "--tw-border": "143 21% 88%",
608
+ "--radius": "0.8rem",
609
+ "--surface": "rgba(245, 252, 249, 0.92)",
610
+ "--surface-strong": "rgba(232, 245, 239, 0.96)",
611
+ "--grey": "rgba(240, 248, 244, 0.96)",
612
+ "--grey-strong": "rgba(225, 240, 232, 0.98)",
613
+ "--shadow-sm": "0 12px 28px -18px rgba(5, 86, 66, 0.18)",
614
+ "--shadow": "0 26px 62px -30px rgba(6, 78, 59, 0.24)",
615
+ "--primary-grad-from": "173 58% 44%",
616
+ "--primary-grad-to": "158 64% 40%"
617
+ },
618
+ dark: {
619
+ "--tw-background": "164 35% 8%",
620
+ "--tw-foreground": "144 35% 96%",
621
+ "--tw-primary": "160 70% 46%",
622
+ "--tw-accent": "174 72% 45%",
623
+ "--tw-success": "145 68% 46%",
624
+ "--tw-destructive": "0 70% 52%",
625
+ "--tw-border": "160 20% 20%",
626
+ "--radius": "0.8rem",
627
+ "--surface": "rgba(16, 36, 31, 0.84)",
628
+ "--surface-strong": "rgba(21, 51, 44, 0.9)",
629
+ "--grey": "rgba(24, 61, 52, 0.88)",
630
+ "--grey-strong": "rgba(31, 77, 65, 0.9)",
631
+ "--shadow-sm": "0 18px 34px -20px rgba(1, 44, 34, 0.48)",
632
+ "--shadow": "0 30px 82px -34px rgba(1, 44, 34, 0.6)",
633
+ "--primary-grad-from": "174 72% 45%",
634
+ "--primary-grad-to": "160 70% 46%"
635
+ }
636
+ }
637
+ },
638
+ {
639
+ id: "graphite",
640
+ label: "Graphite Pulse",
641
+ description: "Neutral slate preset with restrained blue accents for products that need a steadier enterprise tone.",
642
+ recommendedFor: [
643
+ "Enterprise admin panels",
644
+ "B2B internal tools",
645
+ "Projects that want a more neutral interface"
646
+ ],
647
+ tokens: {
648
+ light: {
649
+ "--tw-background": "220 18% 97%",
650
+ "--tw-foreground": "222 24% 14%",
651
+ "--tw-primary": "221 24% 32%",
652
+ "--tw-accent": "198 83% 44%",
653
+ "--tw-success": "160 56% 38%",
654
+ "--tw-destructive": "0 72% 54%",
655
+ "--tw-border": "218 17% 86%",
656
+ "--radius": "0.7rem",
657
+ "--surface": "rgba(248, 250, 252, 0.94)",
658
+ "--surface-strong": "rgba(226, 232, 240, 0.96)",
659
+ "--grey": "rgba(241, 245, 249, 0.98)",
660
+ "--grey-strong": "rgba(226, 232, 240, 0.99)",
661
+ "--shadow-sm": "0 12px 30px -20px rgba(15, 23, 42, 0.18)",
662
+ "--shadow": "0 28px 66px -30px rgba(15, 23, 42, 0.24)",
663
+ "--primary-grad-from": "198 83% 44%",
664
+ "--primary-grad-to": "221 24% 32%"
665
+ },
666
+ dark: {
667
+ "--tw-background": "222 32% 8%",
668
+ "--tw-foreground": "210 25% 96%",
669
+ "--tw-primary": "210 24% 82%",
670
+ "--tw-accent": "192 92% 52%",
671
+ "--tw-success": "158 64% 45%",
672
+ "--tw-destructive": "0 72% 56%",
673
+ "--tw-border": "217 19% 24%",
674
+ "--radius": "0.7rem",
675
+ "--surface": "rgba(15, 23, 42, 0.82)",
676
+ "--surface-strong": "rgba(30, 41, 59, 0.92)",
677
+ "--grey": "rgba(30, 41, 59, 0.9)",
678
+ "--grey-strong": "rgba(51, 65, 85, 0.92)",
679
+ "--shadow-sm": "0 18px 38px -22px rgba(2, 6, 23, 0.48)",
680
+ "--shadow": "0 32px 88px -34px rgba(2, 6, 23, 0.68)",
681
+ "--primary-grad-from": "192 92% 52%",
682
+ "--primary-grad-to": "210 24% 82%"
683
+ }
684
+ }
685
+ }
686
+ ];
687
+ var uiDefaultThemePreset = uiThemePresets[0];
688
+ function createThemePresetCssExample(themePreset) {
689
+ const renderBlock = (tokens) => Object.entries(tokens).map(([token, value]) => ` ${token}: ${value};`).join("\n");
690
+ return `:root {
691
+ ${renderBlock(themePreset.tokens.light)}
692
+ }
693
+
694
+ .dark,
695
+ [data-ui-theme='dark'] {
696
+ ${renderBlock(themePreset.tokens.dark)}
697
+ }`;
698
+ }
699
+ var uiThemeContract = {
700
+ packageName: "@lucasvu/scope-ui",
701
+ importOrder: [
702
+ "import '@lucasvu/scope-ui/styles.css'",
703
+ "import './styles/ui-theme.css'"
704
+ ],
705
+ selectors: {
706
+ light: [":root", "[data-ui-theme='light']"],
707
+ dark: [".dark", "[data-ui-theme='dark']"]
708
+ },
709
+ overrideFile: "src/styles/ui-theme.css",
710
+ defaultPreset: uiDefaultThemePreset.id,
711
+ presets: uiThemePresets,
712
+ rules: [
713
+ "Import the package stylesheet before your project theme override stylesheet.",
714
+ "Choose one approved preset and keep the entire project on that preset instead of mixing palettes page by page.",
715
+ "Override tokens in one shared theme file instead of scattering colors across components.",
716
+ "Use :root for the default light theme and .dark or [data-ui-theme='dark'] for dark theme.",
717
+ "Only override tokens declared here. Do not patch component internals unless you are extending the library.",
718
+ "For AI-generated screens, choose one theme source of truth and keep component code token-driven."
719
+ ],
720
+ tokens: [
721
+ {
722
+ name: "--tw-background",
723
+ description: "Base app background in HSL triplet format.",
724
+ defaultLight: "0 0% 100%",
725
+ defaultDark: "222.2 84% 4.9%",
726
+ usedBy: ["body background", "Input fallback", "page background"]
727
+ },
728
+ {
729
+ name: "--tw-foreground",
730
+ description: "Primary text color in HSL triplet format.",
731
+ defaultLight: "222.2 47.4% 11.2%",
732
+ defaultDark: "210 40% 98%",
733
+ usedBy: ["Button", "Input", "Card", "Typography"]
734
+ },
735
+ {
736
+ name: "--tw-primary",
737
+ description: "Primary brand color in HSL triplet format.",
738
+ defaultLight: "221.2 83.2% 53.3%",
739
+ defaultDark: "217.2 91.2% 59.8%",
740
+ usedBy: ["Button", "focus rings", "brand emphasis"]
741
+ },
742
+ {
743
+ name: "--tw-accent",
744
+ description: "Accent color in HSL triplet format.",
745
+ defaultLight: "199 89% 48%",
746
+ defaultDark: "199 89% 48%",
747
+ usedBy: ["Tabs", "checkbox accent", "highlight states"]
748
+ },
749
+ {
750
+ name: "--tw-success",
751
+ description: "Success state color in HSL triplet format.",
752
+ defaultLight: "142.1 76.2% 36.3%",
753
+ defaultDark: "142.1 70.6% 45.3%",
754
+ usedBy: ["Alert", "confirm buttons", "status text"]
755
+ },
756
+ {
757
+ name: "--tw-destructive",
758
+ description: "Danger state color in HSL triplet format.",
759
+ defaultLight: "0 84.2% 60.2%",
760
+ defaultDark: "0 62.8% 30.6%",
761
+ usedBy: ["Alert", "destructive buttons", "error states"]
762
+ },
763
+ {
764
+ name: "--tw-border",
765
+ description: "Border color in HSL triplet format.",
766
+ defaultLight: "214.3 31.8% 91.4%",
767
+ defaultDark: "217.2 32.6% 17.5%",
768
+ usedBy: ["Input", "Select", "Card", "DataTable"]
769
+ },
770
+ {
771
+ name: "--radius",
772
+ description: "Shared component radius.",
773
+ defaultLight: "0.75rem",
774
+ defaultDark: "0.75rem",
775
+ usedBy: ["Button", "Input", "Card", "Tabs"]
776
+ },
777
+ {
778
+ name: "--surface",
779
+ description: "Default surface background as a CSS color.",
780
+ defaultLight: "rgba(255, 255, 255, 0.92)",
781
+ defaultDark: "rgba(15, 23, 42, 0.78)",
782
+ usedBy: ["Tabs", "Pagination", "glass surfaces"]
783
+ },
784
+ {
785
+ name: "--surface-strong",
786
+ description: "Stronger elevated surface as a CSS color.",
787
+ defaultLight: "rgba(241, 245, 249, 0.96)",
788
+ defaultDark: "rgba(30, 41, 59, 0.92)",
789
+ usedBy: ["Pagination hover", "raised surfaces"]
790
+ },
791
+ {
792
+ name: "--grey",
793
+ description: "Neutral table/header background as a CSS color.",
794
+ defaultLight: "rgba(248, 250, 252, 0.96)",
795
+ defaultDark: "rgba(30, 41, 59, 0.88)",
796
+ usedBy: ["DataTable header"]
797
+ },
798
+ {
799
+ name: "--grey-strong",
800
+ description: "Hover background for neutral rows as a CSS color.",
801
+ defaultLight: "rgba(241, 245, 249, 0.98)",
802
+ defaultDark: "rgba(51, 65, 85, 0.92)",
803
+ usedBy: ["DataTable row hover"]
804
+ },
805
+ {
806
+ name: "--shadow-sm",
807
+ description: "Small elevation shadow.",
808
+ defaultLight: "0 10px 28px -18px rgba(15, 23, 42, 0.22)",
809
+ defaultDark: "0 16px 32px -18px rgba(2, 6, 23, 0.48)",
810
+ usedBy: ["Tabs", "Pagination", "subtle elevation"]
811
+ },
812
+ {
813
+ name: "--shadow",
814
+ description: "Primary elevation shadow.",
815
+ defaultLight: "0 24px 60px -28px rgba(15, 23, 42, 0.3)",
816
+ defaultDark: "0 28px 80px -32px rgba(2, 6, 23, 0.7)",
817
+ usedBy: ["Card", "Button emphasis", "floating surfaces"]
818
+ },
819
+ {
820
+ name: "--text",
821
+ description: "Generic text color alias as a CSS color.",
822
+ defaultLight: "hsl(var(--tw-foreground))",
823
+ defaultDark: "hsl(var(--tw-foreground))",
824
+ usedBy: ["Pagination", "legacy utility surfaces"]
825
+ },
826
+ {
827
+ name: "--muted",
828
+ description: "Generic muted text color alias as a CSS color.",
829
+ defaultLight: "hsl(var(--tw-muted-foreground))",
830
+ defaultDark: "hsl(var(--tw-muted-foreground))",
831
+ usedBy: ["Pagination", "DataTable sort icon", "helper text"]
832
+ },
833
+ {
834
+ name: "--accent",
835
+ description: "Generic accent color alias as a CSS color.",
836
+ defaultLight: "hsl(var(--tw-accent))",
837
+ defaultDark: "hsl(var(--tw-accent))",
838
+ usedBy: ["DataTable checkbox accent", "focus accents"]
839
+ },
840
+ {
841
+ name: "--primary-grad-from",
842
+ description: "Gradient start color in HSL triplet format.",
843
+ defaultLight: "199 89% 48%",
844
+ defaultDark: "198.6 88.7% 48.4%",
845
+ usedBy: ["Button variant create"]
846
+ },
847
+ {
848
+ name: "--primary-grad-to",
849
+ description: "Gradient end color in HSL triplet format.",
850
+ defaultLight: "221.2 83.2% 53.3%",
851
+ defaultDark: "221.2 83.2% 53.3%",
852
+ usedBy: ["Button variant create"]
853
+ }
854
+ ],
855
+ exampleCss: createThemePresetCssExample(uiDefaultThemePreset)
856
+ };
857
+ var uiProjectAiRules = [
858
+ "Use @lucasvu/scope-ui as the default UI library for React projects, or @lucasvu/scope-ui/core for framework-agnostic theme/runtime usage.",
859
+ "Import @lucasvu/scope-ui/styles.css once at the app entry.",
860
+ "Import the project override theme file after the package stylesheet or bundled theme layer.",
861
+ "Stay inside the approved theme preset declared by the project and do not invent a second palette.",
862
+ "Read uiAiManifest to choose the correct component by intent before coding.",
863
+ "Read uiThemeContract and uiThemeLayerAssets before changing colors, shadows, radius, or theme behavior.",
864
+ "Prefer root exports and avoid MainFe unless the task explicitly targets a legacy screen."
865
+ ];
866
+
867
+ // src/theme-layer-assets.ts
868
+ var uiThemeLayerAssets = [
869
+ {
870
+ id: "default",
871
+ label: "Theme Default",
872
+ importPath: "@lucasvu/scope-ui/themes/theme-default.css",
873
+ activation: "global",
874
+ description: "Global default token layer close to the legacy shared theme baseline.",
875
+ recommendedFor: [
876
+ "Projects that want a direct global theme layer without extra setup",
877
+ "Legacy migrations"
878
+ ]
879
+ },
880
+ {
881
+ id: "new-main-fe",
882
+ label: "New Main FE",
883
+ importPath: "@lucasvu/scope-ui/themes/theme-ui-new-main-fe.css",
884
+ activation: "global",
885
+ description: "Global Argon-inspired shell layer that works immediately after import.",
886
+ recommendedFor: [
887
+ "Admin shells",
888
+ "Projects that want the current shared main-fe tone"
889
+ ]
890
+ },
891
+ {
892
+ id: "citrus-ink",
893
+ label: "Citrus Ink",
894
+ importPath: "@lucasvu/scope-ui/themes/theme-ui-citrus-ink.css",
895
+ activation: "data-client-theme",
896
+ clientThemeAttributeValue: "citrus-ink",
897
+ description: "High-contrast graphite theme with mint and citrus accents.",
898
+ recommendedFor: ["Brand-heavy dashboards"]
899
+ },
900
+ {
901
+ id: "facebook-blue",
902
+ label: "Facebook Blue",
903
+ importPath: "@lucasvu/scope-ui/themes/theme-ui-facebook-blue.css",
904
+ activation: "data-client-theme",
905
+ clientThemeAttributeValue: "facebook-blue",
906
+ description: "Blue-first neutral theme tuned around a familiar social palette.",
907
+ recommendedFor: ["Blue-branded products"]
908
+ },
909
+ {
910
+ id: "neo-slate",
911
+ label: "Neo Slate",
912
+ importPath: "@lucasvu/scope-ui/themes/theme-ui-neo-slate.css",
913
+ activation: "data-client-theme",
914
+ clientThemeAttributeValue: "neo-slate",
915
+ description: "Modern slate-indigo workspace theme with richer shell tokens.",
916
+ recommendedFor: ["Contemporary SaaS dashboards", "Modern admin shells"]
917
+ },
918
+ {
919
+ id: "siraya-brand",
920
+ label: "Siraya Brand Sunset",
921
+ importPath: "@lucasvu/scope-ui/themes/theme-ui-new-main-fe-sunset.css",
922
+ activation: "data-client-theme",
923
+ clientThemeAttributeValue: "siraya-brand",
924
+ description: "Warm branded shell with typography and page-slot overrides.",
925
+ recommendedFor: ["Brand-led marketing or hybrid product shells"]
926
+ },
927
+ {
928
+ id: "pure-white",
929
+ label: "Pure White",
930
+ importPath: "@lucasvu/scope-ui/themes/theme-ui-pure-white.css",
931
+ activation: "data-client-theme",
932
+ clientThemeAttributeValue: "pure-white",
933
+ description: "Clean white workspace theme with cobalt accents.",
934
+ recommendedFor: ["Minimalist backoffices"]
935
+ },
936
+ {
937
+ id: "siraya-vii-light",
938
+ label: "Siraya VII Light",
939
+ importPath: "@lucasvu/scope-ui/themes/theme-ui-siraya-vii-light.css",
940
+ activation: "data-client-theme",
941
+ clientThemeAttributeValue: "siraya-vii-light",
942
+ description: "Light brand system based on the Siraya VII palette.",
943
+ recommendedFor: ["Brand refresh experiments"]
944
+ },
945
+ {
946
+ id: "solstice-pop",
947
+ label: "Solstice Pop",
948
+ importPath: "@lucasvu/scope-ui/themes/theme-ui-solstice-pop.css",
949
+ activation: "data-client-theme",
950
+ clientThemeAttributeValue: "solstice-pop",
951
+ description: "Editorial warm-stone theme with stronger typography direction.",
952
+ recommendedFor: ["Content-heavy dashboards"]
953
+ },
954
+ {
955
+ id: "uat-aurora",
956
+ label: "UAT Aurora",
957
+ importPath: "@lucasvu/scope-ui/themes/theme-ui-uat-aurora.css",
958
+ activation: "data-client-theme",
959
+ clientThemeAttributeValue: "uat-aurora",
960
+ description: "Soft slate-blue experimental theme using the same token structure as sunset.",
961
+ recommendedFor: ["UAT or staging environments", "Blue-toned shells"]
962
+ },
963
+ {
964
+ id: "custom-crisp",
965
+ label: "Custom Crisp",
966
+ importPath: "@lucasvu/scope-ui/themes/theme-ui-custom-crisp.css",
967
+ activation: "data-client-theme",
968
+ clientThemeAttributeValue: "*",
969
+ description: "Cross-theme typography and control crispness overrides for client-theme-driven shells.",
970
+ recommendedFor: ["Attribute-scoped client themes that need sharper controls"]
971
+ }
972
+ ];
973
+
974
+ // src/color-mode.ts
975
+ var ROOT_COLOR_MODE_ATTRIBUTE = "data-ui-color-mode";
976
+ var ROOT_CLIENT_THEME_ATTRIBUTE = "data-client-theme";
977
+ var ROOT_DARK_CLASS = "dark";
978
+ var SYSTEM_DARK_MEDIA_QUERY = "(prefers-color-scheme: dark)";
979
+ var resolveColorModeMediaQuery = () => {
980
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
981
+ return null;
982
+ }
983
+ return window.matchMedia(SYSTEM_DARK_MEDIA_QUERY);
984
+ };
985
+ var isColorMode = (value) => value === "light" || value === "dark";
986
+ var getSystemColorMode = () => resolveColorModeMediaQuery()?.matches ? "dark" : "light";
987
+ var applyDocumentColorMode = (colorMode) => {
988
+ if (typeof document === "undefined") {
989
+ return;
990
+ }
991
+ const root = document.documentElement;
992
+ root.classList.toggle(ROOT_DARK_CLASS, colorMode === "dark");
993
+ root.setAttribute(ROOT_COLOR_MODE_ATTRIBUTE, colorMode);
994
+ root.style.colorScheme = colorMode;
995
+ };
996
+ var initializeDocumentColorMode = (colorMode = getSystemColorMode()) => {
997
+ applyDocumentColorMode(colorMode);
998
+ return colorMode;
999
+ };
1000
+ var subscribeToSystemColorMode = (listener) => {
1001
+ const mediaQuery = resolveColorModeMediaQuery();
1002
+ if (!mediaQuery) {
1003
+ return () => void 0;
1004
+ }
1005
+ const handleChange = (event) => {
1006
+ listener(event.matches ? "dark" : "light");
1007
+ };
1008
+ if (typeof mediaQuery.addEventListener === "function") {
1009
+ mediaQuery.addEventListener("change", handleChange);
1010
+ return () => mediaQuery.removeEventListener("change", handleChange);
1011
+ }
1012
+ mediaQuery.addListener(handleChange);
1013
+ return () => mediaQuery.removeListener(handleChange);
1014
+ };
1015
+ var syncDocumentColorModeToSystem = () => {
1016
+ const applySystemColorMode = (colorMode) => {
1017
+ applyDocumentColorMode(colorMode);
1018
+ };
1019
+ applySystemColorMode(getSystemColorMode());
1020
+ return subscribeToSystemColorMode(applySystemColorMode);
1021
+ };
1022
+ var applyDocumentClientTheme = (themeId) => {
1023
+ if (typeof document === "undefined") {
1024
+ return;
1025
+ }
1026
+ const root = document.documentElement;
1027
+ if (!themeId) {
1028
+ root.removeAttribute(ROOT_CLIENT_THEME_ATTRIBUTE);
1029
+ return;
1030
+ }
1031
+ root.setAttribute(ROOT_CLIENT_THEME_ATTRIBUTE, themeId);
1032
+ };
1033
+
1034
+ // src/lib/cn.ts
1035
+ function cn(...values) {
1036
+ return values.filter(Boolean).join(" ");
1037
+ }
1038
+
1039
+ export { ROOT_CLIENT_THEME_ATTRIBUTE, ROOT_COLOR_MODE_ATTRIBUTE, __export, applyDocumentClientTheme, applyDocumentColorMode, cn, getSystemColorMode, initializeDocumentColorMode, isColorMode, subscribeToSystemColorMode, syncDocumentColorModeToSystem, uiAiManifest, uiDefaultThemePreset, uiProjectAiRules, uiScreenBlueprint, uiScreenBriefFields, uiThemeContract, uiThemeLayerAssets, uiThemePresets };