@sovereignfs/ui 0.41.0 → 0.45.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.
Files changed (174) hide show
  1. package/dist/Accordion.module.css +3 -0
  2. package/dist/Alert.module.css +62 -0
  3. package/dist/AspectRatio.module.css +15 -0
  4. package/dist/Badge.module.css +75 -9
  5. package/dist/Breadcrumb.module.css +49 -0
  6. package/dist/Button.module.css +12 -3
  7. package/dist/ButtonGroup.module.css +29 -0
  8. package/dist/Card.module.css +6 -0
  9. package/dist/Checkbox.module.css +3 -1
  10. package/dist/Collapsible.module.css +70 -0
  11. package/dist/Combobox.module.css +122 -0
  12. package/dist/Command.module.css +80 -0
  13. package/dist/ConfirmDialog.module.css +5 -1
  14. package/dist/DataTable.module.css +28 -0
  15. package/dist/DatePicker.module.css +6 -2
  16. package/dist/Dialog.module.css +3 -0
  17. package/dist/HoverCard.module.css +3 -0
  18. package/dist/Input.module.css +6 -1
  19. package/dist/Item.module.css +70 -0
  20. package/dist/Kbd.module.css +14 -0
  21. package/dist/Label.module.css +11 -0
  22. package/dist/Marker.module.css +33 -0
  23. package/dist/Menu.module.css +8 -1
  24. package/dist/Message.module.css +99 -0
  25. package/dist/MessageScroller.module.css +54 -0
  26. package/dist/NavigationMenu.module.css +53 -0
  27. package/dist/Pagination.module.css +98 -0
  28. package/dist/Progress.module.css +20 -0
  29. package/dist/RadioGroup.module.css +88 -0
  30. package/dist/Resizable.module.css +70 -0
  31. package/dist/ScrollArea.module.css +20 -0
  32. package/dist/Select.module.css +7 -3
  33. package/dist/Slider.module.css +95 -0
  34. package/dist/Table.module.css +45 -0
  35. package/dist/Tabs.module.css +6 -1
  36. package/dist/Toggle.module.css +6 -1
  37. package/dist/Typography.module.css +47 -0
  38. package/dist/index.d.ts +617 -5
  39. package/dist/index.js +1847 -499
  40. package/dist/tokens/primitives.css +22 -8
  41. package/package.json +1 -1
  42. package/src/components/Accordion/Accordion.module.css +3 -0
  43. package/src/components/Accordion/Accordion.stories.tsx +110 -0
  44. package/src/components/Accordion/Accordion.tsx +53 -0
  45. package/src/components/Accordion/__tests__/Accordion.test.tsx +61 -0
  46. package/src/components/Alert/Alert.module.css +62 -0
  47. package/src/components/Alert/Alert.stories.tsx +96 -0
  48. package/src/components/Alert/Alert.tsx +58 -0
  49. package/src/components/Alert/__tests__/Alert.test.tsx +75 -0
  50. package/src/components/AspectRatio/AspectRatio.module.css +15 -0
  51. package/src/components/AspectRatio/AspectRatio.stories.tsx +78 -0
  52. package/src/components/AspectRatio/AspectRatio.tsx +21 -0
  53. package/src/components/AspectRatio/__tests__/AspectRatio.test.tsx +27 -0
  54. package/src/components/Badge/Badge.module.css +75 -9
  55. package/src/components/Badge/Badge.stories.tsx +109 -1
  56. package/src/components/Badge/Badge.tsx +30 -2
  57. package/src/components/Badge/__tests__/Badge.test.tsx +26 -2
  58. package/src/components/Breadcrumb/Breadcrumb.module.css +49 -0
  59. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +47 -0
  60. package/src/components/Breadcrumb/Breadcrumb.tsx +57 -0
  61. package/src/components/Breadcrumb/__tests__/Breadcrumb.test.tsx +52 -0
  62. package/src/components/Button/Button.module.css +12 -3
  63. package/src/components/Button/Button.stories.tsx +3 -2
  64. package/src/components/Button/Button.tsx +1 -1
  65. package/src/components/Button/__tests__/Button.test.tsx +10 -0
  66. package/src/components/ButtonGroup/ButtonGroup.module.css +29 -0
  67. package/src/components/ButtonGroup/ButtonGroup.stories.tsx +57 -0
  68. package/src/components/ButtonGroup/ButtonGroup.tsx +27 -0
  69. package/src/components/ButtonGroup/__tests__/ButtonGroup.test.tsx +29 -0
  70. package/src/components/Card/Card.module.css +6 -0
  71. package/src/components/Checkbox/Checkbox.module.css +3 -1
  72. package/src/components/Collapsible/Collapsible.module.css +70 -0
  73. package/src/components/Collapsible/Collapsible.stories.tsx +62 -0
  74. package/src/components/Collapsible/Collapsible.tsx +66 -0
  75. package/src/components/Collapsible/__tests__/Collapsible.test.tsx +71 -0
  76. package/src/components/Combobox/Combobox.module.css +122 -0
  77. package/src/components/Combobox/Combobox.stories.tsx +79 -0
  78. package/src/components/Combobox/Combobox.tsx +186 -0
  79. package/src/components/Combobox/__tests__/Combobox.test.tsx +158 -0
  80. package/src/components/Command/Command.module.css +80 -0
  81. package/src/components/Command/Command.stories.tsx +104 -0
  82. package/src/components/Command/Command.tsx +168 -0
  83. package/src/components/Command/__tests__/Command.test.tsx +162 -0
  84. package/src/components/ConfirmDialog/ConfirmDialog.module.css +5 -1
  85. package/src/components/ContextMenu/ContextMenu.stories.tsx +81 -0
  86. package/src/components/ContextMenu/ContextMenu.tsx +100 -0
  87. package/src/components/ContextMenu/__tests__/ContextMenu.test.tsx +87 -0
  88. package/src/components/DataTable/DataTable.module.css +28 -0
  89. package/src/components/DataTable/DataTable.stories.tsx +74 -0
  90. package/src/components/DataTable/DataTable.tsx +154 -0
  91. package/src/components/DataTable/__tests__/DataTable.test.tsx +123 -0
  92. package/src/components/DatePicker/DatePicker.module.css +6 -2
  93. package/src/components/Dialog/Dialog.module.css +3 -0
  94. package/src/components/HoverCard/HoverCard.module.css +3 -0
  95. package/src/components/HoverCard/HoverCard.stories.tsx +66 -0
  96. package/src/components/HoverCard/HoverCard.tsx +111 -0
  97. package/src/components/HoverCard/__tests__/HoverCard.test.tsx +99 -0
  98. package/src/components/Icon/icons/circle-check.tsx +21 -0
  99. package/src/components/Icon/icons/circle-x.tsx +22 -0
  100. package/src/components/Icon/icons/index.ts +8 -0
  101. package/src/components/Input/Input.module.css +6 -1
  102. package/src/components/Item/Item.module.css +70 -0
  103. package/src/components/Item/Item.stories.tsx +62 -0
  104. package/src/components/Item/Item.tsx +54 -0
  105. package/src/components/Item/__tests__/Item.test.tsx +39 -0
  106. package/src/components/Kbd/Kbd.module.css +14 -0
  107. package/src/components/Kbd/Kbd.stories.tsx +32 -0
  108. package/src/components/Kbd/Kbd.tsx +12 -0
  109. package/src/components/Kbd/__tests__/Kbd.test.tsx +14 -0
  110. package/src/components/Label/Label.module.css +11 -0
  111. package/src/components/Label/Label.stories.tsx +40 -0
  112. package/src/components/Label/Label.tsx +24 -0
  113. package/src/components/Label/__tests__/Label.test.tsx +32 -0
  114. package/src/components/Marker/Marker.module.css +33 -0
  115. package/src/components/Marker/Marker.stories.tsx +39 -0
  116. package/src/components/Marker/Marker.tsx +34 -0
  117. package/src/components/Marker/__tests__/Marker.test.tsx +23 -0
  118. package/src/components/Menu/Menu.module.css +8 -1
  119. package/src/components/Menu/Menu.tsx +30 -8
  120. package/src/components/Menubar/Menubar.stories.tsx +52 -0
  121. package/src/components/Menubar/Menubar.tsx +42 -0
  122. package/src/components/Menubar/__tests__/Menubar.test.tsx +45 -0
  123. package/src/components/Message/Message.module.css +99 -0
  124. package/src/components/Message/Message.stories.tsx +79 -0
  125. package/src/components/Message/Message.tsx +41 -0
  126. package/src/components/Message/__tests__/Message.test.tsx +46 -0
  127. package/src/components/MessageScroller/MessageScroller.module.css +54 -0
  128. package/src/components/MessageScroller/MessageScroller.stories.tsx +84 -0
  129. package/src/components/MessageScroller/MessageScroller.tsx +72 -0
  130. package/src/components/MessageScroller/__tests__/MessageScroller.test.tsx +105 -0
  131. package/src/components/NavigationMenu/NavigationMenu.module.css +53 -0
  132. package/src/components/NavigationMenu/NavigationMenu.stories.tsx +58 -0
  133. package/src/components/NavigationMenu/NavigationMenu.tsx +125 -0
  134. package/src/components/NavigationMenu/__tests__/NavigationMenu.test.tsx +93 -0
  135. package/src/components/Pagination/Pagination.module.css +98 -0
  136. package/src/components/Pagination/Pagination.stories.tsx +49 -0
  137. package/src/components/Pagination/Pagination.tsx +87 -0
  138. package/src/components/Pagination/__tests__/Pagination.test.tsx +56 -0
  139. package/src/components/Progress/Progress.module.css +20 -0
  140. package/src/components/Progress/Progress.stories.tsx +40 -0
  141. package/src/components/Progress/Progress.tsx +35 -0
  142. package/src/components/Progress/__tests__/Progress.test.tsx +31 -0
  143. package/src/components/RadioGroup/RadioGroup.module.css +88 -0
  144. package/src/components/RadioGroup/RadioGroup.stories.tsx +69 -0
  145. package/src/components/RadioGroup/RadioGroup.tsx +99 -0
  146. package/src/components/RadioGroup/__tests__/RadioGroup.test.tsx +59 -0
  147. package/src/components/Resizable/Resizable.module.css +70 -0
  148. package/src/components/Resizable/Resizable.stories.tsx +82 -0
  149. package/src/components/Resizable/Resizable.tsx +313 -0
  150. package/src/components/Resizable/__tests__/Resizable.test.tsx +128 -0
  151. package/src/components/ScrollArea/ScrollArea.module.css +20 -0
  152. package/src/components/ScrollArea/ScrollArea.stories.tsx +57 -0
  153. package/src/components/ScrollArea/ScrollArea.tsx +27 -0
  154. package/src/components/ScrollArea/__tests__/ScrollArea.test.tsx +39 -0
  155. package/src/components/Select/Select.module.css +7 -3
  156. package/src/components/Slider/Slider.module.css +95 -0
  157. package/src/components/Slider/Slider.stories.tsx +64 -0
  158. package/src/components/Slider/Slider.tsx +77 -0
  159. package/src/components/Slider/__tests__/Slider.test.tsx +45 -0
  160. package/src/components/Table/Table.module.css +45 -0
  161. package/src/components/Table/Table.stories.tsx +125 -0
  162. package/src/components/Table/Table.tsx +51 -0
  163. package/src/components/Table/__tests__/Table.test.tsx +50 -0
  164. package/src/components/Tabs/Tabs.module.css +6 -1
  165. package/src/components/Toggle/Toggle.module.css +6 -1
  166. package/src/components/Typography/Typography.module.css +47 -0
  167. package/src/components/Typography/Typography.stories.tsx +62 -0
  168. package/src/components/Typography/Typography.tsx +30 -0
  169. package/src/components/Typography/__tests__/Typography.test.tsx +34 -0
  170. package/src/index.ts +78 -1
  171. package/src/stories/DesignSystemOverview.stories.tsx +614 -2
  172. package/src/stories/MobilePatterns.stories.tsx +8 -2
  173. package/src/stories/TokenGallery.stories.tsx +112 -16
  174. package/src/tokens/primitives.css +22 -8
@@ -33,6 +33,44 @@ import { ToastProvider, useToast } from '../components/Toast/Toast';
33
33
  import { Toggle } from '../components/Toggle/Toggle';
34
34
  import { Tooltip } from '../components/Tooltip/Tooltip';
35
35
  import { Checkbox } from '../components/Checkbox/Checkbox';
36
+ import { RadioGroup } from '../components/RadioGroup/RadioGroup';
37
+ import { Slider } from '../components/Slider/Slider';
38
+ import { Progress } from '../components/Progress/Progress';
39
+ import {
40
+ Table,
41
+ TableHead,
42
+ TableBody,
43
+ TableRow,
44
+ TableHeaderCell,
45
+ TableCell,
46
+ } from '../components/Table/Table';
47
+ import { Alert } from '../components/Alert/Alert';
48
+ import { Breadcrumb } from '../components/Breadcrumb/Breadcrumb';
49
+ import { Pagination } from '../components/Pagination/Pagination';
50
+ import { Kbd } from '../components/Kbd/Kbd';
51
+ import { Accordion } from '../components/Accordion/Accordion';
52
+ import { AspectRatio } from '../components/AspectRatio/AspectRatio';
53
+ import { ButtonGroup } from '../components/ButtonGroup/ButtonGroup';
54
+ import { Item } from '../components/Item/Item';
55
+ import { Label } from '../components/Label/Label';
56
+ import { ScrollArea } from '../components/ScrollArea/ScrollArea';
57
+ import { Typography } from '../components/Typography/Typography';
58
+ import { Marker } from '../components/Marker/Marker';
59
+ import { Message } from '../components/Message/Message';
60
+ import { MessageScroller } from '../components/MessageScroller/MessageScroller';
61
+ import { HoverCard } from '../components/HoverCard/HoverCard';
62
+ import { ContextMenu } from '../components/ContextMenu/ContextMenu';
63
+ import { Command } from '../components/Command/Command';
64
+ import {
65
+ ResizableHandle,
66
+ ResizablePanel,
67
+ ResizablePanelGroup,
68
+ } from '../components/Resizable/Resizable';
69
+ import { DataTable } from '../components/DataTable/DataTable';
70
+ import { Combobox } from '../components/Combobox/Combobox';
71
+ import { NavigationMenu } from '../components/NavigationMenu/NavigationMenu';
72
+ import { Menubar } from '../components/Menubar/Menubar';
73
+ import { Collapsible } from '../components/Collapsible/Collapsible';
36
74
  import { DragHandleRow } from '../components/DragHandleRow/DragHandleRow';
37
75
  import { FileDropzone } from '../components/FileDropzone/FileDropzone';
38
76
  import { OverlayHeader } from '../components/OverlayHeader/OverlayHeader';
@@ -466,6 +504,64 @@ function TabsDemo() {
466
504
  );
467
505
  }
468
506
 
507
+ function CommandDemo() {
508
+ const [open, setOpen] = useState(false);
509
+ return (
510
+ <>
511
+ <Button size="sm" onClick={() => setOpen(true)}>
512
+ Open command palette
513
+ </Button>
514
+ <Command
515
+ open={open}
516
+ onClose={() => setOpen(false)}
517
+ aria-label="Command palette"
518
+ items={[
519
+ {
520
+ id: 'new',
521
+ label: 'New conversation',
522
+ group: 'Actions',
523
+ icon: 'plus',
524
+ onSelect: () => {},
525
+ },
526
+ {
527
+ id: 'export',
528
+ label: 'Export chat',
529
+ group: 'Actions',
530
+ icon: 'upload',
531
+ onSelect: () => {},
532
+ },
533
+ {
534
+ id: 'profile',
535
+ label: 'Go to profile',
536
+ group: 'Navigation',
537
+ icon: 'user',
538
+ onSelect: () => {},
539
+ },
540
+ ]}
541
+ />
542
+ </>
543
+ );
544
+ }
545
+
546
+ function ComboboxDemo() {
547
+ const [value, setValue] = useState<string | null>(null);
548
+ return (
549
+ <div style={{ width: 240 }}>
550
+ <Combobox
551
+ options={[
552
+ { value: 'tasks', label: 'sovereign-tasks' },
553
+ { value: 'ledger', label: 'sovereign-ledger' },
554
+ { value: 'shopper', label: 'sovereign-shopper' },
555
+ ]}
556
+ value={value}
557
+ onChange={setValue}
558
+ placeholder="Select a plugin"
559
+ aria-label="Plugin"
560
+ />
561
+ </div>
562
+ );
563
+ }
564
+
469
565
  function DialogDemo() {
470
566
  const [open, setOpen] = useState(false);
471
567
  return (
@@ -1508,7 +1604,7 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1508
1604
  <ComponentCard
1509
1605
  name="Button"
1510
1606
  importLine="import { Button } from '@sovereignfs/ui';"
1511
- usage="Primary interactive control. Four variants: primary, secondary, ghost, destructive. Two sizes: md (default), sm. loading disables the button, sets aria-busy, and shows a spinner. 44px min-height under (pointer: coarse); hover behind (hover: hover)."
1607
+ usage="Primary interactive control. Four variants: primary, secondary, ghost, destructive. Three sizes: lg, md (default), sm. loading disables the button, sets aria-busy, and shows a spinner. 44px min-height under (pointer: coarse); hover behind (hover: hover)."
1512
1608
  >
1513
1609
  <Button>Primary</Button>
1514
1610
  <Button variant="secondary">Secondary</Button>
@@ -1516,6 +1612,7 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1516
1612
  <Button variant="destructive" size="sm">
1517
1613
  Delete
1518
1614
  </Button>
1615
+ <Button size="lg">Large</Button>
1519
1616
  <Button loading>Saving…</Button>
1520
1617
  </ComponentCard>
1521
1618
 
@@ -1523,7 +1620,7 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1523
1620
  <ComponentCard
1524
1621
  name="Badge"
1525
1622
  importLine="import { Badge } from '@sovereignfs/ui';"
1526
- usage="Compact label for roles (role), lifecycle states (status), and type/version tags (mono). RSC-safe."
1623
+ usage="Compact label for roles (role), lifecycle states (status), and type/version tags (mono). sm/md/lg sizes; ALL CAPS by default, or uppercase={false} for title case. RSC-safe."
1527
1624
  >
1528
1625
  <Badge variant="role">Admin</Badge>
1529
1626
  <Badge variant="status" status="active">
@@ -1533,6 +1630,9 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1533
1630
  Invited
1534
1631
  </Badge>
1535
1632
  <Badge variant="mono">v1.2.0</Badge>
1633
+ <Badge variant="role" size="lg" uppercase={false}>
1634
+ Owner
1635
+ </Badge>
1536
1636
  </ComponentCard>
1537
1637
 
1538
1638
  {/* StatusBadge */}
@@ -2032,6 +2132,518 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
2032
2132
  })()}
2033
2133
  </ComponentCard>
2034
2134
 
2135
+ {/* RadioGroup */}
2136
+ <ComponentCard
2137
+ name="RadioGroup"
2138
+ importLine="import { RadioGroup } from '@sovereignfs/ui';"
2139
+ usage="Single-select list of options. Renders real <input type=radio> elements sharing one name, so arrow-key navigation between options is native browser behavior, not hand-rolled."
2140
+ >
2141
+ {(() => {
2142
+ const [size, setSize] = useState('md');
2143
+ return (
2144
+ <RadioGroup
2145
+ items={[
2146
+ { label: 'Small', value: 'sm' },
2147
+ { label: 'Medium', value: 'md' },
2148
+ { label: 'Large', value: 'lg' },
2149
+ ]}
2150
+ value={size}
2151
+ onChange={setSize}
2152
+ aria-label="Size"
2153
+ />
2154
+ );
2155
+ })()}
2156
+ </ComponentCard>
2157
+
2158
+ {/* Slider */}
2159
+ <ComponentCard
2160
+ name="Slider"
2161
+ importLine="import { Slider } from '@sovereignfs/ui';"
2162
+ usage="Single-thumb range input. A native <input type=range> under custom styling — arrow keys, Home/End, and touch-drag all come from the browser."
2163
+ >
2164
+ {(() => {
2165
+ const [value, setValue] = useState(50);
2166
+ return (
2167
+ <div style={{ width: 240 }}>
2168
+ <Slider value={value} onChange={setValue} min={0} max={100} label="Volume" />
2169
+ </div>
2170
+ );
2171
+ })()}
2172
+ </ComponentCard>
2173
+
2174
+ {/* Progress */}
2175
+ <ComponentCard
2176
+ name="Progress"
2177
+ importLine="import { Progress } from '@sovereignfs/ui';"
2178
+ usage="Determinate progress bar. role=progressbar with aria-valuenow/min/max so screen readers announce the current value."
2179
+ >
2180
+ <div style={{ width: 240 }}>
2181
+ <Progress value={65} label="Upload progress" />
2182
+ </div>
2183
+ </ComponentCard>
2184
+
2185
+ {/* Table */}
2186
+ <ComponentCard
2187
+ name="Table"
2188
+ importLine="import { Table, TableHead, TableBody, TableRow, TableHeaderCell, TableCell } from '@sovereignfs/ui';"
2189
+ usage="Thin, styled wrappers around the native table elements. Not a data grid — no sort/filter/virtualization. Scrolls horizontally (masked-edge fade) at any viewport size."
2190
+ >
2191
+ <Table>
2192
+ <TableHead>
2193
+ <TableRow>
2194
+ <TableHeaderCell>Plugin</TableHeaderCell>
2195
+ <TableHeaderCell>Status</TableHeaderCell>
2196
+ </TableRow>
2197
+ </TableHead>
2198
+ <TableBody>
2199
+ <TableRow>
2200
+ <TableCell>sovereign-tasks</TableCell>
2201
+ <TableCell>Active</TableCell>
2202
+ </TableRow>
2203
+ <TableRow>
2204
+ <TableCell>sovereign-shopper</TableCell>
2205
+ <TableCell>Disabled</TableCell>
2206
+ </TableRow>
2207
+ </TableBody>
2208
+ </Table>
2209
+ </ComponentCard>
2210
+
2211
+ {/* Alert */}
2212
+ <ComponentCard
2213
+ name="Alert"
2214
+ importLine="import { Alert } from '@sovereignfs/ui';"
2215
+ usage="Inline, non-dismissible banner. Distinct from Toast (transient) and SystemBanner (instance-wide) — for form-level errors or explaining an empty/blocked state. Leading icon defaults per variant; pass icon to override or icon={false} to suppress."
2216
+ >
2217
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
2218
+ <Alert variant="error" heading="Something went wrong">
2219
+ We couldn’t save your changes. Check your connection and try again.
2220
+ </Alert>
2221
+ <Alert variant="success">Invite sent.</Alert>
2222
+ <Alert variant="neutral">This project has no members yet.</Alert>
2223
+ </div>
2224
+ </ComponentCard>
2225
+
2226
+ {/* Breadcrumb */}
2227
+ <ComponentCard
2228
+ name="Breadcrumb"
2229
+ importLine="import { Breadcrumb } from '@sovereignfs/ui';"
2230
+ usage="Link trail. The last item (no href) renders as plain text with aria-current=page. Pass renderLink to keep navigation client-side inside overlay-shell plugins."
2231
+ >
2232
+ <Breadcrumb
2233
+ items={[
2234
+ { label: 'Console', href: '/console' },
2235
+ { label: 'Plugins', href: '/console/plugins' },
2236
+ { label: 'sovereign-tasks' },
2237
+ ]}
2238
+ />
2239
+ </ComponentCard>
2240
+
2241
+ {/* Pagination */}
2242
+ <ComponentCard
2243
+ name="Pagination"
2244
+ importLine="import { Pagination } from '@sovereignfs/ui';"
2245
+ usage="Page-number / prev-next control. Shows first, last, and pages near the current one, with an ellipsis for gaps once the page count grows."
2246
+ >
2247
+ {(() => {
2248
+ const [page, setPage] = useState(4);
2249
+ return <Pagination page={page} totalPages={12} onChange={setPage} />;
2250
+ })()}
2251
+ </ComponentCard>
2252
+
2253
+ {/* Kbd */}
2254
+ <ComponentCard
2255
+ name="Kbd"
2256
+ importLine="import { Kbd } from '@sovereignfs/ui';"
2257
+ usage="Inline keyboard-key styling. Renders a real <kbd> element."
2258
+ >
2259
+ <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4 }}>
2260
+ <Kbd>Ctrl</Kbd>
2261
+ <span>+</span>
2262
+ <Kbd>K</Kbd>
2263
+ </span>
2264
+ </ComponentCard>
2265
+
2266
+ {/* Collapsible */}
2267
+ <ComponentCard
2268
+ name="Collapsible"
2269
+ importLine="import { Collapsible } from '@sovereignfs/ui';"
2270
+ usage="Single expand/collapse primitive. Independently useful (e.g. a 'show more' toggle), and composed internally by Accordion."
2271
+ >
2272
+ {(() => {
2273
+ const [open, setOpen] = useState(false);
2274
+ return (
2275
+ <Collapsible open={open} onOpenChange={setOpen} trigger="Advanced settings">
2276
+ These settings are rarely needed — change them only if you know what you're
2277
+ doing.
2278
+ </Collapsible>
2279
+ );
2280
+ })()}
2281
+ </ComponentCard>
2282
+
2283
+ {/* Accordion */}
2284
+ <ComponentCard
2285
+ name="Accordion"
2286
+ importLine="import { Accordion } from '@sovereignfs/ui';"
2287
+ usage="One or more Collapsible sections. type=single closes other sections when one opens; type=multiple allows any number open at once."
2288
+ >
2289
+ {(() => {
2290
+ const [openIds, setOpenIds] = useState<string[]>(['plugins']);
2291
+ return (
2292
+ <Accordion
2293
+ items={[
2294
+ {
2295
+ id: 'plugins',
2296
+ trigger: 'What plugins ship by default?',
2297
+ content:
2298
+ 'Only the platform plugins (Console, Launcher, Account) plus Sovereign Tasks.',
2299
+ },
2300
+ {
2301
+ id: 'hosting',
2302
+ trigger: 'Can I self-host this?',
2303
+ content:
2304
+ 'Yes — Sovereign is designed to be self-hosted via Docker Compose.',
2305
+ },
2306
+ ]}
2307
+ type="single"
2308
+ openIds={openIds}
2309
+ onOpenIdsChange={setOpenIds}
2310
+ />
2311
+ );
2312
+ })()}
2313
+ </ComponentCard>
2314
+
2315
+ {/* AspectRatio */}
2316
+ <ComponentCard
2317
+ name="AspectRatio"
2318
+ importLine="import { AspectRatio } from '@sovereignfs/ui';"
2319
+ usage="Constrains content (image, video, embed) to a fixed ratio."
2320
+ >
2321
+ <div style={{ width: 200 }}>
2322
+ <AspectRatio ratio={16 / 9}>
2323
+ <div
2324
+ style={{
2325
+ width: '100%',
2326
+ height: '100%',
2327
+ background: 'var(--sv-color-surface-sunken)',
2328
+ display: 'flex',
2329
+ alignItems: 'center',
2330
+ justifyContent: 'center',
2331
+ color: 'var(--sv-color-text-muted)',
2332
+ fontSize: 'var(--sv-font-size-xs)',
2333
+ }}
2334
+ >
2335
+ 16:9
2336
+ </div>
2337
+ </AspectRatio>
2338
+ </div>
2339
+ </ComponentCard>
2340
+
2341
+ {/* ButtonGroup */}
2342
+ <ComponentCard
2343
+ name="ButtonGroup"
2344
+ importLine="import { ButtonGroup } from '@sovereignfs/ui';"
2345
+ usage="Visually joins adjacent Buttons into one control (shared border, connected corners)."
2346
+ >
2347
+ <ButtonGroup aria-label="View">
2348
+ <Button variant="secondary" size="sm">
2349
+ Day
2350
+ </Button>
2351
+ <Button variant="secondary" size="sm">
2352
+ Week
2353
+ </Button>
2354
+ <Button variant="secondary" size="sm">
2355
+ Month
2356
+ </Button>
2357
+ </ButtonGroup>
2358
+ </ComponentCard>
2359
+
2360
+ {/* Item */}
2361
+ <ComponentCard
2362
+ name="Item"
2363
+ importLine="import { Item } from '@sovereignfs/ui';"
2364
+ usage="Generic row primitive: leading slot, title + optional description, trailing slot. For settings rows and list rows that don't fit Menu's own item shape."
2365
+ >
2366
+ <Item
2367
+ title="Notifications"
2368
+ description="Manage email and push alerts"
2369
+ leading={<Icon name="bell" aria-hidden />}
2370
+ trailing={<Icon name="chevron-right" size="sm" aria-hidden />}
2371
+ onClick={() => {}}
2372
+ />
2373
+ </ComponentCard>
2374
+
2375
+ {/* Label */}
2376
+ <ComponentCard
2377
+ name="Label"
2378
+ importLine="import { Label } from '@sovereignfs/ui';"
2379
+ usage="Standalone accessible form label, independent of FormField. Use FormField when a control needs hint/error text too."
2380
+ >
2381
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
2382
+ <Label htmlFor="label-demo-input">Email address</Label>
2383
+ <input
2384
+ id="label-demo-input"
2385
+ type="email"
2386
+ placeholder="you@example.com"
2387
+ style={{
2388
+ padding: 'var(--sv-space-2) var(--sv-space-3)',
2389
+ border: '1px solid var(--sv-color-border-strong)',
2390
+ borderRadius: 'var(--sv-radius-md)',
2391
+ fontFamily: 'var(--sv-font-family)',
2392
+ fontSize: 'var(--sv-font-size-sm)',
2393
+ }}
2394
+ />
2395
+ </div>
2396
+ </ComponentCard>
2397
+
2398
+ {/* ScrollArea */}
2399
+ <ComponentCard
2400
+ name="ScrollArea"
2401
+ importLine="import { ScrollArea } from '@sovereignfs/ui';"
2402
+ usage="Styled scrollable container — thin, token-colored scrollbar instead of the OS default. Native overflow scrolling underneath."
2403
+ >
2404
+ <ScrollArea maxHeight={120}>
2405
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 8, padding: 4 }}>
2406
+ {Array.from({ length: 10 }, (_, i) => (
2407
+ <div key={i} style={{ fontSize: 13 }}>
2408
+ Row {i + 1}
2409
+ </div>
2410
+ ))}
2411
+ </div>
2412
+ </ScrollArea>
2413
+ </ComponentCard>
2414
+
2415
+ {/* Typography */}
2416
+ <ComponentCard
2417
+ name="Typography"
2418
+ importLine="import { Typography } from '@sovereignfs/ui';"
2419
+ usage="Text bound to the design system's font-size/weight scale. Pass `as` to override the rendered tag while keeping the variant's visual style."
2420
+ >
2421
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
2422
+ <Typography variant="h3">Heading 3</Typography>
2423
+ <Typography variant="body">Body copy for descriptions.</Typography>
2424
+ <Typography variant="caption">Caption — secondary text.</Typography>
2425
+ </div>
2426
+ </ComponentCard>
2427
+
2428
+ {/* Marker */}
2429
+ <ComponentCard
2430
+ name="Marker"
2431
+ importLine="import { Marker } from '@sovereignfs/ui';"
2432
+ usage="Inline citation/reference marker — attributes part of an assistant answer to a source (Sovereign Harness 'source trace', RFC 0040)."
2433
+ >
2434
+ <p style={{ fontSize: 14, margin: 0 }}>
2435
+ Your next task is due Thursday
2436
+ <Marker index={1} label="Source: Tasks" onClick={() => {}} />.
2437
+ </p>
2438
+ </ComponentCard>
2439
+
2440
+ {/* Message */}
2441
+ <ComponentCard
2442
+ name="Message"
2443
+ importLine="import { Message } from '@sovereignfs/ui';"
2444
+ usage="A single chat turn for the Sovereign Harness assistant. role is user/assistant/tool, matching harness_messages (RFC 0040). Content is caller-controlled ReactNode."
2445
+ >
2446
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
2447
+ <Message sender="user">What's on my plate this week?</Message>
2448
+ <Message sender="assistant">You have 3 tasks due this week.</Message>
2449
+ </div>
2450
+ </ComponentCard>
2451
+
2452
+ {/* MessageScroller */}
2453
+ <ComponentCard
2454
+ name="MessageScroller"
2455
+ importLine="import { MessageScroller } from '@sovereignfs/ui';"
2456
+ usage="Auto-scrolling chat container. Scrolls to the newest message while the user is near the bottom; shows a 'New messages' button instead of yanking them down if they've scrolled up."
2457
+ >
2458
+ <div style={{ height: 160, border: '1px solid var(--sv-color-border)' }}>
2459
+ <MessageScroller>
2460
+ <Message sender="user">Hi there</Message>
2461
+ <Message sender="assistant">How can I help?</Message>
2462
+ </MessageScroller>
2463
+ </div>
2464
+ </ComponentCard>
2465
+
2466
+ {/* HoverCard */}
2467
+ <ComponentCard
2468
+ name="HoverCard"
2469
+ importLine="import { HoverCard } from '@sovereignfs/ui';"
2470
+ usage="Hover-triggered popover on desktop, tap-to-toggle on touch. Built on Popover for positioning — adds hover-intent timing and the touch fallback. Opens on keyboard focus too."
2471
+ >
2472
+ <HoverCard
2473
+ aria-label="User preview"
2474
+ trigger={
2475
+ <button
2476
+ type="button"
2477
+ style={{ background: 'none', border: 'none', padding: 0, cursor: 'pointer' }}
2478
+ >
2479
+ <Avatar name="Jane Smith" size="sm" />
2480
+ </button>
2481
+ }
2482
+ >
2483
+ <div style={{ padding: 'var(--sv-space-3)', fontSize: 14 }}>
2484
+ <div style={{ fontWeight: 600 }}>Jane Smith</div>
2485
+ <div style={{ color: 'var(--sv-color-text-muted)' }}>jane@example.com</div>
2486
+ </div>
2487
+ </HoverCard>
2488
+ </ComponentCard>
2489
+
2490
+ {/* ContextMenu */}
2491
+ <ComponentCard
2492
+ name="ContextMenu"
2493
+ importLine="import { ContextMenu } from '@sovereignfs/ui';"
2494
+ usage="Right-click menu on desktop, long-press on touch. Desktop positioning reuses Popover's collision detection anchored to the click point; touch opens the same items in a Drawer."
2495
+ >
2496
+ <ContextMenu
2497
+ aria-label="Row actions"
2498
+ items={[
2499
+ { label: 'Rename', onSelect: () => {} },
2500
+ { label: 'Duplicate', onSelect: () => {} },
2501
+ { type: 'separator' },
2502
+ { label: 'Delete', destructive: true, onSelect: () => {} },
2503
+ ]}
2504
+ >
2505
+ <div
2506
+ style={{
2507
+ padding: 'var(--sv-space-4)',
2508
+ border: '1px dashed var(--sv-color-border-strong)',
2509
+ borderRadius: 'var(--sv-radius-md)',
2510
+ textAlign: 'center',
2511
+ fontSize: 14,
2512
+ color: 'var(--sv-color-text-muted)',
2513
+ }}
2514
+ >
2515
+ Right-click here
2516
+ </div>
2517
+ </ContextMenu>
2518
+ </ComponentCard>
2519
+
2520
+ {/* NavigationMenu */}
2521
+ <ComponentCard
2522
+ name="NavigationMenu"
2523
+ importLine="import { NavigationMenu } from '@sovereignfs/ui';"
2524
+ usage="Top-level nav bar where some items open a flyout panel. Desktop-oriented — a hover-triggered flyout bar has no mobile equivalent."
2525
+ >
2526
+ <NavigationMenu
2527
+ aria-label="Main"
2528
+ items={[
2529
+ { label: 'Home', href: '/home' },
2530
+ {
2531
+ label: 'Products',
2532
+ content: (
2533
+ <div style={{ padding: 'var(--sv-space-3)', fontSize: 14 }}>
2534
+ Tasks · Ledger · Shopper
2535
+ </div>
2536
+ ),
2537
+ },
2538
+ ]}
2539
+ />
2540
+ </ComponentCard>
2541
+
2542
+ {/* Menubar */}
2543
+ <ComponentCard
2544
+ name="Menubar"
2545
+ importLine="import { Menubar } from '@sovereignfs/ui';"
2546
+ usage="Desktop app-style menu bar (File/Edit/View...). A thin composition of NavigationMenu with MenuEntries as each item's flyout content."
2547
+ >
2548
+ <Menubar
2549
+ menus={[
2550
+ {
2551
+ label: 'File',
2552
+ items: [
2553
+ { label: 'New conversation', onSelect: () => {} },
2554
+ { label: 'Export', onSelect: () => {} },
2555
+ ],
2556
+ },
2557
+ {
2558
+ label: 'Edit',
2559
+ items: [
2560
+ { label: 'Undo', onSelect: () => {} },
2561
+ { label: 'Redo', onSelect: () => {} },
2562
+ ],
2563
+ },
2564
+ ]}
2565
+ />
2566
+ </ComponentCard>
2567
+
2568
+ {/* Command */}
2569
+ <ComponentCard
2570
+ name="Command"
2571
+ importLine="import { Command } from '@sovereignfs/ui';"
2572
+ usage="⌘K-style command palette. Opened via Dialog for the modal shell — adds the search input, substring filtering, and arrow-key/Enter selection. Controlled: consumer owns the open state."
2573
+ >
2574
+ <CommandDemo />
2575
+ </ComponentCard>
2576
+
2577
+ {/* Resizable */}
2578
+ <ComponentCard
2579
+ name="Resizable"
2580
+ importLine="import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@sovereignfs/ui';"
2581
+ usage="A row or column of resizable panes. Each handle resizes only its two immediate neighbor panels. Desktop-oriented — panels render at their default sizes on touch."
2582
+ >
2583
+ <div style={{ width: '100%', height: 160 }}>
2584
+ <ResizablePanelGroup direction="horizontal">
2585
+ <ResizablePanel defaultSize={30} minSize={15} maxSize={50}>
2586
+ <div
2587
+ style={{
2588
+ display: 'flex',
2589
+ alignItems: 'center',
2590
+ justifyContent: 'center',
2591
+ height: '100%',
2592
+ fontSize: 14,
2593
+ color: 'var(--sv-color-text-muted)',
2594
+ }}
2595
+ >
2596
+ Sidebar
2597
+ </div>
2598
+ </ResizablePanel>
2599
+ <ResizableHandle aria-label="Resize sidebar" />
2600
+ <ResizablePanel defaultSize={70}>
2601
+ <div
2602
+ style={{
2603
+ display: 'flex',
2604
+ alignItems: 'center',
2605
+ justifyContent: 'center',
2606
+ height: '100%',
2607
+ fontSize: 14,
2608
+ color: 'var(--sv-color-text-muted)',
2609
+ }}
2610
+ >
2611
+ Content
2612
+ </div>
2613
+ </ResizablePanel>
2614
+ </ResizablePanelGroup>
2615
+ </div>
2616
+ </ComponentCard>
2617
+
2618
+ {/* DataTable */}
2619
+ <ComponentCard
2620
+ name="DataTable"
2621
+ importLine="import { DataTable } from '@sovereignfs/ui';"
2622
+ usage="Sortable table built on Table. Column-driven: pass columns + data instead of composing TableRow/TableCell by hand. Clicking a sortable header cycles ascending → descending → unsorted."
2623
+ >
2624
+ <DataTable
2625
+ columns={[
2626
+ { key: 'name', header: 'Plugin', sortable: true },
2627
+ { key: 'installs', header: 'Installs', sortable: true, align: 'end' },
2628
+ ]}
2629
+ data={[
2630
+ { id: 'tasks', name: 'sovereign-tasks', installs: 482 },
2631
+ { id: 'ledger', name: 'sovereign-ledger', installs: 219 },
2632
+ { id: 'shopper', name: 'sovereign-shopper', installs: 37 },
2633
+ ]}
2634
+ getRowKey={(row) => row.id}
2635
+ />
2636
+ </ComponentCard>
2637
+
2638
+ {/* Combobox */}
2639
+ <ComponentCard
2640
+ name="Combobox"
2641
+ importLine="import { Combobox } from '@sovereignfs/ui';"
2642
+ usage="A searchable single-select: Popover on desktop, a bottom-sheet Drawer on mobile. For a short fixed list where search adds no value, use Select's native <select> instead."
2643
+ >
2644
+ <ComboboxDemo />
2645
+ </ComponentCard>
2646
+
2035
2647
  {/* DragHandleRow */}
2036
2648
  <ComponentCard
2037
2649
  name="DragHandleRow"
@@ -1077,8 +1077,14 @@ function MobilePatternsDoc() {
1077
1077
  label="Button (sm) — 32 px visual, but padded to 44 px tap area via inline-flex"
1078
1078
  size={44}
1079
1079
  />
1080
- <TouchTargetDemo label="Button (md) — 44 px height" size={44} />
1081
- <TouchTargetDemo label="Button (lg) — 52 px height" size={52} />
1080
+ <TouchTargetDemo
1081
+ label="Button (md) — 36 px visual, but padded to 44 px tap area via inline-flex"
1082
+ size={44}
1083
+ />
1084
+ <TouchTargetDemo
1085
+ label="Button (lg) — 40 px visual, but padded to 44 px tap area via inline-flex"
1086
+ size={44}
1087
+ />
1082
1088
  <TouchTargetDemo
1083
1089
  label="Avoid — 28 px tap area is too small for confident tapping"
1084
1090
  size={28}