@cavulsqa/create 2.9.2 → 2.10.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 (209) hide show
  1. package/README.md +12 -1
  2. package/bin/create.mjs +18 -25
  3. package/lib/templates.mjs +56 -0
  4. package/package.json +13 -5
  5. package/templates/f7-app/CLAUDE.md +3 -0
  6. package/templates/f7-app/auto-imports.d.ts +15 -0
  7. package/templates/f7-app/package.json +6 -3
  8. package/templates/f7-app/src/App.vue +1 -0
  9. package/templates/f7-app/src/env.d.ts +1 -0
  10. package/templates/f7-app/src/main.ts +3 -0
  11. package/templates/f7-app/src/modules/demo/router/routes/demo.routes.ts +4 -16
  12. package/templates/f7-app/src/modules/home/router/routes/home.routes.ts +3 -11
  13. package/templates/f7-app/src/modules/settings/router/routes/settings.routes.ts +2 -6
  14. package/templates/f7-app/src/plugins/bootstrapError.ts +2 -0
  15. package/templates/f7-app/src/plugins/recorder.plugin.ts +155 -0
  16. package/templates/f7-app/src/router/global/global.routes.ts +2 -6
  17. package/templates/f7-app/src/shared/composables/useNavigationGuard.ts +48 -0
  18. package/templates/f7-app/src/shared/recorder/capacitorSink.ts +124 -0
  19. package/templates/f7-app/src/shared/utils/lazyRoute.ts +25 -0
  20. package/templates/f7-app/tests/lazyRoute.test.ts +30 -0
  21. package/templates/f7-app/tests/navigationGuard.test.ts +82 -0
  22. package/templates/f7-app/vite.config.ts +1 -0
  23. package/templates/m3e-app/.claude/rules/data-fetching.md +68 -0
  24. package/templates/m3e-app/.claude/rules/database.md +106 -0
  25. package/templates/m3e-app/.claude/rules/m3e-ui.md +48 -0
  26. package/templates/m3e-app/.claude/rules/modules.md +43 -0
  27. package/templates/m3e-app/.claude/rules/native.md +60 -0
  28. package/templates/m3e-app/.claude/skills/m3-expressive/SKILL.md +68 -0
  29. package/templates/m3e-app/.claude/skills/m3-expressive/color.md +44 -0
  30. package/templates/m3e-app/.claude/skills/m3-expressive/components.md +325 -0
  31. package/templates/m3e-app/.claude/skills/m3-expressive/layout.md +42 -0
  32. package/templates/m3e-app/.claude/skills/m3-expressive/motion.md +51 -0
  33. package/templates/m3e-app/.claude/skills/m3-expressive/shapes-type.md +48 -0
  34. package/templates/m3e-app/.claude/skills/m3-expressive/sources.md +53 -0
  35. package/templates/m3e-app/.claude/skills/module-architecture/SKILL.md +65 -0
  36. package/templates/m3e-app/.claude/skills/module-architecture/file-templates.md +178 -0
  37. package/templates/m3e-app/.claude/skills/reactive-data/SKILL.md +90 -0
  38. package/templates/m3e-app/.claude/skills/reactive-data/testing.md +55 -0
  39. package/templates/m3e-app/.env.example +32 -0
  40. package/templates/m3e-app/CLAUDE.md +93 -0
  41. package/templates/m3e-app/auto-imports.d.ts +851 -0
  42. package/templates/m3e-app/capacitor.config.ts +44 -0
  43. package/templates/m3e-app/components.d.ts +252 -0
  44. package/templates/m3e-app/index.html +16 -0
  45. package/templates/m3e-app/package.json +107 -0
  46. package/templates/m3e-app/src/App.vue +129 -0
  47. package/templates/m3e-app/src/app/pragmas.config.ts +35 -0
  48. package/templates/m3e-app/src/app/scroll.config.ts +20 -0
  49. package/templates/m3e-app/src/app/storage.config.ts +65 -0
  50. package/templates/m3e-app/src/app/tabs.ts +33 -0
  51. package/templates/m3e-app/src/app/theme.config.ts +51 -0
  52. package/templates/m3e-app/src/assets/css/app.css +18 -0
  53. package/templates/m3e-app/src/assets/css/base.css +53 -0
  54. package/templates/m3e-app/src/assets/css/layout/container-transform.css +113 -0
  55. package/templates/m3e-app/src/assets/css/layout/shell.css +69 -0
  56. package/templates/m3e-app/src/assets/css/layout/transitions.css +121 -0
  57. package/templates/m3e-app/src/assets/css/m3e.css +6 -0
  58. package/templates/m3e-app/src/assets/css/theme/framework7.css +18 -0
  59. package/templates/m3e-app/src/assets/css/theme/tailwind.css +365 -0
  60. package/templates/m3e-app/src/domains/benchmark/benchmark.dataset.ts +209 -0
  61. package/templates/m3e-app/src/domains/benchmark/benchmark.suite.ts +662 -0
  62. package/templates/m3e-app/src/domains/sales/sales.repository.ts +458 -0
  63. package/templates/m3e-app/src/env.d.ts +40 -0
  64. package/templates/m3e-app/src/locales/ar.json +1260 -0
  65. package/templates/m3e-app/src/locales/en.json +1260 -0
  66. package/templates/m3e-app/src/locales/fr.json +1260 -0
  67. package/templates/m3e-app/src/main.ts +41 -0
  68. package/templates/m3e-app/src/modules/demo/components/DemoBenchmark.vue +119 -0
  69. package/templates/m3e-app/src/modules/demo/components/DemoBusLog.vue +63 -0
  70. package/templates/m3e-app/src/modules/demo/components/DemoCreateOrderSheet.vue +203 -0
  71. package/templates/m3e-app/src/modules/demo/components/DemoMetricsSheet.vue +75 -0
  72. package/templates/m3e-app/src/modules/demo/components/DemoOrderList.vue +75 -0
  73. package/templates/m3e-app/src/modules/demo/components/DemoPipelineBenchmark.vue +65 -0
  74. package/templates/m3e-app/src/modules/demo/components/DemoStatCards.vue +71 -0
  75. package/templates/m3e-app/src/modules/demo/composables/useBenchmark.ts +139 -0
  76. package/templates/m3e-app/src/modules/demo/composables/useOrderStatus.ts +64 -0
  77. package/templates/m3e-app/src/modules/demo/composables/useReactiveDemo.ts +171 -0
  78. package/templates/m3e-app/src/modules/demo/router/routes/demo.routes.ts +22 -0
  79. package/templates/m3e-app/src/modules/demo/views/DemoView.vue +210 -0
  80. package/templates/m3e-app/src/modules/demo/views/OrderDetailView.vue +136 -0
  81. package/templates/m3e-app/src/modules/demo/views/OrderSearchView.vue +74 -0
  82. package/templates/m3e-app/src/modules/gallery/components/GalleryBlock.vue +16 -0
  83. package/templates/m3e-app/src/modules/gallery/components/GalleryCarouselTile.vue +71 -0
  84. package/templates/m3e-app/src/modules/gallery/components/GalleryCustomerForm.vue +76 -0
  85. package/templates/m3e-app/src/modules/gallery/components/GalleryProofOfDelivery.vue +69 -0
  86. package/templates/m3e-app/src/modules/gallery/components/agenda/AgendaDay.vue +106 -0
  87. package/templates/m3e-app/src/modules/gallery/components/agenda/AgendaMonth.vue +40 -0
  88. package/templates/m3e-app/src/modules/gallery/components/agenda/AgendaVisitList.vue +45 -0
  89. package/templates/m3e-app/src/modules/gallery/components/chat/ChatAttachSheet.vue +106 -0
  90. package/templates/m3e-app/src/modules/gallery/components/chat/ChatContactPicker.vue +85 -0
  91. package/templates/m3e-app/src/modules/gallery/components/chat/ChatInviteComposer.vue +131 -0
  92. package/templates/m3e-app/src/modules/gallery/components/chat/ChatPollComposer.vue +148 -0
  93. package/templates/m3e-app/src/modules/gallery/components/chat/ChatRecentPhotos.vue +62 -0
  94. package/templates/m3e-app/src/modules/gallery/components/inputs/InputsAccount.vue +79 -0
  95. package/templates/m3e-app/src/modules/gallery/components/inputs/InputsTags.vue +53 -0
  96. package/templates/m3e-app/src/modules/gallery/components/inputs/InputsVerification.vue +73 -0
  97. package/templates/m3e-app/src/modules/gallery/components/inputs/PasswordStrength.vue +37 -0
  98. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryButtons.vue +128 -0
  99. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryCarousels.vue +236 -0
  100. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryCharts.vue +84 -0
  101. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryFabs.vue +70 -0
  102. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryInputs.vue +219 -0
  103. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryNavigation.vue +141 -0
  104. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryOverlays.vue +368 -0
  105. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryPickers.vue +175 -0
  106. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryProgress.vue +118 -0
  107. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryScale.vue +103 -0
  108. package/templates/m3e-app/src/modules/gallery/components/sections/GallerySelection.vue +219 -0
  109. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryShapes.vue +48 -0
  110. package/templates/m3e-app/src/modules/gallery/components/sections/GallerySurfaces.vue +213 -0
  111. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryTables.vue +185 -0
  112. package/templates/m3e-app/src/modules/gallery/components/surfaces/SurfacesContainerTransform.vue +46 -0
  113. package/templates/m3e-app/src/modules/gallery/composables/chatTeam.ts +18 -0
  114. package/templates/m3e-app/src/modules/gallery/composables/composeKind.ts +2 -0
  115. package/templates/m3e-app/src/modules/gallery/composables/currentPosition.ts +35 -0
  116. package/templates/m3e-app/src/modules/gallery/composables/demoOrders.ts +29 -0
  117. package/templates/m3e-app/src/modules/gallery/composables/featuredStories.ts +73 -0
  118. package/templates/m3e-app/src/modules/gallery/composables/fieldFormats.ts +36 -0
  119. package/templates/m3e-app/src/modules/gallery/composables/openExternal.ts +11 -0
  120. package/templates/m3e-app/src/modules/gallery/composables/routePlan.ts +90 -0
  121. package/templates/m3e-app/src/modules/gallery/composables/useAgenda.ts +61 -0
  122. package/templates/m3e-app/src/modules/gallery/composables/useChatCustomers.ts +31 -0
  123. package/templates/m3e-app/src/modules/gallery/composables/useChatDemo.ts +299 -0
  124. package/templates/m3e-app/src/modules/gallery/composables/useGallerySections.ts +230 -0
  125. package/templates/m3e-app/src/modules/gallery/composables/useLocalAttachments.ts +71 -0
  126. package/templates/m3e-app/src/modules/gallery/composables/useLocationShare.ts +38 -0
  127. package/templates/m3e-app/src/modules/gallery/composables/usePhotoScenes.ts +82 -0
  128. package/templates/m3e-app/src/modules/gallery/composables/useTreeDemo.ts +67 -0
  129. package/templates/m3e-app/src/modules/gallery/composables/wilayas.ts +67 -0
  130. package/templates/m3e-app/src/modules/gallery/router/routes/gallery.routes.ts +52 -0
  131. package/templates/m3e-app/src/modules/gallery/views/GalleryAgendaView.vue +117 -0
  132. package/templates/m3e-app/src/modules/gallery/views/GalleryChatView.vue +292 -0
  133. package/templates/m3e-app/src/modules/gallery/views/GalleryContactsView.vue +77 -0
  134. package/templates/m3e-app/src/modules/gallery/views/GalleryFeaturedView.vue +60 -0
  135. package/templates/m3e-app/src/modules/gallery/views/GalleryLoginView.vue +135 -0
  136. package/templates/m3e-app/src/modules/gallery/views/GalleryOnboardingView.vue +116 -0
  137. package/templates/m3e-app/src/modules/gallery/views/GallerySectionView.vue +27 -0
  138. package/templates/m3e-app/src/modules/gallery/views/GalleryTabsView.vue +135 -0
  139. package/templates/m3e-app/src/modules/gallery/views/GalleryView.vue +50 -0
  140. package/templates/m3e-app/src/modules/home/components/HomeHero.vue +57 -0
  141. package/templates/m3e-app/src/modules/home/composables/useHomeFeatures.ts +128 -0
  142. package/templates/m3e-app/src/modules/home/composables/useOpenFeature.ts +22 -0
  143. package/templates/m3e-app/src/modules/home/router/routes/home.routes.ts +21 -0
  144. package/templates/m3e-app/src/modules/home/views/FeatureDetailView.vue +66 -0
  145. package/templates/m3e-app/src/modules/home/views/HomeView.vue +47 -0
  146. package/templates/m3e-app/src/modules/settings/components/RolePalette.vue +38 -0
  147. package/templates/m3e-app/src/modules/settings/components/SeedSwatches.vue +41 -0
  148. package/templates/m3e-app/src/modules/settings/components/SettingsChoice.vue +43 -0
  149. package/templates/m3e-app/src/modules/settings/components/StudioPreview.vue +52 -0
  150. package/templates/m3e-app/src/modules/settings/router/routes/settings.routes.ts +17 -0
  151. package/templates/m3e-app/src/modules/settings/types.ts +7 -0
  152. package/templates/m3e-app/src/modules/settings/views/ColorStudioView.vue +166 -0
  153. package/templates/m3e-app/src/modules/settings/views/SettingsView.vue +157 -0
  154. package/templates/m3e-app/src/plugins/bootstrapError.ts +62 -0
  155. package/templates/m3e-app/src/plugins/capacitor/index.ts +14 -0
  156. package/templates/m3e-app/src/plugins/capacitor/useAndroidBackButton.ts +36 -0
  157. package/templates/m3e-app/src/plugins/capacitor/useKeyboard.ts +48 -0
  158. package/templates/m3e-app/src/plugins/capacitor/useSplashScreen.ts +43 -0
  159. package/templates/m3e-app/src/plugins/capacitor/useStatusBar.ts +16 -0
  160. package/templates/m3e-app/src/plugins/framework7.plugin.ts +36 -0
  161. package/templates/m3e-app/src/plugins/i18n.plugin.ts +87 -0
  162. package/templates/m3e-app/src/plugins/m3e.plugin.ts +22 -0
  163. package/templates/m3e-app/src/plugins/seed.plugin.ts +7 -0
  164. package/templates/m3e-app/src/plugins/sqlite.plugin.ts +32 -0
  165. package/templates/m3e-app/src/router/global/global.routes.ts +12 -0
  166. package/templates/m3e-app/src/router/index.ts +20 -0
  167. package/templates/m3e-app/src/shared/components/error/404.vue +13 -0
  168. package/templates/m3e-app/src/shared/components/layout/EmptyState.vue +30 -0
  169. package/templates/m3e-app/src/shared/components/layout/SectionHeader.vue +10 -0
  170. package/templates/m3e-app/src/shared/components/page/AppPage.vue +104 -0
  171. package/templates/m3e-app/src/shared/composables/navigation/useActiveTab.ts +39 -0
  172. package/templates/m3e-app/src/shared/composables/navigation/useContainerTransform.ts +117 -0
  173. package/templates/m3e-app/src/shared/composables/navigation/useNavigationGuard.ts +48 -0
  174. package/templates/m3e-app/src/shared/composables/navigation/useNavigationVisibility.ts +64 -0
  175. package/templates/m3e-app/src/shared/composables/navigation/useViewRouter.ts +25 -0
  176. package/templates/m3e-app/src/shared/composables/navigation/useWindowClass.ts +22 -0
  177. package/templates/m3e-app/src/shared/composables/theme/useThemeSettings.ts +155 -0
  178. package/templates/m3e-app/src/shared/database/candidates/capacitorSqlite.ts +36 -0
  179. package/templates/m3e-app/src/shared/database/candidates/index.ts +4 -0
  180. package/templates/m3e-app/src/shared/database/candidates/opfsSahPool.ts +25 -0
  181. package/templates/m3e-app/src/shared/database/candidates/types.ts +56 -0
  182. package/templates/m3e-app/src/shared/database/candidates/waSqlite.ts +56 -0
  183. package/templates/m3e-app/src/shared/database/database.ts +219 -0
  184. package/templates/m3e-app/src/shared/database/index.ts +3 -0
  185. package/templates/m3e-app/src/shared/database/migrations.ts +223 -0
  186. package/templates/m3e-app/src/shared/database/opfs.worker.ts +5 -0
  187. package/templates/m3e-app/src/shared/database/queries.ts +26 -0
  188. package/templates/m3e-app/src/shared/database/schema.ts +153 -0
  189. package/templates/m3e-app/src/shared/database/storage.ts +64 -0
  190. package/templates/m3e-app/src/shared/database/wa.worker.ts +5 -0
  191. package/templates/m3e-app/src/shared/utils/lazyRoute.ts +41 -0
  192. package/templates/m3e-app/src/shared/utils/resolvers/resolvers.ts +42 -0
  193. package/templates/m3e-app/src/shared/utils/textDirection.ts +22 -0
  194. package/templates/m3e-app/src/shared/utils/theme/themeSettings.ts +59 -0
  195. package/templates/m3e-app/src/shared/utils/tone.ts +8 -0
  196. package/templates/m3e-app/tests/benchmark.suite.test.ts +104 -0
  197. package/templates/m3e-app/tests/containerTransform.test.ts +58 -0
  198. package/templates/m3e-app/tests/fieldFormats.test.ts +25 -0
  199. package/templates/m3e-app/tests/lazyRoute.test.ts +30 -0
  200. package/templates/m3e-app/tests/locales.test.ts +73 -0
  201. package/templates/m3e-app/tests/migrations.test.ts +74 -0
  202. package/templates/m3e-app/tests/navigationGuard.test.ts +82 -0
  203. package/templates/m3e-app/tests/openDatabase.test.ts +54 -0
  204. package/templates/m3e-app/tests/sales.repository.test.ts +329 -0
  205. package/templates/m3e-app/tests/storage.test.ts +117 -0
  206. package/templates/m3e-app/tests/textDirection.test.ts +11 -0
  207. package/templates/m3e-app/tooling/linkedPackages.ts +112 -0
  208. package/templates/m3e-app/tsconfig.json +21 -0
  209. package/templates/m3e-app/vite.config.ts +144 -0
@@ -0,0 +1,68 @@
1
+ ---
2
+ name: m3-expressive
3
+ description: Material 3 Expressive UI in this app. Use for any screen, component, layout, animation, transition, sheet, dialog, menu, list, button, progress or loading state, colour or theme change, icon, or app-bar work - before writing markup.
4
+ ---
5
+
6
+ # Material 3 Expressive
7
+
8
+ This app is Material 3 Expressive end to end. Framework7 is only the navigation engine (router, tab
9
+ views, page lifecycle); everything visible is an `M3*` component from `@cavulsqa/m3e-vue`, styled by
10
+ `--md-sys-*` tokens from `@cavulsqa/m3e`. Expressive is not decoration: shape, spring and tonal
11
+ colour say _what is happening_ - a selection rounds off, a sheet springs, progress waves while work
12
+ moves. Every number comes from Google's Compose tokens; [sources.md](sources.md) links each spec.
13
+
14
+ ## Building a screen
15
+
16
+ 1. **Name the job in one sentence** and the nearest Google app that does it (Messages, Photos,
17
+ Settings, Files, Wallet). Open its pattern on m3.material.io from [sources.md](sources.md).
18
+ 2. **Pick the component** from [components.md](components.md). Every visible element is an `M3*`
19
+ component or Tailwind layout around one. Missing a component? It belongs in
20
+ `packages/m3e-vue`, built from its spec page - not a one-off in a module.
21
+ 3. **Wrap the route in `AppPage`** (`shared/components/page`): tab roots get the large flexible app
22
+ bar, pushed pages pass `back`. Layout rules: [layout.md](layout.md).
23
+ 4. **Choose shape, colour role and motion** - [shapes-type.md](shapes-type.md),
24
+ [color.md](color.md), [motion.md](motion.md). Each decision names its token.
25
+ 5. **Strings in both locales**, icons from Material Symbols (`<i-ms-name-rounded />`), filled
26
+ variant for a selected state.
27
+ 6. **Done means**: `vp check`, `pnpm type-check` and `vp test` pass; you checked light, dark, a 360dp
28
+ width, reduced motion, and RTL by reasoning or on screen; and you said plainly whether you saw it
29
+ run. Type-checking proves nothing about how a screen looks.
30
+
31
+ ## The rules every screen keeps
32
+
33
+ - **Tokens only.** Colour is a role (`bg-surface-container-low`, `text-on-primary-container`),
34
+ corners are the shape scale (`rounded-lg`, `rounded-xl`), type is a style (`type-title-medium`),
35
+ shadows are levels (`shadow-1`..`shadow-5`). Tailwind's own palette is deleted from the theme, so
36
+ only roles resolve; a hex value belongs in `src/app/theme.config.ts` and nowhere else.
37
+ - **Pairs travel together.** A container role always carries its `on-` role:
38
+ `bg-tertiary-container text-on-tertiary-container`. Text on a surface is `text-on-surface` or
39
+ `text-on-surface-variant`.
40
+ - **Surfaces stack by container tone**, not by shadow: page `surface`, grouped content
41
+ `surface-container-low`, raised panels `surface-container`/`-high`. Shadows only on what floats.
42
+ - **Overlays come from the services**: `useSnackbar`, `useDialog`, `useActionSheet` (auto-imported),
43
+ or `M3BottomSheet`/`M3Menu` with `v-model:open`. They register on the overlay stack, so Android
44
+ back and Escape close the top one. Never hand-build a modal.
45
+ - **One tap target per row.** A list row's own button lives in `M3ListItem`'s `#action` slot, never
46
+ inside a clickable row.
47
+ - **Motion is spatial or effects.** Position, size and shape use the spatial springs; colour and
48
+ opacity the effects springs. Never animate `width`/`top` of a large surface - `transform` and
49
+ `opacity`.
50
+ - **Reduced motion keeps meaning, drops travel**: morphs and fades stay, rotation, bounce and slide
51
+ go. The tokens collapse spatial durations to 1 ms for you; JS-driven motion reads
52
+ `useReducedMotion()`.
53
+ - **Every control is labelled**: icon buttons, FABs and switches take `label`; it becomes the
54
+ accessible name and tooltip.
55
+ - **Empty is a state, not a blank.** `EmptyState` (shape, one sentence, the one action that fills it).
56
+ Waits under ~5 s show `M3LoadingIndicator`; known progress shows the wavy indicators.
57
+
58
+ ## Where things live
59
+
60
+ | Need | Use |
61
+ | ----------------------------- | ---------------------------------------------------------------- |
62
+ | A screen | `AppPage` + modules/`<feature>`/views |
63
+ | Section label | `SectionHeader` |
64
+ | Theme state | `useThemeSettings()` - seed, variant, contrast, mode, motion |
65
+ | Shell navigation | `useActiveTab()` (`show`, `open(tab, path)`), `useWindowClass()` |
66
+ | Hide the bar on a pushed page | `AppPage back` does it; elsewhere `useHiddenNavigation()` |
67
+ | Decorative tone for a shape | `TONE_CLASSES` from `shared/utils/tone` |
68
+ | Live component examples | the Gallery tab - `modules/gallery/components/sections` |
@@ -0,0 +1,44 @@
1
+ # Colour
2
+
3
+ One seed (`BRAND_SEED` in `src/app/theme.config.ts`) generates every role in both modes through
4
+ material-color-utilities' 2025 (Expressive) spec. The user can change seed, variant and contrast in
5
+ the colour studio; `useThemeSettings()` persists it and repaints in one frame. Dark mode is a `.dark`
6
+ class on `<html>` - both modes are always in the stylesheet, so switching regenerates nothing.
7
+
8
+ ## Roles to reach for
9
+
10
+ | Purpose | Role |
11
+ | ----------------------------------------------------------- | -------------------------------------------------------------------- |
12
+ | Page background | `surface` |
13
+ | Grouped content, list segments, cards on a page | `surface-container-low` |
14
+ | Raised panels, the nav bar, bars over scrolled content | `surface-container` / `-high` |
15
+ | Text fields, inactive tracks | `surface-container-highest` |
16
+ | Main emphasis (FAB, hero, selected nav) | `primary-container` + `on-primary-container` |
17
+ | Strong action fill | `primary` + `on-primary` |
18
+ | Selected or active but quiet (chips, list rows, indicators) | `secondary-container` |
19
+ | Contrasting accent, decorative variety | `tertiary-container` |
20
+ | Snackbar, tooltips | `inverse-surface` + `inverse-on-surface`, action `inverse-primary` |
21
+ | Errors and destructive | `error`, `error-container` |
22
+ | Success, warning | custom groups `success-*`, `warning-*` (theme.config `EXTRA_COLORS`) |
23
+ | Hairlines | `outline-variant`; field borders `outline` |
24
+ | Secondary text, icons | `on-surface-variant` |
25
+
26
+ Disabled content is `on-surface` at 38%, disabled containers `on-surface` at 10-12% - the components
27
+ already do this.
28
+
29
+ ## Variants and the spec trap
30
+
31
+ Material applies the 2025 rules only to **tonalSpot, expressive, vibrant, neutral**. `brand`
32
+ (seed exact as `primary-container`, Theme Builder's "Match colour"), fidelity, content, monochrome,
33
+ rainbow and fruitSalad silently use 2021 rules - `effectiveSpec(variant)` says which. The studio shows
34
+ it to the user.
35
+
36
+ ## Rules
37
+
38
+ - A new semantic colour is a custom group in `EXTRA_COLORS` (harmonised to the seed), then a role
39
+ name in `assets/css/theme/tailwind.css` - never a hex in a component.
40
+ - Contrast is a user setting (standard / medium / high): never compensate a weak pair by hand-picking
41
+ a darker colour; fix the role choice.
42
+ - Status needs more than colour: pair it with a shape or glyph (see `STATUS_LOOK` in the demo module).
43
+
44
+ See [sources.md](sources.md) → Colour.
@@ -0,0 +1,325 @@
1
+ # Component catalogue
2
+
3
+ Every component is auto-imported by `M3eResolver`; write the tag, never an import. Composables
4
+ (`useSnackbar`, `useDialog`, `useActionSheet`, `useHaptics`, `useReducedMotion`) are auto-imported
5
+ too. Each section names the spec page to read when a detail is not covered here.
6
+
7
+ ## Actions
8
+
9
+ **`M3Button`** - `variant` filled | tonal | outlined | elevated | text, `size` xs | s | m | l | xl,
10
+ `shape` round | square, `toggle` + `v-model:selected`, slots `#icon` (receives `selected`), `#trailing`.
11
+ Hierarchy: one filled action per screen region; tonal for secondary; outlined/text below that. Size
12
+ `s` is the default for in-content actions, `m` for a screen's primary action and sheet footers, `l`/`xl`
13
+ for hero moments only. → buttons/specs
14
+
15
+ **`M3IconButton`** - `label` required, `variant` standard | filled | tonal | outlined, `width` narrow |
16
+ default | wide, `size`, `shape`, `toggle`. App-bar actions are `standard`; a toolbar's emphasised
17
+ action is `filled`. → icon-buttons/specs
18
+
19
+ **`M3ButtonGroup`** - `variant` standard (pressed button widens 15%, neighbours give way) or
20
+ connected (2dp gaps, replaces segmented buttons; use toggle buttons inside for single select), `size`.
21
+ → button-groups/specs
22
+
23
+ **`M3SplitButton`** - primary action + `#menu`; pair with `M3Menu` anchored inside the menu slot.
24
+ → split-button/specs
25
+
26
+ **`M3Fab`** - `label` required, `size` small | default | medium | large, `color` *-container (default)
27
+ or solid, `extended` (undefined = plain; true/false = extended FAB shown/collapsed - collapse on scroll
28
+ down). One FAB per screen, for its single most important constructive action, in `AppPage`'s `#fab`.
29
+ → floating-action-button/specs, extended-fab/specs
30
+
31
+ **`M3FabMenu` + `M3FabMenuItem`** - when the main action has 2-6 variants. Items take `index`
32
+ (0 nearest the FAB). → fab-menu/specs
33
+
34
+ **`M3FabMorph`** - Framework7's FAB morph as a container transform: the FAB grows into a floating
35
+ toolbar of icon buttons (`variant="toolbar"`) or a panel (`panel`, with a scrim) and shrinks back.
36
+ `v-model:open`, `label`, `#icon`, default slot with `{ close }`. Use it when the FAB opens a set of
37
+ peer actions on one screen; variants of one action are a FAB menu.
38
+
39
+ ## Navigation
40
+
41
+ **Scroll behaviour** - `src/app/scroll.config.ts` sets it for the whole app: `topAppBar:
42
+ "enterAlways"` slides a small top app bar away with the content and back on the way up (the status
43
+ strip stays covered, `#bottom` and sticky `M3ListGroup` titles move with it); `navigationBar:
44
+ "hideOnScroll"` slides the navigation bar off while scrolling down. `AppPage` wires both; a page
45
+ passes `scrollBehavior` to override the top bar. Outside `AppPage`, `M3TopAppBar`'s
46
+ `scrollBehavior` and `useHideOnScroll(scroller)` give the same behaviour.
47
+
48
+ **`M3Breadcrumbs`** - `items` (`{ label, href? }`), `@select`, `max` (the middle collapses into a
49
+ menu past it), `label`. The last item is the current page. Fades the edge it overflows past.
50
+
51
+ **`M3NavigationBar` / `M3NavigationRail` + `M3NavigationItem`** - the shell already renders these from
52
+ `src/app/tabs.ts`; add a destination there. Items take `#icon="{ selected }"` (outlined → filled),
53
+ `badge` (number or `true`). `@reselect` fires on tapping the current destination.
54
+ → navigation-bar/specs, navigation-rail/specs
55
+
56
+ **`M3Tabs` + `M3Tab`** - `variant` primary (content-width indicator, optional `#icon`) for peer views
57
+ of one subject; secondary for subdivisions inside a primary tab. `scrollable` beyond ~4 tabs.
58
+ Arrow keys move. Swipeable pages: put the tabs in `AppPage`'s `#bottom` slot (pinned under the
59
+ bar), the pages in `M3TabPanels` + `M3TabPanel value`, bind one `v-model` to both and pass both
60
+ the same `createTabPager()` so the indicator follows the swipe. Give `M3TabPanels` a height; each
61
+ page scrolls on its own. A carousel or swipe row inside a page keeps its own sideways drag.
62
+ → tabs/specs
63
+
64
+ **`M3TopAppBar`** - rendered by `AppPage`. `small` for pushed pages, `medium`/`large` flexible for
65
+ roots and long titles. Tapping the bar outside its buttons scrolls the page to the top (Android's
66
+ status bar keeps taps for its shade); nav-bar reselect does the same. → app-bars/specs
67
+
68
+ **`M3FloatingToolbar`** - page actions over content: `variant` standard | vibrant, `#fab`,
69
+ `hideOnScroll`. Compose gives it no shadow (1dp with a FAB) - do not add one. **`M3DockedToolbar`** - a detail screen's persistent actions at the bottom edge.
70
+ → toolbars/specs
71
+
72
+ ## Containment
73
+
74
+ **`M3List` + `M3ListItem`** - `variant` segmented (default; each row its own surface, 2dp apart) or
75
+ standard. Items: `headline`, `supporting`, `overline`, `trailingText`, `multiline`, `clickable` or
76
+ `href`, `selected`, `tone="destructive"`; slots `#leading` (icon, `M3Shape` avatar 40dp), `#trailing`
77
+ (decoration), `#action` (a control with its own target). Swipe actions: `M3SwipeAction` buttons
78
+ (`label`, `tone`, `@click`) in `#swipe-start` / `#swipe-end`; `swipe-full="end"` arms the outermost
79
+ action past half the row (deletes, archives - pair with an undo snackbar), `v-model:swiped` reads
80
+ the open side. One row open at a time; a tap outside closes it. Expandable items: put the hidden
81
+ content in `#details` (`v-model:expanded`); `M3List accordion` keeps one open, as Framework7's
82
+ accordion list does - FAQs, settings groups, order lines. `M3List sortable` is Framework7's
83
+ sortable list: a drag handle on every item plus long-press-and-drag on the row; handle
84
+ `@sort="(from, to) => (rows = moveItem(rows, from, to))"` and persist the order yourself. Not for
85
+ `M3VirtualList`. → lists/specs
86
+
87
+ **`M3Carousel`** - Compose's carousel, keyline for keyline: `items`, `label`, `variant`
88
+ multi-browse (default; browsing many items - photos, products) | hero (one featured item at a time;
89
+ a fling moves one) | uncontained (fixed `item-width`, coasts without snapping), `item-width`
90
+ (preferred large width, 186), `item-spacing` 8, `content-padding` 16, `height`, `v-model:item`.
91
+ Items are laid out full size and masked, so put a full-bleed picture in the slot and let it
92
+ parallax; fade labels with `--m3-carousel-item-progress` and slide them with
93
+ `--m3-carousel-mask-start`. The slot's `scrollTo` brings a tapped item forward. → carousel/specs
94
+
95
+ **`M3Tree`** - Framework7's treeview from data: `items` (`{ id, label, supporting?, children?,
96
+ lazy? }`), `label`, `mode` select (`v-model:selected`) | check (`v-model:checked` leaf ids,
97
+ tri-state branches), `v-model:expanded`, `load` for lazy children, `#icon="{ node, expanded }"`,
98
+ `#trailing`. Categories, charts of accounts, permission sets.
99
+
100
+ **`M3DataTable`** - Framework7's data table: `rows`, `columns` (`{ key, label, value?, format?,
101
+ numeric?, sortable?, width? }`), `row-key`, `label`, `v-model:sort` (sorts locally unless
102
+ `:sort-locally="false"` - then sort in SQL from the model), `selectable` + `v-model:selected`,
103
+ `max-height` + `sticky-first-column` for wide tables, `dense`, `#cell`, `#footer` (totals), `#empty`.
104
+ For phones prefer a list with the key fields; use the table where rows are compared across columns.
105
+
106
+ **`M3Card`** - `variant` elevated | filled | outlined; `clickable` makes the whole card one target -
107
+ then put no other buttons inside. → cards/specs
108
+
109
+ **`M3Divider`** - only where whitespace and surfaces do not already separate. → divider/specs
110
+
111
+ ## Overlays
112
+
113
+ **`M3BottomSheet`** - `v-model:open`, `title`, `dismissible`, slots `#header`, default (scrolls),
114
+ `#footer` (fixed actions). Spring-driven; drag from handle or top-scrolled content. Prefer it to a
115
+ dialog for anything with more than one choice or any form. → bottom-sheets/specs
116
+
117
+ **`M3StandardBottomSheet`** - the non-modal sheet beside the content (a map, a route, a player):
118
+ `v-model:detent` peek | half | expanded (| hidden with `hideable`), `label`, `title`, `peekHeight`
119
+ (56 by default - raise it to show the title), `half`. Drags anywhere until expanded, then the content
120
+ scrolls; pulling down from the content's top brings it back. Fixed to the window: set
121
+ `--m3-standard-sheet-inset-bottom` above a navigation bar and pad the page by the peek.
122
+
123
+ **Action sheet** - `await useActionSheet().open({ title, supporting, quickActions, groups })` resolves
124
+ the chosen id or null. Options with icons in segmented groups; `selected` shows a check, `tone:
125
+ "destructive"` paints error. Icons are components wrapped in `markRaw`.
126
+
127
+ **`M3Dialog` / `useDialog().confirm({...})`** - only for a decision that must interrupt: destructive
128
+ confirmations, required choices. `destructive` paints the confirm action; `icon` centres the headline.
129
+ → dialogs/specs
130
+
131
+ **`M3FullScreenDialog`** - Framework7's popup, M3's full-screen dialog: `v-model:open`, `title`,
132
+ `confirm-label` (+ `confirm-disabled`), `#actions`, `@confirm`, `@close`. For a task that needs the
133
+ whole phone screen - a new order, an intake form; from 600dp it becomes a basic dialog. While the
134
+ form is dirty pass `:dismissible="false"` and ask in `@close` before discarding (`useDialog`
135
+ confirmations draw above it).
136
+
137
+ **`M3Menu` + `M3MenuItem` + `M3MenuGroup`** - anchored to an element ref (`:anchor`), `variant`
138
+ standard | vibrant; groups give the expressive segmented look. `checkable` for single/multi select.
139
+ A `#submenu` slot of items makes a cascading item (trailing arrow; opens beside it on tap, hover or
140
+ the arrow key); choosing inside closes the whole chain, Back closes the submenu alone. Keep it one
141
+ level deep on phones. → menus/specs
142
+
143
+ **Snackbar** - `await useSnackbar().show({ message, action, duration })` resolves `action` |
144
+ `dismissed` | `timeout`. One action max, never for errors that need a decision. Undo is the classic.
145
+ → snackbar/specs
146
+
147
+ **In-app notification** - `await useNotification().show({ title, text, source, meta, icon, duration })`
148
+ resolves `opened` | `dismissed` | `timeout` | `replaced`. Framework7's notification: a banner from the
149
+ top for something that happened elsewhere (a new order, a finished sync) that the person may want
150
+ to open; a snackbar is for feedback on what they just did. One at a time - a new one replaces it.
151
+
152
+ **`M3SideSheet`** - `v-model:open`, `side` start | end, `title`; filters and secondary detail beside
153
+ the content. **`M3ModalNavigationRail`** - the expressive replacement for the navigation drawer;
154
+ closes itself once a destination is chosen. → side-sheets/specs, navigation-rail/specs
155
+
156
+ **`M3Popover`** - Framework7's popover: any content anchored to an element. `anchor` (element),
157
+ `v-model:open`, `label`, `side` bottom | top, `align` start | center | end, `modal` (scrim + focus
158
+ trap); default slot gets `{ close }`. Flips and stays on screen, grows out of the anchor, closes on a
159
+ tap outside, Escape or back, and focus returns to the anchor. For a few controls or an explanation
160
+ tied to one element; a list of actions is `M3Menu`.
161
+
162
+ **`M3Tooltip`** - plain (long-press, hover, focus; labels an icon button) or rich (`title`, text,
163
+ actions; explains, never holds the only path to a task). Placed so it never leaves the screen.
164
+ → tooltips/specs
165
+
166
+ ## Date and time
167
+
168
+ Values are strings end to end - `IsoDate` (`YYYY-MM-DD`) and `IsoTime` (`HH:mm`, 24-hour) - so no
169
+ timezone or DST shift can move a picked day. Utilities (`formatIso`, `parseLocalDate`,
170
+ `formatIsoTime`, `uses12Hour`...) are exported beside the components.
171
+
172
+ **`M3DatePicker`** - `v-model`, `v-model:open`, `presentation` dialog (Material's modal, default) |
173
+ sheet (Framework7's, one-handed). `modes` defaults to calendar + typed input in a dialog, calendar +
174
+ wheel in a sheet. Edits a draft; only OK commits. `min`, `max`, `isDisabled`, `locale`, every label
175
+ a prop. **`M3Calendar`** - the grid alone, inline or docked in a card: `v-model:value`, or
176
+ `mode="range"` with `v-model:start` / `v-model:end`. → date-pickers/specs
177
+
178
+ **`M3TimePicker`** - `v-model` `HH:mm`, `presentation` dialog | sheet, `modes` dial + keyboard
179
+ (dialog) or dial + wheel (sheet), `hour12` (else the locale decides), `minuteStep`. The dial is
180
+ **`M3ClockDial`**: 24-hour faces put 00 and 13-23 on the inner ring. → time-pickers/specs
181
+
182
+ **`M3WeekStrip`** - the head of an agenda: `v-model` `IsoDate`, `label`, `marks` (`{ [IsoDate]:
183
+ count }` for the dots), `markLabel`, `locale`, `firstDay`. Swipes week to week endlessly; pin it
184
+ under the app bar through `AppPage`'s `#bottom`, the day's items in a list below. Inset with
185
+ `--m3-week-strip-inset`, never padding.
186
+
187
+ **`M3WheelPicker`** - Framework7's picker: drums over one band, `columns` of `{ key, label,
188
+ options, flex, align, loop }`, `v-model` keyed by column; `loop` rolls a drum over at its ends. Built like Framework7's: native scroll-snap, flat rows, gradient fades in `--m3-wheel-surface`
189
+ (the colour behind it) - no per-row 3D or masks, which made it lag on phones - and a
190
+ tick per detent. **`M3DateWheel`** / **`M3TimeWheel`** are the ready date and time drums; put them in
191
+ an `M3BottomSheet` with a draft when the choice needs confirming. No M3 spec - it is the F7 parity
192
+ piece.
193
+
194
+ ## Photos
195
+
196
+ **`M3PhotoBrowser`** - Framework7's photo browser: `photos` (`{ src, alt, caption?, width?, height? }`),
197
+ `v-model:open`, `v-model:index`, `label`, `#actions` for the bar (share, delete). Swipe between,
198
+ pinch or double-tap to zoom, pan when zoomed, swipe up or down to close; only neighbours load. Give
199
+ `width`/`height` when known. It is drawn on black - call `useDarkStatusBar(() => open.value)` so the
200
+ status bar icons turn light while it shows.
201
+
202
+ **`M3Image`** - lazy image: `src`, `alt` (`""` when decorative), `width` + `height` or `ratio` to
203
+ reserve its space, `placeholder` (a colour or a tiny image, drawn blurred), `fit`, `eager` for the
204
+ first screen, `#error`. Fades in once decoded; cached pictures appear at once.
205
+
206
+ **`M3Pager`** + **`M3PagerPage`** - Framework7's swiper as a pager on native scroll-snap: one page per
207
+ swipe, `v-model:page`, `label`; dots underneath, or `#footer` with `{ page, count, progress, go,
208
+ next, previous }` for Skip / Next. **`M3PageIndicator`** (`count`, `progress`, `@select`) is the
209
+ dots alone; its pill follows the finger. Onboarding, a product's photos, a feature tour.
210
+
211
+ ## Lists at scale
212
+
213
+ **`M3ListGroup` + `M3ListIndex`** - Framework7's contacts list and list index: groups whose titles
214
+ stick under the app bar (`title`, `indexKey`; `groupKey(label)` folds accents and buckets digits
215
+ under `#`), and an A–Z rail (`keys`, `label`) in `AppPage`'s `#fixed` slot that jumps as the finger
216
+ drags. Pad the content ~24px at the end edge so rows clear the rail.
217
+
218
+ **`M3InfiniteScroll`** - after a list: `:load` returns a promise and resolves `false` when there is
219
+ no more; failures wait for the user's retry; `endText` closes the list; expose `reset()` after a new
220
+ filter. Feed it from a repository with `LIMIT/OFFSET` (or a keyset) - never load everything to
221
+ slice it in memory. **`M3Timeline` + `M3TimelineItem`** - an order's journey, a patient's visits:
222
+ `title`, `time`, `supporting`, `state` done | current | upcoming, `#icon` for the current step,
223
+ `stateLabel` so the state is read out.
224
+
225
+ **`M3VirtualList`** - thousands of rows against the page's own scroller (the app bar still
226
+ collapses): `items`, `itemSize` (number or function: 56 / 72 / 88 + 2 for the segment gap),
227
+ `itemKey`, `inset`. Rows are recycled - row components must render from props alone; `recycle=false`
228
+ keys by `itemKey` for rows with local state. **`M3PullToRefresh`** - `:refresh` returns a promise;
229
+ the loading indicator fills with the pull and loops until it settles.
230
+
231
+ ## Progress
232
+
233
+ **`M3Skeleton` + `M3SkeletonBlock` + `M3SkeletonText`** - Framework7's skeleton: placeholders in
234
+ the shape of the content on its way, for loads you expect to take more than a moment (a list from
235
+ the database, a detail screen). Mirror the real layout exactly - same rows, same type scales via
236
+ `M3SkeletonText typescale` - so nothing jumps when it lands. `effect` wave (default) | pulse | none;
237
+ set `--m3-skeleton-surface` to the colour behind the group. Use `M3LoadingIndicator` instead when
238
+ there is no layout to predict.
239
+
240
+ **`M3LoadingIndicator`** - waits up to a few seconds, or `progress` 0-1 for determinate. `contained`
241
+ over content. Replaces every spinner. → loading-indicator/specs
242
+
243
+ **`M3LinearProgress` / `M3CircularProgress`** - `value` 0-1 or omitted (indeterminate), `wavy` (default
244
+ on), `thickness`; circular takes `gauge` and a centred slot. Linear sits 4dp in from the edges.
245
+ → progress-indicators/specs
246
+
247
+ ## Selection and input
248
+
249
+ **`M3Switch`** (`icons`, `bothIcons`) for an instant on/off; **`M3Checkbox`** for items in a list
250
+ or a form submitted later; **`M3Radio`** (`v-model` + `value`) for one of few visible options - more
251
+ than ~5, use an action sheet. **`M3Slider`** - `size` xs..xl, `step`, `ticks`, `#icon` from m, `format`
252
+ for the value bubble. **`M3RangeSlider`** - `v-model:start` / `v-model:end`, `startLabel` /
253
+ `endLabel` (each handle is its own slider), `minDistance` keeps them apart. **`M3Stepper`** - Framework7's stepper for small counts (cart quantity, guests, a dose):
254
+ `v-model`, `label`, `min`/`max`/`step` (decimals stay clean), `variant` outlined | tonal, `size` s | m,
255
+ `editable` (type the value; a decimal comma is read), `format` for units. Holding a button repeats
256
+ and speeds up across wide ranges. Use a slider when the exact value matters less than its position.
257
+ **`M3Chip`** - `kind` assist | filter (`v-model:selected`) | input
258
+ (`removable`) | suggestion. **`M3TextField`** - `variant` filled (default) | outlined, `supporting`,
259
+ `error`, `maxlength` counter, `prefix`/`suffix`, `multiline`, `#leading`/`#trailing`; native
260
+ attributes pass through. **`M3ExposedDropdown`** - Compose's exposed dropdown menu: `options`
261
+ (`{ value, label, supporting?, disabled? }`), `v-model`, `label`; read-only it is a select (arrow
262
+ keys, type-ahead), `editable` makes it Framework7's autocomplete (accent- and case-insensitive
263
+ filter, bolded match, `limit`). For a remote source, `:filter="false"` + `v-model:query` +
264
+ `loading`. Use it over radios past ~5 options, and over an action sheet when the field sits in a
265
+ form. **`M3SmartSelect`** - Framework7's smart select, a list row that opens a sheet of options:
266
+ `options`, `v-model` (an array with `multiple`), `label`, `placeholder`; search appears past
267
+ `searchFrom` (10). Prefer it to the exposed dropdown for multi-choice and inside settings lists.
268
+ **`M3SearchBar`** - `v-model`, `@search`, `#leading`/`#trailing`, for
269
+ filtering what is already on screen. **`M3SearchView`** - the bar that opens into a full-screen
270
+ view (`v-model`, `v-model:expanded`, `placeholder`; results in the default slot with `{ query }`,
271
+ recent searches when the query is empty); use it for searching a whole data set.
272
+ → switch, checkbox, radio-button, sliders, chips, text-fields, search /specs
273
+
274
+ ## Charts
275
+
276
+ `M3LineChart` (`area` for filled), `M3BarChart` (`stacked`) and `M3DonutChart` - plain SVG, no
277
+ library: `labels` + `series` (`{ label, values, color? }`, `null` breaks a line) or `segments`
278
+ (`{ label, value }`), `label` (the accessible name), `format` for numbers (use
279
+ `Intl.NumberFormat` compact). Colours come from the theme's roles; touching reads values;
280
+ screen readers get a hidden table. Lines for change over time, bars to compare categories, a
281
+ donut for parts of one whole (at most ~6 segments). Feed them aggregates from SQL, never raw rows.
282
+
283
+ ## Forms
284
+
285
+ **`M3TextEditor`** - Framework7's text editor: `v-model` HTML, `label`, `placeholder`, `toolbar`
286
+ (commands and `"|"`), `labels` for i18n. Bold, italic, underline, strikethrough, lists, links
287
+ (through a popover), clear formatting. Its HTML is sanitised in and out (`sanitizeHtml` is exported
288
+
289
+ - run it again wherever stored HTML is rendered with `v-html`). Use a plain multiline `M3TextField`
290
+ unless formatting is the point.
291
+
292
+ **`M3SignaturePad`** - proof of delivery: `v-model:strokes` (fractions of the pad, so a draft or a
293
+ rotation redraws it sharp), `height`, `label`, `placeholder`; ref methods `undo()`, `clear()`,
294
+ `toDataURL(type, background)` for the upload, `toSvg()`. Always pair it with a typed name - signing
295
+ has no keyboard path.
296
+
297
+ **`M3ColorPicker`** - `v-model` `#rrggbb`, hue / chroma / tone sliders in HCT with previewing tracks,
298
+ a hex field, optional `swatches`. Put it in a sheet with a draft and apply on confirm when the
299
+ colour drives something expensive, such as the app theme.
300
+
301
+ **`useFormDraft(key, state, options)`** - keeps what is typed into a form as a draft (debounced,
302
+ written at once when the app goes to the background) and restores it when the form opens again.
303
+ Returns `{ restored, savedAt, error, clear, flush }`; call `clear()` on submit or discard. `storage`
304
+ takes any async store (default `localStorage`), `version` drops drafts from an older form, `maxAge`
305
+ expires them. Never draft passwords or card numbers - give it only the fields worth keeping.
306
+
307
+ ## Messages
308
+
309
+ **`M3Messages`** - a conversation: `messages` (`{ id, sent, at, text?, author?, avatar?, status?,
310
+ image? }`, oldest first), `label`, `locale`, `authors` for a group (names and avatars), `typing`
311
+ (`true` or `{ author }`), label props for i18n. It scrolls with the page: opens at the newest,
312
+ follows new ones while the reader is there, keeps their place otherwise and counts what arrived.
313
+ Older history goes in `#before` as `<M3InfiniteScroll edge="start">` - page it, never pass
314
+ thousands. `@hold` (long-press) for a message menu via `useActionSheet`, `@press` for an image,
315
+ `@retry` for a failed send. Give images `width`/`height` so nothing jumps as they load.
316
+ **`M3MessageBar`** - the composer, in `AppPage`'s `#fixed`: `v-model`, `label`, `@send` (trimmed
317
+ text; it clears itself), `#leading` (attach), `#trailing` (emoji), `#idle` (stands in for send
318
+ while empty), `enterSends` for hardware keyboards. It publishes its height for `M3Messages`; zero
319
+ the page's own bottom padding (`.page-content { padding-bottom: 0 }`) on a chat page.
320
+
321
+ ## Shape and decoration
322
+
323
+ **`M3Shape`** - masks its content to one of the 35 shapes (square box). **`M3ShapeMorph`** - an SVG
324
+ shape that springs into the next when `shape` changes; fills with `currentColor`. **`M3Badge`** -
325
+ dot or count on its slot. Details: [shapes-type.md](shapes-type.md).
@@ -0,0 +1,42 @@
1
+ # Layout
2
+
3
+ ## The shell
4
+
5
+ - Window classes from `useWindowClass()`: compact < 600dp, medium < 840, expanded < 1200, large.
6
+ Key layout on these, never on device type - a phone in landscape is medium.
7
+ - Compact: `M3NavigationBar` floats at the bottom; the shell writes `--app-nav-offset` (64px while
8
+ visible) and `--app-bottom-inset` (offset + gesture area). Anything floating above content uses
9
+ them; nothing measures the bar.
10
+ - Medium and up: `M3NavigationRail` beside the views, expandable from its menu button.
11
+ - Pushed pages hide the bar (`AppPage back`), the rail stays.
12
+
13
+ ## Page anatomy (`AppPage`)
14
+
15
+ ```vue
16
+ <AppPage :title="t('feature.title')" name="feature"> <!-- large flexible bar -->
17
+ <template #actions><M3IconButton :label="…"><i-ms-search-rounded /></M3IconButton></template>
18
+ <SectionHeader :title="t('feature.section')" />
19
+ <M3List variant="segmented" inset>…</M3List>
20
+ <template #fab><M3Fab :label="…"><i-ms-add-rounded /></M3Fab></template>
21
+ <template #fixed><MyFeatureSheet v-model:open="open" /></template>
22
+ </AppPage>
23
+ ```
24
+
25
+ A pushed page: `<AppPage :title back>` (small bar, back button, bar hidden); `variant="medium"` for a
26
+ long title. Sheets and toolbars that must not scroll go in `#fixed`.
27
+
28
+ ## Spacing and rhythm
29
+
30
+ - Content gutters 16dp (`px-4`, `mx-4`, `inset` on lists); section labels 24dp (`px-6`).
31
+ - Between blocks 12-24dp; inside a block 8-16dp. Segments 2dp apart.
32
+ - Touch targets ≥ 48dp - the components extend small visuals with an invisible target.
33
+ - At most ~840dp of reading width on large windows: wrap long content in `max-w-[52rem] mx-auto`.
34
+
35
+ ## Placement
36
+
37
+ - One FAB, bottom-end, in `#fab`. With a floating toolbar, the FAB goes in the toolbar's `#fab`.
38
+ - Destructive actions go behind a menu or the end of an action sheet, never next to the main action.
39
+ - A detail screen's state changes go in `M3DockedToolbar` (connected button group).
40
+ - Forms of more than two fields open in an `M3BottomSheet` with the submit button in `#footer`.
41
+
42
+ See [sources.md](sources.md) → Layout.
@@ -0,0 +1,51 @@
1
+ # Motion
2
+
3
+ M3 Expressive motion is physics: springs with a damping ratio and a stiffness. **Spatial** springs
4
+ move position, size, rotation and shape and may overshoot; **effects** springs change colour and
5
+ opacity and never do. Speed follows the size of what moves: **fast** for small components, **default**
6
+ for partial-screen surfaces (sheets, menus), **slow** for full-screen moves.
7
+
8
+ ## The tokens
9
+
10
+ | Token | Expressive ζ / k | CSS easing var | Duration var |
11
+ | ----------------------------- | --------------------------- | ------------------------------------- | ------------------------------- |
12
+ | fast spatial | 0.6 / 800 (≈9.5% overshoot) | `--md-sys-motion-spring-fast-spatial` | `…-fast-spatial-duration` 350ms |
13
+ | default spatial | 0.8 / 380 | `…-default-spatial` | 500ms |
14
+ | slow spatial | 0.8 / 200 | `…-slow-spatial` | 650ms |
15
+ | fast / default / slow effects | 1.0 / 3800, 1600, 800 | `…-fast-effects` etc. | 150 / 200 / 300ms |
16
+
17
+ Spatial easings are generated `linear()` curves that keep the overshoot; effects are the official
18
+ beziers. Tailwind: `ease-fast-spatial`, `ease-default-effects`… with `duration-spring-fast`,
19
+ `duration-spring-default`, `duration-spring-slow`. Legacy tokens (`--md-sys-motion-easing-emphasized-
20
+ decelerate`, `--md-sys-motion-duration-medium2`…) remain for transitions that are not physical moves:
21
+ dialogs entering, fades.
22
+
23
+ ## Choosing the mechanism
24
+
25
+ - **CSS transition on a token** - state changes nobody interrupts: a toggle, a selected tab indicator,
26
+ a chip check, a list row rounding off.
27
+ - **`animateSpring`** (`@cavulsqa/m3e`) - anything a gesture drives or that can reverse mid-flight:
28
+ sheets, drags, interrupted opens. It carries velocity into the new target; a CSS transition restarts
29
+ from zero and stutters.
30
+ - **`useFrame`** (`@cavulsqa/m3e-vue`) - continuous drawing (progress, loaders). One shared rAF loop;
31
+ pause when off screen with `useInView`.
32
+ - **Vue `<Transition>` / `<TransitionGroup>`** - enter/leave of elements, with the tokens above.
33
+
34
+ ## Patterns already in the app
35
+
36
+ - Page change: `m3e-axis` shared-axis X (`assets/css/layout/transitions.css`) - slide 64px on slow
37
+ spatial, fade-through. Do not set per-route transitions.
38
+ - Press: shapes tighten on default effects (no bounce); selection reshapes on spatial.
39
+ - Enter from a FAB or anchor: scale/translate from that corner, stagger 15-30ms per item.
40
+ - Exit is always shorter than enter and accelerates.
41
+
42
+ ## Rules
43
+
44
+ - Animate `transform` and `opacity`. A layout property may animate only on a small element (a button's
45
+ padding in a group); never on a page-sized surface.
46
+ - Under reduced motion spatial durations are 1 ms (not 0 - `transitionend` must still fire); keep
47
+ fades and morphs, drop rotation, bounce and travel. JS motion passes `instant: reduced.value`.
48
+ - Haptics accompany a confirmed state change (`useHaptics().tick()` on a toggle, `confirm()` on a
49
+ completed action), never scrolling or hovering.
50
+
51
+ See [sources.md](sources.md) → Motion.
@@ -0,0 +1,48 @@
1
+ # Shape and type
2
+
3
+ ## Corner scale
4
+
5
+ | Tailwind | Token | dp | Typical use |
6
+ | ---------------------- | --------------------- | ---- | ------------------------------------------------- |
7
+ | `rounded-xs` | extra-small | 4 | inner corners of segmented lists, menus, snackbar |
8
+ | `rounded-sm` | small | 8 | chips, small thumbnails |
9
+ | `rounded-md` | medium | 12 | cards, inner panels |
10
+ | `rounded-lg` | large | 16 | outer corners of segments, grouped blocks |
11
+ | `rounded-lg-increased` | large-increased | 20 | medium FAB |
12
+ | `rounded-xl` | extra-large | 28 | sheets, dialogs, hero blocks |
13
+ | `rounded-xl-increased` | extra-large-increased | 32 | large hero surfaces |
14
+ | `rounded-2xl` | extra-extra-large | 48 | full-bleed media corners |
15
+ | `rounded-full` | full | pill | buttons, chips at rest, indicators |
16
+
17
+ Nested corners stay concentric: inner radius = outer radius − padding.
18
+
19
+ ## The shape library
20
+
21
+ 35 shapes (`MATERIAL_SHAPES`): circle, square, slanted, arch, fan, arrow, semiCircle, oval, pill,
22
+ triangle, diamond, clamShell, pentagon, gem, sunny, verySunny, cookie4Sided, cookie6Sided,
23
+ cookie7Sided, cookie9Sided, cookie12Sided, ghostish, clover4Leaf, clover8Leaf, burst, softBurst,
24
+ boom, softBoom, flower, puffy, puffyDiamond, pixelCircle, pixelTriangle, bun, heart. All visible in
25
+ Gallery → Shapes.
26
+
27
+ - **Use shapes for identity and emphasis, not for controls**: avatars and leading icons in lists
28
+ (`M3Shape` 40dp with `TONE_CLASSES`), hero art, empty states, status marks, selected swatches.
29
+ - **Morph to mark a change of state**: `M3ShapeMorph` springs between two shapes - selection, a step
30
+ completing, a status advancing.
31
+ - **Rounded shapes for calm, spiky (burst, boom) for celebration or alerts** - sparingly.
32
+ - A shape needs a square box; a photo cut to a shape uses `M3Shape` around the `<img>`.
33
+
34
+ ## Type
35
+
36
+ Google Sans Flex (self-hosted, `rond.css` - weight + roundness axes) for every style. Utilities:
37
+ `type-<style>` and `type-<style>-emphasized`, styles display/headline/title/body/label ×
38
+ large/medium/small. Sizes are in rem, so the user's font scale applies.
39
+
40
+ - Titles of screens: the app bar owns them. In content, `type-title-medium` for card titles,
41
+ `type-title-small-emphasized` (primary) for section labels, `type-body-large` for list headlines,
42
+ `type-body-medium` for supporting text, `type-label-large` for button-like labels.
43
+ - **Emphasized** styles are for the one thing on a surface that should land first: a total, a hero
44
+ headline, a selected value - not for every heading.
45
+ - `font-rounded` (ROND 100) belongs to display and headline moments in heroes and totals, never body.
46
+ - Numbers that change in place get `tabular-nums`.
47
+
48
+ See [sources.md](sources.md) → Shape, Typography.
@@ -0,0 +1,53 @@
1
+ # Official sources
2
+
3
+ m3.material.io renders in the browser only (fetching returns an empty shell): open it in a browser
4
+ tool. When the spec page and androidx source disagree, the androidx token files win - they are
5
+ generated from the same design-token database and are newer.
6
+
7
+ ## Foundations
8
+
9
+ - Overview and blog: https://m3.material.io/blog/building-with-m3-expressive
10
+ - Motion theming: https://m3.material.io/blog/m3-expressive-motion-theming
11
+ - State layers: https://m3.material.io/foundations/interaction/states/state-layers
12
+ - Accessibility: https://m3.material.io/foundations/accessible-design/overview
13
+ - Layout and window size classes: https://m3.material.io/foundations/layout/applying-layout ·
14
+ https://developer.android.com/develop/ui/compose/layouts/adaptive/use-window-size-classes
15
+
16
+ ## Styles
17
+
18
+ - Motion: https://m3.material.io/styles/motion/overview/how-it-works ·
19
+ specs and web curves: https://m3.material.io/styles/motion/overview/specs ·
20
+ transitions: https://m3.material.io/styles/motion/transitions/transition-patterns
21
+ - Colour: https://m3.material.io/styles/color/roles ·
22
+ https://m3.material.io/styles/color/choosing-a-scheme ·
23
+ https://m3.material.io/styles/color/dynamic-color/overview ·
24
+ custom colours: https://m3.material.io/styles/color/advanced/define-new-colors
25
+ - Shape: https://m3.material.io/styles/shape/corner-radius-scale ·
26
+ https://m3.material.io/styles/shape/shape-morph
27
+ - Typography: https://m3.material.io/styles/typography/type-scale-tokens ·
28
+ https://m3.material.io/styles/typography/fonts
29
+ - Elevation: https://m3.material.io/styles/elevation/tokens
30
+
31
+ ## Components (append `/specs` or `/guidelines`)
32
+
33
+ `https://m3.material.io/components/<name>/specs` for: buttons, icon-buttons, button-groups,
34
+ split-button, floating-action-button, extended-fab, fab-menu, loading-indicator,
35
+ progress-indicators, toolbars, navigation-bar, navigation-rail, app-bars, search, bottom-sheets,
36
+ side-sheets, dialogs, menus, lists, carousel, chips, sliders, switch, tabs, snackbar, cards,
37
+ text-fields, checkbox, radio-button, badges, divider, tooltips, date-pickers, time-pickers.
38
+
39
+ ## Source of the numbers
40
+
41
+ - Compose token files:
42
+ https://github.com/androidx/androidx/tree/androidx-main/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens
43
+ - Shapes: …/material3/MaterialShapes.kt · loader: …/LoadingIndicator.kt · wavy: …/WavyProgressIndicator.kt
44
+ (same folder as above, without `/tokens`)
45
+ - Colour library: https://github.com/material-foundation/material-color-utilities/tree/main/typescript
46
+ - MDC-Android component docs (before/after numbers):
47
+ https://github.com/material-components/material-components-android/tree/master/docs/components
48
+
49
+ ## Where a spec lands in this repo
50
+
51
+ `packages/m3e` holds the tokens and geometry (each file's JSDoc links its source);
52
+ `packages/m3e-vue` the components (each SFC's JSDoc links its spec page). Extend those, then use the
53
+ result here.