@loworbitstudio/visor 1.6.0 → 1.11.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-11T14:53:30.668Z",
3
+ "generated_at": "2026-06-12T05:08:25.418Z",
4
4
  "components": {
5
5
  "accessibility-specimen": {
6
6
  "category": "specimen",
@@ -311,7 +311,7 @@
311
311
  },
312
312
  {
313
313
  "name": "AvatarStack",
314
- "description": "Compound primitive for overlapping avatar groups with a `+N` overflow indicator. Accepts an array of image sources (`avatars`), a `total` count (supports server-truncated lists), a `max` cap (default 6), a `size` pass-through (`\"sm\" | \"default\" | \"lg\"`, default `\"sm\"`), and an optional accessible `label` override. Renders `role=\"img\"` with `data-slot=\"avatar-stack\"`.\n"
314
+ "description": "Compound primitive for overlapping avatar groups with a `+N` overflow indicator. Each entry in `avatars` is a plain image-URL string, `undefined` (renders a placeholder disc), or an `AvatarStackItem` object with `initials`, `src`, `alt`, and an optional `style` escape hatch for per-disc gradient backgrounds. Accepts a `total` count (supports server-truncated lists), a `max` cap (default 6), an explicit `overflowCount` override, a `size` pass-through (`\"sm\" | \"default\" | \"lg\"`, default `\"sm\"`), and an optional accessible `label` override. Renders `role=\"img\"` with `data-slot=\"avatar-stack\"`.\n"
315
315
  }
316
316
  ],
317
317
  "dependencies": [
@@ -361,6 +361,9 @@
361
361
  "sm",
362
362
  "md",
363
363
  "lg"
364
+ ],
365
+ "case": [
366
+ "sentence"
364
367
  ]
365
368
  },
366
369
  "props": [
@@ -374,22 +377,34 @@
374
377
  "type": "\"sm\" | \"md\" | \"lg\"",
375
378
  "default": "\"md\"",
376
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"
377
391
  }
378
392
  ],
379
393
  "dependencies": [
380
394
  "class-variance-authority"
381
395
  ],
382
396
  "tokens_used": [
383
- "--badge-text-transform",
384
397
  "--border-default",
385
398
  "--border-focus",
386
399
  "--color-neutral-600",
387
400
  "--color-white",
401
+ "--destructive",
388
402
  "--focus-ring-offset",
389
403
  "--focus-ring-width",
390
404
  "--font-size-sm",
391
405
  "--font-size-xs",
392
406
  "--font-weight-medium",
407
+ "--info",
393
408
  "--interactive-primary-bg",
394
409
  "--interactive-primary-text",
395
410
  "--interactive-secondary-bg",
@@ -400,6 +415,8 @@
400
415
  "--spacing-1",
401
416
  "--spacing-2",
402
417
  "--spacing-3",
418
+ "--success",
419
+ "--surface-card",
403
420
  "--surface-error-default",
404
421
  "--surface-error-subtle",
405
422
  "--surface-info-default",
@@ -416,7 +433,8 @@
416
433
  "--text-primary",
417
434
  "--text-secondary",
418
435
  "--text-success",
419
- "--text-warning"
436
+ "--text-warning",
437
+ "--warning"
420
438
  ],
421
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"
422
440
  },
@@ -945,6 +963,7 @@
945
963
  "--border-default",
946
964
  "--font-size-sm",
947
965
  "--font-weight-medium",
966
+ "--hairline",
948
967
  "--line-height-tight",
949
968
  "--motion-duration-moderate",
950
969
  "--motion-easing-standard",
@@ -953,8 +972,10 @@
953
972
  "--spacing-2",
954
973
  "--spacing-3",
955
974
  "--spacing-4",
975
+ "--spacing-5",
956
976
  "--surface-card",
957
- "--text-primary"
977
+ "--text-primary",
978
+ "--text-tertiary"
958
979
  ],
959
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"
960
981
  },
@@ -1055,7 +1076,9 @@
1055
1076
  "--spacing-3",
1056
1077
  "--spacing-4",
1057
1078
  "--spacing-6",
1079
+ "--surface-card",
1058
1080
  "--surface-muted",
1081
+ "--surface-subtle",
1059
1082
  "--text-primary"
1060
1083
  ],
1061
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"
@@ -1266,6 +1289,93 @@
1266
1289
  ],
1267
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"
1268
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
+ },
1269
1379
  "chart": {
1270
1380
  "category": "data-display",
1271
1381
  "description": "A Recharts wrapper providing themed chart container, tooltip, and legend components.",
@@ -1409,19 +1519,17 @@
1409
1519
  "--border-error",
1410
1520
  "--border-focus",
1411
1521
  "--border-strong",
1412
- "--checkbox-bg",
1413
1522
  "--checkbox-bg-checked",
1414
- "--checkbox-bg-hover",
1415
- "--checkbox-border",
1416
1523
  "--checkbox-border-checked",
1417
- "--checkbox-border-hover",
1418
1524
  "--focus-ring-width",
1525
+ "--hairline-strong",
1419
1526
  "--interactive-primary-bg",
1420
1527
  "--interactive-primary-text",
1421
1528
  "--motion-duration-150",
1422
1529
  "--motion-easing-default",
1423
1530
  "--primary",
1424
- "--radius-sm"
1531
+ "--radius-sm",
1532
+ "--surface-subtle"
1425
1533
  ],
1426
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"
1427
1535
  },
@@ -1553,6 +1661,12 @@
1553
1661
  "type": "\"accent\" | \"neutral\"",
1554
1662
  "default": "\"accent\"",
1555
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"
1556
1670
  }
1557
1671
  ]
1558
1672
  }
@@ -1572,9 +1686,12 @@
1572
1686
  "--font-size-sm",
1573
1687
  "--font-size-xs",
1574
1688
  "--font-weight-medium",
1689
+ "--font-weight-semibold",
1575
1690
  "--motion-duration-150",
1576
1691
  "--motion-easing-default",
1577
1692
  "--opacity-40",
1693
+ "--primary",
1694
+ "--primary-text",
1578
1695
  "--radius-full",
1579
1696
  "--spacing-1",
1580
1697
  "--spacing-2",
@@ -1584,13 +1701,15 @@
1584
1701
  "--surface-accent-default",
1585
1702
  "--surface-accent-subtle",
1586
1703
  "--surface-card",
1704
+ "--surface-elev",
1587
1705
  "--surface-interactive-hover",
1588
1706
  "--surface-muted",
1589
1707
  "--surface-success-default",
1590
1708
  "--text-inverse",
1591
1709
  "--text-link",
1592
1710
  "--text-primary",
1593
- "--text-secondary"
1711
+ "--text-secondary",
1712
+ "--text-tertiary"
1594
1713
  ],
1595
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"
1596
1715
  },
@@ -2232,9 +2351,82 @@
2232
2351
  ],
2233
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"
2234
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
+ },
2235
2427
  "confirm-dialog": {
2236
2428
  "category": "admin",
2237
- "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.",
2238
2430
  "when_to_use": [
2239
2431
  "Confirming a destructive action like delete, revoke, or purge",
2240
2432
  "Gating a high-stakes action with a typed confirmation phrase",
@@ -2255,6 +2447,14 @@
2255
2447
  ],
2256
2448
  "severity_deprecated": [
2257
2449
  "danger"
2450
+ ],
2451
+ "iconTreatment": [
2452
+ "inline",
2453
+ "plated"
2454
+ ],
2455
+ "mode": [
2456
+ "dialog",
2457
+ "inline"
2258
2458
  ]
2259
2459
  },
2260
2460
  "props": [
@@ -2280,6 +2480,28 @@
2280
2480
  "default": "warning",
2281
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"
2282
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
+ },
2283
2505
  {
2284
2506
  "name": "trigger",
2285
2507
  "type": "React.ReactNode",
@@ -2342,17 +2564,29 @@
2342
2564
  "@phosphor-icons/react"
2343
2565
  ],
2344
2566
  "tokens_used": [
2567
+ "--accent",
2345
2568
  "--border-default",
2346
2569
  "--border-focus",
2570
+ "--confirm-dialog-plate-icon-size",
2571
+ "--confirm-dialog-plate-size",
2572
+ "--destructive",
2347
2573
  "--focus-ring-offset",
2348
2574
  "--focus-ring-width",
2575
+ "--font-size-2xl",
2576
+ "--font-size-lg",
2349
2577
  "--font-size-sm",
2350
2578
  "--font-weight-medium",
2579
+ "--font-weight-semibold",
2351
2580
  "--line-height-normal",
2352
2581
  "--line-height-relaxed",
2353
2582
  "--motion-duration-150",
2354
2583
  "--motion-easing-default",
2584
+ "--radius-full",
2585
+ "--radius-lg",
2355
2586
  "--radius-md",
2587
+ "--radius-xl",
2588
+ "--shadow-lg",
2589
+ "--shadow-xl",
2356
2590
  "--spacing-1",
2357
2591
  "--spacing-10",
2358
2592
  "--spacing-2",
@@ -2360,6 +2594,8 @@
2360
2594
  "--spacing-4",
2361
2595
  "--spacing-5",
2362
2596
  "--spacing-6",
2597
+ "--spacing-8",
2598
+ "--surface-card",
2363
2599
  "--surface-error-subtle",
2364
2600
  "--surface-info-subtle",
2365
2601
  "--surface-page",
@@ -2368,7 +2604,9 @@
2368
2604
  "--text-info",
2369
2605
  "--text-primary",
2370
2606
  "--text-secondary",
2371
- "--text-warning"
2607
+ "--text-tertiary",
2608
+ "--text-warning",
2609
+ "--warning"
2372
2610
  ],
2373
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"
2374
2612
  },
@@ -2650,6 +2888,11 @@
2650
2888
  "type": "React.ReactNode",
2651
2889
  "description": "Override for the default EmptyState slot rendered when data is empty."
2652
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
+ },
2653
2896
  {
2654
2897
  "name": "stickyHeader",
2655
2898
  "type": "boolean",
@@ -2660,7 +2903,7 @@
2660
2903
  "name": "density",
2661
2904
  "type": "\"compact\" | \"default\" | \"editorial\"",
2662
2905
  "default": "\"default\"",
2663
- "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) — column headers also receive the editorial treatment: uppercase, `--font-size-xs` (~11px), `--text-tertiary` color, and `letter-spacing: 0.08em`. 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"
2664
2907
  },
2665
2908
  {
2666
2909
  "name": "rowTone",
@@ -2682,6 +2925,12 @@
2682
2925
  "--border-default",
2683
2926
  "--border-strong",
2684
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",
2685
2934
  "--dt-row-py",
2686
2935
  "--focus-ring-color",
2687
2936
  "--focus-ring-offset",
@@ -2708,6 +2957,7 @@
2708
2957
  "--surface-info-subtle",
2709
2958
  "--surface-interactive-active",
2710
2959
  "--surface-interactive-default",
2960
+ "--surface-page",
2711
2961
  "--surface-selected",
2712
2962
  "--surface-subtle",
2713
2963
  "--surface-success-subtle",
@@ -2921,6 +3171,10 @@
2921
3171
  "name": "DialogHeader",
2922
3172
  "description": "Container for title and description at the top."
2923
3173
  },
3174
+ {
3175
+ "name": "DialogFooter",
3176
+ "description": "Right-aligned action row with a hairline top border, for dialog buttons."
3177
+ },
2924
3178
  {
2925
3179
  "name": "DialogTitle",
2926
3180
  "description": "Accessible dialog heading."
@@ -2948,11 +3202,16 @@
2948
3202
  ],
2949
3203
  "tokens_used": [
2950
3204
  "--border-focus",
3205
+ "--border-subtle",
2951
3206
  "--focus-ring-offset",
2952
3207
  "--focus-ring-width",
3208
+ "--font-size-2xl",
2953
3209
  "--font-size-lg",
2954
3210
  "--font-size-sm",
3211
+ "--font-weight-bold",
2955
3212
  "--font-weight-semibold",
3213
+ "--hairline",
3214
+ "--hairline-width",
2956
3215
  "--motion-duration-150",
2957
3216
  "--motion-duration-normal",
2958
3217
  "--motion-easing-default",
@@ -2961,13 +3220,20 @@
2961
3220
  "--overlay-bg",
2962
3221
  "--radius-lg",
2963
3222
  "--radius-sm",
3223
+ "--radius-xl",
3224
+ "--shadow-lg",
2964
3225
  "--shadow-xl",
2965
3226
  "--spacing-1",
3227
+ "--spacing-2",
3228
+ "--spacing-3",
2966
3229
  "--spacing-4",
2967
3230
  "--spacing-6",
3231
+ "--spacing-8",
3232
+ "--surface-card",
2968
3233
  "--surface-page",
2969
3234
  "--text-primary",
2970
- "--text-secondary"
3235
+ "--text-secondary",
3236
+ "--text-tertiary"
2971
3237
  ],
2972
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"
2973
3239
  },
@@ -3074,27 +3340,37 @@
3074
3340
  ],
3075
3341
  "tokens_used": [
3076
3342
  "--border-default",
3343
+ "--border-subtle",
3344
+ "--destructive",
3077
3345
  "--font-size-sm",
3078
3346
  "--font-size-xs",
3347
+ "--hairline",
3079
3348
  "--motion-duration-fast",
3080
3349
  "--motion-easing-default",
3081
3350
  "--motion-easing-enter",
3082
3351
  "--motion-easing-exit",
3083
3352
  "--radius-lg",
3084
3353
  "--radius-md",
3354
+ "--radius-sm",
3085
3355
  "--shadow-lg",
3086
3356
  "--shadow-xl",
3087
3357
  "--spacing-1",
3088
3358
  "--spacing-2",
3089
3359
  "--spacing-3",
3090
3360
  "--spacing-8",
3361
+ "--stroke-width-thin",
3091
3362
  "--surface-card",
3363
+ "--surface-elev",
3092
3364
  "--surface-error-subtle",
3093
3365
  "--surface-interactive-hover",
3366
+ "--surface-muted",
3094
3367
  "--surface-popover",
3368
+ "--surface-subtle",
3095
3369
  "--text-error",
3370
+ "--text-muted",
3096
3371
  "--text-primary",
3097
- "--text-secondary"
3372
+ "--text-secondary",
3373
+ "--text-tertiary"
3098
3374
  ],
3099
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"
3100
3376
  },
@@ -3154,6 +3430,10 @@
3154
3430
  "tone": [
3155
3431
  "default",
3156
3432
  "subtle"
3433
+ ],
3434
+ "variant": [
3435
+ "default",
3436
+ "editorial"
3157
3437
  ]
3158
3438
  },
3159
3439
  "props": [
@@ -3195,6 +3475,12 @@
3195
3475
  "default": "default",
3196
3476
  "description": "Default uses a dashed border on a muted surface. Subtle is borderless."
3197
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
+ },
3198
3484
  {
3199
3485
  "name": "headingAs",
3200
3486
  "type": "'h2' | 'h3' | 'h4'",
@@ -3219,22 +3505,82 @@
3219
3505
  "--letter-spacing-tight",
3220
3506
  "--line-height-relaxed",
3221
3507
  "--line-height-tight",
3508
+ "--radius-full",
3222
3509
  "--radius-lg",
3223
3510
  "--spacing-1",
3224
3511
  "--spacing-12",
3512
+ "--spacing-16",
3225
3513
  "--spacing-2",
3226
3514
  "--spacing-3",
3227
3515
  "--spacing-4",
3228
3516
  "--spacing-5",
3229
3517
  "--spacing-6",
3230
3518
  "--spacing-8",
3519
+ "--surface-card",
3231
3520
  "--surface-muted",
3521
+ "--surface-subtle",
3232
3522
  "--text-primary",
3233
3523
  "--text-secondary",
3234
3524
  "--text-tertiary"
3235
3525
  ],
3236
3526
  "example": "<EmptyState\n icon={<FolderOpen />}\n heading=\"No profiles yet\"\n description=\"Create your first profile to get started.\"\n action={<Button>New profile</Button>}\n secondaryAction={<Button variant=\"outline\">Learn more</Button>}\n/>\n"
3237
3527
  },
3528
+ "error-placard": {
3529
+ "category": "feedback",
3530
+ "description": "Inline failed-load placard with a destructive icon chip, title, body, and optional recovery actions.",
3531
+ "when_to_use": [
3532
+ "A specific section or data panel failed to load and requires user action to recover",
3533
+ "Displaying a named error with both context and a retry or dismiss option in-place",
3534
+ "Inline error states within cards, list items, or content panels (not page-level)"
3535
+ ],
3536
+ "when_not_to_use": [
3537
+ "Passive informational notices or warnings (use Alert)",
3538
+ "Site-wide or page-level announcements (use Banner)",
3539
+ "Empty states where no data exists yet (use EmptyState)",
3540
+ "Ephemeral transient notifications (use Toast)"
3541
+ ],
3542
+ "props": [
3543
+ {
3544
+ "name": "icon",
3545
+ "type": "ReactNode",
3546
+ "required": true,
3547
+ "description": "Leading visual rendered in a destructive-tinted circular chip. Typically a Phosphor icon at 22px."
3548
+ },
3549
+ {
3550
+ "name": "title",
3551
+ "type": "ReactNode",
3552
+ "required": true,
3553
+ "description": "Short direct statement of the failure."
3554
+ },
3555
+ {
3556
+ "name": "body",
3557
+ "type": "ReactNode",
3558
+ "required": true,
3559
+ "description": "One or two sentences of explanation or recovery guidance."
3560
+ },
3561
+ {
3562
+ "name": "actions",
3563
+ "type": "ReactNode",
3564
+ "required": false,
3565
+ "description": "Optional right-aligned action cluster — typically one or more Buttons (retry, dismiss, contact support)."
3566
+ }
3567
+ ],
3568
+ "dependencies": [
3569
+ "@loworbitstudio/visor-core"
3570
+ ],
3571
+ "tokens_used": [
3572
+ "--destructive",
3573
+ "--font-size-base",
3574
+ "--radius-lg",
3575
+ "--spacing-1",
3576
+ "--spacing-2",
3577
+ "--spacing-5",
3578
+ "--spacing-8",
3579
+ "--surface-card",
3580
+ "--text-tertiary"
3581
+ ],
3582
+ "example": "import { WarningCircle } from '@phosphor-icons/react';\n\n<ErrorPlacard\n icon={<WarningCircle weight=\"fill\" />}\n title=\"Could not load members\"\n body=\"The request timed out. Check your connection and try again.\"\n actions={\n <>\n <Button variant=\"ghost\" size=\"sm\">Dismiss</Button>\n <Button variant=\"outline\" size=\"sm\">Retry</Button>\n </>\n }\n/>\n"
3583
+ },
3238
3584
  "field": {
3239
3585
  "category": "form",
3240
3586
  "description": "A form field wrapper composing label, description, and error components.",
@@ -3273,7 +3619,7 @@
3273
3619
  },
3274
3620
  {
3275
3621
  "name": "FieldError",
3276
- "description": "Error message display with role=\"alert\". Accepts an errors array or children."
3622
+ "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"
3277
3623
  }
3278
3624
  ],
3279
3625
  "dependencies": [
@@ -3289,7 +3635,8 @@
3289
3635
  "--spacing-4",
3290
3636
  "--text-error",
3291
3637
  "--text-primary",
3292
- "--text-secondary"
3638
+ "--text-secondary",
3639
+ "--text-tertiary"
3293
3640
  ],
3294
3641
  "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"
3295
3642
  },
@@ -4187,6 +4534,7 @@
4187
4534
  "--border-error",
4188
4535
  "--border-focus",
4189
4536
  "--border-strong",
4537
+ "--field-control-bg",
4190
4538
  "--focus-ring-offset",
4191
4539
  "--focus-ring-width",
4192
4540
  "--font-size-base",
@@ -4194,8 +4542,17 @@
4194
4542
  "--input-bg",
4195
4543
  "--input-bg-invalid",
4196
4544
  "--input-border",
4545
+ "--input-height-lg",
4546
+ "--input-height-md",
4547
+ "--input-padding-lg",
4548
+ "--input-padding-md",
4549
+ "--input-padding-sm",
4550
+ "--input-placeholder-color",
4551
+ "--input-radius-lg",
4552
+ "--input-radius-md",
4197
4553
  "--motion-duration-150",
4198
4554
  "--motion-easing-default",
4555
+ "--radius-md",
4199
4556
  "--radius-sm",
4200
4557
  "--spacing-1",
4201
4558
  "--spacing-3",
@@ -4203,8 +4560,10 @@
4203
4560
  "--spacing-4",
4204
4561
  "--spacing-4_5",
4205
4562
  "--spacing-5",
4563
+ "--surface-card",
4206
4564
  "--surface-error-subtle",
4207
4565
  "--surface-interactive-default",
4566
+ "--surface-subtle",
4208
4567
  "--text-primary",
4209
4568
  "--text-secondary"
4210
4569
  ],
@@ -4544,10 +4903,11 @@
4544
4903
  },
4545
4904
  "matrix-table": {
4546
4905
  "category": "admin",
4547
- "description": "A fixed assignment grid component for members×roles patterns. Sticky-left identity column, centered boolean cells (22px filled-success check when active / muted empty when not), and multi-line centered column headers (label over count) that opt out of the uppercase header treatment. No list machinery — no selection column, no sort buttons, no pagination footer.",
4906
+ "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.",
4548
4907
  "when_to_use": [
4549
4908
  "Displaying a members × roles assignment matrix in organization settings",
4550
- "Any fixed grid where rows are identities (users, items) and columns are boolean attributes (permissions, roles, features)",
4909
+ "A plans × features monetization pricing matrix (string + boolean cells)",
4910
+ "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",
4551
4911
  "When the shape is a matrix, not a sortable/pageable list — where DataTable's list machinery would fight the layout"
4552
4912
  ],
4553
4913
  "when_not_to_use": [
@@ -4561,19 +4921,24 @@
4561
4921
  "name": "columns",
4562
4922
  "type": "MatrixColumn[]",
4563
4923
  "required": true,
4564
- "description": "Column definitions. Each column has an `id`, `label`, and optional `count` sub-label rendered below the label in the header."
4924
+ "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"
4565
4925
  },
4566
4926
  {
4567
4927
  "name": "rows",
4568
4928
  "type": "MatrixRow<TIdentity>[]",
4569
4929
  "required": true,
4570
- "description": "Row data. Each row has an `id`, an `identity` value passed to `renderIdentity`, an `activeColumns` set/array of column ids that should show the active check indicator, and an optional `cells` map of per-column values. A `cells` entry (`string | boolean`) overrides `activeColumns` for that column — `true`/`false` render the boolean indicator, a `string` renders as plain text in the standard cell style."
4930
+ "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"
4571
4931
  },
4572
4932
  {
4573
4933
  "name": "renderIdentity",
4574
- "type": "(identity: TIdentity) => React.ReactNode",
4934
+ "type": "(row: MatrixRow<TIdentity>) => React.ReactNode",
4575
4935
  "required": true,
4576
- "description": "Render slot for the sticky-left identity cell. Receives the row's `identity` valuetypically renders a name, avatar+name, or any identity compound."
4936
+ "description": "Render slot for the sticky-left identity cell. Receives the whole row (blessed shape)read `row.identity` for structured identity data."
4937
+ },
4938
+ {
4939
+ "name": "identityLabel",
4940
+ "type": "React.ReactNode",
4941
+ "description": "Header label for the sticky identity column. Defaults to \"Member\" (rendered with the uppercase, tracked eyebrow treatment)."
4577
4942
  },
4578
4943
  {
4579
4944
  "name": "aria-label",
@@ -4586,28 +4951,39 @@
4586
4951
  "@loworbitstudio/visor-core"
4587
4952
  ],
4588
4953
  "tokens_used": [
4589
- "--border-default",
4954
+ "--dt-container-radius",
4955
+ "--dt-container-shadow",
4956
+ "--dt-header-bg",
4957
+ "--dt-row-bg",
4958
+ "--font-size-base",
4590
4959
  "--font-size-sm",
4591
4960
  "--font-weight-medium",
4592
- "--font-weight-normal",
4593
- "--line-height-tight",
4594
- "--motion-duration-fast",
4595
- "--motion-easing-standard",
4961
+ "--font-weight-semibold",
4962
+ "--hairline",
4963
+ "--hairline-width",
4964
+ "--motion-duration-100",
4965
+ "--motion-easing-default",
4966
+ "--primary-text",
4596
4967
  "--radius-full",
4597
4968
  "--radius-lg",
4969
+ "--radius-sm",
4970
+ "--shadow-md",
4598
4971
  "--shadow-sm",
4599
4972
  "--spacing-1",
4600
4973
  "--spacing-2",
4601
4974
  "--spacing-3",
4602
- "--stroke-width-thin",
4975
+ "--spacing-4",
4976
+ "--spacing-5",
4977
+ "--success",
4603
4978
  "--surface-card",
4604
- "--surface-muted",
4605
- "--surface-success-default",
4606
- "--text-inverse",
4979
+ "--surface-elev",
4980
+ "--surface-page",
4981
+ "--surface-subtle",
4982
+ "--text-muted",
4607
4983
  "--text-primary",
4608
4984
  "--text-tertiary"
4609
4985
  ],
4610
- "example": "const columns: MatrixColumn[] = [\n { id: 'admin', label: 'Admin', count: 3 },\n { id: 'editor', label: 'Editor', count: 5 },\n { id: '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' }, activeColumns: new Set(['admin', 'editor']) },\n { id: '2', identity: { name: 'Grace Hopper', email: 'grace@example.com' }, activeColumns: ['viewer'] },\n // Mixed string + boolean cells via `cells` (string renders as plain text):\n { id: '3', identity: { name: 'Storage', email: '' }, activeColumns: [], cells: { admin: '50GB', editor: true, viewer: false } },\n]\n\n<MatrixTable\n columns={columns}\n rows={rows}\n renderIdentity={(identity) => <span>{identity.name}</span>}\n aria-label=\"Member role assignments\"\n/>\n"
4986
+ "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"
4611
4987
  },
4612
4988
  "menubar": {
4613
4989
  "category": "overlay",
@@ -5188,6 +5564,11 @@
5188
5564
  },
5189
5565
  {
5190
5566
  "name": "leading",
5567
+ "type": "React.ReactNode",
5568
+ "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"
5569
+ },
5570
+ {
5571
+ "name": "titleLeading",
5191
5572
  "type": "string | number",
5192
5573
  "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"
5193
5574
  }
@@ -5209,6 +5590,7 @@
5209
5590
  "--letter-spacing-wide",
5210
5591
  "--line-height-relaxed",
5211
5592
  "--line-height-tight",
5593
+ "--page-header-leading-gap",
5212
5594
  "--page-header-title-family",
5213
5595
  "--page-header-title-leading",
5214
5596
  "--page-header-title-size",
@@ -5720,6 +6102,10 @@
5720
6102
  "Linear capacity bars (use Progress with size=\"thin\")"
5721
6103
  ],
5722
6104
  "variants": {
6105
+ "variant": [
6106
+ "ring",
6107
+ "solid"
6108
+ ],
5723
6109
  "size": [
5724
6110
  "sm",
5725
6111
  "md",
@@ -5751,6 +6137,12 @@
5751
6137
  "default": "100",
5752
6138
  "description": "Maximum value the score can reach."
5753
6139
  },
6140
+ {
6141
+ "name": "variant",
6142
+ "type": "\"ring\" | \"solid\"",
6143
+ "default": "\"ring\"",
6144
+ "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."
6145
+ },
5754
6146
  {
5755
6147
  "name": "size",
5756
6148
  "type": "\"sm\" | \"md\" | \"lg\"",
@@ -5787,11 +6179,13 @@
5787
6179
  ],
5788
6180
  "tokens_used": [
5789
6181
  "--border-default",
6182
+ "--destructive",
5790
6183
  "--font-size-base",
5791
6184
  "--font-size-sm",
5792
6185
  "--font-size-xs",
5793
6186
  "--font-weight-regular",
5794
6187
  "--font-weight-semibold",
6188
+ "--info",
5795
6189
  "--line-height-tight",
5796
6190
  "--motion-duration-150",
5797
6191
  "--motion-duration-500",
@@ -5799,11 +6193,14 @@
5799
6193
  "--radius-full",
5800
6194
  "--score-indicator-center-bg",
5801
6195
  "--score-indicator-icon-color",
6196
+ "--score-indicator-solid-bg",
6197
+ "--score-indicator-solid-value-color",
5802
6198
  "--score-indicator-stroke-color",
5803
6199
  "--score-indicator-track-color",
5804
6200
  "--score-indicator-value-color",
5805
6201
  "--spacing-1",
5806
6202
  "--spacing-2",
6203
+ "--success",
5807
6204
  "--surface-card",
5808
6205
  "--surface-error-subtle",
5809
6206
  "--surface-info-subtle",
@@ -5814,7 +6211,8 @@
5814
6211
  "--text-primary",
5815
6212
  "--text-success",
5816
6213
  "--text-tertiary",
5817
- "--text-warning"
6214
+ "--text-warning",
6215
+ "--warning"
5818
6216
  ],
5819
6217
  "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"
5820
6218
  },
@@ -5948,6 +6346,86 @@
5948
6346
  ],
5949
6347
  "example": "<SectionHeader title=\"Tonight's events\" meta=\"2 events\" />\n<SectionHeader title=\"Activity\" meta=\"last hour\" />\n<SectionHeader title=\"Top promoters · 30d\" />\n"
5950
6348
  },
6349
+ "section-intro": {
6350
+ "category": "general",
6351
+ "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.",
6352
+ "when_to_use": [
6353
+ "Opening a marketing section with an eyebrow + display heading pattern",
6354
+ "Replacing hand-rolled eyebrow/heading combos that repeat across homepage sections",
6355
+ "Binding the eyebrow color to a keyed brand accent that changes at runtime",
6356
+ "Using left or center alignment for section intros on marketing pages"
6357
+ ],
6358
+ "when_not_to_use": [
6359
+ "Admin page headers with actions and breadcrumbs (use PageHeader)",
6360
+ "In-page content section dividers with meta labels (use SectionHeader)",
6361
+ "Plain typographic headings inside body copy (use Heading)",
6362
+ "Sections that require icon, badge, or button inside the heading area"
6363
+ ],
6364
+ "variants": {
6365
+ "align": [
6366
+ "left",
6367
+ "center"
6368
+ ]
6369
+ },
6370
+ "props": [
6371
+ {
6372
+ "name": "eyebrow",
6373
+ "type": "React.ReactNode",
6374
+ "description": "Short mono uppercase label rendered above the heading. Reads color from --section-intro-eyebrow-color."
6375
+ },
6376
+ {
6377
+ "name": "heading",
6378
+ "type": "React.ReactNode",
6379
+ "required": true,
6380
+ "description": "Display-font heading — the main marketing statement."
6381
+ },
6382
+ {
6383
+ "name": "lede",
6384
+ "type": "React.ReactNode",
6385
+ "description": "Optional supporting paragraph rendered beneath the heading."
6386
+ },
6387
+ {
6388
+ "name": "align",
6389
+ "type": "'left' | 'center'",
6390
+ "default": "left",
6391
+ "description": "Text alignment for all three slots."
6392
+ },
6393
+ {
6394
+ "name": "headingAs",
6395
+ "type": "'h1' | 'h2' | 'h3'",
6396
+ "default": "h2",
6397
+ "description": "Heading level for the heading slot."
6398
+ },
6399
+ {
6400
+ "name": "as",
6401
+ "type": "'header' | 'div' | 'section'",
6402
+ "default": "header",
6403
+ "description": "Root element tag."
6404
+ }
6405
+ ],
6406
+ "dependencies": [],
6407
+ "tokens_used": [
6408
+ "--accent-default",
6409
+ "--font-display",
6410
+ "--font-family-heading",
6411
+ "--font-mono",
6412
+ "--font-size-base",
6413
+ "--font-size-xs",
6414
+ "--font-weight-light",
6415
+ "--font-weight-medium",
6416
+ "--interactive-primary-bg",
6417
+ "--letter-spacing-tight",
6418
+ "--letter-spacing-widest",
6419
+ "--line-height-none",
6420
+ "--line-height-relaxed",
6421
+ "--line-height-tight",
6422
+ "--section-intro-eyebrow-color",
6423
+ "--spacing-4",
6424
+ "--text-primary",
6425
+ "--text-secondary"
6426
+ ],
6427
+ "example": "<SectionIntro\n eyebrow=\"On the map\"\n heading=\"Built for the world's best venues\"\n lede=\"One platform. Every door.\"\n/>\n"
6428
+ },
5951
6429
  "section-nav": {
5952
6430
  "category": "navigation",
5953
6431
  "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.",
@@ -5976,8 +6454,8 @@
5976
6454
  },
5977
6455
  {
5978
6456
  "name": "icon",
5979
- "type": "Icon",
5980
- "description": "Leading Phosphor icon component (e.g. `UsersIcon`). (SectionNavItem)"
6457
+ "type": "Icon | React.ReactNode",
6458
+ "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"
5981
6459
  },
5982
6460
  {
5983
6461
  "name": "count",
@@ -6008,9 +6486,8 @@
6008
6486
  "--focus-ring-offset",
6009
6487
  "--focus-ring-width",
6010
6488
  "--font-size-sm",
6011
- "--font-size-xs",
6012
6489
  "--font-weight-medium",
6013
- "--line-height-tight",
6490
+ "--hairline",
6014
6491
  "--motion-duration-fast",
6015
6492
  "--motion-easing-standard",
6016
6493
  "--primary",
@@ -6019,15 +6496,80 @@
6019
6496
  "--spacing-1",
6020
6497
  "--spacing-2",
6021
6498
  "--spacing-3",
6499
+ "--spacing-4",
6022
6500
  "--stroke-width-thin",
6023
- "--surface-accent-subtle",
6024
- "--surface-muted",
6025
- "--text-link",
6501
+ "--surface-subtle",
6026
6502
  "--text-primary",
6027
- "--text-secondary"
6503
+ "--text-tertiary"
6028
6504
  ],
6029
6505
  "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"
6030
6506
  },
6507
+ "segmented-progress": {
6508
+ "category": "data-display",
6509
+ "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.",
6510
+ "when_to_use": [
6511
+ "Multi-step onboarding or wizard flows where each discrete step needs individual visual state",
6512
+ "Brand elicitation, survey, or questionnaire progress where step-level completion is meaningful",
6513
+ "Short sequence indicators (≤12 steps) where a pill-per-step treatment reads clearly",
6514
+ "Inline progress in cards or compact surfaces where a stepper is too heavy"
6515
+ ],
6516
+ "when_not_to_use": [
6517
+ "Use `progress` for continuous values like upload percentage or loading progress",
6518
+ "Use `stepper` when step labels or click-to-navigate behavior is needed",
6519
+ "Sequences longer than ~15 segments (pills become too narrow to read)"
6520
+ ],
6521
+ "variants": {
6522
+ "size": [
6523
+ "sm",
6524
+ "md"
6525
+ ]
6526
+ },
6527
+ "props": [
6528
+ {
6529
+ "name": "total",
6530
+ "type": "number",
6531
+ "required": true,
6532
+ "description": "Total number of segments to render."
6533
+ },
6534
+ {
6535
+ "name": "value",
6536
+ "type": "number",
6537
+ "required": true,
6538
+ "description": "Count of completed segments. Indices 0..value-1 render as done."
6539
+ },
6540
+ {
6541
+ "name": "current",
6542
+ "type": "number",
6543
+ "description": "Optional 0-based index of the in-progress segment. Renders with a primary→muted gradient. Typically equals `value`."
6544
+ },
6545
+ {
6546
+ "name": "size",
6547
+ "type": "\"sm\" | \"md\"",
6548
+ "default": "\"sm\"",
6549
+ "description": "Segment height. sm=6px (prototype height), md=8px."
6550
+ },
6551
+ {
6552
+ "name": "aria-label",
6553
+ "type": "string",
6554
+ "required": true,
6555
+ "description": "Accessible name for the progressbar. Required — describe the current step context (e.g. \"Step 3 of 10\")."
6556
+ }
6557
+ ],
6558
+ "dependencies": [
6559
+ "class-variance-authority",
6560
+ "@loworbitstudio/visor-core"
6561
+ ],
6562
+ "tokens_used": [
6563
+ "--interactive-primary-bg",
6564
+ "--motion-duration-150",
6565
+ "--motion-easing-default",
6566
+ "--primary",
6567
+ "--radius-full",
6568
+ "--spacing-1",
6569
+ "--surface-muted"
6570
+ ],
6571
+ "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"
6572
+ },
6031
6573
  "select": {
6032
6574
  "category": "form",
6033
6575
  "description": "An accessible select component built on Radix UI Select with all sub-components.",
@@ -6070,7 +6612,7 @@
6070
6612
  "sub_components": [
6071
6613
  {
6072
6614
  "name": "SelectTrigger",
6073
- "description": "The button that opens the select dropdown. Accepts a size prop (\"sm\" | \"md\" | \"lg\")."
6615
+ "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."
6074
6616
  },
6075
6617
  {
6076
6618
  "name": "SelectValue",
@@ -6106,6 +6648,7 @@
6106
6648
  "--border-error",
6107
6649
  "--border-focus",
6108
6650
  "--border-strong",
6651
+ "--field-control-bg",
6109
6652
  "--field-menu-bg",
6110
6653
  "--focus-ring-width",
6111
6654
  "--font-size-base",
@@ -6134,6 +6677,7 @@
6134
6677
  "--surface-interactive-default",
6135
6678
  "--surface-interactive-hover",
6136
6679
  "--surface-popover",
6680
+ "--surface-subtle",
6137
6681
  "--text-primary",
6138
6682
  "--text-secondary"
6139
6683
  ],
@@ -6472,16 +7016,23 @@
6472
7016
  {
6473
7017
  "name": "className",
6474
7018
  "type": "string",
6475
- "description": "Control dimensions and shape (e.g., \"w-full h-4 rounded\")"
7019
+ "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"
6476
7020
  }
6477
7021
  ],
6478
7022
  "dependencies": [],
6479
7023
  "tokens_used": [
6480
7024
  "--_shimmer-duration",
6481
7025
  "--motion-easing-linear",
7026
+ "--radius-full",
6482
7027
  "--radius-md",
7028
+ "--radius-sm",
6483
7029
  "--skeleton-from",
6484
- "--skeleton-to"
7030
+ "--skeleton-logo-height",
7031
+ "--skeleton-logo-width",
7032
+ "--skeleton-pill-height",
7033
+ "--skeleton-pill-width",
7034
+ "--skeleton-to",
7035
+ "--text-primary"
6485
7036
  ],
6486
7037
  "example": "<Skeleton className=\"h-4 w-[200px]\" />\n<Skeleton className=\"h-12 w-12 rounded-full\" />\n"
6487
7038
  },
@@ -6708,6 +7259,116 @@
6708
7259
  ],
6709
7260
  "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"
6710
7261
  },
7262
+ "specimen-card": {
7263
+ "category": "specimen",
7264
+ "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.",
7265
+ "when_to_use": [
7266
+ "Displaying tone-by-context previews in a brand workbench grid (e.g. \"error\" + \"warm, accountable\" above a real Alert component)",
7267
+ "Building brand-voice proof blocks that show live components annotated with their voice/tone attribution"
7268
+ ],
7269
+ "when_not_to_use": [
7270
+ "General-purpose card containers (use Card) — SpecimenCard is specifically a labeled component-specimen frame",
7271
+ "Production UI layouts where the context/feel labeling would be confusing to end users"
7272
+ ],
7273
+ "variants": {
7274
+ "footer": [
7275
+ "with-footer",
7276
+ "without-footer"
7277
+ ]
7278
+ },
7279
+ "props": [
7280
+ {
7281
+ "name": "context",
7282
+ "type": "string",
7283
+ "required": true,
7284
+ "description": "Context token label rendered uppercase (e.g. \"error\", \"onboarding\")."
7285
+ },
7286
+ {
7287
+ "name": "feel",
7288
+ "type": "string",
7289
+ "description": "Italic feel descriptor rendered at the end of the label row (e.g. \"warm, accountable\")."
7290
+ }
7291
+ ],
7292
+ "dependencies": [
7293
+ "@loworbitstudio/visor-core"
7294
+ ],
7295
+ "tokens_used": [
7296
+ "--hairline",
7297
+ "--radius-lg",
7298
+ "--shadow-sm",
7299
+ "--spacing-1",
7300
+ "--spacing-1-5",
7301
+ "--spacing-2",
7302
+ "--spacing-3",
7303
+ "--stroke-width-thin",
7304
+ "--surface-card",
7305
+ "--text-secondary",
7306
+ "--text-tertiary"
7307
+ ],
7308
+ "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"
7309
+ },
7310
+ "spinner": {
7311
+ "category": "data-display",
7312
+ "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.",
7313
+ "when_to_use": [
7314
+ "Indicating an async operation is in progress within a button, form field, or inline context",
7315
+ "Replacing text or an icon while content is loading (inline, non-blocking)",
7316
+ "Decorative loading affordance inside a card or panel where a full-page skeleton would be too heavy"
7317
+ ],
7318
+ "when_not_to_use": [
7319
+ "Full-page or full-section loading states (use Skeleton instead)",
7320
+ "Progress that can be measured and communicated as a percentage (use Progress)",
7321
+ "Indicating indeterminate progress for a long-running task visible to the user (use a labeled progress bar)"
7322
+ ],
7323
+ "variants": {
7324
+ "size": [
7325
+ "xs",
7326
+ "sm",
7327
+ "md"
7328
+ ],
7329
+ "tone": [
7330
+ "default",
7331
+ "primary"
7332
+ ]
7333
+ },
7334
+ "props": [
7335
+ {
7336
+ "name": "size",
7337
+ "type": "\"xs\" | \"sm\" | \"md\"",
7338
+ "default": "\"md\"",
7339
+ "description": "Ring size. xs=12px, sm=16px, md=24px."
7340
+ },
7341
+ {
7342
+ "name": "tone",
7343
+ "type": "\"default\" | \"primary\"",
7344
+ "default": "\"default\"",
7345
+ "description": "Color treatment for the leading edge. default=text-tertiary, primary=primary brand token."
7346
+ },
7347
+ {
7348
+ "name": "label",
7349
+ "type": "string",
7350
+ "description": "Accessible label. When provided, renders role=\"status\" with visually-hidden text. When omitted, aria-hidden=\"true\" (decorative)."
7351
+ }
7352
+ ],
7353
+ "dependencies": [
7354
+ "class-variance-authority",
7355
+ "@loworbitstudio/visor-core"
7356
+ ],
7357
+ "tokens_used": [
7358
+ "--border-default",
7359
+ "--motion-duration-1500",
7360
+ "--motion-easing-linear",
7361
+ "--primary",
7362
+ "--radius-full",
7363
+ "--spinner-edge-color",
7364
+ "--spinner-track-color",
7365
+ "--stroke-width-medium",
7366
+ "--stroke-width-regular",
7367
+ "--stroke-width-thin",
7368
+ "--text-tertiary"
7369
+ ],
7370
+ "example": "<Spinner />\n<Spinner size=\"xs\" tone=\"primary\" />\n<Spinner size=\"sm\" label=\"Loading results…\" />\n"
7371
+ },
6711
7372
  "stack": {
6712
7373
  "category": "layout",
6713
7374
  "description": "Vertical flex container with token-driven gap, alignment, and `as` polymorphism.",
@@ -7200,7 +7861,7 @@
7200
7861
  },
7201
7862
  "stepper": {
7202
7863
  "category": "navigation",
7203
- "description": "A multi-step progress indicator with horizontal and vertical orientations and auto-derived step status.",
7864
+ "description": "A multi-step progress indicator with horizontal and vertical orientations, auto-derived step status, and a locked status for non-interactive steps.",
7204
7865
  "when_to_use": [
7205
7866
  "Multi-step forms or wizards showing progress",
7206
7867
  "Onboarding flows with sequential steps",
@@ -7213,6 +7874,18 @@
7213
7874
  "Timelines of past events (use Timeline)",
7214
7875
  "Pagination through pages of content (use Pagination)"
7215
7876
  ],
7877
+ "variants": [
7878
+ {
7879
+ "name": "status",
7880
+ "values": [
7881
+ "complete",
7882
+ "active",
7883
+ "upcoming",
7884
+ "locked"
7885
+ ],
7886
+ "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"
7887
+ }
7888
+ ],
7216
7889
  "props": [
7217
7890
  {
7218
7891
  "name": "activeStep",
@@ -7233,15 +7906,15 @@
7233
7906
  },
7234
7907
  {
7235
7908
  "name": "StepperItem",
7236
- "description": "Container for a single step with auto-derived status from context."
7909
+ "description": "Container for a single step with auto-derived status from context. Pass status=\"locked\" to override."
7237
7910
  },
7238
7911
  {
7239
7912
  "name": "StepperTrigger",
7240
- "description": "Clickable step indicator circle with status-based styling and check icon."
7913
+ "description": "Clickable step indicator circle with status-based styling; check icon for complete, lock glyph for locked."
7241
7914
  },
7242
7915
  {
7243
7916
  "name": "StepperTitle",
7244
- "description": "Step label text."
7917
+ "description": "Step label text. Rendered in secondary color when the parent item is locked."
7245
7918
  },
7246
7919
  {
7247
7920
  "name": "StepperDescription",
@@ -7264,12 +7937,14 @@
7264
7937
  "--font-size-sm",
7265
7938
  "--font-size-xs",
7266
7939
  "--font-weight-medium",
7940
+ "--font-weight-normal",
7267
7941
  "--interactive-primary-bg",
7268
7942
  "--interactive-primary-text",
7269
7943
  "--line-height-normal",
7270
7944
  "--line-height-snug",
7271
7945
  "--motion-duration-200",
7272
7946
  "--motion-easing-default",
7947
+ "--opacity-60",
7273
7948
  "--primary",
7274
7949
  "--radius-full",
7275
7950
  "--spacing-1",
@@ -7282,37 +7957,128 @@
7282
7957
  ],
7283
7958
  "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"
7284
7959
  },
7285
- "surface-row": {
7286
- "category": "specimen",
7287
- "description": "Surface background variant display showing a surface token with its name and token reference.",
7960
+ "structured-prompt": {
7961
+ "category": "form",
7962
+ "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.",
7288
7963
  "when_to_use": [
7289
- "Displaying surface background tokens in a design system specimen",
7290
- "Comparing surface layers and hierarchy",
7291
- "Theme validation for surface tokens"
7964
+ "Guiding users through structured sentence completion (e.g. \"For [audience], we are the only [category] that [differentiator]\")",
7965
+ "Brand elicitation or onliness-statement workflows where each bracket is a fillable slot",
7966
+ "AI-assisted prompt templates where the model pre-fills slots and the user confirms or edits them",
7967
+ "Any pattern that mixes fixed prose with discrete editable inputs inline in a sentence"
7292
7968
  ],
7293
7969
  "when_not_to_use": [
7294
- "Actual page or card surfaces (use the surface tokens directly)",
7295
- "Color swatches without surface context (use ColorSwatch)"
7970
+ "Free-form text entry where sentence structure is not fixed (use Textarea or Input)",
7971
+ "Long multi-field forms (use Field + Fieldset)",
7972
+ "Structured data entry with labels and descriptions (use KeyValueList or a form layout)"
7296
7973
  ],
7974
+ "variants": {
7975
+ "slot_state": [
7976
+ "filled",
7977
+ "empty"
7978
+ ]
7979
+ },
7297
7980
  "props": [
7298
7981
  {
7299
- "name": "token",
7982
+ "name": "className",
7300
7983
  "type": "string",
7301
- "description": "CSS custom property for the surface background"
7984
+ "description": "Additional CSS classes for the root card element."
7302
7985
  },
7303
7986
  {
7304
- "name": "name",
7305
- "type": "string",
7306
- "description": "Display name for the surface"
7987
+ "name": "icon",
7988
+ "type": "ReactNode",
7989
+ "description": "(StructuredPromptHeader) Phosphor icon rendered before the eyebrow label."
7307
7990
  },
7308
7991
  {
7309
- "name": "lightText",
7992
+ "name": "filled",
7310
7993
  "type": "boolean",
7311
7994
  "default": "false",
7312
- "description": "When true, text renders in light colors for dark surfaces"
7995
+ "description": "(StructuredPromptSlot) When true, renders the primary-soft filled chip. When false, renders the dashed empty chip."
7996
+ },
7997
+ {
7998
+ "name": "onClick",
7999
+ "type": "() => void",
8000
+ "description": "(StructuredPromptSlot) When provided, the slot renders as a focusable <button>. Without onClick it renders as a <span>."
7313
8001
  }
7314
8002
  ],
7315
- "dependencies": [],
8003
+ "sub_components": [
8004
+ {
8005
+ "name": "StructuredPromptHeader",
8006
+ "description": "Eyebrow header bar with optional Phosphor icon and uppercase label. Renders a hairline bottom border separating it from the body."
8007
+ },
8008
+ {
8009
+ "name": "StructuredPromptBody",
8010
+ "description": "Prose container at tall line-height (2.05) so inline slot chips breathe within the sentence flow."
8011
+ },
8012
+ {
8013
+ "name": "StructuredPromptSlot",
8014
+ "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)."
8015
+ },
8016
+ {
8017
+ "name": "StructuredPromptHint",
8018
+ "description": "Footer hint text at small tertiary size, rendered below the body."
8019
+ }
8020
+ ],
8021
+ "dependencies": [
8022
+ "@loworbitstudio/visor-core"
8023
+ ],
8024
+ "tokens_used": [
8025
+ "--border-default",
8026
+ "--border-focus",
8027
+ "--focus-ring-offset",
8028
+ "--focus-ring-width",
8029
+ "--font-weight-medium",
8030
+ "--font-weight-semibold",
8031
+ "--hairline",
8032
+ "--radius-md",
8033
+ "--radius-xl",
8034
+ "--shadow-sm",
8035
+ "--spacing-0-5",
8036
+ "--spacing-2",
8037
+ "--spacing-2-5",
8038
+ "--spacing-3",
8039
+ "--spacing-3-5",
8040
+ "--stroke-width-thin",
8041
+ "--surface-accent-default",
8042
+ "--surface-accent-subtle",
8043
+ "--surface-card",
8044
+ "--surface-subtle",
8045
+ "--text-link",
8046
+ "--text-primary",
8047
+ "--text-tertiary"
8048
+ ],
8049
+ "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"
8050
+ },
8051
+ "surface-row": {
8052
+ "category": "specimen",
8053
+ "description": "Surface background variant display showing a surface token with its name and token reference.",
8054
+ "when_to_use": [
8055
+ "Displaying surface background tokens in a design system specimen",
8056
+ "Comparing surface layers and hierarchy",
8057
+ "Theme validation for surface tokens"
8058
+ ],
8059
+ "when_not_to_use": [
8060
+ "Actual page or card surfaces (use the surface tokens directly)",
8061
+ "Color swatches without surface context (use ColorSwatch)"
8062
+ ],
8063
+ "props": [
8064
+ {
8065
+ "name": "token",
8066
+ "type": "string",
8067
+ "description": "CSS custom property for the surface background"
8068
+ },
8069
+ {
8070
+ "name": "name",
8071
+ "type": "string",
8072
+ "description": "Display name for the surface"
8073
+ },
8074
+ {
8075
+ "name": "lightText",
8076
+ "type": "boolean",
8077
+ "default": "false",
8078
+ "description": "When true, text renders in light colors for dark surfaces"
8079
+ }
8080
+ ],
8081
+ "dependencies": [],
7316
8082
  "tokens_used": [
7317
8083
  "--border-muted",
7318
8084
  "--font-mono",
@@ -7496,15 +8262,22 @@
7496
8262
  "dependencies": [],
7497
8263
  "tokens_used": [
7498
8264
  "--border-default",
8265
+ "--dt-container-radius",
8266
+ "--dt-container-shadow",
8267
+ "--dt-header-bg",
8268
+ "--dt-row-bg",
7499
8269
  "--font-size-sm",
7500
8270
  "--font-weight-medium",
8271
+ "--hairline",
7501
8272
  "--motion-duration-150",
7502
8273
  "--motion-easing-default",
7503
8274
  "--radius-lg",
7504
8275
  "--shadow-sm",
7505
8276
  "--spacing-3",
7506
8277
  "--spacing-4",
8278
+ "--surface-card",
7507
8279
  "--surface-muted",
8280
+ "--surface-page",
7508
8281
  "--text-primary",
7509
8282
  "--text-secondary"
7510
8283
  ],
@@ -7593,18 +8366,20 @@
7593
8366
  "--font-size-sm",
7594
8367
  "--font-size-xs",
7595
8368
  "--font-weight-medium",
8369
+ "--hairline",
7596
8370
  "--motion-duration-150",
7597
8371
  "--motion-duration-normal",
7598
8372
  "--motion-easing-default",
8373
+ "--primary",
7599
8374
  "--radius-full",
7600
8375
  "--radius-md",
7601
8376
  "--shadow-sm",
7602
8377
  "--spacing-1",
7603
8378
  "--spacing-2",
8379
+ "--spacing-3",
7604
8380
  "--surface-accent-subtle",
7605
8381
  "--surface-muted",
7606
8382
  "--surface-page",
7607
- "--tabs-indicator-color",
7608
8383
  "--text-link",
7609
8384
  "--text-primary",
7610
8385
  "--text-secondary"
@@ -8059,6 +8834,117 @@
8059
8834
  ],
8060
8835
  "example": "import { Toaster, toast } from \"@/components/ui/toast\"\n\n{/* Place once in your layout */}\n<Toaster />\n\n{/* Call imperatively */}\ntoast.success(\"Changes saved\")\ntoast.error(\"Something went wrong\")\n"
8061
8836
  },
8837
+ "toast-card": {
8838
+ "category": "feedback",
8839
+ "description": "A static, server-renderable notification card for editorial display of toast anatomy. Use in state galleries and design documentation — not for imperative portal notifications.",
8840
+ "when_to_use": [
8841
+ "State gallery screens showing notification anatomy in-flow",
8842
+ "Design documentation illustrating toast variants",
8843
+ "Static editorial rendering of notification cards without portal or timer machinery",
8844
+ "Layouts that need declarative, server-rendered notification cards"
8845
+ ],
8846
+ "when_not_to_use": [
8847
+ "Imperative portal notifications that auto-dismiss (use Toast with Sonner instead)",
8848
+ "Notifications triggered by user actions at runtime (use Toast)",
8849
+ "Full-width announcement bars (use Banner)",
8850
+ "Inline contextual callouts within content (use Alert)"
8851
+ ],
8852
+ "variants": {
8853
+ "variant": [
8854
+ "success",
8855
+ "error",
8856
+ "info",
8857
+ "warning"
8858
+ ]
8859
+ },
8860
+ "props": [
8861
+ {
8862
+ "name": "variant",
8863
+ "type": "\"success\" | \"error\" | \"info\" | \"warning\"",
8864
+ "default": "\"info\"",
8865
+ "description": "Intent variant controlling the icon badge color and default glyph"
8866
+ },
8867
+ {
8868
+ "name": "title",
8869
+ "type": "React.ReactNode",
8870
+ "description": "Bold one-line headline. Required."
8871
+ },
8872
+ {
8873
+ "name": "body",
8874
+ "type": "React.ReactNode",
8875
+ "description": "Muted supporting line beneath the title. Optional."
8876
+ },
8877
+ {
8878
+ "name": "action",
8879
+ "type": "React.ReactNode",
8880
+ "description": "Primary-colored action affordance text. Optional. Renders as a button when onAction is provided, otherwise static text."
8881
+ },
8882
+ {
8883
+ "name": "onAction",
8884
+ "type": "React.MouseEventHandler<HTMLButtonElement>",
8885
+ "description": "Handler for the action affordance. When omitted with action, renders action as static text."
8886
+ },
8887
+ {
8888
+ "name": "onDismiss",
8889
+ "type": "React.MouseEventHandler<HTMLButtonElement>",
8890
+ "description": "Dismiss handler. When provided, renders a muted X dismiss control."
8891
+ },
8892
+ {
8893
+ "name": "icon",
8894
+ "type": "React.ReactNode",
8895
+ "description": "Override the leading glyph. Defaults to the per-variant Phosphor icon (CheckCircle / WarningCircle / Info / Warning)."
8896
+ }
8897
+ ],
8898
+ "sub_components": [
8899
+ {
8900
+ "name": "ToastCardStack",
8901
+ "description": "Fixed top-right stacking container for multiple ToastCards. Declarative and server-renderable. Accepts `top` and `gap` string props that map to CSS custom properties for consumer retuning without forking. Renders `data-slot=\"toast-card-stack\"`.\n"
8902
+ }
8903
+ ],
8904
+ "dependencies": [
8905
+ "class-variance-authority",
8906
+ "@phosphor-icons/react"
8907
+ ],
8908
+ "tokens_used": [
8909
+ "--accent",
8910
+ "--border-focus",
8911
+ "--color-white",
8912
+ "--destructive",
8913
+ "--focus-ring-offset",
8914
+ "--focus-ring-width",
8915
+ "--font-size-sm",
8916
+ "--font-weight-medium",
8917
+ "--font-weight-semibold",
8918
+ "--motion-duration-150",
8919
+ "--motion-easing-default",
8920
+ "--primary",
8921
+ "--primary-text",
8922
+ "--radius-full",
8923
+ "--radius-md",
8924
+ "--radius-sm",
8925
+ "--shadow-lg",
8926
+ "--spacing-1",
8927
+ "--spacing-3",
8928
+ "--spacing-4",
8929
+ "--spacing-6",
8930
+ "--success",
8931
+ "--surface-card",
8932
+ "--surface-elev",
8933
+ "--text-inverse",
8934
+ "--text-muted",
8935
+ "--text-primary",
8936
+ "--text-tertiary",
8937
+ "--toast-card-body-font-size",
8938
+ "--toast-card-icon-glyph",
8939
+ "--toast-card-icon-size",
8940
+ "--toast-card-stack-gap",
8941
+ "--toast-card-stack-right",
8942
+ "--toast-card-stack-top",
8943
+ "--toast-card-stack-z",
8944
+ "--warning"
8945
+ ],
8946
+ "example": "<ToastCard variant=\"success\" title=\"Changes saved\" body=\"Your profile has been updated.\" />\n\n<ToastCard\n variant=\"error\"\n title=\"Upload failed\"\n body=\"The file exceeds the 10MB limit.\"\n action=\"Try again\"\n onAction={() => {}}\n onDismiss={() => {}}\n/>\n"
8947
+ },
8062
8948
  "toggle-group": {
8063
8949
  "category": "form",
8064
8950
  "description": "A toggle group component with single-select and multi-select modes using CVA variants.",
@@ -8887,6 +9773,235 @@
8887
9773
  "tokens_used": [],
8888
9774
  "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"
8889
9775
  },
9776
+ "ambient-glow": {
9777
+ "category": "visual-elements",
9778
+ "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).",
9779
+ "when_to_use": [
9780
+ "Atmospheric background lighting behind hero sections or focal objects",
9781
+ "Live color-reactive glows that track a dynamically rewritten CSS custom property (e.g. hero crossfade accent)",
9782
+ "Marketing and landing pages that need depth via subtle light fields",
9783
+ "Pro/premium surface accents (gold variant)"
9784
+ ],
9785
+ "when_not_to_use": [
9786
+ "Functional UI elements — AmbientGlow is purely decorative",
9787
+ "When the user prefers reduced motion and you need a visible element (the glow becomes static but stays visible)",
9788
+ {
9789
+ "Inside overflow": "hidden containers without adjusting inset values"
9790
+ }
9791
+ ],
9792
+ "props": [
9793
+ {
9794
+ "name": "variant",
9795
+ "type": "'keyed' | 'gold'",
9796
+ "default": "keyed",
9797
+ "description": "keyed — color-mix from --glow-color (live-rewritable); gold — static warm gold at 7% opacity"
9798
+ },
9799
+ {
9800
+ "name": "glowColor",
9801
+ "type": "string",
9802
+ "description": "Static glow color override — sets --glow-color as an inline custom property. For live runtime rewrites use a parent-element CSS variable instead."
9803
+ },
9804
+ {
9805
+ "name": "className",
9806
+ "type": "string",
9807
+ "description": "Additional class names for positioning and sizing (position/inset/width/height)."
9808
+ },
9809
+ {
9810
+ "name": "style",
9811
+ "type": "React.CSSProperties",
9812
+ "description": "Inline styles, typically inset/width/height. --glow-color can also be set here."
9813
+ }
9814
+ ],
9815
+ "dependencies": [
9816
+ "class-variance-authority",
9817
+ "@loworbitstudio/visor-core"
9818
+ ],
9819
+ "tokens_used": [
9820
+ "--accent",
9821
+ "--glow-color",
9822
+ "--interactive-primary-bg"
9823
+ ],
9824
+ "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"
9825
+ },
9826
+ "browser-frame": {
9827
+ "category": "visual-elements",
9828
+ "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.",
9829
+ "when_to_use": [
9830
+ "Showing a product screenshot or live embed inside a browser mockup on a marketing or landing page",
9831
+ "EPK-style artist/product demos where the \"in-browser\" framing provides context",
9832
+ "Any dark surface that needs a browser-chrome visual metaphor without hand-rolling the chrome bar"
9833
+ ],
9834
+ "when_not_to_use": [
9835
+ "Real browser chrome or app shells — this is a decorative mockup, not a functional navigation component",
9836
+ "Light-mode surfaces (the default deep-dark background will clash; override --browser-frame-bg to adapt)",
9837
+ "Nested inside another BrowserFrame (a browser inside a browser is confusing)"
9838
+ ],
9839
+ "props": [
9840
+ {
9841
+ "name": "url",
9842
+ "type": "string",
9843
+ "required": true,
9844
+ "description": "Display URL shown in the chrome bar, e.g. \"sharlese.epk.pro\"."
9845
+ },
9846
+ {
9847
+ "name": "href",
9848
+ "type": "string",
9849
+ "required": false,
9850
+ "description": "Optional real link target. When set, the URL pill becomes a clickable <a> with a focus ring."
9851
+ },
9852
+ {
9853
+ "name": "children",
9854
+ "type": "React.ReactNode",
9855
+ "required": true,
9856
+ "description": "Content rendered inside the frame below the chrome bar."
9857
+ },
9858
+ {
9859
+ "name": "className",
9860
+ "type": "string",
9861
+ "required": false,
9862
+ "description": "Additional class name applied to the outer wrapper (e.g. to set --lit-color or --browser-frame-focus-color)."
9863
+ }
9864
+ ],
9865
+ "dependencies": [
9866
+ "@loworbitstudio/visor-core",
9867
+ "@phosphor-icons/react"
9868
+ ],
9869
+ "tokens_used": [
9870
+ "--accent",
9871
+ "--browser-frame-bg",
9872
+ "--browser-frame-focus-color",
9873
+ "--focus-ring-offset",
9874
+ "--focus-ring-width",
9875
+ "--font-weight-light",
9876
+ "--hairline",
9877
+ "--hairline-strong",
9878
+ "--motion-duration-fast",
9879
+ "--motion-easing-standard",
9880
+ "--opacity-10",
9881
+ "--opacity-12",
9882
+ "--opacity-5",
9883
+ "--opacity-60",
9884
+ "--opacity-80",
9885
+ "--radius-md",
9886
+ "--radius-xl",
9887
+ "--spacing-1",
9888
+ "--spacing-10",
9889
+ "--spacing-3",
9890
+ "--spacing-3_5",
9891
+ "--stroke-width-thin"
9892
+ ],
9893
+ "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"
9894
+ },
9895
+ "card-lift": {
9896
+ "category": "visual-elements",
9897
+ "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).",
9898
+ "when_to_use": [
9899
+ "Adding hover-lift affordance to cards, tiles, or any clickable surface",
9900
+ "Marketing pages and showcase walls where surfaces should \"catch the light\" on hover",
9901
+ "Keying the halo color to a live CSS variable (e.g. artist accent color) that changes at runtime"
9902
+ ],
9903
+ "when_not_to_use": [
9904
+ "Standard admin/app UI where motion-heavy hover effects are distracting",
9905
+ "When you need the entire hover interaction (border, background, text) — CardLift is a motion-only layer",
9906
+ "Contexts where `prefers-reduced-motion` users will miss the affordance (pair with a visible focus style on the inner element)"
9907
+ ],
9908
+ "props": [
9909
+ {
9910
+ "name": "liftColor",
9911
+ "type": "string",
9912
+ "default": "var(--accent, #6366f1)",
9913
+ "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"
9914
+ },
9915
+ {
9916
+ "name": "children",
9917
+ "type": "React.ReactNode",
9918
+ "description": "Content to wrap (typically a Card or BentoGrid tile)."
9919
+ },
9920
+ {
9921
+ "name": "className",
9922
+ "type": "string",
9923
+ "description": "Additional CSS class names to merge onto the wrapper."
9924
+ },
9925
+ {
9926
+ "name": "style",
9927
+ "type": "React.CSSProperties",
9928
+ "description": "Inline styles merged onto the wrapper (after `--lift-color`)."
9929
+ }
9930
+ ],
9931
+ "dependencies": [
9932
+ "@loworbitstudio/visor-core"
9933
+ ],
9934
+ "tokens_used": [
9935
+ "--accent",
9936
+ "--lift-color",
9937
+ "--shadow-"
9938
+ ],
9939
+ "example": "<CardLift liftColor=\"var(--accent)\">\n <Card>\n <CardContent>Hover me</CardContent>\n </Card>\n</CardLift>\n"
9940
+ },
9941
+ "grain-overlay": {
9942
+ "category": "visual-elements",
9943
+ "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.",
9944
+ "when_to_use": [
9945
+ "Dark marketing surfaces where large near-black fills read as flat and digital",
9946
+ "Hero sections or landing pages with a cinema/atmospheric aesthetic",
9947
+ "Any surface using the Blacklight or similar dark depth-system look"
9948
+ ],
9949
+ "when_not_to_use": [
9950
+ "Light-mode surfaces (grain is near-invisible and adds nothing)",
9951
+ "Pages where every byte of bundle size counts (the SVG data URI is ~250 bytes — fine in practice)",
9952
+ "Animated grain effects (this is intentionally static; animated grain repaints the full viewport every frame)"
9953
+ ],
9954
+ "props": [
9955
+ {
9956
+ "name": "opacity",
9957
+ "type": "number",
9958
+ "default": "0.035",
9959
+ "description": "Grain opacity (0–1). Default matches the Blacklight bl-grain reference."
9960
+ },
9961
+ {
9962
+ "name": "zIndex",
9963
+ "type": "number",
9964
+ "default": "30",
9965
+ "description": "z-index of the overlay layer. Default 30 sits above page sections but below modals (z-100)."
9966
+ }
9967
+ ],
9968
+ "dependencies": [
9969
+ "@loworbitstudio/visor-core"
9970
+ ],
9971
+ "tokens_used": [],
9972
+ "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"
9973
+ },
9974
+ "hero-glow": {
9975
+ "category": "visual-elements",
9976
+ "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.",
9977
+ "when_to_use": [
9978
+ "Behind hero images or carousels to cast a color-keyed ambient glow",
9979
+ "When the glow color must track a live value (e.g. current artist color, active palette)",
9980
+ "Marketing hero sections that need atmospheric depth without a heavy shader"
9981
+ ],
9982
+ "when_not_to_use": [
9983
+ "As a layout element — HeroGlow is purely decorative (aria-hidden, pointer-events none)",
9984
+ {
9985
+ "When the parent is not position": "relative (the negative inset will be clipped or invisible)"
9986
+ },
9987
+ "When a static, non-breathing glow is preferred (use a box-shadow or plain gradient instead)"
9988
+ ],
9989
+ "props": [
9990
+ {
9991
+ "name": "glowColor",
9992
+ "type": "string",
9993
+ "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",
9994
+ "default": "undefined"
9995
+ }
9996
+ ],
9997
+ "dependencies": [
9998
+ "@loworbitstudio/visor-core"
9999
+ ],
10000
+ "tokens_used": [
10001
+ "--glow-color"
10002
+ ],
10003
+ "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"
10004
+ },
8890
10005
  "sphere": {
8891
10006
  "category": "visual-elements",
8892
10007
  "description": "GPU-accelerated particle sphere visualization with 256K particles, custom GLSL shaders, 6 geometry modes, 5 color schemes, and think-mode animation effects.",
@@ -8977,6 +10092,50 @@
8977
10092
  ],
8978
10093
  "tokens_used": [],
8979
10094
  "example": "<Sphere\n mode=\"sphere\"\n colorScheme=\"solar\"\n speed={1}\n style={{ width: \"100%\", height: \"400px\" }}\n/>\n"
10095
+ },
10096
+ "vignette": {
10097
+ "category": "visual-elements",
10098
+ "description": "Fixed, full-viewport radial vignette layer. Decorative atmosphere atom — pointer-events-none, aria-hidden, static. Ported from Blacklight bl-vignette (BL-326).",
10099
+ "when_to_use": [
10100
+ "Adding cinematic depth to dark-background hero sections",
10101
+ "Creating stage-lighting atmosphere on full-viewport canvases",
10102
+ "Softening hard edges on near-black page backgrounds"
10103
+ ],
10104
+ "when_not_to_use": [
10105
+ "Light-mode pages (the dark falloff color will be jarring)",
10106
+ "Content-heavy pages where the dark vignette obscures readable text at the edges",
10107
+ "When you need a scrolling (non-fixed) overlay — Vignette is always fixed"
10108
+ ],
10109
+ "props": [
10110
+ {
10111
+ "name": "zIndex",
10112
+ "type": "number",
10113
+ "default": 20,
10114
+ "description": "CSS z-index for the vignette layer"
10115
+ },
10116
+ {
10117
+ "name": "className",
10118
+ "type": "string",
10119
+ "description": "Additional class names"
10120
+ },
10121
+ {
10122
+ "name": "style",
10123
+ "type": "React.CSSProperties",
10124
+ "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"
10125
+ }
10126
+ ],
10127
+ "dependencies": [
10128
+ "@loworbitstudio/visor-core"
10129
+ ],
10130
+ "tokens_used": [
10131
+ "--vignette-color",
10132
+ "--vignette-color-stop",
10133
+ "--vignette-position",
10134
+ "--vignette-size-x",
10135
+ "--vignette-size-y",
10136
+ "--vignette-transparent-stop"
10137
+ ],
10138
+ "example": "<div style={{ position: 'relative', height: '400px', background: '#020214' }}>\n <Vignette />\n</div>\n"
8980
10139
  }
8981
10140
  },
8982
10141
  "blocks": {
@@ -10188,6 +11347,7 @@
10188
11347
  "opacity-bar",
10189
11348
  "radius-scale",
10190
11349
  "spacing-scale",
11350
+ "specimen-card",
10191
11351
  "surface-row",
10192
11352
  "surface-scale-stack",
10193
11353
  "type-scale-stack",
@@ -10203,7 +11363,9 @@
10203
11363
  "key-value-list",
10204
11364
  "marquee",
10205
11365
  "name-roster",
11366
+ "segmented-progress",
10206
11367
  "sparkline",
11368
+ "spinner",
10207
11369
  "status-dot",
10208
11370
  "table",
10209
11371
  "timeline"
@@ -10227,10 +11389,13 @@
10227
11389
  "feedback": [
10228
11390
  "alert",
10229
11391
  "banner",
11392
+ "challenge-card",
11393
+ "error-placard",
10230
11394
  "popover",
10231
11395
  "progress",
10232
11396
  "skeleton",
10233
11397
  "toast",
11398
+ "toast-card",
10234
11399
  "tooltip"
10235
11400
  ],
10236
11401
  "layout": [
@@ -10263,6 +11428,7 @@
10263
11428
  "chip",
10264
11429
  "color-picker",
10265
11430
  "combobox",
11431
+ "composer",
10266
11432
  "date-picker",
10267
11433
  "date-range-picker",
10268
11434
  "field",
@@ -10280,6 +11446,7 @@
10280
11446
  "select",
10281
11447
  "slider",
10282
11448
  "slider-control",
11449
+ "structured-prompt",
10283
11450
  "switch",
10284
11451
  "tag-input",
10285
11452
  "textarea",
@@ -10304,13 +11471,20 @@
10304
11471
  "menubar",
10305
11472
  "sheet"
10306
11473
  ],
10307
- "visual-elements": [
10308
- "sphere",
10309
- "station-spectrum"
10310
- ],
10311
11474
  "general": [
11475
+ "section-intro",
10312
11476
  "theme-switcher"
10313
11477
  ],
11478
+ "visual-elements": [
11479
+ "ambient-glow",
11480
+ "browser-frame",
11481
+ "card-lift",
11482
+ "grain-overlay",
11483
+ "hero-glow",
11484
+ "sphere",
11485
+ "station-spectrum",
11486
+ "vignette"
11487
+ ],
10314
11488
  "deck": [
10315
11489
  "deck-card-grid",
10316
11490
  "deck-carousel-gallery",
@@ -10375,7 +11549,12 @@
10375
11549
  "components": [
10376
11550
  "*"
10377
11551
  ]
10378
- }
11552
+ },
11553
+ "proof": [
11554
+ "Switch theme or mode and the entire surface re-resolves — nothing is pinned to a hard-coded value.",
11555
+ "Every component reads tokens, never literals: --primary flows to semantic, then adaptive, then the rendered pixel.",
11556
+ "Change one .visor.yaml file and the whole system follows — the file is the single source the cascade derives from."
11557
+ ]
10379
11558
  },
10380
11559
  {
10381
11560
  "id": "openness",
@@ -10386,7 +11565,12 @@
10386
11565
  "cli",
10387
11566
  "component-metadata"
10388
11567
  ]
10389
- }
11568
+ },
11569
+ "proof": [
11570
+ "An agent can discover, select, and compose a component from structured data alone — the manifest, when_to_use metadata, and an agent-first CLI.",
11571
+ "The source is open and free to take; the same file an engineer reads is the one an agent queries.",
11572
+ "Even brand strategy ships as readable data — this record — not a locked PDF."
11573
+ ]
10390
11574
  },
10391
11575
  {
10392
11576
  "id": "ownership",
@@ -10395,7 +11579,12 @@
10395
11579
  "components": [
10396
11580
  "*"
10397
11581
  ]
10398
- }
11582
+ },
11583
+ "proof": [
11584
+ "npx visor add button copies real source into your project — yours to edit, with nothing to eject from.",
11585
+ "Tokens still update via npm update, so you keep design consistency without surrendering control.",
11586
+ "Copy-and-own is the starting state, not an escape hatch — there's no wrapper to fight."
11587
+ ]
10399
11588
  }
10400
11589
  ],
10401
11590
  "voice": {
@@ -10483,7 +11672,64 @@
10483
11672
  "essence",
10484
11673
  "pillars"
10485
11674
  ],
10486
- "visibility": "public"
11675
+ "visibility": "public",
11676
+ "messaging": {
11677
+ "roof": "Design intent as data — all the way up to brand."
11678
+ },
11679
+ "taglines": [
11680
+ "Your entire brand system, created and encoded in one file."
11681
+ ],
11682
+ "boilerplate": {
11683
+ "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.",
11684
+ "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."
11685
+ },
11686
+ "colorUsage": {
11687
+ "pairings": [
11688
+ {
11689
+ "use": "--primary",
11690
+ "with": "--surface-card / --surface-base",
11691
+ "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."
11692
+ },
11693
+ {
11694
+ "use": "--accent",
11695
+ "with": "--primary",
11696
+ "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."
11697
+ },
11698
+ {
11699
+ "use": "--text-primary / --text-secondary",
11700
+ "with": "--surface-card / --surface-base",
11701
+ "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."
11702
+ },
11703
+ {
11704
+ "use": "--destructive",
11705
+ "with": "--surface-card",
11706
+ "rule": "Destructive is reserved for irreversible or error states; it is never a decorative or emphasis color."
11707
+ },
11708
+ {
11709
+ "use": "fallback neutral (Gray)",
11710
+ "with": "any un-themed surface",
11711
+ "rule": "Fallbacks use Gray, not Slate, so an un-themed component lands on a neutral that fits the palette instead of clashing."
11712
+ }
11713
+ ]
11714
+ },
11715
+ "accessibility": {
11716
+ "standard": "WCAG 2.1 AA",
11717
+ "contrast": [
11718
+ {
11719
+ "context": "Body text and other normal-size text",
11720
+ "ratio": "4.5:1"
11721
+ },
11722
+ {
11723
+ "context": "Large text (≥ 24px, or ≥ 18.66px bold)",
11724
+ "ratio": "3:1"
11725
+ },
11726
+ {
11727
+ "context": "Non-text UI — icons, focus rings, control boundaries",
11728
+ "ratio": "3:1"
11729
+ }
11730
+ ],
11731
+ "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."
11732
+ }
10487
11733
  },
10488
11734
  "tokens": {
10489
11735
  "primitives": [