@sovereignfs/ui 0.54.0 → 0.89.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 (473) hide show
  1. package/LICENSE +373 -661
  2. package/NOTICE +8 -0
  3. package/README.md +4 -6
  4. package/dist/Accordion.module.css +1 -1
  5. package/dist/Alert.module.css +1 -1
  6. package/dist/AppsLauncher.module.css +102 -0
  7. package/dist/Avatar.module.css +1 -1
  8. package/dist/Badge.module.css +30 -7
  9. package/dist/BalanceChip.module.css +1 -1
  10. package/dist/Button.module.css +23 -2
  11. package/dist/Card.module.css +7 -1
  12. package/dist/CardTile.module.css +187 -0
  13. package/dist/CheckableListRow.module.css +8 -0
  14. package/dist/Checkbox.module.css +8 -0
  15. package/dist/CodeTextarea.module.css +7 -1
  16. package/dist/Collapsible.module.css +1 -1
  17. package/dist/ColorPicker.module.css +138 -0
  18. package/dist/Combobox.module.css +8 -2
  19. package/dist/Command.module.css +7 -1
  20. package/dist/ConfirmDialog.module.css +8 -2
  21. package/dist/ConsentPrompt.module.css +21 -0
  22. package/dist/DatePicker.module.css +7 -1
  23. package/dist/Dialog.module.css +130 -21
  24. package/dist/DragHandleRow.module.css +6 -0
  25. package/dist/Drawer.module.css +36 -7
  26. package/dist/FileDropzone.module.css +25 -1
  27. package/dist/Header.module.css +68 -0
  28. package/dist/HeaderFooterLayout.module.css +21 -0
  29. package/dist/IconPicker.module.css +15 -5
  30. package/dist/Input.module.css +7 -1
  31. package/dist/Kbd.module.css +1 -1
  32. package/dist/LegalLinks.module.css +9 -2
  33. package/dist/Markdown.module.css +29 -0
  34. package/dist/Menu.module.css +1 -0
  35. package/dist/Message.module.css +10 -1
  36. package/dist/MessageScroller.module.css +12 -1
  37. package/dist/MobileAppsDrawer.module.css +19 -14
  38. package/dist/MobileFooter.module.css +7 -1
  39. package/dist/MobileHeader.module.css +14 -1
  40. package/dist/NavList.module.css +139 -0
  41. package/dist/NavTabs.module.css +8 -2
  42. package/dist/NotificationsPanel.module.css +268 -0
  43. package/dist/OverlayHeader.module.css +1 -1
  44. package/dist/PageContainer.module.css +62 -0
  45. package/dist/PageHeader.module.css +54 -0
  46. package/dist/PageLayout.module.css +51 -0
  47. package/dist/Popover.module.css +1 -1
  48. package/dist/QrCode.module.css +17 -0
  49. package/dist/QuantityStepper.module.css +5 -3
  50. package/dist/RadioGroup.module.css +6 -0
  51. package/dist/Resizable.module.css +3 -3
  52. package/dist/RootLayout.module.css +12 -0
  53. package/dist/SegmentedControl.module.css +7 -1
  54. package/dist/Select.module.css +7 -1
  55. package/dist/Sheet.module.css +20 -2
  56. package/dist/SplitPane.module.css +5 -5
  57. package/dist/StatusBadge.module.css +1 -1
  58. package/dist/StepStrip.module.css +55 -0
  59. package/dist/SuggestionInput.module.css +14 -3
  60. package/dist/SwipableMobileCarousel.module.css +12 -3
  61. package/dist/SwipableMobileCarouselDots.module.css +21 -0
  62. package/dist/SwipeStack.module.css +101 -0
  63. package/dist/SystemBanner.module.css +16 -0
  64. package/dist/Table.module.css +2 -2
  65. package/dist/Tabs.module.css +7 -1
  66. package/dist/TagInput.module.css +8 -2
  67. package/dist/Textarea.module.css +15 -1
  68. package/dist/ThreeColumnLayout.module.css +35 -0
  69. package/dist/Toast.module.css +67 -3
  70. package/dist/Toggle.module.css +7 -0
  71. package/dist/Tooltip.module.css +7 -1
  72. package/dist/UserMenu.module.css +95 -0
  73. package/dist/fonts/OFL.txt +93 -0
  74. package/dist/fonts/hanken-grotesk-cyrillic-ext-wght-normal.woff2 +0 -0
  75. package/dist/fonts/hanken-grotesk-latin-ext-wght-normal.woff2 +0 -0
  76. package/dist/fonts/hanken-grotesk-latin-wght-normal.woff2 +0 -0
  77. package/dist/fonts/hanken-grotesk-vietnamese-wght-normal.woff2 +0 -0
  78. package/dist/index.d.ts +1156 -76
  79. package/dist/index.js +4223 -1513
  80. package/dist/tokens/fonts.css +53 -0
  81. package/dist/tokens/primitives.css +23 -12
  82. package/dist/tokens/semantic.css +32 -0
  83. package/dist/tokens/theme-presets.css +33 -0
  84. package/dist/tokens.css +2 -1
  85. package/package.json +46 -19
  86. package/src/__tests__/motion.test.tsx +0 -105
  87. package/src/components/Accordion/Accordion.module.css +0 -3
  88. package/src/components/Accordion/Accordion.stories.tsx +0 -110
  89. package/src/components/Accordion/Accordion.tsx +0 -53
  90. package/src/components/Accordion/__tests__/Accordion.test.tsx +0 -61
  91. package/src/components/Alert/Alert.module.css +0 -62
  92. package/src/components/Alert/Alert.stories.tsx +0 -96
  93. package/src/components/Alert/Alert.tsx +0 -58
  94. package/src/components/Alert/__tests__/Alert.test.tsx +0 -75
  95. package/src/components/AspectRatio/AspectRatio.module.css +0 -15
  96. package/src/components/AspectRatio/AspectRatio.stories.tsx +0 -78
  97. package/src/components/AspectRatio/AspectRatio.tsx +0 -21
  98. package/src/components/AspectRatio/__tests__/AspectRatio.test.tsx +0 -27
  99. package/src/components/Avatar/Avatar.module.css +0 -38
  100. package/src/components/Avatar/Avatar.tsx +0 -42
  101. package/src/components/Badge/Badge.module.css +0 -154
  102. package/src/components/Badge/Badge.stories.tsx +0 -226
  103. package/src/components/Badge/Badge.tsx +0 -81
  104. package/src/components/Badge/__tests__/Badge.test.tsx +0 -88
  105. package/src/components/BalanceChip/BalanceChip.module.css +0 -32
  106. package/src/components/BalanceChip/BalanceChip.stories.tsx +0 -45
  107. package/src/components/BalanceChip/BalanceChip.tsx +0 -33
  108. package/src/components/Breadcrumb/Breadcrumb.module.css +0 -49
  109. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +0 -47
  110. package/src/components/Breadcrumb/Breadcrumb.tsx +0 -57
  111. package/src/components/Breadcrumb/__tests__/Breadcrumb.test.tsx +0 -52
  112. package/src/components/Button/Button.module.css +0 -153
  113. package/src/components/Button/Button.stories.tsx +0 -83
  114. package/src/components/Button/Button.tsx +0 -52
  115. package/src/components/Button/__tests__/Button.test.tsx +0 -66
  116. package/src/components/ButtonGroup/ButtonGroup.module.css +0 -29
  117. package/src/components/ButtonGroup/ButtonGroup.stories.tsx +0 -57
  118. package/src/components/ButtonGroup/ButtonGroup.tsx +0 -27
  119. package/src/components/ButtonGroup/__tests__/ButtonGroup.test.tsx +0 -29
  120. package/src/components/Calendar/Calendar.module.css +0 -126
  121. package/src/components/Calendar/Calendar.tsx +0 -213
  122. package/src/components/Calendar/__tests__/Calendar.test.tsx +0 -98
  123. package/src/components/Calendar/dateUtils.ts +0 -97
  124. package/src/components/Card/Card.module.css +0 -42
  125. package/src/components/Card/Card.tsx +0 -34
  126. package/src/components/CheckableListRow/CheckableListRow.module.css +0 -95
  127. package/src/components/CheckableListRow/CheckableListRow.stories.tsx +0 -99
  128. package/src/components/CheckableListRow/CheckableListRow.tsx +0 -89
  129. package/src/components/Checkbox/Checkbox.module.css +0 -111
  130. package/src/components/Checkbox/Checkbox.tsx +0 -78
  131. package/src/components/CodeTextarea/CodeTextarea.module.css +0 -53
  132. package/src/components/CodeTextarea/CodeTextarea.stories.tsx +0 -77
  133. package/src/components/CodeTextarea/CodeTextarea.tsx +0 -36
  134. package/src/components/Collapsible/Collapsible.module.css +0 -70
  135. package/src/components/Collapsible/Collapsible.stories.tsx +0 -62
  136. package/src/components/Collapsible/Collapsible.tsx +0 -66
  137. package/src/components/Collapsible/__tests__/Collapsible.test.tsx +0 -71
  138. package/src/components/Combobox/Combobox.module.css +0 -122
  139. package/src/components/Combobox/Combobox.stories.tsx +0 -79
  140. package/src/components/Combobox/Combobox.tsx +0 -186
  141. package/src/components/Combobox/__tests__/Combobox.test.tsx +0 -158
  142. package/src/components/Command/Command.module.css +0 -80
  143. package/src/components/Command/Command.stories.tsx +0 -104
  144. package/src/components/Command/Command.tsx +0 -168
  145. package/src/components/Command/__tests__/Command.test.tsx +0 -162
  146. package/src/components/ConfirmDialog/ConfirmDialog.module.css +0 -113
  147. package/src/components/ConfirmDialog/ConfirmDialog.tsx +0 -124
  148. package/src/components/ConfirmDialog/__tests__/ConfirmDialog.test.tsx +0 -146
  149. package/src/components/ContextMenu/ContextMenu.stories.tsx +0 -81
  150. package/src/components/ContextMenu/ContextMenu.tsx +0 -100
  151. package/src/components/ContextMenu/__tests__/ContextMenu.test.tsx +0 -87
  152. package/src/components/CurrencyInput/CurrencyInput.stories.tsx +0 -50
  153. package/src/components/CurrencyInput/CurrencyInput.tsx +0 -60
  154. package/src/components/DataTable/DataTable.module.css +0 -28
  155. package/src/components/DataTable/DataTable.stories.tsx +0 -74
  156. package/src/components/DataTable/DataTable.tsx +0 -154
  157. package/src/components/DataTable/__tests__/DataTable.test.tsx +0 -123
  158. package/src/components/DatePicker/DatePicker.module.css +0 -61
  159. package/src/components/DatePicker/DatePicker.tsx +0 -103
  160. package/src/components/DatePicker/__tests__/DatePicker.test.tsx +0 -89
  161. package/src/components/DeviceOnlyGate/DeviceOnlyGate.tsx +0 -58
  162. package/src/components/DeviceOnlyGate/__tests__/DeviceOnlyGate.test.tsx +0 -45
  163. package/src/components/DeviceStorageKeyGate/DeviceStorageKeyGate.tsx +0 -100
  164. package/src/components/DeviceStorageKeyGate/__tests__/DeviceStorageKeyGate.test.tsx +0 -119
  165. package/src/components/Dialog/Dialog.module.css +0 -226
  166. package/src/components/Dialog/Dialog.stories.tsx +0 -147
  167. package/src/components/Dialog/Dialog.tsx +0 -160
  168. package/src/components/Dialog/__tests__/Dialog.test.tsx +0 -137
  169. package/src/components/DragHandleRow/DragHandleRow.module.css +0 -57
  170. package/src/components/DragHandleRow/DragHandleRow.tsx +0 -61
  171. package/src/components/Drawer/Drawer.module.css +0 -92
  172. package/src/components/Drawer/Drawer.stories.tsx +0 -132
  173. package/src/components/Drawer/Drawer.tsx +0 -176
  174. package/src/components/Drawer/__tests__/Drawer.test.tsx +0 -199
  175. package/src/components/EmptyState/EmptyState.module.css +0 -27
  176. package/src/components/EmptyState/EmptyState.tsx +0 -27
  177. package/src/components/FileDropzone/FileDropzone.module.css +0 -63
  178. package/src/components/FileDropzone/FileDropzone.tsx +0 -109
  179. package/src/components/FileDropzone/__tests__/FileDropzone.test.tsx +0 -67
  180. package/src/components/FormField/FormField.module.css +0 -33
  181. package/src/components/FormField/FormField.tsx +0 -78
  182. package/src/components/FormField/__tests__/FormField.test.tsx +0 -83
  183. package/src/components/HoverCard/HoverCard.module.css +0 -3
  184. package/src/components/HoverCard/HoverCard.stories.tsx +0 -66
  185. package/src/components/HoverCard/HoverCard.tsx +0 -111
  186. package/src/components/HoverCard/__tests__/HoverCard.test.tsx +0 -99
  187. package/src/components/Icon/Icon.module.css +0 -27
  188. package/src/components/Icon/Icon.stories.tsx +0 -95
  189. package/src/components/Icon/Icon.tsx +0 -43
  190. package/src/components/Icon/__tests__/Icon.test.tsx +0 -40
  191. package/src/components/Icon/icons/activity.tsx +0 -20
  192. package/src/components/Icon/icons/alert-triangle.tsx +0 -22
  193. package/src/components/Icon/icons/apple.tsx +0 -21
  194. package/src/components/Icon/icons/banana.tsx +0 -21
  195. package/src/components/Icon/icons/beef.tsx +0 -22
  196. package/src/components/Icon/icons/beer.tsx +0 -24
  197. package/src/components/Icon/icons/bell.tsx +0 -21
  198. package/src/components/Icon/icons/calendar.tsx +0 -23
  199. package/src/components/Icon/icons/candy.tsx +0 -24
  200. package/src/components/Icon/icons/carrot.tsx +0 -22
  201. package/src/components/Icon/icons/check.tsx +0 -20
  202. package/src/components/Icon/icons/chevron-down.tsx +0 -20
  203. package/src/components/Icon/icons/chevron-left.tsx +0 -20
  204. package/src/components/Icon/icons/chevron-right.tsx +0 -20
  205. package/src/components/Icon/icons/chevron-up.tsx +0 -20
  206. package/src/components/Icon/icons/circle-check.tsx +0 -21
  207. package/src/components/Icon/icons/circle-x.tsx +0 -22
  208. package/src/components/Icon/icons/coffee.tsx +0 -23
  209. package/src/components/Icon/icons/cookie.tsx +0 -25
  210. package/src/components/Icon/icons/croissant.tsx +0 -24
  211. package/src/components/Icon/icons/cup-soda.tsx +0 -23
  212. package/src/components/Icon/icons/drumstick.tsx +0 -21
  213. package/src/components/Icon/icons/egg.tsx +0 -20
  214. package/src/components/Icon/icons/ellipsis-vertical.tsx +0 -22
  215. package/src/components/Icon/icons/external-link.tsx +0 -22
  216. package/src/components/Icon/icons/eye-off.tsx +0 -23
  217. package/src/components/Icon/icons/eye.tsx +0 -21
  218. package/src/components/Icon/icons/file.tsx +0 -21
  219. package/src/components/Icon/icons/fish.tsx +0 -25
  220. package/src/components/Icon/icons/grid-2x2.tsx +0 -22
  221. package/src/components/Icon/icons/house.tsx +0 -21
  222. package/src/components/Icon/icons/index.ts +0 -234
  223. package/src/components/Icon/icons/info.tsx +0 -22
  224. package/src/components/Icon/icons/lock.tsx +0 -21
  225. package/src/components/Icon/icons/log-out.tsx +0 -22
  226. package/src/components/Icon/icons/mail.tsx +0 -21
  227. package/src/components/Icon/icons/menu.tsx +0 -22
  228. package/src/components/Icon/icons/milk.tsx +0 -22
  229. package/src/components/Icon/icons/package.tsx +0 -23
  230. package/src/components/Icon/icons/pencil.tsx +0 -21
  231. package/src/components/Icon/icons/pizza.tsx +0 -24
  232. package/src/components/Icon/icons/plus.tsx +0 -21
  233. package/src/components/Icon/icons/rotate-ccw.tsx +0 -21
  234. package/src/components/Icon/icons/salad.tsx +0 -24
  235. package/src/components/Icon/icons/search.tsx +0 -21
  236. package/src/components/Icon/icons/settings.tsx +0 -21
  237. package/src/components/Icon/icons/shield.tsx +0 -20
  238. package/src/components/Icon/icons/shopping-basket.tsx +0 -26
  239. package/src/components/Icon/icons/sliders-horizontal.tsx +0 -28
  240. package/src/components/Icon/icons/smartphone.tsx +0 -21
  241. package/src/components/Icon/icons/snowflake.tsx +0 -31
  242. package/src/components/Icon/icons/spray-can.tsx +0 -29
  243. package/src/components/Icon/icons/trash-2.tsx +0 -24
  244. package/src/components/Icon/icons/upload.tsx +0 -22
  245. package/src/components/Icon/icons/user.tsx +0 -21
  246. package/src/components/Icon/icons/wine.tsx +0 -23
  247. package/src/components/Icon/icons/x.tsx +0 -21
  248. package/src/components/IconPicker/IconPicker.module.css +0 -63
  249. package/src/components/IconPicker/IconPicker.stories.tsx +0 -84
  250. package/src/components/IconPicker/IconPicker.tsx +0 -90
  251. package/src/components/Input/Input.module.css +0 -52
  252. package/src/components/Input/Input.stories.tsx +0 -86
  253. package/src/components/Input/Input.tsx +0 -14
  254. package/src/components/Input/__tests__/Input.test.tsx +0 -22
  255. package/src/components/Item/Item.module.css +0 -70
  256. package/src/components/Item/Item.stories.tsx +0 -62
  257. package/src/components/Item/Item.tsx +0 -54
  258. package/src/components/Item/__tests__/Item.test.tsx +0 -39
  259. package/src/components/Kbd/Kbd.module.css +0 -14
  260. package/src/components/Kbd/Kbd.stories.tsx +0 -32
  261. package/src/components/Kbd/Kbd.tsx +0 -12
  262. package/src/components/Kbd/__tests__/Kbd.test.tsx +0 -14
  263. package/src/components/Label/Label.module.css +0 -11
  264. package/src/components/Label/Label.stories.tsx +0 -40
  265. package/src/components/Label/Label.tsx +0 -24
  266. package/src/components/Label/__tests__/Label.test.tsx +0 -32
  267. package/src/components/LegalLinks/LegalLinks.module.css +0 -24
  268. package/src/components/LegalLinks/LegalLinks.stories.tsx +0 -22
  269. package/src/components/LegalLinks/LegalLinks.tsx +0 -40
  270. package/src/components/Markdown/Markdown.module.css +0 -49
  271. package/src/components/Markdown/Markdown.stories.tsx +0 -30
  272. package/src/components/Markdown/Markdown.tsx +0 -168
  273. package/src/components/Marker/Marker.module.css +0 -33
  274. package/src/components/Marker/Marker.stories.tsx +0 -39
  275. package/src/components/Marker/Marker.tsx +0 -34
  276. package/src/components/Marker/__tests__/Marker.test.tsx +0 -23
  277. package/src/components/MemberMultiSelect/MemberMultiSelect.module.css +0 -27
  278. package/src/components/MemberMultiSelect/MemberMultiSelect.stories.tsx +0 -74
  279. package/src/components/MemberMultiSelect/MemberMultiSelect.tsx +0 -67
  280. package/src/components/Menu/Menu.module.css +0 -89
  281. package/src/components/Menu/Menu.tsx +0 -159
  282. package/src/components/Menu/__tests__/Menu.test.tsx +0 -197
  283. package/src/components/Menubar/Menubar.stories.tsx +0 -52
  284. package/src/components/Menubar/Menubar.tsx +0 -42
  285. package/src/components/Menubar/__tests__/Menubar.test.tsx +0 -45
  286. package/src/components/Message/Message.module.css +0 -99
  287. package/src/components/Message/Message.stories.tsx +0 -79
  288. package/src/components/Message/Message.tsx +0 -41
  289. package/src/components/Message/__tests__/Message.test.tsx +0 -46
  290. package/src/components/MessageScroller/MessageScroller.module.css +0 -54
  291. package/src/components/MessageScroller/MessageScroller.stories.tsx +0 -84
  292. package/src/components/MessageScroller/MessageScroller.tsx +0 -72
  293. package/src/components/MessageScroller/__tests__/MessageScroller.test.tsx +0 -105
  294. package/src/components/MobileAppsDrawer/MobileAppsDrawer.module.css +0 -81
  295. package/src/components/MobileAppsDrawer/MobileAppsDrawer.tsx +0 -78
  296. package/src/components/MobileAppsDrawer/__tests__/MobileAppsDrawer.test.tsx +0 -80
  297. package/src/components/MobileFooter/MobileFooter.module.css +0 -73
  298. package/src/components/MobileFooter/MobileFooter.stories.tsx +0 -47
  299. package/src/components/MobileFooter/MobileFooter.tsx +0 -109
  300. package/src/components/MobileFooter/__tests__/MobileFooter.test.tsx +0 -71
  301. package/src/components/MobileHeader/MobileHeader.module.css +0 -63
  302. package/src/components/MobileHeader/MobileHeader.stories.tsx +0 -57
  303. package/src/components/MobileHeader/MobileHeader.tsx +0 -77
  304. package/src/components/MobileHeader/__tests__/MobileHeader.test.tsx +0 -58
  305. package/src/components/NavTabs/NavTabs.module.css +0 -50
  306. package/src/components/NavTabs/NavTabs.tsx +0 -61
  307. package/src/components/NavigationMenu/NavigationMenu.module.css +0 -53
  308. package/src/components/NavigationMenu/NavigationMenu.stories.tsx +0 -58
  309. package/src/components/NavigationMenu/NavigationMenu.tsx +0 -125
  310. package/src/components/NavigationMenu/__tests__/NavigationMenu.test.tsx +0 -93
  311. package/src/components/OfflineGate/OfflineGate.tsx +0 -44
  312. package/src/components/OfflineGate/__tests__/OfflineGate.test.tsx +0 -66
  313. package/src/components/OverlayHeader/OverlayHeader.module.css +0 -98
  314. package/src/components/OverlayHeader/OverlayHeader.tsx +0 -62
  315. package/src/components/OverlayHeader/__tests__/OverlayHeader.test.tsx +0 -51
  316. package/src/components/PageContainer/PageContainer.module.css +0 -20
  317. package/src/components/PageContainer/PageContainer.tsx +0 -31
  318. package/src/components/PageContainer/__tests__/PageContainer.test.tsx +0 -37
  319. package/src/components/PageHeader/PageHeader.module.css +0 -32
  320. package/src/components/PageHeader/PageHeader.tsx +0 -35
  321. package/src/components/Pagination/Pagination.module.css +0 -98
  322. package/src/components/Pagination/Pagination.stories.tsx +0 -49
  323. package/src/components/Pagination/Pagination.tsx +0 -87
  324. package/src/components/Pagination/__tests__/Pagination.test.tsx +0 -56
  325. package/src/components/Popover/Popover.module.css +0 -74
  326. package/src/components/Popover/Popover.stories.tsx +0 -357
  327. package/src/components/Popover/Popover.tsx +0 -208
  328. package/src/components/Popover/__tests__/Popover.test.tsx +0 -98
  329. package/src/components/Progress/Progress.module.css +0 -20
  330. package/src/components/Progress/Progress.stories.tsx +0 -40
  331. package/src/components/Progress/Progress.tsx +0 -35
  332. package/src/components/Progress/__tests__/Progress.test.tsx +0 -31
  333. package/src/components/QuantityStepper/QuantityStepper.module.css +0 -67
  334. package/src/components/QuantityStepper/QuantityStepper.stories.tsx +0 -79
  335. package/src/components/QuantityStepper/QuantityStepper.tsx +0 -107
  336. package/src/components/RadioGroup/RadioGroup.module.css +0 -88
  337. package/src/components/RadioGroup/RadioGroup.stories.tsx +0 -69
  338. package/src/components/RadioGroup/RadioGroup.tsx +0 -99
  339. package/src/components/RadioGroup/__tests__/RadioGroup.test.tsx +0 -59
  340. package/src/components/Resizable/Resizable.module.css +0 -70
  341. package/src/components/Resizable/Resizable.stories.tsx +0 -82
  342. package/src/components/Resizable/Resizable.tsx +0 -313
  343. package/src/components/Resizable/__tests__/Resizable.test.tsx +0 -128
  344. package/src/components/ResponsiveSurface/ResponsiveSurface.tsx +0 -24
  345. package/src/components/ResponsiveSurface/__tests__/ResponsiveSurface.test.tsx +0 -73
  346. package/src/components/ScrollArea/ScrollArea.module.css +0 -20
  347. package/src/components/ScrollArea/ScrollArea.stories.tsx +0 -57
  348. package/src/components/ScrollArea/ScrollArea.tsx +0 -27
  349. package/src/components/ScrollArea/__tests__/ScrollArea.test.tsx +0 -39
  350. package/src/components/SegmentedControl/SegmentedControl.module.css +0 -55
  351. package/src/components/SegmentedControl/SegmentedControl.stories.tsx +0 -118
  352. package/src/components/SegmentedControl/SegmentedControl.tsx +0 -56
  353. package/src/components/SegmentedControl/__tests__/SegmentedControl.test.tsx +0 -63
  354. package/src/components/Select/Select.module.css +0 -82
  355. package/src/components/Select/Select.stories.tsx +0 -115
  356. package/src/components/Select/Select.tsx +0 -48
  357. package/src/components/Select/__tests__/Select.test.tsx +0 -59
  358. package/src/components/Sheet/Sheet.module.css +0 -55
  359. package/src/components/Sheet/Sheet.tsx +0 -109
  360. package/src/components/Sheet/__tests__/Sheet.test.tsx +0 -99
  361. package/src/components/Slider/Slider.module.css +0 -95
  362. package/src/components/Slider/Slider.stories.tsx +0 -64
  363. package/src/components/Slider/Slider.tsx +0 -77
  364. package/src/components/Slider/__tests__/Slider.test.tsx +0 -45
  365. package/src/components/Spinner/Spinner.module.css +0 -39
  366. package/src/components/Spinner/Spinner.tsx +0 -23
  367. package/src/components/SplitMethodSelector/SplitMethodSelector.stories.tsx +0 -33
  368. package/src/components/SplitMethodSelector/SplitMethodSelector.tsx +0 -43
  369. package/src/components/SplitPane/SplitPane.module.css +0 -85
  370. package/src/components/SplitPane/SplitPane.stories.tsx +0 -160
  371. package/src/components/SplitPane/SplitPane.tsx +0 -149
  372. package/src/components/SplitPane/__tests__/SplitPane.test.tsx +0 -51
  373. package/src/components/StatusBadge/StatusBadge.module.css +0 -59
  374. package/src/components/StatusBadge/StatusBadge.stories.tsx +0 -74
  375. package/src/components/StatusBadge/StatusBadge.tsx +0 -65
  376. package/src/components/SuggestionInput/SuggestionInput.module.css +0 -103
  377. package/src/components/SuggestionInput/SuggestionInput.stories.tsx +0 -144
  378. package/src/components/SuggestionInput/SuggestionInput.tsx +0 -207
  379. package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.module.css +0 -55
  380. package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.tsx +0 -227
  381. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlide.tsx +0 -35
  382. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideBody.tsx +0 -42
  383. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideFooter.tsx +0 -18
  384. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideHeader.tsx +0 -21
  385. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideParts.module.css +0 -29
  386. package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarousel.test.tsx +0 -133
  387. package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarouselDevGuard.test.tsx +0 -61
  388. package/src/components/SwipableMobileCarousel/context.ts +0 -13
  389. package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.module.css +0 -49
  390. package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.tsx +0 -62
  391. package/src/components/SwipableMobileCarouselDots/__tests__/SwipableMobileCarouselDots.test.tsx +0 -63
  392. package/src/components/SystemBanner/SystemBanner.module.css +0 -69
  393. package/src/components/SystemBanner/SystemBanner.stories.tsx +0 -86
  394. package/src/components/SystemBanner/SystemBanner.tsx +0 -50
  395. package/src/components/SystemBanner/__tests__/SystemBanner.test.tsx +0 -50
  396. package/src/components/Table/Table.module.css +0 -45
  397. package/src/components/Table/Table.stories.tsx +0 -125
  398. package/src/components/Table/Table.tsx +0 -51
  399. package/src/components/Table/__tests__/Table.test.tsx +0 -50
  400. package/src/components/Tabs/Tabs.module.css +0 -56
  401. package/src/components/Tabs/Tabs.stories.tsx +0 -100
  402. package/src/components/Tabs/Tabs.tsx +0 -46
  403. package/src/components/Tabs/__tests__/Tabs.test.tsx +0 -45
  404. package/src/components/TagInput/TagInput.module.css +0 -141
  405. package/src/components/TagInput/TagInput.stories.tsx +0 -109
  406. package/src/components/TagInput/TagInput.tsx +0 -174
  407. package/src/components/TagInput/__tests__/TagInput.test.tsx +0 -69
  408. package/src/components/Textarea/Textarea.module.css +0 -39
  409. package/src/components/Textarea/Textarea.tsx +0 -15
  410. package/src/components/Textarea/__tests__/Textarea.test.tsx +0 -29
  411. package/src/components/Toast/Toast.module.css +0 -132
  412. package/src/components/Toast/Toast.stories.tsx +0 -191
  413. package/src/components/Toast/Toast.tsx +0 -139
  414. package/src/components/Toggle/Toggle.module.css +0 -57
  415. package/src/components/Toggle/Toggle.stories.tsx +0 -95
  416. package/src/components/Toggle/Toggle.tsx +0 -39
  417. package/src/components/Toggle/__tests__/Toggle.test.tsx +0 -47
  418. package/src/components/Tooltip/Tooltip.module.css +0 -56
  419. package/src/components/Tooltip/Tooltip.tsx +0 -30
  420. package/src/components/Typography/Typography.module.css +0 -47
  421. package/src/components/Typography/Typography.stories.tsx +0 -62
  422. package/src/components/Typography/Typography.tsx +0 -30
  423. package/src/components/Typography/__tests__/Typography.test.tsx +0 -34
  424. package/src/css-modules.d.ts +0 -4
  425. package/src/hooks/__tests__/useCarouselRouteSync.test.tsx +0 -126
  426. package/src/hooks/__tests__/useDoubleTap.test.tsx +0 -117
  427. package/src/hooks/__tests__/useIsMobile.test.tsx +0 -80
  428. package/src/hooks/__tests__/useIsOffline.test.tsx +0 -53
  429. package/src/hooks/__tests__/useLongPress.test.tsx +0 -158
  430. package/src/hooks/__tests__/useOfflineTileState.test.tsx +0 -69
  431. package/src/hooks/__tests__/useResponsiveLayout.test.tsx +0 -87
  432. package/src/hooks/__tests__/useSnapCarousel.test.tsx +0 -93
  433. package/src/hooks/__tests__/useSwipeReveal.test.tsx +0 -110
  434. package/src/hooks/index.ts +0 -24
  435. package/src/hooks/useCarouselRouteSync.ts +0 -92
  436. package/src/hooks/useCommitOnEnterOrBlur.ts +0 -39
  437. package/src/hooks/useDoubleTap.ts +0 -91
  438. package/src/hooks/useIsMobile.ts +0 -33
  439. package/src/hooks/useIsOffline.ts +0 -41
  440. package/src/hooks/useLongPress.ts +0 -185
  441. package/src/hooks/useOfflineTileState.ts +0 -45
  442. package/src/hooks/useResponsiveLayout.ts +0 -36
  443. package/src/hooks/useSnapCarousel.ts +0 -79
  444. package/src/hooks/useSwipeReveal.ts +0 -107
  445. package/src/index.ts +0 -251
  446. package/src/motion.ts +0 -78
  447. package/src/overlay-shell.ts +0 -94
  448. package/src/scroll-lock.ts +0 -22
  449. package/src/stories/Avatar.stories.tsx +0 -44
  450. package/src/stories/Card.stories.tsx +0 -35
  451. package/src/stories/Checkbox.stories.tsx +0 -45
  452. package/src/stories/DesignSystemOverview.stories.tsx +0 -3187
  453. package/src/stories/DeviceOnlyGate.stories.tsx +0 -35
  454. package/src/stories/DeviceStorageKeyGate.stories.tsx +0 -57
  455. package/src/stories/DragHandleRow.stories.tsx +0 -41
  456. package/src/stories/EmptyState.stories.tsx +0 -33
  457. package/src/stories/FileDropzone.stories.tsx +0 -69
  458. package/src/stories/FormField.stories.tsx +0 -47
  459. package/src/stories/InteractionHooks.stories.tsx +0 -925
  460. package/src/stories/MobilePatterns.stories.tsx +0 -1536
  461. package/src/stories/NavTabs.stories.tsx +0 -53
  462. package/src/stories/OfflineGate.stories.tsx +0 -42
  463. package/src/stories/PageContainer.stories.tsx +0 -37
  464. package/src/stories/PageHeader.stories.tsx +0 -42
  465. package/src/stories/ResponsiveSurface.stories.tsx +0 -47
  466. package/src/stories/Spinner.stories.tsx +0 -25
  467. package/src/stories/SwipableMobileCarousel.stories.tsx +0 -304
  468. package/src/stories/Textarea.stories.tsx +0 -51
  469. package/src/stories/TokenGallery.stories.tsx +0 -602
  470. package/src/stories/Tooltip.stories.tsx +0 -59
  471. package/src/tokens/primitives.css +0 -157
  472. package/src/tokens/semantic.css +0 -130
  473. package/src/tokens.css +0 -5
@@ -1,1536 +0,0 @@
1
- import { useState } from 'react';
2
- import type { Meta, StoryObj } from '@storybook/react-vite';
3
- import { Button } from '../components/Button/Button';
4
- import { Card } from '../components/Card/Card';
5
- import { Dialog } from '../components/Dialog/Dialog';
6
- import { Drawer } from '../components/Drawer/Drawer';
7
- import { EmptyState } from '../components/EmptyState/EmptyState';
8
- import { FormField } from '../components/FormField/FormField';
9
- import { Icon } from '../components/Icon/Icon';
10
- import { Input } from '../components/Input/Input';
11
- import { MobileFooter } from '../components/MobileFooter/MobileFooter';
12
- import { MobileHeader } from '../components/MobileHeader/MobileHeader';
13
- import { NavTabs } from '../components/NavTabs/NavTabs';
14
- import { PageHeader } from '../components/PageHeader/PageHeader';
15
-
16
- // ---------------------------------------------------------------------------
17
- // Shared primitives
18
- // ---------------------------------------------------------------------------
19
-
20
- const ff = 'var(--sv-font-family)';
21
-
22
- function Heading({ level = 2, children }: { level?: 1 | 2 | 3; children: React.ReactNode }) {
23
- const sizes: Record<number, string> = { 1: '1.75rem', 2: '1.125rem', 3: '0.9375rem' };
24
- const weights: Record<number, number> = { 1: 700, 2: 600, 3: 600 };
25
- const Tag = `h${level}` as 'h1' | 'h2' | 'h3';
26
- return (
27
- <Tag
28
- style={{
29
- fontFamily: ff,
30
- fontSize: sizes[level],
31
- fontWeight: weights[level],
32
- color: 'var(--sv-color-text-primary)',
33
- margin: 0,
34
- lineHeight: 1.3,
35
- }}
36
- >
37
- {children}
38
- </Tag>
39
- );
40
- }
41
-
42
- function Body({ children }: { children: React.ReactNode }) {
43
- return (
44
- <p
45
- style={{
46
- fontFamily: ff,
47
- fontSize: 'var(--sv-font-size-sm)',
48
- color: 'var(--sv-color-text-muted)',
49
- margin: '0.5rem 0 0',
50
- lineHeight: 1.6,
51
- }}
52
- >
53
- {children}
54
- </p>
55
- );
56
- }
57
-
58
- function SectionHeader({ title, subtitle }: { title: string; subtitle?: string }) {
59
- return (
60
- <div
61
- style={{
62
- paddingBottom: 'var(--sv-space-3)',
63
- borderBottom: '2px solid var(--sv-color-accent)',
64
- marginBottom: 'var(--sv-space-5)',
65
- }}
66
- >
67
- <Heading level={2}>{title}</Heading>
68
- {subtitle && <Body>{subtitle}</Body>}
69
- </div>
70
- );
71
- }
72
-
73
- function Callout({
74
- type = 'info',
75
- children,
76
- }: {
77
- type?: 'info' | 'warn' | 'tip';
78
- children: React.ReactNode;
79
- }) {
80
- const map = {
81
- info: {
82
- bg: 'var(--sv-color-info-surface)',
83
- border: 'var(--sv-color-info-border)',
84
- text: 'var(--sv-color-info-text)',
85
- label: 'ℹ',
86
- },
87
- warn: {
88
- bg: 'var(--sv-color-warning-surface)',
89
- border: 'var(--sv-color-warning-border)',
90
- text: 'var(--sv-color-warning-text)',
91
- label: '⚠',
92
- },
93
- tip: {
94
- bg: 'var(--sv-color-success-surface)',
95
- border: 'var(--sv-color-success-border)',
96
- text: 'var(--sv-color-success-text)',
97
- label: '✓',
98
- },
99
- };
100
- const c = map[type];
101
- return (
102
- <div
103
- style={{
104
- background: c.bg,
105
- border: `1px solid ${c.border}`,
106
- borderRadius: 'var(--sv-radius-md)',
107
- padding: 'var(--sv-space-3) var(--sv-space-4)',
108
- display: 'flex',
109
- gap: 'var(--sv-space-2)',
110
- alignItems: 'flex-start',
111
- marginBottom: 'var(--sv-space-4)',
112
- }}
113
- >
114
- <span style={{ color: c.text, fontWeight: 700, flexShrink: 0, fontSize: '0.875rem' }}>
115
- {c.label}
116
- </span>
117
- <p
118
- style={{
119
- fontFamily: ff,
120
- fontSize: 'var(--sv-font-size-sm)',
121
- color: c.text,
122
- margin: 0,
123
- lineHeight: 1.55,
124
- }}
125
- >
126
- {children}
127
- </p>
128
- </div>
129
- );
130
- }
131
-
132
- function BreakpointRow({ px, name, behavior }: { px: number; name: string; behavior: string }) {
133
- return (
134
- <div
135
- style={{
136
- display: 'grid',
137
- gridTemplateColumns: '80px 140px 1fr',
138
- alignItems: 'center',
139
- gap: 'var(--sv-space-3)',
140
- padding: 'var(--sv-space-2) 0',
141
- borderBottom: '1px solid var(--sv-color-border)',
142
- fontFamily: ff,
143
- fontSize: 'var(--sv-font-size-sm)',
144
- }}
145
- >
146
- <code
147
- style={{
148
- fontFamily: 'var(--sv-font-family-mono)',
149
- fontSize: '0.75rem',
150
- background: 'var(--sv-color-surface-sunken)',
151
- padding: '2px 6px',
152
- borderRadius: 'var(--sv-radius-sm)',
153
- color: 'var(--sv-color-text-primary)',
154
- }}
155
- >
156
- {px}px
157
- </code>
158
- <span style={{ color: 'var(--sv-color-text-primary)', fontWeight: 600 }}>{name}</span>
159
- <span style={{ color: 'var(--sv-color-text-muted)' }}>{behavior}</span>
160
- </div>
161
- );
162
- }
163
-
164
- function TouchTargetDemo({ label, size }: { label: string; size: number }) {
165
- return (
166
- <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--sv-space-3)' }}>
167
- <div
168
- style={{
169
- width: size,
170
- height: size,
171
- background:
172
- size >= 44 ? 'var(--sv-color-success-surface)' : 'var(--sv-color-warning-surface)',
173
- border: `1px solid ${size >= 44 ? 'var(--sv-color-success-border)' : 'var(--sv-color-warning-border)'}`,
174
- borderRadius: 'var(--sv-radius-sm)',
175
- display: 'flex',
176
- alignItems: 'center',
177
- justifyContent: 'center',
178
- flexShrink: 0,
179
- }}
180
- >
181
- <span
182
- style={{
183
- fontFamily: 'var(--sv-font-family-mono)',
184
- fontSize: '0.6875rem',
185
- color: size >= 44 ? 'var(--sv-color-success-text)' : 'var(--sv-color-warning-text)',
186
- fontWeight: 600,
187
- }}
188
- >
189
- {size}px
190
- </span>
191
- </div>
192
- <span
193
- style={{
194
- fontFamily: ff,
195
- fontSize: 'var(--sv-font-size-sm)',
196
- color: 'var(--sv-color-text-muted)',
197
- }}
198
- >
199
- {label}
200
- </span>
201
- </div>
202
- );
203
- }
204
-
205
- // ---------------------------------------------------------------------------
206
- // Interactive demos
207
- // ---------------------------------------------------------------------------
208
-
209
- function DialogDemo() {
210
- const [open, setOpen] = useState(false);
211
- return (
212
- <>
213
- <Button size="sm" onClick={() => setOpen(true)}>
214
- Open dialog
215
- </Button>
216
- <Dialog open={open} onClose={() => setOpen(false)} title="Mobile dialog" size="md">
217
- <p
218
- style={{
219
- fontFamily: ff,
220
- fontSize: 'var(--sv-font-size-sm)',
221
- color: 'var(--sv-color-text-muted)',
222
- margin: 0,
223
- }}
224
- >
225
- On screens ≤ 768 px this panel slides up from the bottom as a full-width sheet. On larger
226
- screens it appears centered. Resize the viewport to see the difference.
227
- </p>
228
- <div
229
- style={{ marginTop: 'var(--sv-space-4)', display: 'flex', justifyContent: 'flex-end' }}
230
- >
231
- <Button size="sm" onClick={() => setOpen(false)}>
232
- Close
233
- </Button>
234
- </div>
235
- </Dialog>
236
- </>
237
- );
238
- }
239
-
240
- function DrawerDemo() {
241
- const [open, setOpen] = useState(false);
242
- return (
243
- <>
244
- <Button size="sm" variant="secondary" onClick={() => setOpen(true)}>
245
- Open drawer
246
- </Button>
247
- <Drawer open={open} onClose={() => setOpen(false)} aria-label="Mobile navigation">
248
- <p
249
- style={{
250
- fontFamily: ff,
251
- fontSize: 'var(--sv-font-size-sm)',
252
- color: 'var(--sv-color-text-muted)',
253
- margin: 0,
254
- }}
255
- >
256
- Drawer is always a bottom sheet — it never becomes a side panel. Use it for plugin grids,
257
- navigation menus, or contextual actions on mobile. Respects{' '}
258
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.75rem' }}>
259
- env(safe-area-inset-bottom)
260
- </code>{' '}
261
- for devices with a home indicator.
262
- </p>
263
- </Drawer>
264
- </>
265
- );
266
- }
267
-
268
- function MobileHeaderFooterDemo() {
269
- const [showTitle, setShowTitle] = useState(false);
270
- const [customLogo, setCustomLogo] = useState(true);
271
- const [wideFooter, setWideFooter] = useState(false);
272
- const [appsOpen, setAppsOpen] = useState(false);
273
-
274
- const logo = (
275
- <span
276
- aria-label="Home"
277
- style={{
278
- display: 'flex',
279
- alignItems: 'center',
280
- justifyContent: 'center',
281
- width: 28,
282
- height: 28,
283
- borderRadius: 'var(--sv-radius-sm)',
284
- background: 'var(--sv-color-accent)',
285
- color: 'var(--sv-color-text-on-accent)',
286
- fontWeight: 700,
287
- fontSize: 13,
288
- flexShrink: 0,
289
- }}
290
- >
291
- S
292
- </span>
293
- );
294
-
295
- const leftOne = [
296
- { icon: <Icon name="house" size="md" aria-hidden />, label: 'Home', active: true },
297
- ];
298
- const rightOne = [{ icon: <Icon name="search" size="md" aria-hidden />, label: 'Search' }];
299
- const leftTwo = [
300
- ...leftOne,
301
- { icon: <Icon name="calendar" size="md" aria-hidden />, label: 'Calendar' },
302
- ];
303
- const rightTwo = [
304
- ...rightOne,
305
- { icon: <Icon name="activity" size="md" aria-hidden />, label: 'Activity' },
306
- ];
307
-
308
- return (
309
- <>
310
- <div style={{ display: 'flex', gap: 'var(--sv-space-4)', marginBottom: 'var(--sv-space-4)' }}>
311
- <label
312
- style={{
313
- display: 'flex',
314
- alignItems: 'center',
315
- gap: 'var(--sv-space-2)',
316
- fontSize: 'var(--sv-font-size-sm)',
317
- color: 'var(--sv-color-text-primary)',
318
- cursor: 'pointer',
319
- }}
320
- >
321
- <input
322
- type="checkbox"
323
- checked={showTitle}
324
- onChange={(e) => setShowTitle(e.target.checked)}
325
- />
326
- Header <code>title</code> prop
327
- </label>
328
- <label
329
- style={{
330
- display: 'flex',
331
- alignItems: 'center',
332
- gap: 'var(--sv-space-2)',
333
- fontSize: 'var(--sv-font-size-sm)',
334
- color: 'var(--sv-color-text-primary)',
335
- cursor: 'pointer',
336
- }}
337
- >
338
- <input
339
- type="checkbox"
340
- checked={customLogo}
341
- onChange={(e) => setCustomLogo(e.target.checked)}
342
- />
343
- Custom <code>logo</code> prop (unchecked = default badge)
344
- </label>
345
- <label
346
- style={{
347
- display: 'flex',
348
- alignItems: 'center',
349
- gap: 'var(--sv-space-2)',
350
- fontSize: 'var(--sv-font-size-sm)',
351
- color: 'var(--sv-color-text-primary)',
352
- cursor: 'pointer',
353
- }}
354
- >
355
- <input
356
- type="checkbox"
357
- checked={wideFooter}
358
- onChange={(e) => setWideFooter(e.target.checked)}
359
- />
360
- Footer: 2 + 2 icons
361
- </label>
362
- </div>
363
-
364
- <div
365
- style={{
366
- border: '1px solid var(--sv-color-border)',
367
- borderRadius: 'var(--sv-radius-lg)',
368
- overflow: 'hidden',
369
- }}
370
- >
371
- <MobileHeader
372
- logo={customLogo ? logo : undefined}
373
- title={showTitle ? 'Tasks' : undefined}
374
- bell={
375
- <button
376
- type="button"
377
- aria-label="Notifications"
378
- style={{
379
- display: 'inline-flex',
380
- alignItems: 'center',
381
- justifyContent: 'center',
382
- width: 44,
383
- height: 44,
384
- background: 'none',
385
- border: 'none',
386
- borderRadius: 'var(--sv-radius-md)',
387
- color: 'var(--sv-color-text-muted)',
388
- cursor: 'pointer',
389
- }}
390
- >
391
- <Icon name="bell" size="lg" aria-hidden />
392
- </button>
393
- }
394
- avatarMenu={
395
- <span
396
- aria-label="Jamie Doe"
397
- style={{
398
- display: 'flex',
399
- alignItems: 'center',
400
- justifyContent: 'center',
401
- width: 36,
402
- height: 36,
403
- borderRadius: 'var(--sv-radius-full)',
404
- background: 'var(--sv-color-accent)',
405
- border: '1px solid var(--sv-color-accent)',
406
- color: 'var(--sv-color-text-on-accent)',
407
- fontSize: 'var(--sv-font-size-xs)',
408
- fontWeight: 'var(--sv-font-weight-semibold)',
409
- }}
410
- >
411
- JD
412
- </span>
413
- }
414
- />
415
- <div
416
- style={{
417
- background: 'var(--sv-color-surface-sunken)',
418
- padding: 'var(--sv-space-6) var(--sv-space-4)',
419
- textAlign: 'center',
420
- color: 'var(--sv-color-text-muted)',
421
- fontSize: 'var(--sv-font-size-xs)',
422
- }}
423
- >
424
- Plugin content area
425
- </div>
426
- <MobileFooter
427
- onOpenApps={() => setAppsOpen((v) => !v)}
428
- launcherOpen={appsOpen}
429
- leftIcons={wideFooter ? leftTwo : leftOne}
430
- rightIcons={wideFooter ? rightTwo : rightOne}
431
- />
432
- </div>
433
- </>
434
- );
435
- }
436
-
437
- // ---------------------------------------------------------------------------
438
- // Main story component
439
- // ---------------------------------------------------------------------------
440
-
441
- function MobilePatternsDoc() {
442
- return (
443
- <div
444
- style={{
445
- padding: 'var(--sv-space-6)',
446
- background: 'var(--sv-color-surface)',
447
- minHeight: '100vh',
448
- fontFamily: ff,
449
- maxWidth: 760,
450
- }}
451
- >
452
- <div style={{ marginBottom: 'var(--sv-space-8)' }}>
453
- <Heading level={1}>Mobile Patterns</Heading>
454
- <Body>
455
- Plugin developer reference for building layouts that work at 375 px (mobile), 768 px
456
- (tablet), and 1280 px (desktop). Use the viewport toolbar above to preview each size. The
457
- Sovereign shell handles the chrome (sidebar ↔ bottom nav, drawer) — plugins only need to
458
- own their content area.
459
- </Body>
460
- </div>
461
-
462
- {/* ── Breakpoints ── */}
463
- <section style={{ marginBottom: 'var(--sv-space-10)' }}>
464
- <SectionHeader
465
- title="Breakpoints"
466
- subtitle="Two breakpoints in use across the design system. No token variables — use the pixel values directly in your CSS."
467
- />
468
- <div>
469
- <div
470
- style={{
471
- display: 'grid',
472
- gridTemplateColumns: '80px 140px 1fr',
473
- gap: 'var(--sv-space-3)',
474
- padding: 'var(--sv-space-2) 0',
475
- fontFamily: ff,
476
- fontSize: 'var(--sv-font-size-xs)',
477
- fontWeight: 700,
478
- color: 'var(--sv-color-text-muted)',
479
- letterSpacing: '0.05em',
480
- textTransform: 'uppercase',
481
- borderBottom: '2px solid var(--sv-color-border)',
482
- }}
483
- >
484
- <span>Width</span>
485
- <span>Name</span>
486
- <span>What changes</span>
487
- </div>
488
- <BreakpointRow
489
- px={640}
490
- name="sm"
491
- behavior="NavTabs switches to horizontal scroll (no visible scrollbar)."
492
- />
493
- <BreakpointRow
494
- px={768}
495
- name="md"
496
- behavior="Shell sidebar becomes a bottom nav + drawer. Dialog becomes a full-screen bottom sheet."
497
- />
498
- </div>
499
- <Callout type="info">
500
- Use{' '}
501
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
502
- max-width: 768px
503
- </code>{' '}
504
- for the primary mobile breakpoint in your plugin CSS. Components below this width are
505
- already adapted by the design system.
506
- </Callout>
507
- </section>
508
-
509
- {/* ── Layout conventions ── */}
510
- <section style={{ marginBottom: 'var(--sv-space-10)' }}>
511
- <SectionHeader
512
- title="Layout conventions"
513
- subtitle="Patterns used across all built-in plugins."
514
- />
515
- <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--sv-space-5)' }}>
516
- <Card padding="md">
517
- <Heading level={3}>Constrained content column</Heading>
518
- <Body>
519
- Wrap your page content in a centered column with a readable max-width. All built-in
520
- plugins use this pattern. Content fills full width below 640 px automatically.
521
- </Body>
522
- <pre
523
- style={{
524
- fontFamily: 'var(--sv-font-family-mono)',
525
- fontSize: '0.75rem',
526
- background: 'var(--sv-color-surface-sunken)',
527
- border: '1px solid var(--sv-color-border)',
528
- borderRadius: 'var(--sv-radius-md)',
529
- padding: 'var(--sv-space-3) var(--sv-space-4)',
530
- marginTop: 'var(--sv-space-3)',
531
- overflowX: 'auto',
532
- color: 'var(--sv-color-text-primary)',
533
- lineHeight: 1.6,
534
- }}
535
- >
536
- {`.page {
537
- max-width: 680px; /* or 720px for wider layouts */
538
- margin: 0 auto;
539
- padding: var(--sv-space-6) var(--sv-space-4);
540
- }
541
-
542
- @media (max-width: 768px) {
543
- .page {
544
- padding: var(--sv-space-4);
545
- }
546
- }`}
547
- </pre>
548
- </Card>
549
-
550
- <Card padding="md">
551
- <Heading level={3}>PageHeader on mobile</Heading>
552
- <Body>
553
- PageHeader stacks its action below the title on narrow viewports. Pass a compact
554
- action (a single small Button) — avoid toolbars with multiple controls.
555
- </Body>
556
- <div style={{ marginTop: 'var(--sv-space-4)' }}>
557
- <PageHeader
558
- title="Settings"
559
- description="Manage your plugin preferences."
560
- action={<Button size="sm">Save</Button>}
561
- />
562
- </div>
563
- </Card>
564
-
565
- <Card padding="md">
566
- <Heading level={3}>Form layout</Heading>
567
- <Body>
568
- Forms are always single-column on mobile. Never use multi-column grid layouts for form
569
- fields — they're too narrow below 640 px.
570
- </Body>
571
- <div
572
- style={{
573
- marginTop: 'var(--sv-space-4)',
574
- display: 'flex',
575
- flexDirection: 'column',
576
- gap: 'var(--sv-space-3)',
577
- }}
578
- >
579
- <FormField label="Display name" id="mp-name">
580
- {(field) => <Input {...field} placeholder="Your name" />}
581
- </FormField>
582
- <FormField label="Email" id="mp-email">
583
- {(field) => <Input {...field} type="email" placeholder="you@example.com" />}
584
- </FormField>
585
- <Button type="submit">Save changes</Button>
586
- </div>
587
- </Card>
588
- </div>
589
- </section>
590
-
591
- {/* ── Auto-adapting components ── */}
592
- <section style={{ marginBottom: 'var(--sv-space-10)' }}>
593
- <SectionHeader
594
- title="Auto-adapting components"
595
- subtitle="These components change behaviour at the 640 px or 768 px breakpoint automatically — no extra work needed."
596
- />
597
- <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--sv-space-5)' }}>
598
- <Card padding="md">
599
- <Heading level={3}>NavTabs — horizontal scroll below 640 px</Heading>
600
- <Body>
601
- Tabs overflow horizontally with no visible scrollbar below 640 px. Use the{' '}
602
- <em>Mobile</em> viewport to test overflow.
603
- </Body>
604
- <div style={{ marginTop: 'var(--sv-space-4)' }}>
605
- <NavTabs
606
- items={[
607
- { label: 'Profile', href: '#', active: true },
608
- { label: 'Security', href: '#' },
609
- { label: 'Preferences', href: '#' },
610
- { label: 'Notifications', href: '#' },
611
- { label: 'Billing', href: '#' },
612
- { label: 'Data', href: '#' },
613
- ]}
614
- />
615
- </div>
616
- </Card>
617
-
618
- <Card padding="md">
619
- <Heading level={3}>Dialog — full-screen sheet below 768 px</Heading>
620
- <Body>
621
- Dialog slides up as a bottom sheet on mobile — identical to native OS sheets. No size
622
- prop differences needed; all sizes become full-screen below 768 px.
623
- </Body>
624
- <div style={{ marginTop: 'var(--sv-space-4)' }}>
625
- <DialogDemo />
626
- </div>
627
- </Card>
628
-
629
- <Card padding="md">
630
- <Heading level={3}>Drawer — always a bottom sheet</Heading>
631
- <Body>
632
- Drawer is the component the shell uses for the mobile plugin navigation grid. Your
633
- plugin can use it for contextual navigation menus or action sheets. It always occupies
634
- the bottom of the screen regardless of viewport width.
635
- </Body>
636
- <div style={{ marginTop: 'var(--sv-space-4)' }}>
637
- <DrawerDemo />
638
- </div>
639
- </Card>
640
-
641
- <Card padding="md">
642
- <Heading level={3}>EmptyState — centered, narrow</Heading>
643
- <Body>
644
- EmptyState constrains its content to{' '}
645
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
646
- 36ch
647
- </code>{' '}
648
- and works well at all widths. No extra handling needed.
649
- </Body>
650
- <div style={{ marginTop: 'var(--sv-space-4)' }}>
651
- <EmptyState
652
- icon="search"
653
- heading="Nothing here yet"
654
- description="Add your first item to get started."
655
- action={<Button size="sm">Add item</Button>}
656
- />
657
- </div>
658
- </Card>
659
- </div>
660
- </section>
661
-
662
- {/* ── Shell chrome on mobile ── */}
663
- <section style={{ marginBottom: 'var(--sv-space-10)' }}>
664
- <SectionHeader
665
- title="Shell chrome on mobile"
666
- subtitle="What the runtime shell renders on your behalf at ≤ 768 px. Plugins only own the content area — everything else is provided."
667
- />
668
-
669
- {/* Anatomy diagram */}
670
- <div
671
- style={{
672
- border: '1px solid var(--sv-color-border)',
673
- borderRadius: 'var(--sv-radius-lg)',
674
- overflow: 'hidden',
675
- marginBottom: 'var(--sv-space-5)',
676
- fontFamily: ff,
677
- fontSize: 'var(--sv-font-size-sm)',
678
- }}
679
- >
680
- {/* Header */}
681
- <div
682
- style={{
683
- background: 'var(--sv-color-surface)',
684
- borderBottom: '1px solid var(--sv-color-border)',
685
- padding: 'var(--sv-space-3) var(--sv-space-4)',
686
- display: 'flex',
687
- alignItems: 'center',
688
- justifyContent: 'space-between',
689
- }}
690
- >
691
- <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--sv-space-2)' }}>
692
- <div
693
- style={{
694
- width: 28,
695
- height: 28,
696
- borderRadius: 'var(--sv-radius-sm)',
697
- background: 'var(--sv-color-accent)',
698
- display: 'flex',
699
- alignItems: 'center',
700
- justifyContent: 'center',
701
- color: 'var(--sv-color-text-on-accent)',
702
- fontWeight: 700,
703
- fontSize: 13,
704
- }}
705
- >
706
- S
707
- </div>
708
- <span style={{ fontWeight: 600, color: 'var(--sv-color-text-primary)' }}>
709
- My Instance
710
- </span>
711
- </div>
712
- <div style={{ display: 'flex', gap: 'var(--sv-space-2)', alignItems: 'center' }}>
713
- <div
714
- style={{
715
- width: 28,
716
- height: 28,
717
- borderRadius: 'var(--sv-radius-full)',
718
- background: 'var(--sv-color-surface-sunken)',
719
- border: '1px solid var(--sv-color-border)',
720
- }}
721
- />
722
- <div
723
- style={{
724
- width: 28,
725
- height: 28,
726
- borderRadius: 'var(--sv-radius-full)',
727
- background: 'var(--sv-color-accent)',
728
- display: 'flex',
729
- alignItems: 'center',
730
- justifyContent: 'center',
731
- color: 'var(--sv-color-text-on-accent)',
732
- fontSize: 11,
733
- fontWeight: 700,
734
- }}
735
- >
736
- JD
737
- </div>
738
- </div>
739
- </div>
740
- <div
741
- style={{
742
- background: 'var(--sv-color-info-surface)',
743
- padding: 'var(--sv-space-2) var(--sv-space-4)',
744
- fontSize: 'var(--sv-font-size-xs)',
745
- color: 'var(--sv-color-info-text)',
746
- fontFamily: 'var(--sv-font-family-mono)',
747
- }}
748
- >
749
- ↑ Mobile header — 60 px, sticky · Logo · Instance name · Notification bell · Account
750
- menu
751
- </div>
752
-
753
- {/* Content area */}
754
- <div
755
- style={{
756
- background: 'var(--sv-color-surface-sunken)',
757
- padding: 'var(--sv-space-6) var(--sv-space-4)',
758
- textAlign: 'center',
759
- color: 'var(--sv-color-text-muted)',
760
- minHeight: 100,
761
- display: 'flex',
762
- alignItems: 'center',
763
- justifyContent: 'center',
764
- flexDirection: 'column',
765
- gap: 'var(--sv-space-1)',
766
- }}
767
- >
768
- <span style={{ fontWeight: 600, color: 'var(--sv-color-text-primary)' }}>
769
- Plugin content area
770
- </span>
771
- <span style={{ fontSize: 'var(--sv-font-size-xs)' }}>
772
- Shell sets padding-bottom automatically — no overlap with footer
773
- </span>
774
- </div>
775
- <div
776
- style={{
777
- background: 'var(--sv-color-success-surface)',
778
- padding: 'var(--sv-space-2) var(--sv-space-4)',
779
- fontSize: 'var(--sv-font-size-xs)',
780
- color: 'var(--sv-color-success-text)',
781
- fontFamily: 'var(--sv-font-family-mono)',
782
- }}
783
- >
784
- ↓ Your plugin renders here — full width, auto scroll
785
- </div>
786
-
787
- {/* Footer */}
788
- <div
789
- style={{
790
- background: 'var(--sv-color-surface)',
791
- borderTop: '1px solid var(--sv-color-border)',
792
- padding: 'var(--sv-space-3) var(--sv-space-6)',
793
- display: 'flex',
794
- alignItems: 'center',
795
- justifyContent: 'space-around',
796
- }}
797
- >
798
- {(
799
- [
800
- { iconName: 'house', label: 'Home', note: '→ /' },
801
- { iconName: 'grid-2x2', label: 'Apps', note: 'opens Drawer' },
802
- { iconName: 'search', label: 'Search', note: 'opens overlay' },
803
- ] as { iconName: Parameters<typeof Icon>[0]['name']; label: string; note: string }[]
804
- ).map(({ iconName, label, note }) => (
805
- <div
806
- key={label}
807
- style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2 }}
808
- >
809
- <Icon name={iconName} size="md" aria-hidden />
810
- <span
811
- style={{
812
- fontSize: 'var(--sv-font-size-xs)',
813
- color: 'var(--sv-color-text-primary)',
814
- fontWeight: 600,
815
- }}
816
- >
817
- {label}
818
- </span>
819
- <span
820
- style={{
821
- fontSize: '0.625rem',
822
- color: 'var(--sv-color-text-muted)',
823
- fontFamily: 'var(--sv-font-family-mono)',
824
- }}
825
- >
826
- {note}
827
- </span>
828
- </div>
829
- ))}
830
- </div>
831
- <div
832
- style={{
833
- background: 'var(--sv-color-warning-surface)',
834
- padding: 'var(--sv-space-2) var(--sv-space-4)',
835
- fontSize: 'var(--sv-font-size-xs)',
836
- color: 'var(--sv-color-warning-text)',
837
- fontFamily: 'var(--sv-font-family-mono)',
838
- }}
839
- >
840
- ↑ Mobile footer (MobileNav) — 60 px · Home · Apps · Search
841
- </div>
842
- </div>
843
-
844
- {/* Header details */}
845
- <Card padding="md" style={{ marginBottom: 'var(--sv-space-4)' }}>
846
- <Heading level={3}>Mobile header</Heading>
847
- <Body>
848
- The header is 60 px tall and sticks to the top during scroll. It contains the instance
849
- logo + name (left), notification bell, and the account/console menu (right). Dialogs
850
- automatically slide up from below it — the shell sets{' '}
851
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
852
- --sv-dialog-inset-top
853
- </code>{' '}
854
- so your Dialog calls need no special offset prop.
855
- </Body>
856
- <pre
857
- style={{
858
- fontFamily: 'var(--sv-font-family-mono)',
859
- fontSize: '0.75rem',
860
- background: 'var(--sv-color-surface-sunken)',
861
- border: '1px solid var(--sv-color-border)',
862
- borderRadius: 'var(--sv-radius-md)',
863
- padding: 'var(--sv-space-3) var(--sv-space-4)',
864
- marginTop: 'var(--sv-space-3)',
865
- overflowX: 'auto',
866
- color: 'var(--sv-color-text-primary)',
867
- lineHeight: 1.6,
868
- }}
869
- >
870
- {`/* Shell-exposed CSS variables (read-only for plugins) */
871
- --sv-shell-header-height: 60px /* height of the sticky mobile header */
872
- --sv-shell-footer-height: 60px /* height of the sticky mobile footer */
873
- --sv-dialog-inset-top: 60px /* auto-set — Dialog slides below header */`}
874
- </pre>
875
- </Card>
876
-
877
- {/* Footer details */}
878
- <Card padding="md" style={{ marginBottom: 'var(--sv-space-4)' }}>
879
- <Heading level={3}>Mobile footer (MobileNav)</Heading>
880
- <Body>
881
- A three-button bar fixed to the bottom. Plugins don't render here — the shell adds it
882
- automatically. The plugin content area already has{' '}
883
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
884
- padding-bottom
885
- </code>{' '}
886
- so content never scrolls behind the footer.
887
- </Body>
888
- <div
889
- style={{
890
- marginTop: 'var(--sv-space-3)',
891
- display: 'flex',
892
- flexDirection: 'column',
893
- gap: 'var(--sv-space-2)',
894
- }}
895
- >
896
- {(
897
- [
898
- {
899
- slot: 'Home',
900
- iconName: 'house',
901
- desc: 'Returns to "/" (launcher). Active when pathname is "/" or "/launcher".',
902
- },
903
- {
904
- slot: 'Apps',
905
- iconName: 'grid-2x2',
906
- desc: 'Opens the Launcher Drawer showing all installed plugins as a grid.',
907
- },
908
- {
909
- slot: 'Search',
910
- iconName: 'search',
911
- desc: 'Opens the Search overlay — a full-screen input with a live-filtered plugin list. Closes on back button, Esc, or after navigating to a result.',
912
- },
913
- ] as {
914
- slot: string;
915
- iconName: Parameters<typeof Icon>[0]['name'];
916
- desc: string;
917
- }[]
918
- ).map(({ slot, iconName, desc }) => (
919
- <div
920
- key={slot}
921
- style={{
922
- display: 'flex',
923
- gap: 'var(--sv-space-3)',
924
- alignItems: 'flex-start',
925
- padding: 'var(--sv-space-2) 0',
926
- borderBottom: '1px solid var(--sv-color-border)',
927
- }}
928
- >
929
- <span style={{ flexShrink: 0, display: 'flex' }}>
930
- <Icon name={iconName} size="md" aria-hidden />
931
- </span>
932
- <div>
933
- <span
934
- style={{
935
- fontFamily: ff,
936
- fontWeight: 600,
937
- fontSize: 'var(--sv-font-size-sm)',
938
- color: 'var(--sv-color-text-primary)',
939
- }}
940
- >
941
- {slot}
942
- </span>
943
- <span
944
- style={{
945
- fontFamily: ff,
946
- fontSize: 'var(--sv-font-size-sm)',
947
- color: 'var(--sv-color-text-muted)',
948
- display: 'block',
949
- }}
950
- >
951
- {desc}
952
- </span>
953
- </div>
954
- </div>
955
- ))}
956
- </div>
957
- </Card>
958
-
959
- {/* Launcher Drawer */}
960
- <Card padding="md">
961
- <Heading level={3}>Launcher Drawer</Heading>
962
- <Body>
963
- Opened by the Apps button in the mobile footer. Renders all installed plugins as a
964
- scrollable 2-column icon grid using the{' '}
965
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
966
- Drawer
967
- </code>{' '}
968
- component from{' '}
969
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
970
- @sovereignfs/ui
971
- </code>
972
- . It includes a drag handle, "Apps" title, and close button. Tapping any plugin tile
973
- navigates to its route and closes the drawer. Respects{' '}
974
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
975
- env(safe-area-inset-bottom)
976
- </code>{' '}
977
- via the Drawer component.
978
- </Body>
979
- <div
980
- style={{
981
- marginTop: 'var(--sv-space-4)',
982
- background: 'var(--sv-color-surface)',
983
- border: '1px solid var(--sv-color-border)',
984
- borderRadius: 'var(--sv-radius-lg)',
985
- overflow: 'hidden',
986
- }}
987
- >
988
- <div
989
- style={{
990
- display: 'flex',
991
- justifyContent: 'center',
992
- padding: 'var(--sv-space-2) 0 var(--sv-space-1)',
993
- }}
994
- >
995
- <div
996
- style={{
997
- width: 36,
998
- height: 4,
999
- background: 'var(--sv-color-border-strong)',
1000
- borderRadius: 'var(--sv-radius-full)',
1001
- }}
1002
- />
1003
- </div>
1004
- <div
1005
- style={{
1006
- display: 'flex',
1007
- alignItems: 'center',
1008
- justifyContent: 'space-between',
1009
- padding: 'var(--sv-space-3) var(--sv-space-4)',
1010
- borderBottom: '1px solid var(--sv-color-border)',
1011
- }}
1012
- >
1013
- <span
1014
- style={{
1015
- fontFamily: ff,
1016
- fontWeight: 600,
1017
- fontSize: 'var(--sv-font-size-md)',
1018
- color: 'var(--sv-color-text-primary)',
1019
- }}
1020
- >
1021
- Apps
1022
- </span>
1023
- <div
1024
- style={{
1025
- width: 28,
1026
- height: 28,
1027
- borderRadius: 'var(--sv-radius-sm)',
1028
- background: 'var(--sv-color-surface-sunken)',
1029
- border: '1px solid var(--sv-color-border)',
1030
- display: 'flex',
1031
- alignItems: 'center',
1032
- justifyContent: 'center',
1033
- fontSize: 13,
1034
- color: 'var(--sv-color-text-muted)',
1035
- }}
1036
- >
1037
- ✕
1038
- </div>
1039
- </div>
1040
- <div
1041
- style={{
1042
- display: 'grid',
1043
- gridTemplateColumns: 'repeat(4, 1fr)',
1044
- gap: 'var(--sv-space-2)',
1045
- padding: 'var(--sv-space-4)',
1046
- }}
1047
- >
1048
- {['Console', 'Account', 'Tasks', 'Files'].map((name) => (
1049
- <div
1050
- key={name}
1051
- style={{
1052
- display: 'flex',
1053
- flexDirection: 'column',
1054
- alignItems: 'center',
1055
- gap: 'var(--sv-space-2)',
1056
- }}
1057
- >
1058
- <div
1059
- style={{
1060
- width: 52,
1061
- height: 52,
1062
- borderRadius: 'var(--sv-radius-lg)',
1063
- background: 'var(--sv-color-surface-sunken)',
1064
- border: '1px solid var(--sv-color-border)',
1065
- display: 'flex',
1066
- alignItems: 'center',
1067
- justifyContent: 'center',
1068
- fontWeight: 700,
1069
- fontSize: 16,
1070
- color: 'var(--sv-color-text-muted)',
1071
- }}
1072
- >
1073
- {name.slice(0, 2).toUpperCase()}
1074
- </div>
1075
- <span
1076
- style={{
1077
- fontFamily: ff,
1078
- fontSize: 'var(--sv-font-size-xs)',
1079
- color: 'var(--sv-color-text-muted)',
1080
- textAlign: 'center',
1081
- }}
1082
- >
1083
- {name}
1084
- </span>
1085
- </div>
1086
- ))}
1087
- </div>
1088
- </div>
1089
- <Callout type="info">
1090
- Plugin developers cannot customise the Launcher Drawer. Use{' '}
1091
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
1092
- MobileAppsDrawer
1093
- </code>{' '}
1094
- from{' '}
1095
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
1096
- @sovereignfs/ui
1097
- </code>{' '}
1098
- if you need this exact grid pattern inside your own plugin (e.g. a mobile-only launcher
1099
- for the plugin's own sections) — same tile styling as the shell's own drawer (no header
1100
- row; Drawer's own grab handle/swipe-down/scrim-tap already dismiss it), with the tile
1101
- list supplied by you via its <code>items</code> prop. See the Component Gallery for a
1102
- live demo.
1103
- </Callout>
1104
- </Card>
1105
-
1106
- {/* Search overlay */}
1107
- <Card padding="md" style={{ marginTop: 'var(--sv-space-4)' }}>
1108
- <Heading level={3}>Search overlay</Heading>
1109
- <Body>
1110
- Opened by the Search button in the mobile footer. Renders a full-screen overlay (z-index
1111
- 150, above the footer and Drawer scrim) with an auto-focused search input and a
1112
- live-filtered list of installed plugins. Tapping a result navigates to its route and
1113
- closes the overlay. Scroll lock is applied to{' '}
1114
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
1115
- document.body
1116
- </code>{' '}
1117
- while open.
1118
- </Body>
1119
- {/* Anatomy mock */}
1120
- <div
1121
- style={{
1122
- marginTop: 'var(--sv-space-4)',
1123
- background: 'var(--sv-color-surface)',
1124
- border: '1px solid var(--sv-color-border)',
1125
- borderRadius: 'var(--sv-radius-lg)',
1126
- overflow: 'hidden',
1127
- fontFamily: ff,
1128
- }}
1129
- >
1130
- {/* Header row */}
1131
- <div
1132
- style={{
1133
- display: 'flex',
1134
- alignItems: 'center',
1135
- gap: 'var(--sv-space-2)',
1136
- padding: 'var(--sv-space-3) var(--sv-space-4)',
1137
- borderBottom: '1px solid var(--sv-color-border)',
1138
- }}
1139
- >
1140
- <span
1141
- style={{
1142
- display: 'flex',
1143
- alignItems: 'center',
1144
- justifyContent: 'center',
1145
- width: 36,
1146
- height: 36,
1147
- borderRadius: 'var(--sv-radius-md)',
1148
- background: 'var(--sv-color-surface-sunken)',
1149
- flexShrink: 0,
1150
- }}
1151
- >
1152
- <Icon name="chevron-left" size="md" aria-hidden />
1153
- </span>
1154
- <span
1155
- style={{
1156
- fontWeight: 600,
1157
- fontSize: 'var(--sv-font-size-md)',
1158
- color: 'var(--sv-color-text-primary)',
1159
- }}
1160
- >
1161
- Search
1162
- </span>
1163
- </div>
1164
- {/* Input row */}
1165
- <div style={{ padding: 'var(--sv-space-3) var(--sv-space-4)' }}>
1166
- <div
1167
- style={{
1168
- display: 'flex',
1169
- alignItems: 'center',
1170
- gap: 'var(--sv-space-2)',
1171
- padding: 'var(--sv-space-2) var(--sv-space-3)',
1172
- borderRadius: 'var(--sv-radius-md)',
1173
- border: '1px solid var(--sv-color-accent)',
1174
- background: 'var(--sv-color-surface-sunken)',
1175
- }}
1176
- >
1177
- <Icon name="search" size="sm" aria-hidden />
1178
- <span
1179
- style={{
1180
- color: 'var(--sv-color-text-muted)',
1181
- fontSize: 'var(--sv-font-size-sm)',
1182
- }}
1183
- >
1184
- Search apps…
1185
- </span>
1186
- </div>
1187
- </div>
1188
- {/* Result rows */}
1189
- {['Console', 'Account', 'Launcher'].map((name) => (
1190
- <div
1191
- key={name}
1192
- style={{
1193
- display: 'flex',
1194
- alignItems: 'center',
1195
- gap: 'var(--sv-space-3)',
1196
- padding: 'var(--sv-space-2) var(--sv-space-4)',
1197
- borderBottom: '1px solid var(--sv-color-border)',
1198
- minHeight: 44,
1199
- }}
1200
- >
1201
- <div
1202
- style={{
1203
- width: 40,
1204
- height: 40,
1205
- borderRadius: 'var(--sv-radius-lg)',
1206
- background: 'var(--sv-color-surface-sunken)',
1207
- display: 'flex',
1208
- alignItems: 'center',
1209
- justifyContent: 'center',
1210
- fontWeight: 700,
1211
- fontSize: 12,
1212
- color: 'var(--sv-color-text-muted)',
1213
- flexShrink: 0,
1214
- }}
1215
- >
1216
- {name.slice(0, 2).toUpperCase()}
1217
- </div>
1218
- <span
1219
- style={{
1220
- flex: 1,
1221
- fontSize: 'var(--sv-font-size-sm)',
1222
- fontWeight: 500,
1223
- color: 'var(--sv-color-text-primary)',
1224
- }}
1225
- >
1226
- {name}
1227
- </span>
1228
- <Icon name="chevron-right" size="sm" aria-hidden />
1229
- </div>
1230
- ))}
1231
- </div>
1232
- <Callout type="info">
1233
- The search overlay is shell chrome — plugin developers do not render it. The result set
1234
- is the platform's installed plugin list. If you need in-plugin search, build your own
1235
- input using{' '}
1236
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
1237
- Input
1238
- </code>{' '}
1239
- from{' '}
1240
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
1241
- @sovereignfs/ui
1242
- </code>
1243
- .
1244
- </Callout>
1245
- </Card>
1246
- </section>
1247
-
1248
- {/* ── MobileHeader / MobileFooter components (RFC 0088) ── */}
1249
- <section style={{ marginBottom: 'var(--sv-space-10)' }}>
1250
- <SectionHeader
1251
- title="MobileHeader / MobileFooter components"
1252
- subtitle="The presentational components behind the shell chrome above (RFC 0088). Bell, avatar menu, and the centered launcher are always rendered by the consumer — the header's logo/title (logo defaults to an 'S' badge when omitted) and the footer's leftIcons/rightIcons are the only overridable parts."
1253
- />
1254
- <Card padding="md">
1255
- <MobileHeaderFooterDemo />
1256
- <div style={{ marginTop: 'var(--sv-space-4)' }}>
1257
- <Callout type="info">
1258
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
1259
- leftIcons
1260
- </code>{' '}
1261
- and{' '}
1262
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
1263
- rightIcons
1264
- </code>{' '}
1265
- must have the same length so the launcher stays centered — a mismatch logs a
1266
- dev-mode-only{' '}
1267
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
1268
- console.error
1269
- </code>{' '}
1270
- (never thrown), the same non-fatal guard pattern used by{' '}
1271
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
1272
- SwipableMobileCarousel
1273
- </code>
1274
- .
1275
- </Callout>
1276
- </div>
1277
- </Card>
1278
- </section>
1279
-
1280
- {/* ── Touch targets ── */}
1281
- <section style={{ marginBottom: 'var(--sv-space-10)' }}>
1282
- <SectionHeader
1283
- title="Touch targets"
1284
- subtitle="WCAG 2.5.5 (AAA) recommends 44 × 44 px minimum for interactive elements. All design system buttons meet this by default."
1285
- />
1286
- <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--sv-space-3)' }}>
1287
- <TouchTargetDemo
1288
- label="Button (sm) — 32 px visual, but padded to 44 px tap area via inline-flex"
1289
- size={44}
1290
- />
1291
- <TouchTargetDemo
1292
- label="Button (md) — 36 px visual, but padded to 44 px tap area via inline-flex"
1293
- size={44}
1294
- />
1295
- <TouchTargetDemo
1296
- label="Button (lg) — 40 px visual, but padded to 44 px tap area via inline-flex"
1297
- size={44}
1298
- />
1299
- <TouchTargetDemo
1300
- label="Avoid — 28 px tap area is too small for confident tapping"
1301
- size={28}
1302
- />
1303
- </div>
1304
- <Callout type="tip">
1305
- When building custom interactive elements (icon-only buttons, list rows, toggle rows), add
1306
- explicit{' '}
1307
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
1308
- min-height: 44px
1309
- </code>{' '}
1310
- and enough horizontal padding to make the tap area comfortable.
1311
- </Callout>
1312
- </section>
1313
-
1314
- {/* ── Safe area insets ── */}
1315
- <section style={{ marginBottom: 'var(--sv-space-10)' }}>
1316
- <SectionHeader
1317
- title="Safe area insets"
1318
- subtitle="iOS devices with a home indicator and Android devices with gesture navigation require bottom clearance. The shell handles this for its own chrome; plugins only need to account for it on fixed-bottom elements."
1319
- />
1320
- <Callout type="warn">
1321
- If your plugin has a sticky/fixed footer bar (e.g., a compose toolbar), add{' '}
1322
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
1323
- padding-bottom: env(safe-area-inset-bottom)
1324
- </code>{' '}
1325
- to it. The Drawer already does this.
1326
- </Callout>
1327
- <pre
1328
- style={{
1329
- fontFamily: 'var(--sv-font-family-mono)',
1330
- fontSize: '0.75rem',
1331
- background: 'var(--sv-color-surface-sunken)',
1332
- border: '1px solid var(--sv-color-border)',
1333
- borderRadius: 'var(--sv-radius-md)',
1334
- padding: 'var(--sv-space-3) var(--sv-space-4)',
1335
- overflowX: 'auto',
1336
- color: 'var(--sv-color-text-primary)',
1337
- lineHeight: 1.6,
1338
- }}
1339
- >
1340
- {`.stickyFooter {
1341
- position: sticky;
1342
- bottom: 0;
1343
- /* clears the home indicator bar on iOS/Android */
1344
- padding-bottom: max(var(--sv-space-3), env(safe-area-inset-bottom));
1345
- background: var(--sv-color-surface);
1346
- border-top: 1px solid var(--sv-color-border);
1347
- }`}
1348
- </pre>
1349
- </section>
1350
-
1351
- {/* ── Typography ── */}
1352
- <section style={{ marginBottom: 'var(--sv-space-10)' }}>
1353
- <SectionHeader
1354
- title="Typography on mobile"
1355
- subtitle="All --sv-font-size-* tokens are fixed values — they do not scale with viewport. Use them as-is; they were calibrated for legibility at 375 px."
1356
- />
1357
- <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--sv-space-3)' }}>
1358
- {[
1359
- { token: '--sv-font-size-xs', hint: 'Labels, captions. Minimum for body text.' },
1360
- { token: '--sv-font-size-sm', hint: 'Default body text. Use for most content.' },
1361
- { token: '--sv-font-size-md', hint: 'Slightly larger body. Section intros.' },
1362
- { token: '--sv-font-size-lg', hint: 'Sub-headings, card titles.' },
1363
- { token: '--sv-font-size-xl', hint: 'Page headings (h2 equivalent).' },
1364
- { token: '--sv-font-size-2xl', hint: 'Hero headings. Use sparingly on mobile.' },
1365
- ].map(({ token, hint }) => (
1366
- <div
1367
- key={token}
1368
- style={{
1369
- display: 'flex',
1370
- alignItems: 'baseline',
1371
- gap: 'var(--sv-space-4)',
1372
- borderBottom: '1px solid var(--sv-color-border)',
1373
- padding: 'var(--sv-space-2) 0',
1374
- }}
1375
- >
1376
- <span
1377
- style={{
1378
- fontFamily: ff,
1379
- fontSize: `var(${token})`,
1380
- color: 'var(--sv-color-text-primary)',
1381
- lineHeight: 1.2,
1382
- flexShrink: 0,
1383
- }}
1384
- >
1385
- Aa
1386
- </span>
1387
- <code
1388
- style={{
1389
- fontFamily: 'var(--sv-font-family-mono)',
1390
- fontSize: '0.6875rem',
1391
- color: 'var(--sv-color-text-muted)',
1392
- minWidth: 200,
1393
- }}
1394
- >
1395
- {token}
1396
- </code>
1397
- <span
1398
- style={{
1399
- fontFamily: ff,
1400
- fontSize: 'var(--sv-font-size-xs)',
1401
- color: 'var(--sv-color-text-subtle)',
1402
- }}
1403
- >
1404
- {hint}
1405
- </span>
1406
- </div>
1407
- ))}
1408
- </div>
1409
- <Callout type="info">
1410
- Line lengths should stay between 45–75 characters per line for comfortable reading. The{' '}
1411
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
1412
- max-width: 680px
1413
- </code>{' '}
1414
- container enforces this on wide viewports; on mobile the full width is fine because the
1415
- viewport itself constrains line length.
1416
- </Callout>
1417
- </section>
1418
-
1419
- {/* ── Layout: swipeable route views ── */}
1420
- <section style={{ marginBottom: 'var(--sv-space-10)' }}>
1421
- <SectionHeader
1422
- title="Layout: swipeable route views"
1423
- subtitle="Some views are route-based on web but want a different mobile UX — a swipeable filmstrip of the same sibling routes, not a squeeze of a multi-column layout. This is a recurring pattern (sovereign-tasks, sovereign-shopper) — build it with the shared primitives, not a hand-rolled scroller."
1424
- />
1425
- <p
1426
- style={{
1427
- fontFamily: ff,
1428
- fontSize: 'var(--sv-font-size-sm)',
1429
- color: 'var(--sv-color-text-muted)',
1430
- lineHeight: 1.6,
1431
- }}
1432
- >
1433
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
1434
- SwipableMobileCarousel
1435
- </code>{' '}
1436
- renders full-width slides over a native <code>scroll-snap-type: x</code> container — no
1437
- hand-rolled pointer/touch dragging. Pair it with <code>useCarouselRouteSync</code> to keep
1438
- the active slide synced to the URL, and <code>ResponsiveSurface</code>/
1439
- <code>useResponsiveLayout</code> for the breakpoint fork itself. See the{' '}
1440
- <em>Interaction Hooks</em> story for the full API and live demos of both hooks.
1441
- </p>
1442
- <Callout type="warn">
1443
- <code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
1444
- SwipableMobileCarousel
1445
- </code>{' '}
1446
- owns rendering and mount-window mechanics only — it has no opinion on where slide data
1447
- lives. Don&apos;t aggregate cross-slide data or mount a detail overlay (Sheet, Dialog)
1448
- inside a Slide&apos;s children; mount overlays as a sibling of the carousel instead. See
1449
- docs/design-system.md&apos;s carousel section for the full rationale.
1450
- </Callout>
1451
- </section>
1452
-
1453
- {/* ── Checklist ── */}
1454
- <section style={{ marginBottom: 'var(--sv-space-10)' }}>
1455
- <SectionHeader title="Mobile readiness checklist" />
1456
- {[
1457
- {
1458
- done: true,
1459
- text: 'Content column has a max-width (680–720 px) and horizontal padding (--sv-space-4).',
1460
- },
1461
- { done: true, text: 'Forms are single-column — never two-column grids below 640 px.' },
1462
- { done: true, text: 'All tap targets are ≥ 44 × 44 px.' },
1463
- { done: true, text: 'NavTabs used for page-level navigation (not a custom tab row).' },
1464
- { done: true, text: 'Dialogs use Dialog component (auto full-screen on mobile).' },
1465
- { done: true, text: 'Fixed/sticky footers have env(safe-area-inset-bottom) padding.' },
1466
- { done: true, text: 'No hardcoded pixel widths on content — use max-width instead.' },
1467
- { done: true, text: 'Dark mode tested — use semantic --sv-color-* tokens only.' },
1468
- ].map(({ done, text }, i) => (
1469
- <div
1470
- key={i}
1471
- style={{
1472
- display: 'flex',
1473
- alignItems: 'flex-start',
1474
- gap: 'var(--sv-space-3)',
1475
- padding: 'var(--sv-space-2) 0',
1476
- borderBottom: '1px solid var(--sv-color-border)',
1477
- }}
1478
- >
1479
- <span
1480
- style={{
1481
- color: done ? 'var(--sv-color-success-text)' : 'var(--sv-color-text-subtle)',
1482
- fontWeight: 700,
1483
- flexShrink: 0,
1484
- fontSize: 'var(--sv-font-size-sm)',
1485
- }}
1486
- >
1487
- {done ? '✓' : '○'}
1488
- </span>
1489
- <span
1490
- style={{
1491
- fontFamily: ff,
1492
- fontSize: 'var(--sv-font-size-sm)',
1493
- color: 'var(--sv-color-text-muted)',
1494
- lineHeight: 1.5,
1495
- }}
1496
- >
1497
- {text}
1498
- </span>
1499
- </div>
1500
- ))}
1501
- </section>
1502
- </div>
1503
- );
1504
- }
1505
-
1506
- // ---------------------------------------------------------------------------
1507
-
1508
- const meta = {
1509
- title: 'Overview/Mobile Patterns',
1510
- component: MobilePatternsDoc,
1511
- parameters: {
1512
- layout: 'fullscreen',
1513
- viewport: { defaultViewport: 'mobile' },
1514
- docs: {
1515
- description: {
1516
- component:
1517
- 'Plugin developer reference for mobile-responsive layouts. Switch to the Mobile viewport (375 px) in the toolbar to preview at target size.',
1518
- },
1519
- },
1520
- },
1521
- } satisfies Meta<typeof MobilePatternsDoc>;
1522
-
1523
- export default meta;
1524
- type Story = StoryObj<typeof meta>;
1525
-
1526
- export const MobileView: Story = {
1527
- parameters: { viewport: { defaultViewport: 'mobile' } },
1528
- };
1529
-
1530
- export const TabletView: Story = {
1531
- parameters: { viewport: { defaultViewport: 'tablet' } },
1532
- };
1533
-
1534
- export const DesktopView: Story = {
1535
- parameters: { viewport: { defaultViewport: 'desktop' } },
1536
- };