@loworbitstudio/visor 1.5.1 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "0.4.0",
3
- "generated_at": "2026-06-04T02:06:36.185Z",
3
+ "generated_at": "2026-06-12T03:42:08.191Z",
4
4
  "components": {
5
5
  "accessibility-specimen": {
6
6
  "category": "specimen",
@@ -164,15 +164,16 @@
164
164
  "variant": [
165
165
  "default",
166
166
  "compact",
167
- "timeline"
167
+ "timeline",
168
+ "compact-3col"
168
169
  ]
169
170
  },
170
171
  "props": [
171
172
  {
172
173
  "name": "variant",
173
- "type": "'default' | 'compact' | 'timeline'",
174
+ "type": "'default' | 'compact' | 'timeline' | 'compact-3col'",
174
175
  "default": "default",
175
- "description": "Display mode. Default is padded rows with borders, compact is denser, timeline adds a connecting rail between leading indicators."
176
+ "description": "Display mode. Default is padded rows with borders, compact is denser, timeline adds a connecting rail between leading indicators, compact-3col pins timestamp to the leftmost column with [time][dot][text] layout."
176
177
  },
177
178
  {
178
179
  "name": "children",
@@ -182,6 +183,7 @@
182
183
  ],
183
184
  "dependencies": [],
184
185
  "tokens_used": [
186
+ "--af-time-col",
185
187
  "--border-default",
186
188
  "--border-muted",
187
189
  "--font-size-sm",
@@ -359,6 +361,9 @@
359
361
  "sm",
360
362
  "md",
361
363
  "lg"
364
+ ],
365
+ "case": [
366
+ "sentence"
362
367
  ]
363
368
  },
364
369
  "props": [
@@ -372,6 +377,17 @@
372
377
  "type": "\"sm\" | \"md\" | \"lg\"",
373
378
  "default": "\"md\"",
374
379
  "description": "Controls token-driven padding, font-size, gap, and embedded-icon scaling. `md` (the default) reproduces the original badge sizing exactly — existing call sites are unchanged. `sm` is tighter for dense inline data contexts; `lg` is larger for editorial contexts like page headers and stat-card status pills. Icons inside the badge scale with the size step.\n"
380
+ },
381
+ {
382
+ "name": "case",
383
+ "type": "\"sentence\"",
384
+ "description": "`case=\"sentence\"` opts a sm/md badge OUT of the editorial uppercase + letter-spacing treatment so prose-y labels (e.g. a plan name like \"Enterprise\") read as a label, not a tag. The `lg` size already opts out intrinsically. Omit for the default, theme-driven casing.\n"
385
+ },
386
+ {
387
+ "name": "iconOnly",
388
+ "type": "boolean",
389
+ "default": "false",
390
+ "description": "Render as a circular, fixed-square chip carrying a single centered glyph (e.g. a filled-success check in a roles matrix). Collapses padding/gap and forces a perfect circle. Opt-in; sets `data-icon-only` for styling hooks. Default badges are unaffected.\n"
375
391
  }
376
392
  ],
377
393
  "dependencies": [
@@ -382,11 +398,13 @@
382
398
  "--border-focus",
383
399
  "--color-neutral-600",
384
400
  "--color-white",
401
+ "--destructive",
385
402
  "--focus-ring-offset",
386
403
  "--focus-ring-width",
387
404
  "--font-size-sm",
388
405
  "--font-size-xs",
389
406
  "--font-weight-medium",
407
+ "--info",
390
408
  "--interactive-primary-bg",
391
409
  "--interactive-primary-text",
392
410
  "--interactive-secondary-bg",
@@ -397,6 +415,8 @@
397
415
  "--spacing-1",
398
416
  "--spacing-2",
399
417
  "--spacing-3",
418
+ "--success",
419
+ "--surface-card",
400
420
  "--surface-error-default",
401
421
  "--surface-error-subtle",
402
422
  "--surface-info-default",
@@ -413,7 +433,8 @@
413
433
  "--text-primary",
414
434
  "--text-secondary",
415
435
  "--text-success",
416
- "--text-warning"
436
+ "--text-warning",
437
+ "--warning"
417
438
  ],
418
439
  "example": "<Badge variant=\"default\">Active</Badge>\n<Badge variant=\"secondary\">Secondary</Badge>\n<Badge variant=\"outline\">v2.1.0</Badge>\n<Badge variant=\"neutral\">Draft</Badge>\n<Badge variant=\"destructive\">Error</Badge>\n<Badge variant=\"success\">Published</Badge>\n<Badge variant=\"warning\">Pending</Badge>\n<Badge variant=\"info\">Beta</Badge>\n<Badge variant=\"filled-destructive\">Critical</Badge>\n\n{/* Sizes — sm for dense inline data, md (default) inline, lg for headers/pills */}\n<Badge size=\"sm\" variant=\"secondary\">v2.1.0</Badge>\n<Badge size=\"md\" variant=\"success\">Published</Badge>\n<Badge size=\"lg\" variant=\"info\">Beta</Badge>\n"
419
440
  },
@@ -899,6 +920,12 @@
899
920
  "default": "false",
900
921
  "description": "Render inline (non-sticky) instead of fixed to the viewport bottom."
901
922
  },
923
+ {
924
+ "name": "flat",
925
+ "type": "boolean",
926
+ "default": "false",
927
+ "description": "Render flat — no shadow, no border-radius, border-top only. Use inside table cards or panels where the floating card look is wrong."
928
+ },
902
929
  {
903
930
  "name": "label",
904
931
  "type": "(count: number) => React.ReactNode",
@@ -936,6 +963,7 @@
936
963
  "--border-default",
937
964
  "--font-size-sm",
938
965
  "--font-weight-medium",
966
+ "--hairline",
939
967
  "--line-height-tight",
940
968
  "--motion-duration-moderate",
941
969
  "--motion-easing-standard",
@@ -944,8 +972,10 @@
944
972
  "--spacing-2",
945
973
  "--spacing-3",
946
974
  "--spacing-4",
975
+ "--spacing-5",
947
976
  "--surface-card",
948
- "--text-primary"
977
+ "--text-primary",
978
+ "--text-tertiary"
949
979
  ],
950
980
  "example": "<BulkActionBar\n count={selected.length}\n onClear={() => setSelected([])}\n>\n <Button variant=\"outline\" size=\"sm\">Archive</Button>\n <Button variant=\"destructive\" size=\"sm\">Delete</Button>\n</BulkActionBar>\n"
951
981
  },
@@ -1039,14 +1069,16 @@
1039
1069
  "--interactive-secondary-text",
1040
1070
  "--motion-duration-150",
1041
1071
  "--motion-easing-default",
1042
- "--opacity-50",
1072
+ "--opacity-40",
1043
1073
  "--primary",
1044
1074
  "--radius-md",
1045
1075
  "--spacing-2",
1046
1076
  "--spacing-3",
1047
1077
  "--spacing-4",
1048
1078
  "--spacing-6",
1079
+ "--surface-card",
1049
1080
  "--surface-muted",
1081
+ "--surface-subtle",
1050
1082
  "--text-primary"
1051
1083
  ],
1052
1084
  "example": "<Button variant=\"default\" size=\"md\">Save changes</Button>\n\n<Button variant=\"destructive\">Delete account</Button>\n\n<Button variant=\"ghost\" size=\"sm\">Cancel</Button>\n\n{/* Gated — no reason (tooltip suppressed) */}\n<Button gated>Delete organization</Button>\n\n{/* Gated with reason — tooltip shown on hover/focus */}\n<TooltipProvider>\n <Button gated gatedReason=\"You can't delete this — you're not an owner\">\n Delete organization\n </Button>\n</TooltipProvider>\n"
@@ -1257,6 +1289,93 @@
1257
1289
  ],
1258
1290
  "example": "<Carousel>\n <CarouselContent>\n <CarouselItem>Slide 1</CarouselItem>\n <CarouselItem>Slide 2</CarouselItem>\n <CarouselItem>Slide 3</CarouselItem>\n </CarouselContent>\n <CarouselPrevious />\n <CarouselNext />\n</Carousel>\n"
1259
1291
  },
1292
+ "challenge-card": {
1293
+ "category": "feedback",
1294
+ "description": "Adversarial challenge message card — an AI pushes back on the user's input and the human holds the gate. Distinct from Alert: ChallengeCard is for messages that require an explicit human decision, not passive notices.\n",
1295
+ "when_to_use": [
1296
+ "Surfacing an AI-generated challenge or push-back that the user must actively accept or override",
1297
+ "Presenting an adversarial review result (e.g. \"Is it actually only?\") where a human must decide the next action",
1298
+ "Any context where the system is flagging user input as potentially wrong and a conscious human gate is required before proceeding",
1299
+ "Chat or workflow UIs where the AI acts as a strategic antagonist and the human must confirm their intent"
1300
+ ],
1301
+ "when_not_to_use": [
1302
+ "Use `alert` for passive notices — ChallengeCard is for adversarial prompts requiring an explicit human decision",
1303
+ "Use Banner for persistent, page-level system notices that don't require an immediate response",
1304
+ "Use Toast for transient feedback that auto-dismisses — ChallengeCard is persistent until the user acts"
1305
+ ],
1306
+ "variants": {
1307
+ "action_variant": [
1308
+ "primary",
1309
+ "ghost"
1310
+ ]
1311
+ },
1312
+ "props": [
1313
+ {
1314
+ "name": "icon",
1315
+ "type": "React.ReactNode | null",
1316
+ "description": "Override the default Flag icon in ChallengeCardHeader. Pass null to suppress the icon entirely.\n"
1317
+ },
1318
+ {
1319
+ "name": "variant",
1320
+ "type": "\"primary\" | \"ghost\"",
1321
+ "default": "\"primary\"",
1322
+ "description": "Action button visual style. primary = filled warning-toned; ghost = transparent with border.\n",
1323
+ "component": "ChallengeCardAction"
1324
+ },
1325
+ {
1326
+ "name": "icon",
1327
+ "type": "React.ReactNode | null",
1328
+ "description": "Optional icon for ChallengeCardAction. Primary defaults to a Check icon; ghost has no default icon.\n",
1329
+ "component": "ChallengeCardAction"
1330
+ },
1331
+ {
1332
+ "name": "onClick",
1333
+ "type": "React.MouseEventHandler<HTMLButtonElement>",
1334
+ "description": "Click handler for the action button.",
1335
+ "component": "ChallengeCardAction"
1336
+ },
1337
+ {
1338
+ "name": "children",
1339
+ "type": "React.ReactNode",
1340
+ "description": "Gate label text. Defaults to \"You hold the gate\" when no children are provided.\n",
1341
+ "component": "ChallengeCardGate"
1342
+ }
1343
+ ],
1344
+ "dependencies": [
1345
+ "@phosphor-icons/react",
1346
+ "@loworbitstudio/visor-core"
1347
+ ],
1348
+ "tokens_used": [
1349
+ "--border-default",
1350
+ "--border-warning",
1351
+ "--challenge-card-action-primary-text",
1352
+ "--color-warning-900",
1353
+ "--focus-ring-offset",
1354
+ "--focus-ring-width",
1355
+ "--motion-duration-150",
1356
+ "--motion-easing-default",
1357
+ "--opacity-40",
1358
+ "--opacity-80",
1359
+ "--radius-lg",
1360
+ "--radius-xl",
1361
+ "--spacing-1",
1362
+ "--spacing-1-5",
1363
+ "--spacing-2",
1364
+ "--spacing-2-5",
1365
+ "--spacing-3",
1366
+ "--spacing-3-5",
1367
+ "--spacing-4",
1368
+ "--stroke-width-thin",
1369
+ "--surface-card",
1370
+ "--surface-warning-subtle",
1371
+ "--text-primary",
1372
+ "--text-secondary",
1373
+ "--text-tertiary",
1374
+ "--text-warning",
1375
+ "--warning"
1376
+ ],
1377
+ "example": "<ChallengeCard>\n <ChallengeCardHeader>Is it actually only?</ChallengeCardHeader>\n <ChallengeCardBody>\n The word \"only\" in this claim is absolute — it invites factual rebuttal.\n Do you have data to back it up, or would a sharper qualifier work better?\n </ChallengeCardBody>\n <ChallengeCardActions>\n <ChallengeCardAction variant=\"primary\" onClick={() => {}}>\n Use the sharper version\n </ChallengeCardAction>\n <ChallengeCardAction variant=\"ghost\" onClick={() => {}}>\n I'll rewrite it\n </ChallengeCardAction>\n <ChallengeCardGate />\n </ChallengeCardActions>\n</ChallengeCard>\n"
1378
+ },
1260
1379
  "chart": {
1261
1380
  "category": "data-display",
1262
1381
  "description": "A Recharts wrapper providing themed chart container, tooltip, and legend components.",
@@ -1400,19 +1519,17 @@
1400
1519
  "--border-error",
1401
1520
  "--border-focus",
1402
1521
  "--border-strong",
1403
- "--checkbox-bg",
1404
1522
  "--checkbox-bg-checked",
1405
- "--checkbox-bg-hover",
1406
- "--checkbox-border",
1407
1523
  "--checkbox-border-checked",
1408
- "--checkbox-border-hover",
1409
1524
  "--focus-ring-width",
1525
+ "--hairline-strong",
1410
1526
  "--interactive-primary-bg",
1411
1527
  "--interactive-primary-text",
1412
1528
  "--motion-duration-150",
1413
1529
  "--motion-easing-default",
1414
1530
  "--primary",
1415
- "--radius-sm"
1531
+ "--radius-sm",
1532
+ "--surface-subtle"
1416
1533
  ],
1417
1534
  "example": "<div style={{ display: \"flex\", alignItems: \"center\", gap: \"0.5rem\" }}>\n <Checkbox id=\"terms\" />\n <Label htmlFor=\"terms\">Accept terms and conditions</Label>\n</div>\n"
1418
1535
  },
@@ -1538,6 +1655,18 @@
1538
1655
  "type": "\"primary\" | \"neutral\"",
1539
1656
  "default": "\"neutral\"",
1540
1657
  "description": "Tone for the count pill. \"neutral\" uses muted surface; \"primary\" uses accent surface. Auto re-tones on selected state."
1658
+ },
1659
+ {
1660
+ "name": "selectedTreatment",
1661
+ "type": "\"accent\" | \"neutral\"",
1662
+ "default": "\"accent\"",
1663
+ "description": "Selected-state visual treatment. \"accent\" (default) uses accent-tinted bg + accent border + link text. \"neutral\" uses --surface-card bg + neutral border + primary text; the count pill renders as a solid mint pill (--surface-success-default) when selected. Use in editorial/admin contexts where accent bleed is undesirable.\n"
1664
+ },
1665
+ {
1666
+ "name": "trailingIcon",
1667
+ "type": "React.ReactNode",
1668
+ "optional": true,
1669
+ "description": "Muted trailing icon (e.g. a caret) rendered after the label/count for \"filter-as-dropdown\" chips. Orthogonal to count/selectedTreatment. Hidden from assistive tech (aria-hidden). Default render is unchanged when omitted.\n"
1541
1670
  }
1542
1671
  ]
1543
1672
  }
@@ -1549,6 +1678,7 @@
1549
1678
  "tokens_used": [
1550
1679
  "--border-default",
1551
1680
  "--border-focus",
1681
+ "--border-strong",
1552
1682
  "--focus-ring-offset",
1553
1683
  "--focus-ring-width",
1554
1684
  "--font-body",
@@ -1556,9 +1686,12 @@
1556
1686
  "--font-size-sm",
1557
1687
  "--font-size-xs",
1558
1688
  "--font-weight-medium",
1689
+ "--font-weight-semibold",
1559
1690
  "--motion-duration-150",
1560
1691
  "--motion-easing-default",
1561
1692
  "--opacity-40",
1693
+ "--primary",
1694
+ "--primary-text",
1562
1695
  "--radius-full",
1563
1696
  "--spacing-1",
1564
1697
  "--spacing-2",
@@ -1568,11 +1701,15 @@
1568
1701
  "--surface-accent-default",
1569
1702
  "--surface-accent-subtle",
1570
1703
  "--surface-card",
1704
+ "--surface-elev",
1571
1705
  "--surface-interactive-hover",
1572
1706
  "--surface-muted",
1707
+ "--surface-success-default",
1708
+ "--text-inverse",
1573
1709
  "--text-link",
1574
1710
  "--text-primary",
1575
- "--text-secondary"
1711
+ "--text-secondary",
1712
+ "--text-tertiary"
1576
1713
  ],
1577
1714
  "example": "<Chip label=\"React\" />\n<Chip label=\"Featured\" onDeleted={() => removeTag(\"featured\")} />\n<ChoiceChip label=\"Compact\" selected={density === \"compact\"} onPressed={() => setDensity(\"compact\")} />\n<FilterChip label=\"Events\" selected={filters.includes(\"events\")} onPressed={() => toggleFilter(\"events\")} />\n<FilterChip label=\"All\" count={132} onPressed={() => setFilter(\"all\")} />\n<FilterChip label=\"Active\" count={47} countTone=\"primary\" selected onPressed={() => setFilter(\"active\")} />\n<FilterChip label=\"Suspended\" count={8} onPressed={() => setFilter(\"suspended\")} />\n"
1578
1715
  },
@@ -2214,9 +2351,82 @@
2214
2351
  ],
2215
2352
  "example": "<CommandDialog open={open} onOpenChange={setOpen}>\n <CommandInput placeholder=\"Type a command...\" />\n <CommandList>\n <CommandEmpty>No results found.</CommandEmpty>\n <CommandGroup heading=\"Actions\">\n <CommandItem>\n New File\n <CommandShortcut>⌘N</CommandShortcut>\n </CommandItem>\n <CommandItem>Search</CommandItem>\n </CommandGroup>\n </CommandList>\n</CommandDialog>\n"
2216
2353
  },
2354
+ "composer": {
2355
+ "category": "form",
2356
+ "description": "AI-chat composer — a rounded card container with an auto-growing text field, a tools row for icon buttons and status chips, and a circular primary send button.",
2357
+ "when_to_use": [
2358
+ "Building an AI chat or assistant input area where users type multi-line messages",
2359
+ "Any composer-style input that needs contextual tool buttons (attachment, mic, model picker) alongside a submit affordance",
2360
+ "Interfaces where the field must grow with content rather than scroll within a fixed height",
2361
+ "Placing a branded status chip (e.g. \"Claude · key active\") inside the toolbar alongside action buttons"
2362
+ ],
2363
+ "when_not_to_use": [
2364
+ "Plain multi-line text entry without action affordances — use textarea instead",
2365
+ "Single-line search or filter inputs — use search-input or input",
2366
+ "Form fields that need validation, labels, or fieldset grouping — use field + textarea",
2367
+ "Full-page editor surfaces where a document metaphor is more appropriate"
2368
+ ],
2369
+ "variants": {},
2370
+ "props": [
2371
+ {
2372
+ "name": "value",
2373
+ "type": "string",
2374
+ "description": "Controlled field value."
2375
+ },
2376
+ {
2377
+ "name": "onValueChange",
2378
+ "type": "(value: string) => void",
2379
+ "description": "Called when the value changes (controlled mode)."
2380
+ },
2381
+ {
2382
+ "name": "onSubmit",
2383
+ "type": "(value: string) => void",
2384
+ "description": "Called when the user submits (Enter key or send button)."
2385
+ },
2386
+ {
2387
+ "name": "disabled",
2388
+ "type": "boolean",
2389
+ "default": "false",
2390
+ "description": "Disables all interactive children."
2391
+ }
2392
+ ],
2393
+ "dependencies": [
2394
+ "@phosphor-icons/react",
2395
+ "@loworbitstudio/visor-core"
2396
+ ],
2397
+ "tokens_used": [
2398
+ "--border-default",
2399
+ "--border-strong",
2400
+ "--focus-ring-offset",
2401
+ "--focus-ring-width",
2402
+ "--font-size-base",
2403
+ "--line-height-normal",
2404
+ "--motion-duration-150",
2405
+ "--motion-easing-default",
2406
+ "--opacity-40",
2407
+ "--opacity-50",
2408
+ "--primary",
2409
+ "--primary-foreground",
2410
+ "--radius-2xl",
2411
+ "--radius-full",
2412
+ "--radius-sm",
2413
+ "--shadow-md",
2414
+ "--shadow-sm",
2415
+ "--spacing-2",
2416
+ "--spacing-3",
2417
+ "--spacing-4",
2418
+ "--stroke-width-thin",
2419
+ "--surface-card",
2420
+ "--surface-hover",
2421
+ "--text-primary",
2422
+ "--text-secondary",
2423
+ "--text-tertiary"
2424
+ ],
2425
+ "example": "<Composer onSubmit={(v) => console.log(v)}>\n <ComposerField placeholder=\"Ask anything…\" />\n <ComposerToolbar>\n <ComposerToolButton icon={<Paperclip size={16} />} aria-label=\"Attach file\" />\n <ComposerToolButton icon={<Microphone size={16} />} aria-label=\"Voice input\" />\n <Chip size=\"sm\" leadingIcon={<StatusDot tone=\"mint\" />} label=\"Claude · key active\" />\n <ComposerSpacer />\n <ComposerSend />\n </ComposerToolbar>\n</Composer>\n"
2426
+ },
2217
2427
  "confirm-dialog": {
2218
2428
  "category": "admin",
2219
- "description": "Admin confirmation dialog compound wrapping Dialog with severity-driven icon/color/button variant, optional confirm-text gate, and async-aware confirm handler.",
2429
+ "description": "Admin confirmation dialog compound wrapping Dialog with severity-driven icon/color/button variant, an editorial icon-treatment + render-mode API, optional confirm-text gate, and async-aware confirm handler.",
2220
2430
  "when_to_use": [
2221
2431
  "Confirming a destructive action like delete, revoke, or purge",
2222
2432
  "Gating a high-stakes action with a typed confirmation phrase",
@@ -2237,6 +2447,14 @@
2237
2447
  ],
2238
2448
  "severity_deprecated": [
2239
2449
  "danger"
2450
+ ],
2451
+ "iconTreatment": [
2452
+ "inline",
2453
+ "plated"
2454
+ ],
2455
+ "mode": [
2456
+ "dialog",
2457
+ "inline"
2240
2458
  ]
2241
2459
  },
2242
2460
  "props": [
@@ -2262,6 +2480,28 @@
2262
2480
  "default": "warning",
2263
2481
  "description": "Drives icon, tinted icon plate, and confirm button variant. Use \"info\", \"warning\", or \"destructive\". The value \"danger\" is a deprecated alias for \"destructive\" and will be removed in the next major version.\n"
2264
2482
  },
2483
+ {
2484
+ "name": "icon",
2485
+ "type": "React.ReactNode",
2486
+ "description": "Custom severity icon — overrides the built-in icon for the active severity. Sizing/color is handled by the icon container, so pass a bare Phosphor icon (no explicit size).\n"
2487
+ },
2488
+ {
2489
+ "name": "iconTreatment",
2490
+ "type": "'inline' | 'plated'",
2491
+ "description": "Severity-icon treatment. Leave unset for the canonical default — a tinted circular plate stacked above the title. Set \"plated\" for a 40px color-mix severity plate leading the title column, or \"inline\" for a small leading icon next to the title. Default rendering (unset) is unchanged.\n"
2492
+ },
2493
+ {
2494
+ "name": "mode",
2495
+ "type": "'dialog' | 'inline'",
2496
+ "default": "dialog",
2497
+ "description": "Rendering mode. \"dialog\" uses Radix Dialog + Portal + Overlay + auto X-close. \"inline\" renders only the content surface (no portal, no overlay, no X-close) so multiple dialogs can stack inside one shared scrim.\n"
2498
+ },
2499
+ {
2500
+ "name": "cancelVariant",
2501
+ "type": "ButtonProps[\"variant\"]",
2502
+ "default": "outline",
2503
+ "description": "Cancel button variant. Golden editorial screens use \"ghost\"."
2504
+ },
2265
2505
  {
2266
2506
  "name": "trigger",
2267
2507
  "type": "React.ReactNode",
@@ -2324,17 +2564,29 @@
2324
2564
  "@phosphor-icons/react"
2325
2565
  ],
2326
2566
  "tokens_used": [
2567
+ "--accent",
2327
2568
  "--border-default",
2328
2569
  "--border-focus",
2570
+ "--confirm-dialog-plate-icon-size",
2571
+ "--confirm-dialog-plate-size",
2572
+ "--destructive",
2329
2573
  "--focus-ring-offset",
2330
2574
  "--focus-ring-width",
2575
+ "--font-size-2xl",
2576
+ "--font-size-lg",
2331
2577
  "--font-size-sm",
2332
2578
  "--font-weight-medium",
2579
+ "--font-weight-semibold",
2333
2580
  "--line-height-normal",
2334
2581
  "--line-height-relaxed",
2335
2582
  "--motion-duration-150",
2336
2583
  "--motion-easing-default",
2584
+ "--radius-full",
2585
+ "--radius-lg",
2337
2586
  "--radius-md",
2587
+ "--radius-xl",
2588
+ "--shadow-lg",
2589
+ "--shadow-xl",
2338
2590
  "--spacing-1",
2339
2591
  "--spacing-10",
2340
2592
  "--spacing-2",
@@ -2342,6 +2594,8 @@
2342
2594
  "--spacing-4",
2343
2595
  "--spacing-5",
2344
2596
  "--spacing-6",
2597
+ "--spacing-8",
2598
+ "--surface-card",
2345
2599
  "--surface-error-subtle",
2346
2600
  "--surface-info-subtle",
2347
2601
  "--surface-page",
@@ -2350,7 +2604,9 @@
2350
2604
  "--text-info",
2351
2605
  "--text-primary",
2352
2606
  "--text-secondary",
2353
- "--text-warning"
2607
+ "--text-tertiary",
2608
+ "--text-warning",
2609
+ "--warning"
2354
2610
  ],
2355
2611
  "example": "<ConfirmDialog\n trigger={<Button variant=\"destructive\">Delete project</Button>}\n severity=\"destructive\"\n title=\"Delete project?\"\n description=\"This action cannot be undone. All associated data will be permanently removed.\"\n confirmText=\"acme\"\n onConfirm={async () => {\n await deleteProject()\n }}\n/>\n"
2356
2612
  },
@@ -2632,6 +2888,11 @@
2632
2888
  "type": "React.ReactNode",
2633
2889
  "description": "Override for the default EmptyState slot rendered when data is empty."
2634
2890
  },
2891
+ {
2892
+ "name": "loadingSkeletonCell",
2893
+ "type": "(args: { rowIndex: number; colIndex: number; columnId: string }) => React.ReactNode",
2894
+ "description": "Opt-in per-column skeleton shapes for the loading state. When supplied, each skeleton row's cell contents come from this render prop instead of the default uniform full-width bars, letting the caller mirror the real row's silhouette (logo plates, badge pills, two-line id stacks). Returns the cell contents; DataTable still owns the `tr`/`td` structure, key, and `data-slot`. `colIndex` walks the resolved column list (the injected selection column, when present, is index 0). Return `null`/`undefined` to leave a cell empty. Default (prop omitted) is unchanged: every cell renders a full-width Skeleton bar. Pair with the Skeleton shape classes (`shapeLogo`, `shapePill`, `shapeCircle`).\n"
2895
+ },
2635
2896
  {
2636
2897
  "name": "stickyHeader",
2637
2898
  "type": "boolean",
@@ -2642,7 +2903,7 @@
2642
2903
  "name": "density",
2643
2904
  "type": "\"compact\" | \"default\" | \"editorial\"",
2644
2905
  "default": "\"default\"",
2645
- "description": "Vertical row padding step. Maps to a `data-density` attribute on the root, which sets the `--dt-row-py` custom property the table's cells consume. `\"compact\"` = 8px (sub-content density: long lists, narrow viewports). `\"default\"` = 12px (current behaviour; backward-compatible with consumers that don't pass the prop). `\"editorial\"` = 20px (generous; each row reads as a card; high-design admin patterns). Themes can override per-density values without forking the component by targeting `[data-density=\"…\"]` from their own selector.\n"
2906
+ "description": "Vertical row padding step. Maps to a `data-density` attribute on the root, which sets the `--dt-row-py` custom property the table's cells consume. `\"compact\"` = 8px (sub-content density: long lists, narrow viewports). `\"default\"` = 12px (current behaviour; backward-compatible with consumers that don't pass the prop). `\"editorial\"` = 16px (spacing-4; generous but compact enough for dense admin grids) — column headers also receive the editorial treatment: uppercase, 11px (overridable via `--dt-header-font-size`), `--text-tertiary` color, and `letter-spacing: 0.08em`; the editorial surface treatment (flush container, card/page row mix) is also applied. Themes can override per-density values without forking the component by targeting `[data-density=\"…\"]` from their own selector.\n"
2646
2907
  },
2647
2908
  {
2648
2909
  "name": "rowTone",
@@ -2663,6 +2924,13 @@
2663
2924
  "tokens_used": [
2664
2925
  "--border-default",
2665
2926
  "--border-strong",
2927
+ "--data-table-sort-bar-radius",
2928
+ "--dt-cell-px",
2929
+ "--dt-container-radius",
2930
+ "--dt-container-shadow",
2931
+ "--dt-header-bg",
2932
+ "--dt-header-font-size",
2933
+ "--dt-row-bg",
2666
2934
  "--dt-row-py",
2667
2935
  "--focus-ring-color",
2668
2936
  "--focus-ring-offset",
@@ -2674,6 +2942,7 @@
2674
2942
  "--line-height-tight",
2675
2943
  "--motion-duration-fast",
2676
2944
  "--motion-easing-standard",
2945
+ "--radius-lg",
2677
2946
  "--radius-md",
2678
2947
  "--radius-sm",
2679
2948
  "--spacing-1",
@@ -2688,6 +2957,7 @@
2688
2957
  "--surface-info-subtle",
2689
2958
  "--surface-interactive-active",
2690
2959
  "--surface-interactive-default",
2960
+ "--surface-page",
2691
2961
  "--surface-selected",
2692
2962
  "--surface-subtle",
2693
2963
  "--surface-success-subtle",
@@ -2901,6 +3171,10 @@
2901
3171
  "name": "DialogHeader",
2902
3172
  "description": "Container for title and description at the top."
2903
3173
  },
3174
+ {
3175
+ "name": "DialogFooter",
3176
+ "description": "Right-aligned action row with a hairline top border, for dialog buttons."
3177
+ },
2904
3178
  {
2905
3179
  "name": "DialogTitle",
2906
3180
  "description": "Accessible dialog heading."
@@ -2928,11 +3202,16 @@
2928
3202
  ],
2929
3203
  "tokens_used": [
2930
3204
  "--border-focus",
3205
+ "--border-subtle",
2931
3206
  "--focus-ring-offset",
2932
3207
  "--focus-ring-width",
3208
+ "--font-size-2xl",
2933
3209
  "--font-size-lg",
2934
3210
  "--font-size-sm",
3211
+ "--font-weight-bold",
2935
3212
  "--font-weight-semibold",
3213
+ "--hairline",
3214
+ "--hairline-width",
2936
3215
  "--motion-duration-150",
2937
3216
  "--motion-duration-normal",
2938
3217
  "--motion-easing-default",
@@ -2941,13 +3220,20 @@
2941
3220
  "--overlay-bg",
2942
3221
  "--radius-lg",
2943
3222
  "--radius-sm",
3223
+ "--radius-xl",
3224
+ "--shadow-lg",
2944
3225
  "--shadow-xl",
2945
3226
  "--spacing-1",
3227
+ "--spacing-2",
3228
+ "--spacing-3",
2946
3229
  "--spacing-4",
2947
3230
  "--spacing-6",
3231
+ "--spacing-8",
3232
+ "--surface-card",
2948
3233
  "--surface-page",
2949
3234
  "--text-primary",
2950
- "--text-secondary"
3235
+ "--text-secondary",
3236
+ "--text-tertiary"
2951
3237
  ],
2952
3238
  "example": "<Dialog>\n <DialogTrigger asChild>\n <Button>Edit profile</Button>\n </DialogTrigger>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Edit profile</DialogTitle>\n <DialogDescription>Make changes to your profile here.</DialogDescription>\n </DialogHeader>\n {/* form content */}\n </DialogContent>\n</Dialog>\n"
2953
3239
  },
@@ -3054,27 +3340,37 @@
3054
3340
  ],
3055
3341
  "tokens_used": [
3056
3342
  "--border-default",
3343
+ "--border-subtle",
3344
+ "--destructive",
3057
3345
  "--font-size-sm",
3058
3346
  "--font-size-xs",
3347
+ "--hairline",
3059
3348
  "--motion-duration-fast",
3060
3349
  "--motion-easing-default",
3061
3350
  "--motion-easing-enter",
3062
3351
  "--motion-easing-exit",
3063
3352
  "--radius-lg",
3064
3353
  "--radius-md",
3354
+ "--radius-sm",
3065
3355
  "--shadow-lg",
3066
3356
  "--shadow-xl",
3067
3357
  "--spacing-1",
3068
3358
  "--spacing-2",
3069
3359
  "--spacing-3",
3070
3360
  "--spacing-8",
3361
+ "--stroke-width-thin",
3071
3362
  "--surface-card",
3363
+ "--surface-elev",
3072
3364
  "--surface-error-subtle",
3073
3365
  "--surface-interactive-hover",
3366
+ "--surface-muted",
3074
3367
  "--surface-popover",
3368
+ "--surface-subtle",
3075
3369
  "--text-error",
3370
+ "--text-muted",
3076
3371
  "--text-primary",
3077
- "--text-secondary"
3372
+ "--text-secondary",
3373
+ "--text-tertiary"
3078
3374
  ],
3079
3375
  "example": "<DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\">Actions</Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent>\n <DropdownMenuItem>Edit</DropdownMenuItem>\n <DropdownMenuItem>Duplicate</DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem variant=\"destructive\">Delete</DropdownMenuItem>\n </DropdownMenuContent>\n</DropdownMenu>\n"
3080
3376
  },
@@ -3134,6 +3430,10 @@
3134
3430
  "tone": [
3135
3431
  "default",
3136
3432
  "subtle"
3433
+ ],
3434
+ "variant": [
3435
+ "default",
3436
+ "editorial"
3137
3437
  ]
3138
3438
  },
3139
3439
  "props": [
@@ -3175,6 +3475,12 @@
3175
3475
  "default": "default",
3176
3476
  "description": "Default uses a dashed border on a muted surface. Subtle is borderless."
3177
3477
  },
3478
+ {
3479
+ "name": "variant",
3480
+ "type": "'default' | 'editorial'",
3481
+ "default": "default",
3482
+ "description": "Surface treatment. Default is the base placeholder (dashed/borderless per tone). Editorial is an opt-in filled card on --surface-card with a 64px circular icon chip and a larger heading; it overrides the tone surface and the size icon/heading scale.\n"
3483
+ },
3178
3484
  {
3179
3485
  "name": "headingAs",
3180
3486
  "type": "'h2' | 'h3' | 'h4'",
@@ -3199,16 +3505,20 @@
3199
3505
  "--letter-spacing-tight",
3200
3506
  "--line-height-relaxed",
3201
3507
  "--line-height-tight",
3508
+ "--radius-full",
3202
3509
  "--radius-lg",
3203
3510
  "--spacing-1",
3204
3511
  "--spacing-12",
3512
+ "--spacing-16",
3205
3513
  "--spacing-2",
3206
3514
  "--spacing-3",
3207
3515
  "--spacing-4",
3208
3516
  "--spacing-5",
3209
3517
  "--spacing-6",
3210
3518
  "--spacing-8",
3519
+ "--surface-card",
3211
3520
  "--surface-muted",
3521
+ "--surface-subtle",
3212
3522
  "--text-primary",
3213
3523
  "--text-secondary",
3214
3524
  "--text-tertiary"
@@ -3253,7 +3563,7 @@
3253
3563
  },
3254
3564
  {
3255
3565
  "name": "FieldError",
3256
- "description": "Error message display with role=\"alert\". Accepts an errors array or children."
3566
+ "description": "Error message display with role=\"alert\". Accepts an errors array or children. Optional `icon` prop renders an aria-hidden leading glyph and switches to a centered icon + message row; without it the error renders as plain inline text (default, unchanged). Typography follows the density axis: place data-density=\"editorial\" on any ancestor to activate the 13px/11px discrete ramp and tertiary color treatment.\n"
3257
3567
  }
3258
3568
  ],
3259
3569
  "dependencies": [
@@ -3269,7 +3579,8 @@
3269
3579
  "--spacing-4",
3270
3580
  "--text-error",
3271
3581
  "--text-primary",
3272
- "--text-secondary"
3582
+ "--text-secondary",
3583
+ "--text-tertiary"
3273
3584
  ],
3274
3585
  "example": "<Field>\n <FieldLabel htmlFor=\"email\">Email</FieldLabel>\n <FieldDescription>We will never share your email.</FieldDescription>\n <Input id=\"email\" type=\"email\" />\n <FieldError errors={[{ message: \"Email is required\" }]} />\n</Field>\n"
3275
3586
  },
@@ -3969,59 +4280,156 @@
3969
4280
  ],
3970
4281
  "example": "<Image\n src=\"/photos/hero.jpg\"\n alt=\"Mountain landscape\"\n aspectRatio=\"video\"\n/>\n\n<Image\n src={user.avatar}\n alt={user.name}\n aspectRatio=\"square\"\n objectFit=\"cover\"\n fallback={<span>No photo</span>}\n/>\n"
3971
4282
  },
3972
- "inline": {
3973
- "category": "layout",
3974
- "description": "Horizontal flex container with token-driven gap, alignment, optional wrap, and `as` polymorphism.",
4283
+ "infographic-bar": {
4284
+ "category": "admin",
4285
+ "description": "Composable continuous stat-card band. Lays out N stat-cards as a single infographic strip — outer corners rounded, inner corners square, hairline dividers, no full per-card borders.",
3975
4286
  "when_to_use": [
3976
- "Laying out a row of buttons, chips, badges, or icons",
3977
- "Toolbar-style horizontal groupings",
3978
- "Anywhere you would write `flex items-center gap-N` in Tailwind"
4287
+ "Rendering a KPI/metric row that should read as ONE continuous band, not separate bordered cards",
4288
+ "Admin dashboard summary strips where several metrics share a single framed surface",
4289
+ "Theme-portable metric rows that must look right in both bordered and borderless palettes"
3979
4290
  ],
3980
4291
  "when_not_to_use": [
3981
- "Vertical arrangements (use Stack)",
3982
- "Two-dimensional grids (use Grid)",
3983
- "Layouts that need to align to a specific column count (use Grid)"
4292
+ "Showing a single metric (use StatCard directly)",
4293
+ "Metrics that each need their own card with its own border and elevation (compose StatCards in a grid instead)",
4294
+ "Tabular metrics across many rows (use DataTable)",
4295
+ "Marketing feature tiles (use a marketing card pattern)"
3984
4296
  ],
3985
4297
  "props": [
3986
4298
  {
3987
- "name": "as",
3988
- "type": "keyof JSX.IntrinsicElements",
3989
- "default": "'div'",
3990
- "description": "HTML element to render"
4299
+ "name": "stats",
4300
+ "type": "InfographicBarStat[]",
4301
+ "required": true,
4302
+ "description": "Ordered stat cells, rendered left-to-right as a single continuous band. Each item is a subset of StatCardProps."
3991
4303
  },
3992
4304
  {
3993
- "name": "gap",
3994
- "type": "InlineSpacing | ResponsiveProp<InlineSpacing>",
3995
- "default": "'md'",
3996
- "description": "Space between children"
4305
+ "name": "stats[].label",
4306
+ "type": "React.ReactNode",
4307
+ "required": true,
4308
+ "description": "Small uppercase label describing the metric."
3997
4309
  },
3998
4310
  {
3999
- "name": "align",
4000
- "type": "'start' | 'center' | 'end' | 'stretch' | 'baseline'",
4001
- "default": "'center'",
4002
- "description": "Cross-axis alignment"
4311
+ "name": "stats[].value",
4312
+ "type": "React.ReactNode",
4313
+ "required": true,
4314
+ "description": "Prominent metric value."
4003
4315
  },
4004
4316
  {
4005
- "name": "justify",
4006
- "type": "'start' | 'center' | 'end' | 'between' | 'around' | 'evenly'",
4007
- "description": "Main-axis alignment"
4317
+ "name": "stats[].delta",
4318
+ "type": "StatCardDelta",
4319
+ "description": "Change indicator with value, direction (\"up\" | \"down\" | \"flat\"), and optional label."
4008
4320
  },
4009
4321
  {
4010
- "name": "wrap",
4011
- "type": "boolean",
4012
- "default": "false",
4013
- "description": "Allow children to wrap onto multiple lines"
4014
- }
4015
- ],
4016
- "dependencies": [],
4017
- "tokens_used": [
4018
- "--inline-gap",
4019
- "--inline-gap-lg",
4020
- "--inline-gap-md",
4021
- "--inline-gap-sm",
4022
- "--inline-gap-xl",
4023
- "--spacing-4"
4024
- ],
4322
+ "name": "stats[].trend",
4323
+ "type": "React.ReactNode",
4324
+ "description": "Slot for a sparkline, chart, or icon."
4325
+ },
4326
+ {
4327
+ "name": "stats[].trendPosition",
4328
+ "type": "'header' | 'footer'",
4329
+ "default": "footer",
4330
+ "description": "Where the trend slot renders within the cell."
4331
+ },
4332
+ {
4333
+ "name": "stats[].footer",
4334
+ "type": "React.ReactNode",
4335
+ "description": "Sublabel or link rendered beneath the value."
4336
+ },
4337
+ {
4338
+ "name": "stats[].variant",
4339
+ "type": "'default' | 'highlight'",
4340
+ "default": "default",
4341
+ "description": "Per-cell visual emphasis. Highlight tints that cell's surface."
4342
+ },
4343
+ {
4344
+ "name": "stats[].valueAs",
4345
+ "type": "'default' | 'hero' | 'compact'",
4346
+ "default": "default",
4347
+ "description": "Typography scale for the cell value."
4348
+ },
4349
+ {
4350
+ "name": "stats[].id",
4351
+ "type": "string",
4352
+ "description": "Stable React key for the cell. Falls back to the array index when omitted."
4353
+ },
4354
+ {
4355
+ "name": "size",
4356
+ "type": "'sm' | 'md'",
4357
+ "default": "md",
4358
+ "description": "Dimensional density forwarded to every cell."
4359
+ },
4360
+ {
4361
+ "name": "className",
4362
+ "type": "string",
4363
+ "description": "Additional CSS class names merged onto the band element."
4364
+ }
4365
+ ],
4366
+ "dependencies": [
4367
+ "stat-card"
4368
+ ],
4369
+ "tokens_used": [
4370
+ "--border-default",
4371
+ "--hairline",
4372
+ "--infographic-bar-divider",
4373
+ "--radius-lg",
4374
+ "--shadow-xs",
4375
+ "--stroke-width-thin",
4376
+ "--surface-card"
4377
+ ],
4378
+ "example": "<InfographicBar\n stats={[\n { label: \"Total Revenue\", value: \"$48,120\", delta: { value: \"+12.4%\", direction: \"up\", label: \"vs last month\" } },\n { label: \"Active Users\", value: \"892\", delta: { value: \"+3.1%\", direction: \"up\" } },\n { label: \"Churn Rate\", value: \"3.2%\", delta: { value: \"-0.4%\", direction: \"down\" } },\n { label: \"Signups\", value: \"412\", variant: \"highlight\" },\n ]}\n/>\n"
4379
+ },
4380
+ "inline": {
4381
+ "category": "layout",
4382
+ "description": "Horizontal flex container with token-driven gap, alignment, optional wrap, and `as` polymorphism.",
4383
+ "when_to_use": [
4384
+ "Laying out a row of buttons, chips, badges, or icons",
4385
+ "Toolbar-style horizontal groupings",
4386
+ "Anywhere you would write `flex items-center gap-N` in Tailwind"
4387
+ ],
4388
+ "when_not_to_use": [
4389
+ "Vertical arrangements (use Stack)",
4390
+ "Two-dimensional grids (use Grid)",
4391
+ "Layouts that need to align to a specific column count (use Grid)"
4392
+ ],
4393
+ "props": [
4394
+ {
4395
+ "name": "as",
4396
+ "type": "keyof JSX.IntrinsicElements",
4397
+ "default": "'div'",
4398
+ "description": "HTML element to render"
4399
+ },
4400
+ {
4401
+ "name": "gap",
4402
+ "type": "InlineSpacing | ResponsiveProp<InlineSpacing>",
4403
+ "default": "'md'",
4404
+ "description": "Space between children"
4405
+ },
4406
+ {
4407
+ "name": "align",
4408
+ "type": "'start' | 'center' | 'end' | 'stretch' | 'baseline'",
4409
+ "default": "'center'",
4410
+ "description": "Cross-axis alignment"
4411
+ },
4412
+ {
4413
+ "name": "justify",
4414
+ "type": "'start' | 'center' | 'end' | 'between' | 'around' | 'evenly'",
4415
+ "description": "Main-axis alignment"
4416
+ },
4417
+ {
4418
+ "name": "wrap",
4419
+ "type": "boolean",
4420
+ "default": "false",
4421
+ "description": "Allow children to wrap onto multiple lines"
4422
+ }
4423
+ ],
4424
+ "dependencies": [],
4425
+ "tokens_used": [
4426
+ "--inline-gap",
4427
+ "--inline-gap-lg",
4428
+ "--inline-gap-md",
4429
+ "--inline-gap-sm",
4430
+ "--inline-gap-xl",
4431
+ "--spacing-4"
4432
+ ],
4025
4433
  "example": "<Inline gap=\"sm\">\n <Button>Save</Button>\n <Button variant=\"secondary\">Cancel</Button>\n</Inline>\n\n<Inline gap=\"xs\" wrap>\n {tags.map(t => <Badge key={t}>{t}</Badge>)}\n</Inline>\n"
4026
4434
  },
4027
4435
  "input": {
@@ -4070,6 +4478,7 @@
4070
4478
  "--border-error",
4071
4479
  "--border-focus",
4072
4480
  "--border-strong",
4481
+ "--field-control-bg",
4073
4482
  "--focus-ring-offset",
4074
4483
  "--focus-ring-width",
4075
4484
  "--font-size-base",
@@ -4077,8 +4486,17 @@
4077
4486
  "--input-bg",
4078
4487
  "--input-bg-invalid",
4079
4488
  "--input-border",
4489
+ "--input-height-lg",
4490
+ "--input-height-md",
4491
+ "--input-padding-lg",
4492
+ "--input-padding-md",
4493
+ "--input-padding-sm",
4494
+ "--input-placeholder-color",
4495
+ "--input-radius-lg",
4496
+ "--input-radius-md",
4080
4497
  "--motion-duration-150",
4081
4498
  "--motion-easing-default",
4499
+ "--radius-md",
4082
4500
  "--radius-sm",
4083
4501
  "--spacing-1",
4084
4502
  "--spacing-3",
@@ -4086,8 +4504,10 @@
4086
4504
  "--spacing-4",
4087
4505
  "--spacing-4_5",
4088
4506
  "--spacing-5",
4507
+ "--surface-card",
4089
4508
  "--surface-error-subtle",
4090
4509
  "--surface-interactive-default",
4510
+ "--surface-subtle",
4091
4511
  "--text-primary",
4092
4512
  "--text-secondary"
4093
4513
  ],
@@ -4425,6 +4845,90 @@
4425
4845
  ],
4426
4846
  "example": "<Marquee\n items={['Acme Corp', 'Vercel', 'Low Orbit Studio', 'Figma', 'Notion']}\n durationSec={20}\n separator=\"·\"\n/>\n\n<Marquee\n bands={[\n { items: clients, direction: 'left', durationSec: 40, separator: '●' },\n { items: clientsReversed, direction: 'right', durationSec: 50, separator: '○' },\n ]}\n pauseOnHover\n gap=\"2.5rem\"\n/>\n"
4427
4847
  },
4848
+ "matrix-table": {
4849
+ "category": "admin",
4850
+ "description": "A fixed members×roles assignment grid. Sticky-left identity column with an uppercase, tracked \"MEMBER\" eyebrow header; centered boolean cells rendered as a 22px filled-success disc with a check glyph (active) or a muted dash (not assigned); optional string cells render as plain text; multi-line centered role-column headers (label over count) that opt out of the uppercase header treatment; an editorial cell rhythm (spacing-5 horizontal) and hairline borders driven by the shared --dt-* table hooks (--dt-container-radius/-shadow, --dt-header-bg, --dt-row-bg, --hairline). Participates in the density axis — placing an ancestor with data-density=\"editorial\" activates the editorial surface treatment (flush container, page/card row mix). No list machinery — no selection column, no sort buttons, no pagination footer.",
4851
+ "when_to_use": [
4852
+ "Displaying a members × roles assignment matrix in organization settings",
4853
+ "A plans × features monetization pricing matrix (string + boolean cells)",
4854
+ "Any fixed grid where rows are identities (users, plans) and columns are attributes (permissions, roles, features), rendered as a sibling of the editorial data-table",
4855
+ "When the shape is a matrix, not a sortable/pageable list — where DataTable's list machinery would fight the layout"
4856
+ ],
4857
+ "when_not_to_use": [
4858
+ "Sortable, paginated lists of records (use DataTable)",
4859
+ "Editable grids with input cells (extend or build a dedicated grid)",
4860
+ "Simple key-value tables (use the Table primitive directly)"
4861
+ ],
4862
+ "variants": {},
4863
+ "props": [
4864
+ {
4865
+ "name": "columns",
4866
+ "type": "MatrixColumn[]",
4867
+ "required": true,
4868
+ "description": "Column (role/feature) definitions. Each column carries a `key` (stable identifier indexing the per-row cell map; `id` is accepted as a compatible alias), a `label`, an optional `count` sub-label rendered below the label, and an optional `description` shown as a tooltip when hovering an active cell in that column.\n"
4869
+ },
4870
+ {
4871
+ "name": "rows",
4872
+ "type": "MatrixRow<TIdentity>[]",
4873
+ "required": true,
4874
+ "description": "Row data. Each row has an `id` (React key), an optional `identity` payload passed through to `renderIdentity` via the row, and its truth values. Truth is supplied via `cells` — a map keyed by column key whose values are `string | boolean` (`true`/`false` render the boolean indicator, a `string` renders as plain text). `activeColumns` (a Set/array of column keys) is accepted as a compatible alias for the boolean half of `cells`; a `cells` entry takes precedence for the same column.\n"
4875
+ },
4876
+ {
4877
+ "name": "renderIdentity",
4878
+ "type": "(row: MatrixRow<TIdentity>) => React.ReactNode",
4879
+ "required": true,
4880
+ "description": "Render slot for the sticky-left identity cell. Receives the whole row (blessed shape) — read `row.identity` for structured identity data."
4881
+ },
4882
+ {
4883
+ "name": "identityLabel",
4884
+ "type": "React.ReactNode",
4885
+ "description": "Header label for the sticky identity column. Defaults to \"Member\" (rendered with the uppercase, tracked eyebrow treatment)."
4886
+ },
4887
+ {
4888
+ "name": "aria-label",
4889
+ "type": "string",
4890
+ "description": "Accessible label for the table element."
4891
+ }
4892
+ ],
4893
+ "dependencies": [
4894
+ "@phosphor-icons/react",
4895
+ "@loworbitstudio/visor-core"
4896
+ ],
4897
+ "tokens_used": [
4898
+ "--dt-container-radius",
4899
+ "--dt-container-shadow",
4900
+ "--dt-header-bg",
4901
+ "--dt-row-bg",
4902
+ "--font-size-base",
4903
+ "--font-size-sm",
4904
+ "--font-weight-medium",
4905
+ "--font-weight-semibold",
4906
+ "--hairline",
4907
+ "--hairline-width",
4908
+ "--motion-duration-100",
4909
+ "--motion-easing-default",
4910
+ "--primary-text",
4911
+ "--radius-full",
4912
+ "--radius-lg",
4913
+ "--radius-sm",
4914
+ "--shadow-md",
4915
+ "--shadow-sm",
4916
+ "--spacing-1",
4917
+ "--spacing-2",
4918
+ "--spacing-3",
4919
+ "--spacing-4",
4920
+ "--spacing-5",
4921
+ "--success",
4922
+ "--surface-card",
4923
+ "--surface-elev",
4924
+ "--surface-page",
4925
+ "--surface-subtle",
4926
+ "--text-muted",
4927
+ "--text-primary",
4928
+ "--text-tertiary"
4929
+ ],
4930
+ "example": "const columns: MatrixColumn[] = [\n { key: 'admin', label: 'Admin', count: 3 },\n { key: 'editor', label: 'Editor', count: 5, description: 'Can edit content' },\n { key: 'viewer', label: 'Viewer', count: 12 },\n]\n\nconst rows: MatrixRow<{ name: string; email: string }>[] = [\n { id: '1', identity: { name: 'Ada Lovelace', email: 'ada@example.com' }, cells: { admin: true, editor: true } },\n { id: '2', identity: { name: 'Grace Hopper', email: 'grace@example.com' }, cells: { viewer: true } },\n // Mixed string + boolean cells (string renders as plain text):\n { id: '3', identity: { name: 'Storage', email: '' }, cells: { admin: '50GB', editor: true, viewer: false } },\n // Compatible alias: activeColumns instead of a boolean cells map:\n { id: '4', identity: { name: 'Legacy', email: '' }, activeColumns: ['admin'] },\n]\n\n<MatrixTable\n columns={columns}\n rows={rows}\n renderIdentity={(row) => <span>{row.identity?.name}</span>}\n identityLabel=\"Member\"\n aria-label=\"Member role assignments\"\n/>\n"
4931
+ },
4428
4932
  "menubar": {
4429
4933
  "category": "overlay",
4430
4934
  "description": "Horizontal menu bar with dropdown menus, keyboard navigation, and full a11y support.",
@@ -5001,6 +5505,16 @@
5001
5505
  "name": "titleFamily",
5002
5506
  "type": "'heading' | 'display' | string",
5003
5507
  "description": "Title font-family override. `\"heading\"` and `\"display\"` are tokens that map to `data-title-family` attributes; `\"display\"` resolves to `var(--font-display, var(--font-family-heading, inherit))` so themes without a display font degrade to the heading family. Any other string is forwarded as a raw CSS family on an inline `--page-header-title-family` declaration.\n"
5508
+ },
5509
+ {
5510
+ "name": "leading",
5511
+ "type": "React.ReactNode",
5512
+ "description": "Optional media/identity node rendered to the LEFT of the title block inside the title row (typically an Avatar or identity plate). When present the row forms an identity lockup and the slot top-aligns against the title/description stack (VI-539). Omit to keep the default text|actions layout, so existing call sites render unchanged.\n"
5513
+ },
5514
+ {
5515
+ "name": "titleLeading",
5516
+ "type": "string | number",
5517
+ "description": "Title line-height override, forwarded as an inline `--page-header-title-leading` custom property (a unitless number like `1.05` or any CSS length). When omitted the title keeps its default line-height (tight for the standard title, `1` for the marquee scale), so existing call sites render unchanged.\n"
5004
5518
  }
5005
5519
  ],
5006
5520
  "dependencies": [
@@ -5020,7 +5534,9 @@
5020
5534
  "--letter-spacing-wide",
5021
5535
  "--line-height-relaxed",
5022
5536
  "--line-height-tight",
5537
+ "--page-header-leading-gap",
5023
5538
  "--page-header-title-family",
5539
+ "--page-header-title-leading",
5024
5540
  "--page-header-title-size",
5025
5541
  "--spacing-1",
5026
5542
  "--spacing-2",
@@ -5530,6 +6046,10 @@
5530
6046
  "Linear capacity bars (use Progress with size=\"thin\")"
5531
6047
  ],
5532
6048
  "variants": {
6049
+ "variant": [
6050
+ "ring",
6051
+ "solid"
6052
+ ],
5533
6053
  "size": [
5534
6054
  "sm",
5535
6055
  "md",
@@ -5561,6 +6081,12 @@
5561
6081
  "default": "100",
5562
6082
  "description": "Maximum value the score can reach."
5563
6083
  },
6084
+ {
6085
+ "name": "variant",
6086
+ "type": "\"ring\" | \"solid\"",
6087
+ "default": "\"ring\"",
6088
+ "description": "Display treatment. \"ring\" draws the SVG track + progress arc (default). \"solid\" renders a flat filled tinted disc with no ring — the editorial health-score look."
6089
+ },
5564
6090
  {
5565
6091
  "name": "size",
5566
6092
  "type": "\"sm\" | \"md\" | \"lg\"",
@@ -5597,11 +6123,13 @@
5597
6123
  ],
5598
6124
  "tokens_used": [
5599
6125
  "--border-default",
6126
+ "--destructive",
5600
6127
  "--font-size-base",
5601
6128
  "--font-size-sm",
5602
6129
  "--font-size-xs",
5603
6130
  "--font-weight-regular",
5604
6131
  "--font-weight-semibold",
6132
+ "--info",
5605
6133
  "--line-height-tight",
5606
6134
  "--motion-duration-150",
5607
6135
  "--motion-duration-500",
@@ -5609,11 +6137,14 @@
5609
6137
  "--radius-full",
5610
6138
  "--score-indicator-center-bg",
5611
6139
  "--score-indicator-icon-color",
6140
+ "--score-indicator-solid-bg",
6141
+ "--score-indicator-solid-value-color",
5612
6142
  "--score-indicator-stroke-color",
5613
6143
  "--score-indicator-track-color",
5614
6144
  "--score-indicator-value-color",
5615
6145
  "--spacing-1",
5616
6146
  "--spacing-2",
6147
+ "--success",
5617
6148
  "--surface-card",
5618
6149
  "--surface-error-subtle",
5619
6150
  "--surface-info-subtle",
@@ -5624,7 +6155,8 @@
5624
6155
  "--text-primary",
5625
6156
  "--text-success",
5626
6157
  "--text-tertiary",
5627
- "--text-warning"
6158
+ "--text-warning",
6159
+ "--warning"
5628
6160
  ],
5629
6161
  "example": "<ScoreIndicator value={98} max={100} />\n<ScoreIndicator value={42} max={100} size=\"lg\" />\n<ScoreIndicator value={86} max={100} tone=\"success\" denominator=\"below\" />\n"
5630
6162
  },
@@ -5758,73 +6290,297 @@
5758
6290
  ],
5759
6291
  "example": "<SectionHeader title=\"Tonight's events\" meta=\"2 events\" />\n<SectionHeader title=\"Activity\" meta=\"last hour\" />\n<SectionHeader title=\"Top promoters · 30d\" />\n"
5760
6292
  },
5761
- "select": {
5762
- "category": "form",
5763
- "description": "An accessible select component built on Radix UI Select with all sub-components.",
6293
+ "section-intro": {
6294
+ "category": "general",
6295
+ "description": "Marketing section opener with a mono uppercase eyebrow, display-font heading, and optional lede paragraph. Eyebrow color tracks a live-rewritten CSS custom property for brand-accent binding.",
5764
6296
  "when_to_use": [
5765
- "Choosing one option from a predefined list (country, role, status)",
5766
- "Dropdown menus for form fields with 5+ options",
5767
- "Replacing native <select> elements with a styled alternative"
6297
+ "Opening a marketing section with an eyebrow + display heading pattern",
6298
+ "Replacing hand-rolled eyebrow/heading combos that repeat across homepage sections",
6299
+ "Binding the eyebrow color to a keyed brand accent that changes at runtime",
6300
+ "Using left or center alignment for section intros on marketing pages"
5768
6301
  ],
5769
6302
  "when_not_to_use": [
5770
- "Searchable/filterable option lists (use Combobox)",
5771
- "Multi-select with checkboxes (use Combobox or Checkbox group)",
5772
- "Binary on/off choices (use Switch or Checkbox)",
5773
- "Navigation actions (use Dropdown Menu)"
6303
+ "Admin page headers with actions and breadcrumbs (use PageHeader)",
6304
+ "In-page content section dividers with meta labels (use SectionHeader)",
6305
+ "Plain typographic headings inside body copy (use Heading)",
6306
+ "Sections that require icon, badge, or button inside the heading area"
5774
6307
  ],
6308
+ "variants": {
6309
+ "align": [
6310
+ "left",
6311
+ "center"
6312
+ ]
6313
+ },
5775
6314
  "props": [
5776
6315
  {
5777
- "name": "value",
5778
- "type": "string",
5779
- "description": "Controlled selected value"
6316
+ "name": "eyebrow",
6317
+ "type": "React.ReactNode",
6318
+ "description": "Short mono uppercase label rendered above the heading. Reads color from --section-intro-eyebrow-color."
5780
6319
  },
5781
6320
  {
5782
- "name": "defaultValue",
5783
- "type": "string"
6321
+ "name": "heading",
6322
+ "type": "React.ReactNode",
6323
+ "required": true,
6324
+ "description": "Display-font heading — the main marketing statement."
5784
6325
  },
5785
6326
  {
5786
- "name": "onValueChange",
5787
- "type": "(value: string) => void"
6327
+ "name": "lede",
6328
+ "type": "React.ReactNode",
6329
+ "description": "Optional supporting paragraph rendered beneath the heading."
5788
6330
  },
5789
6331
  {
5790
- "name": "disabled",
5791
- "type": "boolean",
5792
- "default": false
6332
+ "name": "align",
6333
+ "type": "'left' | 'center'",
6334
+ "default": "left",
6335
+ "description": "Text alignment for all three slots."
5793
6336
  },
5794
6337
  {
5795
- "name": "placeholder",
5796
- "type": "string",
5797
- "description": "Shown via SelectValue when no value is selected"
6338
+ "name": "headingAs",
6339
+ "type": "'h1' | 'h2' | 'h3'",
6340
+ "default": "h2",
6341
+ "description": "Heading level for the heading slot."
6342
+ },
6343
+ {
6344
+ "name": "as",
6345
+ "type": "'header' | 'div' | 'section'",
6346
+ "default": "header",
6347
+ "description": "Root element tag."
5798
6348
  }
5799
6349
  ],
5800
- "sub_components": [
5801
- {
5802
- "name": "SelectTrigger",
5803
- "description": "The button that opens the select dropdown. Accepts a size prop (\"sm\" | \"md\" | \"lg\")."
5804
- },
6350
+ "dependencies": [],
6351
+ "tokens_used": [
6352
+ "--accent-default",
6353
+ "--font-display",
6354
+ "--font-family-heading",
6355
+ "--font-mono",
6356
+ "--font-size-base",
6357
+ "--font-size-xs",
6358
+ "--font-weight-light",
6359
+ "--font-weight-medium",
6360
+ "--interactive-primary-bg",
6361
+ "--letter-spacing-tight",
6362
+ "--letter-spacing-widest",
6363
+ "--line-height-none",
6364
+ "--line-height-relaxed",
6365
+ "--line-height-tight",
6366
+ "--section-intro-eyebrow-color",
6367
+ "--spacing-4",
6368
+ "--text-primary",
6369
+ "--text-secondary"
6370
+ ],
6371
+ "example": "<SectionIntro\n eyebrow=\"On the map\"\n heading=\"Built for the world's best venues\"\n lede=\"One platform. Every door.\"\n/>\n"
6372
+ },
6373
+ "section-nav": {
6374
+ "category": "navigation",
6375
+ "description": "A link/anchor-based section sub-navigation strip — a leading Phosphor icon + label + optional trailing count pill per item, with a static 2px primary underline on the active item. Items navigate via href (use `asChild` with `next/link` for client-side routing). Distinct from Tabs — no button triggers, no content panels, no animated indicator.",
6376
+ "when_to_use": [
6377
+ "A sub-navigation strip across the top of a detail view whose tabs are real routes (e.g. Organization → Detail / Roles / Invites)",
6378
+ "When each section is its own URL and you want client-side navigation via next/link rather than in-page panel switching",
6379
+ "When sections carry a count (member count, pending invites) you want to surface in a trailing pill"
6380
+ ],
6381
+ "when_not_to_use": [
6382
+ "In-page panel switching where the content lives on the same route (use Tabs)",
6383
+ "Top-level site navigation with brand + actions (use Navbar)",
6384
+ "Paginated record lists (use Pagination)"
6385
+ ],
6386
+ "variants": {},
6387
+ "props": [
5805
6388
  {
5806
- "name": "SelectValue",
5807
- "description": "Displays the selected value or placeholder text inside the trigger."
6389
+ "name": "label",
6390
+ "type": "React.ReactNode",
6391
+ "required": true,
6392
+ "description": "The item's visible label text. (SectionNavItem)"
5808
6393
  },
5809
6394
  {
5810
- "name": "SelectContent",
5811
- "description": "The dropdown container with scroll buttons and portal positioning."
6395
+ "name": "href",
6396
+ "type": "string",
6397
+ "description": "Destination URL. Applied directly when not using `asChild`. (SectionNavItem)"
5812
6398
  },
5813
6399
  {
5814
- "name": "SelectItem",
5815
- "description": "An individual selectable option with a check indicator."
6400
+ "name": "icon",
6401
+ "type": "Icon | React.ReactNode",
6402
+ "description": "Leading icon, accepted in two forms — (1) a Phosphor icon component (`icon={UsersIcon}`), rendered as `<Icon className=... weight=\"regular\" />`; or (2) a rendered element (`icon={<Users size={16} weight=\"bold\" />}`), rendered as-is so its own size/weight/props are preserved, wrapped in the icon slot span. Branches on `React.isValidElement`. (SectionNavItem)\n"
5816
6403
  },
5817
6404
  {
5818
- "name": "SelectGroup",
5819
- "description": "Groups related items together."
6405
+ "name": "count",
6406
+ "type": "number",
6407
+ "description": "Optional trailing count pill value. `0` is rendered; `undefined`/`null` hides the pill. (SectionNavItem)"
5820
6408
  },
5821
6409
  {
5822
- "name": "SelectLabel",
5823
- "description": "A non-selectable label for a group of items."
6410
+ "name": "isActive",
6411
+ "type": "boolean",
6412
+ "default": "false",
6413
+ "description": "Marks the item as the current section — text-primary, 2px primary underline, primary-tinted count pill, and `aria-current=\"page\"`. (SectionNavItem)"
5824
6414
  },
5825
6415
  {
5826
- "name": "SelectSeparator",
5827
- "description": "A visual divider between groups or items."
6416
+ "name": "asChild",
6417
+ "type": "boolean",
6418
+ "default": "false",
6419
+ "description": "Merge the item's chrome onto the immediate child element instead of rendering an `<a>`. Use with `next/link` for client-side navigation. (SectionNavItem)"
6420
+ }
6421
+ ],
6422
+ "dependencies": [
6423
+ "@radix-ui/react-slot",
6424
+ "@phosphor-icons/react",
6425
+ "@loworbitstudio/visor-core"
6426
+ ],
6427
+ "tokens_used": [
6428
+ "--border-default",
6429
+ "--border-focus",
6430
+ "--focus-ring-offset",
6431
+ "--focus-ring-width",
6432
+ "--font-size-sm",
6433
+ "--font-weight-medium",
6434
+ "--hairline",
6435
+ "--motion-duration-fast",
6436
+ "--motion-easing-standard",
6437
+ "--primary",
6438
+ "--radius-full",
6439
+ "--radius-sm",
6440
+ "--spacing-1",
6441
+ "--spacing-2",
6442
+ "--spacing-3",
6443
+ "--spacing-4",
6444
+ "--stroke-width-thin",
6445
+ "--surface-subtle",
6446
+ "--text-primary",
6447
+ "--text-tertiary"
6448
+ ],
6449
+ "example": "import Link from 'next/link'\nimport { UsersIcon, ShieldIcon, EnvelopeIcon } from '@phosphor-icons/react'\nimport { SectionNav, SectionNavItem } from '@/components/ui/section-nav/section-nav'\n\n<SectionNav aria-label=\"Organization sections\">\n <SectionNavItem asChild isActive icon={UsersIcon} label=\"Detail\">\n <Link href=\"/org/detail\" />\n </SectionNavItem>\n <SectionNavItem asChild icon={ShieldIcon} label=\"Roles\" count={4}>\n <Link href=\"/org/roles\" />\n </SectionNavItem>\n <SectionNavItem asChild icon={EnvelopeIcon} label=\"Invites\" count={2}>\n <Link href=\"/org/invites\" />\n </SectionNavItem>\n</SectionNav>\n"
6450
+ },
6451
+ "segmented-progress": {
6452
+ "category": "data-display",
6453
+ "description": "Discrete per-step progress meter — N equal pill segments in a row, each individually expressing done, current, or pending state. Designed for multi-step flows where individual step completion matters, not just an overall percentage.",
6454
+ "when_to_use": [
6455
+ "Multi-step onboarding or wizard flows where each discrete step needs individual visual state",
6456
+ "Brand elicitation, survey, or questionnaire progress where step-level completion is meaningful",
6457
+ "Short sequence indicators (≤12 steps) where a pill-per-step treatment reads clearly",
6458
+ "Inline progress in cards or compact surfaces where a stepper is too heavy"
6459
+ ],
6460
+ "when_not_to_use": [
6461
+ "Use `progress` for continuous values like upload percentage or loading progress",
6462
+ "Use `stepper` when step labels or click-to-navigate behavior is needed",
6463
+ "Sequences longer than ~15 segments (pills become too narrow to read)"
6464
+ ],
6465
+ "variants": {
6466
+ "size": [
6467
+ "sm",
6468
+ "md"
6469
+ ]
6470
+ },
6471
+ "props": [
6472
+ {
6473
+ "name": "total",
6474
+ "type": "number",
6475
+ "required": true,
6476
+ "description": "Total number of segments to render."
6477
+ },
6478
+ {
6479
+ "name": "value",
6480
+ "type": "number",
6481
+ "required": true,
6482
+ "description": "Count of completed segments. Indices 0..value-1 render as done."
6483
+ },
6484
+ {
6485
+ "name": "current",
6486
+ "type": "number",
6487
+ "description": "Optional 0-based index of the in-progress segment. Renders with a primary→muted gradient. Typically equals `value`."
6488
+ },
6489
+ {
6490
+ "name": "size",
6491
+ "type": "\"sm\" | \"md\"",
6492
+ "default": "\"sm\"",
6493
+ "description": "Segment height. sm=6px (prototype height), md=8px."
6494
+ },
6495
+ {
6496
+ "name": "aria-label",
6497
+ "type": "string",
6498
+ "required": true,
6499
+ "description": "Accessible name for the progressbar. Required — describe the current step context (e.g. \"Step 3 of 10\")."
6500
+ }
6501
+ ],
6502
+ "dependencies": [
6503
+ "class-variance-authority",
6504
+ "@loworbitstudio/visor-core"
6505
+ ],
6506
+ "tokens_used": [
6507
+ "--interactive-primary-bg",
6508
+ "--motion-duration-150",
6509
+ "--motion-easing-default",
6510
+ "--primary",
6511
+ "--radius-full",
6512
+ "--spacing-1",
6513
+ "--surface-muted"
6514
+ ],
6515
+ "example": "<SegmentedProgress total={10} value={2} current={2} aria-label=\"Step 2 of 10\" />\n<SegmentedProgress total={6} value={3} aria-label=\"Step 3 of 6\" size=\"md\" />\n"
6516
+ },
6517
+ "select": {
6518
+ "category": "form",
6519
+ "description": "An accessible select component built on Radix UI Select with all sub-components.",
6520
+ "when_to_use": [
6521
+ "Choosing one option from a predefined list (country, role, status)",
6522
+ "Dropdown menus for form fields with 5+ options",
6523
+ "Replacing native <select> elements with a styled alternative"
6524
+ ],
6525
+ "when_not_to_use": [
6526
+ "Searchable/filterable option lists (use Combobox)",
6527
+ "Multi-select with checkboxes (use Combobox or Checkbox group)",
6528
+ "Binary on/off choices (use Switch or Checkbox)",
6529
+ "Navigation actions (use Dropdown Menu)"
6530
+ ],
6531
+ "props": [
6532
+ {
6533
+ "name": "value",
6534
+ "type": "string",
6535
+ "description": "Controlled selected value"
6536
+ },
6537
+ {
6538
+ "name": "defaultValue",
6539
+ "type": "string"
6540
+ },
6541
+ {
6542
+ "name": "onValueChange",
6543
+ "type": "(value: string) => void"
6544
+ },
6545
+ {
6546
+ "name": "disabled",
6547
+ "type": "boolean",
6548
+ "default": false
6549
+ },
6550
+ {
6551
+ "name": "placeholder",
6552
+ "type": "string",
6553
+ "description": "Shown via SelectValue when no value is selected"
6554
+ }
6555
+ ],
6556
+ "sub_components": [
6557
+ {
6558
+ "name": "SelectTrigger",
6559
+ "description": "The button that opens the select dropdown. Accepts a size prop (\"sm\" | \"md\" | \"lg\") and a variant prop (\"default\" | \"borderless\"); borderless drops the border and sits on --field-control-bg to match a borderless Input in an in-dialog form."
6560
+ },
6561
+ {
6562
+ "name": "SelectValue",
6563
+ "description": "Displays the selected value or placeholder text inside the trigger."
6564
+ },
6565
+ {
6566
+ "name": "SelectContent",
6567
+ "description": "The dropdown container with scroll buttons and portal positioning."
6568
+ },
6569
+ {
6570
+ "name": "SelectItem",
6571
+ "description": "An individual selectable option with a check indicator."
6572
+ },
6573
+ {
6574
+ "name": "SelectGroup",
6575
+ "description": "Groups related items together."
6576
+ },
6577
+ {
6578
+ "name": "SelectLabel",
6579
+ "description": "A non-selectable label for a group of items."
6580
+ },
6581
+ {
6582
+ "name": "SelectSeparator",
6583
+ "description": "A visual divider between groups or items."
5828
6584
  }
5829
6585
  ],
5830
6586
  "dependencies": [
@@ -5836,6 +6592,7 @@
5836
6592
  "--border-error",
5837
6593
  "--border-focus",
5838
6594
  "--border-strong",
6595
+ "--field-control-bg",
5839
6596
  "--field-menu-bg",
5840
6597
  "--focus-ring-width",
5841
6598
  "--font-size-base",
@@ -5864,6 +6621,7 @@
5864
6621
  "--surface-interactive-default",
5865
6622
  "--surface-interactive-hover",
5866
6623
  "--surface-popover",
6624
+ "--surface-subtle",
5867
6625
  "--text-primary",
5868
6626
  "--text-secondary"
5869
6627
  ],
@@ -6202,16 +6960,23 @@
6202
6960
  {
6203
6961
  "name": "className",
6204
6962
  "type": "string",
6205
- "description": "Control dimensions and shape (e.g., \"w-full h-4 rounded\")"
6963
+ "description": "Control dimensions and shape (e.g., \"w-full h-4 rounded\"). Opt into a preset silhouette by applying a shape modifier class from the module: shapeLogo (~104x24), shapePill (~64x18), or shapeCircle (square, fully rounded). These layer on top of the base .skeleton class.\n"
6206
6964
  }
6207
6965
  ],
6208
6966
  "dependencies": [],
6209
6967
  "tokens_used": [
6210
6968
  "--_shimmer-duration",
6211
6969
  "--motion-easing-linear",
6970
+ "--radius-full",
6212
6971
  "--radius-md",
6972
+ "--radius-sm",
6213
6973
  "--skeleton-from",
6214
- "--skeleton-to"
6974
+ "--skeleton-logo-height",
6975
+ "--skeleton-logo-width",
6976
+ "--skeleton-pill-height",
6977
+ "--skeleton-pill-width",
6978
+ "--skeleton-to",
6979
+ "--text-primary"
6215
6980
  ],
6216
6981
  "example": "<Skeleton className=\"h-4 w-[200px]\" />\n<Skeleton className=\"h-12 w-12 rounded-full\" />\n"
6217
6982
  },
@@ -6438,6 +7203,116 @@
6438
7203
  ],
6439
7204
  "example": "<Sparkline values={[12, 18, 22, 19, 27, 36, 41, 48, 54, 62, 71, 84]} />\n<Sparkline values={[12, 18, 22, 19, 27, 36, 41, 48, 54, 62, 71, 84]} width={120} height={32} />\n<Sparkline values={[12, 18, 22, 19, 27, 36, 41, 48, 54, 62, 71, 84]} color=\"var(--text-success)\" />\n<Sparkline values={[12, 18, 22, 19, 27, 36, 41, 48, 54, 62, 71, 84]} fluid />\n<Sparkline values={[12, 18, 22, 19, 27, 36, 41, 48, 54, 62, 71, 84]} aria-label=\"RSVPs over the last 12 weeks\" />\n"
6440
7205
  },
7206
+ "specimen-card": {
7207
+ "category": "specimen",
7208
+ "description": "Labeled frame that pairs a context token + \"feel\" descriptor with arbitrary live component children — proving how a brand voice/tone renders on real components.",
7209
+ "when_to_use": [
7210
+ "Displaying tone-by-context previews in a brand workbench grid (e.g. \"error\" + \"warm, accountable\" above a real Alert component)",
7211
+ "Building brand-voice proof blocks that show live components annotated with their voice/tone attribution"
7212
+ ],
7213
+ "when_not_to_use": [
7214
+ "General-purpose card containers (use Card) — SpecimenCard is specifically a labeled component-specimen frame",
7215
+ "Production UI layouts where the context/feel labeling would be confusing to end users"
7216
+ ],
7217
+ "variants": {
7218
+ "footer": [
7219
+ "with-footer",
7220
+ "without-footer"
7221
+ ]
7222
+ },
7223
+ "props": [
7224
+ {
7225
+ "name": "context",
7226
+ "type": "string",
7227
+ "required": true,
7228
+ "description": "Context token label rendered uppercase (e.g. \"error\", \"onboarding\")."
7229
+ },
7230
+ {
7231
+ "name": "feel",
7232
+ "type": "string",
7233
+ "description": "Italic feel descriptor rendered at the end of the label row (e.g. \"warm, accountable\")."
7234
+ }
7235
+ ],
7236
+ "dependencies": [
7237
+ "@loworbitstudio/visor-core"
7238
+ ],
7239
+ "tokens_used": [
7240
+ "--hairline",
7241
+ "--radius-lg",
7242
+ "--shadow-sm",
7243
+ "--spacing-1",
7244
+ "--spacing-1-5",
7245
+ "--spacing-2",
7246
+ "--spacing-3",
7247
+ "--stroke-width-thin",
7248
+ "--surface-card",
7249
+ "--text-secondary",
7250
+ "--text-tertiary"
7251
+ ],
7252
+ "example": "<SpecimenCard context=\"error\" feel=\"warm, accountable\">\n <Alert variant=\"destructive\">Something went wrong — we've got it.</Alert>\n</SpecimenCard>\n\n<SpecimenCard context=\"onboarding\">\n <Button>Get started</Button>\n <Button variant=\"outline\">Learn more</Button>\n <SpecimenCardFooter>voice — precise · warm · direct</SpecimenCardFooter>\n</SpecimenCard>\n"
7253
+ },
7254
+ "spinner": {
7255
+ "category": "data-display",
7256
+ "description": "Inline loading spinner — a rotating border ring with a subtle track and a tone-colored leading edge. Three sizes (xs/sm/md), two tones (default/primary), and full reduced-motion support.",
7257
+ "when_to_use": [
7258
+ "Indicating an async operation is in progress within a button, form field, or inline context",
7259
+ "Replacing text or an icon while content is loading (inline, non-blocking)",
7260
+ "Decorative loading affordance inside a card or panel where a full-page skeleton would be too heavy"
7261
+ ],
7262
+ "when_not_to_use": [
7263
+ "Full-page or full-section loading states (use Skeleton instead)",
7264
+ "Progress that can be measured and communicated as a percentage (use Progress)",
7265
+ "Indicating indeterminate progress for a long-running task visible to the user (use a labeled progress bar)"
7266
+ ],
7267
+ "variants": {
7268
+ "size": [
7269
+ "xs",
7270
+ "sm",
7271
+ "md"
7272
+ ],
7273
+ "tone": [
7274
+ "default",
7275
+ "primary"
7276
+ ]
7277
+ },
7278
+ "props": [
7279
+ {
7280
+ "name": "size",
7281
+ "type": "\"xs\" | \"sm\" | \"md\"",
7282
+ "default": "\"md\"",
7283
+ "description": "Ring size. xs=12px, sm=16px, md=24px."
7284
+ },
7285
+ {
7286
+ "name": "tone",
7287
+ "type": "\"default\" | \"primary\"",
7288
+ "default": "\"default\"",
7289
+ "description": "Color treatment for the leading edge. default=text-tertiary, primary=primary brand token."
7290
+ },
7291
+ {
7292
+ "name": "label",
7293
+ "type": "string",
7294
+ "description": "Accessible label. When provided, renders role=\"status\" with visually-hidden text. When omitted, aria-hidden=\"true\" (decorative)."
7295
+ }
7296
+ ],
7297
+ "dependencies": [
7298
+ "class-variance-authority",
7299
+ "@loworbitstudio/visor-core"
7300
+ ],
7301
+ "tokens_used": [
7302
+ "--border-default",
7303
+ "--motion-duration-1500",
7304
+ "--motion-easing-linear",
7305
+ "--primary",
7306
+ "--radius-full",
7307
+ "--spinner-edge-color",
7308
+ "--spinner-track-color",
7309
+ "--stroke-width-medium",
7310
+ "--stroke-width-regular",
7311
+ "--stroke-width-thin",
7312
+ "--text-tertiary"
7313
+ ],
7314
+ "example": "<Spinner />\n<Spinner size=\"xs\" tone=\"primary\" />\n<Spinner size=\"sm\" label=\"Loading results…\" />\n"
7315
+ },
6441
7316
  "stack": {
6442
7317
  "category": "layout",
6443
7318
  "description": "Vertical flex container with token-driven gap, alignment, and `as` polymorphism.",
@@ -6930,7 +7805,7 @@
6930
7805
  },
6931
7806
  "stepper": {
6932
7807
  "category": "navigation",
6933
- "description": "A multi-step progress indicator with horizontal and vertical orientations and auto-derived step status.",
7808
+ "description": "A multi-step progress indicator with horizontal and vertical orientations, auto-derived step status, and a locked status for non-interactive steps.",
6934
7809
  "when_to_use": [
6935
7810
  "Multi-step forms or wizards showing progress",
6936
7811
  "Onboarding flows with sequential steps",
@@ -6943,6 +7818,18 @@
6943
7818
  "Timelines of past events (use Timeline)",
6944
7819
  "Pagination through pages of content (use Pagination)"
6945
7820
  ],
7821
+ "variants": [
7822
+ {
7823
+ "name": "status",
7824
+ "values": [
7825
+ "complete",
7826
+ "active",
7827
+ "upcoming",
7828
+ "locked"
7829
+ ],
7830
+ "description": "Per-item status override. \"complete\", \"active\", and \"upcoming\" can be auto-derived from activeStep; \"locked\" must always be set explicitly and is never auto-derived. A locked item renders a lock glyph in its trigger circle and its trigger is non-interactive (aria-disabled, removed from tab order).\n"
7831
+ }
7832
+ ],
6946
7833
  "props": [
6947
7834
  {
6948
7835
  "name": "activeStep",
@@ -6963,15 +7850,15 @@
6963
7850
  },
6964
7851
  {
6965
7852
  "name": "StepperItem",
6966
- "description": "Container for a single step with auto-derived status from context."
7853
+ "description": "Container for a single step with auto-derived status from context. Pass status=\"locked\" to override."
6967
7854
  },
6968
7855
  {
6969
7856
  "name": "StepperTrigger",
6970
- "description": "Clickable step indicator circle with status-based styling and check icon."
7857
+ "description": "Clickable step indicator circle with status-based styling; check icon for complete, lock glyph for locked."
6971
7858
  },
6972
7859
  {
6973
7860
  "name": "StepperTitle",
6974
- "description": "Step label text."
7861
+ "description": "Step label text. Rendered in secondary color when the parent item is locked."
6975
7862
  },
6976
7863
  {
6977
7864
  "name": "StepperDescription",
@@ -6994,12 +7881,14 @@
6994
7881
  "--font-size-sm",
6995
7882
  "--font-size-xs",
6996
7883
  "--font-weight-medium",
7884
+ "--font-weight-normal",
6997
7885
  "--interactive-primary-bg",
6998
7886
  "--interactive-primary-text",
6999
7887
  "--line-height-normal",
7000
7888
  "--line-height-snug",
7001
7889
  "--motion-duration-200",
7002
7890
  "--motion-easing-default",
7891
+ "--opacity-60",
7003
7892
  "--primary",
7004
7893
  "--radius-full",
7005
7894
  "--spacing-1",
@@ -7012,6 +7901,97 @@
7012
7901
  ],
7013
7902
  "example": "<Stepper activeStep={1}>\n <StepperItem step={0}>\n <StepperTrigger step={0} />\n <StepperTitle>Account</StepperTitle>\n </StepperItem>\n <StepperSeparator complete />\n <StepperItem step={1}>\n <StepperTrigger step={1} />\n <StepperTitle>Profile</StepperTitle>\n </StepperItem>\n <StepperSeparator />\n <StepperItem step={2}>\n <StepperTrigger step={2} />\n <StepperTitle>Review</StepperTitle>\n </StepperItem>\n</Stepper>\n"
7014
7903
  },
7904
+ "structured-prompt": {
7905
+ "category": "form",
7906
+ "description": "Inline \"mad-lib\" fill-in-the-blank card for structured elicitation flows. A card with an eyebrow header, flowing prose at tall line-height, and inline slot chips (filled or empty) embedded in the prose, plus a hint footer.",
7907
+ "when_to_use": [
7908
+ "Guiding users through structured sentence completion (e.g. \"For [audience], we are the only [category] that [differentiator]\")",
7909
+ "Brand elicitation or onliness-statement workflows where each bracket is a fillable slot",
7910
+ "AI-assisted prompt templates where the model pre-fills slots and the user confirms or edits them",
7911
+ "Any pattern that mixes fixed prose with discrete editable inputs inline in a sentence"
7912
+ ],
7913
+ "when_not_to_use": [
7914
+ "Free-form text entry where sentence structure is not fixed (use Textarea or Input)",
7915
+ "Long multi-field forms (use Field + Fieldset)",
7916
+ "Structured data entry with labels and descriptions (use KeyValueList or a form layout)"
7917
+ ],
7918
+ "variants": {
7919
+ "slot_state": [
7920
+ "filled",
7921
+ "empty"
7922
+ ]
7923
+ },
7924
+ "props": [
7925
+ {
7926
+ "name": "className",
7927
+ "type": "string",
7928
+ "description": "Additional CSS classes for the root card element."
7929
+ },
7930
+ {
7931
+ "name": "icon",
7932
+ "type": "ReactNode",
7933
+ "description": "(StructuredPromptHeader) Phosphor icon rendered before the eyebrow label."
7934
+ },
7935
+ {
7936
+ "name": "filled",
7937
+ "type": "boolean",
7938
+ "default": "false",
7939
+ "description": "(StructuredPromptSlot) When true, renders the primary-soft filled chip. When false, renders the dashed empty chip."
7940
+ },
7941
+ {
7942
+ "name": "onClick",
7943
+ "type": "() => void",
7944
+ "description": "(StructuredPromptSlot) When provided, the slot renders as a focusable <button>. Without onClick it renders as a <span>."
7945
+ }
7946
+ ],
7947
+ "sub_components": [
7948
+ {
7949
+ "name": "StructuredPromptHeader",
7950
+ "description": "Eyebrow header bar with optional Phosphor icon and uppercase label. Renders a hairline bottom border separating it from the body."
7951
+ },
7952
+ {
7953
+ "name": "StructuredPromptBody",
7954
+ "description": "Prose container at tall line-height (2.05) so inline slot chips breathe within the sentence flow."
7955
+ },
7956
+ {
7957
+ "name": "StructuredPromptSlot",
7958
+ "description": "Inline chip embedded in prose. filled=true renders a primary-soft chip; false/absent renders a dashed empty chip. Renders a <button> when onClick is provided (interactive), otherwise a <span> (display-only)."
7959
+ },
7960
+ {
7961
+ "name": "StructuredPromptHint",
7962
+ "description": "Footer hint text at small tertiary size, rendered below the body."
7963
+ }
7964
+ ],
7965
+ "dependencies": [
7966
+ "@loworbitstudio/visor-core"
7967
+ ],
7968
+ "tokens_used": [
7969
+ "--border-default",
7970
+ "--border-focus",
7971
+ "--focus-ring-offset",
7972
+ "--focus-ring-width",
7973
+ "--font-weight-medium",
7974
+ "--font-weight-semibold",
7975
+ "--hairline",
7976
+ "--radius-md",
7977
+ "--radius-xl",
7978
+ "--shadow-sm",
7979
+ "--spacing-0-5",
7980
+ "--spacing-2",
7981
+ "--spacing-2-5",
7982
+ "--spacing-3",
7983
+ "--spacing-3-5",
7984
+ "--stroke-width-thin",
7985
+ "--surface-accent-default",
7986
+ "--surface-accent-subtle",
7987
+ "--surface-card",
7988
+ "--surface-subtle",
7989
+ "--text-link",
7990
+ "--text-primary",
7991
+ "--text-tertiary"
7992
+ ],
7993
+ "example": "<StructuredPrompt>\n <StructuredPromptHeader icon={<ListBullets weight=\"stroke\" />}>\n ONLINESS · THE SPEARHEAD\n </StructuredPromptHeader>\n <StructuredPromptBody>\n For <StructuredPromptSlot filled>design-led product teams</StructuredPromptSlot>,\n Visor is the only <StructuredPromptSlot filled>component system</StructuredPromptSlot>\n that <StructuredPromptSlot>derives every surface from one brand record</StructuredPromptSlot>.\n </StructuredPromptBody>\n <StructuredPromptHint>Click any slot to edit</StructuredPromptHint>\n</StructuredPrompt>\n"
7994
+ },
7015
7995
  "surface-row": {
7016
7996
  "category": "specimen",
7017
7997
  "description": "Surface background variant display showing a surface token with its name and token reference.",
@@ -7055,6 +8035,57 @@
7055
8035
  ],
7056
8036
  "example": "<SurfaceRow token=\"--surface-card\" name=\"Card\" />\n<SurfaceRow token=\"--surface-overlay\" name=\"Overlay\" lightText />\n"
7057
8037
  },
8038
+ "surface-scale-stack": {
8039
+ "category": "specimen",
8040
+ "description": "Multi-tier stacked surface aggregator block. Composes ordered SurfaceRow specimens into a rounded vertical stack with an optional use-note column.",
8041
+ "when_to_use": [
8042
+ "Documenting a full surface scale (e.g. page → screen → panel → panel-2 → panel-3)",
8043
+ "Design system foundation pages showing tier hierarchy",
8044
+ "Theme validation for a complete surface token set"
8045
+ ],
8046
+ "when_not_to_use": [
8047
+ "Displaying a single surface tier (use SurfaceRow)",
8048
+ "Actual page or card surfaces (use the surface tokens directly)"
8049
+ ],
8050
+ "props": [
8051
+ {
8052
+ "name": "surfaces",
8053
+ "type": "SurfaceScaleItem[]",
8054
+ "description": "Ordered array of surface tiers — each with token, name, and optional note"
8055
+ },
8056
+ {
8057
+ "name": "surfaces[].token",
8058
+ "type": "string",
8059
+ "description": "CSS custom property for the surface background (e.g. \"--surface-page\")"
8060
+ },
8061
+ {
8062
+ "name": "surfaces[].name",
8063
+ "type": "string",
8064
+ "description": "Display name for the surface tier"
8065
+ },
8066
+ {
8067
+ "name": "surfaces[].note",
8068
+ "type": "string",
8069
+ "description": "Optional use-note shown in the right-aligned column"
8070
+ },
8071
+ {
8072
+ "name": "surfaces[].lightText",
8073
+ "type": "boolean",
8074
+ "default": "false",
8075
+ "description": "When true, text renders in light colors for dark surfaces"
8076
+ }
8077
+ ],
8078
+ "dependencies": [
8079
+ "surface-row"
8080
+ ],
8081
+ "tokens_used": [
8082
+ "--font-size-xs",
8083
+ "--radius-md",
8084
+ "--spacing-4",
8085
+ "--text-tertiary"
8086
+ ],
8087
+ "example": "<SurfaceScaleStack\n surfaces={[\n { token: \"--surface-page\", name: \"Page\", note: \"Root canvas\" },\n { token: \"--surface-screen\", name: \"Screen\", note: \"Full-bleed section\" },\n { token: \"--surface-card\", name: \"Card\", note: \"Primary content card\" },\n { token: \"--surface-raised\", name: \"Raised\", note: \"Elevated card\" },\n { token: \"--surface-overlay\", name: \"Overlay\", note: \"Modal / sheet\", lightText: true },\n ]}\n/>\n"
8088
+ },
7058
8089
  "switch": {
7059
8090
  "category": "form",
7060
8091
  "description": "An accessible toggle switch component built on Radix UI Switch.",
@@ -7175,15 +8206,22 @@
7175
8206
  "dependencies": [],
7176
8207
  "tokens_used": [
7177
8208
  "--border-default",
8209
+ "--dt-container-radius",
8210
+ "--dt-container-shadow",
8211
+ "--dt-header-bg",
8212
+ "--dt-row-bg",
7178
8213
  "--font-size-sm",
7179
8214
  "--font-weight-medium",
8215
+ "--hairline",
7180
8216
  "--motion-duration-150",
7181
8217
  "--motion-easing-default",
7182
8218
  "--radius-lg",
7183
8219
  "--shadow-sm",
7184
8220
  "--spacing-3",
7185
8221
  "--spacing-4",
8222
+ "--surface-card",
7186
8223
  "--surface-muted",
8224
+ "--surface-page",
7187
8225
  "--text-primary",
7188
8226
  "--text-secondary"
7189
8227
  ],
@@ -7272,18 +8310,20 @@
7272
8310
  "--font-size-sm",
7273
8311
  "--font-size-xs",
7274
8312
  "--font-weight-medium",
8313
+ "--hairline",
7275
8314
  "--motion-duration-150",
7276
8315
  "--motion-duration-normal",
7277
8316
  "--motion-easing-default",
8317
+ "--primary",
7278
8318
  "--radius-full",
7279
8319
  "--radius-md",
7280
8320
  "--shadow-sm",
7281
8321
  "--spacing-1",
7282
8322
  "--spacing-2",
8323
+ "--spacing-3",
7283
8324
  "--surface-accent-subtle",
7284
8325
  "--surface-muted",
7285
8326
  "--surface-page",
7286
- "--tabs-indicator-color",
7287
8327
  "--text-link",
7288
8328
  "--text-primary",
7289
8329
  "--text-secondary"
@@ -7893,6 +8933,37 @@
7893
8933
  ],
7894
8934
  "example": "<TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"ghost\" size=\"sm\"><TrashIcon /></Button>\n </TooltipTrigger>\n <TooltipContent>Delete item</TooltipContent>\n </Tooltip>\n</TooltipProvider>\n"
7895
8935
  },
8936
+ "type-scale-stack": {
8937
+ "category": "specimen",
8938
+ "description": "Discrete N-tier type-scale aggregator that wraps an ordered list of TypeSpecimen rows in a rounded, screen-tier, borderless vertical stack.",
8939
+ "when_to_use": [
8940
+ "Documenting a discrete N-tier type scale on a foundation or design system page",
8941
+ "Displaying a full project type scale (e.g. 11-tier V7 scale) as a single visual block",
8942
+ "Theme validation for the complete set of font size tokens at once"
8943
+ ],
8944
+ "when_not_to_use": [
8945
+ "Rendering actual page content (use Heading or Text)",
8946
+ "Continuous or modular-scale rendering",
8947
+ "Single type scale rows (use TypeSpecimen directly)"
8948
+ ],
8949
+ "props": [
8950
+ {
8951
+ "name": "steps",
8952
+ "type": "TypeScaleStepProps[]",
8953
+ "description": "Ordered array of type scale steps — each with token, label, sizePx, and sample"
8954
+ }
8955
+ ],
8956
+ "dependencies": [
8957
+ "type-specimen"
8958
+ ],
8959
+ "tokens_used": [
8960
+ "--radius-lg",
8961
+ "--spacing-3",
8962
+ "--spacing-4",
8963
+ "--surface-screen"
8964
+ ],
8965
+ "example": "<TypeScaleStack steps={[\n { token: \"--font-size-xs\", label: \"xs\", sizePx: 12, sample: \"Caption / helper text\" },\n { token: \"--font-size-sm\", label: \"sm\", sizePx: 14, sample: \"Small body text\" },\n { token: \"--font-size-md\", label: \"md\", sizePx: 16, sample: \"Base body text\" },\n { token: \"--font-size-lg\", label: \"lg\", sizePx: 18, sample: \"Large body text\" },\n { token: \"--font-size-xl\", label: \"xl\", sizePx: 20, sample: \"Subsection heading\" },\n]} />\n"
8966
+ },
7896
8967
  "type-specimen": {
7897
8968
  "category": "specimen",
7898
8969
  "description": "A single typography row displaying sample text at a given font-size token with weight and size metadata.",
@@ -8440,100 +9511,329 @@
8440
9511
  "--text-on-dark",
8441
9512
  "--text-primary"
8442
9513
  ],
8443
- "example": "<Slide id=\"s-intro\" theme=\"dark\" hero heroImage=\"/hero.jpg\">\n <SlideHeader subtitle=\"Welcome\" title=\"Our Vision\" />\n</Slide>\n"
9514
+ "example": "<Slide id=\"s-intro\" theme=\"dark\" hero heroImage=\"/hero.jpg\">\n <SlideHeader subtitle=\"Welcome\" title=\"Our Vision\" />\n</Slide>\n"
9515
+ },
9516
+ "deck-slide-header": {
9517
+ "category": "deck",
9518
+ "description": "Slide section header with animated subtitle, title, and optional description. Used at the top of content slides to establish section context.",
9519
+ "when_to_use": [
9520
+ "Opening content slides where a section title and subtitle are needed",
9521
+ "Any slide that requires a consistent heading hierarchy with staggered entrance animations"
9522
+ ],
9523
+ "when_not_to_use": [
9524
+ "Hero slides (use HeroSlide which has its own heading layout)",
9525
+ "Slides where the heading needs custom layout or typography"
9526
+ ],
9527
+ "props": [
9528
+ {
9529
+ "name": "subtitle",
9530
+ "type": "string",
9531
+ "description": "Section label displayed above the title"
9532
+ },
9533
+ {
9534
+ "name": "title",
9535
+ "type": "string",
9536
+ "description": "Main heading text"
9537
+ },
9538
+ {
9539
+ "name": "description",
9540
+ "type": "string",
9541
+ "description": "Optional paragraph below the title"
9542
+ }
9543
+ ],
9544
+ "dependencies": [
9545
+ "@loworbitstudio/visor-core"
9546
+ ],
9547
+ "tokens_used": [],
9548
+ "example": "<SlideHeader\n subtitle=\"Phase 1\"\n title=\"Discovery & Research\"\n description=\"Understanding the problem space before committing to solutions.\"\n/>\n"
9549
+ },
9550
+ "slide-theme-context": {
9551
+ "category": "deck",
9552
+ "description": "React context provider that propagates the active slide theme (\"light\" | \"dark\") to child components within a slide.",
9553
+ "when_to_use": [
9554
+ "Building slide sub-components that need to adapt styling to the parent slide's theme",
9555
+ "Any component inside a <Slide> that calls useSlideTheme()"
9556
+ ],
9557
+ "when_not_to_use": [
9558
+ "Outside of a <Slide> wrapper (returns \"light\" as default, not an error, but context is meaningless)",
9559
+ "Choosing between light/dark themes — that is controlled by the slide's theme prop"
9560
+ ],
9561
+ "props": [
9562
+ {
9563
+ "name": "theme",
9564
+ "type": "'light' | 'dark'",
9565
+ "description": "Theme variant propagated to all slide descendants"
9566
+ }
9567
+ ],
9568
+ "dependencies": [
9569
+ "react",
9570
+ "@loworbitstudio/visor-core"
9571
+ ],
9572
+ "tokens_used": [],
9573
+ "example": "// Consumed via useSlideTheme() inside any child of <Slide>\nfunction SlideCard() {\n const theme = useSlideTheme()\n return <div data-theme={theme}>...</div>\n}\n"
9574
+ },
9575
+ "deck-toc-slide": {
9576
+ "category": "deck",
9577
+ "description": "Auto-generated table of contents slide with clickable section + item links for deep navigation. Rendered automatically by DeckRenderer when showTOC is true.",
9578
+ "when_to_use": [
9579
+ "Decks with 3+ sections where an upfront overview helps orientation",
9580
+ "Any deck where users need to jump non-linearly to specific slides"
9581
+ ],
9582
+ "when_not_to_use": [
9583
+ "Short decks (under 6 slides) where a TOC adds noise",
9584
+ "Decks built with DeckRenderer (showTOC=true handles this automatically)"
9585
+ ],
9586
+ "props": [
9587
+ {
9588
+ "name": "sections",
9589
+ "type": "TOCSection[]",
9590
+ "description": "Array of { section: string; items: { id: string; title: string }[] }"
9591
+ },
9592
+ {
9593
+ "name": "backgroundImage",
9594
+ "type": "string",
9595
+ "description": "Optional background image (activates dark theme on the slide)"
9596
+ },
9597
+ {
9598
+ "name": "id",
9599
+ "type": "string",
9600
+ "default": "s-toc"
9601
+ }
9602
+ ],
9603
+ "dependencies": [
9604
+ "@loworbitstudio/visor-core"
9605
+ ],
9606
+ "tokens_used": [],
9607
+ "example": "<TOCSlide\n sections={[\n {\n section: \"Strategy\",\n items: [\n { id: \"s-vision\", title: \"Vision\" },\n { id: \"s-goals\", title: \"Goals\" },\n ],\n },\n ]}\n/>\n"
9608
+ },
9609
+ "ambient-glow": {
9610
+ "category": "visual-elements",
9611
+ "description": "Absolutely-positioned drifting radial glow with live CSS-var-driven color. Decorative only — aria-hidden, pointer-events-none. Ported from the Blacklight marketing depth system (BL-326).",
9612
+ "when_to_use": [
9613
+ "Atmospheric background lighting behind hero sections or focal objects",
9614
+ "Live color-reactive glows that track a dynamically rewritten CSS custom property (e.g. hero crossfade accent)",
9615
+ "Marketing and landing pages that need depth via subtle light fields",
9616
+ "Pro/premium surface accents (gold variant)"
9617
+ ],
9618
+ "when_not_to_use": [
9619
+ "Functional UI elements — AmbientGlow is purely decorative",
9620
+ "When the user prefers reduced motion and you need a visible element (the glow becomes static but stays visible)",
9621
+ {
9622
+ "Inside overflow": "hidden containers without adjusting inset values"
9623
+ }
9624
+ ],
9625
+ "props": [
9626
+ {
9627
+ "name": "variant",
9628
+ "type": "'keyed' | 'gold'",
9629
+ "default": "keyed",
9630
+ "description": "keyed — color-mix from --glow-color (live-rewritable); gold — static warm gold at 7% opacity"
9631
+ },
9632
+ {
9633
+ "name": "glowColor",
9634
+ "type": "string",
9635
+ "description": "Static glow color override — sets --glow-color as an inline custom property. For live runtime rewrites use a parent-element CSS variable instead."
9636
+ },
9637
+ {
9638
+ "name": "className",
9639
+ "type": "string",
9640
+ "description": "Additional class names for positioning and sizing (position/inset/width/height)."
9641
+ },
9642
+ {
9643
+ "name": "style",
9644
+ "type": "React.CSSProperties",
9645
+ "description": "Inline styles, typically inset/width/height. --glow-color can also be set here."
9646
+ }
9647
+ ],
9648
+ "dependencies": [
9649
+ "class-variance-authority",
9650
+ "@loworbitstudio/visor-core"
9651
+ ],
9652
+ "tokens_used": [
9653
+ "--accent",
9654
+ "--glow-color",
9655
+ "--interactive-primary-bg"
9656
+ ],
9657
+ "example": "<div style={{ position: 'relative', width: 400, height: 300 }}>\n <AmbientGlow style={{ inset: 0 }} />\n <p style={{ position: 'relative' }}>Content above the glow</p>\n</div>\n"
9658
+ },
9659
+ "browser-frame": {
9660
+ "category": "visual-elements",
9661
+ "description": "Browser-chrome mockup frame with traffic-light dots and a URL pill. Wraps arbitrary content for marketing and landing surfaces. Elevation-free — compose with .lit / .lit-soft / .lit-strong for depth.",
9662
+ "when_to_use": [
9663
+ "Showing a product screenshot or live embed inside a browser mockup on a marketing or landing page",
9664
+ "EPK-style artist/product demos where the \"in-browser\" framing provides context",
9665
+ "Any dark surface that needs a browser-chrome visual metaphor without hand-rolling the chrome bar"
9666
+ ],
9667
+ "when_not_to_use": [
9668
+ "Real browser chrome or app shells — this is a decorative mockup, not a functional navigation component",
9669
+ "Light-mode surfaces (the default deep-dark background will clash; override --browser-frame-bg to adapt)",
9670
+ "Nested inside another BrowserFrame (a browser inside a browser is confusing)"
9671
+ ],
9672
+ "props": [
9673
+ {
9674
+ "name": "url",
9675
+ "type": "string",
9676
+ "required": true,
9677
+ "description": "Display URL shown in the chrome bar, e.g. \"sharlese.epk.pro\"."
9678
+ },
9679
+ {
9680
+ "name": "href",
9681
+ "type": "string",
9682
+ "required": false,
9683
+ "description": "Optional real link target. When set, the URL pill becomes a clickable <a> with a focus ring."
9684
+ },
9685
+ {
9686
+ "name": "children",
9687
+ "type": "React.ReactNode",
9688
+ "required": true,
9689
+ "description": "Content rendered inside the frame below the chrome bar."
9690
+ },
9691
+ {
9692
+ "name": "className",
9693
+ "type": "string",
9694
+ "required": false,
9695
+ "description": "Additional class name applied to the outer wrapper (e.g. to set --lit-color or --browser-frame-focus-color)."
9696
+ }
9697
+ ],
9698
+ "dependencies": [
9699
+ "@loworbitstudio/visor-core",
9700
+ "@phosphor-icons/react"
9701
+ ],
9702
+ "tokens_used": [
9703
+ "--accent",
9704
+ "--browser-frame-bg",
9705
+ "--browser-frame-focus-color",
9706
+ "--focus-ring-offset",
9707
+ "--focus-ring-width",
9708
+ "--font-weight-light",
9709
+ "--hairline",
9710
+ "--hairline-strong",
9711
+ "--motion-duration-fast",
9712
+ "--motion-easing-standard",
9713
+ "--opacity-10",
9714
+ "--opacity-12",
9715
+ "--opacity-5",
9716
+ "--opacity-60",
9717
+ "--opacity-80",
9718
+ "--radius-md",
9719
+ "--radius-xl",
9720
+ "--spacing-1",
9721
+ "--spacing-10",
9722
+ "--spacing-3",
9723
+ "--spacing-3_5",
9724
+ "--stroke-width-thin"
9725
+ ],
9726
+ "example": "{/* Static display-only */}\n<BrowserFrame url=\"myproduct.example.com\">\n <img src=\"/screenshot.png\" alt=\"Product screenshot\" />\n</BrowserFrame>\n\n{/* Clickable URL pill */}\n<BrowserFrame url=\"sharlese.epk.pro\" href=\"https://sharlese.epk.pro\">\n <div style={{ padding: '24px' }}>Artist EPK content</div>\n</BrowserFrame>\n\n{/* With lit elevation from visor-core/utilities */}\n<BrowserFrame\n url=\"sharlese.epk.pro\"\n href=\"https://sharlese.epk.pro\"\n className=\"lit\"\n style={{ '--browser-frame-focus-color': '#c8ff00' }}\n>\n <div style={{ padding: '24px' }}>Artist EPK content</div>\n</BrowserFrame>\n"
8444
9727
  },
8445
- "deck-slide-header": {
8446
- "category": "deck",
8447
- "description": "Slide section header with animated subtitle, title, and optional description. Used at the top of content slides to establish section context.",
9728
+ "card-lift": {
9729
+ "category": "visual-elements",
9730
+ "description": "CSS-only hover-lift wrapper translateY(-4px) + deep shadow + live-keyed color-mix halo on hover. Port of .bl-card-lift from blacklight-website (BL-326).",
8448
9731
  "when_to_use": [
8449
- "Opening content slides where a section title and subtitle are needed",
8450
- "Any slide that requires a consistent heading hierarchy with staggered entrance animations"
9732
+ "Adding hover-lift affordance to cards, tiles, or any clickable surface",
9733
+ "Marketing pages and showcase walls where surfaces should \"catch the light\" on hover",
9734
+ "Keying the halo color to a live CSS variable (e.g. artist accent color) that changes at runtime"
8451
9735
  ],
8452
9736
  "when_not_to_use": [
8453
- "Hero slides (use HeroSlide which has its own heading layout)",
8454
- "Slides where the heading needs custom layout or typography"
9737
+ "Standard admin/app UI where motion-heavy hover effects are distracting",
9738
+ "When you need the entire hover interaction (border, background, text) — CardLift is a motion-only layer",
9739
+ "Contexts where `prefers-reduced-motion` users will miss the affordance (pair with a visible focus style on the inner element)"
8455
9740
  ],
8456
9741
  "props": [
8457
9742
  {
8458
- "name": "subtitle",
9743
+ "name": "liftColor",
8459
9744
  "type": "string",
8460
- "description": "Section label displayed above the title"
9745
+ "default": "var(--accent, #6366f1)",
9746
+ "description": "CSS color for the halo on hover. Accepts any CSS color or a `var()` reference. Written to `--lift-color` inline; the halo tracks live rewrites at paint time.\n"
8461
9747
  },
8462
9748
  {
8463
- "name": "title",
8464
- "type": "string",
8465
- "description": "Main heading text"
9749
+ "name": "children",
9750
+ "type": "React.ReactNode",
9751
+ "description": "Content to wrap (typically a Card or BentoGrid tile)."
8466
9752
  },
8467
9753
  {
8468
- "name": "description",
9754
+ "name": "className",
8469
9755
  "type": "string",
8470
- "description": "Optional paragraph below the title"
9756
+ "description": "Additional CSS class names to merge onto the wrapper."
9757
+ },
9758
+ {
9759
+ "name": "style",
9760
+ "type": "React.CSSProperties",
9761
+ "description": "Inline styles merged onto the wrapper (after `--lift-color`)."
8471
9762
  }
8472
9763
  ],
8473
9764
  "dependencies": [
8474
9765
  "@loworbitstudio/visor-core"
8475
9766
  ],
8476
- "tokens_used": [],
8477
- "example": "<SlideHeader\n subtitle=\"Phase 1\"\n title=\"Discovery & Research\"\n description=\"Understanding the problem space before committing to solutions.\"\n/>\n"
9767
+ "tokens_used": [
9768
+ "--accent",
9769
+ "--lift-color",
9770
+ "--shadow-"
9771
+ ],
9772
+ "example": "<CardLift liftColor=\"var(--accent)\">\n <Card>\n <CardContent>Hover me</CardContent>\n </Card>\n</CardLift>\n"
8478
9773
  },
8479
- "slide-theme-context": {
8480
- "category": "deck",
8481
- "description": "React context provider that propagates the active slide theme (\"light\" | \"dark\") to child components within a slide.",
9774
+ "grain-overlay": {
9775
+ "category": "visual-elements",
9776
+ "description": "Fixed, full-viewport film-grain noise layer. Ported from Blacklight's bl-grain depth-system primitive (BL-326). Decorative only pointer-events-none, aria-hidden.",
8482
9777
  "when_to_use": [
8483
- "Building slide sub-components that need to adapt styling to the parent slide's theme",
8484
- "Any component inside a <Slide> that calls useSlideTheme()"
9778
+ "Dark marketing surfaces where large near-black fills read as flat and digital",
9779
+ "Hero sections or landing pages with a cinema/atmospheric aesthetic",
9780
+ "Any surface using the Blacklight or similar dark depth-system look"
8485
9781
  ],
8486
9782
  "when_not_to_use": [
8487
- "Outside of a <Slide> wrapper (returns \"light\" as default, not an error, but context is meaningless)",
8488
- "Choosing between light/dark themes that is controlled by the slide's theme prop"
9783
+ "Light-mode surfaces (grain is near-invisible and adds nothing)",
9784
+ "Pages where every byte of bundle size counts (the SVG data URI is ~250 bytes fine in practice)",
9785
+ "Animated grain effects (this is intentionally static; animated grain repaints the full viewport every frame)"
8489
9786
  ],
8490
9787
  "props": [
8491
9788
  {
8492
- "name": "theme",
8493
- "type": "'light' | 'dark'",
8494
- "description": "Theme variant propagated to all slide descendants"
9789
+ "name": "opacity",
9790
+ "type": "number",
9791
+ "default": "0.035",
9792
+ "description": "Grain opacity (0–1). Default matches the Blacklight bl-grain reference."
9793
+ },
9794
+ {
9795
+ "name": "zIndex",
9796
+ "type": "number",
9797
+ "default": "30",
9798
+ "description": "z-index of the overlay layer. Default 30 sits above page sections but below modals (z-100)."
8495
9799
  }
8496
9800
  ],
8497
9801
  "dependencies": [
8498
- "react",
8499
9802
  "@loworbitstudio/visor-core"
8500
9803
  ],
8501
9804
  "tokens_used": [],
8502
- "example": "// Consumed via useSlideTheme() inside any child of <Slide>\nfunction SlideCard() {\n const theme = useSlideTheme()\n return <div data-theme={theme}>...</div>\n}\n"
9805
+ "example": "{/* Drop at the top of your dark page layout — renders nothing visible on its own */}\n<GrainOverlay />\n\n{/* Custom opacity for a more prominent grain */}\n<GrainOverlay opacity={0.06} />\n"
8503
9806
  },
8504
- "deck-toc-slide": {
8505
- "category": "deck",
8506
- "description": "Auto-generated table of contents slide with clickable section + item links for deep navigation. Rendered automatically by DeckRenderer when showTOC is true.",
9807
+ "hero-glow": {
9808
+ "category": "visual-elements",
9809
+ "description": "Breathing radial glow band for hero media. Color is driven by a live CSS custom property (--glow-color) so the caller can rewrite it every rAF frame without triggering a React re-render.",
8507
9810
  "when_to_use": [
8508
- "Decks with 3+ sections where an upfront overview helps orientation",
8509
- "Any deck where users need to jump non-linearly to specific slides"
9811
+ "Behind hero images or carousels to cast a color-keyed ambient glow",
9812
+ "When the glow color must track a live value (e.g. current artist color, active palette)",
9813
+ "Marketing hero sections that need atmospheric depth without a heavy shader"
8510
9814
  ],
8511
9815
  "when_not_to_use": [
8512
- "Short decks (under 6 slides) where a TOC adds noise",
8513
- "Decks built with DeckRenderer (showTOC=true handles this automatically)"
8514
- ],
8515
- "props": [
8516
- {
8517
- "name": "sections",
8518
- "type": "TOCSection[]",
8519
- "description": "Array of { section: string; items: { id: string; title: string }[] }"
8520
- },
9816
+ "As a layout element HeroGlow is purely decorative (aria-hidden, pointer-events none)",
8521
9817
  {
8522
- "name": "backgroundImage",
8523
- "type": "string",
8524
- "description": "Optional background image (activates dark theme on the slide)"
9818
+ "When the parent is not position": "relative (the negative inset will be clipped or invisible)"
8525
9819
  },
9820
+ "When a static, non-breathing glow is preferred (use a box-shadow or plain gradient instead)"
9821
+ ],
9822
+ "props": [
8526
9823
  {
8527
- "name": "id",
9824
+ "name": "glowColor",
8528
9825
  "type": "string",
8529
- "default": "s-toc"
9826
+ "description": "CSS color value applied as --glow-color. Accepts any CSS color (hex, oklch, rgb, custom property reference). When omitted, --glow-color must be set by an ancestor for the glow to appear; the fallback is transparent.\n",
9827
+ "default": "undefined"
8530
9828
  }
8531
9829
  ],
8532
9830
  "dependencies": [
8533
9831
  "@loworbitstudio/visor-core"
8534
9832
  ],
8535
- "tokens_used": [],
8536
- "example": "<TOCSlide\n sections={[\n {\n section: \"Strategy\",\n items: [\n { id: \"s-vision\", title: \"Vision\" },\n { id: \"s-goals\", title: \"Goals\" },\n ],\n },\n ]}\n/>\n"
9833
+ "tokens_used": [
9834
+ "--glow-color"
9835
+ ],
9836
+ "example": "{/* Static color */}\n<div style={{ position: 'relative', height: 400 }}>\n <HeroGlow glowColor=\"oklch(70% 0.3 145)\" />\n <img src=\"hero.jpg\" alt=\"Hero\" />\n</div>\n\n{/* Live rAF-driven color (no React re-render) */}\n<div\n ref={(el) => {\n if (!el) return;\n const glow = el.querySelector('[data-slot=\"hero-glow\"]');\n let frame: number;\n const tick = () => {\n glow?.style.setProperty('--glow-color', computeArtistColor());\n frame = requestAnimationFrame(tick);\n };\n frame = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(frame);\n }}\n style={{ position: 'relative' }}\n>\n <HeroGlow />\n <img src=\"hero.jpg\" alt=\"Hero\" />\n</div>\n"
8537
9837
  },
8538
9838
  "sphere": {
8539
9839
  "category": "visual-elements",
@@ -8625,6 +9925,50 @@
8625
9925
  ],
8626
9926
  "tokens_used": [],
8627
9927
  "example": "<Sphere\n mode=\"sphere\"\n colorScheme=\"solar\"\n speed={1}\n style={{ width: \"100%\", height: \"400px\" }}\n/>\n"
9928
+ },
9929
+ "vignette": {
9930
+ "category": "visual-elements",
9931
+ "description": "Fixed, full-viewport radial vignette layer. Decorative atmosphere atom — pointer-events-none, aria-hidden, static. Ported from Blacklight bl-vignette (BL-326).",
9932
+ "when_to_use": [
9933
+ "Adding cinematic depth to dark-background hero sections",
9934
+ "Creating stage-lighting atmosphere on full-viewport canvases",
9935
+ "Softening hard edges on near-black page backgrounds"
9936
+ ],
9937
+ "when_not_to_use": [
9938
+ "Light-mode pages (the dark falloff color will be jarring)",
9939
+ "Content-heavy pages where the dark vignette obscures readable text at the edges",
9940
+ "When you need a scrolling (non-fixed) overlay — Vignette is always fixed"
9941
+ ],
9942
+ "props": [
9943
+ {
9944
+ "name": "zIndex",
9945
+ "type": "number",
9946
+ "default": 20,
9947
+ "description": "CSS z-index for the vignette layer"
9948
+ },
9949
+ {
9950
+ "name": "className",
9951
+ "type": "string",
9952
+ "description": "Additional class names"
9953
+ },
9954
+ {
9955
+ "name": "style",
9956
+ "type": "React.CSSProperties",
9957
+ "description": "Inline styles; use CSS custom properties to tune the gradient: --vignette-transparent-stop (52%), --vignette-color-stop (100%), --vignette-color (rgba(0,0,6,0.5)), --vignette-size-x (120%), --vignette-size-y (90%), --vignette-position (50% 38%)\n"
9958
+ }
9959
+ ],
9960
+ "dependencies": [
9961
+ "@loworbitstudio/visor-core"
9962
+ ],
9963
+ "tokens_used": [
9964
+ "--vignette-color",
9965
+ "--vignette-color-stop",
9966
+ "--vignette-position",
9967
+ "--vignette-size-x",
9968
+ "--vignette-size-y",
9969
+ "--vignette-transparent-stop"
9970
+ ],
9971
+ "example": "<div style={{ position: 'relative', height: '400px', background: '#020214' }}>\n <Vignette />\n</div>\n"
8628
9972
  }
8629
9973
  },
8630
9974
  "blocks": {
@@ -9062,7 +10406,7 @@
9062
10406
  },
9063
10407
  "right-rail-list": {
9064
10408
  "category": "data-display",
9065
- "description": "Compact vertical list block tuned for admin dashboard side rails. Each row pairs an optional leading slot (short label, avatar, badge, status dot), a primary label (typically a link), and an optional trailing meta value (count, value, tone-tinted status word). Trailing accepts default / mint / muted / warn tones that bind to the shared semantic text tokens. Supports compact density and ul / ol / div root element variants.",
10409
+ "description": "Compact vertical list block tuned for admin dashboard side rails. Each row pairs an optional leading slot (short label, avatar, badge, status dot), a primary label (typically a link), and an optional trailing meta value (count, value, tone-tinted status word). Trailing accepts default / mint / muted / warn tones that bind to the shared semantic text tokens. Supports compact density, xs row text size for dense rails, and ul / ol / div root element variants.",
9066
10410
  "components_used": [],
9067
10411
  "when_to_use": [
9068
10412
  "Side-rail summary panels in dashboards (this-week digests, top-N rankings, status callouts)",
@@ -9605,6 +10949,10 @@
9605
10949
  "Sign-in and sign-up pages",
9606
10950
  "Forgot password and reset password flows",
9607
10951
  "Invitation acceptance screens"
10952
+ ],
10953
+ "when_not_to_use": [
10954
+ "Collecting setup preferences after the user is already signed in — use onboarding-flow",
10955
+ "A standalone validated form that is not authentication — use form-with-validation"
9608
10956
  ]
9609
10957
  },
9610
10958
  "card-grid": {
@@ -9621,30 +10969,19 @@
9621
10969
  "Displaying a collection of items as visual tiles (projects, products, team members)",
9622
10970
  "When a table layout is too data-dense and card previews are more scannable",
9623
10971
  "Catalog or gallery pages with paginated content"
9624
- ]
9625
- },
9626
- "crud-table": {
9627
- "description": "Data table with row actions, confirmation dialog, and pagination for managing collections of records.",
9628
- "components_used": [
9629
- "table",
9630
- "button",
9631
- "dropdown-menu",
9632
- "dialog",
9633
- "alert",
9634
- "pagination",
9635
- "badge"
9636
10972
  ],
9637
- "when_to_use": [
9638
- "Admin pages listing and managing resources (users, orders, products)",
9639
- "Any collection view with create, read, update, delete operations",
9640
- "Data tables needing row-level actions and bulk operations"
10973
+ "when_not_to_use": [
10974
+ "Results driven by a search query or type-ahead — use search-results",
10975
+ "Tabular records needing sorting and filtering rather than cards — use data-table-with-filters"
9641
10976
  ]
9642
10977
  },
9643
10978
  "dashboard-layout": {
9644
- "description": "App shell with collapsible sidebar navigation, top navbar, and content area with cards and tabs.",
10979
+ "description": "Responsive app shell a persistent sidebar on desktop that collapses to a Sheet drawer on mobile — with a top navbar and a content area of cards and tabs.",
9645
10980
  "components_used": [
9646
10981
  "sidebar",
10982
+ "sheet",
9647
10983
  "navbar",
10984
+ "button",
9648
10985
  "card",
9649
10986
  "tabs",
9650
10987
  "separator",
@@ -9652,12 +10989,17 @@
9652
10989
  ],
9653
10990
  "when_to_use": [
9654
10991
  "Admin dashboards and internal tools",
9655
- "Multi-section apps with persistent navigation",
9656
- "Content-heavy layouts needing sidebar + main area structure"
10992
+ "Multi-section apps with persistent navigation that must also work on mobile",
10993
+ "Content-heavy layouts needing sidebar + main area structure",
10994
+ "Any app shell where the desktop sidebar should collapse to a drawer without duplicating nav markup"
10995
+ ],
10996
+ "when_not_to_use": [
10997
+ "A single configuration screen rather than the whole app shell — use settings-page",
10998
+ "Just the tabular data inside the shell — use data-table-with-filters or data-table-row-actions"
9657
10999
  ]
9658
11000
  },
9659
11001
  "data-table-row-actions": {
9660
- "description": "A data table with per-row action menus, bulk selection with a bulk action bar, confirmation dialogs, and toast feedback for destructive operations.",
11002
+ "description": "A data table for managing collections of records (CRUD) — per-row action menus, bulk selection with a bulk action bar, confirmation dialogs, and toast feedback for destructive operations.",
9661
11003
  "components_used": [
9662
11004
  "data-table",
9663
11005
  "dropdown-menu",
@@ -9667,9 +11009,13 @@
9667
11009
  "bulk-action-bar"
9668
11010
  ],
9669
11011
  "when_to_use": [
11012
+ "Admin pages listing and managing resources (users, orders, products)",
9670
11013
  "Admin lists where rows have contextual actions (edit, archive, delete)",
9671
11014
  "When supporting both single-row and bulk operations on the same table",
9672
- "Any CRUD interface where destructive actions require confirmation"
11015
+ "Any CRUD interface (create, read, update, delete) where destructive actions require confirmation"
11016
+ ],
11017
+ "when_not_to_use": [
11018
+ "Read-heavy lists whose main need is search, filtering, and sorting rather than row mutations — use data-table-with-filters"
9673
11019
  ]
9674
11020
  },
9675
11021
  "data-table-with-filters": {
@@ -9687,21 +11033,9 @@
9687
11033
  "Data-heavy pages where users need to find specific records",
9688
11034
  "Admin dashboards with sortable, filterable lists",
9689
11035
  "Search result pages with faceted filtering"
9690
- ]
9691
- },
9692
- "empty-state": {
9693
- "description": "Placeholder shown when a page, list, or search has no content to display.",
9694
- "components_used": [
9695
- "card",
9696
- "button",
9697
- "heading",
9698
- "text"
9699
11036
  ],
9700
- "when_to_use": [
9701
- "Empty lists or tables (no data yet)",
9702
- "Search results returning zero matches",
9703
- "First-time user onboarding (no content created yet)",
9704
- "Error states where content failed to load"
11037
+ "when_not_to_use": [
11038
+ "Per-row action menus, bulk selection, and confirm-on-delete on the table — use data-table-row-actions"
9705
11039
  ]
9706
11040
  },
9707
11041
  "form-with-validation": {
@@ -9718,6 +11052,10 @@
9718
11052
  "Any form collecting user input (settings, profile, onboarding)",
9719
11053
  "Forms needing inline validation feedback",
9720
11054
  "Multi-field forms with consistent spacing and error display"
11055
+ ],
11056
+ "when_not_to_use": [
11057
+ "A form presented inside a modal or dialog overlay — use modal-form",
11058
+ "A multi-step guided flow rather than a single form — use onboarding-flow"
9721
11059
  ]
9722
11060
  },
9723
11061
  "modal-form": {
@@ -9735,6 +11073,10 @@
9735
11073
  "Creating or editing a record without leaving the current page",
9736
11074
  "Collecting a small set of inputs (fewer than 8 fields)",
9737
11075
  "When the action is contextual to the parent list or table"
11076
+ ],
11077
+ "when_not_to_use": [
11078
+ "A full-page or inline form that is not in an overlay — use form-with-validation",
11079
+ "A multi-step guided flow — use onboarding-flow"
9738
11080
  ]
9739
11081
  },
9740
11082
  "notification-center": {
@@ -9750,10 +11092,14 @@
9750
11092
  "Apps with persistent in-app notifications or activity alerts",
9751
11093
  "When users need to review, dismiss, or act on multiple notifications at once",
9752
11094
  "Combining real-time toast feedback with a browsable notification history"
11095
+ ],
11096
+ "when_not_to_use": [
11097
+ "A single transient confirmation after one action — use a bare Toast, not the full center",
11098
+ "A command palette for navigation or search — use search-results"
9753
11099
  ]
9754
11100
  },
9755
11101
  "onboarding-flow": {
9756
- "description": "A step-by-step welcome flow that collects user preferences and setup choices using progress tracking, cards, and inline forms with checkbox selections.",
11102
+ "description": "A multi-step guided flow — first-run onboarding, setup wizards, or checkout — that breaks a long or gated process into validated steps with progress tracking, cards, and inline forms.",
9757
11103
  "components_used": [
9758
11104
  "stepper",
9759
11105
  "progress",
@@ -9767,23 +11113,14 @@
9767
11113
  "when_to_use": [
9768
11114
  "First-run experiences after account creation",
9769
11115
  "Feature setup flows where configuration choices shape the initial workspace",
9770
- "Product tours that collect preferences before showing the main dashboard"
9771
- ]
9772
- },
9773
- "responsive-sidebar-layout": {
9774
- "description": "A full-app shell that renders a persistent sidebar on desktop and a Sheet-based drawer on mobile, sharing the same navigation content.",
9775
- "components_used": [
9776
- "sidebar",
9777
- "sheet",
9778
- "navbar",
9779
- "button",
9780
- "separator",
9781
- "scroll-area"
11116
+ "Product tours that collect preferences before showing the main dashboard",
11117
+ "Checkout or other multi-step sequences that stage a long process",
11118
+ "When a single long form would overwhelm the user — break into logical steps",
11119
+ "Flows where earlier steps gate or inform later steps"
9782
11120
  ],
9783
- "when_to_use": [
9784
- "Any multi-section app needing sidebar navigation that must work on mobile",
9785
- "When the desktop sidebar should collapse to a drawer without duplicating nav markup",
9786
- "Admin tools, dashboards, or SaaS apps requiring a responsive shell"
11121
+ "when_not_to_use": [
11122
+ "Login or signup itself use auth-flow",
11123
+ "A single validated form rather than a multi-step flow use form-with-validation"
9787
11124
  ]
9788
11125
  },
9789
11126
  "search-results": {
@@ -9800,6 +11137,9 @@
9800
11137
  "Search pages where users need to filter and browse a large dataset",
9801
11138
  "When combining free-text search with faceted category filters",
9802
11139
  "Directory, catalog, or discovery interfaces with card-based result display"
11140
+ ],
11141
+ "when_not_to_use": [
11142
+ "A static grid of cards not driven by a query — use card-grid"
9803
11143
  ]
9804
11144
  },
9805
11145
  "settings-page": {
@@ -9821,23 +11161,10 @@
9821
11161
  "User profile and account settings",
9822
11162
  "Application preferences and configuration",
9823
11163
  "Team or organization settings"
9824
- ]
9825
- },
9826
- "wizard-flow": {
9827
- "description": "A multi-step guided flow with per-step validation, progress tracking, and a linear navigation structure for complex data collection.",
9828
- "components_used": [
9829
- "stepper",
9830
- "field",
9831
- "input",
9832
- "button",
9833
- "alert",
9834
- "progress",
9835
- "separator"
9836
11164
  ],
9837
- "when_to_use": [
9838
- "Onboarding flows, setup wizards, or checkout sequences",
9839
- "When a single long form would overwhelm the userbreak into logical steps",
9840
- "Flows where earlier steps gate or inform later steps"
11165
+ "when_not_to_use": [
11166
+ "The whole app shell and navigation chrome — use dashboard-layout",
11167
+ "A single focused form rather than grouped settings sections use form-with-validation"
9841
11168
  ]
9842
11169
  }
9843
11170
  },
@@ -9853,7 +11180,10 @@
9853
11180
  "opacity-bar",
9854
11181
  "radius-scale",
9855
11182
  "spacing-scale",
11183
+ "specimen-card",
9856
11184
  "surface-row",
11185
+ "surface-scale-stack",
11186
+ "type-scale-stack",
9857
11187
  "type-specimen"
9858
11188
  ],
9859
11189
  "data-display": [
@@ -9866,7 +11196,9 @@
9866
11196
  "key-value-list",
9867
11197
  "marquee",
9868
11198
  "name-roster",
11199
+ "segmented-progress",
9869
11200
  "sparkline",
11201
+ "spinner",
9870
11202
  "status-dot",
9871
11203
  "table",
9872
11204
  "timeline"
@@ -9879,7 +11211,9 @@
9879
11211
  "data-table",
9880
11212
  "empty-state",
9881
11213
  "filter-bar",
11214
+ "infographic-bar",
9882
11215
  "kbd",
11216
+ "matrix-table",
9883
11217
  "score-indicator",
9884
11218
  "stat-card",
9885
11219
  "stat-hero",
@@ -9888,6 +11222,7 @@
9888
11222
  "feedback": [
9889
11223
  "alert",
9890
11224
  "banner",
11225
+ "challenge-card",
9891
11226
  "popover",
9892
11227
  "progress",
9893
11228
  "skeleton",
@@ -9913,6 +11248,7 @@
9913
11248
  "pagination",
9914
11249
  "quick-actions",
9915
11250
  "section-header",
11251
+ "section-nav",
9916
11252
  "stepper",
9917
11253
  "tabs"
9918
11254
  ],
@@ -9923,6 +11259,7 @@
9923
11259
  "chip",
9924
11260
  "color-picker",
9925
11261
  "combobox",
11262
+ "composer",
9926
11263
  "date-picker",
9927
11264
  "date-range-picker",
9928
11265
  "field",
@@ -9940,6 +11277,7 @@
9940
11277
  "select",
9941
11278
  "slider",
9942
11279
  "slider-control",
11280
+ "structured-prompt",
9943
11281
  "switch",
9944
11282
  "tag-input",
9945
11283
  "textarea",
@@ -9964,13 +11302,20 @@
9964
11302
  "menubar",
9965
11303
  "sheet"
9966
11304
  ],
9967
- "visual-elements": [
9968
- "sphere",
9969
- "station-spectrum"
9970
- ],
9971
11305
  "general": [
11306
+ "section-intro",
9972
11307
  "theme-switcher"
9973
11308
  ],
11309
+ "visual-elements": [
11310
+ "ambient-glow",
11311
+ "browser-frame",
11312
+ "card-lift",
11313
+ "grain-overlay",
11314
+ "hero-glow",
11315
+ "sphere",
11316
+ "station-spectrum",
11317
+ "vignette"
11318
+ ],
9974
11319
  "deck": [
9975
11320
  "deck-card-grid",
9976
11321
  "deck-carousel-gallery",
@@ -9988,6 +11333,235 @@
9988
11333
  "slide-theme-context"
9989
11334
  ]
9990
11335
  },
11336
+ "brand_strategy": {
11337
+ "positioning": {
11338
+ "onliness": "The only design system that compiles a complete brand — visual and verbal — from one portable file, for humans and agents alike.",
11339
+ "category": "design system",
11340
+ "differentiation": "brand strategy, visual and verbal, as derivable machine-readable data"
11341
+ },
11342
+ "essence": [
11343
+ "coherent",
11344
+ "open",
11345
+ "yours"
11346
+ ],
11347
+ "personality": [
11348
+ {
11349
+ "trait": "precise",
11350
+ "not": "fussy"
11351
+ },
11352
+ {
11353
+ "trait": "candid",
11354
+ "not": "cold"
11355
+ },
11356
+ {
11357
+ "trait": "generous",
11358
+ "not": "indulgent"
11359
+ },
11360
+ {
11361
+ "trait": "warm",
11362
+ "not": "saccharine"
11363
+ }
11364
+ ],
11365
+ "archetype": {
11366
+ "primary": "sage",
11367
+ "secondary": "creator",
11368
+ "tertiary": "everyman"
11369
+ },
11370
+ "pillars": [
11371
+ {
11372
+ "id": "coherence",
11373
+ "statement": "Every layer derives from the one above it.",
11374
+ "governs": {
11375
+ "tokens": [
11376
+ "--primary",
11377
+ "--surface-card",
11378
+ "--text-primary"
11379
+ ],
11380
+ "components": [
11381
+ "*"
11382
+ ]
11383
+ },
11384
+ "proof": [
11385
+ "Switch theme or mode and the entire surface re-resolves — nothing is pinned to a hard-coded value.",
11386
+ "Every component reads tokens, never literals: --primary flows to semantic, then adaptive, then the rendered pixel.",
11387
+ "Change one .visor.yaml file and the whole system follows — the file is the single source the cascade derives from."
11388
+ ]
11389
+ },
11390
+ {
11391
+ "id": "openness",
11392
+ "statement": "The whole system is open — readable by humans and agents, and free to take.",
11393
+ "governs": {
11394
+ "surfaces": [
11395
+ "manifest",
11396
+ "cli",
11397
+ "component-metadata"
11398
+ ]
11399
+ },
11400
+ "proof": [
11401
+ "An agent can discover, select, and compose a component from structured data alone — the manifest, when_to_use metadata, and an agent-first CLI.",
11402
+ "The source is open and free to take; the same file an engineer reads is the one an agent queries.",
11403
+ "Even brand strategy ships as readable data — this record — not a locked PDF."
11404
+ ]
11405
+ },
11406
+ {
11407
+ "id": "ownership",
11408
+ "statement": "Copy-and-own. You hold the source; there's no lock-in, ever.",
11409
+ "governs": {
11410
+ "components": [
11411
+ "*"
11412
+ ]
11413
+ },
11414
+ "proof": [
11415
+ "npx visor add button copies real source into your project — yours to edit, with nothing to eject from.",
11416
+ "Tokens still update via npm update, so you keep design consistency without surrendering control.",
11417
+ "Copy-and-own is the starting state, not an escape hatch — there's no wrapper to fight."
11418
+ ]
11419
+ }
11420
+ ],
11421
+ "voice": {
11422
+ "traits": [
11423
+ {
11424
+ "name": "plainspoken",
11425
+ "do": "Say it in one clause. Lead with the answer.",
11426
+ "dont": "Bury the point under qualifiers and throat-clearing.",
11427
+ "example": "Copy-and-own is just that — the source is yours. Edit anything; there's no wrapper to fight."
11428
+ },
11429
+ {
11430
+ "name": "candid",
11431
+ "do": "Name the tradeoff and the cost before the reader hits it.",
11432
+ "dont": "Oversell, hide the sharp edges, or hedge to sound safe.",
11433
+ "example": "Heads up — this theme fails WCAG AA on small text. Bump the contrast a notch, or keep the warning if that's intentional."
11434
+ },
11435
+ {
11436
+ "name": "generous",
11437
+ "do": "Give the why. Show the worked example. Assume the reader will go further than you did.",
11438
+ "dont": "Gatekeep, wave at best practices, or make them read the source to understand.",
11439
+ "example": "Fallbacks use Gray, not Slate — so an un-themed component lands on a neutral that fits your palette instead of clashing. Small thing, but it's what keeps a theme feeling whole."
11440
+ },
11441
+ {
11442
+ "name": "warm",
11443
+ "do": "Greet the reader like a peer you're glad to help. A little delight is welcome.",
11444
+ "dont": "Go cold and transactional — or paper over it with forced cheer.",
11445
+ "example": "Welcome — let's get your first theme on the screen. It takes about a minute."
11446
+ }
11447
+ ]
11448
+ },
11449
+ "tone": {
11450
+ "error": {
11451
+ "feeling": "warm, accountable, already holding the fix",
11452
+ "example": "That didn't save — looks like the theme name's taken. Pick another and we'll keep everything else just as you left it."
11453
+ },
11454
+ "success": {
11455
+ "feeling": "a real, small celebration — a little confetti is fine",
11456
+ "example": "Saved! Your theme's live across every component — go take a look."
11457
+ },
11458
+ "empty": {
11459
+ "feeling": "inviting, a friendly nudge to start",
11460
+ "example": "Nothing here yet — let's change that. Start from a blank file, or clone one and make it yours."
11461
+ },
11462
+ "loading": {
11463
+ "feeling": "unhurried and friendly, honest about the wait",
11464
+ "example": "Compiling your tokens — just a moment…"
11465
+ },
11466
+ "validation-warning": {
11467
+ "feeling": "a friend flagging a smell, never a scold",
11468
+ "example": "Quick one — your primary and accent are nearly twins. Themes can read a little flat this close, so nudge one if it's not on purpose."
11469
+ }
11470
+ },
11471
+ "lexicon": [
11472
+ {
11473
+ "use": "theme",
11474
+ "avoid": "skin"
11475
+ },
11476
+ {
11477
+ "use": "copy-and-own",
11478
+ "avoid": "fork"
11479
+ },
11480
+ {
11481
+ "use": "compose",
11482
+ "avoid": "drag-and-drop"
11483
+ },
11484
+ {
11485
+ "use": "token",
11486
+ "avoid": "variable"
11487
+ },
11488
+ {
11489
+ "use": "adapter",
11490
+ "avoid": "plugin"
11491
+ },
11492
+ {
11493
+ "use": "transform",
11494
+ "avoid": "restyle"
11495
+ },
11496
+ {
11497
+ "use": "portable",
11498
+ "avoid": "exportable"
11499
+ }
11500
+ ],
11501
+ "core": [
11502
+ "positioning",
11503
+ "essence",
11504
+ "pillars"
11505
+ ],
11506
+ "visibility": "public",
11507
+ "messaging": {
11508
+ "roof": "Design intent as data — all the way up to brand."
11509
+ },
11510
+ "taglines": [
11511
+ "Your entire brand system, created and encoded in one file."
11512
+ ],
11513
+ "boilerplate": {
11514
+ "short": "Visor is an open design system that compiles a complete brand — visual and verbal — from one portable file, legible to humans and agents alike. Components are yours to copy and own; shared tokens keep every layer coherent.",
11515
+ "long": "Visor is Low Orbit Studio's open design system, built on one idea: design intent should live as data — typed, portable, and machine-readable — from a single color token all the way up to a brand's voice. Components are copy-and-own, so `npx visor add` drops real source into your project for you to edit, while shared tokens keep design consistent across every app through `npm update`. A theme is a complete design system carried in one `.visor.yaml` file; change the file and the whole surface re-resolves, light to dark, one brand to another. And because that same file reads cleanly to people and agents alike, Visor is as legible to the engineer editing it as to the agent composing against it — coherent, open, and yours."
11516
+ },
11517
+ "colorUsage": {
11518
+ "pairings": [
11519
+ {
11520
+ "use": "--primary",
11521
+ "with": "--surface-card / --surface-base",
11522
+ "rule": "Primary is the one emphatic action per view — reserve it for the single most important action and let everything else recede to surfaces and text tokens."
11523
+ },
11524
+ {
11525
+ "use": "--accent",
11526
+ "with": "--primary",
11527
+ "rule": "Accent is a supporting highlight, never a second primary. Keep accent and primary visibly distinct — Visor flags them when they're near-twins, because a theme reads flat when they sit too close."
11528
+ },
11529
+ {
11530
+ "use": "--text-primary / --text-secondary",
11531
+ "with": "--surface-card / --surface-base",
11532
+ "rule": "Text always uses the semantic text tokens against a surface token, never raw hex, so contrast tracks the active theme instead of being pinned."
11533
+ },
11534
+ {
11535
+ "use": "--destructive",
11536
+ "with": "--surface-card",
11537
+ "rule": "Destructive is reserved for irreversible or error states; it is never a decorative or emphasis color."
11538
+ },
11539
+ {
11540
+ "use": "fallback neutral (Gray)",
11541
+ "with": "any un-themed surface",
11542
+ "rule": "Fallbacks use Gray, not Slate, so an un-themed component lands on a neutral that fits the palette instead of clashing."
11543
+ }
11544
+ ]
11545
+ },
11546
+ "accessibility": {
11547
+ "standard": "WCAG 2.1 AA",
11548
+ "contrast": [
11549
+ {
11550
+ "context": "Body text and other normal-size text",
11551
+ "ratio": "4.5:1"
11552
+ },
11553
+ {
11554
+ "context": "Large text (≥ 24px, or ≥ 18.66px bold)",
11555
+ "ratio": "3:1"
11556
+ },
11557
+ {
11558
+ "context": "Non-text UI — icons, focus rings, control boundaries",
11559
+ "ratio": "3:1"
11560
+ }
11561
+ ],
11562
+ "intent": "Every stock Visor theme is meant to clear WCAG 2.1 AA against these targets. The theme validator surfaces pairings that fall short as a candid, non-blocking warning — 'this theme fails AA on small text' — so the author can bump the contrast or keep the warning if it's intentional."
11563
+ }
11564
+ },
9991
11565
  "tokens": {
9992
11566
  "primitives": [
9993
11567
  {
@@ -11357,6 +12931,20 @@
11357
12931
  "defaultLight": "var(--spacing-24)",
11358
12932
  "defaultDark": "var(--spacing-24)"
11359
12933
  },
12934
+ {
12935
+ "name": "--layout-section-padding",
12936
+ "tier": "semantic",
12937
+ "description": "Semantic spacing: layout-section-padding",
12938
+ "defaultLight": "var(--spacing-24)",
12939
+ "defaultDark": "var(--spacing-24)"
12940
+ },
12941
+ {
12942
+ "name": "--layout-section-padding-mobile",
12943
+ "tier": "semantic",
12944
+ "description": "Semantic spacing: layout-section-padding-mobile",
12945
+ "defaultLight": "var(--spacing-16)",
12946
+ "defaultDark": "var(--spacing-16)"
12947
+ },
11360
12948
  {
11361
12949
  "name": "--motion-duration-fast",
11362
12950
  "tier": "semantic",
@@ -12180,7 +13768,7 @@
12180
13768
  }
12181
13769
  ],
12182
13770
  "summary": {
12183
- "total": 312
13771
+ "total": 314
12184
13772
  }
12185
13773
  }
12186
13774
  }