zabi-components 8.0.0 → 9.0.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 (456) hide show
  1. package/CHANGELOG.md +2935 -0
  2. package/README.md +243 -10
  3. package/THEME.md +162 -332
  4. package/THEMING.md +1419 -259
  5. package/dist/atoms/ActionPanel.svelte +15 -5
  6. package/dist/atoms/Avatar.svelte +120 -0
  7. package/dist/atoms/Avatar.svelte.d.ts +46 -0
  8. package/dist/atoms/Badge.svelte +71 -13
  9. package/dist/atoms/Badge.svelte.d.ts +8 -2
  10. package/dist/atoms/Block.svelte +74 -0
  11. package/dist/atoms/Block.svelte.d.ts +29 -0
  12. package/dist/atoms/Button.svelte +217 -30
  13. package/dist/atoms/Button.svelte.d.ts +33 -4
  14. package/dist/atoms/Card.svelte +182 -26
  15. package/dist/atoms/Card.svelte.d.ts +55 -3
  16. package/dist/atoms/CardHeader.svelte +16 -4
  17. package/dist/atoms/Checkbox.svelte +81 -21
  18. package/dist/atoms/Checkbox.svelte.d.ts +31 -2
  19. package/dist/atoms/Chip.svelte +351 -0
  20. package/dist/atoms/Chip.svelte.d.ts +57 -0
  21. package/dist/atoms/CodeBlock.svelte +20 -2
  22. package/dist/atoms/CodeBlock.svelte.d.ts +4 -0
  23. package/dist/atoms/CodeField.svelte +385 -0
  24. package/dist/atoms/CodeField.svelte.d.ts +61 -0
  25. package/dist/atoms/ColorPicker.svelte +260 -49
  26. package/dist/atoms/ColorPicker.svelte.d.ts +3 -0
  27. package/dist/atoms/Container.svelte +16 -8
  28. package/dist/atoms/DateField.svelte +145 -0
  29. package/dist/atoms/DateField.svelte.d.ts +81 -0
  30. package/dist/atoms/FieldMessages.svelte +69 -0
  31. package/dist/atoms/FieldMessages.svelte.d.ts +23 -0
  32. package/dist/atoms/FloatingActionButton.svelte +176 -0
  33. package/dist/atoms/FloatingActionButton.svelte.d.ts +51 -0
  34. package/dist/atoms/Heading.svelte +63 -5
  35. package/dist/atoms/Heading.svelte.d.ts +14 -0
  36. package/dist/atoms/IconButton.svelte +294 -26
  37. package/dist/atoms/IconButton.svelte.d.ts +62 -4
  38. package/dist/atoms/Input.svelte +259 -53
  39. package/dist/atoms/Input.svelte.d.ts +33 -2
  40. package/dist/atoms/List.svelte +5 -1
  41. package/dist/atoms/ListItem.svelte +70 -13
  42. package/dist/atoms/ListItem.svelte.d.ts +11 -0
  43. package/dist/atoms/PickerField.svelte +239 -0
  44. package/dist/atoms/PickerField.svelte.d.ts +67 -0
  45. package/dist/atoms/Progress.svelte +99 -19
  46. package/dist/atoms/Progress.svelte.d.ts +15 -1
  47. package/dist/atoms/Radio.svelte +51 -3
  48. package/dist/atoms/Radio.svelte.d.ts +30 -2
  49. package/dist/atoms/Rating.svelte +620 -0
  50. package/dist/atoms/Rating.svelte.d.ts +77 -0
  51. package/dist/atoms/Select.svelte +438 -98
  52. package/dist/atoms/Select.svelte.d.ts +61 -3
  53. package/dist/atoms/SelectionControl.svelte +193 -23
  54. package/dist/atoms/SelectionControl.svelte.d.ts +29 -3
  55. package/dist/atoms/Skeleton.svelte +114 -18
  56. package/dist/atoms/Skeleton.svelte.d.ts +30 -3
  57. package/dist/atoms/SkeletonGroup.svelte +43 -0
  58. package/dist/atoms/SkeletonGroup.svelte.d.ts +20 -0
  59. package/dist/atoms/Slider.svelte +344 -0
  60. package/dist/atoms/Slider.svelte.d.ts +40 -0
  61. package/dist/atoms/Sparkline.svelte +109 -0
  62. package/dist/atoms/Sparkline.svelte.d.ts +27 -0
  63. package/dist/atoms/SpeechBubble.svelte +187 -0
  64. package/dist/atoms/SpeechBubble.svelte.d.ts +33 -0
  65. package/dist/atoms/Spinner.svelte +57 -0
  66. package/dist/atoms/Spinner.svelte.d.ts +19 -0
  67. package/dist/atoms/Stat.svelte +72 -0
  68. package/dist/atoms/Stat.svelte.d.ts +19 -0
  69. package/dist/atoms/Table.svelte +151 -6
  70. package/dist/atoms/Table.svelte.d.ts +15 -3
  71. package/dist/atoms/TemporalField.svelte +472 -0
  72. package/dist/atoms/TemporalField.svelte.d.ts +48 -0
  73. package/dist/atoms/Text.svelte +23 -11
  74. package/dist/atoms/Text.svelte.d.ts +6 -3
  75. package/dist/atoms/Textarea.svelte +150 -44
  76. package/dist/atoms/Textarea.svelte.d.ts +35 -2
  77. package/dist/atoms/ThemeToggle.svelte +223 -44
  78. package/dist/atoms/ThemeToggle.svelte.d.ts +26 -3
  79. package/dist/atoms/TimeColumn.svelte +150 -0
  80. package/dist/atoms/TimeColumn.svelte.d.ts +25 -0
  81. package/dist/atoms/TimeField.svelte +151 -0
  82. package/dist/atoms/TimeField.svelte.d.ts +85 -0
  83. package/dist/atoms/Toast.svelte +22 -8
  84. package/dist/atoms/Toast.svelte.d.ts +2 -0
  85. package/dist/atoms/Toggle.svelte +98 -14
  86. package/dist/atoms/Toggle.svelte.d.ts +26 -2
  87. package/dist/atoms/Tooltip.svelte +528 -24
  88. package/dist/atoms/Tooltip.svelte.d.ts +18 -0
  89. package/dist/atoms/ZabiStringsProvider.svelte +45 -0
  90. package/dist/atoms/ZabiStringsProvider.svelte.d.ts +27 -0
  91. package/dist/atoms/index.d.ts +38 -0
  92. package/dist/atoms/index.js +29 -0
  93. package/dist/atoms/selection-control.styles.d.ts +47 -5
  94. package/dist/atoms/selection-control.styles.js +52 -10
  95. package/dist/components/atoms/index.d.ts +38 -0
  96. package/dist/components/atoms/index.d.ts.map +1 -1
  97. package/dist/components/atoms/selection-control.styles.d.ts +47 -5
  98. package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
  99. package/dist/components/index.d.ts +104 -2
  100. package/dist/components/index.d.ts.map +1 -1
  101. package/dist/components/map.d.ts +11 -0
  102. package/dist/components/map.d.ts.map +1 -0
  103. package/dist/components/molecules/index.d.ts +51 -0
  104. package/dist/components/molecules/index.d.ts.map +1 -1
  105. package/dist/components/molecules/toast-store.d.ts +83 -7
  106. package/dist/components/molecules/toast-store.d.ts.map +1 -1
  107. package/dist/components/organisms/index.d.ts +10 -0
  108. package/dist/components/organisms/index.d.ts.map +1 -1
  109. package/dist/components/types/variants.d.ts +24 -1
  110. package/dist/components/types/variants.d.ts.map +1 -1
  111. package/dist/components/util/app-shell.d.ts +79 -0
  112. package/dist/components/util/app-shell.d.ts.map +1 -0
  113. package/dist/components/util/avatar.d.ts +39 -0
  114. package/dist/components/util/avatar.d.ts.map +1 -0
  115. package/dist/components/util/bottom-sheet.d.ts +30 -0
  116. package/dist/components/util/bottom-sheet.d.ts.map +1 -0
  117. package/dist/components/util/bottom-tab-bar.d.ts +41 -0
  118. package/dist/components/util/bottom-tab-bar.d.ts.map +1 -0
  119. package/dist/components/util/calendar.d.ts +56 -0
  120. package/dist/components/util/calendar.d.ts.map +1 -0
  121. package/dist/components/util/chart.d.ts +140 -0
  122. package/dist/components/util/chart.d.ts.map +1 -0
  123. package/dist/components/util/chip.d.ts +66 -0
  124. package/dist/components/util/chip.d.ts.map +1 -0
  125. package/dist/components/util/cn.d.ts.map +1 -1
  126. package/dist/components/util/code-field.d.ts +63 -0
  127. package/dist/components/util/code-field.d.ts.map +1 -0
  128. package/dist/components/util/collapsible.d.ts +64 -0
  129. package/dist/components/util/collapsible.d.ts.map +1 -0
  130. package/dist/components/util/confirm-dialog.d.ts +12 -0
  131. package/dist/components/util/confirm-dialog.d.ts.map +1 -0
  132. package/dist/components/util/date.d.ts +96 -0
  133. package/dist/components/util/date.d.ts.map +1 -0
  134. package/dist/components/util/display-voice.d.ts +10 -0
  135. package/dist/components/util/display-voice.d.ts.map +1 -0
  136. package/dist/components/util/drawer.d.ts +13 -0
  137. package/dist/components/util/drawer.d.ts.map +1 -0
  138. package/dist/components/util/dropdown.d.ts +47 -0
  139. package/dist/components/util/dropdown.d.ts.map +1 -0
  140. package/dist/components/util/field-control.d.ts +30 -0
  141. package/dist/components/util/field-control.d.ts.map +1 -0
  142. package/dist/components/util/field.d.ts +45 -0
  143. package/dist/components/util/field.d.ts.map +1 -0
  144. package/dist/components/util/fit-in-viewport.d.ts +201 -0
  145. package/dist/components/util/fit-in-viewport.d.ts.map +1 -0
  146. package/dist/components/util/focus-utils.d.ts +77 -2
  147. package/dist/components/util/focus-utils.d.ts.map +1 -1
  148. package/dist/components/util/hydration.d.ts +45 -0
  149. package/dist/components/util/hydration.d.ts.map +1 -0
  150. package/dist/components/util/keyboard-inset.d.ts +18 -0
  151. package/dist/components/util/keyboard-inset.d.ts.map +1 -0
  152. package/dist/components/util/map-view.d.ts +144 -0
  153. package/dist/components/util/map-view.d.ts.map +1 -0
  154. package/dist/components/util/media-grid.d.ts +51 -0
  155. package/dist/components/util/media-grid.d.ts.map +1 -0
  156. package/dist/components/util/media.d.ts +6 -0
  157. package/dist/components/util/media.d.ts.map +1 -0
  158. package/dist/components/util/overlay-chrome.d.ts +53 -0
  159. package/dist/components/util/overlay-chrome.d.ts.map +1 -0
  160. package/dist/components/util/overlay.d.ts +72 -0
  161. package/dist/components/util/overlay.d.ts.map +1 -0
  162. package/dist/components/util/photo.d.ts +234 -0
  163. package/dist/components/util/photo.d.ts.map +1 -0
  164. package/dist/components/util/portal.d.ts +23 -0
  165. package/dist/components/util/portal.d.ts.map +1 -0
  166. package/dist/components/util/progress.d.ts +19 -0
  167. package/dist/components/util/progress.d.ts.map +1 -0
  168. package/dist/components/util/pull-to-refresh.d.ts +27 -0
  169. package/dist/components/util/pull-to-refresh.d.ts.map +1 -0
  170. package/dist/components/util/radio-keys.d.ts +25 -0
  171. package/dist/components/util/radio-keys.d.ts.map +1 -0
  172. package/dist/components/util/rating.d.ts +23 -0
  173. package/dist/components/util/rating.d.ts.map +1 -0
  174. package/dist/components/util/ready-made-strings.d.ts +77 -0
  175. package/dist/components/util/ready-made-strings.d.ts.map +1 -0
  176. package/dist/components/util/scroll-edge.d.ts +33 -0
  177. package/dist/components/util/scroll-edge.d.ts.map +1 -0
  178. package/dist/components/util/scroll-padding.d.ts +20 -0
  179. package/dist/components/util/scroll-padding.d.ts.map +1 -0
  180. package/dist/components/util/segmented-control.d.ts +16 -0
  181. package/dist/components/util/segmented-control.d.ts.map +1 -0
  182. package/dist/components/util/select.d.ts +41 -0
  183. package/dist/components/util/select.d.ts.map +1 -0
  184. package/dist/components/util/sheet-drag.d.ts +84 -0
  185. package/dist/components/util/sheet-drag.d.ts.map +1 -0
  186. package/dist/components/util/sidebar.d.ts +63 -0
  187. package/dist/components/util/sidebar.d.ts.map +1 -0
  188. package/dist/components/util/skeleton.d.ts +15 -0
  189. package/dist/components/util/skeleton.d.ts.map +1 -0
  190. package/dist/components/util/sliding-indicator.d.ts +25 -0
  191. package/dist/components/util/sliding-indicator.d.ts.map +1 -0
  192. package/dist/components/util/sortable-list.d.ts +65 -0
  193. package/dist/components/util/sortable-list.d.ts.map +1 -0
  194. package/dist/components/util/stepper.d.ts +40 -0
  195. package/dist/components/util/stepper.d.ts.map +1 -0
  196. package/dist/components/util/swipeable-list-item.d.ts +36 -0
  197. package/dist/components/util/swipeable-list-item.d.ts.map +1 -0
  198. package/dist/components/util/temporal-field.d.ts +89 -0
  199. package/dist/components/util/temporal-field.d.ts.map +1 -0
  200. package/dist/components/util/theme-mode.d.ts +80 -0
  201. package/dist/components/util/theme-mode.d.ts.map +1 -0
  202. package/dist/components/util/toaster.d.ts +133 -0
  203. package/dist/components/util/toaster.d.ts.map +1 -0
  204. package/dist/components/util/tone-surface.d.ts +34 -0
  205. package/dist/components/util/tone-surface.d.ts.map +1 -0
  206. package/dist/components/util/tooltip.d.ts +10 -0
  207. package/dist/components/util/tooltip.d.ts.map +1 -0
  208. package/dist/components/util/top-navbar.d.ts +35 -0
  209. package/dist/components/util/top-navbar.d.ts.map +1 -0
  210. package/dist/components/util/touch-target.d.ts +31 -0
  211. package/dist/components/util/touch-target.d.ts.map +1 -0
  212. package/dist/components/util/zabi-strings.d.ts +142 -0
  213. package/dist/components/util/zabi-strings.d.ts.map +1 -0
  214. package/dist/create-theme/cli.js +173 -0
  215. package/dist/create-theme/index.d.ts +190 -0
  216. package/dist/create-theme/index.js +1034 -0
  217. package/dist/create-theme/lib/ramp-math.js +128 -0
  218. package/dist/create-theme/lib/resolve.js +238 -0
  219. package/dist/create-theme/theme-data.js +2644 -0
  220. package/dist/index.d.ts +104 -2
  221. package/dist/index.js +75 -2
  222. package/dist/map.d.ts +10 -0
  223. package/dist/map.js +9 -0
  224. package/dist/molecules/Alert.svelte +63 -16
  225. package/dist/molecules/Alert.svelte.d.ts +14 -1
  226. package/dist/molecules/AppBar.svelte +674 -0
  227. package/dist/molecules/AppBar.svelte.d.ts +131 -0
  228. package/dist/molecules/AvatarGroup.svelte +116 -0
  229. package/dist/molecules/AvatarGroup.svelte.d.ts +40 -0
  230. package/dist/molecules/BarChart.svelte +381 -0
  231. package/dist/molecules/BarChart.svelte.d.ts +45 -0
  232. package/dist/molecules/BottomSheet.svelte +608 -0
  233. package/dist/molecules/BottomSheet.svelte.d.ts +109 -0
  234. package/dist/molecules/BottomTabBar.svelte +403 -0
  235. package/dist/molecules/BottomTabBar.svelte.d.ts +98 -0
  236. package/dist/molecules/Calendar.svelte +597 -0
  237. package/dist/molecules/Calendar.svelte.d.ts +72 -0
  238. package/dist/molecules/ChipGroup.svelte +325 -0
  239. package/dist/molecules/ChipGroup.svelte.d.ts +41 -0
  240. package/dist/molecules/Collapsible.svelte +347 -0
  241. package/dist/molecules/Collapsible.svelte.d.ts +61 -0
  242. package/dist/molecules/CollapsibleGroup.svelte +136 -0
  243. package/dist/molecules/CollapsibleGroup.svelte.d.ts +14 -0
  244. package/dist/molecules/ComponentDemo.svelte +16 -4
  245. package/dist/molecules/ComponentDemo.svelte.d.ts +3 -0
  246. package/dist/molecules/ConfirmDialog.svelte +257 -0
  247. package/dist/molecules/ConfirmDialog.svelte.d.ts +58 -0
  248. package/dist/molecules/ContactForm.svelte +34 -17
  249. package/dist/molecules/ContactForm.svelte.d.ts +8 -0
  250. package/dist/molecules/Drawer.svelte +415 -0
  251. package/dist/molecules/Drawer.svelte.d.ts +55 -0
  252. package/dist/molecules/Dropdown.svelte +517 -48
  253. package/dist/molecules/Dropdown.svelte.d.ts +47 -6
  254. package/dist/molecules/DropdownItem.svelte +122 -0
  255. package/dist/molecules/DropdownItem.svelte.d.ts +28 -0
  256. package/dist/molecules/EmptyState.svelte +55 -10
  257. package/dist/molecules/EmptyState.svelte.d.ts +8 -0
  258. package/dist/molecules/FormField.svelte +9 -1
  259. package/dist/molecules/FormField.svelte.d.ts +2 -0
  260. package/dist/molecules/Header.svelte +1 -1
  261. package/dist/molecules/ImageUpload.svelte +492 -69
  262. package/dist/molecules/ImageUpload.svelte.d.ts +82 -2
  263. package/dist/molecules/LineChart.svelte +459 -0
  264. package/dist/molecules/LineChart.svelte.d.ts +53 -0
  265. package/dist/molecules/MapView.svelte +565 -0
  266. package/dist/molecules/MapView.svelte.d.ts +88 -0
  267. package/dist/molecules/MediaGrid.svelte +527 -0
  268. package/dist/molecules/MediaGrid.svelte.d.ts +77 -0
  269. package/dist/molecules/Modal.svelte +293 -72
  270. package/dist/molecules/Modal.svelte.d.ts +51 -2
  271. package/dist/molecules/NavigationMenuContent.svelte +58 -1
  272. package/dist/molecules/NavigationMenuItem.svelte +0 -1
  273. package/dist/molecules/NavigationMenuLink.svelte +1 -1
  274. package/dist/molecules/NavigationMenuList.svelte +10 -3
  275. package/dist/molecules/NavigationMenuTrigger.svelte +2 -2
  276. package/dist/molecules/Page.svelte +32 -2
  277. package/dist/molecules/Page.svelte.d.ts +11 -0
  278. package/dist/molecules/PhotoGrid.svelte +523 -0
  279. package/dist/molecules/PhotoGrid.svelte.d.ts +73 -0
  280. package/dist/molecules/PhotoViewer.svelte +1213 -0
  281. package/dist/molecules/PhotoViewer.svelte.d.ts +74 -0
  282. package/dist/molecules/PieChart.svelte +421 -0
  283. package/dist/molecules/PieChart.svelte.d.ts +51 -0
  284. package/dist/molecules/PropsTable.svelte +22 -10
  285. package/dist/molecules/PropsTable.svelte.d.ts +3 -0
  286. package/dist/molecules/PullToRefresh.svelte +263 -0
  287. package/dist/molecules/PullToRefresh.svelte.d.ts +52 -0
  288. package/dist/molecules/RadioGroup.svelte +72 -18
  289. package/dist/molecules/RadioGroup.svelte.d.ts +19 -3
  290. package/dist/molecules/Section.svelte +1 -1
  291. package/dist/molecules/SegmentedControl.svelte +456 -0
  292. package/dist/molecules/SegmentedControl.svelte.d.ts +40 -0
  293. package/dist/molecules/SidebarBrandHeader.svelte +11 -1
  294. package/dist/molecules/SidebarBrandHeader.svelte.d.ts +3 -0
  295. package/dist/molecules/SidebarFooter.svelte +30 -7
  296. package/dist/molecules/SidebarFooter.svelte.d.ts +3 -0
  297. package/dist/molecules/SidebarNavSection.svelte +1 -1
  298. package/dist/molecules/SlideUp.svelte +220 -61
  299. package/dist/molecules/SlideUp.svelte.d.ts +26 -0
  300. package/dist/molecules/SortableList.svelte +633 -0
  301. package/dist/molecules/SortableList.svelte.d.ts +69 -0
  302. package/dist/molecules/Stepper.svelte +562 -0
  303. package/dist/molecules/Stepper.svelte.d.ts +39 -0
  304. package/dist/molecules/StickyActionBar.svelte +250 -0
  305. package/dist/molecules/StickyActionBar.svelte.d.ts +78 -0
  306. package/dist/molecules/SwipeableListItem.svelte +334 -0
  307. package/dist/molecules/SwipeableListItem.svelte.d.ts +54 -0
  308. package/dist/molecules/Tabs.svelte +310 -9
  309. package/dist/molecules/Tabs.svelte.d.ts +7 -0
  310. package/dist/molecules/Toaster.svelte +514 -5
  311. package/dist/molecules/Toaster.svelte.d.ts +153 -2
  312. package/dist/molecules/ToasterToast.svelte +495 -77
  313. package/dist/molecules/ToasterToast.svelte.d.ts +15 -0
  314. package/dist/molecules/UnsavedChangesBar.svelte +223 -0
  315. package/dist/molecules/UnsavedChangesBar.svelte.d.ts +43 -0
  316. package/dist/molecules/index.d.ts +51 -0
  317. package/dist/molecules/index.js +31 -0
  318. package/dist/molecules/toast-store.d.ts +83 -7
  319. package/dist/molecules/toast-store.js +25 -1
  320. package/dist/organisms/AppNavigation.svelte +440 -0
  321. package/dist/organisms/AppNavigation.svelte.d.ts +72 -0
  322. package/dist/organisms/AppShell.svelte +696 -0
  323. package/dist/organisms/AppShell.svelte.d.ts +157 -0
  324. package/dist/organisms/AppShellHeaderPart.svelte +163 -0
  325. package/dist/organisms/AppShellHeaderPart.svelte.d.ts +57 -0
  326. package/dist/organisms/SidebarAccountPanel.svelte +107 -12
  327. package/dist/organisms/SidebarAccountPanel.svelte.d.ts +19 -0
  328. package/dist/organisms/SidebarNavigation.svelte +111 -43
  329. package/dist/organisms/SidebarNavigation.svelte.d.ts +29 -1
  330. package/dist/organisms/SidebarPanel.svelte +34 -17
  331. package/dist/organisms/SidebarPanel.svelte.d.ts +1 -0
  332. package/dist/organisms/SidebarShell.svelte +164 -14
  333. package/dist/organisms/SidebarShell.svelte.d.ts +56 -2
  334. package/dist/organisms/TopNavbar.svelte +353 -39
  335. package/dist/organisms/TopNavbar.svelte.d.ts +47 -0
  336. package/dist/organisms/TopNavbarSheetContent.svelte +145 -0
  337. package/dist/organisms/TopNavbarSheetContent.svelte.d.ts +24 -0
  338. package/dist/organisms/index.d.ts +10 -0
  339. package/dist/organisms/index.js +7 -0
  340. package/dist/types/components.d.ts +21 -1
  341. package/dist/types/components.d.ts.map +1 -1
  342. package/dist/types/index.d.ts +1093 -26
  343. package/dist/types/index.d.ts.map +1 -1
  344. package/dist/types/variants.ts +28 -1
  345. package/dist/util/app-shell.d.ts +78 -0
  346. package/dist/util/app-shell.js +97 -0
  347. package/dist/util/avatar.d.ts +38 -0
  348. package/dist/util/avatar.js +52 -0
  349. package/dist/util/bottom-sheet.d.ts +29 -0
  350. package/dist/util/bottom-sheet.js +43 -0
  351. package/dist/util/bottom-tab-bar.d.ts +40 -0
  352. package/dist/util/bottom-tab-bar.js +54 -0
  353. package/dist/util/calendar.d.ts +55 -0
  354. package/dist/util/calendar.js +74 -0
  355. package/dist/util/chart.d.ts +139 -0
  356. package/dist/util/chart.js +305 -0
  357. package/dist/util/chip.d.ts +65 -0
  358. package/dist/util/chip.js +58 -0
  359. package/dist/util/cn.js +28 -2
  360. package/dist/util/code-field.d.ts +62 -0
  361. package/dist/util/code-field.js +112 -0
  362. package/dist/util/collapsible.d.ts +63 -0
  363. package/dist/util/collapsible.js +23 -0
  364. package/dist/util/confirm-dialog.d.ts +11 -0
  365. package/dist/util/confirm-dialog.js +2 -0
  366. package/dist/util/date.d.ts +95 -0
  367. package/dist/util/date.js +237 -0
  368. package/dist/util/display-voice.d.ts +9 -0
  369. package/dist/util/display-voice.js +9 -0
  370. package/dist/util/drawer.d.ts +12 -0
  371. package/dist/util/drawer.js +7 -0
  372. package/dist/util/dropdown.d.ts +46 -0
  373. package/dist/util/dropdown.js +21 -0
  374. package/dist/util/field-control.d.ts +29 -0
  375. package/dist/util/field-control.js +32 -0
  376. package/dist/util/field.d.ts +44 -0
  377. package/dist/util/field.js +38 -0
  378. package/dist/util/fit-in-viewport.d.ts +200 -0
  379. package/dist/util/fit-in-viewport.js +399 -0
  380. package/dist/util/focus-utils.d.ts +77 -2
  381. package/dist/util/focus-utils.js +186 -9
  382. package/dist/util/hydration.d.ts +44 -0
  383. package/dist/util/hydration.js +58 -0
  384. package/dist/util/keyboard-inset.d.ts +17 -0
  385. package/dist/util/keyboard-inset.js +53 -0
  386. package/dist/util/map-view.d.ts +143 -0
  387. package/dist/util/map-view.js +121 -0
  388. package/dist/util/media-grid.d.ts +50 -0
  389. package/dist/util/media-grid.js +81 -0
  390. package/dist/util/media.d.ts +5 -0
  391. package/dist/util/media.js +10 -0
  392. package/dist/util/overlay-chrome.d.ts +52 -0
  393. package/dist/util/overlay-chrome.js +56 -0
  394. package/dist/util/overlay.d.ts +71 -0
  395. package/dist/util/overlay.js +240 -0
  396. package/dist/util/photo.d.ts +233 -0
  397. package/dist/util/photo.js +209 -0
  398. package/dist/util/portal.d.ts +22 -0
  399. package/dist/util/portal.js +50 -0
  400. package/dist/util/progress.d.ts +18 -0
  401. package/dist/util/progress.js +17 -0
  402. package/dist/util/pull-to-refresh.d.ts +26 -0
  403. package/dist/util/pull-to-refresh.js +30 -0
  404. package/dist/util/radio-keys.d.ts +24 -0
  405. package/dist/util/radio-keys.js +49 -0
  406. package/dist/util/rating.d.ts +22 -0
  407. package/dist/util/rating.js +26 -0
  408. package/dist/util/ready-made-strings.d.ts +76 -0
  409. package/dist/util/ready-made-strings.js +67 -0
  410. package/dist/util/scroll-edge.d.ts +32 -0
  411. package/dist/util/scroll-edge.js +87 -0
  412. package/dist/util/scroll-padding.d.ts +19 -0
  413. package/dist/util/scroll-padding.js +44 -0
  414. package/dist/util/segmented-control.d.ts +15 -0
  415. package/dist/util/segmented-control.js +1 -0
  416. package/dist/util/select.d.ts +40 -0
  417. package/dist/util/select.js +63 -0
  418. package/dist/util/sheet-drag.d.ts +83 -0
  419. package/dist/util/sheet-drag.js +311 -0
  420. package/dist/util/sidebar.d.ts +62 -0
  421. package/dist/util/sidebar.js +38 -0
  422. package/dist/util/skeleton.d.ts +14 -0
  423. package/dist/util/skeleton.js +22 -0
  424. package/dist/util/sliding-indicator.d.ts +24 -0
  425. package/dist/util/sliding-indicator.js +44 -0
  426. package/dist/util/sortable-list.d.ts +64 -0
  427. package/dist/util/sortable-list.js +53 -0
  428. package/dist/util/stepper.d.ts +39 -0
  429. package/dist/util/stepper.js +38 -0
  430. package/dist/util/swipeable-list-item.d.ts +35 -0
  431. package/dist/util/swipeable-list-item.js +32 -0
  432. package/dist/util/temporal-field.d.ts +88 -0
  433. package/dist/util/temporal-field.js +136 -0
  434. package/dist/util/theme-mode.d.ts +79 -0
  435. package/dist/util/theme-mode.js +121 -0
  436. package/dist/util/toaster.d.ts +132 -0
  437. package/dist/util/toaster.js +130 -0
  438. package/dist/util/tone-surface.d.ts +33 -0
  439. package/dist/util/tone-surface.js +35 -0
  440. package/dist/util/tooltip.d.ts +9 -0
  441. package/dist/util/tooltip.js +18 -0
  442. package/dist/util/top-navbar.d.ts +34 -0
  443. package/dist/util/top-navbar.js +28 -0
  444. package/dist/util/touch-target.d.ts +30 -0
  445. package/dist/util/touch-target.js +30 -0
  446. package/dist/util/zabi-strings.d.ts +141 -0
  447. package/dist/util/zabi-strings.js +105 -0
  448. package/dist/zabi-components-colors.css +11 -2
  449. package/dist/zabi-components-theme-dark-only.css +870 -258
  450. package/dist/zabi-components-theme-dark.css +870 -258
  451. package/dist/zabi-components-theme-only.css +2419 -368
  452. package/dist/zabi-components-theme.css +2419 -368
  453. package/dist/zabi-components.css +6032 -711
  454. package/docs/lucide-icons.md +40 -17
  455. package/docs/theme-imports.md +81 -11
  456. package/package.json +45 -8
@@ -0,0 +1,54 @@
1
+ import { type Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ import { type SwipeableListItemAction, type SwipeableListItemStrings } from "../util/swipeable-list-item.js";
4
+ /**
5
+ * A row with one or two actions behind its inline end, such as Delete on
6
+ * a draft. A finger swipes the row towards the inline start to show them;
7
+ * a swipe only shows them, and an action runs when its button is pressed.
8
+ *
9
+ * The swipe is a shortcut and never the only way. The row has a "more"
10
+ * button at its end that opens the same actions for a mouse, a keyboard,
11
+ * a switch or a screen reader, and they are ordinary buttons once open.
12
+ * Escape or a press elsewhere closes the row. In a list (`ul`, `ol`,
13
+ * `List`) one row is open at a time.
14
+ *
15
+ * It is the row's content and a group of buttons, not a list item: put
16
+ * it inside your own `li` or `ListItem`.
17
+ *
18
+ * ```svelte
19
+ * <ul>
20
+ * {#each drafts as draft (draft.id)}
21
+ * <li>
22
+ * <SwipeableListItem
23
+ * actions={[{ id: "delete", label: "Delete", tone: "danger", onselect: () => remove(draft) }]}
24
+ * >
25
+ * {draft.title}
26
+ * </SwipeableListItem>
27
+ * </li>
28
+ * {/each}
29
+ * </ul>
30
+ * ```
31
+ */
32
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "children"> & {
33
+ /** One or two actions, shown at the inline end. */
34
+ actions: SwipeableListItemAction[];
35
+ /** Whether the actions are shown. Bindable. */
36
+ open?: boolean;
37
+ /** Runs when the row opens or closes, by whatever means. */
38
+ onopenchange?: (open: boolean) => void;
39
+ /**
40
+ * The button at the end of the row that opens the actions without a
41
+ * swipe. Without it the swipe is the only way this component offers,
42
+ * and the app has to give another: the same actions on the page the
43
+ * row leads to, or in a menu (WCAG 2.5.1, 2.5.7).
44
+ */
45
+ showMoreButton?: boolean;
46
+ /** The words the row says by itself, for another language. */
47
+ strings?: Partial<SwipeableListItemStrings>;
48
+ class?: string;
49
+ /** The row's content. */
50
+ children?: Snippet;
51
+ };
52
+ declare const SwipeableListItem: import("svelte").Component<Props, {}, "open">;
53
+ type SwipeableListItem = ReturnType<typeof SwipeableListItem>;
54
+ export default SwipeableListItem;
@@ -2,6 +2,12 @@
2
2
  import type { Snippet } from "svelte";
3
3
  import { generateId } from "../util/ssr-safe.js";
4
4
  import { cn } from "../util/cn.js";
5
+ import {
6
+ afterFrame,
7
+ measureIndicator,
8
+ observeSizes,
9
+ type IndicatorBox,
10
+ } from "../util/sliding-indicator.js";
5
11
 
6
12
  interface Props {
7
13
  /** Extra classes for the host element. */
@@ -14,6 +20,13 @@
14
20
  /** Selected tab id; supports `bind:activeTab`. */
15
21
  activeTab?: string;
16
22
  variant?: "default" | "pills";
23
+ /**
24
+ * The tabs share the row equally. Meant for two or three tabs on a
25
+ * phone. A label too long for its share wraps between words; when a
26
+ * word does not fit, the tabs keep their words whole and the row
27
+ * scrolls sideways instead.
28
+ */
29
+ fullWidth?: boolean;
17
30
  onclick?: (event: Event) => void;
18
31
  onkeydown?: (event: Event) => void;
19
32
  children?: Snippet<[{ activeTab: string }]>;
@@ -22,12 +35,24 @@
22
35
  let {
23
36
  class: className = "",
24
37
  tabs = [],
25
- activeTab = $bindable(""),
38
+ activeTab = $bindable<Exclude<Props["activeTab"], undefined>>(),
26
39
  variant = "default",
40
+ fullWidth = false,
27
41
  children,
28
42
  ...restProps
29
43
  }: Props = $props();
30
44
 
45
+ // No fallback on a bindable prop: Svelte refuses `bind:…={undefined}` on
46
+ // one that has a fallback (`props_invalid_value`), and a page that throws
47
+ // while it hydrates never becomes interactive. The default is applied
48
+ // here instead: at once, for the server and the first render, and again
49
+ // whenever a parent hands back `undefined`.
50
+ const applyDefaults = () => {
51
+ if (activeTab === undefined) activeTab = "";
52
+ };
53
+ applyDefaults();
54
+ $effect.pre(applyDefaults);
55
+
31
56
  const tabsBaseId = generateId("tabs");
32
57
 
33
58
  /** Button refs keyed by tab id — roving tabindex needs focus to follow selection. */
@@ -36,8 +61,151 @@
36
61
  function selectTab(tabId: string, moveFocus = false) {
37
62
  activeTab = tabId;
38
63
  if (moveFocus) {
39
- tabElements[tabId]?.focus();
64
+ // The list brings the tab into view itself (`handleFocusIn`), with
65
+ // room for the fade; the browser's own jump would come first.
66
+ tabElements[tabId]?.focus({ preventScroll: true });
67
+ }
68
+ }
69
+
70
+ /**
71
+ * When the tabs do not fit, the list scrolls sideways. It used to do
72
+ * neither that nor wrap: in a 160px card the third tab ended 94px outside
73
+ * it. Each edge with more tabs beyond it fades out, so a cut-off label
74
+ * reads as "there is more" and not as a mistake.
75
+ */
76
+ let listElement = $state<HTMLDivElement | null>(null);
77
+ /** Physical sides, since a mask is drawn in physical directions. */
78
+ let fadeLeft = $state(false);
79
+ let fadeRight = $state(false);
80
+
81
+ /** Room kept between a revealed tab and the edge: the fade, and the tab's focus ring. */
82
+ const REVEAL_MARGIN = 32;
83
+
84
+ function updateFades() {
85
+ const list = listElement;
86
+ if (!list) return;
87
+ const hidden = list.scrollWidth - list.clientWidth;
88
+ if (hidden <= 1) {
89
+ fadeLeft = false;
90
+ fadeRight = false;
91
+ return;
92
+ }
93
+ // Right to left, `scrollLeft` runs from 0 down to minus the hidden width.
94
+ const rtl = getComputedStyle(list).direction === "rtl";
95
+ const fromLeft = rtl ? hidden + list.scrollLeft : list.scrollLeft;
96
+ fadeLeft = fromLeft > 1;
97
+ fadeRight = fromLeft < hidden - 1;
98
+ }
99
+
100
+ /** Scrolls the list, and only the list, until `tab` is clear of both edges. */
101
+ function reveal(tab: HTMLElement | undefined, animate: boolean) {
102
+ const list = listElement;
103
+ if (!list || !tab || list.scrollWidth - list.clientWidth <= 1) return;
104
+ const box = list.getBoundingClientRect();
105
+ const item = tab.getBoundingClientRect();
106
+ let delta = 0;
107
+ if (item.left < box.left + REVEAL_MARGIN) {
108
+ delta = item.left - box.left - REVEAL_MARGIN;
109
+ } else if (item.right > box.right - REVEAL_MARGIN) {
110
+ delta = item.right - box.right + REVEAL_MARGIN;
111
+ }
112
+ if (delta === 0) return;
113
+ const still =
114
+ !animate ||
115
+ (typeof window.matchMedia === "function" &&
116
+ window.matchMedia("(prefers-reduced-motion: reduce)").matches);
117
+ // `scrollBy` with a behaviour is missing from older engines and from jsdom.
118
+ if (typeof list.scrollBy === "function") {
119
+ list.scrollBy({ left: delta, behavior: still ? "instant" : "smooth" });
120
+ } else {
121
+ list.scrollLeft += delta;
122
+ }
123
+ }
124
+
125
+ // The selected tab is brought into view when the list mounts and when the
126
+ // selection changes: at once the first time, so the page does not open on
127
+ // a moving list.
128
+ let revealedOnce = false;
129
+ $effect(() => {
130
+ const tab = tabElements[activeTab];
131
+ if (!listElement) return;
132
+ reveal(tab, revealedOnce);
133
+ revealedOnce = true;
134
+ updateFades();
135
+ });
136
+
137
+ // The fades follow the list's own size and the number of tabs.
138
+ $effect(() => {
139
+ const list = listElement;
140
+ void tabs.length;
141
+ if (!list) return;
142
+ updateFades();
143
+ if (typeof ResizeObserver === "undefined") return;
144
+ const observer = new ResizeObserver(updateFades);
145
+ observer.observe(list);
146
+ return () => observer.disconnect();
147
+ });
148
+
149
+ /**
150
+ * The sliding indicator: the underline, or the pill. Until a box has been
151
+ * measured (on the server, before hydration, with no layout) it is null and
152
+ * the selected tab draws its own mark; with a box, that mark moves to the
153
+ * indicator, which slides between tabs. It lives in the scrolling list and
154
+ * is placed against the list's content, so it stays on its tab as the list
155
+ * scrolls.
156
+ */
157
+ let indicator = $state<IndicatorBox | null>(null);
158
+ /** Off for the first placement, so the indicator appears in place and does not fly in. */
159
+ let indicatorMotion = $state(false);
160
+
161
+ function measure() {
162
+ const tab = tabElements[activeTab];
163
+ indicator = listElement && tab ? measureIndicator(listElement, tab) : null;
164
+ }
165
+
166
+ $effect(() => {
167
+ void [tabs, activeTab, variant, fullWidth, listElement];
168
+ measure();
169
+ });
170
+
171
+ // The tabs resize with a loaded font, zoomed text or a wider row.
172
+ $effect(() => {
173
+ const list = listElement;
174
+ void tabs.length;
175
+ if (!list) return;
176
+ return observeSizes([list, ...Object.values(tabElements)], measure);
177
+ });
178
+
179
+ $effect(() => {
180
+ if (!indicator || indicatorMotion) return;
181
+ return afterFrame(() => (indicatorMotion = true));
182
+ });
183
+
184
+ /** The underline is the bottom 2px of the tab's box; the pill is all of it. */
185
+ const indicatorStyle = $derived.by(() => {
186
+ if (!indicator) return "";
187
+ const { x, y, width, height } = indicator;
188
+ const line = variant === "pills" ? height : 2;
189
+ const top = variant === "pills" ? y : y + height - 2;
190
+ return `transform: translate(${x}px, ${top}px); width: ${width}px; height: ${line}px;`;
191
+ });
192
+
193
+ /**
194
+ * Arrow keys and Tab move focus to a tab that may be out of view. Keyboard
195
+ * focus only: a press focuses the tab on the way down, and moving the list
196
+ * then would take the tab from under the pointer before the click lands.
197
+ * A pressed tab is revealed once it is selected, by the effect above.
198
+ */
199
+ function handleFocusIn(event: FocusEvent) {
200
+ const tab = (event.target as HTMLElement | null)?.closest<HTMLElement>('[role="tab"]');
201
+ if (!tab) return;
202
+ let fromKeyboard = true;
203
+ try {
204
+ fromKeyboard = tab.matches(":focus-visible");
205
+ } catch {
206
+ // An engine without `:focus-visible`: treat every focus as the keyboard's.
40
207
  }
208
+ if (fromKeyboard) reveal(tab, true);
41
209
  }
42
210
 
43
211
  function getEnabledTabs() {
@@ -91,27 +259,92 @@
91
259
  }
92
260
  // Enter/Space: native <button> click already selects the tab.
93
261
  }
262
+
263
+ const TAB_BASE =
264
+ "focus-ring relative cursor-pointer border-b-2 px-4 py-2 pointer-coarse:min-h-11 text-sm font-medium transition-colors focus:outline-none focus-visible:outline-none disabled:cursor-not-allowed";
265
+ // A tab keeps its width and the list scrolls; sharing the row, it may shrink and its label wrap.
266
+ const TAB_NATURAL = "shrink-0 whitespace-nowrap";
267
+ // `min-w-min` is the tab's longest word: a label wraps between words, never
268
+ // inside one, and when the words do not fit the shares the row scrolls like
269
+ // any other.
270
+ const TAB_SHARED = "min-w-min flex-1 basis-0 text-center";
271
+ // A disabled tab is never the selected one, so only this arm carries the disabled state.
272
+ const TAB_IDLE =
273
+ "border-transparent text-description hover:border-border-medium hover:text-body active:bg-surface-active disabled:opacity-50 disabled:hover:border-transparent disabled:hover:text-description disabled:active:bg-transparent";
274
+ const TAB_SELECTED = "border-brand-500 text-body active:bg-surface-active";
275
+ // The pill has a fill of its own, so its pressed state is a further step of
276
+ // that fill; a translucent tint in its place would be a weaker fill, not a
277
+ // stronger one. It is the pressed role, not the hover step: the hover step
278
+ // is 1.24:1 from the resting fill in light, under the 1.25:1 a pressed fill
279
+ // is held to. The label darkens with it (`.text-link:active` in app.css)
280
+ // and keeps 4.89:1 on the pressed fill.
281
+ const TAB_SELECTED_PILL =
282
+ "border-brand-500 bg-action-primary-subtle text-link active:bg-action-primary-subtle-active";
283
+
284
+ // With the indicator the tab keeps its label colour and its pressed fill
285
+ // only. Forced colours drop backgrounds, so the underline of the tab itself
286
+ // stays there and the indicator is hidden.
287
+ const TAB_SELECTED_MOVED =
288
+ "border-brand-500 not-forced-colors:border-transparent text-body active:bg-surface-active";
289
+ const TAB_SELECTED_PILL_MOVED =
290
+ "border-brand-500 not-forced-colors:border-transparent text-link active:bg-action-primary-subtle-active";
291
+
292
+ function tabClasses(tabId: string): string {
293
+ const base = `${TAB_BASE} ${fullWidth ? TAB_SHARED : TAB_NATURAL}`;
294
+ if (activeTab !== tabId) return `${base} ${TAB_IDLE}`;
295
+ if (indicator) {
296
+ return `${base} ${variant === "pills" ? TAB_SELECTED_PILL_MOVED : TAB_SELECTED_MOVED}`;
297
+ }
298
+ return `${base} ${variant === "pills" ? TAB_SELECTED_PILL : TAB_SELECTED}`;
299
+ }
94
300
  </script>
95
301
 
96
302
  <div class={cn("tabs-container", className)}>
303
+ <!-- The tablist keeps the line under the tabs, and the tabs scroll in a
304
+ box of their own inside it, so the line stays put. That box is
305
+ presentational: the tabs are still the tablist's tabs. -->
306
+ <!-- `flow-root`: the inner box's negative margin stays inside this one,
307
+ so the row is as tall as its tabs and its line, as it was. -->
97
308
  <div
98
- class="flex border-b border-base-200"
309
+ class="flow-root border-b border-border"
99
310
  role="tablist"
100
311
  tabindex="-1"
101
312
  onkeydown={handleKeydown}
102
313
  >
314
+ <!-- `tabindex="-1"`: a scrolling box is otherwise a Tab stop of its own. -->
315
+ <div
316
+ bind:this={listElement}
317
+ class="tabs-list relative flex"
318
+ role="presentation"
319
+ tabindex="-1"
320
+ data-fade-left={fadeLeft ? "" : undefined}
321
+ data-fade-right={fadeRight ? "" : undefined}
322
+ onfocusin={handleFocusIn}
323
+ onscroll={updateFades}
324
+ >
325
+ {#if indicator}
326
+ <!-- Under the tabs (they are positioned and come later), and never in their way. -->
327
+ <span
328
+ class={cn(
329
+ "pointer-events-none absolute top-0 left-0 forced-colors:hidden",
330
+ variant === "pills" ? "bg-action-primary-subtle border-b-2 border-brand-500" : "bg-brand-500",
331
+ // The underline must not overshoot; the pill may spring.
332
+ indicatorMotion &&
333
+ "transition-[transform,width,height] duration-(--duration-moderate) motion-reduce:transition-none",
334
+ indicatorMotion && (variant === "pills" ? "ease-spring" : "ease-standard"),
335
+ )}
336
+ aria-hidden="true"
337
+ data-indicator={variant}
338
+ style={indicatorStyle}
339
+ ></span>
340
+ {/if}
103
341
  {#each tabs as tab (tab.id)}
104
342
  <button
105
343
  bind:this={tabElements[tab.id]}
106
344
  type="button"
107
345
  role="tab"
108
346
  id={getTabId(tab.id)}
109
- class="focus-ring cursor-pointer border-b-2 px-4 py-2 text-sm font-medium transition-colors focus:outline-none focus-visible:outline-none active:bg-surface-active disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:border-transparent disabled:hover:text-description {activeTab ===
110
- tab.id
111
- ? variant === 'pills'
112
- ? 'border-brand-500 bg-brand-100 text-brand-700'
113
- : 'border-brand-500 text-body'
114
- : 'border-transparent text-description hover:border-border-medium hover:text-body'}"
347
+ class={tabClasses(tab.id)}
115
348
  onclick={() => selectTab(tab.id)}
116
349
  disabled={tab.disabled}
117
350
  aria-selected={activeTab === tab.id}
@@ -122,6 +355,7 @@
122
355
  </button>
123
356
  {/each}
124
357
  </div>
358
+ </div>
125
359
 
126
360
  <div
127
361
  class="mt-4"
@@ -132,3 +366,70 @@
132
366
  {@render children?.({ activeTab })}
133
367
  </div>
134
368
  </div>
369
+
370
+ <style>
371
+ /*
372
+ * The list scrolls sideways when its tabs do not fit, with no scrollbar:
373
+ * the fade says there is more. A scroll container clips what is outside
374
+ * it, and a tab's focus ring is 4px outside the tab, so the list is 4px
375
+ * larger on every side than the row it fills and pulled back by the same
376
+ * amount. A list that fits looks exactly as it did.
377
+ */
378
+ .tabs-list {
379
+ --tabs-ring-room: 4px;
380
+ margin: calc(var(--tabs-ring-room) * -1);
381
+ padding: var(--tabs-ring-room);
382
+ overflow-x: auto;
383
+ overflow-y: hidden;
384
+ overscroll-behavior-x: contain;
385
+ scrollbar-width: none;
386
+ }
387
+
388
+ .tabs-list::-webkit-scrollbar {
389
+ display: none;
390
+ }
391
+
392
+ /*
393
+ * A mask, not a gradient laid over the tabs: it fades them into whatever
394
+ * surface is behind, and nothing sits on top of a tab or its focus ring.
395
+ * Only an edge with more tabs beyond it fades, and a list that fits has no
396
+ * mask at all. Left and right are physical here; the script works out
397
+ * which is which in a right-to-left layout.
398
+ */
399
+ .tabs-list[data-fade-left],
400
+ .tabs-list[data-fade-right] {
401
+ --tabs-fade-left: 0px;
402
+ --tabs-fade-right: 0px;
403
+ -webkit-mask-image: linear-gradient(
404
+ to right,
405
+ transparent,
406
+ black var(--tabs-fade-left),
407
+ black calc(100% - var(--tabs-fade-right)),
408
+ transparent
409
+ );
410
+ mask-image: linear-gradient(
411
+ to right,
412
+ transparent,
413
+ black var(--tabs-fade-left),
414
+ black calc(100% - var(--tabs-fade-right)),
415
+ transparent
416
+ );
417
+ }
418
+
419
+ .tabs-list[data-fade-left] {
420
+ --tabs-fade-left: 1.5rem;
421
+ }
422
+
423
+ .tabs-list[data-fade-right] {
424
+ --tabs-fade-right: 1.5rem;
425
+ }
426
+
427
+ /* Forced colours: a half-transparent label is harder to read than a cut-off one. */
428
+ @media (forced-colors: active) {
429
+ .tabs-list[data-fade-left],
430
+ .tabs-list[data-fade-right] {
431
+ -webkit-mask-image: none;
432
+ mask-image: none;
433
+ }
434
+ }
435
+ </style>
@@ -10,6 +10,13 @@ interface Props {
10
10
  /** Selected tab id; supports `bind:activeTab`. */
11
11
  activeTab?: string;
12
12
  variant?: "default" | "pills";
13
+ /**
14
+ * The tabs share the row equally. Meant for two or three tabs on a
15
+ * phone. A label too long for its share wraps between words; when a
16
+ * word does not fit, the tabs keep their words whole and the row
17
+ * scrolls sideways instead.
18
+ */
19
+ fullWidth?: boolean;
13
20
  onclick?: (event: Event) => void;
14
21
  onkeydown?: (event: Event) => void;
15
22
  children?: Snippet<[{