zabi-components 7.0.2 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (433) hide show
  1. package/CHANGELOG.md +1924 -0
  2. package/README.md +287 -11
  3. package/THEME.md +149 -332
  4. package/THEMING.md +1329 -163
  5. package/dist/atoms/ActionPanel.svelte +24 -9
  6. package/dist/atoms/ActionPanel.svelte.d.ts +2 -0
  7. package/dist/atoms/Avatar.svelte +104 -0
  8. package/dist/atoms/Avatar.svelte.d.ts +34 -0
  9. package/dist/atoms/Badge.svelte +124 -58
  10. package/dist/atoms/Badge.svelte.d.ts +17 -5
  11. package/dist/atoms/Button.svelte +212 -62
  12. package/dist/atoms/Button.svelte.d.ts +33 -2
  13. package/dist/atoms/Card.svelte +31 -28
  14. package/dist/atoms/Card.svelte.d.ts +5 -2
  15. package/dist/atoms/CardContent.svelte +10 -2
  16. package/dist/atoms/CardContent.svelte.d.ts +2 -0
  17. package/dist/atoms/CardFooter.svelte +10 -2
  18. package/dist/atoms/CardFooter.svelte.d.ts +2 -0
  19. package/dist/atoms/CardHeader.svelte +26 -6
  20. package/dist/atoms/CardHeader.svelte.d.ts +2 -0
  21. package/dist/atoms/Checkbox.svelte +52 -21
  22. package/dist/atoms/Checkbox.svelte.d.ts +6 -2
  23. package/dist/atoms/CodeBlock.svelte +35 -9
  24. package/dist/atoms/CodeBlock.svelte.d.ts +13 -3
  25. package/dist/atoms/ColorPicker.svelte +272 -52
  26. package/dist/atoms/ColorPicker.svelte.d.ts +6 -1
  27. package/dist/atoms/Container.svelte +17 -10
  28. package/dist/atoms/DateField.svelte +107 -0
  29. package/dist/atoms/DateField.svelte.d.ts +53 -0
  30. package/dist/atoms/Divider.svelte +4 -4
  31. package/dist/atoms/FeatureCard.svelte +4 -3
  32. package/dist/atoms/FieldMessages.svelte +69 -0
  33. package/dist/atoms/FieldMessages.svelte.d.ts +23 -0
  34. package/dist/atoms/FloatingActionButton.svelte +174 -0
  35. package/dist/atoms/FloatingActionButton.svelte.d.ts +51 -0
  36. package/dist/atoms/Heading.svelte +93 -32
  37. package/dist/atoms/Heading.svelte.d.ts +24 -4
  38. package/dist/atoms/IconButton.svelte +242 -53
  39. package/dist/atoms/IconButton.svelte.d.ts +49 -4
  40. package/dist/atoms/Input.svelte +267 -74
  41. package/dist/atoms/Input.svelte.d.ts +32 -2
  42. package/dist/atoms/List.svelte +13 -2
  43. package/dist/atoms/List.svelte.d.ts +2 -0
  44. package/dist/atoms/ListItem.svelte +44 -11
  45. package/dist/atoms/ListItem.svelte.d.ts +5 -0
  46. package/dist/atoms/ListItemLeading.svelte +4 -2
  47. package/dist/atoms/ListItemLeading.svelte.d.ts +1 -1
  48. package/dist/atoms/OptimizedImage.svelte +11 -2
  49. package/dist/atoms/OptimizedImage.svelte.d.ts +2 -0
  50. package/dist/atoms/Progress.svelte +31 -9
  51. package/dist/atoms/Progress.svelte.d.ts +6 -0
  52. package/dist/atoms/Radio.svelte +23 -3
  53. package/dist/atoms/Radio.svelte.d.ts +6 -2
  54. package/dist/atoms/Rating.svelte +494 -0
  55. package/dist/atoms/Rating.svelte.d.ts +56 -0
  56. package/dist/atoms/Select.svelte +475 -135
  57. package/dist/atoms/Select.svelte.d.ts +52 -2
  58. package/dist/atoms/SelectionControl.svelte +99 -18
  59. package/dist/atoms/SelectionControl.svelte.d.ts +6 -3
  60. package/dist/atoms/Skeleton.svelte +5 -3
  61. package/dist/atoms/Slider.svelte +309 -0
  62. package/dist/atoms/Slider.svelte.d.ts +40 -0
  63. package/dist/atoms/Spinner.svelte +57 -0
  64. package/dist/atoms/Spinner.svelte.d.ts +19 -0
  65. package/dist/atoms/Table.svelte +152 -6
  66. package/dist/atoms/Table.svelte.d.ts +15 -3
  67. package/dist/atoms/TemporalField.svelte +140 -0
  68. package/dist/atoms/TemporalField.svelte.d.ts +32 -0
  69. package/dist/atoms/Text.svelte +56 -15
  70. package/dist/atoms/Text.svelte.d.ts +13 -4
  71. package/dist/atoms/Textarea.svelte +57 -45
  72. package/dist/atoms/Textarea.svelte.d.ts +14 -2
  73. package/dist/atoms/ThemeToggle.svelte +238 -56
  74. package/dist/atoms/ThemeToggle.svelte.d.ts +28 -3
  75. package/dist/atoms/TimeField.svelte +108 -0
  76. package/dist/atoms/TimeField.svelte.d.ts +54 -0
  77. package/dist/atoms/Toast.svelte +25 -10
  78. package/dist/atoms/Toast.svelte.d.ts +2 -0
  79. package/dist/atoms/Toggle.svelte +60 -19
  80. package/dist/atoms/Toggle.svelte.d.ts +18 -2
  81. package/dist/atoms/Tooltip.svelte +583 -40
  82. package/dist/atoms/Tooltip.svelte.d.ts +26 -0
  83. package/dist/atoms/ZabiStringsProvider.svelte +45 -0
  84. package/dist/atoms/ZabiStringsProvider.svelte.d.ts +27 -0
  85. package/dist/atoms/index.d.ts +17 -0
  86. package/dist/atoms/index.js +12 -0
  87. package/dist/atoms/selection-control.styles.d.ts +36 -5
  88. package/dist/atoms/selection-control.styles.js +40 -9
  89. package/dist/components/atoms/index.d.ts +17 -0
  90. package/dist/components/atoms/index.d.ts.map +1 -1
  91. package/dist/components/atoms/selection-control.styles.d.ts +36 -5
  92. package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
  93. package/dist/components/index.d.ts +83 -3
  94. package/dist/components/index.d.ts.map +1 -1
  95. package/dist/components/molecules/index.d.ts +46 -0
  96. package/dist/components/molecules/index.d.ts.map +1 -1
  97. package/dist/components/molecules/toast-store.d.ts +53 -7
  98. package/dist/components/molecules/toast-store.d.ts.map +1 -1
  99. package/dist/components/organisms/index.d.ts +6 -0
  100. package/dist/components/organisms/index.d.ts.map +1 -1
  101. package/dist/components/types/variants.d.ts +15 -1
  102. package/dist/components/types/variants.d.ts.map +1 -1
  103. package/dist/components/util/app-shell.d.ts +28 -0
  104. package/dist/components/util/app-shell.d.ts.map +1 -0
  105. package/dist/components/util/avatar.d.ts +36 -0
  106. package/dist/components/util/avatar.d.ts.map +1 -0
  107. package/dist/components/util/bottom-sheet.d.ts +30 -0
  108. package/dist/components/util/bottom-sheet.d.ts.map +1 -0
  109. package/dist/components/util/bottom-tab-bar.d.ts +35 -0
  110. package/dist/components/util/bottom-tab-bar.d.ts.map +1 -0
  111. package/dist/components/util/calendar.d.ts +56 -0
  112. package/dist/components/util/calendar.d.ts.map +1 -0
  113. package/dist/components/util/cn.d.ts +8 -0
  114. package/dist/components/util/cn.d.ts.map +1 -0
  115. package/dist/components/util/collapsible.d.ts +58 -0
  116. package/dist/components/util/collapsible.d.ts.map +1 -0
  117. package/dist/components/util/confirm-dialog.d.ts +12 -0
  118. package/dist/components/util/confirm-dialog.d.ts.map +1 -0
  119. package/dist/components/util/date.d.ts +96 -0
  120. package/dist/components/util/date.d.ts.map +1 -0
  121. package/dist/components/util/drawer.d.ts +13 -0
  122. package/dist/components/util/drawer.d.ts.map +1 -0
  123. package/dist/components/util/dropdown.d.ts +47 -0
  124. package/dist/components/util/dropdown.d.ts.map +1 -0
  125. package/dist/components/util/field.d.ts +39 -0
  126. package/dist/components/util/field.d.ts.map +1 -0
  127. package/dist/components/util/fit-in-viewport.d.ts +201 -0
  128. package/dist/components/util/fit-in-viewport.d.ts.map +1 -0
  129. package/dist/components/util/focus-utils.d.ts +75 -2
  130. package/dist/components/util/focus-utils.d.ts.map +1 -1
  131. package/dist/components/util/hydration.d.ts +45 -0
  132. package/dist/components/util/hydration.d.ts.map +1 -0
  133. package/dist/components/util/keyboard-inset.d.ts +18 -0
  134. package/dist/components/util/keyboard-inset.d.ts.map +1 -0
  135. package/dist/components/util/media-grid.d.ts +51 -0
  136. package/dist/components/util/media-grid.d.ts.map +1 -0
  137. package/dist/components/util/media.d.ts +6 -0
  138. package/dist/components/util/media.d.ts.map +1 -0
  139. package/dist/components/util/overlay-chrome.d.ts +53 -0
  140. package/dist/components/util/overlay-chrome.d.ts.map +1 -0
  141. package/dist/components/util/overlay.d.ts +72 -0
  142. package/dist/components/util/overlay.d.ts.map +1 -0
  143. package/dist/components/util/photo.d.ts +215 -0
  144. package/dist/components/util/photo.d.ts.map +1 -0
  145. package/dist/components/util/portal.d.ts +23 -0
  146. package/dist/components/util/portal.d.ts.map +1 -0
  147. package/dist/components/util/pull-to-refresh.d.ts +27 -0
  148. package/dist/components/util/pull-to-refresh.d.ts.map +1 -0
  149. package/dist/components/util/radio-keys.d.ts +25 -0
  150. package/dist/components/util/radio-keys.d.ts.map +1 -0
  151. package/dist/components/util/rating.d.ts +23 -0
  152. package/dist/components/util/rating.d.ts.map +1 -0
  153. package/dist/components/util/ready-made-strings.d.ts +77 -0
  154. package/dist/components/util/ready-made-strings.d.ts.map +1 -0
  155. package/dist/components/util/scroll-padding.d.ts +20 -0
  156. package/dist/components/util/scroll-padding.d.ts.map +1 -0
  157. package/dist/components/util/segmented-control.d.ts +16 -0
  158. package/dist/components/util/segmented-control.d.ts.map +1 -0
  159. package/dist/components/util/select.d.ts +41 -0
  160. package/dist/components/util/select.d.ts.map +1 -0
  161. package/dist/components/util/sheet-drag.d.ts +60 -0
  162. package/dist/components/util/sheet-drag.d.ts.map +1 -0
  163. package/dist/components/util/sidebar.d.ts +63 -0
  164. package/dist/components/util/sidebar.d.ts.map +1 -0
  165. package/dist/components/util/sortable-list.d.ts +65 -0
  166. package/dist/components/util/sortable-list.d.ts.map +1 -0
  167. package/dist/components/util/ssr-safe.d.ts +4 -4
  168. package/dist/components/util/ssr-safe.d.ts.map +1 -1
  169. package/dist/components/util/stepper.d.ts +40 -0
  170. package/dist/components/util/stepper.d.ts.map +1 -0
  171. package/dist/components/util/swipeable-list-item.d.ts +36 -0
  172. package/dist/components/util/swipeable-list-item.d.ts.map +1 -0
  173. package/dist/components/util/theme-mode.d.ts +80 -0
  174. package/dist/components/util/theme-mode.d.ts.map +1 -0
  175. package/dist/components/util/toaster.d.ts +93 -0
  176. package/dist/components/util/toaster.d.ts.map +1 -0
  177. package/dist/components/util/tooltip.d.ts +10 -0
  178. package/dist/components/util/tooltip.d.ts.map +1 -0
  179. package/dist/components/util/top-navbar.d.ts +11 -0
  180. package/dist/components/util/top-navbar.d.ts.map +1 -0
  181. package/dist/components/util/touch-target.d.ts +31 -0
  182. package/dist/components/util/touch-target.d.ts.map +1 -0
  183. package/dist/components/util/zabi-strings.d.ts +133 -0
  184. package/dist/components/util/zabi-strings.d.ts.map +1 -0
  185. package/dist/create-theme/cli.js +169 -0
  186. package/dist/create-theme/index.d.ts +176 -0
  187. package/dist/create-theme/index.js +817 -0
  188. package/dist/create-theme/lib/ramp-math.js +128 -0
  189. package/dist/create-theme/lib/resolve.js +141 -0
  190. package/dist/create-theme/theme-data.js +1116 -0
  191. package/dist/index.d.ts +83 -3
  192. package/dist/index.js +60 -8
  193. package/dist/molecules/Alert.svelte +73 -66
  194. package/dist/molecules/Alert.svelte.d.ts +9 -1
  195. package/dist/molecules/AppBar.svelte +415 -0
  196. package/dist/molecules/AppBar.svelte.d.ts +69 -0
  197. package/dist/molecules/AvatarGroup.svelte +116 -0
  198. package/dist/molecules/AvatarGroup.svelte.d.ts +40 -0
  199. package/dist/molecules/BottomSheet.svelte +540 -0
  200. package/dist/molecules/BottomSheet.svelte.d.ts +93 -0
  201. package/dist/molecules/BottomTabBar.svelte +297 -0
  202. package/dist/molecules/BottomTabBar.svelte.d.ts +64 -0
  203. package/dist/molecules/Calendar.svelte +597 -0
  204. package/dist/molecules/Calendar.svelte.d.ts +72 -0
  205. package/dist/molecules/Collapsible.svelte +243 -0
  206. package/dist/molecules/Collapsible.svelte.d.ts +44 -0
  207. package/dist/molecules/CollapsibleGroup.svelte +126 -0
  208. package/dist/molecules/CollapsibleGroup.svelte.d.ts +14 -0
  209. package/dist/molecules/ComponentDemo.svelte +25 -5
  210. package/dist/molecules/ComponentDemo.svelte.d.ts +5 -0
  211. package/dist/molecules/ConfirmDialog.svelte +253 -0
  212. package/dist/molecules/ConfirmDialog.svelte.d.ts +55 -0
  213. package/dist/molecules/ContactForm.svelte +46 -19
  214. package/dist/molecules/ContactForm.svelte.d.ts +10 -0
  215. package/dist/molecules/Drawer.svelte +394 -0
  216. package/dist/molecules/Drawer.svelte.d.ts +55 -0
  217. package/dist/molecules/Dropdown.svelte +540 -50
  218. package/dist/molecules/Dropdown.svelte.d.ts +49 -6
  219. package/dist/molecules/DropdownItem.svelte +122 -0
  220. package/dist/molecules/DropdownItem.svelte.d.ts +28 -0
  221. package/dist/molecules/EmptyState.svelte +56 -10
  222. package/dist/molecules/EmptyState.svelte.d.ts +8 -0
  223. package/dist/molecules/Form.svelte +25 -14
  224. package/dist/molecules/Form.svelte.d.ts +13 -5
  225. package/dist/molecules/FormField.svelte +11 -2
  226. package/dist/molecules/FormField.svelte.d.ts +2 -0
  227. package/dist/molecules/Header.svelte +13 -3
  228. package/dist/molecules/Header.svelte.d.ts +2 -0
  229. package/dist/molecules/ImageUpload.svelte +422 -70
  230. package/dist/molecules/ImageUpload.svelte.d.ts +61 -5
  231. package/dist/molecules/MediaGrid.svelte +527 -0
  232. package/dist/molecules/MediaGrid.svelte.d.ts +77 -0
  233. package/dist/molecules/Modal.svelte +340 -45
  234. package/dist/molecules/Modal.svelte.d.ts +59 -7
  235. package/dist/molecules/NavigationMenu.svelte +15 -3
  236. package/dist/molecules/NavigationMenu.svelte.d.ts +4 -0
  237. package/dist/molecules/NavigationMenuContent.svelte +91 -36
  238. package/dist/molecules/NavigationMenuContent.svelte.d.ts +2 -0
  239. package/dist/molecules/NavigationMenuItem.svelte +10 -3
  240. package/dist/molecules/NavigationMenuItem.svelte.d.ts +2 -0
  241. package/dist/molecules/NavigationMenuLink.svelte +10 -2
  242. package/dist/molecules/NavigationMenuLink.svelte.d.ts +2 -0
  243. package/dist/molecules/NavigationMenuList.svelte +19 -4
  244. package/dist/molecules/NavigationMenuList.svelte.d.ts +2 -0
  245. package/dist/molecules/NavigationMenuTrigger.svelte +16 -3
  246. package/dist/molecules/NavigationMenuTrigger.svelte.d.ts +2 -0
  247. package/dist/molecules/Page.svelte +38 -2
  248. package/dist/molecules/Page.svelte.d.ts +13 -0
  249. package/dist/molecules/PhotoGrid.svelte +433 -0
  250. package/dist/molecules/PhotoGrid.svelte.d.ts +62 -0
  251. package/dist/molecules/PhotoViewer.svelte +1116 -0
  252. package/dist/molecules/PhotoViewer.svelte.d.ts +56 -0
  253. package/dist/molecules/PropsTable.svelte +26 -11
  254. package/dist/molecules/PropsTable.svelte.d.ts +5 -0
  255. package/dist/molecules/PullToRefresh.svelte +263 -0
  256. package/dist/molecules/PullToRefresh.svelte.d.ts +52 -0
  257. package/dist/molecules/RadioGroup.svelte +57 -15
  258. package/dist/molecules/RadioGroup.svelte.d.ts +16 -3
  259. package/dist/molecules/Section.svelte +13 -5
  260. package/dist/molecules/Section.svelte.d.ts +3 -1
  261. package/dist/molecules/SegmentedControl.svelte +323 -0
  262. package/dist/molecules/SegmentedControl.svelte.d.ts +34 -0
  263. package/dist/molecules/SidebarBrandHeader.svelte +24 -5
  264. package/dist/molecules/SidebarBrandHeader.svelte.d.ts +5 -0
  265. package/dist/molecules/SidebarFooter.svelte +39 -8
  266. package/dist/molecules/SidebarFooter.svelte.d.ts +5 -0
  267. package/dist/molecules/SidebarNavSection.svelte +12 -4
  268. package/dist/molecules/SidebarNavSection.svelte.d.ts +2 -0
  269. package/dist/molecules/SlideUp.svelte +253 -30
  270. package/dist/molecules/SlideUp.svelte.d.ts +31 -4
  271. package/dist/molecules/SortableList.svelte +632 -0
  272. package/dist/molecules/SortableList.svelte.d.ts +69 -0
  273. package/dist/molecules/Stepper.svelte +548 -0
  274. package/dist/molecules/Stepper.svelte.d.ts +39 -0
  275. package/dist/molecules/StickyActionBar.svelte +191 -0
  276. package/dist/molecules/StickyActionBar.svelte.d.ts +65 -0
  277. package/dist/molecules/SwipeableListItem.svelte +334 -0
  278. package/dist/molecules/SwipeableListItem.svelte.d.ts +54 -0
  279. package/dist/molecules/Tabs.svelte +254 -24
  280. package/dist/molecules/Tabs.svelte.d.ts +15 -4
  281. package/dist/molecules/Toaster.svelte +352 -6
  282. package/dist/molecules/Toaster.svelte.d.ts +95 -2
  283. package/dist/molecules/ToasterToast.svelte +279 -62
  284. package/dist/molecules/ToasterToast.svelte.d.ts +11 -0
  285. package/dist/molecules/UnsavedChangesBar.svelte +212 -0
  286. package/dist/molecules/UnsavedChangesBar.svelte.d.ts +43 -0
  287. package/dist/molecules/index.d.ts +46 -0
  288. package/dist/molecules/index.js +27 -0
  289. package/dist/molecules/toast-store.d.ts +53 -7
  290. package/dist/molecules/toast-store.js +21 -0
  291. package/dist/organisms/AppShell.svelte +196 -0
  292. package/dist/organisms/AppShell.svelte.d.ts +61 -0
  293. package/dist/organisms/SidebarAccountPanel.svelte +111 -12
  294. package/dist/organisms/SidebarAccountPanel.svelte.d.ts +21 -0
  295. package/dist/organisms/SidebarNavigation.svelte +193 -69
  296. package/dist/organisms/SidebarNavigation.svelte.d.ts +31 -1
  297. package/dist/organisms/SidebarPanel.svelte +45 -16
  298. package/dist/organisms/SidebarPanel.svelte.d.ts +2 -0
  299. package/dist/organisms/SidebarShell.svelte +263 -0
  300. package/dist/organisms/SidebarShell.svelte.d.ts +106 -0
  301. package/dist/organisms/TopNavbar.svelte +329 -33
  302. package/dist/organisms/TopNavbar.svelte.d.ts +28 -0
  303. package/dist/organisms/index.d.ts +6 -0
  304. package/dist/organisms/index.js +4 -0
  305. package/dist/routes/lib/variant-utils.d.ts +26 -0
  306. package/dist/routes/lib/{variant-utils.ts → variant-utils.js} +15 -46
  307. package/dist/types/components.d.ts +4 -0
  308. package/dist/types/components.d.ts.map +1 -1
  309. package/dist/types/events.js +1 -0
  310. package/dist/types/index.d.ts +605 -25
  311. package/dist/types/index.d.ts.map +1 -1
  312. package/dist/types/index.js +3 -0
  313. package/dist/types/variants.ts +17 -1
  314. package/dist/util/app-shell.d.ts +27 -0
  315. package/dist/util/app-shell.js +72 -0
  316. package/dist/util/avatar.d.ts +35 -0
  317. package/dist/util/avatar.js +52 -0
  318. package/dist/util/bottom-sheet.d.ts +29 -0
  319. package/dist/util/bottom-sheet.js +43 -0
  320. package/dist/util/bottom-tab-bar.d.ts +34 -0
  321. package/dist/util/bottom-tab-bar.js +54 -0
  322. package/dist/util/calendar.d.ts +55 -0
  323. package/dist/util/calendar.js +74 -0
  324. package/dist/util/cn.d.ts +7 -0
  325. package/dist/util/cn.js +48 -0
  326. package/dist/util/collapsible.d.ts +57 -0
  327. package/dist/util/collapsible.js +23 -0
  328. package/dist/util/confirm-dialog.d.ts +11 -0
  329. package/dist/util/confirm-dialog.js +2 -0
  330. package/dist/util/date.d.ts +95 -0
  331. package/dist/util/date.js +237 -0
  332. package/dist/util/drawer.d.ts +12 -0
  333. package/dist/util/drawer.js +7 -0
  334. package/dist/util/dropdown.d.ts +46 -0
  335. package/dist/util/dropdown.js +21 -0
  336. package/dist/util/field.d.ts +38 -0
  337. package/dist/util/field.js +38 -0
  338. package/dist/util/fit-in-viewport.d.ts +200 -0
  339. package/dist/util/fit-in-viewport.js +399 -0
  340. package/dist/util/focus-utils.d.ts +75 -2
  341. package/dist/util/focus-utils.js +182 -9
  342. package/dist/util/hydration.d.ts +44 -0
  343. package/dist/util/hydration.js +58 -0
  344. package/dist/util/keyboard-inset.d.ts +17 -0
  345. package/dist/util/keyboard-inset.js +53 -0
  346. package/dist/util/media-grid.d.ts +50 -0
  347. package/dist/util/media-grid.js +81 -0
  348. package/dist/util/media.d.ts +5 -0
  349. package/dist/util/media.js +10 -0
  350. package/dist/util/overlay-chrome.d.ts +52 -0
  351. package/dist/util/overlay-chrome.js +56 -0
  352. package/dist/util/overlay.d.ts +71 -0
  353. package/dist/util/overlay.js +240 -0
  354. package/dist/util/photo.d.ts +214 -0
  355. package/dist/util/photo.js +206 -0
  356. package/dist/util/portal.d.ts +22 -0
  357. package/dist/util/portal.js +50 -0
  358. package/dist/util/pull-to-refresh.d.ts +26 -0
  359. package/dist/util/pull-to-refresh.js +30 -0
  360. package/dist/util/radio-keys.d.ts +24 -0
  361. package/dist/util/radio-keys.js +49 -0
  362. package/dist/util/rating.d.ts +22 -0
  363. package/dist/util/rating.js +26 -0
  364. package/dist/util/ready-made-strings.d.ts +76 -0
  365. package/dist/util/ready-made-strings.js +67 -0
  366. package/dist/util/scroll-padding.d.ts +19 -0
  367. package/dist/util/scroll-padding.js +44 -0
  368. package/dist/util/segmented-control.d.ts +15 -0
  369. package/dist/util/segmented-control.js +1 -0
  370. package/dist/util/select.d.ts +40 -0
  371. package/dist/util/select.js +63 -0
  372. package/dist/util/sheet-drag.d.ts +59 -0
  373. package/dist/util/sheet-drag.js +241 -0
  374. package/dist/util/sidebar.d.ts +62 -0
  375. package/dist/util/sidebar.js +38 -0
  376. package/dist/util/sortable-list.d.ts +64 -0
  377. package/dist/util/sortable-list.js +53 -0
  378. package/dist/util/ssr-safe.d.ts +4 -4
  379. package/dist/util/stepper.d.ts +39 -0
  380. package/dist/util/stepper.js +38 -0
  381. package/dist/util/swipeable-list-item.d.ts +35 -0
  382. package/dist/util/swipeable-list-item.js +32 -0
  383. package/dist/util/theme-mode.d.ts +79 -0
  384. package/dist/util/theme-mode.js +121 -0
  385. package/dist/util/toaster.d.ts +92 -0
  386. package/dist/util/toaster.js +75 -0
  387. package/dist/util/tooltip.d.ts +9 -0
  388. package/dist/util/tooltip.js +18 -0
  389. package/dist/util/top-navbar.d.ts +10 -0
  390. package/dist/util/top-navbar.js +5 -0
  391. package/dist/util/touch-target.d.ts +30 -0
  392. package/dist/util/touch-target.js +30 -0
  393. package/dist/util/zabi-strings.d.ts +132 -0
  394. package/dist/util/zabi-strings.js +105 -0
  395. package/dist/zabi-components-colors.css +5 -2
  396. package/dist/zabi-components-theme-dark-only.css +574 -225
  397. package/dist/zabi-components-theme-dark.css +574 -225
  398. package/dist/zabi-components-theme-only.css +1680 -317
  399. package/dist/zabi-components-theme.css +1680 -317
  400. package/dist/zabi-components.css +4473 -1158
  401. package/docs/lucide-icons.md +41 -16
  402. package/docs/theme-imports.md +81 -11
  403. package/package.json +51 -29
  404. package/dist/lib/layout-width-tokens.d.ts +0 -17
  405. package/dist/lib/layout-width-tokens.d.ts.map +0 -1
  406. package/dist/lib/showcase/component-docs/Button.d.ts +0 -3
  407. package/dist/lib/showcase/component-docs/Button.d.ts.map +0 -1
  408. package/dist/lib/showcase/component-docs/Input.d.ts +0 -3
  409. package/dist/lib/showcase/component-docs/Input.d.ts.map +0 -1
  410. package/dist/lib/showcase/component-docs/List.d.ts +0 -3
  411. package/dist/lib/showcase/component-docs/List.d.ts.map +0 -1
  412. package/dist/lib/showcase/component-docs/Modal.d.ts +0 -3
  413. package/dist/lib/showcase/component-docs/Modal.d.ts.map +0 -1
  414. package/dist/lib/showcase/component-docs/Radio.d.ts +0 -3
  415. package/dist/lib/showcase/component-docs/Radio.d.ts.map +0 -1
  416. package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts +0 -3
  417. package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts.map +0 -1
  418. package/dist/lib/showcase/component-docs/Skeleton.d.ts +0 -3
  419. package/dist/lib/showcase/component-docs/Skeleton.d.ts.map +0 -1
  420. package/dist/lib/showcase/component-docs/_shared.d.ts +0 -12
  421. package/dist/lib/showcase/component-docs/_shared.d.ts.map +0 -1
  422. package/dist/lib/showcase/component-docs/index.d.ts +0 -4
  423. package/dist/lib/showcase/component-docs/index.d.ts.map +0 -1
  424. package/dist/lib/showcase/components-catalog.d.ts +0 -3
  425. package/dist/lib/showcase/components-catalog.d.ts.map +0 -1
  426. package/dist/lib/showcase/components-showcase-constants.d.ts +0 -89
  427. package/dist/lib/showcase/components-showcase-constants.d.ts.map +0 -1
  428. package/dist/lib/showcase/docs-sidebar-helpers.d.ts +0 -15
  429. package/dist/lib/showcase/docs-sidebar-helpers.d.ts.map +0 -1
  430. package/dist/routes/lib/focus-utils.d.ts +0 -12
  431. package/dist/routes/lib/focus-utils.ts +0 -110
  432. package/dist/routes/lib/ssr-safe.ts +0 -58
  433. package/scripts/fix-lucide-svelte-icon-dts.js +0 -35
@@ -1,11 +1,30 @@
1
1
  <script lang="ts">
2
+ import { mergeStrings } from "../util/ready-made-strings.js";
3
+ import { zabiStringsFor } from "../util/zabi-strings.js";
2
4
  import SidebarPanel, {
3
5
  type SidebarPanelItem,
4
6
  } from "./SidebarPanel.svelte";
5
7
  import { generateId } from "../util/ssr-safe.js";
6
- import { LogOut, Moon, Sun, User } from "@lucide/svelte";
8
+ import {
9
+ DEFAULT_SIDEBAR_ACCOUNT_PANEL_STRINGS,
10
+ type SidebarAccountPanelStrings,
11
+ } from "../util/sidebar.js";
12
+ import {
13
+ getThemeMode,
14
+ isThemeDark,
15
+ setThemeMode,
16
+ THEME_MODES,
17
+ type ThemeMode,
18
+ } from "../util/theme-mode.js";
19
+ import LogOut from "@lucide/svelte/icons/log-out";
20
+ import Monitor from "@lucide/svelte/icons/monitor";
21
+ import Moon from "@lucide/svelte/icons/moon";
22
+ import Sun from "@lucide/svelte/icons/sun";
23
+ import User from "@lucide/svelte/icons/user";
7
24
 
8
25
  interface Props {
26
+ /** Extra classes for the host element. */
27
+ class?: string;
9
28
  /** Target id for `aria-controls` from the sidebar footer/nav. */
10
29
  panelId?: string;
11
30
  style?: string;
@@ -16,6 +35,23 @@
16
35
  showLogout?: boolean;
17
36
  isLightMode?: boolean;
18
37
  onThemeToggle?: (nextIsLightMode: boolean) => void;
38
+ /**
39
+ * `"two"` (the default): the theme row flips `isLightMode` and calls
40
+ * `onThemeToggle`; the app switches the page. `"three"`: the row
41
+ * steps through system, light and dark, switches the page itself
42
+ * (`data-theme` on `<html>`, as ThemeToggle does) and calls
43
+ * `onThemeModeChange`.
44
+ */
45
+ themeModes?: "two" | "three";
46
+ /** With `themeModes="three"`: called with the mode the row has switched to. */
47
+ onThemeModeChange?: (mode: ThemeMode) => void;
48
+ /**
49
+ * With `themeModes="three"`: the `localStorage` key the choice is kept
50
+ * under. Default `"theme"`, as ThemeToggle; `null` keeps nothing.
51
+ */
52
+ themeStorageKey?: string | null;
53
+ /** Every word the panel says by itself, for another language. `logoutLabel` is its own prop. */
54
+ strings?: Partial<SidebarAccountPanelStrings>;
19
55
  onLogout?: () => void;
20
56
  onAccount?: () => void;
21
57
  onClose?: () => void;
@@ -24,6 +60,7 @@
24
60
  }
25
61
 
26
62
  let {
63
+ class: className = "",
27
64
  panelId = generateId("account-panel"),
28
65
  style = "",
29
66
  profileName = "Account",
@@ -31,8 +68,12 @@
31
68
  logoutLabel = "Log out",
32
69
  showThemeToggle = true,
33
70
  showLogout = true,
34
- isLightMode = $bindable(false),
71
+ isLightMode = $bindable<Exclude<Props["isLightMode"], undefined>>(),
35
72
  onThemeToggle,
73
+ themeModes = "two",
74
+ onThemeModeChange,
75
+ themeStorageKey,
76
+ strings,
36
77
  onLogout,
37
78
  onAccount,
38
79
  onClose,
@@ -41,24 +82,70 @@
41
82
  ...restProps
42
83
  }: Props = $props();
43
84
 
85
+ // No fallback on a bindable prop: Svelte refuses `bind:…={undefined}` on
86
+ // one that has a fallback (`props_invalid_value`), and a page that throws
87
+ // while it hydrates never becomes interactive. The default is applied
88
+ // here instead: at once, for the server and the first render, and again
89
+ // whenever a parent hands back `undefined`.
90
+ const applyDefaults = () => {
91
+ if (isLightMode === undefined) isLightMode = false;
92
+ };
93
+ applyDefaults();
94
+ $effect.pre(applyDefaults);
95
+
44
96
  let selectedItemId = $state("");
45
97
 
98
+ /** The app-wide words for this component, from a `ZabiStringsProvider` above it, if there is one. */
99
+ const provided = zabiStringsFor("sidebarAccountPanel");
100
+ const text = $derived(mergeStrings(DEFAULT_SIDEBAR_ACCOUNT_PANEL_STRINGS, provided(), strings));
101
+
102
+ /**
103
+ * With three modes: the page's mode, read when the panel mounts, after
104
+ * each step, and when something else switches the page (a ThemeToggle in
105
+ * the bar, the app's own script) while the panel is there.
106
+ */
107
+ let themeMode = $state<ThemeMode>("light");
108
+ $effect(() => {
109
+ if (themeModes !== "three") return;
110
+ themeMode = getThemeMode();
111
+ if (typeof MutationObserver === "undefined") return;
112
+ const observer = new MutationObserver(() => {
113
+ themeMode = getThemeMode();
114
+ });
115
+ observer.observe(document.documentElement, { attributes: true, attributeFilter: ["class", "data-theme"] });
116
+ return () => observer.disconnect();
117
+ });
118
+
46
119
  const items = $derived.by((): SidebarPanelItem[] => {
47
120
  const list: SidebarPanelItem[] = [
48
121
  {
49
122
  id: "account",
50
- label: "Account",
123
+ label: text.account,
51
124
  description: profileName,
52
125
  icon: User,
53
126
  },
54
127
  ];
55
128
 
56
- if (showThemeToggle) {
129
+ if (showThemeToggle && themeModes === "three") {
57
130
  list.push({
58
131
  id: "theme",
59
- label: "Theme",
60
- description: isLightMode ? "Light mode" : "Dark mode",
61
- badgeText: isLightMode ? "Light" : "Dark",
132
+ label: text.theme,
133
+ description:
134
+ themeMode === "auto"
135
+ ? text.systemMode
136
+ : themeMode === "light"
137
+ ? text.lightMode
138
+ : text.darkMode,
139
+ badgeText:
140
+ themeMode === "auto" ? text.system : themeMode === "light" ? text.light : text.dark,
141
+ icon: themeMode === "auto" ? Monitor : themeMode === "light" ? Sun : Moon,
142
+ });
143
+ } else if (showThemeToggle) {
144
+ list.push({
145
+ id: "theme",
146
+ label: text.theme,
147
+ description: isLightMode ? text.lightMode : text.darkMode,
148
+ badgeText: isLightMode ? text.light : text.dark,
62
149
  icon: isLightMode ? Sun : Moon,
63
150
  });
64
151
  }
@@ -67,7 +154,7 @@
67
154
  list.push({
68
155
  id: "logout",
69
156
  label: logoutLabel,
70
- description: "Sign out of this account",
157
+ description: text.signOut,
71
158
  icon: LogOut,
72
159
  });
73
160
  }
@@ -76,6 +163,16 @@
76
163
  });
77
164
 
78
165
  function toggleTheme(): void {
166
+ if (themeModes === "three") {
167
+ // From where the page is now, which another control may have changed.
168
+ const current = getThemeMode();
169
+ const next = THEME_MODES[(THEME_MODES.indexOf(current) + 1) % THEME_MODES.length];
170
+ setThemeMode(next, themeStorageKey === undefined ? {} : { storageKey: themeStorageKey });
171
+ themeMode = next;
172
+ isLightMode = !isThemeDark();
173
+ onThemeModeChange?.(next);
174
+ return;
175
+ }
79
176
  isLightMode = !isLightMode;
80
177
  onThemeToggle?.(isLightMode);
81
178
  }
@@ -96,16 +193,18 @@
96
193
  <div
97
194
  id={panelId}
98
195
  {style}
196
+ class={className}
99
197
  role="dialog"
100
198
  aria-modal="false"
101
- aria-label="Account panel"
199
+ aria-label={text.panelLabel}
102
200
  {...restProps}
103
201
  >
104
202
  <SidebarPanel
105
- title="Account"
203
+ title={text.title}
106
204
  subtitle={profileEmail}
107
- ariaLabel="Account panel"
108
- closeLabel="Close account panel"
205
+ ariaLabel={text.panelLabel}
206
+ closeLabel={text.closeLabel}
207
+ selectLabel={text.listLabel}
109
208
  {widthClass}
110
209
  {variant}
111
210
  showSearch={false}
@@ -1,4 +1,8 @@
1
+ import { type SidebarAccountPanelStrings } from "../util/sidebar.js";
2
+ import { type ThemeMode } from "../util/theme-mode.js";
1
3
  interface Props {
4
+ /** Extra classes for the host element. */
5
+ class?: string;
2
6
  /** Target id for `aria-controls` from the sidebar footer/nav. */
3
7
  panelId?: string;
4
8
  style?: string;
@@ -9,6 +13,23 @@ interface Props {
9
13
  showLogout?: boolean;
10
14
  isLightMode?: boolean;
11
15
  onThemeToggle?: (nextIsLightMode: boolean) => void;
16
+ /**
17
+ * `"two"` (the default): the theme row flips `isLightMode` and calls
18
+ * `onThemeToggle`; the app switches the page. `"three"`: the row
19
+ * steps through system, light and dark, switches the page itself
20
+ * (`data-theme` on `<html>`, as ThemeToggle does) and calls
21
+ * `onThemeModeChange`.
22
+ */
23
+ themeModes?: "two" | "three";
24
+ /** With `themeModes="three"`: called with the mode the row has switched to. */
25
+ onThemeModeChange?: (mode: ThemeMode) => void;
26
+ /**
27
+ * With `themeModes="three"`: the `localStorage` key the choice is kept
28
+ * under. Default `"theme"`, as ThemeToggle; `null` keeps nothing.
29
+ */
30
+ themeStorageKey?: string | null;
31
+ /** Every word the panel says by itself, for another language. `logoutLabel` is its own prop. */
32
+ strings?: Partial<SidebarAccountPanelStrings>;
12
33
  onLogout?: () => void;
13
34
  onAccount?: () => void;
14
35
  onClose?: () => void;
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import { mergeStrings } from "../util/ready-made-strings.js";
3
+ import { zabiStringsFor, zabiCommonStrings } from "../util/zabi-strings.js";
2
4
  import Badge from "../atoms/Badge.svelte";
3
5
  import Button from "../atoms/Button.svelte";
4
6
  import IconButton from "../atoms/IconButton.svelte";
@@ -6,10 +8,21 @@
6
8
  import SidebarBrandHeader from "../molecules/SidebarBrandHeader.svelte";
7
9
  import SidebarFooter from "../molecules/SidebarFooter.svelte";
8
10
  import SidebarNavSection from "../molecules/SidebarNavSection.svelte";
9
- import { Command, Search } from "@lucide/svelte";
11
+ import Tooltip from "../atoms/Tooltip.svelte";
12
+ import SidebarShell from "./SidebarShell.svelte";
13
+ import type {
14
+ SidebarDrawerCloseReason,
15
+ SidebarTriggerContext,
16
+ } from "./SidebarShell.svelte";
17
+ import Command from "@lucide/svelte/icons/command";
18
+ import Search from "@lucide/svelte/icons/search";
10
19
  import type { Snippet } from "svelte";
11
20
  import type { Component } from "svelte";
12
21
  import type { ButtonVariant, SizeVariant } from "../types/variants.js";
22
+ import {
23
+ DEFAULT_SIDEBAR_NAVIGATION_STRINGS,
24
+ type SidebarNavigationStrings,
25
+ } from "../util/sidebar.js";
13
26
 
14
27
  export interface SidebarNavigationItem {
15
28
  id: string;
@@ -34,6 +47,14 @@
34
47
  items?: SidebarNavigationItem[];
35
48
  currentPath?: string;
36
49
  ariaLabel?: string;
50
+ /**
51
+ * The words the sidebar says by itself, for another language: the
52
+ * names of its lists, the two lines shown when a search finds
53
+ * nothing, and those of the footer and the brand header in it.
54
+ */
55
+ strings?: Partial<SidebarNavigationStrings>;
56
+ class?: string;
57
+ /** @deprecated use `class`. */
37
58
  className?: string;
38
59
  logoSrc?: string;
39
60
  logoAlt?: string;
@@ -69,6 +90,24 @@
69
90
  profilePanel?: Snippet;
70
91
  /** Parent nav href to highlight when `currentPath` is a deeper leaf (e.g. category row). */
71
92
  activePrimaryHref?: string;
93
+ /**
94
+ * What the sidebar is below the `lg` breakpoint. `drawer`: the rail is
95
+ * hidden there and the sidebar opens in a Drawer from the start edge.
96
+ * This and the six props after it are SidebarShell's; see there.
97
+ */
98
+ mobile?: "none" | "drawer";
99
+ /** Whether the drawer is open. Bindable. */
100
+ isOpen?: boolean;
101
+ /** The button that opens the drawer, rendered in the rail's place below `lg`. */
102
+ trigger?: Snippet<[SidebarTriggerContext]>;
103
+ /** Heading of the drawer. Without it, `ariaLabel` is. */
104
+ drawerTitle?: string;
105
+ /** Accessible name of the drawer's close button. */
106
+ closeLabel?: string;
107
+ /** Fired when the drawer closes itself, with why. */
108
+ onclose?: (detail: { reason: SidebarDrawerCloseReason }) => void;
109
+ /** Accessible name of the scrolling region between header and footer. */
110
+ label?: string;
72
111
  }
73
112
 
74
113
  let {
@@ -78,7 +117,9 @@
78
117
  currentPath = "",
79
118
  activePrimaryHref = "",
80
119
  ariaLabel = "Sidebar navigation",
81
- className = "",
120
+ strings,
121
+ class: classAttr = "",
122
+ className: legacyClass = "",
82
123
  logoSrc = "",
83
124
  logoAlt = "",
84
125
  brandName = "",
@@ -88,8 +129,8 @@
88
129
  profileInitials = "ZA",
89
130
  showSearch = true,
90
131
  searchMode = "input",
91
- searchPlaceholder = "Search...",
92
- searchValue = $bindable(""),
132
+ searchPlaceholder: searchPlaceholderGiven,
133
+ searchValue = $bindable<Exclude<Props["searchValue"], undefined>>(),
93
134
  searchTriggerIcon = Command,
94
135
  searchTriggerVariant = "outline",
95
136
  searchTriggerSize = "sm",
@@ -97,7 +138,7 @@
97
138
  logoutLabel = "Logout",
98
139
  showThemeToggle = true,
99
140
  lightModeLabel = "Light mode",
100
- isLightMode = $bindable(false),
141
+ isLightMode = $bindable<Exclude<Props["isLightMode"], undefined>>(),
101
142
  emptyStateTitle = "Create your first navigation item",
102
143
  emptyStateDescription = "Add your first sidebar item so users can start navigating your product.",
103
144
  emptyStateActionLabel = "Add navigation item",
@@ -110,11 +151,43 @@
110
151
  profilePanelOpen = false,
111
152
  profilePanelControlsId = "",
112
153
  profilePanel,
154
+ mobile = "none",
155
+ isOpen = $bindable<Exclude<Props["isOpen"], undefined>>(),
156
+ trigger,
157
+ drawerTitle,
158
+ closeLabel,
159
+ onclose,
160
+ label,
113
161
  ...restProps
114
162
  }: Props = $props();
115
163
 
164
+ // No fallback on a bindable prop: Svelte refuses `bind:…={undefined}` on
165
+ // one that has a fallback (`props_invalid_value`), and a page that throws
166
+ // while it hydrates never becomes interactive. The default is applied
167
+ // here instead: at once, for the server and the first render, and again
168
+ // whenever a parent hands back `undefined`.
169
+ const applyDefaults = () => {
170
+ if (searchValue === undefined) searchValue = "";
171
+ if (isLightMode === undefined) isLightMode = false;
172
+ if (isOpen === undefined) isOpen = false;
173
+ };
174
+ applyDefaults();
175
+ $effect.pre(applyDefaults);
176
+
177
+ /** Words many components share: a `ZabiStringsProvider` above this one may give them; else English. */
178
+ const common = zabiCommonStrings();
179
+ const searchPlaceholder = $derived(searchPlaceholderGiven ?? common().search);
180
+
181
+ /** `class` is the public prop; `className` is a deprecated alias.
182
+ * Both are merged here so existing call sites keep working. */
183
+
184
+ /** The app-wide words for this component, from a `ZabiStringsProvider` above it, if there is one. */
185
+ const provided = zabiStringsFor("sidebarNavigation");
186
+ const text = $derived(mergeStrings(DEFAULT_SIDEBAR_NAVIGATION_STRINGS, provided(), strings));
187
+ /** For the brand header and the footer: the provider's words for this sidebar, then the instance's. */
188
+ const handedOn = $derived(mergeStrings<Partial<SidebarNavigationStrings>>({}, provided(), strings));
189
+
116
190
  const isCollapsed = $derived(mode === "collapsed");
117
- const isCard = $derived(layout === "card");
118
191
  const showBrandRow = $derived(
119
192
  Boolean(logoSrc.trim() || brandName.trim()),
120
193
  );
@@ -178,24 +251,6 @@
178
251
  partitionBySection(filteredPrimaryItems),
179
252
  );
180
253
 
181
- const insetX = $derived(isCollapsed ? "px-2.5" : "px-4");
182
-
183
- const containerClasses = $derived.by(() => {
184
- const widthClass = isCollapsed ? "w-[104px]" : "w-[266px]";
185
- const railSurface = "border-r border-border bg-background text-headline";
186
- const cardSurface =
187
- "border-r border-border bg-background text-headline shadow-sm";
188
- const surfaceClasses = isCard ? cardSurface : railSurface;
189
- const verticalPad = isCard ? "py-4" : "py-5";
190
- const baseClasses = `flex h-full min-h-0 max-h-full flex-col overflow-visible ${verticalPad}`;
191
-
192
- return `${baseClasses} ${widthClass} ${surfaceClasses} ${className}`.trim();
193
- });
194
-
195
- const headerStackClasses = $derived(
196
- `flex w-full shrink-0 flex-col gap-5 ${insetX}`,
197
- );
198
-
199
254
  const iconContainerClasses = $derived(
200
255
  "flex size-6 shrink-0 items-center justify-center leading-none text-current",
201
256
  );
@@ -210,21 +265,49 @@
210
265
  return isMuted ? "text-description" : "text-headline";
211
266
  }
212
267
 
213
- function getNavItemClasses(item: SidebarNavigationItem): string {
214
- const isActive =
268
+ /**
269
+ * The selected row used to be carried by its fill alone, and the fill was
270
+ * 1.05:1 against the sidebar in light mode — not a fill, a rumour. Active
271
+ * and idle labels were #18181b and #27272a at the same weight, a 15-unit
272
+ * difference in one channel that nobody can see. So in light mode there
273
+ * was effectively no indication of where you were.
274
+ *
275
+ * Three cues now, only one of which is colour-discrimination dependent:
276
+ * the label takes --color-nav-menu-item-active (defined all along and
277
+ * never referenced — the row said `text-inherit`), a 3px bar marks the
278
+ * leading edge, and the tint stays as reinforcement rather than as the
279
+ * whole message.
280
+ */
281
+ function isItemActive(item: SidebarNavigationItem): boolean {
282
+ return (
215
283
  currentPath === item.href ||
216
- (Boolean(activePrimaryHref) && item.href === activePrimaryHref);
284
+ (Boolean(activePrimaryHref) && item.href === activePrimaryHref)
285
+ );
286
+ }
287
+
288
+ function getNavItemClasses(item: SidebarNavigationItem): string {
289
+ const isActive = isItemActive(item);
217
290
  const layoutClasses = isCollapsed
218
- ? "flex min-h-10 items-center justify-center px-0 py-2"
219
- : "flex min-h-10 items-center gap-2.5 px-2.5 py-2";
291
+ ? "flex min-h-10 pointer-coarse:min-h-11 items-center justify-center px-0 py-2"
292
+ : "flex min-h-10 pointer-coarse:min-h-11 items-center gap-3 px-2 py-2";
220
293
  const structural =
221
- "focus-ring focus-ring--nav w-full cursor-pointer rounded-lg no-underline transition-colors duration-150 outline-none";
294
+ "focus-ring focus-ring--nav relative w-full cursor-pointer rounded-control no-underline transition-colors duration-150 outline-none";
222
295
 
223
296
  if (isActive) {
224
- return `${structural} ${layoutClasses} bg-nav-menu-active text-inherit hover:bg-nav-menu-active hover:text-inherit active:opacity-90`;
297
+ // In forced colours all three cues are gone: fills (the tint and
298
+ // the bar) become the canvas and every link takes the system's
299
+ // link colour. An outline is a shape and is drawn there, so the
300
+ // current row keeps one. Inset, so a focused row's ring (2px
301
+ // outside) reads as a different thing.
302
+ return (
303
+ `${structural} ${layoutClasses} bg-nav-menu-active ` +
304
+ "text-nav-menu-item-active hover:bg-nav-menu-active " +
305
+ "active:bg-nav-menu-active-hover " +
306
+ "forced-colors:outline-solid forced-colors:outline-2 forced-colors:-outline-offset-2"
307
+ );
225
308
  }
226
309
 
227
- return `${structural} ${layoutClasses} text-nav-menu-item hover:bg-nav-menu-hover hover:text-nav-menu-item-hover active:bg-base-200`;
310
+ return `${structural} ${layoutClasses} text-nav-menu-item hover:bg-nav-menu-hover hover:text-nav-menu-item-hover active:bg-surface-active`;
228
311
  }
229
312
 
230
313
  function handleNavigate(item: SidebarNavigationItem, event: MouseEvent) {
@@ -268,13 +351,25 @@
268
351
  }
269
352
  </script>
270
353
 
271
- <nav class={containerClasses} aria-label={ariaLabel} {...restProps}>
272
- <div
273
- class="flex min-h-0 w-full min-w-0 flex-1 flex-col"
274
- >
275
- <div class={`${headerStackClasses} pb-3`.trim()}>
354
+ <SidebarShell
355
+ {mode}
356
+ {layout}
357
+ {ariaLabel}
358
+ class={classAttr}
359
+ className={legacyClass}
360
+ {mobile}
361
+ bind:isOpen
362
+ {trigger}
363
+ {drawerTitle}
364
+ {closeLabel}
365
+ {onclose}
366
+ {label}
367
+ {...restProps}
368
+ >
369
+ {#snippet header()}
276
370
  {#if showBrandRow}
277
371
  <SidebarBrandHeader
372
+ strings={handedOn}
278
373
  collapsed={isCollapsed}
279
374
  {brandName}
280
375
  logoSrc={logoSrc.trim()}
@@ -307,10 +402,10 @@
307
402
  <Button
308
403
  variant={searchTriggerVariant}
309
404
  size={searchTriggerSize}
310
- isFullWidth
405
+ fullWidth
311
406
  onclick={handleSearchClick}
312
407
  >
313
- <span class="flex w-full items-center justify-start gap-2.5">
408
+ <span class="flex w-full items-center justify-start gap-3">
314
409
  <span class={iconContainerClasses} aria-hidden="true">
315
410
  <TriggerIcon size={17} />
316
411
  </span>
@@ -334,18 +429,13 @@
334
429
  bind:value={searchValue}
335
430
  placeholder={searchPlaceholder}
336
431
  aria-label={searchPlaceholder}
337
- class="focus-ring focus-ring--nav w-full min-w-0 min-h-10 rounded-xl border-transparent !bg-transparent py-2 pl-10 text-sm ring-1 ring-border/60 hover:!bg-nav-menu-hover focus:!bg-transparent"
432
+ class="focus-ring focus-ring--nav w-full min-w-0 min-h-10 pointer-coarse:min-h-11 rounded-container border-transparent !bg-transparent py-2 pl-10 text-sm ring-1 ring-input-border hover:!bg-input-hover focus:!bg-transparent"
338
433
  />
339
434
  </div>
340
435
  {/if}
341
436
  {/if}
342
- </div>
437
+ {/snippet}
343
438
 
344
- <div
345
- class={`flex min-h-0 min-w-0 flex-1 flex-col gap-3 overflow-y-auto overflow-x-hidden overscroll-y-contain pt-4 pb-1.5 ${insetX}`}
346
- role="region"
347
- aria-label="Navigation links"
348
- >
349
439
  {#if hasFilteredItems}
350
440
  <div
351
441
  class="flex w-full min-w-0 flex-col divide-y divide-border"
@@ -356,13 +446,26 @@
356
446
  title={group.sectionLabel ?? ""}
357
447
  sectionKey={`p-${gi}`}
358
448
  listAriaLabel={group.sectionLabel
359
- ? `${group.sectionLabel} navigation`
360
- : "Primary navigation"}
449
+ ? text.sectionNavigation(group.sectionLabel)
450
+ : text.primaryNavigation}
361
451
  collapsed={isCollapsed}
362
452
  >
363
453
  {#each group.items as item (item.id)}
364
454
  {@const Icon = item.icon}
365
455
  <li>
456
+ <!--
457
+ Collapsed rows are icon-only. aria-label
458
+ covers screen readers, but a sighted user
459
+ got an unlabelled glyph with no hover
460
+ affordance at all.
461
+ -->
462
+ <Tooltip
463
+ content={item.label}
464
+ placement="right"
465
+ disabled={!isCollapsed}
466
+ block
467
+ fixed
468
+ >
366
469
  <a
367
470
  href={item.href}
368
471
  class={getNavItemClasses(item)}
@@ -370,6 +473,12 @@
370
473
  aria-current={getAriaCurrent(item)}
371
474
  aria-label={isCollapsed ? item.label : undefined}
372
475
  >
476
+ {#if isItemActive(item)}
477
+ <span
478
+ class="absolute left-0 top-1/2 h-5 w-[3px] -translate-y-1/2 rounded-pill bg-nav-menu-item-active"
479
+ aria-hidden="true"
480
+ ></span>
481
+ {/if}
373
482
  {#if Icon}
374
483
  <span
375
484
  class={iconContainerClasses}
@@ -384,16 +493,31 @@
384
493
  >{item.label}</span
385
494
  >
386
495
  {/if}
387
- {#if !isCollapsed && getItemBadgeText(item) !== null}
388
- <span class="ml-auto">
389
- <Badge
390
- variant="default"
391
- size="sm"
392
- text={getItemBadgeText(item) ?? ""}
393
- />
394
- </span>
496
+ {#if getItemBadgeText(item) !== null}
497
+ {#if isCollapsed}
498
+ <!--
499
+ A notification count that
500
+ disappears when you collapse
501
+ the rail is a real loss; the
502
+ count becomes a dot rather
503
+ than nothing.
504
+ -->
505
+ <span
506
+ class="absolute right-2 top-2 size-2 rounded-pill bg-error"
507
+ aria-hidden="true"
508
+ ></span>
509
+ {:else}
510
+ <span class="ml-auto">
511
+ <Badge
512
+ variant="default"
513
+ size="sm"
514
+ text={getItemBadgeText(item) ?? ""}
515
+ />
516
+ </span>
517
+ {/if}
395
518
  {/if}
396
519
  </a>
520
+ </Tooltip>
397
521
  </li>
398
522
  {/each}
399
523
  </SidebarNavSection>
@@ -408,7 +532,7 @@
408
532
  >
409
533
  <SidebarNavSection
410
534
  sectionKey="secondary"
411
- listAriaLabel="Secondary navigation"
535
+ listAriaLabel={text.secondaryNavigation}
412
536
  collapsed={isCollapsed}
413
537
  >
414
538
  {#each filteredSecondaryItems as item (item.id)}
@@ -446,22 +570,22 @@
446
570
  {/if}
447
571
  {:else}
448
572
  <div
449
- class="rounded-xl border border-border border-dashed bg-transparent px-3.5 py-4 ring-1 ring-border/60"
573
+ class="rounded-container border border-border border-dashed bg-transparent px-4 py-4"
450
574
  >
451
575
  <h3 class="text-sm font-semibold {getTextToneClass()}">
452
576
  {normalizedSearchTerm && searchMode === "input"
453
- ? "No matching navigation items"
577
+ ? text.noMatchesTitle
454
578
  : emptyStateTitle}
455
579
  </h3>
456
580
  <p class="mt-1 text-sm {getTextToneClass(true)}">
457
581
  {normalizedSearchTerm && searchMode === "input"
458
- ? `No results found for "${searchValue}". Try another keyword.`
582
+ ? text.noMatchesDescription(searchValue)
459
583
  : emptyStateDescription}
460
584
  </p>
461
585
  {#if !(normalizedSearchTerm && searchMode === "input")}
462
586
  <button
463
587
  type="button"
464
- class="focus-ring focus-ring--nav mt-3 inline-flex min-h-10 cursor-pointer items-center rounded-lg bg-action-primary px-3 py-2 text-sm font-medium text-action-primary outline-none transition-colors hover:bg-action-primary-hover"
588
+ class="focus-ring focus-ring--nav mt-3 inline-flex min-h-10 pointer-coarse:min-h-11 cursor-pointer items-center rounded-control bg-action-primary px-3 py-2 text-sm font-medium text-action-primary outline-none transition-colors hover:bg-action-primary-hover"
465
589
  onclick={handleEmptyStateAction}
466
590
  >
467
591
  {emptyStateActionLabel}
@@ -469,11 +593,10 @@
469
593
  {/if}
470
594
  </div>
471
595
  {/if}
472
- </div>
473
- </div>
474
-
475
- <SidebarFooter
476
- collapsed={isCollapsed}
596
+ {#snippet footer({ insetX })}
597
+ <SidebarFooter
598
+ collapsed={isCollapsed}
599
+ strings={handedOn}
477
600
  {showProfile}
478
601
  {profileName}
479
602
  {profileEmail}
@@ -489,6 +612,7 @@
489
612
  {profilePanelOpen}
490
613
  {profilePanelControlsId}
491
614
  {profilePanel}
492
- className={insetX}
493
- />
494
- </nav>
615
+ className={insetX}
616
+ />
617
+ {/snippet}
618
+ </SidebarShell>