@noxlovette/material 0.5.1 → 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 (296) 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 +24 -4
  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 +45 -15
  47. package/dist/components/buttons/button-group/theme.js +13 -8
  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 +33 -18
  57. package/dist/components/buttons/connected/theme.js +32 -9
  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 +65 -48
  64. package/dist/components/buttons/split-button/SplitButton.svelte.d.ts +6 -11
  65. package/dist/components/buttons/split-button/theme.d.ts +110 -17
  66. package/dist/components/buttons/split-button/theme.js +74 -16
  67. package/dist/components/buttons/split-button/types.d.ts +24 -18
  68. package/dist/components/buttons/theme.d.ts +422 -230
  69. package/dist/components/buttons/theme.js +286 -380
  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 +5 -5
  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.js +3 -3
  77. package/dist/components/containers/action-rail/ActionRail.stories.svelte +1 -0
  78. package/dist/components/containers/action-rail/theme.js +1 -1
  79. package/dist/components/containers/bottom-sheet/BottomSheet.mdx +64 -0
  80. package/dist/components/containers/bottom-sheet/BottomSheet.stories.svelte +108 -12
  81. package/dist/components/containers/bottom-sheet/BottomSheet.svelte +179 -109
  82. package/dist/components/containers/bottom-sheet/BottomSheet.svelte.d.ts +10 -5
  83. package/dist/components/containers/bottom-sheet/index.d.ts +1 -0
  84. package/dist/components/containers/bottom-sheet/index.js +1 -0
  85. package/dist/components/containers/bottom-sheet/theme.d.ts +45 -0
  86. package/dist/components/containers/bottom-sheet/theme.js +42 -0
  87. package/dist/components/containers/bottom-sheet/types.d.ts +37 -5
  88. package/dist/components/containers/carousel/Carousel.mdx +80 -0
  89. package/dist/components/containers/carousel/Carousel.stories.svelte +131 -0
  90. package/dist/components/containers/carousel/Carousel.stories.svelte.d.ts +4 -0
  91. package/dist/components/containers/carousel/Carousel.svelte +194 -0
  92. package/dist/components/containers/carousel/Carousel.svelte.d.ts +25 -0
  93. package/dist/components/containers/carousel/index.d.ts +3 -0
  94. package/dist/components/containers/{stack → carousel}/index.js +1 -2
  95. package/dist/components/containers/carousel/keylines.d.ts +32 -0
  96. package/dist/components/containers/carousel/keylines.js +88 -0
  97. package/dist/components/containers/carousel/theme.d.ts +162 -0
  98. package/dist/components/containers/carousel/theme.js +67 -0
  99. package/dist/components/containers/carousel/types.d.ts +52 -0
  100. package/dist/components/containers/context-menu/ContextMenu.stories.svelte +1 -0
  101. package/dist/components/containers/context-menu/ContextMenu.svelte +2 -2
  102. package/dist/components/containers/context-menu/ContextMenu.svelte.d.ts +1 -1
  103. package/dist/components/containers/context-menu/theme.js +10 -10
  104. package/dist/components/containers/dialogue/Dialogue.mdx +65 -0
  105. package/dist/components/containers/dialogue/Dialogue.stories.svelte +4 -4
  106. package/dist/components/containers/dialogue/Dialogue.svelte +1 -1
  107. package/dist/components/containers/dialogue/theme.js +3 -3
  108. package/dist/components/containers/divider/Divider.stories.svelte +2 -1
  109. package/dist/components/containers/divider/Divider.svelte +2 -2
  110. package/dist/components/containers/divider/theme.d.ts +6 -6
  111. package/dist/components/containers/divider/theme.js +2 -2
  112. package/dist/components/containers/index.d.ts +1 -1
  113. package/dist/components/containers/index.js +1 -1
  114. package/dist/components/containers/link-preview/theme.js +2 -2
  115. package/dist/components/containers/list/List.svelte +18 -0
  116. package/dist/components/containers/list/List.svelte.d.ts +4 -0
  117. package/dist/components/containers/list/ListItem.mdx +100 -0
  118. package/dist/components/containers/list/ListItem.stories.svelte +177 -42
  119. package/dist/components/containers/list/ListItem.stories.svelte.d.ts +2 -17
  120. package/dist/components/containers/list/ListItem.svelte +92 -37
  121. package/dist/components/containers/list/ListItem.svelte.d.ts +1 -1
  122. package/dist/components/containers/list/context.d.ts +6 -0
  123. package/dist/components/containers/list/context.js +4 -0
  124. package/dist/components/containers/list/index.d.ts +1 -0
  125. package/dist/components/containers/list/index.js +1 -0
  126. package/dist/components/containers/list/theme.d.ts +487 -12
  127. package/dist/components/containers/list/theme.js +140 -13
  128. package/dist/components/containers/list/types.d.ts +39 -5
  129. package/dist/components/containers/menu/Menu.mdx +52 -0
  130. package/dist/components/containers/menu/Menu.stories.svelte +14 -14
  131. package/dist/components/containers/menu/Menu.svelte +1 -1
  132. package/dist/components/containers/menu/MenuGroup.svelte +1 -1
  133. package/dist/components/containers/menu/MenuGroup.svelte.d.ts +1 -1
  134. package/dist/components/containers/menu/menu-item/MenuItem.svelte +1 -1
  135. package/dist/components/containers/menu/theme.js +8 -8
  136. package/dist/components/containers/pane/DraggablePane.stories.svelte +4 -3
  137. package/dist/components/containers/pane/DraggablePane.svelte +2 -2
  138. package/dist/components/containers/pane/Pane.stories.svelte +11 -10
  139. package/dist/components/containers/pane/Pane.svelte +24 -2
  140. package/dist/components/containers/pane/PaneGrid.stories.svelte +19 -18
  141. package/dist/components/containers/pane/PaneGrid.svelte +3 -2
  142. package/dist/components/containers/pane/PaneGrid.svelte.d.ts +2 -1
  143. package/dist/components/containers/pane/PaneHandle.svelte +48 -2
  144. package/dist/components/containers/pane/PaneHandle.svelte.d.ts +4 -0
  145. package/dist/components/containers/pane/resizeStore.svelte.d.ts +10 -1
  146. package/dist/components/containers/pane/resizeStore.svelte.js +13 -2
  147. package/dist/components/containers/pane/theme.js +129 -45
  148. package/dist/components/containers/pane/types.d.ts +3 -2
  149. package/dist/components/containers/popover/Popover.mdx +45 -0
  150. package/dist/components/containers/popover/Popover.stories.svelte +1 -1
  151. package/dist/components/containers/popover/Popover.svelte +1 -1
  152. package/dist/components/containers/popover/theme.js +3 -3
  153. package/dist/components/containers/scroll-area/theme.js +2 -2
  154. package/dist/components/containers/side-sheet/SideSheet.mdx +71 -0
  155. package/dist/components/containers/side-sheet/SideSheet.stories.svelte +113 -17
  156. package/dist/components/containers/side-sheet/SideSheet.svelte +113 -66
  157. package/dist/components/containers/side-sheet/SideSheet.svelte.d.ts +8 -7
  158. package/dist/components/containers/side-sheet/index.d.ts +1 -0
  159. package/dist/components/containers/side-sheet/index.js +1 -0
  160. package/dist/components/containers/side-sheet/theme.d.ts +108 -0
  161. package/dist/components/containers/side-sheet/theme.js +72 -0
  162. package/dist/components/containers/side-sheet/types.d.ts +35 -7
  163. package/dist/components/date/DateField.stories.svelte +4 -3
  164. package/dist/components/date/DateField.svelte +11 -2
  165. package/dist/components/date/DateRangeField.stories.svelte +4 -3
  166. package/dist/components/date/DateRangeField.svelte +12 -3
  167. package/dist/components/date/theme.d.ts +7 -7
  168. package/dist/components/date/theme.js +31 -31
  169. package/dist/components/forms/checkbox/Checkbox.mdx +35 -0
  170. package/dist/components/forms/checkbox/Checkbox.stories.svelte +2 -2
  171. package/dist/components/forms/checkbox/Checkbox.svelte +1 -1
  172. package/dist/components/forms/checkbox/theme.js +3 -3
  173. package/dist/components/forms/command/Command.stories.svelte +1 -0
  174. package/dist/components/forms/command/theme.js +9 -9
  175. package/dist/components/forms/pin/theme.js +4 -4
  176. package/dist/components/forms/radio-group/RadioGroup.svelte +1 -1
  177. package/dist/components/forms/radio-group/theme.js +5 -5
  178. package/dist/components/forms/search/Search.mdx +38 -0
  179. package/dist/components/forms/search/Search.stories.svelte +19 -9
  180. package/dist/components/forms/search/Search.svelte +40 -17
  181. package/dist/components/forms/search/Search.svelte.d.ts +2 -2
  182. package/dist/components/forms/search/theme.d.ts +45 -12
  183. package/dist/components/forms/search/theme.js +13 -8
  184. package/dist/components/forms/search/types.d.ts +18 -12
  185. package/dist/components/forms/select/Select.mdx +53 -0
  186. package/dist/components/forms/select/Select.stories.svelte +4 -4
  187. package/dist/components/forms/select/Select.svelte +10 -2
  188. package/dist/components/forms/select/SelectItem.svelte +1 -1
  189. package/dist/components/forms/select/theme.js +12 -12
  190. package/dist/components/forms/slider/Slider.mdx +85 -0
  191. package/dist/components/forms/slider/Slider.stories.svelte +7 -7
  192. package/dist/components/forms/slider/Slider.svelte +46 -28
  193. package/dist/components/forms/slider/theme.d.ts +14 -0
  194. package/dist/components/forms/slider/theme.js +28 -17
  195. package/dist/components/forms/slider/types.d.ts +3 -2
  196. package/dist/components/forms/switch/Switch.mdx +38 -0
  197. package/dist/components/forms/switch/Switch.stories.svelte +3 -3
  198. package/dist/components/forms/switch/theme.js +6 -6
  199. package/dist/components/forms/textfield/Textfield.mdx +53 -0
  200. package/dist/components/forms/textfield/Textfield.stories.svelte +6 -6
  201. package/dist/components/forms/textfield/Textfield.svelte +1 -0
  202. package/dist/components/forms/textfield/theme.js +19 -19
  203. package/dist/components/forms/toggle-group/theme.js +2 -2
  204. package/dist/components/forms/tooltip/Tooltip.stories.svelte +1 -0
  205. package/dist/components/forms/tooltip/theme.js +3 -3
  206. package/dist/components/misc/Avatar.stories.svelte +5 -4
  207. package/dist/components/misc/ThemeSettings.svelte +9 -9
  208. package/dist/components/misc/ThemeSwitcher.svelte +3 -3
  209. package/dist/components/misc/theme.js +4 -4
  210. package/dist/components/nav/appbar/AppBar.mdx +71 -0
  211. package/dist/components/nav/appbar/AppBar.stories.svelte +93 -3
  212. package/dist/components/nav/appbar/AppBar.stories.svelte.d.ts +2 -17
  213. package/dist/components/nav/appbar/AppBar.svelte +90 -37
  214. package/dist/components/nav/appbar/AppBar.svelte.d.ts +9 -2
  215. package/dist/components/nav/appbar/theme.d.ts +85 -18
  216. package/dist/components/nav/appbar/theme.js +159 -23
  217. package/dist/components/nav/appbar/types.d.ts +46 -9
  218. package/dist/components/nav/breadcrumb/Breadcrumb.stories.svelte +1 -0
  219. package/dist/components/nav/breadcrumb/theme.js +5 -5
  220. package/dist/components/nav/navbar/Navbar.mdx +38 -0
  221. package/dist/components/nav/navbar/Navbar.svelte +1 -1
  222. package/dist/components/nav/navbar/NavbarItem.svelte +12 -9
  223. package/dist/components/nav/navbar/theme.js +6 -6
  224. package/dist/components/nav/rail/Rail.mdx +73 -0
  225. package/dist/components/nav/rail/Rail.stories.svelte +3 -7
  226. package/dist/components/nav/rail/Rail.svelte +1 -0
  227. package/dist/components/nav/rail/RailItem.svelte +12 -9
  228. package/dist/components/nav/rail/theme.js +13 -13
  229. package/dist/components/nav/rail/types.d.ts +4 -1
  230. package/dist/components/nav/tabs/Tabs.mdx +62 -0
  231. package/dist/components/nav/tabs/Tabs.stories.svelte +1 -1
  232. package/dist/components/nav/tabs/theme.js +4 -4
  233. package/dist/components/pill/Pill.mdx +32 -0
  234. package/dist/components/pill/Pill.stories.svelte +1 -1
  235. package/dist/components/pill/theme.d.ts +1 -1
  236. package/dist/components/pill/theme.js +1 -1
  237. package/dist/components/progress/LinearProgress.svelte +1 -1
  238. package/dist/components/progress/Progress.mdx +50 -0
  239. package/dist/components/progress/Progress.stories.svelte +6 -6
  240. package/dist/components/snackbar/Snackbar.mdx +52 -0
  241. package/dist/components/snackbar/Snackbar.stories.svelte +5 -5
  242. package/dist/components/snackbar/Snackbar.svelte +1 -1
  243. package/dist/components/snackbar/theme.d.ts +3 -3
  244. package/dist/components/snackbar/theme.js +4 -4
  245. package/dist/components/table/Table.stories.svelte +1 -0
  246. package/dist/components/table/TableHeader.svelte +1 -1
  247. package/dist/components/table/theme.js +3 -3
  248. package/dist/components/time/TimeField.stories.svelte +3 -2
  249. package/dist/components/time/TimeField.svelte +1 -1
  250. package/dist/components/time/TimepickerInput.svelte +1 -1
  251. package/dist/components/time/theme.js +5 -5
  252. package/dist/components/toolbar/Toolbar.stories.svelte +1 -0
  253. package/dist/components/toolbar/Toolbar.svelte +1 -1
  254. package/dist/components/toolbar/Toolbar.svelte.d.ts +1 -1
  255. package/dist/components/toolbar/theme.js +14 -14
  256. package/dist/components/typography/Typography.mdx +67 -0
  257. package/dist/components/typography/body/Body.stories.svelte +2 -1
  258. package/dist/components/typography/body/theme.d.ts +15 -12
  259. package/dist/components/typography/body/theme.js +19 -4
  260. package/dist/components/typography/body/types.d.ts +2 -1
  261. package/dist/components/typography/display/Display.stories.svelte +2 -1
  262. package/dist/components/typography/display/theme.d.ts +15 -12
  263. package/dist/components/typography/display/theme.js +19 -4
  264. package/dist/components/typography/display/types.d.ts +2 -1
  265. package/dist/components/typography/headline/Headline.stories.svelte +2 -1
  266. package/dist/components/typography/headline/theme.d.ts +15 -12
  267. package/dist/components/typography/headline/theme.js +19 -4
  268. package/dist/components/typography/headline/types.d.ts +2 -1
  269. package/dist/components/typography/kbd/Kbd.stories.svelte +2 -1
  270. package/dist/components/typography/kbd/theme.d.ts +4 -4
  271. package/dist/components/typography/kbd/theme.js +2 -2
  272. package/dist/components/typography/label/Label.stories.svelte +2 -1
  273. package/dist/components/typography/label/theme.d.ts +15 -12
  274. package/dist/components/typography/label/theme.js +19 -4
  275. package/dist/components/typography/label/types.d.ts +2 -1
  276. package/dist/components/typography/title/Title.stories.svelte +2 -1
  277. package/dist/components/typography/title/theme.d.ts +15 -12
  278. package/dist/components/typography/title/theme.js +19 -4
  279. package/dist/components/typography/title/types.d.ts +2 -1
  280. package/dist/index.css +1 -0
  281. package/dist/styles/component.css +78 -175
  282. package/dist/styles/elevation.css +9 -0
  283. package/dist/styles/spacing.css +29 -0
  284. package/dist/styles/typescale.css +635 -20
  285. package/dist/utils/Layer.svelte +1 -1
  286. package/dist/utils/icon/Icon.svelte +8 -6
  287. package/package.json +19 -15
  288. package/dist/components/containers/stack/HStack.svelte +0 -23
  289. package/dist/components/containers/stack/HStack.svelte.d.ts +0 -5
  290. package/dist/components/containers/stack/VStack.svelte +0 -23
  291. package/dist/components/containers/stack/VStack.svelte.d.ts +0 -5
  292. package/dist/components/containers/stack/index.d.ts +0 -4
  293. package/dist/components/containers/stack/theme.d.ts +0 -66
  294. package/dist/components/containers/stack/theme.js +0 -26
  295. package/dist/components/containers/stack/types.d.ts +0 -12
  296. /package/dist/components/containers/{stack → carousel}/types.js +0 -0
@@ -0,0 +1,71 @@
1
+ import { Meta, Title, Subtitle, Canvas, Controls } from '@storybook/addon-docs/blocks';
2
+ import * as SheetStories from './SideSheet.stories.svelte';
3
+
4
+ <Meta of={SheetStories} />
5
+
6
+ <Title />
7
+
8
+ <Subtitle>Side sheets show secondary content anchored to the side of the screen.</Subtitle>
9
+
10
+ [M3 spec](https://m3.material.io/components/side-sheets/specs) ·
11
+ `import { SideSheet } from '@noxlovette/material';`
12
+
13
+ <Canvas of={SheetStories.Modal} />
14
+
15
+ <Controls of={SheetStories.Modal} />
16
+
17
+ ## Modal or standard
18
+
19
+ - **`modal`** (default) opens at the window's end edge above a scrim, and blocks the app until
20
+ it's closed with its close button, Escape, or a scrim tap. Use it on compact and medium windows,
21
+ or when the content needs full attention. It is a native `<dialog>` opened with `showModal()`,
22
+ so the browser handles the focus trap, top-layer stacking and making the page behind it inert.
23
+ - **`standard`** is part of the layout. Make it the last child of a full-height row. It opens by
24
+ widening, so the content beside it reflows instead of being covered, and everything stays
25
+ usable. Use it on expanded windows, for filters or details that people use alongside the main
26
+ content.
27
+
28
+ ```svelte
29
+ <div class="flex h-dvh">
30
+ <main class="min-w-spacing-0 flex-1">…</main>
31
+ <SideSheet variant="standard" headline="Filters" bind:open>…</SideSheet>
32
+ </div>
33
+ ```
34
+
35
+ <Canvas of={SheetStories.Standard} />
36
+
37
+ ## Anatomy
38
+
39
+ - **Header:** an optional back button (`onback`), the `headline`, and a close button. The headline
40
+ is `title-large` and names the sheet for assistive technology.
41
+ - **Content:** your `children`, padded 24dp. It scrolls inside the sheet.
42
+ - **Actions** (optional): the `actions` snippet, pinned to the bottom and left-aligned below a
43
+ divider. Put the most important action first, e.g. Save before Cancel.
44
+
45
+ Show `onback` only when the sheet navigates within itself. The header's start padding then drops
46
+ from 24dp to 16dp.
47
+
48
+ <Canvas of={SheetStories.ModalWithBackButton} />
49
+
50
+ ## Size and shape
51
+
52
+ Side sheets are 256dp wide by default and never wider than 400dp, and they span the full height.
53
+
54
+ | | Docked | `detached` |
55
+ | -------- | -------------------------------------------------------------------- | --------------------------------- |
56
+ | Modal | `surface-container-low`, elevation 1, 16dp corners on the inner side | 16dp from the edges, 16dp corners |
57
+ | Standard | `surface`, elevation 0, square, divider on the inner edge | 16dp from the edges, 16dp corners |
58
+
59
+ `divider={false}` removes the edge divider and the one above the actions.
60
+
61
+ <Canvas of={SheetStories.StandardDetached} />
62
+
63
+ ## Motion
64
+
65
+ A modal sheet slides in from the edge on the spatial spring, without fading, and stays mounted
66
+ until it has slid out. A standard sheet animates its width on the same spring, so the content
67
+ beside it reflows as it slides.
68
+
69
+ ## Not yet supported
70
+
71
+ Right-to-left layouts: the modal sheet sits at the end edge, but always slides in from the right.
@@ -1,41 +1,137 @@
1
1
  <!--
2
- SideSheet is controlled with `bind:open`: it dismisses itself (close button, Esc, backdrop
3
- click) by setting `open` to false, and stays mounted until its exit animation finishes.
2
+ SideSheet is controlled with `bind:open`. A modal sheet closes itself (close button, Esc,
3
+ scrim tap). A standard sheet sits in the layout: put it last in a full-height row and the
4
+ content beside it reflows as it opens and closes.
4
5
  -->
5
6
  <script module lang="ts">
6
7
  import { defineMeta } from '@storybook/addon-svelte-csf';
7
8
  import SideSheet from './SideSheet.svelte';
8
9
  import Button from '../../buttons/Button.svelte';
9
- import { Body } from '../../../index.js';
10
+ import Checkbox from '../../forms/checkbox/Checkbox.svelte';
11
+ import List from '../list/List.svelte';
12
+ import ListItem from '../list/ListItem.svelte';
10
13
 
11
14
  const { Story } = defineMeta({
12
15
  title: 'Containers/Side Sheet',
13
16
  component: SideSheet,
14
17
  argTypes: {
15
- headline: { control: 'text' }
18
+ headline: { control: 'text' },
19
+ width: { control: { type: 'range', min: 256, max: 400, step: 8 } },
20
+ detached: { control: 'boolean' },
21
+ divider: { control: 'boolean' }
16
22
  },
17
- args: {
18
- headline: 'Details'
19
- }
23
+ args: { headline: 'Filters', width: 256, detached: false, divider: true }
20
24
  });
25
+
26
+ const filters = ['In stock', 'Free delivery', 'On sale', 'Recycled materials', 'Local sellers'];
21
27
  </script>
22
28
 
23
29
  <script lang="ts">
24
30
  let playgroundOpen = $state(false);
31
+ let detailOpen = $state(false);
32
+ let detailPage = $state<'list' | 'item'>('list');
33
+ let standardOpen = $state(true);
34
+ let detachedOpen = $state(true);
25
35
  </script>
26
36
 
27
- <Story name="Playground">
37
+ {#snippet filterList()}
38
+ <div class="gap-spacing-200 flex flex-col">
39
+ {#each filters as f (f)}
40
+ <Checkbox labelText={f} />
41
+ {/each}
42
+ </div>
43
+ {/snippet}
44
+
45
+ {#snippet saveCancel(close: () => void)}
46
+ <Button onclick={close}>Save</Button>
47
+ <Button variant="outlined" onclick={close}>Cancel</Button>
48
+ {/snippet}
49
+
50
+ <Story name="Modal">
28
51
  {#snippet template(args)}
29
- <div class="p-6">
30
- <Button onclick={() => (playgroundOpen = true)}>Show side sheet</Button>
31
- <SideSheet headline={args.headline} bind:open={playgroundOpen}>
32
- <div class="flex flex-col gap-3 px-6 pb-6">
33
- <Body class="text-md-sys-color-on-surface-variant">
34
- Supplementary content and actions, anchored to the edge of the screen while the main
35
- content stays visible.
36
- </Body>
37
- </div>
52
+ <div class="p-spacing-300">
53
+ <Button onclick={() => (playgroundOpen = true)}>Show filters</Button>
54
+ <SideSheet
55
+ headline={args.headline}
56
+ width={args.width}
57
+ detached={args.detached}
58
+ divider={args.divider}
59
+ bind:open={playgroundOpen}
60
+ >
61
+ {@render filterList()}
62
+ {#snippet actions()}
63
+ {@render saveCancel(() => (playgroundOpen = false))}
64
+ {/snippet}
38
65
  </SideSheet>
39
66
  </div>
40
67
  {/snippet}
41
68
  </Story>
69
+
70
+ <Story name="Modal with back button" asChild>
71
+ <div class="p-spacing-300">
72
+ <Button
73
+ onclick={() => {
74
+ detailPage = 'list';
75
+ detailOpen = true;
76
+ }}>Show orders</Button
77
+ >
78
+ <SideSheet
79
+ headline={detailPage === 'list' ? 'Orders' : 'Order 1042'}
80
+ width={360}
81
+ bind:open={detailOpen}
82
+ onback={detailPage === 'item' ? () => (detailPage = 'list') : undefined}
83
+ >
84
+ {#if detailPage === 'list'}
85
+ <List class="-mx-spacing-300">
86
+ {#each [1042, 1041, 1040] as n (n)}
87
+ <ListItem
88
+ headline="Order {n}"
89
+ supporting="3 items · delivered"
90
+ onclick={() => (detailPage = 'item')}
91
+ />
92
+ {/each}
93
+ </List>
94
+ {:else}
95
+ <p class="md-sys-typescale-body-medium text-md-sys-color-on-surface-variant">
96
+ The back button appears when the sheet navigates within itself, and the header's start
97
+ padding drops from 24dp to 16dp.
98
+ </p>
99
+ {/if}
100
+ </SideSheet>
101
+ </div>
102
+ </Story>
103
+
104
+ <Story name="Standard" asChild>
105
+ <div class="bg-md-sys-color-surface flex h-[36rem] w-full">
106
+ <main class="min-w-spacing-0 gap-spacing-200 p-spacing-300 flex flex-1 flex-col items-start">
107
+ <p class="md-sys-typescale-body-large max-w-md">
108
+ The standard sheet is part of the layout: this column reflows as it opens and closes, and
109
+ stays usable while it's open.
110
+ </p>
111
+ <Button variant="tonal" onclick={() => (standardOpen = !standardOpen)}>
112
+ {standardOpen ? 'Hide filters' : 'Show filters'}
113
+ </Button>
114
+ </main>
115
+ <SideSheet variant="standard" headline="Filters" width={320} bind:open={standardOpen}>
116
+ {@render filterList()}
117
+ {#snippet actions()}
118
+ {@render saveCancel(() => (standardOpen = false))}
119
+ {/snippet}
120
+ </SideSheet>
121
+ </div>
122
+ </Story>
123
+
124
+ <Story name="Standard detached" asChild>
125
+ <div class="bg-md-sys-color-surface-container flex h-[36rem] w-full">
126
+ <main class="min-w-spacing-0 gap-spacing-200 p-spacing-300 flex flex-1 flex-col items-start">
127
+ <Button variant="tonal" onclick={() => (detachedOpen = !detachedOpen)}>
128
+ {detachedOpen ? 'Hide details' : 'Show details'}
129
+ </Button>
130
+ </main>
131
+ <SideSheet variant="standard" detached headline="Details" width={320} bind:open={detachedOpen}>
132
+ <p class="md-sys-typescale-body-medium text-md-sys-color-on-surface-variant">
133
+ Detached sheets float 16dp from the window edges with 16dp corners.
134
+ </p>
135
+ </SideSheet>
136
+ </div>
137
+ </Story>
@@ -1,25 +1,59 @@
1
1
  <!--
2
2
  @component
3
- Side sheets are supplementary surfaces which are anchored to the edge of the screen.
3
+ Side sheets show secondary content anchored to the side of the screen.
4
4
 
5
- Like BottomSheet, SideSheet owns its own overlay: it renders as a native `<dialog>` pinned to
6
- the right edge of the screen, dims the rest of the app behind a backdrop, and slides in/out.
7
- Visibility is controlled by `bind:open`; the sheet stays mounted until its exit animation
8
- finishes. Conditionally rendering it ({#if open}<SideSheet close={…} />{/if}) still works, but
9
- skips the exit animation.
5
+ - **Modal** (default): a native `<dialog>` at the window's end edge above a scrim. It closes on
6
+ its close button, Esc, or a scrim tap, and stays mounted until its exit animation finishes.
7
+ - **Standard**: part of the layout. Put it last in a full-height row; it opens by widening, so
8
+ the content beside it reflows, and nothing else is blocked.
10
9
 
11
- @see https://m3.material.io/components/side-sheets/guidelines
10
+ Visibility is controlled with `bind:open`.
11
+
12
+ @see https://m3.material.io/components/side-sheets/specs
12
13
  -->
13
14
  <script lang="ts">
14
- import type { SideSheetProps } from './types.js';
15
+ import type { Attachment } from 'svelte/attachments';
16
+ import { untrack } from 'svelte';
17
+ import clsx from 'clsx';
15
18
  import ButtonIcon from '../../buttons/ButtonIcon.svelte';
19
+ import Divider from '../divider/Divider.svelte';
16
20
  import { enterExit, Presence } from '../../../animation/index.js';
21
+ import { sideSheet } from './theme.js';
22
+ import type { SideSheetProps } from './types.js';
17
23
 
18
- let { headline, children, open = $bindable(true), close }: SideSheetProps = $props();
24
+ const MAX_WIDTH = 400;
19
25
 
20
- const sheet = new Presence(() => open);
26
+ let {
27
+ headline,
28
+ children,
29
+ actions,
30
+ variant = 'modal',
31
+ open = $bindable(true),
32
+ width = 256,
33
+ detached = false,
34
+ divider = true,
35
+ onback,
36
+ close,
37
+ class: className
38
+ }: SideSheetProps = $props();
21
39
 
22
- const showModal = (node: HTMLDialogElement) => node.showModal();
40
+ const id = $props.id();
41
+ const sheet = new Presence(() => open && variant === 'modal');
42
+ const cls = $derived(sideSheet({ variant, detached, divider, back: !!onback }));
43
+ const sheetWidth = $derived(Math.min(width, MAX_WIDTH));
44
+ // A detached standard sheet takes its 16dp margins with it.
45
+ const slotWidth = $derived(open ? sheetWidth + (detached ? 32 : 0) : 0);
46
+
47
+ // untrack: re-running this attachment would call showModal() on an open dialog. The dialog
48
+ // unmounts after its exit instead of calling close(), so focus is returned to the opener here.
49
+ const showModal: Attachment<HTMLDialogElement> = (node) =>
50
+ untrack(() => {
51
+ const opener = document.activeElement;
52
+ node.showModal();
53
+ return () => {
54
+ if (opener instanceof HTMLElement && opener.isConnected) opener.focus();
55
+ };
56
+ });
23
57
 
24
58
  const dismiss = () => {
25
59
  open = false;
@@ -27,61 +61,74 @@ skips the exit animation.
27
61
  };
28
62
  </script>
29
63
 
30
- {#if sheet.mounted}
31
- <dialog
32
- class="bg-md-sys-color-surface-container-low text-md-sys-color-on-surface fixed inset-y-0 right-0 left-auto m-0 h-full w-full max-w-sm rounded-l-md"
33
- data-state={open ? 'open' : 'closed'}
34
- {@attach showModal}
35
- {@attach sheet.attach(enterExit.sideSheet)}
36
- oncancel={(e) => {
37
- e.preventDefault();
38
- dismiss();
39
- }}
40
- onmousedown={(e) => {
41
- if (e.target != e.currentTarget) return;
42
- dismiss();
43
- }}
44
- >
45
- <div class="flex h-full flex-col">
46
- <div class="flex items-center justify-between p-6">
47
- <span class="md-sys-typescale-title-large text-md-sys-color-on-surface-variant"
48
- >{headline}</span
49
- >
50
- <ButtonIcon type="button" variant="text" iconProps={{ name: 'close' }} onclick={dismiss} />
64
+ {#snippet surface()}
65
+ <div class={cls.header()}>
66
+ {#if onback}
67
+ <ButtonIcon
68
+ type="button"
69
+ variant="standard"
70
+ class={cls.iconButton()}
71
+ iconProps={{ name: 'arrow_back' }}
72
+ aria-label="Back"
73
+ onclick={onback}
74
+ />
75
+ {/if}
76
+ <h2 id="{id}-headline" class={cls.headline()}>{headline}</h2>
77
+ <ButtonIcon
78
+ type="button"
79
+ variant="standard"
80
+ class={cls.iconButton()}
81
+ iconProps={{ name: 'close' }}
82
+ aria-label="Close"
83
+ onclick={dismiss}
84
+ />
85
+ </div>
86
+ <div class={cls.body()}>
87
+ {@render children()}
88
+ </div>
89
+ {#if actions}
90
+ {#if divider}
91
+ <Divider />
92
+ {/if}
93
+ <div class={cls.actions()}>
94
+ {@render actions()}
95
+ </div>
96
+ {/if}
97
+ {/snippet}
98
+
99
+ {#if variant === 'modal'}
100
+ {#if sheet.mounted}
101
+ <dialog
102
+ class={cls.base({ class: clsx(className) })}
103
+ style:width="{sheetWidth}px"
104
+ aria-labelledby="{id}-headline"
105
+ data-state={open ? 'open' : 'closed'}
106
+ {@attach showModal}
107
+ {@attach sheet.attach(enterExit.sideSheet)}
108
+ oncancel={(e) => {
109
+ e.preventDefault();
110
+ dismiss();
111
+ }}
112
+ onmousedown={(e) => {
113
+ // Only the ::backdrop targets the dialog itself.
114
+ if (e.target === e.currentTarget) dismiss();
115
+ }}
116
+ >
117
+ <div class={cls.container()}>
118
+ {@render surface()}
51
119
  </div>
52
- {@render children()}
120
+ </dialog>
121
+ {/if}
122
+ {:else}
123
+ <aside
124
+ class={cls.base({ class: clsx(className) })}
125
+ style:width="{slotWidth}px"
126
+ aria-labelledby="{id}-headline"
127
+ aria-hidden={!open}
128
+ inert={!open}
129
+ >
130
+ <div class={cls.container()} style:width="{sheetWidth}px">
131
+ {@render surface()}
53
132
  </div>
54
- </dialog>
133
+ </aside>
55
134
  {/if}
56
-
57
- <style>
58
- dialog::backdrop {
59
- background-color: rgba(0, 0, 0, 0.5);
60
- animation: backdrop var(--md-sys-motion-duration-effects-spring)
61
- var(--md-sys-motion-timing-function-effects-spring);
62
- }
63
- dialog[data-state='closed'] {
64
- pointer-events: none;
65
- }
66
- dialog[data-state='closed']::backdrop {
67
- background-color: transparent;
68
- animation: backdropReverse var(--md-sys-motion-duration-fast-effects-spring)
69
- var(--md-sys-motion-timing-function-fast-effects-spring);
70
- }
71
- @keyframes backdrop {
72
- from {
73
- background-color: transparent;
74
- }
75
- to {
76
- background-color: rgba(0, 0, 0, 0.5);
77
- }
78
- }
79
- @keyframes backdropReverse {
80
- from {
81
- background-color: rgba(0, 0, 0, 0.5);
82
- }
83
- to {
84
- background-color: transparent;
85
- }
86
- }
87
- </style>
@@ -1,14 +1,15 @@
1
1
  import type { SideSheetProps } from './types.js';
2
2
  /**
3
- * Side sheets are supplementary surfaces which are anchored to the edge of the screen.
3
+ * Side sheets show secondary content anchored to the side of the screen.
4
4
  *
5
- * Like BottomSheet, SideSheet owns its own overlay: it renders as a native `<dialog>` pinned to
6
- * the right edge of the screen, dims the rest of the app behind a backdrop, and slides in/out.
7
- * Visibility is controlled by `bind:open`; the sheet stays mounted until its exit animation
8
- * finishes. Conditionally rendering it ({#if open}<SideSheet close={…} />{/if}) still works, but
9
- * skips the exit animation.
5
+ * - **Modal** (default): a native `<dialog>` at the window's end edge above a scrim. It closes on
6
+ * its close button, Esc, or a scrim tap, and stays mounted until its exit animation finishes.
7
+ * - **Standard**: part of the layout. Put it last in a full-height row; it opens by widening, so
8
+ * the content beside it reflows, and nothing else is blocked.
10
9
  *
11
- * @see https://m3.material.io/components/side-sheets/guidelines
10
+ * Visibility is controlled with `bind:open`.
11
+ *
12
+ * @see https://m3.material.io/components/side-sheets/specs
12
13
  */
13
14
  declare const SideSheet: import("svelte").Component<SideSheetProps, {}, "open">;
14
15
  type SideSheet = ReturnType<typeof SideSheet>;
@@ -1,2 +1,3 @@
1
+ export * from './theme.js';
1
2
  export * from './types.js';
2
3
  export { default as SideSheet } from './SideSheet.svelte';
@@ -1,5 +1,6 @@
1
1
  // THIS FILE IS AUTO-GENERATED BY scripts/generate-components-index.ts - DO NOT EDIT MANUALLY
2
2
  // re-export TS/JS modules from this folder
3
+ export * from './theme.js';
3
4
  export * from './types.js';
4
5
  // default exports for components in this folder
5
6
  export { default as SideSheet } from './SideSheet.svelte';
@@ -0,0 +1,108 @@
1
+ import { type VariantProps } from 'tailwind-variants';
2
+ export type SideSheetVariants = VariantProps<typeof sideSheet>;
3
+ export declare const sideSheet: import("tailwind-variants").TVReturnType<{
4
+ variant: {
5
+ modal: {
6
+ base: string[];
7
+ container: string;
8
+ };
9
+ standard: {
10
+ base: string;
11
+ container: string;
12
+ };
13
+ };
14
+ detached: {
15
+ true: {
16
+ container: string;
17
+ };
18
+ false: {};
19
+ };
20
+ divider: {
21
+ true: {};
22
+ false: {};
23
+ };
24
+ back: {
25
+ true: {
26
+ header: string;
27
+ };
28
+ false: {};
29
+ };
30
+ }, {
31
+ base: string;
32
+ container: string;
33
+ header: string;
34
+ iconButton: string;
35
+ headline: string;
36
+ body: string;
37
+ actions: string;
38
+ }, undefined, {
39
+ variant: {
40
+ modal: {
41
+ base: string[];
42
+ container: string;
43
+ };
44
+ standard: {
45
+ base: string;
46
+ container: string;
47
+ };
48
+ };
49
+ detached: {
50
+ true: {
51
+ container: string;
52
+ };
53
+ false: {};
54
+ };
55
+ divider: {
56
+ true: {};
57
+ false: {};
58
+ };
59
+ back: {
60
+ true: {
61
+ header: string;
62
+ };
63
+ false: {};
64
+ };
65
+ }, {
66
+ base: string;
67
+ container: string;
68
+ header: string;
69
+ iconButton: string;
70
+ headline: string;
71
+ body: string;
72
+ actions: string;
73
+ }, import("tailwind-variants").TVReturnTypeLike<{
74
+ variant: {
75
+ modal: {
76
+ base: string[];
77
+ container: string;
78
+ };
79
+ standard: {
80
+ base: string;
81
+ container: string;
82
+ };
83
+ };
84
+ detached: {
85
+ true: {
86
+ container: string;
87
+ };
88
+ false: {};
89
+ };
90
+ divider: {
91
+ true: {};
92
+ false: {};
93
+ };
94
+ back: {
95
+ true: {
96
+ header: string;
97
+ };
98
+ false: {};
99
+ };
100
+ }, {
101
+ base: string;
102
+ container: string;
103
+ header: string;
104
+ iconButton: string;
105
+ headline: string;
106
+ body: string;
107
+ actions: string;
108
+ }>>;
@@ -0,0 +1,72 @@
1
+ import { tv } from 'tailwind-variants';
2
+ /*
3
+ M3 side sheet (https://m3.material.io/components/side-sheets/specs). Full height, 256dp wide by
4
+ default and never wider than 400dp. 24dp start/end padding (16dp before a back button), 12dp
5
+ between the header's elements, `title-large` headline in `on-surface-variant`. Bottom actions
6
+ sit 16dp below a divider and 24dp above the bottom edge.
7
+
8
+ - Standard: `surface`, elevation 0, square, an `outline-variant` divider along its inner edge.
9
+ It sits in the layout and the content beside it reflows as it opens.
10
+ - Modal: `surface-container-low`, elevation 1, 16dp corners on its inner side, above a scrim.
11
+ - Detached (either): 16dp from the window edges, 16dp corners all round.
12
+
13
+ The header's icon buttons are 40dp around a 24dp icon; `-m-spacing-100` lines the icons (not the
14
+ buttons) up with the padding, as the spec measures it.
15
+ */
16
+ export const sideSheet = tv({
17
+ slots: {
18
+ base: 'p-spacing-0',
19
+ container: 'flex h-full flex-col overflow-hidden',
20
+ header: 'flex shrink-0 items-center gap-spacing-150 ps-spacing-300 pe-spacing-300 pt-spacing-300',
21
+ iconButton: '-m-spacing-100 shrink-0',
22
+ headline: 'md-sys-typescale-title-large text-md-sys-color-on-surface-variant min-w-spacing-0 flex-1',
23
+ body: 'min-h-spacing-0 flex-1 overflow-y-auto overscroll-contain px-spacing-300 py-spacing-300',
24
+ actions: 'flex min-h-spacing-900 shrink-0 items-center justify-start gap-spacing-100 px-spacing-300 pt-spacing-200 pb-spacing-300'
25
+ },
26
+ variants: {
27
+ variant: {
28
+ modal: {
29
+ base: [
30
+ 'fixed inset-y-spacing-0 start-auto end-spacing-0 m-spacing-0 h-dvh max-h-none w-full overflow-visible bg-transparent outline-none',
31
+ 'backdrop:bg-md-sys-color-scrim/32 backdrop:transition-[background-color] backdrop:md-sys-motion-effects',
32
+ 'starting:backdrop:bg-transparent data-[state=closed]:pointer-events-none data-[state=closed]:backdrop:bg-transparent'
33
+ ],
34
+ container: 'bg-md-sys-color-surface-container-low text-md-sys-color-on-surface shadow-elevation-1 rounded-s-2xl'
35
+ },
36
+ standard: {
37
+ // The wrapper animates its width, so the content beside it reflows; the sheet itself
38
+ // keeps its width and stays pinned to the wrapper's end edge, so it appears to slide in.
39
+ base: 'md-sys-motion-spatial relative h-full shrink-0 overflow-clip transition-[width]',
40
+ container: 'bg-md-sys-color-surface text-md-sys-color-on-surface absolute inset-y-spacing-0 end-spacing-0'
41
+ }
42
+ },
43
+ detached: {
44
+ true: { container: 'rounded-2xl' },
45
+ false: {}
46
+ },
47
+ divider: { true: {}, false: {} },
48
+ back: { true: { header: 'ps-spacing-200' }, false: {} }
49
+ },
50
+ compoundVariants: [
51
+ {
52
+ variant: 'modal',
53
+ detached: true,
54
+ class: {
55
+ base: 'inset-y-spacing-200 end-spacing-200 h-[calc(100dvh-2rem)] max-w-[calc(100%-2rem)]'
56
+ }
57
+ },
58
+ { variant: 'modal', detached: false, class: { base: 'max-w-full' } },
59
+ {
60
+ variant: 'standard',
61
+ detached: true,
62
+ class: { container: 'inset-y-spacing-200 end-spacing-200' }
63
+ },
64
+ {
65
+ variant: 'standard',
66
+ detached: false,
67
+ divider: true,
68
+ class: { container: 'border-md-sys-color-outline-variant border-s' }
69
+ }
70
+ ],
71
+ defaultVariants: { variant: 'modal', detached: false, divider: true, back: false }
72
+ });