@loworbitstudio/visor 1.11.1 → 1.12.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-15T22:40:15.938Z",
3
+ "generated_at": "2026-06-25T04:18:15.839Z",
4
4
  "components": {
5
5
  "accessibility-specimen": {
6
6
  "category": "specimen",
@@ -1861,6 +1861,120 @@
1861
1861
  ],
1862
1862
  "example": "<CodeBlock\n code={`const greeting = \"Hello, world!\"\\nconsole.log(greeting)`}\n language=\"typescript\"\n showLineNumbers\n/>\n\n<CodeBlock code=\"npm install @loworbitstudio/visor-core\" title=\"Install\" />\n"
1863
1863
  },
1864
+ "coherence-check": {
1865
+ "category": "feedback",
1866
+ "description": "Check group with pass/warn/fail rows for displaying coherence audit results — group header + state icon circle + title, description with inline code support, and optional ghost Fix action.",
1867
+ "when_to_use": [
1868
+ "Displaying a grouped list of brand or system coherence checks (pass / warn / fail)",
1869
+ "Presenting audit results where each item has a state, a title, and an optional corrective action",
1870
+ "Building Prove-stage UIs in a Brand Workbench flow where checks gate an export",
1871
+ "Showing accessibility, derivation, or voice-consistency checks inline with fix affordances"
1872
+ ],
1873
+ "when_not_to_use": [
1874
+ "Progress tracking across sequential steps (use Stepper)",
1875
+ "Simple boolean on/off toggles or checkboxes (use Checkbox)",
1876
+ "Validation errors on individual form fields (use Field error state)",
1877
+ "Status badges on cards or list items (use StatusBadge or Badge)"
1878
+ ],
1879
+ "variants": {
1880
+ "state": [
1881
+ "pass",
1882
+ "warn",
1883
+ "fail"
1884
+ ]
1885
+ },
1886
+ "props": [
1887
+ {
1888
+ "name": "heading",
1889
+ "type": "string",
1890
+ "description": "Group header label (rendered uppercase).",
1891
+ "applies_to": "CheckGroup"
1892
+ },
1893
+ {
1894
+ "name": "state",
1895
+ "type": "'pass' | 'warn' | 'fail'",
1896
+ "default": "pass",
1897
+ "description": "Semantic state controlling the icon and tint color.",
1898
+ "applies_to": "CheckRow"
1899
+ },
1900
+ {
1901
+ "name": "title",
1902
+ "type": "string",
1903
+ "description": "Check title / finding label.",
1904
+ "applies_to": "CheckRow"
1905
+ },
1906
+ {
1907
+ "name": "description",
1908
+ "type": "React.ReactNode",
1909
+ "description": "Supporting description. Accepts inline <code> elements for token names.",
1910
+ "applies_to": "CheckRow"
1911
+ },
1912
+ {
1913
+ "name": "fixLabel",
1914
+ "type": "string",
1915
+ "description": "Label for the ghost Fix action button. Omit to hide the button.",
1916
+ "applies_to": "CheckRow"
1917
+ },
1918
+ {
1919
+ "name": "onFix",
1920
+ "type": "() => void",
1921
+ "description": "Callback invoked when the Fix action button is clicked.",
1922
+ "applies_to": "CheckRow"
1923
+ }
1924
+ ],
1925
+ "sub_components": [
1926
+ {
1927
+ "name": "CheckGroup",
1928
+ "description": "Groups related CheckRow items under an uppercase section heading."
1929
+ },
1930
+ {
1931
+ "name": "CheckRow",
1932
+ "description": "Individual check result row with a state icon circle, title, optional description, and optional ghost Fix action button."
1933
+ }
1934
+ ],
1935
+ "dependencies": [
1936
+ "class-variance-authority",
1937
+ "@phosphor-icons/react"
1938
+ ],
1939
+ "tokens_used": [
1940
+ "--border-default",
1941
+ "--border-focus",
1942
+ "--border-strong",
1943
+ "--destructive",
1944
+ "--focus-ring-offset",
1945
+ "--focus-ring-width",
1946
+ "--font-mono",
1947
+ "--font-size-sm",
1948
+ "--font-size-xs",
1949
+ "--font-weight-bold",
1950
+ "--font-weight-extrabold",
1951
+ "--hairline",
1952
+ "--motion-duration-150",
1953
+ "--motion-easing-default",
1954
+ "--radius-full",
1955
+ "--radius-lg",
1956
+ "--radius-md",
1957
+ "--radius-sm",
1958
+ "--shadow-sm",
1959
+ "--spacing-1",
1960
+ "--spacing-2",
1961
+ "--spacing-3",
1962
+ "--spacing-4",
1963
+ "--spacing-5",
1964
+ "--spacing-6",
1965
+ "--spacing-8",
1966
+ "--stroke-width-thin",
1967
+ "--success",
1968
+ "--surface-card",
1969
+ "--surface-muted",
1970
+ "--text-inverse",
1971
+ "--text-primary",
1972
+ "--text-secondary",
1973
+ "--text-tertiary",
1974
+ "--warning"
1975
+ ],
1976
+ "example": "<CheckGroup heading=\"Accessibility — WCAG 2.1 AA\">\n <CheckRow\n state=\"fail\"\n title=\"One pairing fails on small text\"\n description={<>\n <code>--text-tertiary</code> on <code>--surface-subtle</code> is 3.9:1 — below 4.5:1 for body text.\n </>}\n fixLabel=\"Suggest a fix\"\n onFix={() => {}}\n />\n <CheckRow\n state=\"pass\"\n title=\"Primary, text, and focus rings clear AA\"\n description=\"Body 7.1:1 · large text 4.8:1 · non-text UI 3.4:1 — all above target on both light and dark.\"\n />\n</CheckGroup>\n"
1977
+ },
1864
1978
  "collapsible": {
1865
1979
  "category": "layout",
1866
1980
  "description": "Simple expandable section with animated open/close transitions.",
@@ -2612,6 +2726,82 @@
2612
2726
  ],
2613
2727
  "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"
2614
2728
  },
2729
+ "conflict-banner": {
2730
+ "category": "feedback",
2731
+ "description": "Inline conflict detection and optimistic-rollback UI for concurrent edit scenarios.",
2732
+ "when_to_use": [
2733
+ "Detecting and surfacing concurrent edit conflicts (409 / ETag mismatch)",
2734
+ "Presenting a \"Keep my version\" vs \"Load latest\" choice to the user",
2735
+ "Reverting a failed optimistic update cleanly and atomically",
2736
+ "Showing a collapsible field-level diff after a conflict is detected"
2737
+ ],
2738
+ "when_not_to_use": [
2739
+ "Ephemeral success or failure notifications (use Toast)",
2740
+ "Page-level alerts that don't require a user decision (use Alert or Banner)",
2741
+ "Errors unrelated to concurrent edits (use Alert variant=\"destructive\")"
2742
+ ],
2743
+ "variants": {},
2744
+ "props": [
2745
+ {
2746
+ "name": "state",
2747
+ "type": "'pending' | 'conflict' | 'resolving' | 'resolved-local' | 'resolved-remote'",
2748
+ "default": "conflict"
2749
+ },
2750
+ {
2751
+ "name": "description",
2752
+ "type": "string",
2753
+ "default": "'This record was updated by someone else.'"
2754
+ },
2755
+ {
2756
+ "name": "diffs",
2757
+ "type": "ConflictDiff[]",
2758
+ "default": "[]"
2759
+ },
2760
+ {
2761
+ "name": "onKeepMine",
2762
+ "type": "() => void",
2763
+ "default": "undefined"
2764
+ },
2765
+ {
2766
+ "name": "onLoadLatest",
2767
+ "type": "() => void",
2768
+ "default": "undefined"
2769
+ }
2770
+ ],
2771
+ "sub_components": [],
2772
+ "dependencies": [
2773
+ "@phosphor-icons/react"
2774
+ ],
2775
+ "tokens_used": [
2776
+ "--border-default",
2777
+ "--border-warning",
2778
+ "--font-size-sm",
2779
+ "--font-size-xs",
2780
+ "--font-weight-medium",
2781
+ "--font-weight-semibold",
2782
+ "--motion-duration-800",
2783
+ "--motion-duration-fast",
2784
+ "--motion-easing-base",
2785
+ "--motion-easing-enter",
2786
+ "--motion-easing-linear",
2787
+ "--opacity-50",
2788
+ "--radius-md",
2789
+ "--spacing-1",
2790
+ "--spacing-2",
2791
+ "--spacing-3",
2792
+ "--spacing-4",
2793
+ "--stroke-width-medium",
2794
+ "--stroke-width-thin",
2795
+ "--surface-subtle",
2796
+ "--surface-warning-subtle",
2797
+ "--text-error",
2798
+ "--text-primary",
2799
+ "--text-secondary",
2800
+ "--text-tertiary",
2801
+ "--text-warning"
2802
+ ],
2803
+ "example": "<ConflictBanner\n state=\"conflict\"\n description=\"Jordan Kim saved changes 30 seconds ago.\"\n diffs={[{ field: \"Title\", yours: \"Draft v2\", theirs: \"Draft v1\" }]}\n onKeepMine={() => void 0}\n onLoadLatest={() => void 0}\n/>\n"
2804
+ },
2615
2805
  "container": {
2616
2806
  "category": "layout",
2617
2807
  "description": "Max-width centered wrapper for page content with token-driven horizontal padding.",
@@ -3376,6 +3566,109 @@
3376
3566
  ],
3377
3567
  "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"
3378
3568
  },
3569
+ "editable-block": {
3570
+ "category": "general",
3571
+ "description": "Canvas brand block — a card tile with an uppercase label header, value body, hover-revealed edit icon, and editing state with inline input, save button, and AI action slot.",
3572
+ "when_to_use": [
3573
+ "Building a free-edit canvas board where each cell in a brand record (essence, positioning, personality, etc.) is independently editable",
3574
+ "Any situation where a small labeled data block needs in-place editing without a modal or drawer",
3575
+ "Displaying a short AI-derived value the user can challenge or refine with a direct AI action",
3576
+ "Showing a board of brand strategy blocks that each represent a distinct, modifiable attribute"
3577
+ ],
3578
+ "when_not_to_use": [
3579
+ "Multi-field forms — use a form with labeled inputs + a single submit action instead",
3580
+ "Long-form text editing — use a textarea or rich-text editor",
3581
+ "Read-only display cards with no edit intent — use Card + Text",
3582
+ "Situations where editing must be confirmed by a secondary reviewer before saving — use a review workflow instead"
3583
+ ],
3584
+ "variants": {
3585
+ "state": {
3586
+ "default": "View state — label + value body; edit icon visible only on hover.",
3587
+ "done": "Confirmed block — adds a green check to the header; still editable.",
3588
+ "editing": "Active edit — focus ring border, inline input + save button, AI action slot."
3589
+ }
3590
+ },
3591
+ "props": [
3592
+ {
3593
+ "name": "label",
3594
+ "type": "string",
3595
+ "description": "Block label rendered uppercase above the value."
3596
+ },
3597
+ {
3598
+ "name": "value",
3599
+ "type": "string",
3600
+ "description": "The current value to display (and the initial draft value when editing opens)."
3601
+ },
3602
+ {
3603
+ "name": "done",
3604
+ "type": "boolean",
3605
+ "default": "false",
3606
+ "description": "Shows a success check icon in the header when true."
3607
+ },
3608
+ {
3609
+ "name": "onSave",
3610
+ "type": "(value: string) => void",
3611
+ "description": "Called when the user saves an edited value. Parent is responsible for updating `value`."
3612
+ },
3613
+ {
3614
+ "name": "aiActionLabel",
3615
+ "type": "string | null",
3616
+ "default": "\"Ask AI to pressure-test\"",
3617
+ "description": "Label for the AI action button shown in editing state. Pass null to suppress the button."
3618
+ },
3619
+ {
3620
+ "name": "onAiAction",
3621
+ "type": "() => void",
3622
+ "description": "Called when the AI action button is clicked."
3623
+ },
3624
+ {
3625
+ "name": "defaultEditing",
3626
+ "type": "boolean",
3627
+ "default": "false",
3628
+ "description": "Opens the block in editing state on mount. Useful for programmatic pre-open."
3629
+ }
3630
+ ],
3631
+ "dependencies": [
3632
+ "@phosphor-icons/react",
3633
+ "class-variance-authority",
3634
+ "@loworbitstudio/visor-core"
3635
+ ],
3636
+ "tokens_used": [
3637
+ "--border-default",
3638
+ "--focus-ring-offset",
3639
+ "--focus-ring-width",
3640
+ "--font-size-2xs",
3641
+ "--font-size-sm",
3642
+ "--font-size-xs",
3643
+ "--hairline",
3644
+ "--interactive-primary-bg",
3645
+ "--interactive-primary-soft",
3646
+ "--line-height-snug",
3647
+ "--motion-duration-150",
3648
+ "--motion-easing-default",
3649
+ "--opacity-80",
3650
+ "--primary",
3651
+ "--primary-text",
3652
+ "--radius-lg",
3653
+ "--radius-md",
3654
+ "--radius-sm",
3655
+ "--shadow-sm",
3656
+ "--spacing-1",
3657
+ "--spacing-1_5",
3658
+ "--spacing-2",
3659
+ "--spacing-2_5",
3660
+ "--spacing-3",
3661
+ "--spacing-3_5",
3662
+ "--spacing-8",
3663
+ "--stroke-width-thin",
3664
+ "--success",
3665
+ "--surface-card",
3666
+ "--surface-subtle",
3667
+ "--text-primary",
3668
+ "--text-tertiary"
3669
+ ],
3670
+ "example": "<EditableBlock\n label=\"Essence\"\n value=\"coherent · open · yours\"\n done={false}\n onSave={(v) => console.log('saved:', v)}\n onAiAction={() => console.log('AI: pressure-test')}\n/>\n"
3671
+ },
3379
3672
  "elevation-card": {
3380
3673
  "category": "specimen",
3381
3674
  "description": "Shadow level display card showing a single elevation token with its name and token reference.",
@@ -3436,6 +3729,11 @@
3436
3729
  "variant": [
3437
3730
  "default",
3438
3731
  "editorial"
3732
+ ],
3733
+ "intent": [
3734
+ "first-use",
3735
+ "zero-results",
3736
+ "no-access"
3439
3737
  ]
3440
3738
  },
3441
3739
  "props": [
@@ -3450,6 +3748,11 @@
3450
3748
  "type": "React.ReactNode",
3451
3749
  "description": "Leading visual, typically a Phosphor icon. Marked aria-hidden."
3452
3750
  },
3751
+ {
3752
+ "name": "iconWrap",
3753
+ "type": "boolean",
3754
+ "description": "Wraps the icon in a circular chip. Auto-enabled when intent is set. Can be used independently of intent to get the chip treatment without semantic coloring.\n"
3755
+ },
3453
3756
  {
3454
3757
  "name": "description",
3455
3758
  "type": "React.ReactNode",
@@ -3483,6 +3786,11 @@
3483
3786
  "default": "default",
3484
3787
  "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"
3485
3788
  },
3789
+ {
3790
+ "name": "intent",
3791
+ "type": "'first-use' | 'zero-results' | 'no-access'",
3792
+ "description": "Semantic intent of the empty state (Borealis global state spec §5). first-use: no items exist yet; accent-tinted icon chip; encourage creation CTA. zero-results: filter/search returned nothing; accent-tinted chip; offer clear-filter. no-access: permission gate; neutral chip; typically no CTA (terminal state). Setting intent auto-activates iconWrap.\n"
3793
+ },
3486
3794
  {
3487
3795
  "name": "headingAs",
3488
3796
  "type": "'h2' | 'h3' | 'h4'",
@@ -3518,6 +3826,8 @@
3518
3826
  "--spacing-5",
3519
3827
  "--spacing-6",
3520
3828
  "--spacing-8",
3829
+ "--surface-accent-default",
3830
+ "--surface-accent-subtle",
3521
3831
  "--surface-card",
3522
3832
  "--surface-muted",
3523
3833
  "--surface-subtle",
@@ -3981,6 +4291,53 @@
3981
4291
  ],
3982
4292
  "example": "import { Form } from \"@/components/ui/form/form\";\nimport { FormField } from \"@/components/ui/form/form-field\";\nimport { Button } from \"@/components/ui/button/button\";\nimport { contactSchema } from \"@/lib/schemas/contact\";\nimport { contactAction } from \"./actions\";\n\n<Form schema={contactSchema} action={contactAction}>\n {({ fields }) => (\n <>\n <FormField field={fields.name} label=\"Name\" placeholder=\"Your name\" />\n <FormField field={fields.email} label=\"Email\" type=\"email\" />\n <Button type=\"submit\">Send</Button>\n </>\n )}\n</Form>\n"
3983
4293
  },
4294
+ "form-error": {
4295
+ "category": "form",
4296
+ "description": "Form-level submission error banner — appears inside a form card when submission is blocked by field validation failures.",
4297
+ "when_to_use": [
4298
+ "A form submit is blocked by one or more invalid fields and the user needs to know there are errors to fix",
4299
+ "Server-side validation returns field-keyed errors (422) that need a top-of-form summary",
4300
+ "You want a visible anchor point at the top of the form alongside per-field FieldError messages"
4301
+ ],
4302
+ "when_not_to_use": [
4303
+ "Single-field inline validation — use FieldError on its own",
4304
+ "Page-level or section-level error announcements unrelated to a form (use Banner or ErrorPlacard)",
4305
+ "Ephemeral transient notifications (use Toast)",
4306
+ "Inside a dialog or overlay where space is constrained (prefer just FieldError)"
4307
+ ],
4308
+ "props": [
4309
+ {
4310
+ "name": "icon",
4311
+ "type": "ReactNode",
4312
+ "required": false,
4313
+ "description": "Optional leading icon (e.g. a Phosphor WarningCircle). Rendered at the destructive color. aria-hidden — role=\"alert\" carries the semantic weight."
4314
+ },
4315
+ {
4316
+ "name": "className",
4317
+ "type": "string",
4318
+ "required": false,
4319
+ "description": "Additional CSS class names merged onto the container element."
4320
+ }
4321
+ ],
4322
+ "dependencies": [
4323
+ "@loworbitstudio/visor-core"
4324
+ ],
4325
+ "tokens_used": [
4326
+ "--border-error",
4327
+ "--destructive",
4328
+ "--font-size-sm",
4329
+ "--font-weight-normal",
4330
+ "--font-weight-semibold",
4331
+ "--radius-md",
4332
+ "--spacing-1",
4333
+ "--spacing-3",
4334
+ "--spacing-4",
4335
+ "--surface-card",
4336
+ "--text-primary",
4337
+ "--text-secondary"
4338
+ ],
4339
+ "example": "import { WarningCircle } from '@phosphor-icons/react';\nimport { FormError, FormErrorTitle, FormErrorDescription } from '@/components/ui/form-error/form-error';\nimport { Field, FieldLabel, FieldError } from '@/components/ui/field/field';\nimport { Input } from '@/components/ui/input/input';\n\n{submitError && (\n <FormError icon={<WarningCircle size={18} weight=\"fill\" />}>\n <FormErrorTitle>Please fix the errors below before continuing</FormErrorTitle>\n <FormErrorDescription>2 fields need your attention.</FormErrorDescription>\n </FormError>\n)}\n\n<Field>\n <FieldLabel htmlFor=\"name\">Full name</FieldLabel>\n <Input\n id=\"name\"\n aria-invalid={nameError ? 'true' : undefined}\n aria-describedby={nameError ? 'name-error' : undefined}\n />\n {nameError && <FieldError id=\"name-error\">{nameError}</FieldError>}\n</Field>\n"
4340
+ },
3984
4341
  "fullscreen-overlay": {
3985
4342
  "category": "overlay",
3986
4343
  "description": "A full-viewport overlay for expanding content to fill the screen. Built on Radix Dialog with close button, Escape to dismiss, focus trap, and body scroll lock.",
@@ -5396,6 +5753,92 @@
5396
5753
  ],
5397
5754
  "example": "<NumberInput defaultValue={1} min={0} max={10} step={1} />\n"
5398
5755
  },
5756
+ "offline-banner": {
5757
+ "category": "feedback",
5758
+ "description": "App-shell sticky banner for network connectivity loss with offline, reconnecting, and restored states.",
5759
+ "when_to_use": [
5760
+ "When the app must signal network connectivity loss to the user non-intrusively",
5761
+ "As the primary offline indicator below the navigation bar in a full-page shell",
5762
+ "When stale data should remain visible and the app should remain navigable while offline",
5763
+ "When you need a manual retry action that checks connectivity without forcing a page reload"
5764
+ ],
5765
+ "when_not_to_use": [
5766
+ "For generic informational messages unrelated to network status (use Banner or Alert)",
5767
+ "When the failure is a server error, not a connectivity issue (use Alert with error intent)",
5768
+ "As an overlay that blocks content (use Dialog or a full-screen overlay instead)",
5769
+ "For ephemeral success/failure notifications (use Toast)"
5770
+ ],
5771
+ "variants": {
5772
+ "networkState": [
5773
+ "online",
5774
+ "offline",
5775
+ "reconnecting",
5776
+ "restored"
5777
+ ]
5778
+ },
5779
+ "props": [
5780
+ {
5781
+ "name": "networkState",
5782
+ "type": "'online' | 'offline' | 'reconnecting' | 'restored'",
5783
+ "required": true
5784
+ },
5785
+ {
5786
+ "name": "onRetry",
5787
+ "type": "() => void"
5788
+ },
5789
+ {
5790
+ "name": "offlineLabel",
5791
+ "type": "string",
5792
+ "default": "You're offline"
5793
+ },
5794
+ {
5795
+ "name": "reconnectingLabel",
5796
+ "type": "string",
5797
+ "default": "Reconnecting…"
5798
+ },
5799
+ {
5800
+ "name": "restoredLabel",
5801
+ "type": "string",
5802
+ "default": "Back online"
5803
+ },
5804
+ {
5805
+ "name": "retryLabel",
5806
+ "type": "string",
5807
+ "default": "Retry"
5808
+ }
5809
+ ],
5810
+ "dependencies": [
5811
+ "@phosphor-icons/react"
5812
+ ],
5813
+ "tokens_used": [
5814
+ "--accent",
5815
+ "--border-focus",
5816
+ "--border-success",
5817
+ "--focus-ring-offset",
5818
+ "--focus-ring-width",
5819
+ "--font-size-sm",
5820
+ "--font-weight-medium",
5821
+ "--font-weight-semibold",
5822
+ "--motion-duration-150",
5823
+ "--motion-duration-800",
5824
+ "--motion-duration-normal",
5825
+ "--motion-easing-default",
5826
+ "--motion-easing-enter",
5827
+ "--motion-easing-linear",
5828
+ "--radius-full",
5829
+ "--radius-sm",
5830
+ "--spacing-1",
5831
+ "--spacing-2",
5832
+ "--spacing-4",
5833
+ "--stroke-width-regular",
5834
+ "--stroke-width-thin",
5835
+ "--surface-overlay",
5836
+ "--surface-success-subtle",
5837
+ "--text-inverse",
5838
+ "--text-success"
5839
+ ],
5840
+ "example": "const { networkState, retry } = useNetworkStatus();\n<OfflineBanner networkState={networkState} onRetry={retry} />\n"
5841
+ },
5399
5842
  "opacity-bar": {
5400
5843
  "category": "specimen",
5401
5844
  "description": "Opacity level visualization showing bars at varying opacity values with token names.",
@@ -6721,6 +7164,114 @@
6721
7164
  ],
6722
7165
  "example": "<div>\n <h3>Section A</h3>\n <Separator />\n <h3>Section B</h3>\n</div>\n\n<div style={{ display: \"flex\", alignItems: \"center\", gap: 8 }}>\n <span>Item 1</span>\n <Separator orientation=\"vertical\" />\n <span>Item 2</span>\n</div>\n"
6723
7166
  },
7167
+ "session-timeout": {
7168
+ "category": "overlay",
7169
+ "description": "Non-dismissible full-screen overlay for auth session expiry. Renders at portal root, blurs app content, and presents a centered card with a primary Sign in CTA and an optional Return home escape hatch. ESC key and backdrop click are inert.",
7170
+ "when_to_use": [
7171
+ "JWT expiry detected client-side (exp claim check)",
7172
+ "401 response from API with error=invalid_token",
7173
+ "Supabase SIGNED_OUT event with reason TOKEN_REFRESHED_FAILED",
7174
+ "Any scenario where an auth session expires mid-flow and you must redirect to login"
7175
+ ],
7176
+ "when_not_to_use": [
7177
+ "Confirmable dialogs (use ConfirmDialog)",
7178
+ "Standard authentication gates (use a route guard / redirect instead)",
7179
+ "Dismissible notifications (use Banner or Toast)",
7180
+ "Inline inline auth errors in a form (use field-level validation)"
7181
+ ],
7182
+ "props": [
7183
+ {
7184
+ "name": "open",
7185
+ "type": "boolean",
7186
+ "default": "false",
7187
+ "description": "Whether the session timeout overlay is visible. Drive from your auth event listener."
7188
+ },
7189
+ {
7190
+ "name": "onSignIn",
7191
+ "type": "() => void | Promise<void>",
7192
+ "default": "undefined",
7193
+ "description": "Called when the user clicks \"Sign in\". Returning a Promise triggers the redirecting state (spinner + disabled CTA) automatically."
7194
+ },
7195
+ {
7196
+ "name": "onReturnHome",
7197
+ "type": "() => void",
7198
+ "default": "undefined",
7199
+ "description": "Called when the user clicks \"Return home\". If omitted, the ghost link is not rendered."
7200
+ },
7201
+ {
7202
+ "name": "signInLabel",
7203
+ "type": "React.ReactNode",
7204
+ "default": "\"Sign in\"",
7205
+ "description": "Label for the primary CTA."
7206
+ },
7207
+ {
7208
+ "name": "returnHomeLabel",
7209
+ "type": "React.ReactNode",
7210
+ "default": "\"Return home\"",
7211
+ "description": "Label for the optional ghost link."
7212
+ },
7213
+ {
7214
+ "name": "className",
7215
+ "type": "string",
7216
+ "default": "undefined",
7217
+ "description": "Additional class name applied to the card surface."
7218
+ }
7219
+ ],
7220
+ "dependencies": [
7221
+ "@radix-ui/react-dialog",
7222
+ "@phosphor-icons/react",
7223
+ "@loworbitstudio/visor-core"
7224
+ ],
7225
+ "tokens_used": [
7226
+ "--border-default",
7227
+ "--border-focus",
7228
+ "--focus-ring-offset",
7229
+ "--focus-ring-width",
7230
+ "--font-size-sm",
7231
+ "--font-size-xl",
7232
+ "--font-weight-bold",
7233
+ "--font-weight-medium",
7234
+ "--font-weight-semibold",
7235
+ "--interactive-primary-bg",
7236
+ "--interactive-primary-bg-hover",
7237
+ "--interactive-primary-text",
7238
+ "--motion-duration-150",
7239
+ "--motion-duration-1500",
7240
+ "--motion-duration-fast",
7241
+ "--motion-duration-normal",
7242
+ "--motion-easing-default",
7243
+ "--motion-easing-enter",
7244
+ "--motion-easing-exit",
7245
+ "--motion-easing-linear",
7246
+ "--opacity-10",
7247
+ "--opacity-40",
7248
+ "--opacity-60",
7249
+ "--opacity-80",
7250
+ "--overlay-bg",
7251
+ "--primary",
7252
+ "--radius-full",
7253
+ "--radius-md",
7254
+ "--radius-sm",
7255
+ "--radius-xl",
7256
+ "--shadow-lg",
7257
+ "--spacing-1",
7258
+ "--spacing-10",
7259
+ "--spacing-2",
7260
+ "--spacing-3",
7261
+ "--spacing-4",
7262
+ "--spacing-6",
7263
+ "--spacing-7",
7264
+ "--spacing-9",
7265
+ "--stroke-width-regular",
7266
+ "--stroke-width-thin",
7267
+ "--surface-card",
7268
+ "--text-inverse",
7269
+ "--text-primary",
7270
+ "--text-secondary",
7271
+ "--text-tertiary"
7272
+ ],
7273
+ "example": "<SessionTimeout\n open={isExpired}\n onSignIn={() => {\n const redirect = window.location.pathname;\n router.push(`/login?redirect=${encodeURIComponent(redirect)}`);\n }}\n onReturnHome={() => router.push('/')}\n/>\n"
7274
+ },
6724
7275
  "sheet": {
6725
7276
  "category": "overlay",
6726
7277
  "description": "Slide-over panel that enters from any edge of the screen.",
@@ -7024,6 +7575,7 @@
7024
7575
  "dependencies": [],
7025
7576
  "tokens_used": [
7026
7577
  "--_shimmer-duration",
7578
+ "--border-default",
7027
7579
  "--motion-easing-linear",
7028
7580
  "--radius-full",
7029
7581
  "--radius-md",
@@ -7034,6 +7586,9 @@
7034
7586
  "--skeleton-pill-height",
7035
7587
  "--skeleton-pill-width",
7036
7588
  "--skeleton-to",
7589
+ "--spacing-2",
7590
+ "--spacing-3",
7591
+ "--spacing-4",
7037
7592
  "--text-primary"
7038
7593
  ],
7039
7594
  "example": "<Skeleton className=\"h-4 w-[200px]\" />\n<Skeleton className=\"h-12 w-12 rounded-full\" />\n"
@@ -7163,6 +7718,52 @@
7163
7718
  ],
7164
7719
  "example": "<SliderControl\n label=\"Size\"\n value={scale}\n onValueChange={setScale}\n displayValue={scale.toFixed(1)}\n min={0.2}\n max={3.0}\n step={0.05}\n/>\n"
7165
7720
  },
7721
+ "slow-network-bar": {
7722
+ "category": "feedback",
7723
+ "description": "4px indeterminate progress bar that appears after a threshold delay (~3 s) to indicate a slow or stalled network request.",
7724
+ "when_to_use": [
7725
+ "A fetch or mutation is still pending after ~3 000 ms",
7726
+ "An upload or download has produced no progress event for 5 s",
7727
+ "Communicating \"still working\" without blocking interaction"
7728
+ ],
7729
+ "when_not_to_use": [
7730
+ "Initial page load (use Skeleton instead — never both in the same zone)",
7731
+ "Determinate progress with a known percentage (use Progress)",
7732
+ "Operation that has succeeded or failed (dismiss the bar; let the result pattern take over)",
7733
+ "Short operations that complete in < 3 s (the threshold prevents this bar from appearing)"
7734
+ ],
7735
+ "props": [
7736
+ {
7737
+ "name": "state",
7738
+ "type": "'hidden' | 'visible' | 'resolving'",
7739
+ "default": "'hidden'",
7740
+ "description": "Visibility state. hidden = not rendered; visible = indeterminate animation; resolving = sweep completes then fades out."
7741
+ },
7742
+ {
7743
+ "name": "label",
7744
+ "type": "string",
7745
+ "default": "'Loading, please wait…'",
7746
+ "description": "Accessible label announced to screen readers when the bar becomes visible."
7747
+ },
7748
+ {
7749
+ "name": "className",
7750
+ "type": "string",
7751
+ "description": "Additional CSS class names to merge onto the element."
7752
+ }
7753
+ ],
7754
+ "dependencies": [],
7755
+ "tokens_used": [
7756
+ "--accent",
7757
+ "--motion-duration-1500",
7758
+ "--motion-duration-300",
7759
+ "--motion-easing-ease-in",
7760
+ "--motion-easing-ease-in-out",
7761
+ "--motion-easing-ease-out",
7762
+ "--opacity-40",
7763
+ "--primary"
7764
+ ],
7765
+ "example": "// Controlled via useSlowRequest hook\nconst { state, trigger, resolve } = useSlowRequest(3000);\n\nconst handleSave = async () => {\n trigger();\n try {\n await saveData();\n } finally {\n resolve();\n }\n};\n\n<SlowNetworkBar state={state} />\n<button onClick={handleSave}>Save</button>\n"
7766
+ },
7166
7767
  "spacing-scale": {
7167
7768
  "category": "specimen",
7168
7769
  "description": "Visual spacing scale displaying token names, proportional bars, and pixel/rem values.",
@@ -8060,6 +8661,46 @@
8060
8661
  ],
8061
8662
  "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"
8062
8663
  },
8664
+ "success-feedback": {
8665
+ "category": "feedback",
8666
+ "description": "App-wide success/transition feedback pattern. Provides useSuccessToast() hook and SuccessLiveRegion a11y component built on the Toast primitive.",
8667
+ "when_to_use": [
8668
+ "After a CRUD operation completes successfully (create, update, delete, archive)",
8669
+ "When a file upload or export finishes",
8670
+ "After settings are saved",
8671
+ "When an invite is sent or a link is copied",
8672
+ "Any transient confirmation that a user action succeeded"
8673
+ ],
8674
+ "when_not_to_use": [
8675
+ "Error conditions (use inline validation or error Banner)",
8676
+ "Warnings that require user attention (use Banner)",
8677
+ "Persistent messages that must stay visible (use Banner or Alert)",
8678
+ "Content that requires user acknowledgment (use Dialog)"
8679
+ ],
8680
+ "props": [
8681
+ {
8682
+ "name": "message",
8683
+ "type": "string",
8684
+ "required": false,
8685
+ "description": "SuccessLiveRegion only — the success text to announce to screen readers. Updates trigger a re-announcement. Defaults to empty string."
8686
+ }
8687
+ ],
8688
+ "sub_components": [
8689
+ {
8690
+ "name": "useSuccessToast",
8691
+ "description": "Hook returning showSuccess(title, options) that fires a Sonner toast.success() with Borealis-spec defaults."
8692
+ },
8693
+ {
8694
+ "name": "SuccessLiveRegion",
8695
+ "description": "Visually-hidden role=status aria-live=polite node for screen-reader announcements."
8696
+ }
8697
+ ],
8698
+ "dependencies": [
8699
+ "sonner"
8700
+ ],
8701
+ "tokens_used": [],
8702
+ "example": "import { useSuccessToast, SuccessLiveRegion } from \"@/components/ui/success-feedback/success-feedback\"\nimport { Toaster } from \"@/components/ui/toast/toast\"\n\n{/* Mount both once in your root layout */}\n<Toaster />\n<SuccessLiveRegion message=\"\" />\n\n{/* In any component */}\nconst { showSuccess } = useSuccessToast()\nshowSuccess(\"Project saved\", { description: \"All changes have been saved.\" })\n"
8703
+ },
8063
8704
  "surface-row": {
8064
8705
  "category": "specimen",
8065
8706
  "description": "Surface background variant display showing a surface token with its name and token reference.",
@@ -8814,7 +9455,6 @@
8814
9455
  "--border-default",
8815
9456
  "--border-error",
8816
9457
  "--border-info",
8817
- "--border-success",
8818
9458
  "--border-warning",
8819
9459
  "--font-family-sans",
8820
9460
  "--font-size-sm",
@@ -8828,6 +9468,7 @@
8828
9468
  "--motion-easing-enter",
8829
9469
  "--motion-easing-exit",
8830
9470
  "--primary",
9471
+ "--primary-text",
8831
9472
  "--radius-lg",
8832
9473
  "--radius-md",
8833
9474
  "--shadow-lg",
@@ -8835,12 +9476,15 @@
8835
9476
  "--spacing-2",
8836
9477
  "--spacing-3",
8837
9478
  "--spacing-4",
9479
+ "--stroke-width-thin",
8838
9480
  "--surface-card",
8839
9481
  "--surface-error-subtle",
8840
9482
  "--surface-info-subtle",
9483
+ "--surface-inverse",
8841
9484
  "--surface-popover",
8842
- "--surface-success-subtle",
8843
9485
  "--surface-warning-subtle",
9486
+ "--text-inverse",
9487
+ "--text-inverse-secondary",
8844
9488
  "--text-primary",
8845
9489
  "--text-secondary"
8846
9490
  ],
@@ -11402,10 +12046,15 @@
11402
12046
  "alert",
11403
12047
  "banner",
11404
12048
  "challenge-card",
12049
+ "coherence-check",
12050
+ "conflict-banner",
11405
12051
  "error-placard",
12052
+ "offline-banner",
11406
12053
  "popover",
11407
12054
  "progress",
11408
12055
  "skeleton",
12056
+ "slow-network-bar",
12057
+ "success-feedback",
11409
12058
  "toast",
11410
12059
  "toast-card",
11411
12060
  "tooltip"
@@ -11447,6 +12096,7 @@
11447
12096
  "fieldset",
11448
12097
  "file-upload",
11449
12098
  "form",
12099
+ "form-error",
11450
12100
  "input",
11451
12101
  "label",
11452
12102
  "number-input",
@@ -11481,9 +12131,11 @@
11481
12131
  "fullscreen-overlay",
11482
12132
  "hover-card",
11483
12133
  "menubar",
12134
+ "session-timeout",
11484
12135
  "sheet"
11485
12136
  ],
11486
12137
  "general": [
12138
+ "editable-block",
11487
12139
  "section-intro",
11488
12140
  "theme-switcher"
11489
12141
  ],