@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
@@ -17,20 +17,42 @@ export interface SharedAxisOptions extends NavigationOptions {
17
17
  * axis while fading through each other.
18
18
  * https://m3.material.io/styles/motion/transitions/transition-patterns#forward-and-backward
19
19
  *
20
+ * The default for common hierarchical navigation (https://m3.material.io/styles/motion/transitions/applying-transitions).
21
+ * Don't use it for hero moments; use `containerTransform` for those. Keep one axis per relationship
22
+ * across the app (coherent spatial model), and move one `target` region as a unit instead of
23
+ * animating many elements independently.
24
+ *
20
25
  * `update` performs the DOM change (`async () => { step++; await tick(); }`). Built on Motion's
21
26
  * `animateView()` — both states never need to coexist in the DOM.
27
+ *
28
+ * Not built into any component: hierarchy levels and wizard steps live in the consuming app.
22
29
  */
23
30
  export declare const sharedAxis: (update: () => void | Promise<void>, { target, axis, direction, spring }?: SharedAxisOptions) => import("motion-dom").ViewTransitionBuilder;
24
31
  /**
25
32
  * M3 lateral: peer screens at the same level (tabs, carousels) slide past each other edge to edge,
26
33
  * without fading — the new screen pushes the old one out.
27
34
  * https://m3.material.io/styles/motion/transitions/transition-patterns#lateral
35
+ *
36
+ * Only for peers in one set (https://m3.material.io/styles/motion/transitions/applying-transitions). Never use it for hierarchical screens
37
+ * (use `sharedAxis`) or navbar/rail/drawer destinations (use `fadeThrough`, since the implied swipe
38
+ * conflicts with carousels and swipeable list items). Don't add a fade: it hides the peer
39
+ * relationship and makes the slide look like forward/backward.
40
+ *
41
+ * Built in: `TabHolder` (switching content panels) and `DateField`/`DateRangeField` (changing the
42
+ * visible month). Any new component that pages between peer views must use this too.
28
43
  */
29
44
  export declare const lateral: (update: () => void | Promise<void>, { target, direction, spring }?: NavigationOptions) => import("motion-dom").ViewTransitionBuilder;
30
45
  /**
31
46
  * M3 top level (fade through): destinations with no spatial relationship, e.g. navigation bar
32
47
  * items. The outgoing screen fades out, then the incoming one fades in while scaling up from 92%.
33
48
  * https://m3.material.io/styles/motion/transitions/transition-patterns#top-level
49
+ *
50
+ * The quick fade for navigation bar, rail and drawer destinations (https://m3.material.io/styles/motion/transitions/applying-transitions).
51
+ * It deliberately creates no spatial connection between screens, and it's the pattern to use
52
+ * instead of `lateral` there.
53
+ *
54
+ * Not built into `Navbar`/`Rail`: they don't own the content region that changes, so the app wraps
55
+ * its own route change in this.
34
56
  */
35
57
  export declare const fadeThrough: (update: () => void | Promise<void>, { target, spring }?: Omit<NavigationOptions, "direction">) => import("motion-dom").ViewTransitionBuilder;
36
58
  export {};
@@ -5,7 +5,9 @@ const view = (update, target, spring) => {
5
5
  const builder = animateView(update, springTransition(spring));
6
6
  return target ? builder.add(target) : builder;
7
7
  };
8
- /* Outgoing content clears quickly; incoming content waits a beat so the two never overlap fully. */
8
+ /* Outgoing content clears quickly; incoming content waits a beat so the two never overlap fully.
9
+ M3 "clean fades" (https://m3.material.io/styles/motion/transitions/applying-transitions): fade out before fading in; any cross-fade stays short
10
+ and hidden in the fastest part of the motion. Don't lengthen these. */
9
11
  const fadeOut = springTransition(springTokens.fastEffects);
10
12
  const fadeIn = { ...springTransition(springTokens.effects), delay: 0.05 };
11
13
  /**
@@ -13,8 +15,15 @@ const fadeIn = { ...springTransition(springTokens.effects), delay: 0.05 };
13
15
  * axis while fading through each other.
14
16
  * https://m3.material.io/styles/motion/transitions/transition-patterns#forward-and-backward
15
17
  *
18
+ * The default for common hierarchical navigation (https://m3.material.io/styles/motion/transitions/applying-transitions).
19
+ * Don't use it for hero moments; use `containerTransform` for those. Keep one axis per relationship
20
+ * across the app (coherent spatial model), and move one `target` region as a unit instead of
21
+ * animating many elements independently.
22
+ *
16
23
  * `update` performs the DOM change (`async () => { step++; await tick(); }`). Built on Motion's
17
24
  * `animateView()` — both states never need to coexist in the DOM.
25
+ *
26
+ * Not built into any component: hierarchy levels and wizard steps live in the consuming app.
18
27
  */
19
28
  export const sharedAxis = (update, { target, axis = 'x', direction = 'forward', spring = springTokens.spatial } = {}) => {
20
29
  const forward = direction === 'forward';
@@ -33,6 +42,14 @@ export const sharedAxis = (update, { target, axis = 'x', direction = 'forward',
33
42
  * M3 lateral: peer screens at the same level (tabs, carousels) slide past each other edge to edge,
34
43
  * without fading — the new screen pushes the old one out.
35
44
  * https://m3.material.io/styles/motion/transitions/transition-patterns#lateral
45
+ *
46
+ * Only for peers in one set (https://m3.material.io/styles/motion/transitions/applying-transitions). Never use it for hierarchical screens
47
+ * (use `sharedAxis`) or navbar/rail/drawer destinations (use `fadeThrough`, since the implied swipe
48
+ * conflicts with carousels and swipeable list items). Don't add a fade: it hides the peer
49
+ * relationship and makes the slide look like forward/backward.
50
+ *
51
+ * Built in: `TabHolder` (switching content panels) and `DateField`/`DateRangeField` (changing the
52
+ * visible month). Any new component that pages between peer views must use this too.
36
53
  */
37
54
  export const lateral = (update, { target, direction = 'forward', spring = springTokens.spatial } = {}) => {
38
55
  const sign = direction === 'forward' ? 1 : -1;
@@ -45,6 +62,13 @@ export const lateral = (update, { target, direction = 'forward', spring = spring
45
62
  * M3 top level (fade through): destinations with no spatial relationship, e.g. navigation bar
46
63
  * items. The outgoing screen fades out, then the incoming one fades in while scaling up from 92%.
47
64
  * https://m3.material.io/styles/motion/transitions/transition-patterns#top-level
65
+ *
66
+ * The quick fade for navigation bar, rail and drawer destinations (https://m3.material.io/styles/motion/transitions/applying-transitions).
67
+ * It deliberately creates no spatial connection between screens, and it's the pattern to use
68
+ * instead of `lateral` there.
69
+ *
70
+ * Not built into `Navbar`/`Rail`: they don't own the content region that changes, so the app wraps
71
+ * its own route change in this.
48
72
  */
49
73
  export const fadeThrough = (update, { target, spring = springTokens.spatial } = {}) => view(update, target, spring)
50
74
  .old({ opacity: [1, 0] }, fadeOut)
@@ -4,8 +4,13 @@ import type { Attachment } from 'svelte/attachments';
4
4
  * for the content with `presence(…, enterExit.fade)` so the reveal is a fade, not a cut.
5
5
  * https://m3.material.io/styles/motion/transitions/transition-patterns#skeleton-loaders
6
6
  *
7
+ * M3 "stable layouts" (https://m3.material.io/styles/motion/transitions/applying-transitions): the placeholder takes the loaded
8
+ * content's footprint so nothing pops in or shifts position when the data arrives.
9
+ *
10
+ * Not built into any component — none of them load their own data. Apps apply it to placeholders.
11
+ *
7
12
  * ```svelte
8
- * <div class="bg-md-sys-color-surface-container-highest h-4 rounded-sm" {@attach skeleton}></div>
13
+ * <div class="bg-md-sys-color-surface-container-highest h-spacing-200 rounded-sm" {@attach skeleton}></div>
9
14
  * ```
10
15
  */
11
16
  export declare const skeleton: Attachment<HTMLElement>;
@@ -8,8 +8,13 @@ const PULSE_HOLD_S = 0.5;
8
8
  * for the content with `presence(…, enterExit.fade)` so the reveal is a fade, not a cut.
9
9
  * https://m3.material.io/styles/motion/transitions/transition-patterns#skeleton-loaders
10
10
  *
11
+ * M3 "stable layouts" (https://m3.material.io/styles/motion/transitions/applying-transitions): the placeholder takes the loaded
12
+ * content's footprint so nothing pops in or shifts position when the data arrives.
13
+ *
14
+ * Not built into any component — none of them load their own data. Apps apply it to placeholders.
15
+ *
11
16
  * ```svelte
12
- * <div class="bg-md-sys-color-surface-container-highest h-4 rounded-sm" {@attach skeleton}></div>
17
+ * <div class="bg-md-sys-color-surface-container-highest h-spacing-200 rounded-sm" {@attach skeleton}></div>
13
18
  * ```
14
19
  */
15
20
  export const skeleton = (node) => {
@@ -0,0 +1,70 @@
1
+ import { Meta, Title, Subtitle, Canvas, Controls } from '@storybook/addon-docs/blocks';
2
+ import * as BadgeStories from './Badge.stories.svelte';
3
+
4
+ <Meta of={BadgeStories} />
5
+
6
+ <Title />
7
+
8
+ <Subtitle>
9
+ Badges show notifications, counts, or status information on navigation items and icons.
10
+ </Subtitle>
11
+
12
+ [M3 spec](https://m3.material.io/components/badges/specs) ·
13
+ `import { Badge } from '@noxlovette/material';`
14
+
15
+ <Canvas of={BadgeStories.Playground} />
16
+
17
+ <Controls of={BadgeStories.Playground} />
18
+
19
+ ## Choosing a size
20
+
21
+ | Size | Looks like | Use for |
22
+ | ---- | --------------------------------- | ------------------------------------------- |
23
+ | `sm` | 6dp dot, no text | A general notification ("something is new") |
24
+ | `lg` | 16dp-tall pill with a label-small | A specific count |
25
+
26
+ A large badge holds at most four characters, "+" included, so counts above `max` (999 by
27
+ default) show as `999+` in a 16×34dp container. A zero or missing `number` hides a large badge.
28
+
29
+ <Canvas of={BadgeStories.DotSmall} />
30
+
31
+ <Canvas of={BadgeStories.MaxCharacterCount} />
32
+
33
+ ## Placement
34
+
35
+ `NavbarItem` and `RailItem` take a `badge` prop (a count, or `-1` for the dot) and place it for
36
+ you. Use `Badge` directly only on icons those components don't cover, such as a custom icon
37
+ button.
38
+
39
+ By default the badge is absolutely positioned, with offsets measured from the **icon's**
40
+ top-trailing corner:
41
+
42
+ - **Small:** its bottom-leading corner sits 6dp down and 6dp in, so the dot fills the icon's
43
+ top-trailing corner.
44
+ - **Large:** its bottom-leading corner sits 14dp down and 12dp in. The badge grows away from the
45
+ icon as the count gets wider.
46
+
47
+ So wrap the icon itself in a `relative inline-flex` element. Don't anchor the badge to a larger
48
+ indicator or button container, or the offsets will be measured from the wrong corner.
49
+
50
+ ```svelte
51
+ <span class="relative inline-flex">
52
+ <Icon name="mail" />
53
+ <Badge number={5} />
54
+ </span>
55
+ ```
56
+
57
+ In normal flow, such as a list item's trailing slot, pass `standalone`. `ListItem`'s `badge`
58
+ prop already does this.
59
+
60
+ <Canvas of={BadgeStories.Standalone} />
61
+
62
+ ## Accessibility
63
+
64
+ - **Not announced on its own.** `Badge` renders a plain `<span>` with no role or live region.
65
+ Put the count in the anchor's accessible name (e.g. `aria-label="Inbox, 5 new"`), or use an
66
+ `aria-live` region if changes must be announced.
67
+ - **Contrast.** The badge uses the error/on-error pair in every theme, so its label keeps
68
+ contrast against its own container.
69
+ - **Clipping.** An anchored badge overhangs the icon by 2dp at the top and past the trailing edge
70
+ as it widens. Make sure no ancestor with `overflow: hidden` clips it.
@@ -1,9 +1,9 @@
1
1
  <!--
2
- Badge renders `absolute` (top/right offsets baked into its `size` variant), so it always needs a
3
- `position: relative` ancestor to anchor against — it's an overlay primitive, not a
4
- standalone/inline element. NavbarItem/RailItem already wire this up internally via their
5
- `badge` prop; the stories below reproduce that same relative-wrapper pattern by hand so Badge
6
- can be previewed in isolation.
2
+ Badge renders `absolute` by default, offset from its positioned ancestor's top-trailing corner
3
+ per the M3 spec, so that ancestor must hug the 24dp icon (not a larger indicator container).
4
+ NavbarItem/RailItem already wire this up internally via their `badge` prop; the stories below
5
+ reproduce that same `relative inline-flex` wrapper by hand so Badge can be previewed in
6
+ isolation. `standalone` renders it in normal flow instead (e.g. ListItem's trailing slot).
7
7
  -->
8
8
  <script module lang="ts">
9
9
  import { defineMeta } from '@storybook/addon-svelte-csf';
@@ -18,41 +18,59 @@
18
18
  control: 'select',
19
19
  options: ['sm', 'lg']
20
20
  },
21
- number: { control: 'number' }
21
+ number: { control: 'number' },
22
+ max: { control: 'number' },
23
+ standalone: { control: 'boolean' }
22
24
  },
23
25
  args: {
24
26
  size: 'lg',
25
- number: 5
27
+ number: 5,
28
+ max: 999,
29
+ standalone: false
26
30
  }
27
31
  });
28
32
  </script>
29
33
 
30
34
  <Story name="Playground">
31
35
  {#snippet template(args)}
32
- <div class="relative inline-flex size-10 items-center justify-center">
36
+ <span class="relative inline-flex">
33
37
  <Icon name="mail" />
34
- <Badge size={args.size} number={args.number} />
35
- </div>
38
+ <Badge size={args.size} number={args.number} max={args.max} standalone={args.standalone} />
39
+ </span>
36
40
  {/snippet}
37
41
  </Story>
38
42
 
39
- <Story name="Dot (Small)" asChild>
40
- <div class="relative inline-flex size-10 items-center justify-center">
43
+ <Story name="Dot (Small)" exportName="DotSmall" asChild>
44
+ <span class="relative inline-flex">
41
45
  <Icon name="notifications" />
42
46
  <Badge size="sm" />
43
- </div>
47
+ </span>
44
48
  </Story>
45
49
 
46
- <Story name="Numbered (Large)" asChild>
47
- <div class="relative inline-flex size-10 items-center justify-center">
50
+ <Story name="Numbered (Large)" exportName="NumberedLarge" asChild>
51
+ <span class="relative inline-flex">
48
52
  <Icon name="mail" />
49
53
  <Badge size="lg" number={7} />
50
- </div>
54
+ </span>
51
55
  </Story>
52
56
 
53
- <Story name="Overflow (99+)" asChild>
54
- <div class="relative inline-flex size-10 items-center justify-center">
57
+ <Story name="Two digits" asChild>
58
+ <span class="relative inline-flex">
55
59
  <Icon name="mail" />
56
- <Badge size="lg" number={128} />
57
- </div>
60
+ <Badge size="lg" number={42} />
61
+ </span>
62
+ </Story>
63
+
64
+ <Story name="Max character count (999+)" exportName="MaxCharacterCount" asChild>
65
+ <span class="relative inline-flex">
66
+ <Icon name="mail" />
67
+ <Badge size="lg" number={1280} />
68
+ </span>
69
+ </Story>
70
+
71
+ <Story name="Standalone" asChild>
72
+ <span class="gap-spacing-100 inline-flex items-center">
73
+ <Badge size="sm" standalone />
74
+ <Badge size="lg" number={3} standalone />
75
+ </span>
58
76
  </Story>
@@ -12,15 +12,22 @@ Large badges are used for specific counts.
12
12
  import { badge } from './theme.js';
13
13
  import type { BadgeProps } from './types.js';
14
14
 
15
- const { number, size = 'lg', class: className, ...restProps }: BadgeProps = $props();
15
+ const {
16
+ number,
17
+ size = 'lg',
18
+ max = 999,
19
+ standalone = false,
20
+ class: className,
21
+ ...restProps
22
+ }: BadgeProps = $props();
16
23
 
17
- const styling = $derived(badge({ size }));
24
+ const styling = $derived(badge({ size, standalone }));
18
25
  </script>
19
26
 
20
27
  {#if size === 'sm' || number}
21
- <div class={clsx(styling, className)} {...restProps}>
28
+ <span class={clsx(styling, className)} {...restProps}>
22
29
  {#if size !== 'sm' && number}
23
- {number > 99 ? '99+' : number}
30
+ {number > max ? `${max}+` : number}
24
31
  {/if}
25
- </div>
32
+ </span>
26
33
  {/if}
@@ -1,16 +1,39 @@
1
+ /**
2
+ * M3 badge (https://m3.material.io/components/badges/specs).
3
+ *
4
+ * Anchored offsets are measured from the positioned ancestor's top-trailing corner, so that
5
+ * ancestor must hug the icon (24dp box), not the larger active-indicator container:
6
+ * - small: 6dp, its bottom-leading corner 6dp down / 6dp in from the icon's top-trailing corner,
7
+ * i.e. flush with that corner (`top-spacing-0 right-spacing-0`).
8
+ * - large: 16dp tall, bottom-leading corner 14dp down / 12dp in from the icon's top-trailing
9
+ * corner, i.e. top = 14 − 16 = −2dp, left = 100% − 12dp. Anchored by its leading edge so a
10
+ * wider count grows away from the icon.
11
+ */
1
12
  export declare const badge: import("tailwind-variants").TVReturnType<{
2
13
  size: {
3
- sm: "size-1.5 top-0 right-3";
4
- lg: "h-3 min-w-3 p-1 -top-1 right-1";
14
+ sm: "size-spacing-75";
15
+ lg: "h-spacing-200 min-w-spacing-200 px-spacing-50 md-sys-typescale-label-small whitespace-nowrap tabular-nums";
5
16
  };
6
- }, undefined, "bg-md-sys-color-error text-md-sys-color-on-error absolute rounded-full md-sys-typescale-label-small inline-flex items-center justify-center", {
17
+ standalone: {
18
+ true: "relative";
19
+ false: "absolute pointer-events-none";
20
+ };
21
+ }, undefined, "bg-md-sys-color-error text-md-sys-color-on-error rounded-full inline-flex shrink-0 items-center justify-center", {
7
22
  size: {
8
- sm: "size-1.5 top-0 right-3";
9
- lg: "h-3 min-w-3 p-1 -top-1 right-1";
23
+ sm: "size-spacing-75";
24
+ lg: "h-spacing-200 min-w-spacing-200 px-spacing-50 md-sys-typescale-label-small whitespace-nowrap tabular-nums";
25
+ };
26
+ standalone: {
27
+ true: "relative";
28
+ false: "absolute pointer-events-none";
10
29
  };
11
30
  }, undefined, import("tailwind-variants").TVReturnTypeLike<{
12
31
  size: {
13
- sm: "size-1.5 top-0 right-3";
14
- lg: "h-3 min-w-3 p-1 -top-1 right-1";
32
+ sm: "size-spacing-75";
33
+ lg: "h-spacing-200 min-w-spacing-200 px-spacing-50 md-sys-typescale-label-small whitespace-nowrap tabular-nums";
34
+ };
35
+ standalone: {
36
+ true: "relative";
37
+ false: "absolute pointer-events-none";
15
38
  };
16
39
  }, undefined>>;
@@ -1,10 +1,30 @@
1
1
  import { tv } from 'tailwind-variants';
2
+ /**
3
+ * M3 badge (https://m3.material.io/components/badges/specs).
4
+ *
5
+ * Anchored offsets are measured from the positioned ancestor's top-trailing corner, so that
6
+ * ancestor must hug the icon (24dp box), not the larger active-indicator container:
7
+ * - small: 6dp, its bottom-leading corner 6dp down / 6dp in from the icon's top-trailing corner,
8
+ * i.e. flush with that corner (`top-spacing-0 right-spacing-0`).
9
+ * - large: 16dp tall, bottom-leading corner 14dp down / 12dp in from the icon's top-trailing
10
+ * corner, i.e. top = 14 − 16 = −2dp, left = 100% − 12dp. Anchored by its leading edge so a
11
+ * wider count grows away from the icon.
12
+ */
2
13
  export const badge = tv({
3
- base: 'bg-md-sys-color-error text-md-sys-color-on-error absolute rounded-full md-sys-typescale-label-small inline-flex items-center justify-center',
14
+ base: 'bg-md-sys-color-error text-md-sys-color-on-error rounded-full inline-flex shrink-0 items-center justify-center',
4
15
  variants: {
5
16
  size: {
6
- sm: 'size-1.5 top-0 right-3',
7
- lg: 'h-3 min-w-3 p-1 -top-1 right-1'
17
+ sm: 'size-spacing-75',
18
+ lg: 'h-spacing-200 min-w-spacing-200 px-spacing-50 md-sys-typescale-label-small whitespace-nowrap tabular-nums'
19
+ },
20
+ standalone: {
21
+ true: 'relative',
22
+ false: 'absolute pointer-events-none'
8
23
  }
9
- }
24
+ },
25
+ compoundVariants: [
26
+ { standalone: false, size: 'sm', class: 'top-spacing-0 right-spacing-0' },
27
+ { standalone: false, size: 'lg', class: '-top-spacing-25 left-[calc(100%-12px)]' }
28
+ ],
29
+ defaultVariants: { size: 'lg', standalone: false }
10
30
  });
@@ -2,17 +2,31 @@ import type { HTMLAttributes } from 'svelte/elements';
2
2
  import type { VariantProps } from 'tailwind-variants';
3
3
  import type { badge } from './theme.js';
4
4
  export type BadgeVariants = VariantProps<typeof badge>;
5
- export type BadgeProps = BadgeVariants & HTMLAttributes<HTMLDivElement> & {
5
+ export type BadgeProps = BadgeVariants & HTMLAttributes<HTMLSpanElement> & {
6
6
  /**
7
7
  * The size of the badge.
8
- * - 'sm': Small badge, no number displayed.
9
- * - 'lg': Large badge, displays number.
8
+ * - 'sm': Small badge (6dp dot), no number displayed.
9
+ * - 'lg': Large badge (16dp tall), displays number.
10
10
  */
11
11
  size?: BadgeVariants['size'];
12
12
  /**
13
13
  * The number to display in the badge.
14
- * Values greater than 99 will be displayed as "99+".
14
+ * Values greater than `max` are displayed as `${max}+`.
15
15
  * If size is 'sm', the number is not displayed.
16
16
  */
17
17
  number?: number;
18
+ /**
19
+ * Largest count shown before collapsing to `${max}+`. M3 caps a large badge at four
20
+ * characters including the "+" (16×34dp container), so the default is 999 → "999+".
21
+ * @default 999
22
+ */
23
+ max?: number;
24
+ /**
25
+ * By default the badge is absolutely positioned on its nearest positioned ancestor's
26
+ * top-trailing corner per the M3 offsets — that ancestor should hug the icon (e.g.
27
+ * `<span class="relative inline-flex">` around a 24dp `Icon`). Set `standalone` to render it
28
+ * in normal flow instead, e.g. in a list item's trailing slot.
29
+ * @default false
30
+ */
31
+ standalone?: boolean;
18
32
  };
@@ -0,0 +1,85 @@
1
+ import { Meta, Title, Subtitle, Canvas, Controls } from '@storybook/addon-docs/blocks';
2
+ import * as ButtonStories from './Button.stories.svelte';
3
+
4
+ <Meta of={ButtonStories} />
5
+
6
+ <Title />
7
+
8
+ <Subtitle>Buttons prompt most actions in a UI.</Subtitle>
9
+
10
+ [M3 spec](https://m3.material.io/components/buttons/specs) ·
11
+ `import { Button, Toggle } from '@noxlovette/material';`
12
+
13
+ <Canvas of={ButtonStories.Playground} />
14
+
15
+ <Controls of={ButtonStories.Playground} />
16
+
17
+ ## Choosing a button
18
+
19
+ One high-emphasis button per screen, for its primary action. Show the rest's importance through
20
+ their colour style.
21
+
22
+ | Emphasis | Use | For example |
23
+ | -------- | --------------------------------------------------------------------- | -------------------- |
24
+ | Highest | `FAB` or extended FAB, for the screen's main action | Compose, Create |
25
+ | High | `Button variant="filled"`: final or unblocking actions | Save, Confirm, Done |
26
+ | High | `SplitButton`: a key action with related options | Send + Schedule send |
27
+ | Medium | `variant="tonal"`: important, a step below filled | Save draft |
28
+ | Medium | `variant="elevated"`: only to stand apart from a patterned background | Add to cart |
29
+ | Medium | `variant="outlined"`: attention without being primary; leaving a flow | View all, Cancel |
30
+ | Low | `variant="text"`: optional actions | Learn more |
31
+ | Low | `ButtonIcon`: compact supplementary actions | Favourite, Print |
32
+
33
+ Put buttons side by side rather than stacked when there's room, with the higher-emphasis one
34
+ for the more important action.
35
+
36
+ <Canvas of={ButtonStories.ColourStyles} />
37
+
38
+ ## Sizes
39
+
40
+ | `size` | Height | Padding | Icon | Label |
41
+ | ------ | ------ | ------- | ---- | -------------- |
42
+ | `xs` | 32dp | 12dp | 20dp | label large |
43
+ | `sm` | 40dp | 16dp | 20dp | label large |
44
+ | `md` | 56dp | 24dp | 24dp | title medium |
45
+ | `lg` | 96dp | 48dp | 32dp | headline small |
46
+ | `xl` | 136dp | 64dp | 40dp | headline large |
47
+
48
+ `sm` is the default. XS and S buttons keep a 48dp target area around them, so leave room
49
+ between them (a `ButtonGroup` does). The outline thickens to 2dp at L and 3dp at XL.
50
+
51
+ <Canvas of={ButtonStories.Sizes} />
52
+
53
+ ## Shape
54
+
55
+ `round` (default) is fully rounded; `square` has 12, 12, 16, 28 and 28dp corners from XS to XL.
56
+ While pressed, both morph to the same tighter corner (8, 8, 12, 16, 16dp) on the fast spatial
57
+ spring, which carries its velocity when you let go mid-morph, so a quick tap still bounces.
58
+
59
+ <Canvas of={ButtonStories.RoundAndSquare} />
60
+
61
+ ## Toggle buttons
62
+
63
+ `Toggle` is a button with a selected state (`aria-pressed`). Selecting changes its colours and
64
+ swaps its shape: round becomes square, square becomes round. It comes in the elevated, filled,
65
+ tonal and outlined styles; there is no text toggle. See **Buttons/Toggle**.
66
+
67
+ ## States and accessibility
68
+
69
+ - Hover, focus and press tint the container 8%, 10% and 10% in the content colour. Keyboard
70
+ focus draws a 3dp ring 2dp outside the button.
71
+ - Disabled buttons drop to a 10% on-surface container and 38% on-surface content, and lose
72
+ their elevation. A disabled link gets `aria-disabled` and leaves the tab order.
73
+ - `loading` swaps the icon (or the label, with no icon) for a loading indicator and sets
74
+ `aria-busy`.
75
+
76
+ <Canvas of={ButtonStories.Disabled} />
77
+
78
+ ## Where this differs from the spec
79
+
80
+ - The text button's disabled state has no container. Its token lists a 10% container, but the
81
+ spec's figures show none.
82
+ - Shape morphs use the library's fast spatial spring (stiffness 800, damping 0.6) instead of
83
+ the button token's 1400 / 0.9, as every motion here uses the Expressive springs.
84
+ - `color` (primary, secondary, tertiary, error) is gone. M3 has one colour mapping per style;
85
+ restyle with a class if you need another role, keeping 3:1 contrast.
@@ -2,33 +2,22 @@
2
2
  import { defineMeta } from '@storybook/addon-svelte-csf';
3
3
  import Button from './Button.svelte';
4
4
 
5
+ const variants = ['elevated', 'filled', 'tonal', 'outlined', 'text'] as const;
6
+ const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const;
7
+
5
8
  const { Story } = defineMeta({
6
9
  title: 'Buttons/Button',
7
10
  component: Button,
8
11
  argTypes: {
9
- variant: {
10
- control: 'select',
11
- options: ['elevated', 'filled', 'tonal', 'outlined', 'text', 'bare']
12
- },
13
- color: {
14
- control: 'select',
15
- options: ['default', 'primary', 'secondary', 'tertiary', 'error']
16
- },
17
- size: {
18
- control: 'select',
19
- options: ['xs', 'sm', 'md', 'lg', 'xl']
20
- },
21
- shape: {
22
- control: 'select',
23
- options: ['round', 'square']
24
- },
12
+ variant: { control: 'select', options: variants },
13
+ size: { control: 'select', options: sizes },
14
+ shape: { control: 'inline-radio', options: ['round', 'square'] },
25
15
  disabled: { control: 'boolean' },
26
16
  loading: { control: 'boolean' }
27
17
  },
28
18
  args: {
29
19
  variant: 'filled',
30
- color: 'primary',
31
- size: 'md',
20
+ size: 'sm',
32
21
  shape: 'round',
33
22
  disabled: false,
34
23
  loading: false
@@ -36,15 +25,61 @@
36
25
  });
37
26
  </script>
38
27
 
39
- <Story name="Playground">Label</Story>
28
+ <Story name="Playground">
29
+ {#snippet template(args)}
30
+ <Button {...args}>Label</Button>
31
+ {/snippet}
32
+ </Story>
33
+
34
+ <Story name="Colour styles" asChild>
35
+ <div class="gap-spacing-200 p-spacing-300 flex flex-wrap items-center">
36
+ {#each variants as variant (variant)}
37
+ <Button {variant}>{variant[0].toUpperCase() + variant.slice(1)}</Button>
38
+ {/each}
39
+ </div>
40
+ </Story>
41
+
42
+ <Story name="With icon" asChild>
43
+ <div class="gap-spacing-200 p-spacing-300 flex flex-wrap items-center">
44
+ {#each variants as variant (variant)}
45
+ <Button {variant} iconProps={{ name: 'add' }}>Create</Button>
46
+ {/each}
47
+ </div>
48
+ </Story>
49
+
50
+ <Story name="Sizes" asChild>
51
+ <div class="gap-spacing-200 p-spacing-300 flex flex-col items-start">
52
+ {#each sizes as size (size)}
53
+ <div class="gap-spacing-200 flex items-center">
54
+ <Button {size} iconProps={{ name: 'send' }}>Send</Button>
55
+ <Button {size} shape="square" variant="tonal" iconProps={{ name: 'send' }}>Send</Button>
56
+ <Button {size} variant="outlined">Cancel</Button>
57
+ </div>
58
+ {/each}
59
+ </div>
60
+ </Story>
61
+
62
+ <Story name="Round and square" asChild>
63
+ <div class="gap-spacing-200 p-spacing-300 flex flex-wrap items-center">
64
+ <Button size="md">Round</Button>
65
+ <Button size="md" shape="square">Square</Button>
66
+ <p class="md-sys-typescale-body-medium text-md-sys-color-on-surface-variant w-full">
67
+ Press and hold: both morph to the same smaller corner.
68
+ </p>
69
+ </div>
70
+ </Story>
71
+
72
+ <Story name="Disabled" asChild>
73
+ <div class="gap-spacing-200 p-spacing-300 flex flex-wrap items-center">
74
+ {#each variants as variant (variant)}
75
+ <Button {variant} disabled>{variant}</Button>
76
+ {/each}
77
+ </div>
78
+ </Story>
40
79
 
41
- <Story name="Variants" asChild>
42
- <div class="flex flex-wrap items-center gap-4 p-6">
43
- <Button variant="elevated" color="primary">Elevated</Button>
44
- <Button variant="filled" color="primary">Filled</Button>
45
- <Button variant="tonal" color="primary">Tonal</Button>
46
- <Button variant="outlined" color="primary">Outlined</Button>
47
- <Button variant="text" color="primary">Text</Button>
48
- <Button variant="bare" color="primary">Bare</Button>
80
+ <Story name="Link" asChild>
81
+ <div class="gap-spacing-200 p-spacing-300 flex">
82
+ <Button href="#" variant="text">Learn more</Button>
83
+ <Button href="#" variant="outlined" disabled>Disabled link</Button>
49
84
  </div>
50
85
  </Story>