@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,33 +0,0 @@
1
- .field {
2
- display: flex;
3
- flex-direction: column;
4
- gap: var(--sv-space-1);
5
- /* Every local .field/.fieldGroup class this component replaced set this
6
- explicitly. Without it, a parent with align-items: flex-start (needed so
7
- submit buttons don't stretch full-width) shrink-wraps this wrapper to its
8
- content's intrinsic width instead of filling the available space. */
9
- width: 100%;
10
- }
11
-
12
- .label {
13
- font-size: var(--sv-font-size-sm);
14
- font-weight: var(--sv-font-weight-medium);
15
- color: var(--sv-color-text-primary);
16
- }
17
-
18
- .required {
19
- color: var(--sv-color-error-text);
20
- margin-left: var(--sv-space-1);
21
- }
22
-
23
- .hint {
24
- margin: 0;
25
- font-size: var(--sv-font-size-xs);
26
- color: var(--sv-color-text-muted);
27
- }
28
-
29
- .error {
30
- margin: 0;
31
- font-size: var(--sv-font-size-xs);
32
- color: var(--sv-color-error-text);
33
- }
@@ -1,78 +0,0 @@
1
- import type { ReactNode } from 'react';
2
- import { useId } from 'react';
3
- import styles from './FormField.module.css';
4
-
5
- /** Props to spread onto the field's actual form control. */
6
- export interface FormFieldRenderProps {
7
- id: string;
8
- 'aria-describedby'?: string;
9
- 'aria-invalid'?: boolean;
10
- required?: boolean;
11
- }
12
-
13
- export interface FormFieldProps {
14
- label: string;
15
- hint?: string;
16
- error?: string;
17
- /** Explicit control id. Auto-generated via `useId()` when omitted. */
18
- id?: string;
19
- required?: boolean;
20
- className?: string;
21
- /** Render prop — receives the props that must be spread onto the control
22
- * so the label, hint, and error stay associated with it. */
23
- children: (field: FormFieldRenderProps) => ReactNode;
24
- }
25
-
26
- /**
27
- * FormField — accessible label + hint/error wrapper for a single form control.
28
- * Wires `htmlFor`/`id` and `aria-describedby`/`aria-invalid` onto the control
29
- * itself (via the render-prop `field` object), not a surrounding element, so
30
- * screen readers reliably announce the hint or error as part of the control.
31
- */
32
- export function FormField({
33
- label,
34
- hint,
35
- error,
36
- id,
37
- required = false,
38
- className,
39
- children,
40
- }: FormFieldProps) {
41
- const generatedId = useId();
42
- const fieldId = id ?? generatedId;
43
- const hintId = `${fieldId}-hint`;
44
- const errorId = `${fieldId}-error`;
45
- const describedBy =
46
- [hint && !error && hintId, error && errorId].filter(Boolean).join(' ') || undefined;
47
-
48
- const field: FormFieldRenderProps = {
49
- id: fieldId,
50
- 'aria-describedby': describedBy,
51
- 'aria-invalid': error ? true : undefined,
52
- required: required || undefined,
53
- };
54
-
55
- return (
56
- <div className={[styles.field, className].filter(Boolean).join(' ')}>
57
- <label className={styles.label} htmlFor={fieldId}>
58
- {label}
59
- {required && (
60
- <span className={styles.required} aria-hidden="true">
61
- *
62
- </span>
63
- )}
64
- </label>
65
- {children(field)}
66
- {hint && !error && (
67
- <p id={hintId} className={styles.hint}>
68
- {hint}
69
- </p>
70
- )}
71
- {error && (
72
- <p id={errorId} className={styles.error} role="alert">
73
- {error}
74
- </p>
75
- )}
76
- </div>
77
- );
78
- }
@@ -1,83 +0,0 @@
1
- // @vitest-environment jsdom
2
- import { afterEach, describe, expect, it } from 'vitest';
3
- import { cleanup, render, screen } from '@testing-library/react';
4
- import { FormField } from '../FormField';
5
-
6
- afterEach(cleanup);
7
-
8
- describe('FormField', () => {
9
- it('associates the label with the control via htmlFor/id', () => {
10
- render(
11
- <FormField label="Email" id="email">
12
- {(field) => <input {...field} />}
13
- </FormField>,
14
- );
15
- expect(screen.getByLabelText('Email')).toBeDefined();
16
- });
17
-
18
- it('generates an id when none is provided', () => {
19
- render(<FormField label="Email">{(field) => <input {...field} />}</FormField>);
20
- const input = screen.getByLabelText('Email') as HTMLInputElement;
21
- expect(input.id).toBeTruthy();
22
- });
23
-
24
- it('wires the hint to the control via aria-describedby', () => {
25
- render(
26
- <FormField label="Username" hint="Letters and numbers only." id="username">
27
- {(field) => <input {...field} />}
28
- </FormField>,
29
- );
30
- const input = screen.getByLabelText('Username');
31
- const hint = screen.getByText('Letters and numbers only.');
32
- expect(input.getAttribute('aria-describedby')).toBe(hint.id);
33
- });
34
-
35
- it('wires the error to the control via aria-describedby and marks it invalid', () => {
36
- render(
37
- <FormField label="Password" error="Too short." id="password">
38
- {(field) => <input {...field} />}
39
- </FormField>,
40
- );
41
- const input = screen.getByLabelText('Password');
42
- const error = screen.getByText('Too short.');
43
- expect(input.getAttribute('aria-describedby')).toBe(error.id);
44
- expect(input.getAttribute('aria-invalid')).toBe('true');
45
- });
46
-
47
- it('renders the error with role="alert"', () => {
48
- render(
49
- <FormField label="Password" error="Too short." id="password">
50
- {(field) => <input {...field} />}
51
- </FormField>,
52
- );
53
- expect(screen.getByRole('alert').textContent).toBe('Too short.');
54
- });
55
-
56
- it('hides the hint when an error is present', () => {
57
- render(
58
- <FormField label="Password" hint="At least 8 characters." error="Too short." id="password">
59
- {(field) => <input {...field} />}
60
- </FormField>,
61
- );
62
- expect(screen.queryByText('At least 8 characters.')).toBeNull();
63
- });
64
-
65
- it('shows a required indicator and sets required on the control', () => {
66
- render(
67
- <FormField label="Full name" required id="name">
68
- {(field) => <input {...field} />}
69
- </FormField>,
70
- );
71
- const input = screen.getByLabelText(/Full name/) as HTMLInputElement;
72
- expect(input.required).toBe(true);
73
- });
74
-
75
- it('does not set aria-describedby when there is no hint or error', () => {
76
- render(
77
- <FormField label="Nickname" id="nickname">
78
- {(field) => <input {...field} />}
79
- </FormField>,
80
- );
81
- expect(screen.getByLabelText('Nickname').hasAttribute('aria-describedby')).toBe(false);
82
- });
83
- });
@@ -1,3 +0,0 @@
1
- .root {
2
- display: inline-flex;
3
- }
@@ -1,66 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { HoverCard } from './HoverCard';
3
- import { Avatar } from '../Avatar/Avatar';
4
-
5
- const meta = {
6
- title: 'Components/HoverCard',
7
- component: HoverCard,
8
- parameters: {
9
- layout: 'padded',
10
- docs: {
11
- description: {
12
- component:
13
- 'Hover-triggered popover on desktop, tap-to-toggle on touch. Built on Popover for positioning/collision detection — this only adds hover-intent timing and the touch fallback. Opens on keyboard focus too.',
14
- },
15
- },
16
- },
17
- args: { trigger: <span />, children: null, 'aria-label': 'Preview' },
18
- } satisfies Meta<typeof HoverCard>;
19
-
20
- export default meta;
21
- type Story = StoryObj<typeof meta>;
22
-
23
- // ---------------------------------------------------------------------------
24
-
25
- export const Default: Story = {
26
- render: () => (
27
- <HoverCard
28
- aria-label="User preview"
29
- trigger={
30
- <button
31
- type="button"
32
- style={{ background: 'none', border: 'none', padding: 0, cursor: 'pointer' }}
33
- >
34
- <Avatar name="Jane Smith" size="sm" />
35
- </button>
36
- }
37
- >
38
- <div style={{ padding: 'var(--sv-space-3)', fontSize: 14 }}>
39
- <div style={{ fontWeight: 600 }}>Jane Smith</div>
40
- <div style={{ color: 'var(--sv-color-text-muted)' }}>jane@example.com</div>
41
- </div>
42
- </HoverCard>
43
- ),
44
- };
45
-
46
- export const WithLink: Story = {
47
- render: () => (
48
- <p style={{ fontSize: 14 }}>
49
- Assigned to{' '}
50
- <HoverCard
51
- aria-label="Assignee preview"
52
- trigger={
53
- <a href="/console/users/jane-smith" style={{ color: 'var(--sv-color-text-primary)' }}>
54
- Jane Smith
55
- </a>
56
- }
57
- >
58
- <div style={{ padding: 'var(--sv-space-3)', fontSize: 14 }}>
59
- <div style={{ fontWeight: 600 }}>Jane Smith</div>
60
- <div style={{ color: 'var(--sv-color-text-muted)' }}>12 tasks completed this month</div>
61
- </div>
62
- </HoverCard>
63
- .
64
- </p>
65
- ),
66
- };
@@ -1,111 +0,0 @@
1
- 'use client';
2
-
3
- import { cloneElement, useEffect, useRef, useState } from 'react';
4
- import type { ReactElement, ReactNode } from 'react';
5
- import { Popover } from '../Popover/Popover';
6
- import styles from './HoverCard.module.css';
7
-
8
- export interface HoverCardProps {
9
- /** The element that opens the card — cloned with hover/focus handlers
10
- * (desktop) or a tap handler (touch). */
11
- trigger: ReactElement;
12
- children: ReactNode;
13
- align?: 'left' | 'right';
14
- width?: number | 'trigger';
15
- 'aria-label': string;
16
- }
17
-
18
- const OPEN_DELAY_MS = 400;
19
- const CLOSE_DELAY_MS = 150;
20
-
21
- /** Whether the primary pointer can hover. Distinct from viewport-width-based
22
- * mobile detection: a touchscreen laptop with a mouse as its primary
23
- * pointer should still get real hover behavior, and a large-viewport tablet
24
- * with only touch should not — viewport width doesn't capture that, `(hover:
25
- * hover)` does, matching the pointer-based gating this design system already
26
- * uses in CSS (e.g. Button/Checkbox's touch-target expansion). Defaults to
27
- * `true`: this only changes which event handlers get attached, not the
28
- * initial DOM shape, so there's no hydration-mismatch risk to guard against
29
- * the way `useIsMobile` does. */
30
- function useHoverCapable(): boolean {
31
- const [hoverCapable, setHoverCapable] = useState(true);
32
-
33
- useEffect(() => {
34
- const mql = window.matchMedia('(hover: hover)');
35
- setHoverCapable(mql.matches);
36
- function handleChange(e: MediaQueryListEvent) {
37
- setHoverCapable(e.matches);
38
- }
39
- mql.addEventListener('change', handleChange);
40
- return () => mql.removeEventListener('change', handleChange);
41
- }, []);
42
-
43
- return hoverCapable;
44
- }
45
-
46
- /**
47
- * HoverCard — hover-triggered popover on desktop, tap-to-toggle on touch.
48
- *
49
- * Built on `Popover` rather than reimplementing positioning/collision
50
- * detection: this only adds the hover-intent open/close timing (avoids
51
- * flicker on an accidental mouse pass-over, and gives the pointer time to
52
- * travel from the trigger to the card itself across the gap between them)
53
- * and the touch fallback. Also opens on keyboard focus and closes on blur —
54
- * a hover-only trigger is a WCAG failure, since keyboard users can't hover.
55
- */
56
- export function HoverCard({
57
- trigger,
58
- children,
59
- align,
60
- width,
61
- 'aria-label': ariaLabel,
62
- }: HoverCardProps) {
63
- const [open, setOpen] = useState(false);
64
- const hoverCapable = useHoverCapable();
65
- const openTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
66
- const closeTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
67
-
68
- function scheduleOpen() {
69
- clearTimeout(closeTimer.current);
70
- openTimer.current = setTimeout(() => setOpen(true), OPEN_DELAY_MS);
71
- }
72
-
73
- function scheduleClose() {
74
- clearTimeout(openTimer.current);
75
- closeTimer.current = setTimeout(() => setOpen(false), CLOSE_DELAY_MS);
76
- }
77
-
78
- function cancelScheduledClose() {
79
- clearTimeout(closeTimer.current);
80
- }
81
-
82
- const triggerHandlers = hoverCapable
83
- ? {
84
- onMouseEnter: scheduleOpen,
85
- onMouseLeave: scheduleClose,
86
- onFocus: () => setOpen(true),
87
- onBlur: scheduleClose,
88
- }
89
- : { onClick: () => setOpen((o) => !o) };
90
-
91
- const wrappedTrigger = cloneElement(trigger, triggerHandlers);
92
-
93
- return (
94
- <div
95
- className={styles.root}
96
- onMouseEnter={hoverCapable ? cancelScheduledClose : undefined}
97
- onMouseLeave={hoverCapable ? scheduleClose : undefined}
98
- >
99
- <Popover
100
- trigger={wrappedTrigger}
101
- open={open}
102
- onClose={() => setOpen(false)}
103
- align={align}
104
- width={width}
105
- aria-label={ariaLabel}
106
- >
107
- {children}
108
- </Popover>
109
- </div>
110
- );
111
- }
@@ -1,99 +0,0 @@
1
- // @vitest-environment jsdom
2
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
- import { act, cleanup, render, screen, fireEvent } from '@testing-library/react';
4
- import { HoverCard } from '../HoverCard';
5
-
6
- let matches = true;
7
-
8
- function installMatchMedia() {
9
- vi.stubGlobal(
10
- 'matchMedia',
11
- vi.fn().mockImplementation((query: string) => ({
12
- matches,
13
- media: query,
14
- addEventListener: vi.fn(),
15
- removeEventListener: vi.fn(),
16
- addListener: vi.fn(),
17
- removeListener: vi.fn(),
18
- dispatchEvent: vi.fn(),
19
- })),
20
- );
21
- }
22
-
23
- describe('HoverCard', () => {
24
- beforeEach(() => {
25
- matches = true; // hover-capable by default
26
- installMatchMedia();
27
- vi.useFakeTimers();
28
- });
29
-
30
- afterEach(() => {
31
- vi.runOnlyPendingTimers();
32
- vi.useRealTimers();
33
- vi.unstubAllGlobals();
34
- cleanup();
35
- });
36
-
37
- it('does not open immediately on mouse enter (hover-intent delay)', () => {
38
- render(
39
- <HoverCard trigger={<button type="button">Profile</button>} aria-label="Profile preview">
40
- Card content
41
- </HoverCard>,
42
- );
43
- fireEvent.mouseEnter(screen.getByRole('button', { name: 'Profile' }));
44
- expect(screen.queryByText('Card content')).toBeNull();
45
- });
46
-
47
- it('opens after the hover-intent delay elapses', () => {
48
- render(
49
- <HoverCard trigger={<button type="button">Profile</button>} aria-label="Profile preview">
50
- Card content
51
- </HoverCard>,
52
- );
53
- fireEvent.mouseEnter(screen.getByRole('button', { name: 'Profile' }));
54
- act(() => {
55
- vi.advanceTimersByTime(500);
56
- });
57
- expect(screen.getByText('Card content')).toBeDefined();
58
- });
59
-
60
- it('cancels the open timer if the pointer leaves before the delay elapses', () => {
61
- render(
62
- <HoverCard trigger={<button type="button">Profile</button>} aria-label="Profile preview">
63
- Card content
64
- </HoverCard>,
65
- );
66
- const trigger = screen.getByRole('button', { name: 'Profile' });
67
- fireEvent.mouseEnter(trigger);
68
- fireEvent.mouseLeave(trigger);
69
- act(() => {
70
- vi.advanceTimersByTime(1000);
71
- });
72
- expect(screen.queryByText('Card content')).toBeNull();
73
- });
74
-
75
- it('opens immediately on keyboard focus, for keyboard accessibility', () => {
76
- render(
77
- <HoverCard trigger={<button type="button">Profile</button>} aria-label="Profile preview">
78
- Card content
79
- </HoverCard>,
80
- );
81
- fireEvent.focus(screen.getByRole('button', { name: 'Profile' }));
82
- expect(screen.getByText('Card content')).toBeDefined();
83
- });
84
-
85
- it('falls back to tap-to-toggle when the pointer cannot hover', () => {
86
- matches = false;
87
- installMatchMedia();
88
- render(
89
- <HoverCard trigger={<button type="button">Profile</button>} aria-label="Profile preview">
90
- Card content
91
- </HoverCard>,
92
- );
93
- const trigger = screen.getByRole('button', { name: 'Profile' });
94
- fireEvent.click(trigger);
95
- expect(screen.getByText('Card content')).toBeDefined();
96
- fireEvent.click(trigger);
97
- expect(screen.queryByText('Card content')).toBeNull();
98
- });
99
- });
@@ -1,27 +0,0 @@
1
- .root {
2
- display: inline-block;
3
- flex-shrink: 0;
4
- /* Prevent the inline-block from adding line-height whitespace below the SVG. */
5
- line-height: 0;
6
- /* Color follows the surrounding text via currentColor — no token needed. */
7
- }
8
-
9
- .xs {
10
- width: var(--sv-icon-size-xs);
11
- height: var(--sv-icon-size-xs);
12
- }
13
-
14
- .sm {
15
- width: var(--sv-icon-size-sm);
16
- height: var(--sv-icon-size-sm);
17
- }
18
-
19
- .md {
20
- width: var(--sv-icon-size-md);
21
- height: var(--sv-icon-size-md);
22
- }
23
-
24
- .lg {
25
- width: var(--sv-icon-size-lg);
26
- height: var(--sv-icon-size-lg);
27
- }
@@ -1,95 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { ICONS, type IconName } from './icons';
3
- import { Icon } from './Icon';
4
-
5
- const ALL_NAMES = Object.keys(ICONS) as IconName[];
6
-
7
- const meta = {
8
- title: 'Components/Icon',
9
- component: Icon,
10
- parameters: {
11
- layout: 'centered',
12
- docs: {
13
- description: {
14
- component:
15
- 'SVG icon primitive. Icons are either **decorative** (`aria-hidden`) or **meaningful** (`aria-label`). Size is controlled by `--sv-icon-size-*` tokens.',
16
- },
17
- },
18
- },
19
- argTypes: {
20
- name: { control: 'select', options: ALL_NAMES },
21
- size: { control: 'select', options: ['xs', 'sm', 'md', 'lg'] },
22
- },
23
- } satisfies Meta<typeof Icon>;
24
-
25
- export default meta;
26
- type Story = StoryObj<typeof meta>;
27
-
28
- // ---------------------------------------------------------------------------
29
-
30
- export const Decorative: Story = {
31
- args: { name: 'house', size: 'md', 'aria-hidden': true },
32
- };
33
-
34
- export const Meaningful: Story = {
35
- args: { name: 'settings', size: 'md', 'aria-label': 'Open settings' },
36
- };
37
-
38
- export const SizeExtraSmall: Story = { args: { name: 'bell', size: 'xs', 'aria-hidden': true } };
39
- export const SizeSmall: Story = { args: { name: 'bell', size: 'sm', 'aria-hidden': true } };
40
- export const SizeMedium: Story = { args: { name: 'bell', size: 'md', 'aria-hidden': true } };
41
- export const SizeLarge: Story = { args: { name: 'bell', size: 'lg', 'aria-hidden': true } };
42
-
43
- /** Full icon grid — every name across all four sizes. */
44
- export const AllIcons: Story = {
45
- parameters: { layout: 'padded' },
46
- args: { name: 'house', 'aria-hidden': true },
47
- render: (_args) => (
48
- <div>
49
- {(['xs', 'sm', 'md', 'lg'] as const).map((sz) => (
50
- <section key={sz} style={{ marginBottom: 32 }}>
51
- <h3
52
- style={{
53
- fontFamily: 'system-ui',
54
- fontSize: 13,
55
- fontWeight: 600,
56
- color: 'var(--sv-color-text-muted)',
57
- textTransform: 'uppercase',
58
- letterSpacing: '0.08em',
59
- marginBottom: 12,
60
- }}
61
- >
62
- Size: {sz}
63
- </h3>
64
- <div style={{ display: 'flex', flexWrap: 'wrap', gap: 16 }}>
65
- {ALL_NAMES.map((name) => (
66
- <div
67
- key={name}
68
- style={{
69
- display: 'flex',
70
- flexDirection: 'column',
71
- alignItems: 'center',
72
- gap: 4,
73
- width: 64,
74
- }}
75
- >
76
- <Icon name={name} size={sz} aria-hidden />
77
- <span
78
- style={{
79
- fontFamily: 'monospace',
80
- fontSize: 10,
81
- color: 'var(--sv-color-text-muted)',
82
- textAlign: 'center',
83
- wordBreak: 'break-word',
84
- }}
85
- >
86
- {name}
87
- </span>
88
- </div>
89
- ))}
90
- </div>
91
- </section>
92
- ))}
93
- </div>
94
- ),
95
- };
@@ -1,43 +0,0 @@
1
- import { ICONS, type IconName } from './icons';
2
- import styles from './Icon.module.css';
3
-
4
- export type { IconName };
5
-
6
- /** Decorative icon — visually meaningful but described by surrounding text. */
7
- type DecorativeProps = { 'aria-hidden': true; 'aria-label'?: never };
8
- /** Meaningful icon — standalone affordance that requires a label for screen readers. */
9
- type MeaningfulProps = { 'aria-label': string; 'aria-hidden'?: never };
10
-
11
- export interface IconProps {
12
- /** Name of the icon from the curated Sovereign set. */
13
- name: IconName;
14
- /** Visual size. Defaults to `"md"` (20 px). */
15
- size?: 'xs' | 'sm' | 'md' | 'lg';
16
- /** Additional CSS class applied to the SVG element. */
17
- className?: string;
18
- }
19
-
20
- /**
21
- * `<Icon>` — the Sovereign design system icon primitive.
22
- *
23
- * Icons are either **decorative** (described by surrounding text — pass `aria-hidden`)
24
- * or **meaningful** (standalone affordance — pass `aria-label`):
25
- *
26
- * ```tsx
27
- * <Icon name="house" aria-hidden /> // decorative
28
- * <Icon name="log-out" aria-label="Sign out" /> // meaningful
29
- * ```
30
- *
31
- * Color follows `currentColor` so an icon automatically inherits the text color
32
- * of its container and recolors with theme changes — no extra CSS required.
33
- */
34
- export function Icon({
35
- name,
36
- size = 'md',
37
- className,
38
- ...aria
39
- }: IconProps & (DecorativeProps | MeaningfulProps)) {
40
- const Svg = ICONS[name];
41
- const classes = [styles.root, styles[size], className].filter(Boolean).join(' ');
42
- return <Svg className={classes} role={aria['aria-label'] ? 'img' : undefined} {...aria} />;
43
- }
@@ -1,40 +0,0 @@
1
- // @vitest-environment jsdom
2
- import { render, screen } from '@testing-library/react';
3
- import { describe, expect, it } from 'vitest';
4
- import { Icon } from '../Icon';
5
-
6
- describe('Icon', () => {
7
- it('renders an SVG element', () => {
8
- const { container } = render(<Icon name="house" aria-hidden />);
9
- expect(container.querySelector('svg')).toBeTruthy();
10
- });
11
-
12
- it('applies aria-hidden for decorative icons', () => {
13
- const { container } = render(<Icon name="house" aria-hidden />);
14
- expect(container.querySelector('svg')?.getAttribute('aria-hidden')).toBe('true');
15
- });
16
-
17
- it('applies aria-label and role="img" for meaningful icons', () => {
18
- render(<Icon name="log-out" aria-label="Sign out" />);
19
- const svg = screen.getByRole('img', { name: 'Sign out' });
20
- expect(svg).toBeTruthy();
21
- });
22
-
23
- it('defaults to md size', () => {
24
- const { container } = render(<Icon name="settings" aria-hidden />);
25
- const svg = container.querySelector('svg');
26
- // SVG className is an SVGAnimatedString in the DOM — use getAttribute('class').
27
- // CSS Modules are identity-mapped in the test environment, so 'md' is literal.
28
- expect(svg?.getAttribute('class')).toContain('md');
29
- });
30
-
31
- it('applies the requested size class', () => {
32
- const { container } = render(<Icon name="settings" size="lg" aria-hidden />);
33
- expect(container.querySelector('svg')?.getAttribute('class')).toContain('lg');
34
- });
35
-
36
- it('merges an additional className', () => {
37
- const { container } = render(<Icon name="x" aria-hidden className="my-class" />);
38
- expect(container.querySelector('svg')?.getAttribute('class')).toContain('my-class');
39
- });
40
- });