@noxlovette/material 0.5.0 → 0.6.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 (316) hide show
  1. package/bin/material-claude-skill.js +31 -0
  2. package/claude-skill/material-design/SKILL.md +68 -0
  3. package/claude-skill/material-design/references/accessibility-checklist.md +29 -0
  4. package/claude-skill/material-design/references/component-patterns.md +55 -0
  5. package/claude-skill/material-design/references/motion-guide.md +163 -0
  6. package/claude-skill/material-design/references/tokens-and-styles.md +68 -0
  7. package/dist/animation/Shapes.stories.svelte +60 -0
  8. package/dist/animation/Shapes.stories.svelte.d.ts +3 -0
  9. package/dist/animation/TransitionPatterns.stories.svelte +32 -20
  10. package/dist/animation/containerTransform.d.ts +11 -0
  11. package/dist/animation/containerTransform.js +11 -0
  12. package/dist/animation/enterExit.d.ts +9 -0
  13. package/dist/animation/enterExit.js +6 -3
  14. package/dist/animation/index.d.ts +1 -0
  15. package/dist/animation/index.js +1 -0
  16. package/dist/animation/shapeMorph.svelte.d.ts +52 -0
  17. package/dist/animation/shapeMorph.svelte.js +112 -0
  18. package/dist/animation/shapesAnimatableSmall.d.ts +28 -22
  19. package/dist/animation/shapesAnimatableSmall.js +32 -23
  20. package/dist/animation/sharedAxisTransition.d.ts +22 -0
  21. package/dist/animation/sharedAxisTransition.js +25 -1
  22. package/dist/animation/skeleton.d.ts +6 -1
  23. package/dist/animation/skeleton.js +6 -1
  24. package/dist/components/badge/Badge.mdx +70 -0
  25. package/dist/components/badge/Badge.stories.svelte +38 -20
  26. package/dist/components/badge/Badge.svelte +12 -5
  27. package/dist/components/badge/theme.d.ts +30 -7
  28. package/dist/components/badge/theme.js +24 -4
  29. package/dist/components/badge/types.d.ts +18 -4
  30. package/dist/components/buttons/Button.mdx +85 -0
  31. package/dist/components/buttons/Button.stories.svelte +62 -27
  32. package/dist/components/buttons/Button.svelte +29 -25
  33. package/dist/components/buttons/Button.svelte.d.ts +11 -9
  34. package/dist/components/buttons/ButtonIcon.mdx +52 -0
  35. package/dist/components/buttons/ButtonIcon.stories.svelte +72 -48
  36. package/dist/components/buttons/ButtonIcon.svelte +58 -36
  37. package/dist/components/buttons/ButtonIcon.svelte.d.ts +9 -7
  38. package/dist/components/buttons/FAB.mdx +84 -0
  39. package/dist/components/buttons/FAB.stories.svelte +103 -24
  40. package/dist/components/buttons/FAB.stories.svelte.d.ts +2 -17
  41. package/dist/components/buttons/FAB.svelte +353 -29
  42. package/dist/components/buttons/FAB.svelte.d.ts +11 -6
  43. package/dist/components/buttons/FABMenu.svelte +27 -8
  44. package/dist/components/buttons/FABMenu.svelte.d.ts +7 -3
  45. package/dist/components/buttons/FABMenuItem.svelte +20 -16
  46. package/dist/components/buttons/FABMenuItem.svelte.d.ts +3 -3
  47. package/dist/components/buttons/Toggle.stories.svelte +31 -23
  48. package/dist/components/buttons/Toggle.stories.svelte.d.ts +2 -17
  49. package/dist/components/buttons/Toggle.svelte +28 -25
  50. package/dist/components/buttons/Toggle.svelte.d.ts +5 -5
  51. package/dist/components/buttons/button-group/ButtonGroup.mdx +39 -0
  52. package/dist/components/buttons/button-group/ButtonGroup.stories.svelte +62 -43
  53. package/dist/components/buttons/button-group/ButtonGroup.stories.svelte.d.ts +2 -17
  54. package/dist/components/buttons/button-group/ButtonGroup.svelte +102 -8
  55. package/dist/components/buttons/button-group/ButtonGroup.svelte.d.ts +6 -7
  56. package/dist/components/buttons/button-group/theme.d.ts +45 -15
  57. package/dist/components/buttons/button-group/theme.js +13 -8
  58. package/dist/components/buttons/button-group/types.d.ts +13 -7
  59. package/dist/components/buttons/connected/ConnectedButtonGroup.mdx +43 -0
  60. package/dist/components/buttons/connected/ConnectedButtonGroup.stories.svelte +56 -74
  61. package/dist/components/buttons/connected/ConnectedButtonGroup.stories.svelte.d.ts +2 -17
  62. package/dist/components/buttons/connected/ConnectedButtonGroup.svelte +16 -4
  63. package/dist/components/buttons/connected/ConnectedButtonGroup.svelte.d.ts +4 -4
  64. package/dist/components/buttons/connected/ConnectedButtonGroupItem.svelte +35 -8
  65. package/dist/components/buttons/connected/ConnectedButtonGroupItem.svelte.d.ts +2 -3
  66. package/dist/components/buttons/connected/theme.d.ts +33 -18
  67. package/dist/components/buttons/connected/theme.js +32 -9
  68. package/dist/components/buttons/connected/types.d.ts +36 -9
  69. package/dist/components/buttons/shapeMorph.d.ts +26 -0
  70. package/dist/components/buttons/shapeMorph.js +142 -0
  71. package/dist/components/buttons/split-button/SplitButton.mdx +48 -0
  72. package/dist/components/buttons/split-button/SplitButton.stories.svelte +9 -14
  73. package/dist/components/buttons/split-button/SplitButton.svelte +65 -48
  74. package/dist/components/buttons/split-button/SplitButton.svelte.d.ts +6 -11
  75. package/dist/components/buttons/split-button/theme.d.ts +110 -17
  76. package/dist/components/buttons/split-button/theme.js +74 -16
  77. package/dist/components/buttons/split-button/types.d.ts +24 -18
  78. package/dist/components/buttons/theme.d.ts +422 -230
  79. package/dist/components/buttons/theme.js +286 -380
  80. package/dist/components/buttons/types.d.ts +130 -125
  81. package/dist/components/cards/Card.mdx +44 -0
  82. package/dist/components/cards/Card.stories.svelte +11 -11
  83. package/dist/components/cards/theme.js +5 -5
  84. package/dist/components/chips/Chip.mdx +68 -0
  85. package/dist/components/chips/Chip.stories.svelte +4 -4
  86. package/dist/components/chips/theme.js +3 -3
  87. package/dist/components/containers/action-rail/ActionRail.stories.svelte +1 -0
  88. package/dist/components/containers/action-rail/theme.js +1 -1
  89. package/dist/components/containers/bottom-sheet/BottomSheet.mdx +64 -0
  90. package/dist/components/containers/bottom-sheet/BottomSheet.stories.svelte +108 -12
  91. package/dist/components/containers/bottom-sheet/BottomSheet.svelte +179 -109
  92. package/dist/components/containers/bottom-sheet/BottomSheet.svelte.d.ts +10 -5
  93. package/dist/components/containers/bottom-sheet/index.d.ts +1 -0
  94. package/dist/components/containers/bottom-sheet/index.js +1 -0
  95. package/dist/components/containers/bottom-sheet/theme.d.ts +45 -0
  96. package/dist/components/containers/bottom-sheet/theme.js +42 -0
  97. package/dist/components/containers/bottom-sheet/types.d.ts +37 -5
  98. package/dist/components/containers/carousel/Carousel.mdx +80 -0
  99. package/dist/components/containers/carousel/Carousel.stories.svelte +131 -0
  100. package/dist/components/containers/carousel/Carousel.stories.svelte.d.ts +4 -0
  101. package/dist/components/containers/carousel/Carousel.svelte +194 -0
  102. package/dist/components/containers/carousel/Carousel.svelte.d.ts +25 -0
  103. package/dist/components/containers/carousel/index.d.ts +3 -0
  104. package/dist/components/containers/{stack → carousel}/index.js +1 -2
  105. package/dist/components/containers/carousel/keylines.d.ts +32 -0
  106. package/dist/components/containers/carousel/keylines.js +88 -0
  107. package/dist/components/containers/carousel/theme.d.ts +162 -0
  108. package/dist/components/containers/carousel/theme.js +67 -0
  109. package/dist/components/containers/carousel/types.d.ts +52 -0
  110. package/dist/components/containers/context-menu/ContextMenu.stories.svelte +1 -0
  111. package/dist/components/containers/context-menu/ContextMenu.svelte +2 -2
  112. package/dist/components/containers/context-menu/ContextMenu.svelte.d.ts +1 -1
  113. package/dist/components/containers/context-menu/theme.js +10 -10
  114. package/dist/components/containers/dialogue/Dialogue.mdx +65 -0
  115. package/dist/components/containers/dialogue/Dialogue.stories.svelte +4 -4
  116. package/dist/components/containers/dialogue/Dialogue.svelte +1 -1
  117. package/dist/components/containers/dialogue/theme.js +3 -3
  118. package/dist/components/containers/divider/Divider.stories.svelte +2 -1
  119. package/dist/components/containers/divider/Divider.svelte +2 -2
  120. package/dist/components/containers/divider/theme.d.ts +6 -6
  121. package/dist/components/containers/divider/theme.js +2 -2
  122. package/dist/components/containers/index.d.ts +1 -1
  123. package/dist/components/containers/index.js +1 -1
  124. package/dist/components/containers/link-preview/theme.js +2 -2
  125. package/dist/components/containers/list/List.svelte +18 -0
  126. package/dist/components/containers/list/List.svelte.d.ts +4 -0
  127. package/dist/components/containers/list/ListItem.mdx +100 -0
  128. package/dist/components/containers/list/ListItem.stories.svelte +177 -42
  129. package/dist/components/containers/list/ListItem.stories.svelte.d.ts +2 -17
  130. package/dist/components/containers/list/ListItem.svelte +92 -37
  131. package/dist/components/containers/list/ListItem.svelte.d.ts +1 -1
  132. package/dist/components/containers/list/context.d.ts +6 -0
  133. package/dist/components/containers/list/context.js +4 -0
  134. package/dist/components/containers/list/index.d.ts +1 -0
  135. package/dist/components/containers/list/index.js +1 -0
  136. package/dist/components/containers/list/theme.d.ts +487 -12
  137. package/dist/components/containers/list/theme.js +140 -13
  138. package/dist/components/containers/list/types.d.ts +39 -5
  139. package/dist/components/containers/menu/Menu.mdx +52 -0
  140. package/dist/components/containers/menu/Menu.stories.svelte +14 -14
  141. package/dist/components/containers/menu/Menu.svelte +1 -1
  142. package/dist/components/containers/menu/MenuGroup.svelte +1 -1
  143. package/dist/components/containers/menu/MenuGroup.svelte.d.ts +1 -1
  144. package/dist/components/containers/menu/menu-item/MenuItem.svelte +1 -1
  145. package/dist/components/containers/menu/theme.js +8 -8
  146. package/dist/components/containers/pane/DraggablePane.stories.svelte +4 -3
  147. package/dist/components/containers/pane/DraggablePane.svelte +2 -2
  148. package/dist/components/containers/pane/Pane.stories.svelte +11 -10
  149. package/dist/components/containers/pane/Pane.svelte +24 -2
  150. package/dist/components/containers/pane/PaneGrid.stories.svelte +19 -18
  151. package/dist/components/containers/pane/PaneGrid.svelte +3 -2
  152. package/dist/components/containers/pane/PaneGrid.svelte.d.ts +2 -1
  153. package/dist/components/containers/pane/PaneHandle.svelte +48 -2
  154. package/dist/components/containers/pane/PaneHandle.svelte.d.ts +4 -0
  155. package/dist/components/containers/pane/resizeStore.svelte.d.ts +10 -1
  156. package/dist/components/containers/pane/resizeStore.svelte.js +13 -2
  157. package/dist/components/containers/pane/theme.js +129 -45
  158. package/dist/components/containers/pane/types.d.ts +3 -2
  159. package/dist/components/containers/popover/Popover.mdx +45 -0
  160. package/dist/components/containers/popover/Popover.stories.svelte +1 -1
  161. package/dist/components/containers/popover/Popover.svelte +1 -1
  162. package/dist/components/containers/popover/theme.js +3 -3
  163. package/dist/components/containers/scroll-area/theme.js +2 -2
  164. package/dist/components/containers/side-sheet/SideSheet.mdx +71 -0
  165. package/dist/components/containers/side-sheet/SideSheet.stories.svelte +113 -17
  166. package/dist/components/containers/side-sheet/SideSheet.svelte +113 -66
  167. package/dist/components/containers/side-sheet/SideSheet.svelte.d.ts +8 -7
  168. package/dist/components/containers/side-sheet/index.d.ts +1 -0
  169. package/dist/components/containers/side-sheet/index.js +1 -0
  170. package/dist/components/containers/side-sheet/theme.d.ts +108 -0
  171. package/dist/components/containers/side-sheet/theme.js +72 -0
  172. package/dist/components/containers/side-sheet/types.d.ts +35 -7
  173. package/dist/components/date/DateField.stories.svelte +4 -3
  174. package/dist/components/date/DateField.svelte +49 -26
  175. package/dist/components/date/DateField.svelte.d.ts +2 -0
  176. package/dist/components/date/DateRangeField.stories.svelte +4 -3
  177. package/dist/components/date/DateRangeField.svelte +54 -31
  178. package/dist/components/date/DateRangeField.svelte.d.ts +2 -0
  179. package/dist/components/date/calendarMotion.d.ts +9 -0
  180. package/dist/components/date/calendarMotion.js +23 -0
  181. package/dist/components/date/theme.d.ts +7 -7
  182. package/dist/components/date/theme.js +34 -30
  183. package/dist/components/forms/checkbox/Checkbox.mdx +35 -0
  184. package/dist/components/forms/checkbox/Checkbox.stories.svelte +2 -2
  185. package/dist/components/forms/checkbox/Checkbox.svelte +1 -1
  186. package/dist/components/forms/checkbox/theme.js +3 -3
  187. package/dist/components/forms/command/Command.stories.svelte +1 -0
  188. package/dist/components/forms/command/theme.js +9 -9
  189. package/dist/components/forms/pin/theme.js +4 -4
  190. package/dist/components/forms/radio-group/RadioGroup.svelte +1 -1
  191. package/dist/components/forms/radio-group/theme.js +5 -5
  192. package/dist/components/forms/search/Search.mdx +38 -0
  193. package/dist/components/forms/search/Search.stories.svelte +19 -9
  194. package/dist/components/forms/search/Search.svelte +40 -17
  195. package/dist/components/forms/search/Search.svelte.d.ts +2 -2
  196. package/dist/components/forms/search/theme.d.ts +45 -12
  197. package/dist/components/forms/search/theme.js +13 -8
  198. package/dist/components/forms/search/types.d.ts +18 -12
  199. package/dist/components/forms/select/Select.mdx +53 -0
  200. package/dist/components/forms/select/Select.stories.svelte +4 -4
  201. package/dist/components/forms/select/Select.svelte +10 -2
  202. package/dist/components/forms/select/SelectItem.svelte +1 -1
  203. package/dist/components/forms/select/theme.js +12 -12
  204. package/dist/components/forms/slider/Slider.mdx +85 -0
  205. package/dist/components/forms/slider/Slider.stories.svelte +7 -7
  206. package/dist/components/forms/slider/Slider.svelte +46 -28
  207. package/dist/components/forms/slider/theme.d.ts +14 -0
  208. package/dist/components/forms/slider/theme.js +28 -17
  209. package/dist/components/forms/slider/types.d.ts +3 -2
  210. package/dist/components/forms/switch/Switch.mdx +38 -0
  211. package/dist/components/forms/switch/Switch.stories.svelte +3 -3
  212. package/dist/components/forms/switch/theme.js +6 -6
  213. package/dist/components/forms/textfield/Textfield.mdx +53 -0
  214. package/dist/components/forms/textfield/Textfield.stories.svelte +6 -6
  215. package/dist/components/forms/textfield/Textfield.svelte +1 -0
  216. package/dist/components/forms/textfield/theme.d.ts +3 -0
  217. package/dist/components/forms/textfield/theme.js +22 -19
  218. package/dist/components/forms/toggle-group/theme.js +2 -2
  219. package/dist/components/forms/tooltip/Tooltip.stories.svelte +1 -0
  220. package/dist/components/forms/tooltip/theme.js +3 -3
  221. package/dist/components/misc/Avatar.stories.svelte +5 -4
  222. package/dist/components/misc/ThemeSettings.svelte +9 -9
  223. package/dist/components/misc/ThemeSwitcher.svelte +3 -3
  224. package/dist/components/misc/theme.js +4 -4
  225. package/dist/components/nav/appbar/AppBar.mdx +71 -0
  226. package/dist/components/nav/appbar/AppBar.stories.svelte +93 -3
  227. package/dist/components/nav/appbar/AppBar.stories.svelte.d.ts +2 -17
  228. package/dist/components/nav/appbar/AppBar.svelte +90 -37
  229. package/dist/components/nav/appbar/AppBar.svelte.d.ts +9 -2
  230. package/dist/components/nav/appbar/theme.d.ts +85 -18
  231. package/dist/components/nav/appbar/theme.js +159 -23
  232. package/dist/components/nav/appbar/types.d.ts +46 -9
  233. package/dist/components/nav/breadcrumb/Breadcrumb.stories.svelte +1 -0
  234. package/dist/components/nav/breadcrumb/theme.js +5 -5
  235. package/dist/components/nav/navbar/Navbar.mdx +38 -0
  236. package/dist/components/nav/navbar/Navbar.svelte +1 -1
  237. package/dist/components/nav/navbar/NavbarItem.svelte +12 -9
  238. package/dist/components/nav/navbar/theme.js +6 -6
  239. package/dist/components/nav/rail/Rail.mdx +73 -0
  240. package/dist/components/nav/rail/Rail.stories.svelte +3 -7
  241. package/dist/components/nav/rail/Rail.svelte +1 -0
  242. package/dist/components/nav/rail/RailItem.svelte +12 -9
  243. package/dist/components/nav/rail/theme.js +13 -13
  244. package/dist/components/nav/rail/types.d.ts +4 -1
  245. package/dist/components/nav/tabs/TabHolder.svelte +29 -3
  246. package/dist/components/nav/tabs/TabHolder.svelte.d.ts +3 -1
  247. package/dist/components/nav/tabs/Tabs.mdx +62 -0
  248. package/dist/components/nav/tabs/Tabs.stories.svelte +1 -1
  249. package/dist/components/nav/tabs/theme.js +4 -4
  250. package/dist/components/pill/Pill.mdx +32 -0
  251. package/dist/components/pill/Pill.stories.svelte +1 -1
  252. package/dist/components/pill/theme.d.ts +1 -1
  253. package/dist/components/pill/theme.js +1 -1
  254. package/dist/components/progress/LinearProgress.svelte +1 -1
  255. package/dist/components/progress/Progress.mdx +50 -0
  256. package/dist/components/progress/Progress.stories.svelte +6 -6
  257. package/dist/components/snackbar/Snackbar.mdx +52 -0
  258. package/dist/components/snackbar/Snackbar.stories.svelte +5 -5
  259. package/dist/components/snackbar/Snackbar.svelte +1 -1
  260. package/dist/components/snackbar/theme.d.ts +3 -3
  261. package/dist/components/snackbar/theme.js +4 -4
  262. package/dist/components/table/Table.stories.svelte +1 -0
  263. package/dist/components/table/TableHeader.svelte +1 -1
  264. package/dist/components/table/theme.js +3 -3
  265. package/dist/components/time/TimeField.stories.svelte +3 -2
  266. package/dist/components/time/TimeField.svelte +14 -8
  267. package/dist/components/time/TimeField.svelte.d.ts +6 -0
  268. package/dist/components/time/TimepickerInput.svelte +9 -5
  269. package/dist/components/time/theme.js +5 -5
  270. package/dist/components/time/types.d.ts +11 -1
  271. package/dist/components/toolbar/Toolbar.stories.svelte +1 -0
  272. package/dist/components/toolbar/Toolbar.svelte +1 -1
  273. package/dist/components/toolbar/Toolbar.svelte.d.ts +1 -1
  274. package/dist/components/toolbar/theme.js +14 -14
  275. package/dist/components/typography/Typography.mdx +67 -0
  276. package/dist/components/typography/body/Body.stories.svelte +2 -1
  277. package/dist/components/typography/body/theme.d.ts +15 -12
  278. package/dist/components/typography/body/theme.js +19 -4
  279. package/dist/components/typography/body/types.d.ts +2 -1
  280. package/dist/components/typography/display/Display.stories.svelte +2 -1
  281. package/dist/components/typography/display/theme.d.ts +15 -12
  282. package/dist/components/typography/display/theme.js +19 -4
  283. package/dist/components/typography/display/types.d.ts +2 -1
  284. package/dist/components/typography/headline/Headline.stories.svelte +2 -1
  285. package/dist/components/typography/headline/theme.d.ts +15 -12
  286. package/dist/components/typography/headline/theme.js +19 -4
  287. package/dist/components/typography/headline/types.d.ts +2 -1
  288. package/dist/components/typography/kbd/Kbd.stories.svelte +2 -1
  289. package/dist/components/typography/kbd/theme.d.ts +4 -4
  290. package/dist/components/typography/kbd/theme.js +2 -2
  291. package/dist/components/typography/label/Label.stories.svelte +2 -1
  292. package/dist/components/typography/label/theme.d.ts +15 -12
  293. package/dist/components/typography/label/theme.js +19 -4
  294. package/dist/components/typography/label/types.d.ts +2 -1
  295. package/dist/components/typography/title/Title.stories.svelte +2 -1
  296. package/dist/components/typography/title/theme.d.ts +15 -12
  297. package/dist/components/typography/title/theme.js +19 -4
  298. package/dist/components/typography/title/types.d.ts +2 -1
  299. package/dist/index.css +1 -0
  300. package/dist/styles/component.css +78 -175
  301. package/dist/styles/elevation.css +9 -0
  302. package/dist/styles/spacing.css +29 -0
  303. package/dist/styles/typescale.css +635 -20
  304. package/dist/utils/Layer.svelte +1 -1
  305. package/dist/utils/icon/Icon.svelte +8 -6
  306. package/dist/utils/icon/loadingShapes.js +11 -10
  307. package/package.json +19 -15
  308. package/dist/components/containers/stack/HStack.svelte +0 -23
  309. package/dist/components/containers/stack/HStack.svelte.d.ts +0 -5
  310. package/dist/components/containers/stack/VStack.svelte +0 -23
  311. package/dist/components/containers/stack/VStack.svelte.d.ts +0 -5
  312. package/dist/components/containers/stack/index.d.ts +0 -4
  313. package/dist/components/containers/stack/theme.d.ts +0 -66
  314. package/dist/components/containers/stack/theme.js +0 -26
  315. package/dist/components/containers/stack/types.d.ts +0 -12
  316. /package/dist/components/containers/{stack → carousel}/types.js +0 -0
@@ -1,33 +1,52 @@
1
1
  <!--
2
2
  @component
3
- The floating panel of a FAB menu. Rendered by `FAB` when `withMenu` is set — must live inside
4
- its `DropdownMenu.Root`, which owns open state, positioning, and outside-click/Escape dismissal.
5
- Should be used with `FABMenuItem` as children.
3
+ The item stack of a FAB menu, rendered by `FAB` when `withMenu` is set. It lives inside the
4
+ FAB's `DropdownMenu.Root`, which owns open state, positioning, and outside-click/Escape
5
+ dismissal. Items are end-aligned, 4dp apart and 8dp above the close button. Use up to six
6
+ `FABMenuItem`s.
7
+
8
+ Motion: the items grow out of the close button one after another, nearest first, on the fast
9
+ spatial spring; the stack fades out on close.
6
10
  -->
7
11
  <script lang="ts">
8
12
  import clsx from 'clsx';
9
13
  import { DropdownMenu } from 'bits-ui';
14
+ import { animate, stagger } from 'motion';
15
+ import type { Attachment } from 'svelte/attachments';
10
16
  import { fabMenu } from './theme.js';
11
17
  import type { FABMenuProps } from './types.js';
12
18
  import { enterExit, presence } from '../../animation/index.js';
19
+ import { springTokens, springTransition } from '../../animation/spring.js';
13
20
 
14
21
  let { children, class: className, ...restProps }: FABMenuProps = $props();
15
22
 
16
23
  const { base } = fabMenu();
24
+
25
+ const STAGGER_S = 0.03;
26
+ const itemsIn: Attachment<HTMLElement> = (node) => {
27
+ if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;
28
+ const items = [...node.children] as HTMLElement[];
29
+ for (const item of items) item.style.transformOrigin = 'right center';
30
+ const controls = animate(
31
+ items,
32
+ { y: [8, 0], scale: [0.6, 1] },
33
+ { ...springTransition(springTokens.fastSpatial), delay: stagger(STAGGER_S, { from: 'last' }) }
34
+ );
35
+ return () => controls.stop();
36
+ };
17
37
  </script>
18
38
 
19
39
  <DropdownMenu.Portal>
20
- <DropdownMenu.Content side="top" align="end" sideOffset={8} collisionPadding={8}>
40
+ <DropdownMenu.Content side="top" align="end" sideOffset={3} collisionPadding={8}>
21
41
  {#snippet child({ wrapperProps, props, open })}
22
42
  <div {...wrapperProps}>
23
43
  <div
24
44
  data-cy="m3-fab-menu"
25
45
  {...restProps}
26
46
  {...props}
27
- class={base({
28
- class: clsx(className, 'overflow-y-auto')
29
- })}
30
- {@attach presence(() => open, enterExit.scale)}
47
+ class={base({ class: clsx(className) })}
48
+ {@attach presence(() => open, enterExit.fade)}
49
+ {@attach itemsIn}
31
50
  >
32
51
  {@render children()}
33
52
  </div>
@@ -1,8 +1,12 @@
1
1
  import type { FABMenuProps } from './types.js';
2
2
  /**
3
- * The floating panel of a FAB menu. Rendered by `FAB` when `withMenu` is set — must live inside
4
- * its `DropdownMenu.Root`, which owns open state, positioning, and outside-click/Escape dismissal.
5
- * Should be used with `FABMenuItem` as children.
3
+ * The item stack of a FAB menu, rendered by `FAB` when `withMenu` is set. It lives inside the
4
+ * FAB's `DropdownMenu.Root`, which owns open state, positioning, and outside-click/Escape
5
+ * dismissal. Items are end-aligned, 4dp apart and 8dp above the close button. Use up to six
6
+ * `FABMenuItem`s.
7
+ *
8
+ * Motion: the items grow out of the close button one after another, nearest first, on the fast
9
+ * spatial spring; the stack fades out on close.
6
10
  */
7
11
  declare const FABMenu: import("svelte").Component<FABMenuProps, {}, "">;
8
12
  type FABMenu = ReturnType<typeof FABMenu>;
@@ -1,11 +1,12 @@
1
1
  <!--
2
2
  @component
3
- An individual action within a FAB menu. Wraps bits-ui's `DropdownMenu.Item`, so it gets the
4
- `menuitem` role, arrow-key navigation, and closes the menu when selected. Must be rendered
5
- inside a `FAB` with `withMenu`.
3
+ One action in a FAB menu: a 56dp pill in the FAB's colour set (primary, secondary or tertiary
4
+ container). Wraps bits-ui's `DropdownMenu.Item`, so it gets the `menuitem` role, arrow-key
5
+ navigation, and closes the menu when chosen. Render it inside a `FAB` with `withMenu`.
6
6
  -->
7
7
  <script lang="ts">
8
8
  import clsx from 'clsx';
9
+ import { getContext } from 'svelte';
9
10
  import { fabMenuItem } from './theme.js';
10
11
  import type { FABMenuItemProps } from './types.js';
11
12
  import { Icon, LoadingIndicator, Layer } from '../../utils/index.js';
@@ -13,7 +14,6 @@ inside a `FAB` with `withMenu`.
13
14
 
14
15
  let {
15
16
  class: className,
16
- variant = 'secondary',
17
17
  loading,
18
18
  children,
19
19
  iconProps,
@@ -22,24 +22,28 @@ inside a `FAB` with `withMenu`.
22
22
  ...restProps
23
23
  }: FABMenuItemProps = $props();
24
24
 
25
- const { base, icon } = $derived(fabMenuItem({ variant }));
26
- const btnCls = $derived(base({ class: clsx(className) }));
25
+ const menuSet = getContext<(() => 'primary' | 'secondary' | 'tertiary') | undefined>(
26
+ 'fabMenuSet'
27
+ );
28
+ const cls = $derived(fabMenuItem({ set: menuSet?.() ?? 'primary' }));
27
29
  </script>
28
30
 
29
31
  <!-- Spread onto the Item, not the button: bits-ui merges (and chains handlers of) these into `props`. -->
30
32
  <DropdownMenu.Item {disabled} {...restProps as DropdownMenuItemProps}>
31
33
  {#snippet child({ props })}
32
- <Button.Root {formaction} {disabled} data-cy="m3-fab-menu-item" {...props} class={btnCls}>
33
- {#if iconProps}
34
- {#if loading}
35
- <LoadingIndicator />
36
- {:else}
37
- <Icon class={icon()} {...iconProps} />
38
- {/if}
39
- {:else if loading}
40
- <LoadingIndicator />
34
+ <Button.Root
35
+ {formaction}
36
+ {disabled}
37
+ data-cy="m3-fab-menu-item"
38
+ {...props}
39
+ class={cls.base({ class: clsx(className) })}
40
+ >
41
+ {#if loading}
42
+ <LoadingIndicator center={false} class={cls.icon({ class: 'text-current' })} />
43
+ {:else}
44
+ <Icon {...iconProps} class={cls.icon({ class: clsx(iconProps.class) })} />
41
45
  {/if}
42
- {@render children?.()}
46
+ {@render children()}
43
47
  <Layer />
44
48
  </Button.Root>
45
49
  {/snippet}
@@ -1,8 +1,8 @@
1
1
  import type { FABMenuItemProps } from './types.js';
2
2
  /**
3
- * An individual action within a FAB menu. Wraps bits-ui's `DropdownMenu.Item`, so it gets the
4
- * `menuitem` role, arrow-key navigation, and closes the menu when selected. Must be rendered
5
- * inside a `FAB` with `withMenu`.
3
+ * One action in a FAB menu: a 56dp pill in the FAB's colour set (primary, secondary or tertiary
4
+ * container). Wraps bits-ui's `DropdownMenu.Item`, so it gets the `menuitem` role, arrow-key
5
+ * navigation, and closes the menu when chosen. Render it inside a `FAB` with `withMenu`.
6
6
  */
7
7
  declare const FABMenuItem: import("svelte").Component<FABMenuItemProps, {}, "">;
8
8
  type FABMenuItem = ReturnType<typeof FABMenuItem>;
@@ -2,45 +2,53 @@
2
2
  import { defineMeta } from '@storybook/addon-svelte-csf';
3
3
  import Toggle from './Toggle.svelte';
4
4
 
5
+ const variants = ['elevated', 'filled', 'tonal', 'outlined'] as const;
6
+
5
7
  const { Story } = defineMeta({
6
8
  title: 'Buttons/Toggle',
7
9
  component: Toggle,
10
+ tags: ['autodocs'],
8
11
  argTypes: {
9
- size: {
10
- control: 'select',
11
- options: ['xs', 'sm', 'md', 'lg', 'xl']
12
- },
13
- shape: {
14
- control: 'select',
15
- options: ['round', 'square']
16
- },
12
+ variant: { control: 'select', options: variants },
13
+ size: { control: 'select', options: ['xs', 'sm', 'md', 'lg', 'xl'] },
14
+ shape: { control: 'inline-radio', options: ['round', 'square'] },
17
15
  pressed: { control: 'boolean' },
18
- disabled: { control: 'boolean' },
19
- loading: { control: 'boolean' }
16
+ disabled: { control: 'boolean' }
20
17
  },
21
18
  args: {
22
- size: 'md',
19
+ variant: 'filled',
20
+ size: 'sm',
23
21
  shape: 'round',
24
22
  pressed: false,
25
- disabled: false,
26
- loading: false
23
+ disabled: false
27
24
  }
28
25
  });
29
26
  </script>
30
27
 
31
- <Story name="Playground">Toggle</Story>
28
+ <script lang="ts">
29
+ let selected = $state<Record<string, boolean>>({ tonal: true, outlined: true });
30
+ </script>
31
+
32
+ <Story name="Playground">
33
+ {#snippet template(args)}
34
+ <Toggle {...args} iconProps={{ name: 'bookmark' }}>Save</Toggle>
35
+ {/snippet}
36
+ </Story>
32
37
 
33
- <Story name="With Icon" asChild>
34
- <div class="flex flex-wrap items-center gap-4 p-6">
35
- <Toggle iconProps={{ name: 'format_bold' }}>Bold</Toggle>
36
- <Toggle iconProps={{ name: 'format_italic' }} pressed>Italic</Toggle>
38
+ <Story name="Colour styles" asChild>
39
+ <div class="gap-spacing-200 p-spacing-300 grid grid-cols-[auto_auto] items-center justify-start">
40
+ {#each variants as variant (variant)}
41
+ <Toggle {variant} iconProps={{ name: 'favorite' }}>{variant}</Toggle>
42
+ <Toggle {variant} pressed iconProps={{ name: 'favorite' }}>{variant}</Toggle>
43
+ {/each}
37
44
  </div>
38
45
  </Story>
39
46
 
40
- <Story name="States" asChild>
41
- <div class="flex flex-wrap items-center gap-4 p-6">
42
- <Toggle>Unpressed</Toggle>
43
- <Toggle pressed>Pressed</Toggle>
44
- <Toggle disabled>Disabled</Toggle>
47
+ <Story name="Shape swap" asChild>
48
+ <div class="gap-spacing-200 p-spacing-300 flex flex-wrap items-center">
49
+ <Toggle size="md" bind:pressed={selected.tonal} variant="tonal">Round → square</Toggle>
50
+ <Toggle size="md" bind:pressed={selected.outlined} variant="outlined" shape="square">
51
+ Square → round
52
+ </Toggle>
45
53
  </div>
46
54
  </Story>
@@ -1,19 +1,4 @@
1
1
  import Toggle from './Toggle.svelte';
2
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
- $$bindings?: Bindings;
5
- } & Exports;
6
- (internal: unknown, props: {
7
- $$events?: Events;
8
- $$slots?: Slots;
9
- }): Exports & {
10
- $set?: any;
11
- $on?: any;
12
- };
13
- z_$$bindings?: Bindings;
14
- }
15
- declare const Toggle: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
16
- [evt: string]: CustomEvent<any>;
17
- }, {}, {}, string>;
18
- type Toggle = InstanceType<typeof Toggle>;
2
+ declare const Toggle: import("svelte").Component<Record<string, never>, {}, "">;
3
+ type Toggle = ReturnType<typeof Toggle>;
19
4
  export default Toggle;
@@ -1,21 +1,23 @@
1
1
  <!--
2
2
  @component
3
- A toggle button that switches between pressed and unpressed states.
4
- Uses Material 3 selection button styling.
3
+ A toggle button: a button with a selected state, exposed as `aria-pressed`. Selecting changes its
4
+ colours and swaps its resting shape (round ↔ square).
5
5
 
6
- @see https://m3.material.io/components/all-buttons
6
+ @see https://m3.material.io/components/buttons/specs
7
7
  -->
8
8
  <script lang="ts">
9
- import type { ToggleMDProps } from './types.js';
9
+ import type { ToggleProps } from './types.js';
10
10
  import { Icon, LoadingIndicator, Layer } from '../../utils/index.js';
11
- import { button } from './theme.js';
11
+ import { button, buttonColor } from './theme.js';
12
12
  import clsx from 'clsx';
13
+ import { buttonCorners, shapeMorph } from './shapeMorph.js';
13
14
  import { Toggle, type ToggleRootProps } from 'bits-ui';
14
15
 
15
16
  let {
16
17
  children,
17
18
  iconProps,
18
- size = 'md',
19
+ variant = 'filled',
20
+ size = 'sm',
19
21
  shape = 'round',
20
22
  disabled,
21
23
  loading,
@@ -23,38 +25,39 @@ Uses Material 3 selection button styling.
23
25
  onPressedChange,
24
26
  class: className,
25
27
  ...restProps
26
- }: ToggleMDProps = $props();
28
+ }: ToggleProps = $props();
27
29
 
28
- const { base, icon } = $derived(
29
- button({
30
- usage: 'selection',
31
- shape,
32
- size,
33
- selected: pressed
30
+ let ref = $state<HTMLElement | null>(null);
31
+ $effect(() => (ref ? shapeMorph(ref, buttonCorners) : undefined));
32
+
33
+ const cls = $derived(button({ variant, size, shape, selected: pressed }));
34
+ const btnCls = $derived(
35
+ cls.base({
36
+ class: clsx(buttonColor({ variant, state: pressed ? 'selected' : 'unselected' }), className)
34
37
  })
35
38
  );
36
-
37
- const btnCls = $derived(base({ class: clsx(className) }));
38
39
  </script>
39
40
 
40
41
  <Toggle.Root
42
+ bind:ref
41
43
  bind:pressed
42
44
  {onPressedChange}
43
45
  {disabled}
44
46
  class={btnCls}
47
+ aria-busy={loading || undefined}
45
48
  data-cy="m3-toggle"
46
49
  {...restProps as ToggleRootProps}
47
50
  >
48
- {#if iconProps}
49
- {#if loading}
50
- <LoadingIndicator container class={icon()} />
51
- {:else}
52
- <Icon class={icon()} {...iconProps} />
53
- {@render children?.()}
54
- {/if}
55
- {:else if loading}
56
- <LoadingIndicator container />
57
- {:else}
51
+ {#if loading}
52
+ <LoadingIndicator center={false} class={cls.icon({ class: 'text-current' })} />
53
+ {:else if iconProps}
54
+ <Icon
55
+ fill={pressed ? 1 : 0}
56
+ {...iconProps}
57
+ class={cls.icon({ class: clsx(iconProps.class) })}
58
+ />
59
+ {/if}
60
+ {#if !loading || iconProps}
58
61
  {@render children?.()}
59
62
  {/if}
60
63
  <Layer />
@@ -1,11 +1,11 @@
1
- import type { ToggleMDProps } from './types.js';
1
+ import type { ToggleProps } from './types.js';
2
2
  import { Toggle } from 'bits-ui';
3
3
  /**
4
- * A toggle button that switches between pressed and unpressed states.
5
- * Uses Material 3 selection button styling.
4
+ * A toggle button: a button with a selected state, exposed as `aria-pressed`. Selecting changes its
5
+ * colours and swaps its resting shape (round ↔ square).
6
6
  *
7
- * @see https://m3.material.io/components/all-buttons
7
+ * @see https://m3.material.io/components/buttons/specs
8
8
  */
9
- declare const Toggle: import("svelte").Component<ToggleMDProps, {}, "pressed">;
9
+ declare const Toggle: import("svelte").Component<ToggleProps, {}, "pressed">;
10
10
  type Toggle = ReturnType<typeof Toggle>;
11
11
  export default Toggle;
@@ -0,0 +1,39 @@
1
+ import { Meta, Title, Subtitle, Canvas, Controls } from '@storybook/addon-docs/blocks';
2
+ import * as GroupStories from './ButtonGroup.stories.svelte';
3
+
4
+ <Meta of={GroupStories} />
5
+
6
+ <Title />
7
+
8
+ <Subtitle>Standard button groups space buttons and make them react to each other.</Subtitle>
9
+
10
+ [M3 spec](https://m3.material.io/components/button-groups/specs) ·
11
+ `import { ButtonGroup } from '@noxlovette/material';`
12
+
13
+ <Canvas of={GroupStories.Playground} />
14
+
15
+ <Controls of={GroupStories.Playground} />
16
+
17
+ ## Standard or connected
18
+
19
+ - **`ButtonGroup`** (standard) holds separate actions, such as Back, Play, Next. Pressing a
20
+ button widens it by 15% while the buttons beside it narrow to make room, then all settle back.
21
+ - **`ConnectedButtonGroup`** holds related options, one or several selected, such as Walk, Bike,
22
+ Drive. It replaces the segmented button.
23
+
24
+ ## Contents
25
+
26
+ Mix `Button`, `Toggle` and `ButtonIcon` freely, in the filled, tonal, outlined or elevated
27
+ styles. Avoid text buttons and standard icon buttons, which have no container to change. A
28
+ toggle inside swaps its shape as it's selected.
29
+
30
+ <Canvas of={GroupStories.MixedButtons} />
31
+
32
+ ## Size
33
+
34
+ Set `size` to the buttons' size. It sets the space between them so each keeps a 48dp target:
35
+ 18dp at XS, 12dp at S, 8dp from M up. The group takes its buttons' height.
36
+
37
+ <Canvas of={GroupStories.Sizes} />
38
+
39
+ The press interaction is horizontal only, and off under reduced motion.
@@ -2,20 +2,17 @@
2
2
  import { defineMeta } from '@storybook/addon-svelte-csf';
3
3
  import Button from '../Button.svelte';
4
4
  import ButtonIcon from '../ButtonIcon.svelte';
5
+ import Toggle from '../Toggle.svelte';
5
6
  import ButtonGroup from './ButtonGroup.svelte';
6
7
 
8
+ const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const;
9
+
7
10
  const { Story } = defineMeta({
8
11
  title: 'Buttons/Button Group',
9
12
  component: ButtonGroup,
10
13
  argTypes: {
11
- orientation: {
12
- control: 'select',
13
- options: ['horizontal', 'vertical']
14
- },
15
- size: {
16
- control: 'select',
17
- options: ['xs', 'sm', 'md', 'lg', 'xl']
18
- }
14
+ orientation: { control: 'inline-radio', options: ['horizontal', 'vertical'] },
15
+ size: { control: 'select', options: sizes }
19
16
  },
20
17
  args: {
21
18
  orientation: 'horizontal',
@@ -24,57 +21,79 @@
24
21
  });
25
22
  </script>
26
23
 
24
+ <script lang="ts">
25
+ let playing = $state(false);
26
+ </script>
27
+
27
28
  <Story name="Playground">
28
29
  {#snippet template(args)}
29
- <ButtonGroup orientation={args.orientation} size={args.size}>
30
- <Button variant="tonal">Left</Button>
31
- <Button variant="tonal">Center</Button>
32
- <Button variant="tonal">Right</Button>
30
+ <ButtonGroup orientation={args.orientation} size={args.size} aria-label="Playback">
31
+ <Button size={args.size} variant="tonal">Back</Button>
32
+ <Button size={args.size}>Play</Button>
33
+ <Button size={args.size} variant="tonal">Next</Button>
33
34
  </ButtonGroup>
34
35
  {/snippet}
35
36
  </Story>
36
37
 
37
- <Story name="Mixed Variants and Colors" asChild>
38
- <div class="p-6">
39
- <ButtonGroup size="sm">
40
- <Button variant="outlined">Cancel</Button>
41
- <Button variant="tonal" color="secondary">Save draft</Button>
42
- <Button variant="filled" color="primary">Publish</Button>
38
+ <Story name="Mixed buttons" asChild>
39
+ <div class="gap-spacing-400 p-spacing-300 flex flex-col items-start">
40
+ <ButtonGroup size="md" aria-label="Player">
41
+ <ButtonIcon
42
+ size="md"
43
+ variant="tonal"
44
+ width="wide"
45
+ aria-label="Previous"
46
+ iconProps={{ name: 'skip_previous' }}
47
+ />
48
+ <Toggle
49
+ size="md"
50
+ bind:pressed={playing}
51
+ iconProps={{ name: playing ? 'pause' : 'play_arrow' }}
52
+ >
53
+ {playing ? 'Pause' : 'Play'}
54
+ </Toggle>
55
+ <ButtonIcon
56
+ size="md"
57
+ variant="tonal"
58
+ width="wide"
59
+ aria-label="Next"
60
+ iconProps={{ name: 'skip_next' }}
61
+ />
43
62
  </ButtonGroup>
44
- </div>
45
- </Story>
46
-
47
- <Story name="Icon Toolbar" asChild>
48
- <div class="flex flex-wrap gap-8 p-6">
49
- <ButtonGroup size="sm">
50
- <ButtonIcon variant="tonal" variation="toggle" iconProps={{ name: 'mic' }} />
51
- <ButtonIcon variant="tonal" variation="toggle" iconProps={{ name: 'videocam' }} />
52
- <ButtonIcon variant="tonal" iconProps={{ name: 'more_vert' }} />
53
- <ButtonIcon variant="filled" color="error" iconProps={{ name: 'call_end' }} />
63
+ <ButtonGroup size="sm" aria-label="Call">
64
+ <ButtonIcon
65
+ variant="tonal"
66
+ variation="toggle"
67
+ aria-label="Mute"
68
+ iconProps={{ name: 'mic' }}
69
+ />
70
+ <ButtonIcon
71
+ variant="tonal"
72
+ variation="toggle"
73
+ aria-label="Camera"
74
+ iconProps={{ name: 'videocam' }}
75
+ />
76
+ <ButtonIcon variant="outlined" aria-label="More" iconProps={{ name: 'more_vert' }} />
77
+ <ButtonIcon width="wide" aria-label="End call" iconProps={{ name: 'call_end' }} />
54
78
  </ButtonGroup>
55
79
  </div>
56
80
  </Story>
57
81
 
58
82
  <Story name="Sizes" asChild>
59
- <div class="flex flex-wrap items-center gap-8 p-6">
60
- <ButtonGroup size="xs">
61
- <Button size="xs" variant="tonal">XS</Button>
62
- <Button size="xs" variant="tonal">XS</Button>
63
- </ButtonGroup>
64
- <ButtonGroup size="md">
65
- <Button size="md" variant="tonal">M</Button>
66
- <Button size="md" variant="tonal">M</Button>
67
- </ButtonGroup>
68
- <ButtonGroup size="xl">
69
- <Button size="xl" variant="tonal">XL</Button>
70
- <Button size="xl" variant="tonal">XL</Button>
71
- </ButtonGroup>
83
+ <div class="gap-spacing-300 p-spacing-300 flex flex-col items-start">
84
+ {#each sizes as size (size)}
85
+ <ButtonGroup {size}>
86
+ <Button {size} variant="tonal">One</Button>
87
+ <Button {size}>Two</Button>
88
+ <Button {size} variant="tonal">Three</Button>
89
+ </ButtonGroup>
90
+ {/each}
72
91
  </div>
73
92
  </Story>
74
93
 
75
94
  <Story name="Vertical" asChild>
76
- <div class="p-6">
77
- <ButtonGroup orientation="vertical" size="sm">
95
+ <div class="p-spacing-300">
96
+ <ButtonGroup orientation="vertical">
78
97
  <Button variant="tonal">Top</Button>
79
98
  <Button variant="tonal">Middle</Button>
80
99
  <Button variant="tonal">Bottom</Button>
@@ -1,19 +1,4 @@
1
1
  import ButtonGroup from './ButtonGroup.svelte';
2
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
- $$bindings?: Bindings;
5
- } & Exports;
6
- (internal: unknown, props: {
7
- $$events?: Events;
8
- $$slots?: Slots;
9
- }): Exports & {
10
- $set?: any;
11
- $on?: any;
12
- };
13
- z_$$bindings?: Bindings;
14
- }
15
- declare const ButtonGroup: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
16
- [evt: string]: CustomEvent<any>;
17
- }, {}, {}, string>;
18
- type ButtonGroup = InstanceType<typeof ButtonGroup>;
2
+ declare const ButtonGroup: import("svelte").Component<Record<string, never>, {}, "">;
3
+ type ButtonGroup = ReturnType<typeof ButtonGroup>;
19
4
  export default ButtonGroup;