@noxlovette/material 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/bin/material-claude-skill.js +31 -0
  2. package/claude-skill/material-design/SKILL.md +68 -0
  3. package/claude-skill/material-design/references/accessibility-checklist.md +29 -0
  4. package/claude-skill/material-design/references/component-patterns.md +55 -0
  5. package/claude-skill/material-design/references/motion-guide.md +163 -0
  6. package/claude-skill/material-design/references/tokens-and-styles.md +68 -0
  7. package/dist/animation/Shapes.stories.svelte +60 -0
  8. package/dist/animation/Shapes.stories.svelte.d.ts +3 -0
  9. package/dist/animation/TransitionPatterns.stories.svelte +32 -20
  10. package/dist/animation/containerTransform.d.ts +11 -0
  11. package/dist/animation/containerTransform.js +11 -0
  12. package/dist/animation/enterExit.d.ts +9 -0
  13. package/dist/animation/enterExit.js +6 -3
  14. package/dist/animation/index.d.ts +1 -0
  15. package/dist/animation/index.js +1 -0
  16. package/dist/animation/shapeMorph.svelte.d.ts +52 -0
  17. package/dist/animation/shapeMorph.svelte.js +112 -0
  18. package/dist/animation/shapesAnimatableSmall.d.ts +28 -22
  19. package/dist/animation/shapesAnimatableSmall.js +32 -23
  20. package/dist/animation/sharedAxisTransition.d.ts +22 -0
  21. package/dist/animation/sharedAxisTransition.js +25 -1
  22. package/dist/animation/skeleton.d.ts +6 -1
  23. package/dist/animation/skeleton.js +6 -1
  24. package/dist/components/badge/Badge.mdx +70 -0
  25. package/dist/components/badge/Badge.stories.svelte +38 -20
  26. package/dist/components/badge/Badge.svelte +12 -5
  27. package/dist/components/badge/theme.d.ts +30 -7
  28. package/dist/components/badge/theme.js +24 -4
  29. package/dist/components/badge/types.d.ts +18 -4
  30. package/dist/components/buttons/Button.mdx +85 -0
  31. package/dist/components/buttons/Button.stories.svelte +62 -27
  32. package/dist/components/buttons/Button.svelte +29 -25
  33. package/dist/components/buttons/Button.svelte.d.ts +11 -9
  34. package/dist/components/buttons/ButtonIcon.mdx +52 -0
  35. package/dist/components/buttons/ButtonIcon.stories.svelte +72 -48
  36. package/dist/components/buttons/ButtonIcon.svelte +58 -36
  37. package/dist/components/buttons/ButtonIcon.svelte.d.ts +9 -7
  38. package/dist/components/buttons/FAB.mdx +84 -0
  39. package/dist/components/buttons/FAB.stories.svelte +103 -24
  40. package/dist/components/buttons/FAB.stories.svelte.d.ts +2 -17
  41. package/dist/components/buttons/FAB.svelte +353 -29
  42. package/dist/components/buttons/FAB.svelte.d.ts +11 -6
  43. package/dist/components/buttons/FABMenu.svelte +27 -8
  44. package/dist/components/buttons/FABMenu.svelte.d.ts +7 -3
  45. package/dist/components/buttons/FABMenuItem.svelte +20 -16
  46. package/dist/components/buttons/FABMenuItem.svelte.d.ts +3 -3
  47. package/dist/components/buttons/Toggle.stories.svelte +31 -23
  48. package/dist/components/buttons/Toggle.stories.svelte.d.ts +2 -17
  49. package/dist/components/buttons/Toggle.svelte +28 -25
  50. package/dist/components/buttons/Toggle.svelte.d.ts +5 -5
  51. package/dist/components/buttons/button-group/ButtonGroup.mdx +39 -0
  52. package/dist/components/buttons/button-group/ButtonGroup.stories.svelte +62 -43
  53. package/dist/components/buttons/button-group/ButtonGroup.stories.svelte.d.ts +2 -17
  54. package/dist/components/buttons/button-group/ButtonGroup.svelte +102 -8
  55. package/dist/components/buttons/button-group/ButtonGroup.svelte.d.ts +6 -7
  56. package/dist/components/buttons/button-group/theme.d.ts +45 -15
  57. package/dist/components/buttons/button-group/theme.js +13 -8
  58. package/dist/components/buttons/button-group/types.d.ts +13 -7
  59. package/dist/components/buttons/connected/ConnectedButtonGroup.mdx +43 -0
  60. package/dist/components/buttons/connected/ConnectedButtonGroup.stories.svelte +56 -74
  61. package/dist/components/buttons/connected/ConnectedButtonGroup.stories.svelte.d.ts +2 -17
  62. package/dist/components/buttons/connected/ConnectedButtonGroup.svelte +16 -4
  63. package/dist/components/buttons/connected/ConnectedButtonGroup.svelte.d.ts +4 -4
  64. package/dist/components/buttons/connected/ConnectedButtonGroupItem.svelte +35 -8
  65. package/dist/components/buttons/connected/ConnectedButtonGroupItem.svelte.d.ts +2 -3
  66. package/dist/components/buttons/connected/theme.d.ts +33 -18
  67. package/dist/components/buttons/connected/theme.js +32 -9
  68. package/dist/components/buttons/connected/types.d.ts +36 -9
  69. package/dist/components/buttons/shapeMorph.d.ts +26 -0
  70. package/dist/components/buttons/shapeMorph.js +142 -0
  71. package/dist/components/buttons/split-button/SplitButton.mdx +48 -0
  72. package/dist/components/buttons/split-button/SplitButton.stories.svelte +9 -14
  73. package/dist/components/buttons/split-button/SplitButton.svelte +65 -48
  74. package/dist/components/buttons/split-button/SplitButton.svelte.d.ts +6 -11
  75. package/dist/components/buttons/split-button/theme.d.ts +110 -17
  76. package/dist/components/buttons/split-button/theme.js +74 -16
  77. package/dist/components/buttons/split-button/types.d.ts +24 -18
  78. package/dist/components/buttons/theme.d.ts +422 -230
  79. package/dist/components/buttons/theme.js +286 -380
  80. package/dist/components/buttons/types.d.ts +130 -125
  81. package/dist/components/cards/Card.mdx +44 -0
  82. package/dist/components/cards/Card.stories.svelte +11 -11
  83. package/dist/components/cards/theme.js +5 -5
  84. package/dist/components/chips/Chip.mdx +68 -0
  85. package/dist/components/chips/Chip.stories.svelte +4 -4
  86. package/dist/components/chips/theme.js +3 -3
  87. package/dist/components/containers/action-rail/ActionRail.stories.svelte +1 -0
  88. package/dist/components/containers/action-rail/theme.js +1 -1
  89. package/dist/components/containers/bottom-sheet/BottomSheet.mdx +64 -0
  90. package/dist/components/containers/bottom-sheet/BottomSheet.stories.svelte +108 -12
  91. package/dist/components/containers/bottom-sheet/BottomSheet.svelte +179 -109
  92. package/dist/components/containers/bottom-sheet/BottomSheet.svelte.d.ts +10 -5
  93. package/dist/components/containers/bottom-sheet/index.d.ts +1 -0
  94. package/dist/components/containers/bottom-sheet/index.js +1 -0
  95. package/dist/components/containers/bottom-sheet/theme.d.ts +45 -0
  96. package/dist/components/containers/bottom-sheet/theme.js +42 -0
  97. package/dist/components/containers/bottom-sheet/types.d.ts +37 -5
  98. package/dist/components/containers/carousel/Carousel.mdx +80 -0
  99. package/dist/components/containers/carousel/Carousel.stories.svelte +131 -0
  100. package/dist/components/containers/carousel/Carousel.stories.svelte.d.ts +4 -0
  101. package/dist/components/containers/carousel/Carousel.svelte +194 -0
  102. package/dist/components/containers/carousel/Carousel.svelte.d.ts +25 -0
  103. package/dist/components/containers/carousel/index.d.ts +3 -0
  104. package/dist/components/containers/{stack → carousel}/index.js +1 -2
  105. package/dist/components/containers/carousel/keylines.d.ts +32 -0
  106. package/dist/components/containers/carousel/keylines.js +88 -0
  107. package/dist/components/containers/carousel/theme.d.ts +162 -0
  108. package/dist/components/containers/carousel/theme.js +67 -0
  109. package/dist/components/containers/carousel/types.d.ts +52 -0
  110. package/dist/components/containers/context-menu/ContextMenu.stories.svelte +1 -0
  111. package/dist/components/containers/context-menu/ContextMenu.svelte +2 -2
  112. package/dist/components/containers/context-menu/ContextMenu.svelte.d.ts +1 -1
  113. package/dist/components/containers/context-menu/theme.js +10 -10
  114. package/dist/components/containers/dialogue/Dialogue.mdx +65 -0
  115. package/dist/components/containers/dialogue/Dialogue.stories.svelte +4 -4
  116. package/dist/components/containers/dialogue/Dialogue.svelte +1 -1
  117. package/dist/components/containers/dialogue/theme.js +3 -3
  118. package/dist/components/containers/divider/Divider.stories.svelte +2 -1
  119. package/dist/components/containers/divider/Divider.svelte +2 -2
  120. package/dist/components/containers/divider/theme.d.ts +6 -6
  121. package/dist/components/containers/divider/theme.js +2 -2
  122. package/dist/components/containers/index.d.ts +1 -1
  123. package/dist/components/containers/index.js +1 -1
  124. package/dist/components/containers/link-preview/theme.js +2 -2
  125. package/dist/components/containers/list/List.svelte +18 -0
  126. package/dist/components/containers/list/List.svelte.d.ts +4 -0
  127. package/dist/components/containers/list/ListItem.mdx +100 -0
  128. package/dist/components/containers/list/ListItem.stories.svelte +177 -42
  129. package/dist/components/containers/list/ListItem.stories.svelte.d.ts +2 -17
  130. package/dist/components/containers/list/ListItem.svelte +92 -37
  131. package/dist/components/containers/list/ListItem.svelte.d.ts +1 -1
  132. package/dist/components/containers/list/context.d.ts +6 -0
  133. package/dist/components/containers/list/context.js +4 -0
  134. package/dist/components/containers/list/index.d.ts +1 -0
  135. package/dist/components/containers/list/index.js +1 -0
  136. package/dist/components/containers/list/theme.d.ts +487 -12
  137. package/dist/components/containers/list/theme.js +140 -13
  138. package/dist/components/containers/list/types.d.ts +39 -5
  139. package/dist/components/containers/menu/Menu.mdx +52 -0
  140. package/dist/components/containers/menu/Menu.stories.svelte +14 -14
  141. package/dist/components/containers/menu/Menu.svelte +1 -1
  142. package/dist/components/containers/menu/MenuGroup.svelte +1 -1
  143. package/dist/components/containers/menu/MenuGroup.svelte.d.ts +1 -1
  144. package/dist/components/containers/menu/menu-item/MenuItem.svelte +1 -1
  145. package/dist/components/containers/menu/theme.js +8 -8
  146. package/dist/components/containers/pane/DraggablePane.stories.svelte +4 -3
  147. package/dist/components/containers/pane/DraggablePane.svelte +2 -2
  148. package/dist/components/containers/pane/Pane.stories.svelte +11 -10
  149. package/dist/components/containers/pane/Pane.svelte +24 -2
  150. package/dist/components/containers/pane/PaneGrid.stories.svelte +19 -18
  151. package/dist/components/containers/pane/PaneGrid.svelte +3 -2
  152. package/dist/components/containers/pane/PaneGrid.svelte.d.ts +2 -1
  153. package/dist/components/containers/pane/PaneHandle.svelte +48 -2
  154. package/dist/components/containers/pane/PaneHandle.svelte.d.ts +4 -0
  155. package/dist/components/containers/pane/resizeStore.svelte.d.ts +10 -1
  156. package/dist/components/containers/pane/resizeStore.svelte.js +13 -2
  157. package/dist/components/containers/pane/theme.js +129 -45
  158. package/dist/components/containers/pane/types.d.ts +3 -2
  159. package/dist/components/containers/popover/Popover.mdx +45 -0
  160. package/dist/components/containers/popover/Popover.stories.svelte +1 -1
  161. package/dist/components/containers/popover/Popover.svelte +1 -1
  162. package/dist/components/containers/popover/theme.js +3 -3
  163. package/dist/components/containers/scroll-area/theme.js +2 -2
  164. package/dist/components/containers/side-sheet/SideSheet.mdx +71 -0
  165. package/dist/components/containers/side-sheet/SideSheet.stories.svelte +113 -17
  166. package/dist/components/containers/side-sheet/SideSheet.svelte +113 -66
  167. package/dist/components/containers/side-sheet/SideSheet.svelte.d.ts +8 -7
  168. package/dist/components/containers/side-sheet/index.d.ts +1 -0
  169. package/dist/components/containers/side-sheet/index.js +1 -0
  170. package/dist/components/containers/side-sheet/theme.d.ts +108 -0
  171. package/dist/components/containers/side-sheet/theme.js +72 -0
  172. package/dist/components/containers/side-sheet/types.d.ts +35 -7
  173. package/dist/components/date/DateField.stories.svelte +4 -3
  174. package/dist/components/date/DateField.svelte +49 -26
  175. package/dist/components/date/DateField.svelte.d.ts +2 -0
  176. package/dist/components/date/DateRangeField.stories.svelte +4 -3
  177. package/dist/components/date/DateRangeField.svelte +54 -31
  178. package/dist/components/date/DateRangeField.svelte.d.ts +2 -0
  179. package/dist/components/date/calendarMotion.d.ts +9 -0
  180. package/dist/components/date/calendarMotion.js +23 -0
  181. package/dist/components/date/theme.d.ts +7 -7
  182. package/dist/components/date/theme.js +34 -30
  183. package/dist/components/forms/checkbox/Checkbox.mdx +35 -0
  184. package/dist/components/forms/checkbox/Checkbox.stories.svelte +2 -2
  185. package/dist/components/forms/checkbox/Checkbox.svelte +1 -1
  186. package/dist/components/forms/checkbox/theme.js +3 -3
  187. package/dist/components/forms/command/Command.stories.svelte +1 -0
  188. package/dist/components/forms/command/theme.js +9 -9
  189. package/dist/components/forms/pin/theme.js +4 -4
  190. package/dist/components/forms/radio-group/RadioGroup.svelte +1 -1
  191. package/dist/components/forms/radio-group/theme.js +5 -5
  192. package/dist/components/forms/search/Search.mdx +38 -0
  193. package/dist/components/forms/search/Search.stories.svelte +19 -9
  194. package/dist/components/forms/search/Search.svelte +40 -17
  195. package/dist/components/forms/search/Search.svelte.d.ts +2 -2
  196. package/dist/components/forms/search/theme.d.ts +45 -12
  197. package/dist/components/forms/search/theme.js +13 -8
  198. package/dist/components/forms/search/types.d.ts +18 -12
  199. package/dist/components/forms/select/Select.mdx +53 -0
  200. package/dist/components/forms/select/Select.stories.svelte +4 -4
  201. package/dist/components/forms/select/Select.svelte +10 -2
  202. package/dist/components/forms/select/SelectItem.svelte +1 -1
  203. package/dist/components/forms/select/theme.js +12 -12
  204. package/dist/components/forms/slider/Slider.mdx +85 -0
  205. package/dist/components/forms/slider/Slider.stories.svelte +7 -7
  206. package/dist/components/forms/slider/Slider.svelte +46 -28
  207. package/dist/components/forms/slider/theme.d.ts +14 -0
  208. package/dist/components/forms/slider/theme.js +28 -17
  209. package/dist/components/forms/slider/types.d.ts +3 -2
  210. package/dist/components/forms/switch/Switch.mdx +38 -0
  211. package/dist/components/forms/switch/Switch.stories.svelte +3 -3
  212. package/dist/components/forms/switch/theme.js +6 -6
  213. package/dist/components/forms/textfield/Textfield.mdx +53 -0
  214. package/dist/components/forms/textfield/Textfield.stories.svelte +6 -6
  215. package/dist/components/forms/textfield/Textfield.svelte +1 -0
  216. package/dist/components/forms/textfield/theme.d.ts +3 -0
  217. package/dist/components/forms/textfield/theme.js +22 -19
  218. package/dist/components/forms/toggle-group/theme.js +2 -2
  219. package/dist/components/forms/tooltip/Tooltip.stories.svelte +1 -0
  220. package/dist/components/forms/tooltip/theme.js +3 -3
  221. package/dist/components/misc/Avatar.stories.svelte +5 -4
  222. package/dist/components/misc/ThemeSettings.svelte +9 -9
  223. package/dist/components/misc/ThemeSwitcher.svelte +3 -3
  224. package/dist/components/misc/theme.js +4 -4
  225. package/dist/components/nav/appbar/AppBar.mdx +71 -0
  226. package/dist/components/nav/appbar/AppBar.stories.svelte +93 -3
  227. package/dist/components/nav/appbar/AppBar.stories.svelte.d.ts +2 -17
  228. package/dist/components/nav/appbar/AppBar.svelte +90 -37
  229. package/dist/components/nav/appbar/AppBar.svelte.d.ts +9 -2
  230. package/dist/components/nav/appbar/theme.d.ts +85 -18
  231. package/dist/components/nav/appbar/theme.js +159 -23
  232. package/dist/components/nav/appbar/types.d.ts +46 -9
  233. package/dist/components/nav/breadcrumb/Breadcrumb.stories.svelte +1 -0
  234. package/dist/components/nav/breadcrumb/theme.js +5 -5
  235. package/dist/components/nav/navbar/Navbar.mdx +38 -0
  236. package/dist/components/nav/navbar/Navbar.svelte +1 -1
  237. package/dist/components/nav/navbar/NavbarItem.svelte +12 -9
  238. package/dist/components/nav/navbar/theme.js +6 -6
  239. package/dist/components/nav/rail/Rail.mdx +73 -0
  240. package/dist/components/nav/rail/Rail.stories.svelte +3 -7
  241. package/dist/components/nav/rail/Rail.svelte +1 -0
  242. package/dist/components/nav/rail/RailItem.svelte +12 -9
  243. package/dist/components/nav/rail/theme.js +13 -13
  244. package/dist/components/nav/rail/types.d.ts +4 -1
  245. package/dist/components/nav/tabs/TabHolder.svelte +29 -3
  246. package/dist/components/nav/tabs/TabHolder.svelte.d.ts +3 -1
  247. package/dist/components/nav/tabs/Tabs.mdx +62 -0
  248. package/dist/components/nav/tabs/Tabs.stories.svelte +1 -1
  249. package/dist/components/nav/tabs/theme.js +4 -4
  250. package/dist/components/pill/Pill.mdx +32 -0
  251. package/dist/components/pill/Pill.stories.svelte +1 -1
  252. package/dist/components/pill/theme.d.ts +1 -1
  253. package/dist/components/pill/theme.js +1 -1
  254. package/dist/components/progress/LinearProgress.svelte +1 -1
  255. package/dist/components/progress/Progress.mdx +50 -0
  256. package/dist/components/progress/Progress.stories.svelte +6 -6
  257. package/dist/components/snackbar/Snackbar.mdx +52 -0
  258. package/dist/components/snackbar/Snackbar.stories.svelte +5 -5
  259. package/dist/components/snackbar/Snackbar.svelte +1 -1
  260. package/dist/components/snackbar/theme.d.ts +3 -3
  261. package/dist/components/snackbar/theme.js +4 -4
  262. package/dist/components/table/Table.stories.svelte +1 -0
  263. package/dist/components/table/TableHeader.svelte +1 -1
  264. package/dist/components/table/theme.js +3 -3
  265. package/dist/components/time/TimeField.stories.svelte +3 -2
  266. package/dist/components/time/TimeField.svelte +14 -8
  267. package/dist/components/time/TimeField.svelte.d.ts +6 -0
  268. package/dist/components/time/TimepickerInput.svelte +9 -5
  269. package/dist/components/time/theme.js +5 -5
  270. package/dist/components/time/types.d.ts +11 -1
  271. package/dist/components/toolbar/Toolbar.stories.svelte +1 -0
  272. package/dist/components/toolbar/Toolbar.svelte +1 -1
  273. package/dist/components/toolbar/Toolbar.svelte.d.ts +1 -1
  274. package/dist/components/toolbar/theme.js +14 -14
  275. package/dist/components/typography/Typography.mdx +67 -0
  276. package/dist/components/typography/body/Body.stories.svelte +2 -1
  277. package/dist/components/typography/body/theme.d.ts +15 -12
  278. package/dist/components/typography/body/theme.js +19 -4
  279. package/dist/components/typography/body/types.d.ts +2 -1
  280. package/dist/components/typography/display/Display.stories.svelte +2 -1
  281. package/dist/components/typography/display/theme.d.ts +15 -12
  282. package/dist/components/typography/display/theme.js +19 -4
  283. package/dist/components/typography/display/types.d.ts +2 -1
  284. package/dist/components/typography/headline/Headline.stories.svelte +2 -1
  285. package/dist/components/typography/headline/theme.d.ts +15 -12
  286. package/dist/components/typography/headline/theme.js +19 -4
  287. package/dist/components/typography/headline/types.d.ts +2 -1
  288. package/dist/components/typography/kbd/Kbd.stories.svelte +2 -1
  289. package/dist/components/typography/kbd/theme.d.ts +4 -4
  290. package/dist/components/typography/kbd/theme.js +2 -2
  291. package/dist/components/typography/label/Label.stories.svelte +2 -1
  292. package/dist/components/typography/label/theme.d.ts +15 -12
  293. package/dist/components/typography/label/theme.js +19 -4
  294. package/dist/components/typography/label/types.d.ts +2 -1
  295. package/dist/components/typography/title/Title.stories.svelte +2 -1
  296. package/dist/components/typography/title/theme.d.ts +15 -12
  297. package/dist/components/typography/title/theme.js +19 -4
  298. package/dist/components/typography/title/types.d.ts +2 -1
  299. package/dist/index.css +1 -0
  300. package/dist/styles/component.css +78 -175
  301. package/dist/styles/elevation.css +9 -0
  302. package/dist/styles/spacing.css +29 -0
  303. package/dist/styles/typescale.css +635 -20
  304. package/dist/utils/Layer.svelte +1 -1
  305. package/dist/utils/icon/Icon.svelte +8 -6
  306. package/dist/utils/icon/loadingShapes.js +11 -10
  307. package/package.json +19 -15
  308. package/dist/components/containers/stack/HStack.svelte +0 -23
  309. package/dist/components/containers/stack/HStack.svelte.d.ts +0 -5
  310. package/dist/components/containers/stack/VStack.svelte +0 -23
  311. package/dist/components/containers/stack/VStack.svelte.d.ts +0 -5
  312. package/dist/components/containers/stack/index.d.ts +0 -4
  313. package/dist/components/containers/stack/theme.d.ts +0 -66
  314. package/dist/components/containers/stack/theme.js +0 -26
  315. package/dist/components/containers/stack/types.d.ts +0 -12
  316. /package/dist/components/containers/{stack → carousel}/types.js +0 -0
@@ -1,15 +1,20 @@
1
1
  import { tv } from 'tailwind-variants';
2
+ /**
3
+ * M3 Expressive contained search bar (https://m3.material.io/components/search/specs):
4
+ * 56dp, circular, surface-container-high, flat. 48dp leading/trailing targets at a 4dp inset,
5
+ * 4dp between the leading element and the label, 0 between trailing actions; 16dp at an edge
6
+ * with no action. Hover 8% / pressed 10% on-surface; focus 3dp secondary outline, 2dp offset.
7
+ */
2
8
  export const search = tv({
3
9
  slots: {
4
- base: 'group grow cursor-text focus-inside:ring-3 focus-inside:ring-inset-2 focus-inside:ring-md-sys-color-secondary relative bg-md-sys-color-surface-container-high rounded-full h-14 state-layer before:rounded-full hover:before:bg-md-sys-color-on-surface/8 items-center flex w-full max-w-[720px] px-4',
5
- leadingIcon: 'text-md-sys-color-on-surface text-[24px] size-6 mr-4',
6
- trailingIcon: 'text-md-sys-color-on-surface-variant text-[24px] size-6 ml-4',
7
- input: ' placeholder:text-md-sys-color-on-surface-variant placeholder:md-sys-typescale-body-large z-50 md-sys-typescale-body-large text-md-sys-color-on-surface outline-none w-full'
10
+ base: 'state-layer relative flex h-spacing-700 w-full max-w-[720px] grow cursor-text items-center rounded-full bg-md-sys-color-surface-container-high ps-spacing-50 pe-spacing-50 before:rounded-full hover:before:bg-md-sys-color-on-surface/8 active:before:bg-md-sys-color-on-surface/10 has-[input:focus-visible]:outline-3 has-[input:focus-visible]:outline-offset-2 has-[input:focus-visible]:outline-md-sys-color-secondary',
11
+ leading: 'text-md-sys-color-on-surface relative flex shrink-0 items-center',
12
+ leadingIcon: 'text-md-sys-color-on-surface grid size-spacing-600 shrink-0 place-items-center',
13
+ input: 'md-sys-typescale-body-large text-md-sys-color-on-surface placeholder:text-md-sys-color-on-surface-variant relative ms-spacing-50 me-spacing-50 w-full min-w-spacing-0 bg-transparent outline-none [&::-webkit-search-cancel-button]:hidden',
14
+ trailing: 'text-md-sys-color-on-surface-variant relative flex shrink-0 items-center'
8
15
  },
9
16
  variants: {
10
- shadow: {
11
- true: 'shadow-elevation-3',
12
- false: ''
13
- }
17
+ hasLeading: { true: '', false: { input: 'ms-spacing-150' } },
18
+ hasTrailing: { true: '', false: { input: 'me-spacing-150' } }
14
19
  }
15
20
  });
@@ -1,15 +1,10 @@
1
1
  import type { IconProps } from '../../../utils/index.js';
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { HTMLInputAttributes } from 'svelte/elements';
4
- import type { SearchVariants } from './theme.js';
5
4
  /**
6
5
  * Props for the Search component.
7
6
  */
8
- export interface SearchProps extends SearchVariants, Omit<HTMLInputAttributes, 'size'> {
9
- /**
10
- * The content of the search bar, typically used for custom layouts.
11
- */
12
- children?: Snippet;
7
+ export interface SearchProps extends Omit<HTMLInputAttributes, 'size'> {
13
8
  /**
14
9
  * The current value of the search input.
15
10
  */
@@ -19,16 +14,27 @@ export interface SearchProps extends SearchVariants, Omit<HTMLInputAttributes, '
19
14
  */
20
15
  elementRef?: HTMLInputElement;
21
16
  /**
22
- * Callback function when the trailing icon is clicked.
23
- * Default behavior clears the search value.
17
+ * A leading action, e.g. a menu or back icon button. Replaces the leading icon.
18
+ */
19
+ leading?: Snippet;
20
+ /**
21
+ * Trailing actions and/or an avatar, e.g. a mic icon button. Rendered after the clear button.
22
+ */
23
+ trailing?: Snippet;
24
+ /**
25
+ * Called by the clear button. Defaults to clearing the value and refocusing the input.
24
26
  */
25
27
  trailingClick?: () => void;
26
28
  /**
27
- * Props for the leading icon (typically a search icon).
29
+ * Props for the leading icon (a search icon by default); `null` removes it.
30
+ */
31
+ leadingIconProps?: IconProps | null;
32
+ /**
33
+ * Props for the clear button's icon, shown while there is a value; `null` removes the button.
28
34
  */
29
- leadingIconProps?: IconProps;
35
+ trailingIconProps?: IconProps | null;
30
36
  /**
31
- * Props for the trailing icon (typically a clear/close icon).
37
+ * Accessible label for the clear button.
32
38
  */
33
- trailingIconProps?: IconProps;
39
+ clearLabel?: string;
34
40
  }
@@ -0,0 +1,53 @@
1
+ import { Meta, Title, Subtitle, Canvas, Controls } from '@storybook/addon-docs/blocks';
2
+ import * as SelectStories from './Select.stories.svelte';
3
+
4
+ <Meta of={SelectStories} />
5
+
6
+ <Title />
7
+
8
+ <Subtitle>Select lets users choose one option from a list.</Subtitle>
9
+
10
+ [M3 spec (menus)](https://m3.material.io/components/menus/specs) ·
11
+ `import { Select } from '@noxlovette/material';`
12
+
13
+ <Canvas of={SelectStories.Playground} />
14
+
15
+ <Controls of={SelectStories.Playground} />
16
+
17
+ ## Options
18
+
19
+ M3 specifies dropdown selection as part of menus, so `Select` pairs a text-field-style trigger
20
+ with a menu panel. The panel is built entirely from the `options` array; it takes no children.
21
+
22
+ Each entry is either an item (bits-ui's `Select.ItemProps`: `value`, `label`, `disabled`…) or a
23
+ group, `{ type: 'group', heading?, items }`, which renders a labelled section:
24
+
25
+ ```svelte
26
+ <script lang="ts">
27
+ import { Select, type SelectOption } from '@noxlovette/material';
28
+
29
+ let fruit = $state<string | undefined>();
30
+ const options: SelectOption[] = [
31
+ { value: 'apple', label: 'Apple' },
32
+ { value: 'banana', label: 'Banana' }
33
+ ];
34
+ </script>
35
+
36
+ <Select type="single" placeholder="Choose a fruit" {options} bind:value={fruit} />
37
+ ```
38
+
39
+ <Canvas of={SelectStories.GroupedOptions} />
40
+
41
+ <Canvas of={SelectStories.States} />
42
+
43
+ ## Styled primitives
44
+
45
+ `SelectItem`, `SelectGroup`, `SelectLabel` and `SelectSeparator` carry the same styling. Use them
46
+ only when building your own dropdown directly on bits-ui's `Select.Root`; `Select` itself doesn't
47
+ accept them.
48
+
49
+ ## Accessibility
50
+
51
+ - **Keyboard.** Arrow keys move through options, Enter and Space select, and Escape closes.
52
+ - **Selection** shows a check icon as well as a color.
53
+ - **Disabled options** are skipped by keyboard navigation.
@@ -57,20 +57,20 @@
57
57
 
58
58
  <Story name="Playground">
59
59
  {#snippet template(args)}
60
- <div class="max-w-sm p-6">
60
+ <div class="p-spacing-300 max-w-sm">
61
61
  <Select {...args} />
62
62
  </div>
63
63
  {/snippet}
64
64
  </Story>
65
65
 
66
66
  <Story name="Grouped Options" asChild>
67
- <div class="max-w-sm p-6">
67
+ <div class="p-spacing-300 max-w-sm">
68
68
  <Select type="single" placeholder="Choose a fruit" options={groupedOptions} />
69
69
  </div>
70
70
  </Story>
71
71
 
72
72
  <Story name="With Leading Icon" asChild>
73
- <div class="max-w-sm p-6">
73
+ <div class="p-spacing-300 max-w-sm">
74
74
  <Select
75
75
  type="single"
76
76
  placeholder="Choose a fruit"
@@ -81,7 +81,7 @@
81
81
  </Story>
82
82
 
83
83
  <Story name="States" asChild>
84
- <div class="flex max-w-sm flex-col gap-6 p-6">
84
+ <div class="gap-spacing-300 p-spacing-300 flex max-w-sm flex-col">
85
85
  <Select type="single" placeholder="Default" options={fruitOptions} />
86
86
  <Select type="single" placeholder="Error" options={fruitOptions} error value="apple" />
87
87
  <Select type="single" placeholder="Disabled" options={fruitOptions} disabled />
@@ -94,7 +94,11 @@ Powered by bits-ui for accessibility and behavior.
94
94
  {#snippet children({ selected })}
95
95
  <span class="flex-1 truncate">{innerItem.label}</span>
96
96
  {#if selected}
97
- <Icon aria-hidden="true" name="check" class="size-5 shrink-0" />
97
+ <Icon
98
+ aria-hidden="true"
99
+ name="check"
100
+ class="size-spacing-250 shrink-0"
101
+ />
98
102
  {/if}
99
103
  {/snippet}
100
104
  </Select.Item>
@@ -106,7 +110,11 @@ Powered by bits-ui for accessibility and behavior.
106
110
  {#snippet children({ selected })}
107
111
  <span class="flex-1 truncate">{item.label}</span>
108
112
  {#if selected}
109
- <Icon aria-hidden="true" name="check" class="size-5 shrink-0" />
113
+ <Icon
114
+ aria-hidden="true"
115
+ name="check"
116
+ class="size-spacing-250 shrink-0"
117
+ />
110
118
  {/if}
111
119
  {/snippet}
112
120
  </Select.Item>
@@ -28,7 +28,7 @@
28
28
  {:else}
29
29
  <span class="flex-1 truncate">{label ?? value}</span>
30
30
  {#if selected}
31
- <Icon name="check" class="size-5 shrink-0" />
31
+ <Icon name="check" class="size-spacing-250 shrink-0" />
32
32
  {/if}
33
33
  {/if}
34
34
  {/snippet}
@@ -3,14 +3,14 @@ export const select = tv({
3
3
  slots: {
4
4
  root: 'relative w-full',
5
5
  base: `
6
- group w-full h-14 px-0 relative flex items-center
6
+ group w-full h-spacing-700 px-spacing-0 relative flex items-center
7
7
  `,
8
8
  trigger: `
9
9
  group peer w-full h-full flex items-center justify-between bg-transparent outline-none
10
10
  cursor-pointer
11
11
  `,
12
12
  inputWrapper: `
13
- relative flex-1 h-full px-4 flex items-center
13
+ relative flex-1 h-full px-spacing-200 flex items-center
14
14
  `,
15
15
  value: `
16
16
  w-full bg-transparent outline-none
@@ -20,12 +20,12 @@ export const select = tv({
20
20
  truncate text-left
21
21
  `,
22
22
  leadingIcon: `
23
- text-md-sys-color-on-surface-variant size-6
23
+ text-md-sys-color-on-surface-variant size-spacing-300
24
24
  group-has-[[data-state=open]]:text-md-sys-color-primary
25
- group-data-[invalid]:text-md-sys-color-error ml-3 text-[24px]
25
+ group-data-[invalid]:text-md-sys-color-error ml-spacing-150 text-[24px]
26
26
  `,
27
27
  dropdownIcon: `
28
- mr-3 text-md-sys-color-on-surface-variant size-6
28
+ mr-spacing-150 text-md-sys-color-on-surface-variant size-spacing-300
29
29
  group-data-[state=open]:text-md-sys-color-primary
30
30
  group-data-[invalid]:text-md-sys-color-error z-20 text-[24px]
31
31
  transition-transform md-sys-motion-fast-spatial
@@ -33,12 +33,12 @@ export const select = tv({
33
33
  `,
34
34
  content: `
35
35
  z-[100] min-w-[var(--bits-select-anchor-width)] max-h-96 overflow-y-auto
36
- rounded-lg gap-1 bg-md-sys-color-surface-container-high
37
- shadow-elevation-3 ring-md-sys-color-outline/10 px-2 py-1 outline-none
36
+ rounded-lg gap-spacing-50 bg-md-sys-color-surface-container-high
37
+ shadow-elevation-3 ring-md-sys-color-outline/10 px-spacing-100 py-spacing-50 outline-none
38
38
  `,
39
39
  item: `
40
40
  rounded-sm relative flex w-full cursor-pointer select-none items-center
41
- px-3 py-2 outline-none
41
+ px-spacing-150 py-spacing-100 outline-none
42
42
  md-sys-typescale-body-large text-md-sys-color-on-surface
43
43
  hover:bg-md-sys-color-on-surface/8
44
44
  data-[highlighted]:bg-md-sys-color-on-surface/8
@@ -48,14 +48,14 @@ export const select = tv({
48
48
  transition-colors md-sys-motion-fast-effects
49
49
  `,
50
50
  groupLabel: `
51
- px-3 pt-2 pb-1 md-sys-typescale-label-medium
51
+ px-spacing-150 pt-spacing-100 pb-spacing-50 md-sys-typescale-label-medium
52
52
  text-md-sys-color-primary
53
53
  `,
54
54
  separator: `
55
- my-1 h-px bg-md-sys-color-outline-variant
55
+ my-spacing-50 h-px bg-md-sys-color-outline-variant
56
56
  `,
57
57
  supportingText: `
58
- px-4 pt-1 flex justify-between
58
+ px-spacing-200 pt-spacing-50 flex justify-between
59
59
  md-sys-typescale-body-small
60
60
  text-md-sys-color-on-surface-variant
61
61
  group-data-[invalid]:text-md-sys-color-error
@@ -67,7 +67,7 @@ export const select = tv({
67
67
  base: `
68
68
  bg-md-sys-color-surface-container-highest
69
69
  rounded-t-xs state-layer before:rounded-xs hover:before:bg-md-sys-color-on-surface/8
70
- after:absolute after:bottom-0 after:left-0 after:right-0 after:h-px after:bg-md-sys-color-on-surface-variant
70
+ after:absolute after:bottom-spacing-0 after:left-spacing-0 after:right-spacing-0 after:h-px after:bg-md-sys-color-on-surface-variant
71
71
  after:transition-[height,background-color] after:md-sys-motion-fast-spatial
72
72
  hover:after:bg-md-sys-color-on-surface
73
73
  has-[[data-state=open]]:after:bg-md-sys-color-primary
@@ -0,0 +1,85 @@
1
+ import { Meta, Title, Subtitle, Canvas, Controls } from '@storybook/addon-docs/blocks';
2
+ import * as SliderStories from './Slider.stories.svelte';
3
+
4
+ <Meta of={SliderStories} />
5
+
6
+ <Title />
7
+
8
+ <Subtitle>Sliders allow users to make selections from a range of values.</Subtitle>
9
+
10
+ [M3 spec](https://m3.material.io/components/sliders/specs) ·
11
+ `import { Slider } from '@noxlovette/material';`
12
+
13
+ <Canvas of={SliderStories.Playground} />
14
+
15
+ <Controls of={SliderStories.Playground} />
16
+
17
+ This is the M3 Expressive slider. It has two tracks, active and inactive, with a 6dp gap on each
18
+ side of a 4dp bar handle. Bind `value` to read and set it:
19
+
20
+ ```svelte
21
+ <script lang="ts">
22
+ let volume = $state(40);
23
+ </script>
24
+
25
+ <Slider bind:value={volume} min={0} max={100} />
26
+ ```
27
+
28
+ ## Choosing a size
29
+
30
+ `xs` is the default. Go larger when the slider is the main control on the screen, or when it needs
31
+ inset icons.
32
+
33
+ | Size | Track | Handle | Outer corner | Inset icon |
34
+ | ---- | ----- | ------ | ------------ | ------------ |
35
+ | `xs` | 16dp | 44dp | 8dp | — |
36
+ | `s` | 24dp | 44dp | 8dp | — |
37
+ | `m` | 40dp | 52dp | 12dp | 24dp, 6dp in |
38
+ | `l` | 56dp | 68dp | 16dp | 24dp, 6dp in |
39
+ | `xl` | 96dp | 108dp | 28dp | 32dp, 8dp in |
40
+
41
+ The corners next to the handle are always 2dp.
42
+
43
+ <Canvas of={SliderStories.Sizes} />
44
+
45
+ ## Inset icons
46
+
47
+ `leadingIconProps` and `trailingIconProps` put an icon inside the track. M3 defines inset icons
48
+ only for `m`, `l` and `xl`, so they're ignored on `xs` and `s`. When the handle gets too close for
49
+ the icon to fit in its track, the icon moves across the handle into the other track. It also takes
50
+ that track's color.
51
+
52
+ <Canvas of={SliderStories.WithIcons} />
53
+
54
+ ## Continuous or discrete
55
+
56
+ Leave `step="any"` for a continuous slider. Set a numeric `step` to make it discrete, and add
57
+ `stops` to show a 4dp stop indicator at each step. With or without `stops`, `endStops` marks the
58
+ end of the track 4dp from its edge. Stops hide within 6dp of the handle, and wherever an inset
59
+ icon covers them.
60
+
61
+ <Canvas of={SliderStories.DiscreteWithStops} />
62
+
63
+ ## Value indicator
64
+
65
+ With `showValue`, a 48×44dp indicator shows above the handle while it's dragged or has keyboard
66
+ focus. It sits 12dp above the handle, on inverse-surface. Use `format` to add units or localize
67
+ the number.
68
+
69
+ ## States
70
+
71
+ - **Pressed and focused:** the handle narrows from 4dp to 2dp on a fast spatial spring, and the
72
+ 6dp gaps follow its edge.
73
+ - **Disabled:** the active track is on-surface at 38% opacity, the inactive track 12%, and the
74
+ handle 38%.
75
+
76
+ <Canvas of={SliderStories.Disabled} />
77
+
78
+ ## Accessibility
79
+
80
+ - **Keyboard:** the arrow keys move by one step, and Home/End jump to `min`/`max` (via bits-ui's
81
+ `Slider.Root`).
82
+ - **Focus:** keyboard focus narrows the handle, draws an outline around it, and reveals the value
83
+ indicator.
84
+ - **Labelling:** give the slider an accessible name, such as `aria-label="Volume"`. Inset icons
85
+ are decorative and don't label it.
@@ -36,14 +36,14 @@
36
36
 
37
37
  <Story name="Playground">
38
38
  {#snippet template(args)}
39
- <div class="max-w-sm p-10">
39
+ <div class="p-spacing-500 max-w-sm">
40
40
  <Slider {...args} />
41
41
  </div>
42
42
  {/snippet}
43
43
  </Story>
44
44
 
45
45
  <Story name="Sizes" asChild>
46
- <div class="flex max-w-sm flex-col gap-8 p-10">
46
+ <div class="gap-spacing-400 p-spacing-500 flex max-w-sm flex-col">
47
47
  <Slider value={30} size="xs" />
48
48
  <Slider value={30} size="s" />
49
49
  <Slider value={30} size="m" />
@@ -52,8 +52,8 @@
52
52
  </div>
53
53
  </Story>
54
54
 
55
- <Story name="With Icons" asChild>
56
- <div class="max-w-sm p-10">
55
+ <Story name="With Icons" exportName="WithIcons" asChild>
56
+ <div class="p-spacing-500 max-w-sm">
57
57
  <Slider
58
58
  value={60}
59
59
  size="m"
@@ -63,14 +63,14 @@
63
63
  </div>
64
64
  </Story>
65
65
 
66
- <Story name="Discrete With Stops" asChild>
67
- <div class="max-w-sm p-10">
66
+ <Story name="Discrete With Stops" exportName="DiscreteWithStops" asChild>
67
+ <div class="p-spacing-500 max-w-sm">
68
68
  <Slider value={40} step={20} stops />
69
69
  </div>
70
70
  </Story>
71
71
 
72
72
  <Story name="Disabled" asChild>
73
- <div class="max-w-sm p-10">
73
+ <div class="p-spacing-500 max-w-sm">
74
74
  <Slider value={40} disabled />
75
75
  </div>
76
76
  </Story>
@@ -41,13 +41,18 @@ the track, a new `value` from outside) springs to it on the fast spatial spring.
41
41
 
42
42
  /* M3 slider geometry, in px. */
43
43
  const HANDLE_PX = 4;
44
+ /* Pressed and focused handles narrow to 2dp. */
45
+ const HANDLE_NARROW_PX = 2;
44
46
  const GAP_PX = 6;
45
- const ICON_PADDING_PX = 8;
46
- /* Stops closer than this to the handle's center are hidden under its gap. */
47
- const STOP_CLEARANCE_PX = HANDLE_PX / 2 + GAP_PX;
47
+ /* Stop indicators are 4dp with 4dp trailing space, so their centers sit 6dp in from each end. */
48
+ const STOP_INSET_PX = 6;
48
49
 
49
- const iconSize = $derived(size === 'xl' ? 'lg' : 'md');
50
- const iconPx = $derived(size === 'xl' ? 40 : 24);
50
+ /* Inset icons exist only on M, L and XL: 24dp/6dp padding, 32dp/8dp on XL. */
51
+ const hasIcons = $derived(size === 'm' || size === 'l' || size === 'xl');
52
+ const iconPx = $derived(size === 'xl' ? 32 : 24);
53
+ const iconPaddingPx = $derived(size === 'xl' ? 8 : 6);
54
+ const leadingIcon = $derived(hasIcons ? leadingIconProps : undefined);
55
+ const trailingIcon = $derived(hasIcons ? trailingIconProps : undefined);
51
56
 
52
57
  const stepValue = $derived(typeof step === 'number' ? step : undefined);
53
58
  const range = $derived(max - min);
@@ -60,17 +65,21 @@ the track, a new `value` from outside) springs to it on the fast spatial spring.
60
65
  pressed, the handle is being dragged and tracks it directly.
61
66
  */
62
67
  let dragging = $state(false);
68
+ let pressed = $state(false);
69
+ let focusVisible = $state(false);
63
70
 
64
71
  const trackDrag: Attachment<HTMLElement> = (node) => {
65
72
  const move = () => (dragging = true);
66
73
  const release = () => {
67
74
  dragging = false;
75
+ pressed = false;
68
76
  window.removeEventListener('pointermove', move);
69
77
  window.removeEventListener('pointerup', release);
70
78
  window.removeEventListener('pointercancel', release);
71
79
  };
72
80
  const press = () => {
73
81
  if (disabled) return;
82
+ pressed = true;
74
83
  window.addEventListener('pointermove', move);
75
84
  window.addEventListener('pointerup', release);
76
85
  window.addEventListener('pointercancel', release);
@@ -92,6 +101,15 @@ the track, a new `value` from outside) springs to it on the fast spatial spring.
92
101
  /* The spring may overshoot the ends; the track can't. */
93
102
  const pos = $derived(Math.min(1, Math.max(0, fraction.current)));
94
103
 
104
+ /** Handle thickness: 4dp at rest, 2dp while pressed or keyboard-focused. */
105
+ const handleThickness = new SpringValue(
106
+ () => (pressed || focusVisible ? HANDLE_NARROW_PX : HANDLE_PX),
107
+ springTokens.fastSpatial
108
+ );
109
+ const handlePx = $derived(Math.max(1, handleThickness.current));
110
+ /* The gap is measured from the handle's current edge, so it stays 6dp as the handle narrows. */
111
+ const clearPx = $derived(handlePx / 2 + GAP_PX);
112
+
95
113
  /* ---- geometry -------------------------------------------------------------------------- */
96
114
 
97
115
  let trackWidth = $state(0);
@@ -101,21 +119,19 @@ the track, a new `value` from outside) springs to it on the fast spatial spring.
101
119
  const start = $derived(vertical ? 'bottom' : 'left');
102
120
  const extent = $derived(vertical ? 'height' : 'width');
103
121
 
104
- /** Handle center: travels the track inset by half a handle so it never overhangs the ends. */
122
+ /** Handle center: travels the track inset by half a resting handle so it never overhangs the ends. */
105
123
  const center = (f: number) => `calc(${HANDLE_PX / 2}px + ${f} * (100% - ${HANDLE_PX}px))`;
106
124
  const centerPx = $derived(HANDLE_PX / 2 + pos * (length - HANDLE_PX));
107
125
 
108
- const activePx = $derived(centerPx - HANDLE_PX / 2 - GAP_PX);
109
- const inactivePx = $derived(length - centerPx - HANDLE_PX / 2 - GAP_PX);
126
+ const activePx = $derived(centerPx - clearPx);
127
+ const inactivePx = $derived(length - centerPx - clearPx);
110
128
 
111
- const activeStyle = $derived(
112
- `${extent}: max(0px, calc(${pos} * (100% - ${HANDLE_PX}px) - ${GAP_PX}px))`
113
- );
114
- const inactiveStyle = $derived(
115
- `${extent}: max(0px, calc(${1 - pos} * (100% - ${HANDLE_PX}px) - ${GAP_PX}px))`
116
- );
129
+ const trackStyle = (f: number) =>
130
+ `${extent}: max(0px, calc(${f} * (100% - ${HANDLE_PX}px) + ${HANDLE_PX / 2 - clearPx}px))`;
131
+ const activeStyle = $derived(trackStyle(pos));
132
+ const inactiveStyle = $derived(trackStyle(1 - pos));
117
133
 
118
- /* Stops sit inset by half the track thickness so the end stops land in the rounded caps. */
134
+ /* Stops span the track inset by STOP_INSET_PX, so the end stops keep their 4dp trailing space. */
119
135
  const stopList = $derived.by(() => {
120
136
  const output: number[] = [];
121
137
  if (stops && stepValue && range > 0) {
@@ -124,24 +140,24 @@ the track, a new `value` from outside) springs to it on the fast spatial spring.
124
140
  output.push(1);
125
141
  }
126
142
  return output.filter((s) => {
127
- if (leadingIconProps && s === 0) return false;
128
- if (trailingIconProps && s === 1) return false;
129
- return !length || Math.abs(s - pos) * length > STOP_CLEARANCE_PX;
143
+ if (leadingIcon && s === 0) return false;
144
+ if (trailingIcon && s === 1) return false;
145
+ return !length || Math.abs(s - pos) * length > clearPx;
130
146
  });
131
147
  });
132
148
 
133
149
  /* Icons ride inside their track; when it gets too short they hop across the handle. */
134
- const iconFits = (trackPx: number) => trackPx >= iconPx + ICON_PADDING_PX * 2;
150
+ const iconFits = (trackPx: number) => trackPx >= iconPx + iconPaddingPx * 2;
135
151
  const leadingOnActive = $derived(!length || iconFits(activePx));
136
152
  const trailingOnInactive = $derived(!length || iconFits(inactivePx));
137
153
 
138
154
  const leadingOffset = $derived(
139
- leadingOnActive ? ICON_PADDING_PX : centerPx + HANDLE_PX / 2 + GAP_PX + ICON_PADDING_PX
155
+ leadingOnActive ? iconPaddingPx : centerPx + clearPx + iconPaddingPx
140
156
  );
141
157
  const trailingOffset = $derived(
142
158
  trailingOnInactive
143
- ? length - ICON_PADDING_PX - iconPx
144
- : centerPx - HANDLE_PX / 2 - GAP_PX - ICON_PADDING_PX - iconPx
159
+ ? length - iconPaddingPx - iconPx
160
+ : centerPx - clearPx - iconPaddingPx - iconPx
145
161
  );
146
162
 
147
163
  const sliderDir = $derived(dir === null || dir === 'auto' ? undefined : dir);
@@ -175,26 +191,26 @@ the track, a new `value` from outside) springs to it on the fast spatial spring.
175
191
  {#each stopList as s (s)}
176
192
  <div
177
193
  class={cls.stop({ class: s < pos ? cls.stopOnActive() : cls.stopOnInactive() })}
178
- style="{start}: calc(var(--track) / 2 + {s} * (100% - var(--track)))"
194
+ style="{start}: calc({STOP_INSET_PX}px + {s} * (100% - {STOP_INSET_PX * 2}px))"
179
195
  ></div>
180
196
  {/each}
181
197
 
182
- {#if leadingIconProps}
198
+ {#if leadingIcon}
183
199
  <span
184
200
  class={cls.icon({ class: leadingOnActive ? cls.iconOnActive() : cls.iconOnInactive() })}
185
201
  style="{start}: {leadingOffset}px"
186
202
  >
187
- <Icon size={iconSize} {...leadingIconProps} />
203
+ <Icon {...leadingIcon} class={cls.iconGlyph({ class: clsx(leadingIcon.class) })} />
188
204
  </span>
189
205
  {/if}
190
- {#if trailingIconProps}
206
+ {#if trailingIcon}
191
207
  <span
192
208
  class={cls.icon({
193
209
  class: trailingOnInactive ? cls.iconOnInactive() : cls.iconOnActive()
194
210
  })}
195
211
  style="{start}: {trailingOffset}px"
196
212
  >
197
- <Icon size={iconSize} {...trailingIconProps} />
213
+ <Icon {...trailingIcon} class={cls.iconGlyph({ class: clsx(trailingIcon.class) })} />
198
214
  </span>
199
215
  {/if}
200
216
 
@@ -204,7 +220,9 @@ the track, a new `value` from outside) springs to it on the fast spatial spring.
204
220
  {...thumbProps}
205
221
  class={cls.handle()}
206
222
  data-active={active || undefined}
207
- style="{start}: {center(pos)}"
223
+ style="{start}: {center(pos)}; {extent}: {handlePx}px"
224
+ onfocus={(e) => (focusVisible = e.currentTarget.matches(':focus-visible'))}
225
+ onblur={() => (focusVisible = false)}
208
226
  ></div>
209
227
  {/snippet}
210
228
  </Slider.Thumb>
@@ -1,6 +1,8 @@
1
1
  import { type VariantProps } from 'tailwind-variants';
2
2
  /**
3
3
  * M3 Expressive slider: an active and an inactive track separated from a bar handle by a 6dp gap.
4
+ * Per-size tokens (track / handle / outer corner / inset icon): XS 16/44/8/–, S 24/44/8/–,
5
+ * M 40/52/12/24, L 56/68/16/24, XL 96/108/28/32. The value indicator sits 12dp above the handle.
4
6
  * Track/handle positions are set inline by `Slider.svelte` from one animated value; these slots
5
7
  * only carry size, shape and color.
6
8
  *
@@ -16,12 +18,15 @@ export declare const slider: import("tailwind-variants").TVReturnType<{
16
18
  };
17
19
  m: {
18
20
  root: string;
21
+ iconGlyph: string;
19
22
  };
20
23
  l: {
21
24
  root: string;
25
+ iconGlyph: string;
22
26
  };
23
27
  xl: {
24
28
  root: string;
29
+ iconGlyph: string;
25
30
  };
26
31
  };
27
32
  vertical: {
@@ -60,6 +65,7 @@ export declare const slider: import("tailwind-variants").TVReturnType<{
60
65
  stopOnActive: string;
61
66
  stopOnInactive: string;
62
67
  icon: string;
68
+ iconGlyph: string;
63
69
  iconOnActive: string;
64
70
  iconOnInactive: string;
65
71
  handle: string;
@@ -74,12 +80,15 @@ export declare const slider: import("tailwind-variants").TVReturnType<{
74
80
  };
75
81
  m: {
76
82
  root: string;
83
+ iconGlyph: string;
77
84
  };
78
85
  l: {
79
86
  root: string;
87
+ iconGlyph: string;
80
88
  };
81
89
  xl: {
82
90
  root: string;
91
+ iconGlyph: string;
83
92
  };
84
93
  };
85
94
  vertical: {
@@ -118,6 +127,7 @@ export declare const slider: import("tailwind-variants").TVReturnType<{
118
127
  stopOnActive: string;
119
128
  stopOnInactive: string;
120
129
  icon: string;
130
+ iconGlyph: string;
121
131
  iconOnActive: string;
122
132
  iconOnInactive: string;
123
133
  handle: string;
@@ -132,12 +142,15 @@ export declare const slider: import("tailwind-variants").TVReturnType<{
132
142
  };
133
143
  m: {
134
144
  root: string;
145
+ iconGlyph: string;
135
146
  };
136
147
  l: {
137
148
  root: string;
149
+ iconGlyph: string;
138
150
  };
139
151
  xl: {
140
152
  root: string;
153
+ iconGlyph: string;
141
154
  };
142
155
  };
143
156
  vertical: {
@@ -176,6 +189,7 @@ export declare const slider: import("tailwind-variants").TVReturnType<{
176
189
  stopOnActive: string;
177
190
  stopOnInactive: string;
178
191
  icon: string;
192
+ iconGlyph: string;
179
193
  iconOnActive: string;
180
194
  iconOnInactive: string;
181
195
  handle: string;