@sovereignfs/ui 0.45.0 → 0.83.1

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 (323) hide show
  1. package/dist/Accordion.module.css +1 -1
  2. package/dist/Alert.module.css +1 -1
  3. package/dist/AppsLauncher.module.css +96 -0
  4. package/dist/Avatar.module.css +1 -1
  5. package/dist/Badge.module.css +30 -7
  6. package/dist/BalanceChip.module.css +1 -1
  7. package/dist/Button.module.css +18 -2
  8. package/dist/Card.module.css +1 -1
  9. package/dist/CardTile.module.css +181 -0
  10. package/dist/CodeTextarea.module.css +1 -1
  11. package/dist/Collapsible.module.css +1 -1
  12. package/dist/ColorPicker.module.css +132 -0
  13. package/dist/Combobox.module.css +2 -2
  14. package/dist/Command.module.css +1 -1
  15. package/dist/ConfirmDialog.module.css +2 -2
  16. package/dist/ConsentPrompt.module.css +21 -0
  17. package/dist/DatePicker.module.css +1 -1
  18. package/dist/Dialog.module.css +130 -21
  19. package/dist/Drawer.module.css +36 -7
  20. package/dist/Header.module.css +62 -0
  21. package/dist/HeaderFooterLayout.module.css +21 -0
  22. package/dist/IconPicker.module.css +1 -1
  23. package/dist/Input.module.css +1 -1
  24. package/dist/Kbd.module.css +1 -1
  25. package/dist/LegalLinks.module.css +24 -0
  26. package/dist/Markdown.module.css +73 -0
  27. package/dist/Menu.module.css +1 -0
  28. package/dist/Message.module.css +1 -1
  29. package/dist/MessageScroller.module.css +12 -1
  30. package/dist/MobileAppsDrawer.module.css +82 -0
  31. package/dist/MobileFooter.module.css +73 -0
  32. package/dist/MobileHeader.module.css +76 -0
  33. package/dist/NavList.module.css +130 -0
  34. package/dist/NavTabs.module.css +1 -1
  35. package/dist/NotificationsPanel.module.css +258 -0
  36. package/dist/OverlayHeader.module.css +1 -1
  37. package/dist/PageContainer.module.css +82 -0
  38. package/dist/PageHeader.module.css +54 -0
  39. package/dist/PageLayout.module.css +51 -0
  40. package/dist/Popover.module.css +1 -1
  41. package/dist/QuantityStepper.module.css +1 -1
  42. package/dist/Resizable.module.css +3 -3
  43. package/dist/RootLayout.module.css +12 -0
  44. package/dist/SegmentedControl.module.css +1 -1
  45. package/dist/Select.module.css +1 -1
  46. package/dist/Sheet.module.css +4 -1
  47. package/dist/SplitPane.module.css +4 -4
  48. package/dist/StatusBadge.module.css +1 -1
  49. package/dist/StepStrip.module.css +53 -0
  50. package/dist/SuggestionInput.module.css +2 -2
  51. package/dist/SwipableMobileCarousel.module.css +64 -0
  52. package/dist/SwipableMobileCarouselDots.module.css +64 -0
  53. package/dist/SwipableMobileCarouselSlideParts.module.css +29 -0
  54. package/dist/SwipeStack.module.css +101 -0
  55. package/dist/SystemBanner.module.css +10 -0
  56. package/dist/Table.module.css +2 -2
  57. package/dist/Tabs.module.css +1 -1
  58. package/dist/TagInput.module.css +2 -2
  59. package/dist/Textarea.module.css +9 -1
  60. package/dist/ThreeColumnLayout.module.css +35 -0
  61. package/dist/Toast.module.css +62 -3
  62. package/dist/Tooltip.module.css +1 -1
  63. package/dist/UserMenu.module.css +90 -0
  64. package/dist/index.d.ts +1680 -30
  65. package/dist/index.js +4618 -1443
  66. package/dist/tokens/primitives.css +36 -0
  67. package/dist/tokens/semantic.css +32 -0
  68. package/dist/tokens/theme-presets.css +33 -0
  69. package/package.json +32 -12
  70. package/src/__tests__/package-json.test.ts +8 -0
  71. package/src/components/Accordion/Accordion.module.css +1 -1
  72. package/src/components/Alert/Alert.module.css +1 -1
  73. package/src/components/AppsLauncher/AppsLauncher.module.css +96 -0
  74. package/src/components/AppsLauncher/AppsLauncher.stories.tsx +58 -0
  75. package/src/components/AppsLauncher/AppsLauncher.tsx +117 -0
  76. package/src/components/AppsLauncher/__tests__/AppsLauncher.test.tsx +52 -0
  77. package/src/components/Avatar/Avatar.module.css +1 -1
  78. package/src/components/Badge/Badge.module.css +30 -7
  79. package/src/components/Badge/Badge.stories.tsx +16 -4
  80. package/src/components/Badge/Badge.tsx +8 -11
  81. package/src/components/Badge/__tests__/Badge.test.tsx +4 -2
  82. package/src/components/BalanceChip/BalanceChip.module.css +1 -1
  83. package/src/components/Button/Button.module.css +18 -2
  84. package/src/components/Card/Card.module.css +1 -1
  85. package/src/components/CardTile/CardTile.module.css +181 -0
  86. package/src/components/CardTile/CardTile.stories.tsx +86 -0
  87. package/src/components/CardTile/CardTile.tsx +128 -0
  88. package/src/components/CardTile/__tests__/CardTile.test.tsx +56 -0
  89. package/src/components/CodeTextarea/CodeTextarea.module.css +1 -1
  90. package/src/components/Collapsible/Collapsible.module.css +1 -1
  91. package/src/components/ColorPicker/ColorPicker.module.css +132 -0
  92. package/src/components/ColorPicker/ColorPicker.stories.tsx +80 -0
  93. package/src/components/ColorPicker/ColorPicker.tsx +157 -0
  94. package/src/components/ColorPicker/__tests__/ColorPicker.test.tsx +117 -0
  95. package/src/components/Combobox/Combobox.module.css +2 -2
  96. package/src/components/Command/Command.module.css +1 -1
  97. package/src/components/ConfirmDialog/ConfirmDialog.module.css +2 -2
  98. package/src/components/ConfirmDialog/ConfirmDialog.tsx +28 -2
  99. package/src/components/ConfirmDialog/__tests__/ConfirmDialog.test.tsx +71 -3
  100. package/src/components/ConsentPrompt/ConsentPrompt.module.css +21 -0
  101. package/src/components/ConsentPrompt/ConsentPrompt.stories.tsx +31 -0
  102. package/src/components/ConsentPrompt/ConsentPrompt.tsx +66 -0
  103. package/src/components/ContextMenu/ContextMenu.tsx +1 -1
  104. package/src/components/DatePicker/DatePicker.module.css +1 -1
  105. package/src/components/DeviceOnlyGate/DeviceOnlyGate.tsx +58 -0
  106. package/src/components/DeviceOnlyGate/__tests__/DeviceOnlyGate.test.tsx +45 -0
  107. package/src/components/DeviceStorageKeyGate/DeviceStorageKeyGate.tsx +100 -0
  108. package/src/components/DeviceStorageKeyGate/__tests__/DeviceStorageKeyGate.test.tsx +119 -0
  109. package/src/components/Dialog/Dialog.module.css +130 -21
  110. package/src/components/Dialog/Dialog.stories.tsx +179 -2
  111. package/src/components/Dialog/Dialog.tsx +137 -94
  112. package/src/components/Dialog/__tests__/Dialog.test.tsx +190 -9
  113. package/src/components/DragHandleRow/DragHandleRow.tsx +2 -21
  114. package/src/components/Drawer/Drawer.module.css +36 -7
  115. package/src/components/Drawer/Drawer.stories.tsx +44 -0
  116. package/src/components/Drawer/Drawer.tsx +27 -66
  117. package/src/components/Drawer/__tests__/Drawer.test.tsx +55 -6
  118. package/src/components/GripIcon/GripIcon.tsx +27 -0
  119. package/src/components/Header/Header.module.css +62 -0
  120. package/src/components/Header/Header.stories.tsx +452 -0
  121. package/src/components/Header/Header.tsx +102 -0
  122. package/src/components/Header/__tests__/Header.test.tsx +104 -0
  123. package/src/components/HeaderFooterLayout/HeaderFooterLayout.module.css +21 -0
  124. package/src/components/HeaderFooterLayout/HeaderFooterLayout.stories.tsx +111 -0
  125. package/src/components/HeaderFooterLayout/HeaderFooterLayout.tsx +54 -0
  126. package/src/components/HeaderFooterLayout/__tests__/HeaderFooterLayout.test.tsx +58 -0
  127. package/src/components/Icon/icons/arrow-left-right.tsx +23 -0
  128. package/src/components/Icon/icons/bold.tsx +20 -0
  129. package/src/components/Icon/icons/calendar.tsx +4 -4
  130. package/src/components/Icon/icons/carrot.tsx +3 -3
  131. package/src/components/Icon/icons/circle-chevron-left.tsx +21 -0
  132. package/src/components/Icon/icons/code.tsx +21 -0
  133. package/src/components/Icon/icons/copy.tsx +21 -0
  134. package/src/components/Icon/icons/credit-card.tsx +21 -0
  135. package/src/components/Icon/icons/database.tsx +22 -0
  136. package/src/components/Icon/icons/download.tsx +22 -0
  137. package/src/components/Icon/icons/file-text.tsx +24 -0
  138. package/src/components/Icon/icons/folder-closed.tsx +21 -0
  139. package/src/components/Icon/icons/folder-open.tsx +20 -0
  140. package/src/components/Icon/icons/folder.tsx +20 -0
  141. package/src/components/Icon/icons/folders.tsx +21 -0
  142. package/src/components/Icon/icons/hat-glasses.tsx +24 -0
  143. package/src/components/Icon/icons/history.tsx +22 -0
  144. package/src/components/Icon/icons/inbox.tsx +21 -0
  145. package/src/components/Icon/icons/index.ts +169 -1
  146. package/src/components/Icon/icons/italic.tsx +22 -0
  147. package/src/components/Icon/icons/layers.tsx +22 -0
  148. package/src/components/Icon/icons/layout-dashboard.tsx +23 -0
  149. package/src/components/Icon/icons/layout-grid.tsx +23 -0
  150. package/src/components/Icon/icons/link.tsx +21 -0
  151. package/src/components/Icon/icons/list-ordered.tsx +25 -0
  152. package/src/components/Icon/icons/list.tsx +25 -0
  153. package/src/components/Icon/icons/luggage.tsx +24 -0
  154. package/src/components/Icon/icons/map-pin.tsx +21 -0
  155. package/src/components/Icon/icons/menu.tsx +22 -0
  156. package/src/components/Icon/icons/minus.tsx +20 -0
  157. package/src/components/Icon/icons/paint-bucket.tsx +23 -0
  158. package/src/components/Icon/icons/panel-left.tsx +21 -0
  159. package/src/components/Icon/icons/pin.tsx +21 -0
  160. package/src/components/Icon/icons/rectangle-ellipsis.tsx +23 -0
  161. package/src/components/Icon/icons/refresh-cw.tsx +23 -0
  162. package/src/components/Icon/icons/rotate-cw.tsx +21 -0
  163. package/src/components/Icon/icons/route.tsx +22 -0
  164. package/src/components/Icon/icons/send.tsx +21 -0
  165. package/src/components/Icon/icons/share-2.tsx +24 -0
  166. package/src/components/Icon/icons/sheet.tsx +24 -0
  167. package/src/components/Icon/icons/smartphone.tsx +21 -0
  168. package/src/components/Icon/icons/square-kanban.tsx +23 -0
  169. package/src/components/Icon/icons/table.tsx +23 -0
  170. package/src/components/Icon/icons/user-round-plus.tsx +23 -0
  171. package/src/components/Icon/icons/users.tsx +23 -0
  172. package/src/components/IconPicker/IconPicker.module.css +1 -1
  173. package/src/components/Input/Input.module.css +1 -1
  174. package/src/components/Input/Input.tsx +8 -2
  175. package/src/components/Kbd/Kbd.module.css +1 -1
  176. package/src/components/LegalLinks/LegalLinks.module.css +24 -0
  177. package/src/components/LegalLinks/LegalLinks.stories.tsx +22 -0
  178. package/src/components/LegalLinks/LegalLinks.tsx +40 -0
  179. package/src/components/Markdown/Markdown.module.css +73 -0
  180. package/src/components/Markdown/Markdown.stories.tsx +40 -0
  181. package/src/components/Markdown/Markdown.tsx +305 -0
  182. package/src/components/Markdown/__tests__/Markdown.test.tsx +144 -0
  183. package/src/components/Menu/Menu.module.css +1 -0
  184. package/src/components/Menu/Menu.tsx +72 -22
  185. package/src/components/Menu/__tests__/Menu.test.tsx +30 -0
  186. package/src/components/Menubar/Menubar.tsx +1 -1
  187. package/src/components/Message/Message.module.css +1 -1
  188. package/src/components/MessageScroller/MessageScroller.module.css +12 -1
  189. package/src/components/MessageScroller/MessageScroller.tsx +12 -0
  190. package/src/components/MobileAppsDrawer/MobileAppsDrawer.module.css +82 -0
  191. package/src/components/MobileAppsDrawer/MobileAppsDrawer.tsx +78 -0
  192. package/src/components/MobileAppsDrawer/__tests__/MobileAppsDrawer.test.tsx +80 -0
  193. package/src/components/MobileFooter/MobileFooter.module.css +73 -0
  194. package/src/components/MobileFooter/MobileFooter.stories.tsx +47 -0
  195. package/src/components/MobileFooter/MobileFooter.tsx +122 -0
  196. package/src/components/MobileFooter/__tests__/MobileFooter.test.tsx +129 -0
  197. package/src/components/MobileHeader/MobileHeader.module.css +76 -0
  198. package/src/components/MobileHeader/MobileHeader.stories.tsx +57 -0
  199. package/src/components/MobileHeader/MobileHeader.tsx +90 -0
  200. package/src/components/MobileHeader/__tests__/MobileHeader.test.tsx +113 -0
  201. package/src/components/NavList/NavList.module.css +130 -0
  202. package/src/components/NavList/NavList.tsx +136 -0
  203. package/src/components/NavList/__tests__/NavList.test.tsx +109 -0
  204. package/src/components/NavTabs/NavTabs.module.css +1 -1
  205. package/src/components/NavTabs/NavTabs.tsx +39 -11
  206. package/src/components/NotificationsPanel/NotificationsPanel.module.css +258 -0
  207. package/src/components/NotificationsPanel/NotificationsPanel.stories.tsx +61 -0
  208. package/src/components/NotificationsPanel/NotificationsPanel.tsx +187 -0
  209. package/src/components/NotificationsPanel/__tests__/NotificationsPanel.test.tsx +78 -0
  210. package/src/components/OfflineGate/OfflineGate.tsx +44 -0
  211. package/src/components/OfflineGate/__tests__/OfflineGate.test.tsx +66 -0
  212. package/src/components/OverlayHeader/OverlayHeader.module.css +1 -1
  213. package/src/components/OverlayHeader/OverlayHeader.tsx +19 -4
  214. package/src/components/OverlayHeader/__tests__/OverlayHeader.test.tsx +18 -0
  215. package/src/components/PageContainer/PageContainer.module.css +82 -0
  216. package/src/components/PageContainer/PageContainer.tsx +64 -0
  217. package/src/components/PageContainer/__tests__/PageContainer.test.tsx +70 -0
  218. package/src/components/PageHeader/PageHeader.module.css +54 -0
  219. package/src/components/PageHeader/PageHeader.tsx +157 -2
  220. package/src/components/PageHeader/__tests__/PageHeader.test.tsx +103 -0
  221. package/src/components/PageLayout/PageLayout.module.css +51 -0
  222. package/src/components/PageLayout/PageLayout.stories.tsx +73 -0
  223. package/src/components/PageLayout/PageLayout.tsx +51 -0
  224. package/src/components/PageLayout/__tests__/PageLayout.test.tsx +34 -0
  225. package/src/components/Popover/Popover.module.css +1 -1
  226. package/src/components/Popover/Popover.tsx +86 -4
  227. package/src/components/Popover/__tests__/Popover.test.tsx +165 -0
  228. package/src/components/QuantityStepper/QuantityStepper.module.css +1 -1
  229. package/src/components/Resizable/Resizable.module.css +3 -3
  230. package/src/components/ResponsiveSurface/ResponsiveSurface.tsx +24 -0
  231. package/src/components/ResponsiveSurface/__tests__/ResponsiveSurface.test.tsx +73 -0
  232. package/src/components/RootLayout/RootLayout.module.css +12 -0
  233. package/src/components/RootLayout/RootLayout.stories.tsx +125 -0
  234. package/src/components/RootLayout/RootLayout.tsx +185 -0
  235. package/src/components/RootLayout/__tests__/RootLayout.test.tsx +161 -0
  236. package/src/components/SegmentedControl/SegmentedControl.module.css +1 -1
  237. package/src/components/Select/Select.module.css +1 -1
  238. package/src/components/Sheet/Sheet.module.css +4 -1
  239. package/src/components/Sheet/Sheet.tsx +14 -56
  240. package/src/components/SplitPane/SplitPane.module.css +4 -4
  241. package/src/components/StatusBadge/StatusBadge.module.css +1 -1
  242. package/src/components/StepStrip/StepStrip.module.css +53 -0
  243. package/src/components/StepStrip/StepStrip.tsx +74 -0
  244. package/src/components/SuggestionInput/SuggestionInput.module.css +2 -2
  245. package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.module.css +64 -0
  246. package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.tsx +243 -0
  247. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlide.tsx +35 -0
  248. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideBody.tsx +42 -0
  249. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideFooter.tsx +18 -0
  250. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideHeader.tsx +21 -0
  251. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideParts.module.css +29 -0
  252. package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarousel.test.tsx +214 -0
  253. package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarouselDevGuard.test.tsx +61 -0
  254. package/src/components/SwipableMobileCarousel/context.ts +13 -0
  255. package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.module.css +64 -0
  256. package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.tsx +73 -0
  257. package/src/components/SwipableMobileCarouselDots/__tests__/SwipableMobileCarouselDots.test.tsx +63 -0
  258. package/src/components/SwipeStack/SwipeStack.module.css +101 -0
  259. package/src/components/SwipeStack/SwipeStack.tsx +199 -0
  260. package/src/components/SwipeStack/SwipeStackCard.tsx +22 -0
  261. package/src/components/SwipeStack/__tests__/SwipeStack.test.tsx +169 -0
  262. package/src/components/SystemBanner/SystemBanner.module.css +10 -0
  263. package/src/components/Table/Table.module.css +2 -2
  264. package/src/components/Tabs/Tabs.module.css +1 -1
  265. package/src/components/TagInput/TagInput.module.css +2 -2
  266. package/src/components/Textarea/Textarea.module.css +9 -1
  267. package/src/components/Textarea/Textarea.tsx +28 -5
  268. package/src/components/Textarea/__tests__/Textarea.test.tsx +23 -1
  269. package/src/components/ThreeColumnLayout/ThreeColumnLayout.module.css +35 -0
  270. package/src/components/ThreeColumnLayout/ThreeColumnLayout.stories.tsx +152 -0
  271. package/src/components/ThreeColumnLayout/ThreeColumnLayout.tsx +87 -0
  272. package/src/components/ThreeColumnLayout/__tests__/ThreeColumnLayout.test.tsx +78 -0
  273. package/src/components/Toast/Toast.module.css +62 -3
  274. package/src/components/Toast/Toast.stories.tsx +1 -1
  275. package/src/components/Tooltip/Tooltip.module.css +1 -1
  276. package/src/components/UserMenu/UserMenu.module.css +90 -0
  277. package/src/components/UserMenu/UserMenu.stories.tsx +49 -0
  278. package/src/components/UserMenu/UserMenu.tsx +132 -0
  279. package/src/components/UserMenu/__tests__/UserMenu.test.tsx +77 -0
  280. package/src/hooks/__tests__/useCarouselRouteSync.test.tsx +126 -0
  281. package/src/hooks/__tests__/useIsOffline.test.tsx +53 -0
  282. package/src/hooks/__tests__/useOfflineTileState.test.tsx +69 -0
  283. package/src/hooks/__tests__/usePublishShellChromeHeight.test.tsx +118 -0
  284. package/src/hooks/__tests__/useReorderSensors.test.ts +26 -0
  285. package/src/hooks/__tests__/useResponsiveLayout.test.tsx +87 -0
  286. package/src/hooks/__tests__/useSnapCarousel.test.tsx +175 -0
  287. package/src/hooks/__tests__/useSwipeReveal.test.tsx +110 -0
  288. package/src/hooks/__tests__/useSwipeStack.test.tsx +155 -0
  289. package/src/hooks/index.ts +27 -0
  290. package/src/hooks/useCarouselRouteSync.ts +92 -0
  291. package/src/hooks/useIsOffline.ts +41 -0
  292. package/src/hooks/useOfflineTileState.ts +45 -0
  293. package/src/hooks/usePublishShellChromeHeight.ts +96 -0
  294. package/src/hooks/useReorderSensors.ts +88 -0
  295. package/src/hooks/useResponsiveLayout.ts +36 -0
  296. package/src/hooks/useSnapCarousel.ts +284 -0
  297. package/src/hooks/useSwipeReveal.ts +107 -0
  298. package/src/hooks/useSwipeStack.ts +235 -0
  299. package/src/index.ts +124 -3
  300. package/src/overlay-shell.ts +159 -0
  301. package/src/stories/DesignSystemOverview.stories.tsx +933 -6
  302. package/src/stories/DeviceOnlyGate.stories.tsx +35 -0
  303. package/src/stories/DeviceStorageKeyGate.stories.tsx +57 -0
  304. package/src/stories/GripIcon.stories.tsx +29 -0
  305. package/src/stories/InteractionHooks.stories.tsx +483 -0
  306. package/src/stories/LayoutSystem.stories.tsx +639 -0
  307. package/src/stories/MenuEntries.stories.tsx +86 -0
  308. package/src/stories/MobilePatterns.stories.tsx +247 -2
  309. package/src/stories/NavList.stories.tsx +147 -0
  310. package/src/stories/NavTabs.stories.tsx +18 -0
  311. package/src/stories/OfflineGate.stories.tsx +42 -0
  312. package/src/stories/PageContainer.stories.tsx +82 -0
  313. package/src/stories/PageHeader.stories.tsx +65 -1
  314. package/src/stories/ResponsiveSurface.stories.tsx +47 -0
  315. package/src/stories/StepStrip.stories.tsx +76 -0
  316. package/src/stories/SwipableMobileCarousel.stories.tsx +362 -0
  317. package/src/stories/SwipeStack.stories.tsx +200 -0
  318. package/src/stories/Textarea.stories.tsx +12 -0
  319. package/src/stories/TokenGallery.stories.tsx +115 -0
  320. package/src/tokens/primitives.css +36 -0
  321. package/src/tokens/semantic.css +32 -0
  322. package/src/tokens/theme-presets.css +33 -0
  323. package/src/tokens/theme-presets.ts +72 -0
@@ -5,13 +5,18 @@ import { Avatar } from '../components/Avatar/Avatar';
5
5
  import { Badge } from '../components/Badge/Badge';
6
6
  import { Button } from '../components/Button/Button';
7
7
  import { Card } from '../components/Card/Card';
8
+ import { CardTile, CardTileGrid, NewCardTile } from '../components/CardTile/CardTile';
8
9
  import { Dialog } from '../components/Dialog/Dialog';
9
10
  import { Drawer } from '../components/Drawer/Drawer';
10
11
  import { EmptyState } from '../components/EmptyState/EmptyState';
12
+ import { OfflineGate } from '../components/OfflineGate/OfflineGate';
13
+ import { DeviceOnlyGate } from '../components/DeviceOnlyGate/DeviceOnlyGate';
14
+ import { DeviceStorageKeyGate } from '../components/DeviceStorageKeyGate/DeviceStorageKeyGate';
11
15
  import { FormField } from '../components/FormField/FormField';
12
16
  import { Icon } from '../components/Icon/Icon';
13
17
  import type { IconName } from '../components/Icon/Icon';
14
18
  import { IconPicker } from '../components/IconPicker/IconPicker';
19
+ import { ColorPicker } from '../components/ColorPicker/ColorPicker';
15
20
  import { QuantityStepper } from '../components/QuantityStepper/QuantityStepper';
16
21
  import { CheckableListRow } from '../components/CheckableListRow/CheckableListRow';
17
22
  import { Input } from '../components/Input/Input';
@@ -19,11 +24,32 @@ import { CodeTextarea } from '../components/CodeTextarea/CodeTextarea';
19
24
  import { Textarea } from '../components/Textarea/Textarea';
20
25
  import { StatusBadge } from '../components/StatusBadge/StatusBadge';
21
26
  import { SplitPane } from '../components/SplitPane/SplitPane';
27
+ import { ThreeColumnLayout } from '../components/ThreeColumnLayout/ThreeColumnLayout';
28
+ import { HeaderFooterLayout } from '../components/HeaderFooterLayout/HeaderFooterLayout';
29
+ import { RootLayout } from '../components/RootLayout/RootLayout';
30
+ import { PageLayout } from '../components/PageLayout/PageLayout';
22
31
  import { TagInput } from '../components/TagInput/TagInput';
23
32
  import { SuggestionInput } from '../components/SuggestionInput/SuggestionInput';
33
+ import { AppsLauncher } from '../components/AppsLauncher/AppsLauncher';
34
+ import { Header } from '../components/Header/Header';
35
+ import { MobileAppsDrawer } from '../components/MobileAppsDrawer/MobileAppsDrawer';
36
+ import { MobileFooter } from '../components/MobileFooter/MobileFooter';
37
+ import { MobileHeader } from '../components/MobileHeader/MobileHeader';
38
+ import { NavList } from '../components/NavList/NavList';
24
39
  import { NavTabs } from '../components/NavTabs/NavTabs';
40
+ import { NotificationsPanel } from '../components/NotificationsPanel/NotificationsPanel';
25
41
  import { PageHeader } from '../components/PageHeader/PageHeader';
42
+ import { PageContainer } from '../components/PageContainer/PageContainer';
26
43
  import { Popover } from '../components/Popover/Popover';
44
+ import { UserMenu } from '../components/UserMenu/UserMenu';
45
+ import { ResponsiveSurface } from '../components/ResponsiveSurface/ResponsiveSurface';
46
+ import { SwipableMobileCarousel } from '../components/SwipableMobileCarousel/SwipableMobileCarousel';
47
+ import { SwipableMobileCarouselSlide } from '../components/SwipableMobileCarousel/SwipableMobileCarouselSlide';
48
+ import { SwipableMobileCarouselSlideHeader } from '../components/SwipableMobileCarousel/SwipableMobileCarouselSlideHeader';
49
+ import { SwipableMobileCarouselSlideBody } from '../components/SwipableMobileCarousel/SwipableMobileCarouselSlideBody';
50
+ import { SwipableMobileCarouselDots } from '../components/SwipableMobileCarouselDots/SwipableMobileCarouselDots';
51
+ import { SwipeStack } from '../components/SwipeStack/SwipeStack';
52
+ import { SwipeStackCard } from '../components/SwipeStack/SwipeStackCard';
27
53
  import { SegmentedControl } from '../components/SegmentedControl/SegmentedControl';
28
54
  import { Select } from '../components/Select/Select';
29
55
  import { Spinner } from '../components/Spinner/Spinner';
@@ -46,6 +72,9 @@ import {
46
72
  } from '../components/Table/Table';
47
73
  import { Alert } from '../components/Alert/Alert';
48
74
  import { Breadcrumb } from '../components/Breadcrumb/Breadcrumb';
75
+ import { LegalLinks } from '../components/LegalLinks/LegalLinks';
76
+ import { ConsentPrompt } from '../components/ConsentPrompt/ConsentPrompt';
77
+ import { Markdown } from '../components/Markdown/Markdown';
49
78
  import { Pagination } from '../components/Pagination/Pagination';
50
79
  import { Kbd } from '../components/Kbd/Kbd';
51
80
  import { Accordion } from '../components/Accordion/Accordion';
@@ -72,11 +101,13 @@ import { NavigationMenu } from '../components/NavigationMenu/NavigationMenu';
72
101
  import { Menubar } from '../components/Menubar/Menubar';
73
102
  import { Collapsible } from '../components/Collapsible/Collapsible';
74
103
  import { DragHandleRow } from '../components/DragHandleRow/DragHandleRow';
104
+ import { GripIcon } from '../components/GripIcon/GripIcon';
105
+ import { StepStrip, type StepStripItem } from '../components/StepStrip/StepStrip';
75
106
  import { FileDropzone } from '../components/FileDropzone/FileDropzone';
76
107
  import { OverlayHeader } from '../components/OverlayHeader/OverlayHeader';
77
108
  import { Sheet } from '../components/Sheet/Sheet';
78
109
  import { ConfirmDialog } from '../components/ConfirmDialog/ConfirmDialog';
79
- import { Menu } from '../components/Menu/Menu';
110
+ import { Menu, MenuEntries } from '../components/Menu/Menu';
80
111
  import { Calendar } from '../components/Calendar/Calendar';
81
112
  import { DatePicker } from '../components/DatePicker/DatePicker';
82
113
  import { CurrencyInput } from '../components/CurrencyInput/CurrencyInput';
@@ -236,6 +267,8 @@ const COLOR_GROUPS: Array<{
236
267
  { token: '--sv-color-text-on-accent', name: 'text-on-accent' },
237
268
  { token: '--sv-color-text-on-error', name: 'text-on-error' },
238
269
  { token: '--sv-color-text-on-success', name: 'text-on-success' },
270
+ { token: '--sv-color-text-on-light-fill', name: 'text-on-light-fill' },
271
+ { token: '--sv-color-text-on-dark-fill', name: 'text-on-dark-fill' },
239
272
  ],
240
273
  },
241
274
  {
@@ -246,6 +279,12 @@ const COLOR_GROUPS: Array<{
246
279
  { token: '--sv-color-border-strong', name: 'border-strong' },
247
280
  ],
248
281
  },
282
+ {
283
+ label: 'Shadow',
284
+ usage:
285
+ 'High-contrast shadow color for theme presets with hard offset shadows (RFC 0094). Unused by the default theme.',
286
+ tokens: [{ token: '--sv-color-shadow-strong', name: 'shadow-strong' }],
287
+ },
249
288
  {
250
289
  label: 'Accent',
251
290
  usage:
@@ -375,6 +414,7 @@ function ColorGroup({
375
414
  // ---------------------------------------------------------------------------
376
415
 
377
416
  const TYPE_SCALE: Array<{ token: string; label: string; note: string }> = [
417
+ { token: '--sv-font-size-3xl', label: '3xl — 32px', note: 'Document title style' },
378
418
  { token: '--sv-font-size-2xl', label: '2xl — 24px', note: 'Page titles' },
379
419
  { token: '--sv-font-size-xl', label: 'xl — 20px', note: 'Section headings' },
380
420
  { token: '--sv-font-size-lg', label: 'lg — 18px', note: 'Sub-headings' },
@@ -383,6 +423,11 @@ const TYPE_SCALE: Array<{ token: string; label: string; note: string }> = [
383
423
  { token: '--sv-font-size-caption', label: 'caption — 13px', note: 'Secondary / supporting copy' },
384
424
  { token: '--sv-font-size-xs', label: 'xs — 12px', note: 'Mono identifiers, badges' },
385
425
  { token: '--sv-font-size-label', label: 'label — 11px', note: 'All-caps section labels' },
426
+ {
427
+ token: '--sv-font-size-2xs',
428
+ label: '2xs — 10px',
429
+ note: 'Badge size="xs" — smallest badge tier',
430
+ },
386
431
  ];
387
432
 
388
433
  // ---------------------------------------------------------------------------
@@ -631,6 +676,28 @@ function DrawerDemo() {
631
676
  );
632
677
  }
633
678
 
679
+ function MobileAppsDrawerDemo() {
680
+ const [open, setOpen] = useState(false);
681
+ const items = [
682
+ { key: 'home', icon: <Icon name="house" size="lg" aria-hidden />, label: 'Home' },
683
+ { key: 'notes', icon: <Icon name="file" size="lg" aria-hidden />, label: 'Notes' },
684
+ { key: 'gallery', icon: <Icon name="grid-2x2" size="lg" aria-hidden />, label: 'Gallery' },
685
+ ];
686
+ return (
687
+ <>
688
+ <Button size="sm" variant="secondary" onClick={() => setOpen(true)}>
689
+ Open drawer
690
+ </Button>
691
+ <MobileAppsDrawer
692
+ open={open}
693
+ onClose={() => setOpen(false)}
694
+ aria-label="Sections"
695
+ items={items.map((item) => ({ ...item, onClick: () => setOpen(false) }))}
696
+ />
697
+ </>
698
+ );
699
+ }
700
+
634
701
  function PopoverDemo() {
635
702
  const [open, setOpen] = useState(false);
636
703
  return (
@@ -795,6 +862,24 @@ function MenuDemo() {
795
862
  );
796
863
  }
797
864
 
865
+ function MenuEntriesDemo() {
866
+ return (
867
+ <div style={{ width: '100%', maxWidth: 240 }}>
868
+ <MenuEntries
869
+ items={[
870
+ { type: 'label', label: 'Sort by' },
871
+ { label: 'Manual', checked: true },
872
+ { label: 'Title', checked: false },
873
+ { type: 'separator' },
874
+ { label: 'Rename', icon: 'pencil' },
875
+ { label: 'Delete', icon: 'trash-2', destructive: true },
876
+ ]}
877
+ onSelect={() => {}}
878
+ />
879
+ </div>
880
+ );
881
+ }
882
+
798
883
  function CalendarDemo() {
799
884
  const [value, setValue] = useState<Date | null>(new Date());
800
885
  return (
@@ -921,6 +1006,187 @@ function FileDropzoneDemo() {
921
1006
  );
922
1007
  }
923
1008
 
1009
+ interface StepStripDemoStop extends StepStripItem {
1010
+ label: string;
1011
+ sublabel: string;
1012
+ }
1013
+
1014
+ const STEP_STRIP_DEMO_STOPS: StepStripDemoStop[] = [
1015
+ { id: 'lisbon', label: 'Lisbon', sublabel: 'Aug 26 – 28' },
1016
+ { id: 'porto', label: 'Porto', sublabel: 'Aug 28 – 30' },
1017
+ ];
1018
+
1019
+ function StepStripDemo() {
1020
+ const [activeId, setActiveId] = useState('lisbon');
1021
+ return (
1022
+ <StepStrip
1023
+ items={STEP_STRIP_DEMO_STOPS}
1024
+ activeId={activeId}
1025
+ aria-label="Trip stops"
1026
+ onAdd={() => undefined}
1027
+ addLabel="Add a stop"
1028
+ renderItem={(stop, { isActive }) => (
1029
+ <button
1030
+ type="button"
1031
+ onClick={() => setActiveId(stop.id)}
1032
+ style={{
1033
+ boxSizing: 'border-box',
1034
+ minWidth: '150px',
1035
+ padding: 'var(--sv-space-3) var(--sv-space-4)',
1036
+ borderRadius: 'var(--sv-radius-lg)',
1037
+ border: `1px solid ${isActive ? 'var(--sv-color-text-primary)' : 'var(--sv-color-border)'}`,
1038
+ background: isActive
1039
+ ? 'var(--sv-color-text-primary)'
1040
+ : 'var(--sv-color-surface-raised)',
1041
+ color: isActive ? 'var(--sv-color-surface)' : 'var(--sv-color-text-primary)',
1042
+ cursor: 'pointer',
1043
+ textAlign: 'left',
1044
+ }}
1045
+ >
1046
+ <div style={{ fontWeight: 600, fontSize: 'var(--sv-font-size-sm)' }}>{stop.label}</div>
1047
+ <div style={{ fontSize: 'var(--sv-font-size-xs)', opacity: 0.8 }}>{stop.sublabel}</div>
1048
+ </button>
1049
+ )}
1050
+ />
1051
+ );
1052
+ }
1053
+
1054
+ function ResponsiveSurfaceDemo() {
1055
+ return (
1056
+ <div
1057
+ style={{
1058
+ border: '1px dashed var(--sv-color-border)',
1059
+ borderRadius: 'var(--sv-radius-md)',
1060
+ padding: 'var(--sv-space-4)',
1061
+ fontFamily: ff,
1062
+ fontSize: 'var(--sv-font-size-sm)',
1063
+ color: 'var(--sv-color-text-muted)',
1064
+ }}
1065
+ >
1066
+ <ResponsiveSurface
1067
+ web={
1068
+ <span style={{ color: 'var(--sv-color-text-primary)' }}>
1069
+ Web tree (resize below 768px)
1070
+ </span>
1071
+ }
1072
+ mobile={<span style={{ color: 'var(--sv-color-text-primary)' }}>Mobile tree</span>}
1073
+ />
1074
+ </div>
1075
+ );
1076
+ }
1077
+
1078
+ const SWIPABLE_CAROUSEL_DEMO_SLIDES = [
1079
+ { key: 'lists', label: 'Lists' },
1080
+ { key: 'groceries', label: 'Groceries' },
1081
+ { key: 'errands', label: 'Errands' },
1082
+ ];
1083
+
1084
+ function SwipableMobileCarouselDemo() {
1085
+ const [activeIndex, setActiveIndex] = useState(0);
1086
+ return (
1087
+ <div
1088
+ style={{
1089
+ height: 220,
1090
+ border: '1px solid var(--sv-color-border)',
1091
+ borderRadius: 'var(--sv-radius-md)',
1092
+ overflow: 'hidden',
1093
+ }}
1094
+ >
1095
+ <SwipableMobileCarousel
1096
+ activeIndex={activeIndex}
1097
+ onSettle={setActiveIndex}
1098
+ aria-label="Task lists"
1099
+ >
1100
+ {SWIPABLE_CAROUSEL_DEMO_SLIDES.map((slide) => (
1101
+ <SwipableMobileCarouselSlide key={slide.key} slideKey={slide.key} label={slide.label}>
1102
+ <SwipableMobileCarouselSlideHeader>
1103
+ <div
1104
+ style={{
1105
+ padding: 'var(--sv-space-3)',
1106
+ fontFamily: ff,
1107
+ fontWeight: 'var(--sv-font-weight-semibold)',
1108
+ color: 'var(--sv-color-text-primary)',
1109
+ }}
1110
+ >
1111
+ {slide.label}
1112
+ </div>
1113
+ </SwipableMobileCarouselSlideHeader>
1114
+ <SwipableMobileCarouselSlideBody>
1115
+ <div
1116
+ style={{
1117
+ display: 'flex',
1118
+ alignItems: 'center',
1119
+ justifyContent: 'center',
1120
+ height: '100%',
1121
+ fontFamily: ff,
1122
+ color: 'var(--sv-color-text-muted)',
1123
+ }}
1124
+ >
1125
+ {slide.label} content
1126
+ </div>
1127
+ </SwipableMobileCarouselSlideBody>
1128
+ </SwipableMobileCarouselSlide>
1129
+ ))}
1130
+ </SwipableMobileCarousel>
1131
+ </div>
1132
+ );
1133
+ }
1134
+
1135
+ function SwipableMobileCarouselDotsDemo() {
1136
+ const [activeIndex, setActiveIndex] = useState(0);
1137
+ return (
1138
+ <SwipableMobileCarouselDots
1139
+ count={3}
1140
+ activeIndex={activeIndex}
1141
+ onJump={setActiveIndex}
1142
+ labels={['Lists', 'Groceries', 'Errands']}
1143
+ aria-label="Task lists"
1144
+ />
1145
+ );
1146
+ }
1147
+
1148
+ const SWIPE_STACK_DEMO_CARDS = [
1149
+ { id: 'water-plants', title: 'Water the plants' },
1150
+ { id: 'standup', title: 'Team standup · 9:30am' },
1151
+ { id: 'groceries', title: 'Buy groceries' },
1152
+ ];
1153
+
1154
+ function SwipeStackDemo() {
1155
+ return (
1156
+ <div style={{ height: 260, maxWidth: 320 }}>
1157
+ <SwipeStack
1158
+ aria-label="Today's agenda"
1159
+ directions={{
1160
+ up: { label: 'Done', icon: 'check' },
1161
+ down: { label: 'Defer', icon: 'history' },
1162
+ }}
1163
+ onSwipe={() => {}}
1164
+ >
1165
+ {SWIPE_STACK_DEMO_CARDS.map((card) => (
1166
+ <SwipeStackCard key={card.id} cardId={card.id}>
1167
+ <div
1168
+ style={{
1169
+ height: '100%',
1170
+ boxSizing: 'border-box',
1171
+ display: 'flex',
1172
+ alignItems: 'center',
1173
+ justifyContent: 'center',
1174
+ padding: 'var(--sv-space-4)',
1175
+ fontFamily: ff,
1176
+ fontWeight: 'var(--sv-font-weight-semibold)',
1177
+ color: 'var(--sv-color-text-primary)',
1178
+ textAlign: 'center',
1179
+ }}
1180
+ >
1181
+ {card.title}
1182
+ </div>
1183
+ </SwipeStackCard>
1184
+ ))}
1185
+ </SwipeStack>
1186
+ </div>
1187
+ );
1188
+ }
1189
+
924
1190
  function IconPickerDemo() {
925
1191
  const [value, setValue] = useState<IconName | null>('banana');
926
1192
  const options: IconName[] = [
@@ -946,6 +1212,26 @@ function IconPickerDemo() {
946
1212
  );
947
1213
  }
948
1214
 
1215
+ function ColorPickerDemo() {
1216
+ const [value, setValue] = useState<string | null>('#b5c9e8');
1217
+ const swatches = [
1218
+ { label: 'Sky', value: '#b5c9e8' },
1219
+ { label: 'Sage', value: '#c9e0c4' },
1220
+ { label: 'Sand', value: '#e8d3b5' },
1221
+ { label: 'Clay', value: '#e0c4c4' },
1222
+ { label: 'Lilac', value: '#d8c4e0' },
1223
+ ];
1224
+ return (
1225
+ <ColorPicker
1226
+ swatches={swatches}
1227
+ value={value}
1228
+ onChange={setValue}
1229
+ allowNone
1230
+ aria-label="Board color"
1231
+ />
1232
+ );
1233
+ }
1234
+
949
1235
  function SuggestionInputDemo() {
950
1236
  const [value, setValue] = useState('ban');
951
1237
  const options = ['Bananas', 'Banana bread mix']
@@ -1423,6 +1709,21 @@ font-weight: var(--sv-font-weight-medium); /* 500 */
1423
1709
  font-weight: var(--sv-font-weight-semibold); /* 600 */
1424
1710
  font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1425
1711
  </div>
1712
+
1713
+ <div style={{ marginTop: 'var(--sv-space-4)' }}>
1714
+ <p
1715
+ style={{
1716
+ fontSize: 'var(--sv-font-size-caption)',
1717
+ color: 'var(--sv-color-text-muted)',
1718
+ }}
1719
+ >
1720
+ Every size above is rem-based, so the whole scale responds to the in-app text-size
1721
+ control (Account → Preferences → Appearance, task 10.2), which sets{' '}
1722
+ <code>[data-text-size]</code> on <code>&lt;html&gt;</code> to override{' '}
1723
+ <code>--sv-text-size-scale</code> — see the &quot;Text-size presets&quot; section of
1724
+ the Token Gallery.
1725
+ </p>
1726
+ </div>
1426
1727
  </section>
1427
1728
 
1428
1729
  {/* ── Spacing & Radius ─────────────────────────────────────────── */}
@@ -1582,7 +1883,7 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1582
1883
  <section style={{ marginBottom: 'var(--sv-space-12)' }}>
1583
1884
  <SectionHeader
1584
1885
  title="Component gallery"
1585
- subtitle="All 22 components — click each story in the sidebar for the full API, variants, and controls."
1886
+ subtitle="All 77 components — click each story in the sidebar for the full API, variants, and controls."
1586
1887
  />
1587
1888
 
1588
1889
  <div style={{ marginBottom: 'var(--sv-space-6)' }}>
@@ -1620,7 +1921,7 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1620
1921
  <ComponentCard
1621
1922
  name="Badge"
1622
1923
  importLine="import { Badge } from '@sovereignfs/ui';"
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."
1924
+ usage="Compact label for roles (role), lifecycle states (status), and type/version tags (mono). xs/sm/md/lg sizes; ALL CAPS by default, or uppercase={false} for title case. RSC-safe."
1624
1925
  >
1625
1926
  <Badge variant="role">Admin</Badge>
1626
1927
  <Badge variant="status" status="active">
@@ -1633,6 +1934,9 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1633
1934
  <Badge variant="role" size="lg" uppercase={false}>
1634
1935
  Owner
1635
1936
  </Badge>
1937
+ <Badge variant="status" status="active" size="xs">
1938
+ Connected
1939
+ </Badge>
1636
1940
  </ComponentCard>
1637
1941
 
1638
1942
  {/* StatusBadge */}
@@ -1708,6 +2012,15 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1708
2012
  <IconPickerDemo />
1709
2013
  </ComponentCard>
1710
2014
 
2015
+ {/* ColorPicker */}
2016
+ <ComponentCard
2017
+ name="ColorPicker"
2018
+ importLine="import { ColorPicker } from '@sovereignfs/ui';"
2019
+ usage="Curated swatch suggestions plus a native color-input trigger for picking any color — not just the curated set."
2020
+ >
2021
+ <ColorPickerDemo />
2022
+ </ComponentCard>
2023
+
1711
2024
  {/* QuantityStepper */}
1712
2025
  <ComponentCard
1713
2026
  name="QuantityStepper"
@@ -1781,7 +2094,7 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1781
2094
  <ComponentCard
1782
2095
  name="Icon"
1783
2096
  importLine="import { Icon } from '@sovereignfs/ui';"
1784
- usage="SVG icon primitive. 52 bundled icons. Decorative: aria-hidden. Meaningful: aria-label. Three sizes: sm, md, lg."
2097
+ usage="SVG icon primitive. 96 bundled icons. Decorative: aria-hidden. Meaningful: aria-label. Three sizes: sm, md, lg."
1785
2098
  >
1786
2099
  {(
1787
2100
  [
@@ -1864,6 +2177,15 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1864
2177
  <MenuDemo />
1865
2178
  </ComponentCard>
1866
2179
 
2180
+ {/* MenuEntries */}
2181
+ <ComponentCard
2182
+ name="MenuEntries"
2183
+ importLine="import { MenuEntries } from '@sovereignfs/ui';"
2184
+ usage='The <ul role="menu"> item list itself, factored out of Menu so other adaptive-surface components (ContextMenu, Menubar) can render the exact same items/destructive/checked/icon markup inside their own positioning shell instead of re-deriving it. No panel chrome of its own — the background/border/shadow around it belongs to whichever surface hosts it.'
2185
+ >
2186
+ <MenuEntriesDemo />
2187
+ </ComponentCard>
2188
+
1867
2189
  {/* Calendar */}
1868
2190
  <ComponentCard
1869
2191
  name="Calendar"
@@ -1899,7 +2221,7 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1899
2221
  <ComponentCard
1900
2222
  name="Toast"
1901
2223
  importLine="import { ToastProvider, useToast } from '@sovereignfs/ui';"
1902
- usage="Fixed top-right notification stack. Wrap app in ToastProvider; call useToast().show() imperatively. Six categories."
2224
+ usage="Fixed top-right on desktop, bottom-center on mobile. Wrap app in ToastProvider; call useToast().show() imperatively. Six categories."
1903
2225
  >
1904
2226
  <ToastProvider>
1905
2227
  <ToastDemo />
@@ -1920,6 +2242,38 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1920
2242
  </div>
1921
2243
  </ComponentCard>
1922
2244
 
2245
+ {/* CardTile */}
2246
+ <ComponentCard
2247
+ name="CardTile"
2248
+ importLine="import { CardTile, CardTileGrid, NewCardTile } from '@sovereignfs/ui';"
2249
+ usage="A colored/iconed banner over a footer label, for a grid of navigable top-level entities (projects, boards). Card can't express this — its padding always wraps the whole box, with no bleed slot for a full-width banner. Not link-aware; wrap each tile in the host router's own link component. Pair with NewCardTile for the grid's 'add new' affordance. The icon variant below swaps the card chrome for a plain Finder-style icon + label, for entities that read as a single glyph + name (e.g. a folder)."
2250
+ >
2251
+ <CardTileGrid minTileWidth={140}>
2252
+ <CardTile banner={<Icon name="folder" size="lg" aria-hidden={true} />}>
2253
+ Q4 Planning
2254
+ </CardTile>
2255
+ <CardTile bannerColor="#c7d8ff">Marketing site</CardTile>
2256
+ <NewCardTile label="New project" />
2257
+ </CardTileGrid>
2258
+ <div style={{ marginTop: 'var(--sv-space-4)' }}>
2259
+ <CardTileGrid dense minTileWidth={100}>
2260
+ <CardTile
2261
+ variant="icon"
2262
+ banner={<Icon name="folder-closed" size="lg" aria-hidden={true} />}
2263
+ >
2264
+ Handbook
2265
+ </CardTile>
2266
+ <CardTile
2267
+ variant="icon"
2268
+ banner={<Icon name="folder-closed" size="lg" aria-hidden={true} />}
2269
+ >
2270
+ Q4 Planning
2271
+ </CardTile>
2272
+ <NewCardTile variant="icon" label="New" />
2273
+ </CardTileGrid>
2274
+ </div>
2275
+ </ComponentCard>
2276
+
1923
2277
  {/* FormField */}
1924
2278
  <ComponentCard
1925
2279
  name="FormField"
@@ -1998,11 +2352,186 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1998
2352
  />
1999
2353
  </ComponentCard>
2000
2354
 
2355
+ {/* ThreeColumnLayout */}
2356
+ <ComponentCard
2357
+ name="ThreeColumnLayout"
2358
+ importLine="import { ThreeColumnLayout } from '@sovereignfs/ui';"
2359
+ usage="Sidebar + main + optional detail column, for list-app shells (Tasks, Shopper). Purely positional — pass 2 children for sidebar+main, or 3 for sidebar+main+detail; the third is simply omitted when nothing is selected."
2360
+ >
2361
+ <div style={{ width: '100%', height: '220px' }}>
2362
+ <ThreeColumnLayout sidebarWidth={120} detailWidth={140}>
2363
+ <div
2364
+ style={{
2365
+ padding: 'var(--sv-space-3)',
2366
+ fontFamily: ffm,
2367
+ fontSize: 'var(--sv-font-size-xs)',
2368
+ color: 'var(--sv-color-text-primary)',
2369
+ }}
2370
+ >
2371
+ Sidebar
2372
+ </div>
2373
+ <div
2374
+ style={{
2375
+ padding: 'var(--sv-space-3)',
2376
+ fontFamily: ffm,
2377
+ fontSize: 'var(--sv-font-size-xs)',
2378
+ color: 'var(--sv-color-text-primary)',
2379
+ }}
2380
+ >
2381
+ Main
2382
+ </div>
2383
+ <div
2384
+ style={{
2385
+ padding: 'var(--sv-space-3)',
2386
+ fontFamily: ffm,
2387
+ fontSize: 'var(--sv-font-size-xs)',
2388
+ color: 'var(--sv-color-text-primary)',
2389
+ }}
2390
+ >
2391
+ Detail
2392
+ </div>
2393
+ </ThreeColumnLayout>
2394
+ </div>
2395
+ </ComponentCard>
2396
+
2397
+ {/* HeaderFooterLayout */}
2398
+ <ComponentCard
2399
+ name="HeaderFooterLayout"
2400
+ importLine="import { HeaderFooterLayout } from '@sovereignfs/ui';"
2401
+ usage="Header + main + optional footer, both fixed-height and independently optional; main always claims the remaining height. The vertical counterpart to ThreeColumnLayout."
2402
+ >
2403
+ <div style={{ width: '100%', height: '220px' }}>
2404
+ <HeaderFooterLayout
2405
+ headerHeight={36}
2406
+ footerHeight={36}
2407
+ header={
2408
+ <div
2409
+ style={{
2410
+ display: 'flex',
2411
+ alignItems: 'center',
2412
+ justifyContent: 'center',
2413
+ height: '100%',
2414
+ fontFamily: ffm,
2415
+ fontSize: 'var(--sv-font-size-xs)',
2416
+ color: 'var(--sv-color-text-primary)',
2417
+ }}
2418
+ >
2419
+ Header
2420
+ </div>
2421
+ }
2422
+ footer={
2423
+ <div
2424
+ style={{
2425
+ display: 'flex',
2426
+ alignItems: 'center',
2427
+ justifyContent: 'center',
2428
+ height: '100%',
2429
+ fontFamily: ffm,
2430
+ fontSize: 'var(--sv-font-size-xs)',
2431
+ color: 'var(--sv-color-text-primary)',
2432
+ }}
2433
+ >
2434
+ Footer
2435
+ </div>
2436
+ }
2437
+ >
2438
+ <div
2439
+ style={{
2440
+ padding: 'var(--sv-space-3)',
2441
+ fontFamily: ffm,
2442
+ fontSize: 'var(--sv-font-size-xs)',
2443
+ color: 'var(--sv-color-text-primary)',
2444
+ }}
2445
+ >
2446
+ Main (scrolls)
2447
+ </div>
2448
+ </HeaderFooterLayout>
2449
+ </div>
2450
+ </ComponentCard>
2451
+
2452
+ {/* RootLayout */}
2453
+ <ComponentCard
2454
+ name="RootLayout"
2455
+ importLine="import { RootLayout } from '@sovereignfs/ui';"
2456
+ usage="The root-level layout a plugin's page tree sits in. variant='sidebar' is web-only (sidebar + main); variant='shell' is mobile-only (header + main + footer) — same children, different fork per breakpoint via ResponsiveSurface. Shown here: variant='header', identical on both breakpoints."
2457
+ >
2458
+ <div style={{ width: '100%', height: '160px' }}>
2459
+ <RootLayout variant="header" headerHeight={32}>
2460
+ <div
2461
+ style={{
2462
+ display: 'flex',
2463
+ alignItems: 'center',
2464
+ justifyContent: 'center',
2465
+ height: '100%',
2466
+ fontFamily: ffm,
2467
+ fontSize: 'var(--sv-font-size-xs)',
2468
+ color: 'var(--sv-color-text-primary)',
2469
+ }}
2470
+ >
2471
+ Header
2472
+ </div>
2473
+ <div
2474
+ style={{
2475
+ padding: 'var(--sv-space-3)',
2476
+ fontFamily: ffm,
2477
+ fontSize: 'var(--sv-font-size-xs)',
2478
+ color: 'var(--sv-color-text-primary)',
2479
+ }}
2480
+ >
2481
+ Main
2482
+ </div>
2483
+ </RootLayout>
2484
+ </div>
2485
+ </ComponentCard>
2486
+
2487
+ {/* PageLayout */}
2488
+ <ComponentCard
2489
+ name="PageLayout"
2490
+ importLine="import { PageLayout } from '@sovereignfs/ui';"
2491
+ usage="A single page's content area, nested inside RootLayout's main slot. No padding by default (opt in via padding, same scale as PageContainer). The optional header is page-specific — a board title/toolbar, not the app-level header."
2492
+ >
2493
+ <div
2494
+ style={{
2495
+ width: '100%',
2496
+ height: '160px',
2497
+ border: '1px solid var(--sv-color-border)',
2498
+ }}
2499
+ >
2500
+ <PageLayout
2501
+ padding="sm"
2502
+ header={
2503
+ <div
2504
+ style={{
2505
+ padding: 'var(--sv-space-2) var(--sv-space-3)',
2506
+ borderBottom: '1px solid var(--sv-color-border)',
2507
+ fontFamily: ffm,
2508
+ fontSize: 'var(--sv-font-size-xs)',
2509
+ fontWeight: 600,
2510
+ color: 'var(--sv-color-text-primary)',
2511
+ }}
2512
+ >
2513
+ Page header
2514
+ </div>
2515
+ }
2516
+ >
2517
+ <div
2518
+ style={{
2519
+ fontFamily: ffm,
2520
+ fontSize: 'var(--sv-font-size-xs)',
2521
+ color: 'var(--sv-color-text-primary)',
2522
+ }}
2523
+ >
2524
+ Page content
2525
+ </div>
2526
+ </PageLayout>
2527
+ </div>
2528
+ </ComponentCard>
2529
+
2001
2530
  {/* PageHeader */}
2002
2531
  <ComponentCard
2003
2532
  name="PageHeader"
2004
2533
  importLine="import { PageHeader } from '@sovereignfs/ui';"
2005
- usage="Plugin page top section. Title + optional description + right-side action slot. Replaces the hand-rolled .pageHeader pattern in every plugin."
2534
+ usage="Plugin page top section. Title + optional description + right-side action slot. Replaces the hand-rolled .pageHeader pattern in every plugin. An optional `icon` renders before the title text on both breakpoints. On mobile, description drops out and onBack/onMenuClick add an optional back button and vertical-ellipsis menu trigger."
2006
2535
  >
2007
2536
  <div style={{ width: '100%' }}>
2008
2537
  <PageHeader
@@ -2013,6 +2542,223 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
2013
2542
  </div>
2014
2543
  </ComponentCard>
2015
2544
 
2545
+ {/* PageContainer */}
2546
+ <ComponentCard
2547
+ name="PageContainer"
2548
+ importLine="import { PageContainer } from '@sovereignfs/ui';"
2549
+ usage="The wrapper every plugin page renders through. Supplies the page gutter (padding: none/sm/md/lg, default md — the runtime shell no longer pads plugin content) and constrains width (maxWidth: sm/md/lg/full, default full). Use instead of declaring padding or max-width in a plugin's own CSS."
2550
+ >
2551
+ <div style={{ width: '100%', background: 'var(--sv-color-surface-sunken)' }}>
2552
+ <PageContainer maxWidth="sm" padding="md">
2553
+ <div
2554
+ style={{
2555
+ background: 'var(--sv-color-surface-raised)',
2556
+ border: '1px dashed var(--sv-color-border)',
2557
+ borderRadius: 'var(--sv-radius-md)',
2558
+ padding: 'var(--sv-space-4)',
2559
+ }}
2560
+ >
2561
+ maxWidth=&quot;sm&quot; padding=&quot;md&quot;
2562
+ </div>
2563
+ </PageContainer>
2564
+ </div>
2565
+ </ComponentCard>
2566
+
2567
+ {/* Header */}
2568
+ <ComponentCard
2569
+ name="Header"
2570
+ importLine="import { Header } from '@sovereignfs/ui';"
2571
+ usage="The web top-bar header for shell: minimal plugins that own their whole viewport (no platform sidebar). Brand badge + optional active plugin on the left; launcher (AppsLauncher), notifications (NotificationsPanel), and avatarMenu (UserMenu) on the right — all real, shared, independently clickable components. Presentational only — see Components/Header for the mobile counterpart (MobileHeader) shown side by side."
2572
+ >
2573
+ <Header
2574
+ instanceName="Sovereign"
2575
+ homeHref="/launcher"
2576
+ plugin={{
2577
+ // The plugin icon is 28px — the same size as the brand
2578
+ // badge next to it, not a smaller inline glyph.
2579
+ name: 'Notes',
2580
+ icon: (
2581
+ <span
2582
+ aria-hidden="true"
2583
+ style={{
2584
+ display: 'inline-flex',
2585
+ alignItems: 'center',
2586
+ justifyContent: 'center',
2587
+ width: 28,
2588
+ height: 28,
2589
+ }}
2590
+ >
2591
+ <span style={{ display: 'inline-flex', transform: 'scale(1.15)' }}>
2592
+ <Icon name="file" size="lg" aria-hidden />
2593
+ </span>
2594
+ </span>
2595
+ ),
2596
+ href: '/notes',
2597
+ }}
2598
+ launcher={
2599
+ <AppsLauncher
2600
+ items={[
2601
+ {
2602
+ key: 'home',
2603
+ icon: <Icon name="house" size="md" aria-hidden />,
2604
+ label: 'Home',
2605
+ href: '/launcher',
2606
+ },
2607
+ {
2608
+ key: 'notes',
2609
+ icon: <Icon name="file" size="md" aria-hidden />,
2610
+ label: 'Notes',
2611
+ href: '/notes',
2612
+ },
2613
+ ]}
2614
+ />
2615
+ }
2616
+ notifications={<NotificationsPanel items={[]} unreadCount={0} />}
2617
+ avatarMenu={
2618
+ <UserMenu
2619
+ name="Kasun Benthara"
2620
+ email="kasun@openfs.io"
2621
+ items={[
2622
+ { label: 'Account', icon: 'user', href: '/account' },
2623
+ { label: 'Sign out', icon: 'log-out', destructive: true, onSelect: () => {} },
2624
+ ]}
2625
+ size="sm"
2626
+ />
2627
+ }
2628
+ />
2629
+ </ComponentCard>
2630
+
2631
+ {/* AppsLauncher */}
2632
+ <ComponentCard
2633
+ name="AppsLauncher"
2634
+ importLine="import { AppsLauncher } from '@sovereignfs/ui';"
2635
+ usage="The desktop 'Apps' switcher — a 28px grid trigger opening a Popover tile grid. The desktop counterpart to MobileAppsDrawer: same items shape (key/icon/label/href/onClick), different chrome. Presentational only — items are supplied already resolved; loading/error are separate props."
2636
+ >
2637
+ <AppsLauncher
2638
+ items={[
2639
+ {
2640
+ key: 'home',
2641
+ icon: <Icon name="house" size="md" aria-hidden />,
2642
+ label: 'Home',
2643
+ href: '/launcher',
2644
+ },
2645
+ {
2646
+ key: 'console',
2647
+ icon: <Icon name="layout-dashboard" size="md" aria-hidden />,
2648
+ label: 'Console',
2649
+ href: '/console',
2650
+ },
2651
+ {
2652
+ key: 'notes',
2653
+ icon: <Icon name="file" size="md" aria-hidden />,
2654
+ label: 'Notes',
2655
+ href: '/notes',
2656
+ },
2657
+ ]}
2658
+ />
2659
+ </ComponentCard>
2660
+
2661
+ {/* NotificationsPanel */}
2662
+ <ComponentCard
2663
+ name="NotificationsPanel"
2664
+ importLine="import { NotificationsPanel } from '@sovereignfs/ui';"
2665
+ usage="The bell trigger + dropdown, generalized from the runtime shell's own NotificationBell — title/Mark all read/Clear all/close, then category-icon + title + time + unread-dot + dismiss rows. Pure presentation: no fetch/SSE/mark-read logic of its own; the consumer supplies already-resolved items and wires onOpen/onDismiss/onMarkAllRead/onClearAll."
2666
+ >
2667
+ <NotificationsPanel
2668
+ items={[
2669
+ {
2670
+ id: '1',
2671
+ icon: <Icon name="layers" size="sm" aria-hidden />,
2672
+ title: 'Added to a project',
2673
+ timeLabel: '2d ago',
2674
+ read: false,
2675
+ onDismiss: () => {},
2676
+ },
2677
+ ]}
2678
+ unreadCount={1}
2679
+ onMarkAllRead={() => {}}
2680
+ onClearAll={() => {}}
2681
+ />
2682
+ </ComponentCard>
2683
+
2684
+ {/* UserMenu */}
2685
+ <ComponentCard
2686
+ name="UserMenu"
2687
+ importLine="import { UserMenu } from '@sovereignfs/ui';"
2688
+ usage="The account avatar trigger + dropdown, generalized from the runtime shell's own AccountMenu — accent-filled avatar, a name/email header block, then a plain item list. Presentational only: no auth/session logic — items carry their own onSelect and/or href (e.g. a real 'Sign out' item just does whatever the consuming app's sign-out requires)."
2689
+ >
2690
+ <UserMenu
2691
+ name="Kasun Benthara"
2692
+ email="kasun@openfs.io"
2693
+ items={[
2694
+ { label: 'Account', icon: 'user', href: '/account' },
2695
+ {
2696
+ label: 'Preferences',
2697
+ icon: 'sliders-horizontal',
2698
+ href: '/account/preferences',
2699
+ },
2700
+ { label: 'Sign out', icon: 'log-out', destructive: true, onSelect: () => {} },
2701
+ ]}
2702
+ />
2703
+ </ComponentCard>
2704
+
2705
+ {/* MobileHeader */}
2706
+ <ComponentCard
2707
+ name="MobileHeader"
2708
+ importLine="import { MobileHeader } from '@sovereignfs/ui';"
2709
+ usage="Mobile shell header content row (RFC 0088). Bell and avatarMenu are always rendered by the consumer; logo is optional — omit it for a default 'S' badge (matching the shell's own fallback brand icon), or supply your own. Title is the only content-level override. Presentational only — see Overview/Mobile Patterns for the full immutable/overridable boundary."
2710
+ >
2711
+ <div
2712
+ style={{
2713
+ width: '100%',
2714
+ background: 'var(--sv-color-surface)',
2715
+ border: '1px solid var(--sv-color-border)',
2716
+ borderRadius: 'var(--sv-radius-md)',
2717
+ padding: '0 var(--sv-space-3)',
2718
+ }}
2719
+ >
2720
+ <MobileHeader
2721
+ title="Tasks"
2722
+ bell={<Icon name="bell" size="lg" aria-label="Notifications" />}
2723
+ avatarMenu={<Avatar name="Jamie Doe" size="sm" />}
2724
+ />
2725
+ </div>
2726
+ </ComponentCard>
2727
+
2728
+ {/* MobileFooter */}
2729
+ <ComponentCard
2730
+ name="MobileFooter"
2731
+ importLine="import { MobileFooter } from '@sovereignfs/ui';"
2732
+ usage="Mobile shell footer nav row (RFC 0088). The centered launcher (onOpenApps) is always rendered; leftIcons/rightIcons take 1 or 2 items each, symmetric so the launcher stays centered."
2733
+ >
2734
+ <div
2735
+ style={{
2736
+ width: '100%',
2737
+ background: 'var(--sv-color-surface)',
2738
+ border: '1px solid var(--sv-color-border)',
2739
+ borderRadius: 'var(--sv-radius-md)',
2740
+ padding: '0 var(--sv-space-3)',
2741
+ }}
2742
+ >
2743
+ <MobileFooter
2744
+ onOpenApps={() => {}}
2745
+ leftIcons={[{ icon: <Icon name="house" size="md" aria-hidden />, label: 'Home' }]}
2746
+ rightIcons={[
2747
+ { icon: <Icon name="search" size="md" aria-hidden />, label: 'Search' },
2748
+ ]}
2749
+ />
2750
+ </div>
2751
+ </ComponentCard>
2752
+
2753
+ {/* MobileAppsDrawer */}
2754
+ <ComponentCard
2755
+ name="MobileAppsDrawer"
2756
+ importLine="import { MobileAppsDrawer } from '@sovereignfs/ui';"
2757
+ usage="The mobile 'Apps' bottom sheet: a 3-column tile grid matching the runtime shell's own Apps drawer exactly (56px icon tiles, radius 14px). No header row — Drawer's own grab handle plus swipe-down/scrim-tap dismissal are enough. The tile list is supplied by the consumer via `items` — e.g. installed plugins for the shell, or a plugin's own sections for its own launcher button."
2758
+ >
2759
+ <MobileAppsDrawerDemo />
2760
+ </ComponentCard>
2761
+
2016
2762
  {/* EmptyState */}
2017
2763
  <ComponentCard
2018
2764
  name="EmptyState"
@@ -2031,6 +2777,53 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
2031
2777
  />
2032
2778
  </ComponentCard>
2033
2779
 
2780
+ {/* OfflineGate */}
2781
+ <ComponentCard
2782
+ name="OfflineGate"
2783
+ importLine="import { OfflineGate } from '@sovereignfs/ui';"
2784
+ usage="Blocks an administrative/settings surface from rendering possibly-stale cached content while offline, showing an EmptyState instead. Reads the live useIsOffline() hook — toggle DevTools > Network > Offline to see it swap. Wrap the body of a surface like Console or Account where operating against stale data is actively wrong, not every page."
2785
+ >
2786
+ <OfflineGate surfaceName="Console">
2787
+ <p style={{ margin: 0, fontFamily: 'var(--sv-font-family)', fontSize: 14 }}>
2788
+ Renders normally while online.
2789
+ </p>
2790
+ </OfflineGate>
2791
+ </ComponentCard>
2792
+
2793
+ {/* DeviceOnlyGate */}
2794
+ <ComponentCard
2795
+ name="DeviceOnlyGate"
2796
+ importLine="import { DeviceOnlyGate } from '@sovereignfs/ui';"
2797
+ usage="Blocks a device-only-tier plugin's content on a surface without the durable, encrypted, device-auth-gated store that tier needs. `available` is the caller's own isDeviceOnlyTierAvailable() result (from @sovereignfs/sdk/device-client) — a capability signal, never a security boundary; the real protection is that the data is encrypted regardless."
2798
+ >
2799
+ <DeviceOnlyGate available={false} surfaceName="Wallet">
2800
+ <p style={{ margin: 0, fontFamily: 'var(--sv-font-family)', fontSize: 14 }}>
2801
+ Renders normally when available.
2802
+ </p>
2803
+ </DeviceOnlyGate>
2804
+ </ComponentCard>
2805
+
2806
+ {/* DeviceStorageKeyGate */}
2807
+ <ComponentCard
2808
+ name="DeviceStorageKeyGate"
2809
+ importLine="import { DeviceStorageKeyGate } from '@sovereignfs/ui';"
2810
+ usage="Blocks a device-only-tier plugin's content until the user has set up their Device Storage Key. `status` is the caller's own getDeviceStorageKeyStatus() result (from @sovereignfs/sdk/device-only-storage). Enrollment is centralized in Account > Security, not per-plugin — `setupAction` is where the caller supplies a link there."
2811
+ >
2812
+ <DeviceStorageKeyGate
2813
+ status="not-set-up"
2814
+ surfaceName="Notes"
2815
+ setupAction={
2816
+ <a href="/account/security" style={{ color: 'var(--sv-color-accent)' }}>
2817
+ Go to Account → Security
2818
+ </a>
2819
+ }
2820
+ >
2821
+ <p style={{ margin: 0, fontFamily: 'var(--sv-font-family)', fontSize: 14 }}>
2822
+ Renders normally once set up.
2823
+ </p>
2824
+ </DeviceStorageKeyGate>
2825
+ </ComponentCard>
2826
+
2034
2827
  {/* Spinner */}
2035
2828
  <ComponentCard
2036
2829
  name="Spinner"
@@ -2076,6 +2869,42 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
2076
2869
  </div>
2077
2870
  </ComponentCard>
2078
2871
 
2872
+ {/* NavList */}
2873
+ <ComponentCard
2874
+ name="NavList"
2875
+ importLine="import { NavList } from '@sovereignfs/ui';"
2876
+ usage='Vertical list of icon+label rows, optionally grouped under section headers. static: active row highlighted, no chevron — a persistent sidebar. drilldown: trailing chevron on every row, no active state — a native-Settings-app style index. density="compact" (static only) shrinks row height for a short, always-visible list. Framework-agnostic like NavTabs; supply active/renderLink from the consumer.'
2877
+ >
2878
+ <div style={{ width: '100%', maxWidth: 220 }}>
2879
+ <NavList
2880
+ variant="static"
2881
+ aria-label="Example sections"
2882
+ groups={[
2883
+ {
2884
+ id: 'overview',
2885
+ items: [
2886
+ {
2887
+ id: 'overview',
2888
+ label: 'Overview',
2889
+ href: '#',
2890
+ icon: 'layout-dashboard',
2891
+ active: true,
2892
+ },
2893
+ ],
2894
+ },
2895
+ {
2896
+ id: 'people',
2897
+ label: 'People',
2898
+ items: [
2899
+ { id: 'users', label: 'Users', href: '#', icon: 'users' },
2900
+ { id: 'groups', label: 'Groups', href: '#', icon: 'layers' },
2901
+ ],
2902
+ },
2903
+ ]}
2904
+ />
2905
+ </div>
2906
+ </ComponentCard>
2907
+
2079
2908
  {/* Tooltip */}
2080
2909
  <ComponentCard
2081
2910
  name="Tooltip"
@@ -2238,6 +3067,44 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
2238
3067
  />
2239
3068
  </ComponentCard>
2240
3069
 
3070
+ {/* LegalLinks */}
3071
+ <ComponentCard
3072
+ name="LegalLinks"
3073
+ importLine="import { LegalLinks } from '@sovereignfs/ui';"
3074
+ usage="Privacy Policy / Terms of Service link row for standalone auth pages (login, register, forgot/reset password). Pass renderLink to keep navigation client-side if ever mounted inside an SPA-like shell."
3075
+ >
3076
+ <LegalLinks />
3077
+ </ComponentCard>
3078
+
3079
+ {/* ConsentPrompt */}
3080
+ <ComponentCard
3081
+ name="ConsentPrompt"
3082
+ importLine="import { ConsentPrompt } from '@sovereignfs/ui';"
3083
+ usage="Consent screen shown before one plugin is allowed to read another plugin's data (RFC 0002 data grants) or connect to an external service on the user's behalf. description should come from the provider's own manifest declaration, never caller-supplied copy."
3084
+ >
3085
+ <ConsentPrompt
3086
+ consumerName="Ledger"
3087
+ providerName="Finance Tracker"
3088
+ contract="expenses"
3089
+ description="Monthly expense totals by category, no transaction-level detail."
3090
+ onAllow={() => {}}
3091
+ onDeny={() => {}}
3092
+ />
3093
+ </ComponentCard>
3094
+
3095
+ {/* Markdown */}
3096
+ <ComponentCard
3097
+ name="Markdown"
3098
+ importLine="import { Markdown } from '@sovereignfs/ui';"
3099
+ usage="Renders a constrained markdown subset (headings, paragraphs, bold/italic/code, links, ordered and unordered lists, fenced code blocks, blockquotes) as Typography-based React elements — no HTML string injection. Built for first-party long-form content and model-generated replies; still not a full CommonMark parser (no tables, images, or nested blocks)."
3100
+ >
3101
+ <Markdown
3102
+ content={
3103
+ '## Heading\n\nA paragraph with **bold** and a [link](/privacy).\n\n- One\n- Two\n\n1. First\n2. Second\n\n```ts\nconst a = 1;\n```'
3104
+ }
3105
+ />
3106
+ </ComponentCard>
3107
+
2241
3108
  {/* Pagination */}
2242
3109
  <ComponentCard
2243
3110
  name="Pagination"
@@ -2668,6 +3535,26 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
2668
3535
  </div>
2669
3536
  </ComponentCard>
2670
3537
 
3538
+ {/* GripIcon */}
3539
+ <ComponentCard
3540
+ name="GripIcon"
3541
+ importLine="import { GripIcon } from '@sovereignfs/ui';"
3542
+ usage="Six-dot drag-handle icon, sized via `size` (default 14). No baked-in pointer-events or color — DragHandleRow uses it internally; a floating, absolutely-positioned handle can use it directly."
3543
+ >
3544
+ <div style={{ color: 'var(--sv-color-text-muted)' }}>
3545
+ <GripIcon />
3546
+ </div>
3547
+ </ComponentCard>
3548
+
3549
+ {/* StepStrip */}
3550
+ <ComponentCard
3551
+ name="StepStrip"
3552
+ importLine="import { StepStrip } from '@sovereignfs/ui';"
3553
+ usage="Horizontal connected-chip strip (a route/itinerary sequence) with an optional trailing add chip. Purely presentational — renderItem owns each chip's content, click handling, and any drag-reorder wiring (attach dnd-kit's useSortable the same way DragHandleRow's handleProps expects it)."
3554
+ >
3555
+ <StepStripDemo />
3556
+ </ComponentCard>
3557
+
2671
3558
  {/* FileDropzone */}
2672
3559
  <ComponentCard
2673
3560
  name="FileDropzone"
@@ -2676,6 +3563,42 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
2676
3563
  >
2677
3564
  <FileDropzoneDemo />
2678
3565
  </ComponentCard>
3566
+
3567
+ {/* ResponsiveSurface */}
3568
+ <ComponentCard
3569
+ name="ResponsiveSurface"
3570
+ importLine="import { ResponsiveSurface } from '@sovereignfs/ui';"
3571
+ usage="Renders an entirely different component tree below a breakpoint, not a CSS squeeze of the same one — only the active side is ever mounted. See also the useResponsiveLayout hook for non-JSX values."
3572
+ >
3573
+ <ResponsiveSurfaceDemo />
3574
+ </ComponentCard>
3575
+
3576
+ {/* SwipableMobileCarousel */}
3577
+ <ComponentCard
3578
+ name="SwipableMobileCarousel"
3579
+ importLine="import { SwipableMobileCarousel, SwipableMobileCarouselSlide, SwipableMobileCarouselSlideHeader, SwipableMobileCarouselSlideBody } from '@sovereignfs/ui';"
3580
+ usage="Compound component for swiping between full-width slides. A Slide's Header/Footer always render; only Body's own loading prop gates its content, so a title can show immediately while the body's own fetch is still in flight. Owns mount-window/reorder mechanics only — never put cross-slide aggregation or a detail overlay inside a Slide."
3581
+ >
3582
+ <SwipableMobileCarouselDemo />
3583
+ </ComponentCard>
3584
+
3585
+ {/* SwipableMobileCarouselDots */}
3586
+ <ComponentCard
3587
+ name="SwipableMobileCarouselDots"
3588
+ importLine="import { SwipableMobileCarouselDots } from '@sovereignfs/ui';"
3589
+ usage="A real, tappable, labeled slide indicator (role=tablist/tab) — standalone and reusable outside SwipableMobileCarousel too, and its default renderIndicator."
3590
+ >
3591
+ <SwipableMobileCarouselDotsDemo />
3592
+ </ComponentCard>
3593
+
3594
+ {/* SwipeStack */}
3595
+ <ComponentCard
3596
+ name="SwipeStack"
3597
+ importLine="import { SwipeStack, SwipeStackCard } from '@sovereignfs/ui';"
3598
+ usage="Triage one card at a time by dragging left/right/up/down, plus an always-visible fallback button per configured direction wired to the identical commit path. Every direction is independently optional and has no baked-in meaning — the caller supplies label/icon and reacts to onSwipe; once triggered a direction removes the card for good, there is no reversible 'go back'."
3599
+ >
3600
+ <SwipeStackDemo />
3601
+ </ComponentCard>
2679
3602
  </div>
2680
3603
  </section>
2681
3604
 
@@ -2789,6 +3712,10 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
2789
3712
  <li>
2790
3713
  Use <code style={{ fontFamily: ffm }}>--sv-shadow-*</code> for elevation
2791
3714
  </li>
3715
+ <li>
3716
+ Use <code style={{ fontFamily: ffm }}>--sv-border-width-hairline</code> instead of
3717
+ a literal border width (RFC 0094)
3718
+ </li>
2792
3719
  </ul>
2793
3720
  </div>
2794
3721
  <div