@noxlovette/material 0.5.1 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) 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 +10 -6
  8. package/dist/animation/TransitionPatterns.stories.svelte +30 -20
  9. package/dist/animation/containerTransform.d.ts +4 -2
  10. package/dist/animation/containerTransform.js +4 -2
  11. package/dist/animation/enterExit.js +5 -2
  12. package/dist/animation/skeleton.d.ts +1 -1
  13. package/dist/animation/skeleton.js +1 -1
  14. package/dist/components/badge/Badge.mdx +70 -0
  15. package/dist/components/badge/Badge.stories.svelte +38 -20
  16. package/dist/components/badge/Badge.svelte +12 -5
  17. package/dist/components/badge/theme.d.ts +30 -7
  18. package/dist/components/badge/theme.js +25 -5
  19. package/dist/components/badge/types.d.ts +18 -4
  20. package/dist/components/buttons/Button.mdx +85 -0
  21. package/dist/components/buttons/Button.stories.svelte +62 -27
  22. package/dist/components/buttons/Button.svelte +29 -25
  23. package/dist/components/buttons/Button.svelte.d.ts +11 -9
  24. package/dist/components/buttons/ButtonIcon.mdx +52 -0
  25. package/dist/components/buttons/ButtonIcon.stories.svelte +72 -48
  26. package/dist/components/buttons/ButtonIcon.svelte +58 -36
  27. package/dist/components/buttons/ButtonIcon.svelte.d.ts +9 -7
  28. package/dist/components/buttons/FAB.mdx +84 -0
  29. package/dist/components/buttons/FAB.stories.svelte +103 -24
  30. package/dist/components/buttons/FAB.stories.svelte.d.ts +2 -17
  31. package/dist/components/buttons/FAB.svelte +353 -29
  32. package/dist/components/buttons/FAB.svelte.d.ts +11 -6
  33. package/dist/components/buttons/FABMenu.svelte +27 -8
  34. package/dist/components/buttons/FABMenu.svelte.d.ts +7 -3
  35. package/dist/components/buttons/FABMenuItem.svelte +20 -16
  36. package/dist/components/buttons/FABMenuItem.svelte.d.ts +3 -3
  37. package/dist/components/buttons/Toggle.stories.svelte +31 -23
  38. package/dist/components/buttons/Toggle.stories.svelte.d.ts +2 -17
  39. package/dist/components/buttons/Toggle.svelte +28 -25
  40. package/dist/components/buttons/Toggle.svelte.d.ts +5 -5
  41. package/dist/components/buttons/button-group/ButtonGroup.mdx +39 -0
  42. package/dist/components/buttons/button-group/ButtonGroup.stories.svelte +62 -43
  43. package/dist/components/buttons/button-group/ButtonGroup.stories.svelte.d.ts +2 -17
  44. package/dist/components/buttons/button-group/ButtonGroup.svelte +102 -8
  45. package/dist/components/buttons/button-group/ButtonGroup.svelte.d.ts +6 -7
  46. package/dist/components/buttons/button-group/theme.d.ts +46 -16
  47. package/dist/components/buttons/button-group/theme.js +14 -9
  48. package/dist/components/buttons/button-group/types.d.ts +13 -7
  49. package/dist/components/buttons/connected/ConnectedButtonGroup.mdx +43 -0
  50. package/dist/components/buttons/connected/ConnectedButtonGroup.stories.svelte +56 -74
  51. package/dist/components/buttons/connected/ConnectedButtonGroup.stories.svelte.d.ts +2 -17
  52. package/dist/components/buttons/connected/ConnectedButtonGroup.svelte +16 -4
  53. package/dist/components/buttons/connected/ConnectedButtonGroup.svelte.d.ts +4 -4
  54. package/dist/components/buttons/connected/ConnectedButtonGroupItem.svelte +35 -8
  55. package/dist/components/buttons/connected/ConnectedButtonGroupItem.svelte.d.ts +2 -3
  56. package/dist/components/buttons/connected/theme.d.ts +34 -19
  57. package/dist/components/buttons/connected/theme.js +33 -10
  58. package/dist/components/buttons/connected/types.d.ts +36 -9
  59. package/dist/components/buttons/shapeMorph.d.ts +26 -0
  60. package/dist/components/buttons/shapeMorph.js +142 -0
  61. package/dist/components/buttons/split-button/SplitButton.mdx +48 -0
  62. package/dist/components/buttons/split-button/SplitButton.stories.svelte +9 -14
  63. package/dist/components/buttons/split-button/SplitButton.svelte +66 -49
  64. package/dist/components/buttons/split-button/SplitButton.svelte.d.ts +6 -11
  65. package/dist/components/buttons/split-button/theme.d.ts +111 -18
  66. package/dist/components/buttons/split-button/theme.js +75 -17
  67. package/dist/components/buttons/split-button/types.d.ts +24 -18
  68. package/dist/components/buttons/theme.d.ts +423 -231
  69. package/dist/components/buttons/theme.js +287 -381
  70. package/dist/components/buttons/types.d.ts +130 -125
  71. package/dist/components/cards/Card.mdx +44 -0
  72. package/dist/components/cards/Card.stories.svelte +11 -11
  73. package/dist/components/cards/theme.js +6 -6
  74. package/dist/components/chips/Chip.mdx +68 -0
  75. package/dist/components/chips/Chip.stories.svelte +4 -4
  76. package/dist/components/chips/theme.d.ts +1 -1
  77. package/dist/components/chips/theme.js +4 -4
  78. package/dist/components/containers/action-rail/ActionRail.stories.svelte +1 -0
  79. package/dist/components/containers/action-rail/theme.d.ts +1 -1
  80. package/dist/components/containers/action-rail/theme.js +2 -2
  81. package/dist/components/containers/app/theme.d.ts +1 -1
  82. package/dist/components/containers/app/theme.js +1 -1
  83. package/dist/components/containers/bottom-sheet/BottomSheet.mdx +64 -0
  84. package/dist/components/containers/bottom-sheet/BottomSheet.stories.svelte +108 -12
  85. package/dist/components/containers/bottom-sheet/BottomSheet.svelte +179 -109
  86. package/dist/components/containers/bottom-sheet/BottomSheet.svelte.d.ts +10 -5
  87. package/dist/components/containers/bottom-sheet/index.d.ts +1 -0
  88. package/dist/components/containers/bottom-sheet/index.js +1 -0
  89. package/dist/components/containers/bottom-sheet/theme.d.ts +45 -0
  90. package/dist/components/containers/bottom-sheet/theme.js +42 -0
  91. package/dist/components/containers/bottom-sheet/types.d.ts +37 -5
  92. package/dist/components/containers/carousel/Carousel.mdx +80 -0
  93. package/dist/components/containers/carousel/Carousel.stories.svelte +131 -0
  94. package/dist/components/containers/carousel/Carousel.stories.svelte.d.ts +4 -0
  95. package/dist/components/containers/carousel/Carousel.svelte +194 -0
  96. package/dist/components/containers/carousel/Carousel.svelte.d.ts +25 -0
  97. package/dist/components/containers/carousel/index.d.ts +3 -0
  98. package/dist/components/containers/{stack → carousel}/index.js +1 -2
  99. package/dist/components/containers/carousel/keylines.d.ts +32 -0
  100. package/dist/components/containers/carousel/keylines.js +88 -0
  101. package/dist/components/containers/carousel/theme.d.ts +162 -0
  102. package/dist/components/containers/carousel/theme.js +67 -0
  103. package/dist/components/containers/carousel/types.d.ts +52 -0
  104. package/dist/components/containers/context-menu/ContextMenu.stories.svelte +1 -0
  105. package/dist/components/containers/context-menu/ContextMenu.svelte +2 -2
  106. package/dist/components/containers/context-menu/ContextMenu.svelte.d.ts +1 -1
  107. package/dist/components/containers/context-menu/theme.d.ts +1 -1
  108. package/dist/components/containers/context-menu/theme.js +11 -11
  109. package/dist/components/containers/dialogue/Dialogue.mdx +65 -0
  110. package/dist/components/containers/dialogue/Dialogue.stories.svelte +4 -4
  111. package/dist/components/containers/dialogue/Dialogue.svelte +2 -2
  112. package/dist/components/containers/dialogue/theme.d.ts +1 -1
  113. package/dist/components/containers/dialogue/theme.js +4 -4
  114. package/dist/components/containers/dialogue/types.d.ts +1 -1
  115. package/dist/components/containers/divider/Divider.stories.svelte +2 -1
  116. package/dist/components/containers/divider/Divider.svelte +2 -2
  117. package/dist/components/containers/divider/theme.d.ts +7 -7
  118. package/dist/components/containers/divider/theme.js +3 -3
  119. package/dist/components/containers/index.d.ts +1 -1
  120. package/dist/components/containers/index.js +1 -1
  121. package/dist/components/containers/link-preview/theme.d.ts +1 -1
  122. package/dist/components/containers/link-preview/theme.js +3 -3
  123. package/dist/components/containers/list/List.svelte +18 -0
  124. package/dist/components/containers/list/List.svelte.d.ts +4 -0
  125. package/dist/components/containers/list/ListItem.mdx +100 -0
  126. package/dist/components/containers/list/ListItem.stories.svelte +177 -42
  127. package/dist/components/containers/list/ListItem.stories.svelte.d.ts +2 -17
  128. package/dist/components/containers/list/ListItem.svelte +92 -37
  129. package/dist/components/containers/list/ListItem.svelte.d.ts +1 -1
  130. package/dist/components/containers/list/context.d.ts +6 -0
  131. package/dist/components/containers/list/context.js +4 -0
  132. package/dist/components/containers/list/index.d.ts +1 -0
  133. package/dist/components/containers/list/index.js +1 -0
  134. package/dist/components/containers/list/theme.d.ts +487 -12
  135. package/dist/components/containers/list/theme.js +141 -14
  136. package/dist/components/containers/list/types.d.ts +39 -5
  137. package/dist/components/containers/menu/Menu.mdx +52 -0
  138. package/dist/components/containers/menu/Menu.stories.svelte +14 -14
  139. package/dist/components/containers/menu/Menu.svelte +2 -2
  140. package/dist/components/containers/menu/MenuGroup.svelte +1 -1
  141. package/dist/components/containers/menu/MenuGroup.svelte.d.ts +1 -1
  142. package/dist/components/containers/menu/MenuSub.svelte +1 -1
  143. package/dist/components/containers/menu/menu-item/MenuItem.svelte +1 -1
  144. package/dist/components/containers/menu/theme.d.ts +1 -1
  145. package/dist/components/containers/menu/theme.js +9 -9
  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.d.ts +1 -1
  158. package/dist/components/containers/pane/theme.js +131 -47
  159. package/dist/components/containers/pane/types.d.ts +3 -2
  160. package/dist/components/containers/popover/Popover.mdx +45 -0
  161. package/dist/components/containers/popover/Popover.stories.svelte +1 -1
  162. package/dist/components/containers/popover/Popover.svelte +1 -1
  163. package/dist/components/containers/popover/theme.d.ts +1 -1
  164. package/dist/components/containers/popover/theme.js +4 -4
  165. package/dist/components/containers/scroll-area/theme.js +3 -3
  166. package/dist/components/containers/side-sheet/SideSheet.mdx +71 -0
  167. package/dist/components/containers/side-sheet/SideSheet.stories.svelte +113 -17
  168. package/dist/components/containers/side-sheet/SideSheet.svelte +113 -66
  169. package/dist/components/containers/side-sheet/SideSheet.svelte.d.ts +8 -7
  170. package/dist/components/containers/side-sheet/index.d.ts +1 -0
  171. package/dist/components/containers/side-sheet/index.js +1 -0
  172. package/dist/components/containers/side-sheet/theme.d.ts +108 -0
  173. package/dist/components/containers/side-sheet/theme.js +72 -0
  174. package/dist/components/containers/side-sheet/types.d.ts +35 -7
  175. package/dist/components/date/DateField.stories.svelte +4 -3
  176. package/dist/components/date/DateField.svelte +11 -2
  177. package/dist/components/date/DateRangeField.stories.svelte +4 -3
  178. package/dist/components/date/DateRangeField.svelte +12 -3
  179. package/dist/components/date/theme.d.ts +8 -8
  180. package/dist/components/date/theme.js +32 -32
  181. package/dist/components/forms/checkbox/Checkbox.mdx +35 -0
  182. package/dist/components/forms/checkbox/Checkbox.stories.svelte +2 -2
  183. package/dist/components/forms/checkbox/Checkbox.svelte +1 -1
  184. package/dist/components/forms/checkbox/theme.d.ts +1 -1
  185. package/dist/components/forms/checkbox/theme.js +4 -4
  186. package/dist/components/forms/command/Command.stories.svelte +1 -0
  187. package/dist/components/forms/command/theme.d.ts +1 -1
  188. package/dist/components/forms/command/theme.js +10 -10
  189. package/dist/components/forms/pin/theme.d.ts +1 -1
  190. package/dist/components/forms/pin/theme.js +5 -5
  191. package/dist/components/forms/radio-group/RadioGroup.svelte +1 -1
  192. package/dist/components/forms/radio-group/theme.d.ts +1 -1
  193. package/dist/components/forms/radio-group/theme.js +6 -6
  194. package/dist/components/forms/search/Search.mdx +38 -0
  195. package/dist/components/forms/search/Search.stories.svelte +19 -9
  196. package/dist/components/forms/search/Search.svelte +40 -17
  197. package/dist/components/forms/search/Search.svelte.d.ts +2 -2
  198. package/dist/components/forms/search/theme.d.ts +46 -13
  199. package/dist/components/forms/search/theme.js +14 -9
  200. package/dist/components/forms/search/types.d.ts +18 -12
  201. package/dist/components/forms/select/Select.mdx +53 -0
  202. package/dist/components/forms/select/Select.stories.svelte +4 -4
  203. package/dist/components/forms/select/Select.svelte +12 -4
  204. package/dist/components/forms/select/SelectItem.svelte +1 -1
  205. package/dist/components/forms/select/theme.d.ts +1 -1
  206. package/dist/components/forms/select/theme.js +14 -14
  207. package/dist/components/forms/slider/Slider.mdx +85 -0
  208. package/dist/components/forms/slider/Slider.stories.svelte +7 -7
  209. package/dist/components/forms/slider/Slider.svelte +46 -28
  210. package/dist/components/forms/slider/theme.d.ts +15 -1
  211. package/dist/components/forms/slider/theme.js +29 -18
  212. package/dist/components/forms/slider/types.d.ts +3 -2
  213. package/dist/components/forms/switch/Switch.mdx +38 -0
  214. package/dist/components/forms/switch/Switch.stories.svelte +3 -3
  215. package/dist/components/forms/switch/theme.d.ts +1 -1
  216. package/dist/components/forms/switch/theme.js +7 -7
  217. package/dist/components/forms/textfield/Textfield.mdx +53 -0
  218. package/dist/components/forms/textfield/Textfield.stories.svelte +6 -6
  219. package/dist/components/forms/textfield/Textfield.svelte +1 -0
  220. package/dist/components/forms/textfield/theme.d.ts +1 -1
  221. package/dist/components/forms/textfield/theme.js +20 -20
  222. package/dist/components/forms/toggle-group/theme.d.ts +1 -1
  223. package/dist/components/forms/toggle-group/theme.js +3 -3
  224. package/dist/components/forms/tooltip/Tooltip.stories.svelte +1 -0
  225. package/dist/components/forms/tooltip/theme.d.ts +1 -1
  226. package/dist/components/forms/tooltip/theme.js +4 -4
  227. package/dist/components/misc/Avatar.stories.svelte +5 -4
  228. package/dist/components/misc/ThemeSettings.svelte +9 -9
  229. package/dist/components/misc/ThemeSwitcher.svelte +3 -3
  230. package/dist/components/misc/theme.js +5 -5
  231. package/dist/components/nav/appbar/AppBar.mdx +71 -0
  232. package/dist/components/nav/appbar/AppBar.stories.svelte +93 -3
  233. package/dist/components/nav/appbar/AppBar.stories.svelte.d.ts +2 -17
  234. package/dist/components/nav/appbar/AppBar.svelte +90 -37
  235. package/dist/components/nav/appbar/AppBar.svelte.d.ts +9 -2
  236. package/dist/components/nav/appbar/theme.d.ts +86 -19
  237. package/dist/components/nav/appbar/theme.js +160 -24
  238. package/dist/components/nav/appbar/types.d.ts +46 -9
  239. package/dist/components/nav/breadcrumb/Breadcrumb.stories.svelte +1 -0
  240. package/dist/components/nav/breadcrumb/theme.d.ts +1 -1
  241. package/dist/components/nav/breadcrumb/theme.js +6 -6
  242. package/dist/components/nav/navbar/Navbar.mdx +38 -0
  243. package/dist/components/nav/navbar/Navbar.svelte +1 -1
  244. package/dist/components/nav/navbar/NavbarItem.svelte +12 -9
  245. package/dist/components/nav/navbar/theme.d.ts +3 -3
  246. package/dist/components/nav/navbar/theme.js +7 -7
  247. package/dist/components/nav/rail/Rail.mdx +73 -0
  248. package/dist/components/nav/rail/Rail.stories.svelte +3 -7
  249. package/dist/components/nav/rail/Rail.svelte +5 -2
  250. package/dist/components/nav/rail/RailItem.svelte +12 -9
  251. package/dist/components/nav/rail/theme.d.ts +1 -1
  252. package/dist/components/nav/rail/theme.js +14 -14
  253. package/dist/components/nav/rail/types.d.ts +8 -1
  254. package/dist/components/nav/tabs/Tabs.mdx +62 -0
  255. package/dist/components/nav/tabs/Tabs.stories.svelte +1 -1
  256. package/dist/components/nav/tabs/theme.d.ts +1 -1
  257. package/dist/components/nav/tabs/theme.js +5 -5
  258. package/dist/components/pill/Pill.mdx +32 -0
  259. package/dist/components/pill/Pill.stories.svelte +1 -1
  260. package/dist/components/pill/theme.d.ts +2 -2
  261. package/dist/components/pill/theme.js +2 -2
  262. package/dist/components/progress/LinearProgress.svelte +2 -2
  263. package/dist/components/progress/Progress.mdx +50 -0
  264. package/dist/components/progress/Progress.stories.svelte +6 -6
  265. package/dist/components/snackbar/Snackbar.mdx +52 -0
  266. package/dist/components/snackbar/Snackbar.stories.svelte +5 -5
  267. package/dist/components/snackbar/Snackbar.svelte +1 -1
  268. package/dist/components/snackbar/theme.d.ts +4 -4
  269. package/dist/components/snackbar/theme.js +5 -5
  270. package/dist/components/table/Table.stories.svelte +1 -0
  271. package/dist/components/table/TableHeader.svelte +1 -1
  272. package/dist/components/table/theme.d.ts +1 -1
  273. package/dist/components/table/theme.js +4 -4
  274. package/dist/components/time/TimeField.stories.svelte +3 -2
  275. package/dist/components/time/TimeField.svelte +2 -2
  276. package/dist/components/time/TimepickerInput.svelte +1 -1
  277. package/dist/components/time/theme.d.ts +1 -1
  278. package/dist/components/time/theme.js +6 -6
  279. package/dist/components/toolbar/Toolbar.stories.svelte +1 -0
  280. package/dist/components/toolbar/Toolbar.svelte +1 -1
  281. package/dist/components/toolbar/Toolbar.svelte.d.ts +1 -1
  282. package/dist/components/toolbar/theme.d.ts +1 -1
  283. package/dist/components/toolbar/theme.js +15 -15
  284. package/dist/components/typography/Typography.mdx +67 -0
  285. package/dist/components/typography/body/Body.stories.svelte +2 -1
  286. package/dist/components/typography/body/theme.d.ts +16 -13
  287. package/dist/components/typography/body/theme.js +20 -5
  288. package/dist/components/typography/body/types.d.ts +2 -1
  289. package/dist/components/typography/display/Display.stories.svelte +2 -1
  290. package/dist/components/typography/display/theme.d.ts +16 -13
  291. package/dist/components/typography/display/theme.js +20 -5
  292. package/dist/components/typography/display/types.d.ts +2 -1
  293. package/dist/components/typography/headline/Headline.stories.svelte +2 -1
  294. package/dist/components/typography/headline/theme.d.ts +16 -13
  295. package/dist/components/typography/headline/theme.js +20 -5
  296. package/dist/components/typography/headline/types.d.ts +2 -1
  297. package/dist/components/typography/kbd/Kbd.stories.svelte +2 -1
  298. package/dist/components/typography/kbd/theme.d.ts +5 -5
  299. package/dist/components/typography/kbd/theme.js +3 -3
  300. package/dist/components/typography/label/Label.stories.svelte +2 -1
  301. package/dist/components/typography/label/theme.d.ts +15 -12
  302. package/dist/components/typography/label/theme.js +20 -5
  303. package/dist/components/typography/label/types.d.ts +2 -1
  304. package/dist/components/typography/title/Title.stories.svelte +2 -1
  305. package/dist/components/typography/title/theme.d.ts +16 -13
  306. package/dist/components/typography/title/theme.js +20 -5
  307. package/dist/components/typography/title/types.d.ts +2 -1
  308. package/dist/index.css +2 -0
  309. package/dist/styles/component.css +78 -175
  310. package/dist/styles/elevation.css +9 -0
  311. package/dist/styles/layers.css +33 -0
  312. package/dist/styles/spacing.css +29 -0
  313. package/dist/styles/typescale.css +635 -20
  314. package/dist/utils/Layer.svelte +1 -1
  315. package/dist/utils/icon/Icon.svelte +8 -6
  316. package/dist/utils/icon/theme.d.ts +1 -1
  317. package/dist/utils/icon/theme.js +1 -1
  318. package/dist/utils/tv.d.ts +6 -0
  319. package/dist/utils/tv.js +12 -0
  320. package/package.json +19 -15
  321. package/dist/components/containers/stack/HStack.svelte +0 -23
  322. package/dist/components/containers/stack/HStack.svelte.d.ts +0 -5
  323. package/dist/components/containers/stack/VStack.svelte +0 -23
  324. package/dist/components/containers/stack/VStack.svelte.d.ts +0 -5
  325. package/dist/components/containers/stack/index.d.ts +0 -4
  326. package/dist/components/containers/stack/theme.d.ts +0 -66
  327. package/dist/components/containers/stack/theme.js +0 -26
  328. package/dist/components/containers/stack/types.d.ts +0 -12
  329. /package/dist/components/containers/{stack → carousel}/types.js +0 -0
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+ // Installs the `material-design` Claude skill shipped with @noxlovette/material into the current
3
+ // project's .claude/skills/. Usage: npx @noxlovette/material material-claude-skill [--force]
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const source = path.resolve(
9
+ path.dirname(fileURLToPath(import.meta.url)),
10
+ '..',
11
+ 'claude-skill',
12
+ 'material-design'
13
+ );
14
+ const dest = path.resolve(process.cwd(), '.claude', 'skills', 'material-design');
15
+ const force = process.argv.includes('--force');
16
+
17
+ if (!fs.existsSync(source)) {
18
+ console.error(`Skill not found at ${source}; this package build doesn't include it.`);
19
+ process.exit(1);
20
+ }
21
+ if (fs.existsSync(dest) && !force) {
22
+ console.error(
23
+ `${path.relative(process.cwd(), dest)} already exists. Re-run with --force to overwrite it.`
24
+ );
25
+ process.exit(1);
26
+ }
27
+
28
+ fs.rmSync(dest, { recursive: true, force: true });
29
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
30
+ fs.cpSync(source, dest, { recursive: true });
31
+ console.log(`Installed the material-design skill into ${path.relative(process.cwd(), dest)}`);
@@ -0,0 +1,68 @@
1
+ ---
2
+ background: false
3
+ description: Grounds UI/UX decisions in Material Design 3 (m3.material.io) and this repo's actual token/component system when creating or editing components in src/lib/components/, choosing a color role/variant/emphasis level, adding a transition or animation, laying out a showcase or docs page, or reviewing existing UI for M3 compliance. Does NOT apply to pure logic/state changes with no visual surface. For Cypress test authoring use cypress-author/cypress-explain/cypress-docs instead.
4
+ model: inherit
5
+ name: material-design
6
+ ---
7
+
8
+ # Material Design 3 for @noxlovette/material
9
+
10
+ ## Purpose
11
+
12
+ This repo is a single, already-opinionated M3 component library, not a blank canvas. Every visual decision (color, type, elevation, shape, motion) already has a token or utility class for it. The job here is never "invent a look" — it's "find the right existing primitive and apply it correctly." This skill encodes that mapping so you don't re-derive M3 from general knowledge or, worse, hardcode a hex/px/ms value that already has a token.
13
+
14
+ ## When to use
15
+
16
+ - Creating or editing a component under `src/lib/components/`
17
+ - Deciding which variant (filled/tonal/outlined/text/elevated) or color role (primary/secondary/tertiary/error) fits a given action or emphasis level
18
+ - Adding a transition, page transition, or list/expand animation, or choosing between transition patterns (container transform vs forward/backward vs lateral vs top level vs enter/exit)
19
+ - Building a showcase route or docs page for a component — these MUST be built from
20
+ `@noxlovette/material` components (`Pane`/`PaneGrid` for layout), never
21
+ hand-rolled flex/aside divs; see `references/component-patterns.md` for the pane decision table
22
+ - Reviewing UI for M3 correctness (contrast, touch targets, focus, motion)
23
+
24
+ **Skip this skill** for changes with no visual surface (pure logic, data layer, config), and for Cypress test work (use the `cypress-*` skills instead).
25
+
26
+ ## Reasoning flow
27
+
28
+ 1. **Map the ask to a category.** This repo groups components by M3 concept, not by page: `buttons/`, `forms/` (textfield, select, checkbox, switch, slider, radio-group, search, command, pin, tooltip), `containers/` (dialogue, side-sheet, bottom-sheet, popover, menu, list, panes, stack, scroll-area), `nav/` (appbar, navbar, rail, tabs), `cards/`, `badge/`, `pill/`, `snackbar/`, `progress/`, `table/`, `date/`, `time/`, `typography/`. Check the target category's existing `theme.ts` before writing a new one — the pattern you need probably already exists one file over.
29
+
30
+ 2. **Pick emphasis → variant + color role.** See the decision table below and `references/component-patterns.md` for the full reasoning and a worked example (`buttons/theme.ts`).
31
+
32
+ 3. **Only use existing tokens.** Never hardcode a color, shadow, radius, or duration — every one of those has a utility class already. Full inventory in `references/tokens-and-styles.md`.
33
+
34
+ 4. **Pick a motion primitive** from `src/lib/animation/` and a spring utility from `motion.css`. For any transition (a surface appearing, or content/screen changing), first choose the M3 pattern from the relationship between the two states. Is it a component entering this screen, a hero expanding, a parent/child step, peers in one set, or unrelated top-level destinations? Then check it against the qualities of a good transition: consistent, stable layout, no jump cuts, one direction, clean fades and a simple style. Both are covered in `references/motion-guide.md` → "Applying transitions", which is based on [m3.material.io → Applying transitions](https://m3.material.io/styles/motion/transitions/applying-transitions).
35
+
36
+ 5. **Run the pre-delivery checklist** in `references/accessibility-checklist.md` before calling the work done.
37
+
38
+ ## Color-role decision table
39
+
40
+ | Emphasis | Variant | Color role | Example use |
41
+ | ----------------------------------------------------------- | ---------- | -------------------------------------- | --------------------------------------------- |
42
+ | Highest — one primary action per screen | `filled` | `primary` (or `error` for destructive) | "Save", "Submit", "Delete account" |
43
+ | High but not _the_ action | `filled` | `secondary` / `tertiary` | secondary CTA next to a filled-primary |
44
+ | Medium, wants to look "chosen"/selected | `tonal` | matches the concept's color | selected chip, toggle-on state, secondary FAB |
45
+ | Medium, needs a visible boundary but low fill | `outlined` | `primary`/matches concept | "Cancel" next to a filled "Confirm" |
46
+ | Low, inline/repeated actions | `text` | `primary` (default) | list-item trailing action, dialog "Cancel" |
47
+ | Needs to visually separate from a colored surface behind it | `elevated` | matches concept | FAB or card floating over a tonal surface |
48
+
49
+ `color: default` in this codebase's `tv()` variants resolves to the same classes as `primary` — pass `primary` explicitly when in doubt.
50
+
51
+ ## Quick reference
52
+
53
+ - Color tokens: `md-sys-color-{primary,secondary,tertiary,error}`, each with a `-container` tonal pair and `on-*` text/icon pair (guarantees AA contrast by construction).
54
+ - Type: `md-sys-typescale-{display,headline,title,body,label}-{large,medium,small}`, plus `md-sys-typescale-emphasized-*` for selected/active/unread states.
55
+ - Spacing: `*-spacing-{0,25,50,75,100,…,900}` (M3 `md.sys.measurement.space*`, 8dp base: `p-spacing-200` = 16dp).
56
+ - Elevation: `shadow-elevation-{0..5}`.
57
+ - Shape: `radius-{none,xs,sm,md,lg,xl,full}` (buttons instead drive shape via the `--btn-shape`/`--btn-pressed-shape` CSS vars in `.md-btn-morph`).
58
+ - Expressive shapes (circle, cookie, sunny, heart…): `animatableShapes`/`animatableShapesSmall` + `shapeMorph` for morphs. See motion-guide.md → "Shape morphing".
59
+ - Motion: M3 Expressive springs only (`springTokens` in `animation/spring.ts`). JS: `presence`/`Presence` + `enterExit` presets, `containerTransform`, `sharedAxis`, `lateral`, `fadeThrough`, `skeleton` — all on Motion, never Svelte transitions. CSS: `transition-* md-sys-motion-{fast-spatial,spatial,slow-spatial,fast-effects,effects,slow-effects}` utilities, never `duration-*`/`ease-*`.
60
+ - Transition pattern choice: appearing on this screen → enter/exit. Hero expands into its detail → container transform (only in shallow hierarchies). Parent/child or sequential steps → forward/backward (`sharedAxis`). Peers in one set → lateral, which never fades. Navbar/rail/drawer destinations → top level (`fadeThrough`), never lateral. Never use enter/exit or lateral for hierarchical navigation. Sheets slide without fading. Don't use bouncy (`fastSpatial`) springs for navigation transitions.
61
+ - Shared primitives: wrap interactive surfaces with `Layer.svelte` (state layer + ripple, already skips itself under `prefers-reduced-motion`), render icons with `Icon.svelte`, use `Divider.svelte` instead of Bits UI's `Separator`.
62
+
63
+ ## References
64
+
65
+ - [`references/tokens-and-styles.md`](references/tokens-and-styles.md) — full token/utility inventory
66
+ - [`references/component-patterns.md`](references/component-patterns.md) — `tv()` slots/variants/compoundVariants pattern + variant decision tree
67
+ - [`references/motion-guide.md`](references/motion-guide.md) — applying transitions (qualities of a good transition, choosing a pattern), which animation primitive and spring to reach for, reduced-motion target behavior
68
+ - [`references/accessibility-checklist.md`](references/accessibility-checklist.md) — pre-delivery checklist
@@ -0,0 +1,29 @@
1
+ # Pre-delivery checklist
2
+
3
+ Run through this before calling a component/UI change done.
4
+
5
+ ## Contrast
6
+
7
+ - Text/icon color on a colored surface should always be the matching `on-*` token (`on-primary` on `primary`, `on-primary-container` on `primary-container`, etc.), never a manually chosen color — the `on-*` tokens are generated to meet contrast requirements per theme (including the `-hc`/`-mc` high/medium-contrast variants). A manual override bypasses that guarantee.
8
+ - If a design calls for a color combination with no existing `on-*` pair, that's a sign it doesn't map to an M3 role cleanly — reconsider the role choice before improvising a color.
9
+
10
+ ## Touch targets
11
+
12
+ - Interactive elements should hit the M3 minimum 48dp target. The existing `size` scales already encode this (e.g. button `sm`/`md` = `h-spacing-500`/`h-spacing-700`); when adding a new interactive size below that, either pad the hit area (see `.text-link`'s `min-height: 44px` + padding trick) or confirm it's genuinely dense-UI/desktop-only and document why.
13
+
14
+ ## Focus
15
+
16
+ - Anything focusable needs a visible focus state — use `md-sys-state-focus-indicator`, don't rely on the browser default or remove `outline` without replacing it.
17
+ - Verify keyboard operability, not just click handlers — Bits UI primitives (dialogs, menus, selects) handle this by default; if you're building something interactive from scratch, check it against Bits UI's docs (https://bits-ui.com/llms.txt) for the expected keyboard model first.
18
+
19
+ ## Motion
20
+
21
+ - `prefers-reduced-motion`: M3 asks for subtle fades instead of intense slides or scales, and for decorative effects (parallax, shape morphing) to be turned off. Motion that carries meaning, such as confirming a drag-drop, can stay. Don't turn transitions into jump cuts. See `references/motion-guide.md` → "Reduced motion".
22
+ - Transitions: is the pattern right for how the two states relate, and is it the same pattern this kind of change uses elsewhere? Is there no jump cut, no layout shift or content popping in, one primary direction of movement, no slow cross-fade (sheets slide without fading) and no bouncy spring on a navigation transition? See `references/motion-guide.md` → "Applying transitions".
23
+ - Don't introduce a new hand-tuned duration/easing when an existing token fits — inconsistent timing across components is one of the more noticeable ways an M3 implementation starts to feel "off."
24
+
25
+ ## Consistency
26
+
27
+ - No hardcoded hex/rgb colors, px shadows, px border-radius, or ms durations in new component code — every one of those should trace back to a token or utility from `references/tokens-and-styles.md`.
28
+ - New multi-element components use `tv()` with `slots`, not string concatenation or `clsx` with inline conditionals.
29
+ - Icons via `Icon.svelte`, dividers via `Divider.svelte`, state layers via `Layer.svelte` — grep for a raw `material-symbols-` class, a hand-rolled `<hr>`/border-based divider, or manual hover/press opacity as signs one of these was skipped.
@@ -0,0 +1,55 @@
1
+ # Component patterns
2
+
3
+ ## The `tv()` shape
4
+
5
+ Every component's styles live in the category's `theme.ts`, built with `tv()` from `tailwind-variants`:
6
+
7
+ - `slots` — one key per rendered element (`base`, `icon`, `label`, ...) so multi-element components can vary each part independently
8
+ - `variants` — one axis per meaningful design decision (`variant`, `color`, `size`, `shape`, `selected`, ...); each value maps to either a class string (single-slot components) or an object keyed by slot (multi-slot components)
9
+ - `compoundVariants` — combinations that need a class not expressible as a union of independent axes (almost everything about color composition lives here, since `variant` × `color` isn't a clean cross-product of independent classes)
10
+
11
+ Worked example: `src/lib/components/buttons/theme.ts`. The `button` export has slots `base`/`icon`; variants `variant` (elevated/filled/tonal/outlined/text/bare), `color` (default/primary/secondary/tertiary/error), `usage` (selection/default), `size` (xs/sm/md/lg/xl), `shape` (round/square), `selected` (true/false); and ~25 `compoundVariants` entries, one per `variant`×`color` pair, each pointing at the pre-built `md-component-button-*` class from `component.css`. Match this structure exactly for new component families — don't hand-roll `clsx` strings or inline conditional classes.
12
+
13
+ When adding a `size` axis, follow the existing scale's _shape_, not just its numbers: each size variant sets height, gap, padding, typescale, and (for shape-driving components) the shape CSS vars together, in one slot-scoped string — not spread across multiple variant axes that a consumer could combine incorrectly.
14
+
15
+ ## Variant selection tree
16
+
17
+ Ask, in order:
18
+
19
+ 1. **Is this the single highest-priority action on the screen/section?** → `filled`, `color: primary` (or `error` if destructive).
20
+ 2. **Is it a secondary but still prominent action, or a persistent selected/toggled state?** → `tonal`.
21
+ 3. **Does it need a visible boundary but shouldn't compete visually with a filled sibling?** → `outlined`.
22
+ 4. **Is it a low-emphasis, often-repeated action** (list rows, inline links, dialog dismiss)? → `text`.
23
+ 5. **Does it need to visually float above a surface that's already tonal/colored** (a FAB over a colored app bar, a card menu trigger)? → `elevated`.
24
+
25
+ Only fall back to `bare` (no color/background at all) when the component supplies its own coloring downstream (e.g. an icon button whose color comes from a parent's `selected` state).
26
+
27
+ ## Reuse before you build
28
+
29
+ - **State layer / ripple** — wrap the interactive element with `Layer.svelte` (`src/lib/utils/Layer.svelte`) rather than writing hover/press opacity by hand. It listens for `.m3-layer` on its parent, already respects `prefers-reduced-motion` for the ripple, and its tint intensity (hover 0.08 / pressed·focus 0.12) matches the M3 state-layer spec — don't retune those numbers per component.
30
+ - **Icons** — always `Icon.svelte` (`name`, `fill`, `wght`, `size` props), never a raw `<span class="material-symbols-...">` or an inline SVG for a Material Symbol.
31
+ - **Dividers** — `Divider.svelte`, not Bits UI's `Separator` (per project CLAUDE.md — it already implements the same primitive).
32
+ - **Focus ring** — the `md-sys-state-focus-indicator` utility, not a custom `:focus` style.
33
+ - **Disabled state** — `.md-component-button-base`'s disabled handling (via `disabled:`/`aria-disabled`/`data-disabled` selectors in `component.css`), not a manually-toggled opacity class.
34
+
35
+ ## Laying out a showcase or docs page (`src/routes/**`)
36
+
37
+ Routes are not a blank canvas either — every page-level layout shape is built from the two
38
+ `containers/pane` primitives, `Pane` and `PaneGrid`. Reach for these instead of a hand-rolled
39
+ `flex`/`aside`/`sticky` div, and if a shape genuinely doesn't fit, that's a sign the pattern belongs
40
+ in the library, not in a one-off route file:
41
+
42
+ | Shape you need | Component | Notes |
43
+ | --------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
44
+ | Centered/full-width single column | `Pane` (standalone, no `PaneGrid`) | `centered` variant for max-width, `padding`/`gap` for spacing |
45
+ | User-resizable two-column split | `PaneGrid` + `Pane width={...} resizable` | The resizable `Pane` owns its own drag handle and width state; add `persistKey` for `localStorage` persistence |
46
+ | Static sidebar that stays visible while its sibling scrolls | `PaneGrid` + `Pane width={...} sticky` | Native `position: sticky` — works inside any scrolling ancestor (the true page scroll or a bounded box), so this one prop covers what used to be two anchor modes |
47
+ | Main content + fixed-width side panel (TOC, filters, details) | `PaneGrid direction={{ small: 'column', large: 'row' }}` + a `width`+`sticky` `Pane` | Stacks on small viewports, moves to a row from `large` up — the M3 [supporting-pane](https://m3.material.io/foundations/layout/canonical-layouts/supporting-pane) pattern |
48
+ | List-detail, or any pane that should only show at certain sizes | `Pane visibleFrom="medium"` / `hiddenFrom="medium"` | Toggles CSS display only, per Tailwind-aligned breakpoint (`small`/`medium`/`large`/`extraLarge` → unprefixed/`md:`/`lg:`/`xl:`) |
49
+ | Floating panel the user repositions (inspector, tool palette) | `DraggablePane` | Not a `PaneGrid` child — `position: fixed`, dragged by its header via `x`/`y` (bindable); `bounds`/`boundsPadding` clamp the drag, `persistKey` persists position |
50
+
51
+ See `/docs/pane` in the showcase site for the full prop reference and worked examples.
52
+
53
+ ## Before exporting a new component
54
+
55
+ Follow CLAUDE.md's "Adding a New Component" steps as the mechanical checklist (create `.svelte` + `types.ts`, define `theme.ts` with `tv()`, export from the category `index.ts`, run `bun scripts/generate-components-index.ts`, add a showcase route and a docs page) — this skill governs the _design_ decisions (which variant/color/motion) that should be made before or while writing that `theme.ts`, not the export mechanics themselves.
@@ -0,0 +1,163 @@
1
+ # Motion guide
2
+
3
+ Source: `src/lib/animation/` (barrel-exported via `src/lib/animation/index.ts`) + spring tokens and utilities in `src/lib/styles/motion.css`. Live reference: Storybook → **Motion/Transition patterns** (one looping demo per M3 pattern).
4
+
5
+ Two rules frame everything below:
6
+
7
+ - **M3 Expressive springs are the only motion scheme.** There are no Standard-scheme springs and no hand-picked `ms` durations. Every animation — JS or CSS — resolves to one of the six spring tokens in `springTokens` (`spring.ts`).
8
+ - **No Svelte transitions.** `in:`/`out:`/`transition:`/`animate:` are not used anywhere in the library. Mount/unmount and navigation motion goes through Motion (`motion` package, hybrid `animate()`/`animateView()`); state-driven motion (hover, press, selected, focus) stays as CSS transitions on the spring tokens.
9
+
10
+ ## Applying transitions
11
+
12
+ Source: [m3.material.io → Transitions → Applying transitions](https://m3.material.io/styles/motion/transitions/applying-transitions). Read this section before picking a primitive, and use it to review any transition. M3 notes that its transition pages still describe the legacy easing-and-duration system and will move to the physics (spring) system. This library already runs on springs, so read "duration" below as "spring token" (see the spring table).
13
+
14
+ ### What makes a good transition
15
+
16
+ | Quality | M3 rule | What it means here |
17
+ | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
18
+ | **Follows accessibility settings** | With reduced motion on, use subtle fades instead of intense slides or scales, and turn off decorative effects such as parallax and shape morphing. | This is not implemented yet (see [Reduced motion](#reduced-motion)). Don't add new parallax or morph effects that you couldn't easily switch off. |
19
+ | **Consistent** | Use the same transition type for the same kind of change everywhere, so the app feels like one cohesive family. | Always use one primitive for one relationship. Every hierarchy step uses `sharedAxis` on the same axis, and every navbar/rail destination uses `fadeThrough`. Never mix patterns for the same kind of navigation. |
20
+ | **Stable layouts** | Use skeleton loaders so the layout holds still during a transition. Content shouldn't pop in or move around. | Reserve the loaded content's footprint with `{@attach skeleton}` placeholders, then reveal the content with `presence(…, enterExit.fade)`. The content shouldn't be conditionally inserted in a way that shifts its siblings. |
21
+ | **No jarring jump cuts** | By default, avoid instant screen swaps because they disorient users. A jump cut is fine only when pure efficiency matters most, such as opening a menu in a productivity app. | Surfaces and navigation always animate, which is why the ownership table below is mandatory. A jump cut has to be an explicit, documented choice, never the result of leaving the animation out. |
22
+ | **Coherent spatial model** | Transitions should teach the app's physical layout. | A given relationship always moves along the same axis. For example, don't switch between horizontal and vertical when a collapsed view expands. If forward is `x`, backward is `x` reversed and never `y`. |
23
+ | **Unified direction** | Group elements so they move along one primary axis. Only important elements, such as hero images, stay persistent. Don't animate many persistent elements independently. | Transition one `target` region as a unit. Don't give several children their own `animateView` targets or view-transition names. The only element that gets its own identity is the hero, which becomes the `containerTransform` `from`/`to`. |
24
+ | **Clean fades** | Fully fade out the old content before fading the new content in. If a cross-fade can't be avoided, keep it short and hide it in the fastest part of the motion. Don't slowly fade a component over other content while it enters or exits. A centered dialog may fade, but only briefly. | The view-transition helpers already do this: `old` fades on `fastEffects`, `new` fades on `effects` after a delay. Opacity on enter/exit presets always uses `fastEffects`. **Edge-anchored surfaces (bottom and side sheets) slide without any opacity.** Keep it that way, and never put opacity on a slow spring. |
25
+ | **Simple style** | Transitions happen often, cover large parts of the screen and exist to help users finish a task. Common transitions shouldn't use overt style effects such as bouncy springs. | Navigation primitives default to `spatial` (damping 0.8). Don't pass `fastSpatial` (damping 0.6, the bounciest spring) as the `spring` for `sharedAxis`/`lateral`/`fadeThrough`/`containerTransform`. Keep noticeable overshoot for small parts and direct feedback. |
26
+
27
+ ### Choosing a transition pattern
28
+
29
+ | Pattern | Use for | Don't use for |
30
+ | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
31
+ | **Container transform** | Hero moments that should feel expressive, shallow hierarchies where an element expands for detail and then collapses back, and creating a seamless connection between two elements. It is the most dramatic pattern. | Apps with deep hierarchies, where the motion becomes excessive, and utility-focused navigation. It needs a custom implementation and feels heavy when used often. |
32
+ | **Forward and backward** | Common hierarchical navigation. M3 recommends the platform default. On the web that's `sharedAxis` (`x` for steps, `z` for parent → child), because its motion style is simple. | Hero moments, such as opening a photo memory. Use container transform for those. |
33
+ | **Lateral** | Browsing peer content in the same set, such as tabs in a media library or carousel pages. The horizontal slide suggests the area can be swiped. | Hierarchical screens: a full-width slide is too much for a frequent transition and falsely suggests a peer relationship. Top-level destinations: it suggests a swipe that conflicts with carousels and swipeable list items. **Never fade while sliding**, because it hides the peer relationship and looks like forward/backward. |
34
+ | **Top level** | Moving between destinations from a navigation bar, rail or drawer, which use a quick fade (`fadeThrough`). The destinations aren't necessarily related, so the motion deliberately doesn't connect them. | Anything that should read as related (use lateral or forward/backward). |
35
+ | **Enter and exit** | Bringing a component into the context of the current screen. It can be modal (a dialog that needs an action) or non-modal (a standard bottom sheet over a map, where both regions stay usable). | Navigating between hierarchical screens: a full-height slide is excessive and leaves the relationship between screens unclear. |
36
+ | **Skeleton loaders** | Holding the layout still while content loads (see "Stable layouts"). | Content that's already available. Animate it in instead of faking a load. |
37
+
38
+ Quick decision: is a component appearing on this screen? **Enter/exit.** Is the user changing screens? Ask how the screens relate. If one element expands into its own detail as a hero moment, use **container transform**. If the screens are parent/child or sequential steps, use **forward/backward**. If they're siblings in one set, use **lateral**. If they're unrelated navbar/rail/drawer destinations, use **top level**.
39
+
40
+ ## Springs: spatial vs effects
41
+
42
+ M3 principle: motion is **spatial** when something moves, resizes or reshapes; **effects** when only color/opacity change.
43
+
44
+ | Token (`springTokens.*`) | Stiffness / damping ratio | Use for |
45
+ | ------------------------ | ------------------------- | -------------------------------------------------------------------------- |
46
+ | `fastSpatial` | 800 / 0.6 (~410ms) | small parts & direct feedback: handles, indicators, chevrons, shape morphs |
47
+ | `spatial` | 380 / 0.8 (~490ms) | component containers: dialogs, sheets, rail, navigation content |
48
+ | `slowSpatial` | 200 / 0.8 (~660ms) | large / full-screen surfaces |
49
+ | `fastEffects` | 3800 / 1 (~190ms) | hover/press/focus color, state layers, exits |
50
+ | `effects` | 1600 / 1 (~270ms) | color/opacity changes on containers, fades |
51
+ | `slowEffects` | 800 / 1 (~370ms) | large-surface fades |
52
+
53
+ Spatial springs overshoot by design (fast spatial most, at damping ratio 0.6); effects springs are critically damped. The `ms` values are CSS settle times — JS springs have no fixed duration. **Never put opacity on a spatial spring** — split per value instead (see `enterExit.ts`).
54
+
55
+ ## JS: picking a primitive
56
+
57
+ | M3 pattern | Primitive | Notes |
58
+ | ----------------------------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
59
+ | **Enter and exit** — a surface appears/leaves within the screen | `presence(() => open, enterExit.<preset>)` | Attachment. Presets: `fade`, `scale` (menus/popovers/tooltips — sets `transform-origin` to bits-ui's anchor side, so never add an `origin-*` class), `slideUp` (snackbar), `dialog`, `sideSheet`, `bottomSheet`. Interruptible: reopening mid-exit retargets from the current value with velocity. |
60
+ | Same, outside bits-ui (element must stay mounted during its exit) | `new Presence(() => open)` | `{#if p.mounted}<div {@attach p.attach(enterExit.x)}>`. Construct during component init. Used by Snackbar, SideSheet, BottomSheet. |
61
+ | **Container transform** — card → detail, FAB → sheet | `containerTransform(update, { from, to })` | Motion `animateView()` (View Transition API). `update` swaps the DOM (`async () => { open = true; await tick(); }`); `to` may be a selector for an element that only exists after the update. |
62
+ | **Forward and backward** — hierarchy levels, wizard steps | `sharedAxis(update, { target, axis, direction })` | `axis: 'x' \| 'y' \| 'z'`, `direction: 'forward' \| 'backward'`. `target` is the persistent region whose content changes; omit for the whole page. |
63
+ | **Lateral** — peer screens (tabs, carousels) | `lateral(update, { target, direction })` | Edge-to-edge slide, no fade. |
64
+ | **Top level** — unrelated destinations (navigation bar) | `fadeThrough(update, { target })` | Old fades out, new fades in scaling from 92%. |
65
+ | **Skeleton loaders** | `{@attach skeleton}` on the placeholder | Pulses until replaced; reveal the content with `presence(…, enterExit.fade)`. |
66
+ | Anything custom | `animate(node, keyframes, springTransition(springTokens.x))` | `springTransition` converts a token to Motion's physics spring (`stiffness`/`damping`), which inherits velocity on interruption. |
67
+
68
+ ### Which components must use which pattern
69
+
70
+ A pattern belongs in a component only when that component owns both states of the change. When the content that changes lives outside the component (a route, an app screen), the app applies the pattern, not the library. Keep this table in sync when a component gains or loses one of these patterns; each primitive's JSDoc in `src/lib/animation/` repeats its own row.
71
+
72
+ | Pattern | Built into (must use it) | Left to the app |
73
+ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
74
+ | **Enter and exit** | Dialogue, BottomSheet, SideSheet, Menu, MenuSub, ContextMenu, Popover, Tooltip, LinkPreview, Select, FABMenu, SplitButton, Snackbar, DateField/DateRangeField/TimeField popups | — |
75
+ | **Lateral** | TabHolder (switching content panels); DateField/DateRangeField (changing the visible month, via `date/calendarMotion.ts`) | Navigation (`href`) tabs: the route change is the app's |
76
+ | **Container transform** | FAB → its `surface` (`FAB.svelte`: a clip-path and colour morph on Motion, not `containerTransform`) | Card → detail, Carousel item → detail. `Search` has no search view to expand into; add it here if one is built |
77
+ | **Forward and backward** | — | Hierarchy levels, wizard steps |
78
+ | **Top level** | — | Page changes from `Navbar`/`Rail`: they don't own the content region |
79
+ | **Skeleton loaders** | — | Placeholders for data the app loads |
80
+
81
+ A new component that mounts a surface must use enter/exit; one that pages between peer views (a stepper of equal steps) must use lateral. `Carousel` is not a pager: its motion is the scroll itself, items resizing through keylines. Opening a tapped item into its detail is a container transform the app applies.
82
+
83
+ ### bits-ui content
84
+
85
+ bits-ui's presence layer waits for the content element's `getAnimations()` before unmounting, and Motion runs `transform`/`opacity` on WAAPI. So for any bits-ui `*.Content`: **drop `forceMount`, drop the `{#if open}` wrapper**, and attach `presence` to the element that receives `props`:
86
+
87
+ ```svelte
88
+ <Popover.Content>
89
+ {#snippet child({ wrapperProps, props, open })}
90
+ <div {...wrapperProps}>
91
+ <div {...props} {@attach presence(() => open, enterExit.scale)}>…</div>
92
+ </div>
93
+ {/snippet}
94
+ </Popover.Content>
95
+ ```
96
+
97
+ Keep enter/exit keyframes to `transform` and `opacity` — a non-accelerated property falls back to a JS animation that `getAnimations()` can't see, and bits-ui will unmount before the exit plays.
98
+
99
+ ### View-transition patterns: limits
100
+
101
+ `containerTransform`, `sharedAxis`, `lateral`, `fadeThrough` use the View Transition API: one transition per document at a time (Motion queues the next one rather than interrupting), snapshot-based, and a no-op animation (DOM update still runs) in browsers without the API.
102
+
103
+ ## Shape morphing
104
+
105
+ The 35 M3 Expressive shapes (https://m3.material.io/styles/shape) ship in three sets in `src/lib/animation/`:
106
+
107
+ | Set | Points, viewBox | Morphable | Use for |
108
+ | ---------------------------------------------------------------------------- | ------------------------------- | ------------------------------------ | ----------------------------------------------------- |
109
+ | `shapes.ts` (`pathCircle`, …) | original Béziers, `0 0 380 380` | no, each path has its own structure | static shapes only |
110
+ | `shapesAnimatable.ts` (`pathAnimatable*`, `animatableShapes`) | 720, `0 0 380 380` | yes, with any other path in this set | hero-sized shapes |
111
+ | `shapesAnimatableSmall.ts` (`pathAnimatableSmall*`, `animatableShapesSmall`) | 120, `0 0 48 48` | yes, with any other path in this set | icons, avatars, indicators, FABs; about 1/8 the bytes |
112
+
113
+ Within a set, every path has the same point count, start point and winding, so Motion's `mix` interpolates any pair without flipping or twisting. Never morph across sets.
114
+
115
+ - **Morph with `shapeMorph(() => path)`** (attachment on a `<path>`) or `morphShape(pathEl, to)`. Both use `fastSpatial` by default, stop the previous morph and start from the shape on screen, and swap instantly under `prefers-reduced-motion`.
116
+ - **Pick by name** with `animatableShapes[name]` / `animatableShapesSmall[name]` (`ShapeName`, `shapeNames`). A map pulls in its whole set, so import individual `pathAnimatable*` constants when bundle size matters.
117
+ - `LoadingIndicator` morphs through seven of the small shapes (`LOADING_SHAPES`) on its own component spring.
118
+ - Live demo: Storybook → Motion/Shapes.
119
+
120
+ ## CSS: state-driven transitions
121
+
122
+ For hover/press/selected/focus motion driven by pseudo-classes or `data-*` state, use a Tailwind transition-property utility plus one spring utility from `motion.css`:
123
+
124
+ ```ts
125
+ 'transition-colors md-sys-motion-fast-effects'; // state color
126
+ 'transition-transform md-sys-motion-fast-spatial'; // indicator / chevron / handle
127
+ 'transition-[width,padding] md-sys-motion-spatial'; // container resize
128
+ ```
129
+
130
+ Utilities: `md-sys-motion-{fast-spatial,spatial,slow-spatial,fast-effects,effects,slow-effects}`. They set duration + timing function together (and Tailwind's `--tw-duration`/`--tw-ease`, so they win over `transition-*` regardless of order). Never write `duration-200`, `ease-in-out`, bare `transition-colors` (Tailwind's 150ms default), or `transition-all`.
131
+
132
+ In plain CSS (`component.css`, `<style>` blocks) use the variables directly: `transition: border-radius var(--md-sys-motion-duration-fast-spatial) var(--md-sys-motion-timing-function-fast-spatial);`. Button shape morphs use `fast-spatial` for both press and release — no separate accelerate/decelerate curves.
133
+
134
+ **Don't hand-edit the spring tokens.** They sit between `@generated:springs` markers in `motion.css`. Change `springTokens` in `spring.ts`; the lefthook pre-commit job (`motion-springs`) regenerates and stages `motion.css` whenever `spring.ts`, the generator or `motion.css` is committed, and `bun run build` regenerates before packaging. To run it by hand: `bun run generate:springs`.
135
+
136
+ The legacy easing-and-duration tokens (`--md-sys-motion-duration{,-fast,-slow}`, `-timing-function-emphasized*`) no longer exist.
137
+
138
+ ### Where the numbers come from
139
+
140
+ m3.material.io describes the patterns but doesn't list scales or offsets; its reference implementation, Material Components Android (`lib/java/com/google/android/material/transition/`), does. Use these, don't invent new ones:
141
+
142
+ | Value | Source |
143
+ | ----------------------------------------------------- | -------------------------------------------------------------------- |
144
+ | Enter from `scale(0.8)`, exit by fade only (`exited`) | `MaterialFade` / `ScaleProvider` |
145
+ | Shared axis X/Y: 30px slide | `mtrl_transition_shared_axis_slide_distance` |
146
+ | Shared axis Z: in 0.8 → 1, out 1 → 1.1 | `ScaleProvider` |
147
+ | Fade through: in from `scale(0.92)` | `MaterialFadeThrough` |
148
+ | Snackbar: slide by own height | `BaseTransientBottomBar` (slide mode) |
149
+ | Spring tokens | `motion/res/values/tokens.xml` (`m3_sys_motion_expressive_spring_*`) |
150
+
151
+ MDC's own durations/easings (`short1`…`extraLong4` × `emphasized`/`standard`) are replaced by the spring tokens here. Still invented and flagged in code: the skeleton pulse hold (`skeleton.ts`), the 0.05s/0.09s fade-in delays in the view-transition patterns.
152
+
153
+ `Layer.svelte`'s ripple also runs on Motion (scale + fade on `slowEffects`), so there's no fixed-curve animation left in the library.
154
+
155
+ ## Reduced motion
156
+
157
+ Not implemented yet — tracked in https://github.com/noxlovette/material/issues/24. `Layer.svelte`'s ripple is the only thing that currently checks `prefers-reduced-motion`.
158
+
159
+ Target behavior, per [Applying transitions](https://m3.material.io/styles/motion/transitions/applying-transitions). When `prefers-reduced-motion: reduce` is set:
160
+
161
+ - **Swap movement for subtle fades rather than removing the transition** (a jump cut would break the "no jarring jump cuts" rule). `enterExit.scale`/`dialog`/`slideUp`/`sideSheet`/`bottomSheet` fall back to `enterExit.fade`. `sharedAxis`, `lateral` and `containerTransform` fall back to a `fadeThrough`-style opacity-only fade, with no translate or scale.
162
+ - **Disable decorative effects**: shape morphing (button `--btn-shape` morphs; `morphShape`/`shapeMorph` already swap instantly), parallax, the ripple (already done).
163
+ - Keep effects-spring color/opacity feedback (state layers, hover/press color), since that motion carries meaning and isn't intense.
@@ -0,0 +1,68 @@
1
+ # Tokens and styles inventory
2
+
3
+ Source of truth: `src/lib/styles/*.css`. Rule: **never hardcode a color, shadow, radius, or duration value in a component — every one of these already has a token or utility class.** If you think you need a new value, check these files first; you're almost certainly missing an existing one.
4
+
5
+ ## Color roles (`src/lib/styles/theme/*.css`)
6
+
7
+ Defined per theme variant (`light.css`, `dark.css`, `light-hc.css`, `dark-hc.css`, `light-mc.css`, `dark-mc.css` — high-contrast and medium-contrast accessibility variants) as `--md-sys-color-*` custom properties, consumed via Tailwind utilities of the form `bg-md-sys-color-*` / `text-md-sys-color-*` / `outline-md-sys-color-*`.
8
+
9
+ - Core roles: `primary`, `secondary`, `tertiary`, `error`
10
+ - Each has a low-emphasis tonal pair: `{role}-container`
11
+ - Each (including containers) has a guaranteed-contrast text/icon pair: `on-{role}`, `on-{role}-container`
12
+ - Surface roles: `surface`, `surface-variant`, `surface-container` (`-lowest`, `-low`, `-high`, `-highest`), `on-surface`, `on-surface-variant`
13
+ - Structural: `outline`, `outline-variant`, `shadow`
14
+
15
+ Because `on-*` pairs are generated to meet contrast requirements, **any manual color override that doesn't go through an `on-*`/role pair is a contrast bug waiting to happen** — flag it in review.
16
+
17
+ ## Component color composition (`src/lib/styles/component.css`)
18
+
19
+ Components don't reference `md-sys-color-*` directly for every state — they compose pre-built classes:
20
+
21
+ ```
22
+ md-component-button-filled-{default,primary,secondary,tertiary,error}
23
+ md-component-button-tonal-{default,primary,secondary,tertiary,error}
24
+ md-component-button-outline-{default,primary,secondary,tertiary,error}
25
+ md-component-button-text-{default,primary,secondary,tertiary,error}
26
+ md-component-button-elevated-{default,primary,secondary,tertiary,error}
27
+ ```
28
+
29
+ `filled` = `bg-md-sys-color-{role} text-md-sys-color-on-{role}`. `tonal` = the `-container`/`on-{role}-container` pair. `outlined` = `text-md-sys-color-{role} outline-md-sys-color-{role}` (default outline uses `on-surface-variant`/`outline-variant`). `text` = text color only, transparent background. `elevated` = `bg-md-sys-color-surface-container-low` + role text color + `shadow-elevation-1`. When adding a new component family with the same emphasis levels, follow this exact composition rather than reinventing it.
30
+
31
+ Other reusable utilities in this file:
32
+
33
+ - `.state-layer` — the `::before` overlay hook that `Layer.svelte` and hover/press states build on
34
+ - `md-sys-state-focus-indicator` — the `:focus-visible` outline (3px solid, `on-secondary` color, 2px offset) — apply to anything focusable that doesn't already get it via a shared base class
35
+ - `.md-component-button-base` — disabled-state handling (`on-surface` at reduced opacity) + cursor — reuse for any new interactive component base
36
+
37
+ ## Typescale (`src/lib/styles/typescale.css`)
38
+
39
+ The full M3 type scale (https://m3.material.io/styles/typography/type-scale-tokens), in rem:
40
+
41
+ - `md-sys-typescale-{display,headline,title,body,label}-{large,medium,small}`: the 15 baseline styles. Each sets font family, weight, size, line height, and tracking. Don't set `text-*`/`leading-*`/`tracking-*`/`font-*` next to one; pick a different style instead.
42
+ - `md-sys-typescale-emphasized-<style>`: the 15 emphasized styles. They have the same metrics and a heavier weight (400 → 500, 500 → 700). Components use baseline by default. Swap in the emphasized style for selected or active states, unread items, primary-action buttons, badges and the extended FAB. Don't add `font-bold` to a baseline style.
43
+ - `md-sys-typescale-label-{large,medium}-prominent`: the `weight.prominent` (700) label tokens.
44
+ - Typefaces: display, headline and title-large use `--md-ref-typeface-brand`; everything else uses `--md-ref-typeface-plain`. Both default to `--font-sans`; M3's own default is Roboto.
45
+ - Line heights follow the language-height category of the element's `lang`: small (Latin, Cyrillic, Greek, Hebrew), medium (CJK, Arabic, Indic, Thai, Vietnamese…), large (Burmese, Telugu), extra-large (Urdu/Nastaliq). Force a category with `data-md-language-height="small|medium|large|extra-large"`.
46
+
47
+ ## Spacing (`src/lib/styles/spacing.css`)
48
+
49
+ `md.sys.measurement.space<N>` (N/100 × the 8dp base) is registered in Tailwind's spacing namespace. Every spacing utility therefore takes it: `p-spacing-200` (16dp), `gap-spacing-50` (4dp), `mr-spacing-300` (24dp), `size-spacing-600` (48dp). The available numbers are 0, 25, 50, 75, 100, 125, 150, 175, 200, 250, 300, 400, 450, 500, 600, 700, 800 and 900. The number is M3's token number, not Tailwind's 4px multiplier.
50
+
51
+ ## Elevation (`src/lib/styles/elevation.css`)
52
+
53
+ `shadow-elevation-{0..5}`, each a two-layer (spot + ambient) shadow scaled to the M3 elevation spec, using `color-mix` against the `shadow` color role so it adapts per theme automatically. Elevation communicates depth/priority — reach for a higher level when a surface should read as "above" its neighbors (menus, FABs, dialogs), not as a decorative effect.
54
+
55
+ ## Shape (`src/lib/styles/rounding.css`)
56
+
57
+ `radius-{none,xs,sm,md,lg,xl,full}` = `0 / 4px / 8px / 12px / 16px / 28px / 9999px`, matching the M3 shape scale (extra-small through extra-large, plus full/pill). Buttons don't use these directly — they use CSS custom properties (`--btn-shape`, `--btn-shape-override`, `--btn-pressed-shape` set per `size`/`shape` variant in `theme.ts`) consumed by the `.md-btn-morph` utility, which also animates the radius on press (a deliberate M3 "morph" detail — don't remove it when adding button variants).
58
+
59
+ ## Motion (`src/lib/styles/motion.css`)
60
+
61
+ M3 Expressive springs only, generated from `springTokens` in `src/lib/animation/spring.ts` (never hand-edited — a lefthook pre-commit job regenerates them). Each spring exposes `--md-sys-motion-timing-function-<name>` (a `linear()` curve), `--md-sys-motion-duration-<name>` (its settle time) and `--md-sys-motion-easing-<name>` (both combined):
62
+
63
+ - **Spatial** (things that move, resize, or morph — overshoot): `fast-spatial` (410ms), `spatial` (490ms), `slow-spatial` (660ms)
64
+ - **Effects** (color/opacity — no overshoot): `fast-effects-spring` (190ms), `effects-spring` (270ms), `slow-effects-spring` (370ms)
65
+
66
+ In Tailwind classes use the utilities instead: `md-sys-motion-{fast-spatial,spatial,slow-spatial,fast-effects,effects,slow-effects}` alongside a `transition-*` property utility. There are no standard/emphasized easing tokens.
67
+
68
+ Full decision guidance for motion lives in `references/motion-guide.md` — don't just guess a duration.
@@ -26,26 +26,30 @@
26
26
  </script>
27
27
 
28
28
  <Story name="Shape library" asChild>
29
- <div class="flex flex-col gap-6">
30
- <div class="flex items-center gap-6">
29
+ <div class="gap-spacing-300 flex flex-col">
30
+ <div class="gap-spacing-300 flex items-center">
31
31
  <svg viewBox="0 0 380 380" class="text-md-sys-color-primary size-48" aria-hidden="true">
32
32
  <path fill="currentColor" {@attach shapeMorph(() => animatableShapes[selected])} />
33
33
  </svg>
34
- <svg viewBox="0 0 48 48" class="text-md-sys-color-tertiary size-12" aria-hidden="true">
34
+ <svg
35
+ viewBox="0 0 48 48"
36
+ class="text-md-sys-color-tertiary size-spacing-600"
37
+ aria-hidden="true"
38
+ >
35
39
  <path fill="currentColor" {@attach shapeMorph(() => animatableShapesSmall[selected])} />
36
40
  </svg>
37
41
  <Body>{selected}</Body>
38
42
  </div>
39
43
 
40
- <div class="grid grid-cols-5 gap-2 sm:grid-cols-7">
44
+ <div class="gap-spacing-100 grid grid-cols-5 sm:grid-cols-7">
41
45
  {#each shapeNames as name (name)}
42
46
  <button
43
47
  type="button"
44
48
  aria-pressed={selected === name}
45
49
  onclick={() => (selected = name)}
46
- class="md-sys-typescale-label-small text-md-sys-color-on-surface-variant aria-pressed:bg-md-sys-color-secondary-container aria-pressed:text-md-sys-color-on-secondary-container flex flex-col items-center gap-1 rounded-md p-2"
50
+ class="md-sys-typescale-label-small text-md-sys-color-on-surface-variant aria-pressed:bg-md-sys-color-secondary-container aria-pressed:text-md-sys-color-on-secondary-container gap-spacing-50 p-spacing-100 flex flex-col items-center rounded-md"
47
51
  >
48
- <svg viewBox="0 0 48 48" class="size-10" aria-hidden="true">
52
+ <svg viewBox="0 0 48 48" class="size-spacing-500" aria-hidden="true">
49
53
  <path fill="currentColor" d={animatableShapesSmall[name]} />
50
54
  </svg>
51
55
  {name}