@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.
- package/README.md +12 -1
- package/bin/create.mjs +18 -25
- package/lib/templates.mjs +56 -0
- package/package.json +13 -5
- package/templates/f7-app/CLAUDE.md +3 -0
- package/templates/f7-app/auto-imports.d.ts +15 -0
- package/templates/f7-app/package.json +6 -3
- package/templates/f7-app/src/App.vue +1 -0
- package/templates/f7-app/src/env.d.ts +1 -0
- package/templates/f7-app/src/main.ts +3 -0
- package/templates/f7-app/src/modules/demo/router/routes/demo.routes.ts +4 -16
- package/templates/f7-app/src/modules/home/router/routes/home.routes.ts +3 -11
- package/templates/f7-app/src/modules/settings/router/routes/settings.routes.ts +2 -6
- package/templates/f7-app/src/plugins/bootstrapError.ts +2 -0
- package/templates/f7-app/src/plugins/recorder.plugin.ts +155 -0
- package/templates/f7-app/src/router/global/global.routes.ts +2 -6
- package/templates/f7-app/src/shared/composables/useNavigationGuard.ts +48 -0
- package/templates/f7-app/src/shared/recorder/capacitorSink.ts +124 -0
- package/templates/f7-app/src/shared/utils/lazyRoute.ts +25 -0
- package/templates/f7-app/tests/lazyRoute.test.ts +30 -0
- package/templates/f7-app/tests/navigationGuard.test.ts +82 -0
- package/templates/f7-app/vite.config.ts +1 -0
- package/templates/m3e-app/.claude/rules/data-fetching.md +68 -0
- package/templates/m3e-app/.claude/rules/database.md +106 -0
- package/templates/m3e-app/.claude/rules/m3e-ui.md +48 -0
- package/templates/m3e-app/.claude/rules/modules.md +43 -0
- package/templates/m3e-app/.claude/rules/native.md +60 -0
- package/templates/m3e-app/.claude/skills/m3-expressive/SKILL.md +68 -0
- package/templates/m3e-app/.claude/skills/m3-expressive/color.md +44 -0
- package/templates/m3e-app/.claude/skills/m3-expressive/components.md +325 -0
- package/templates/m3e-app/.claude/skills/m3-expressive/layout.md +42 -0
- package/templates/m3e-app/.claude/skills/m3-expressive/motion.md +51 -0
- package/templates/m3e-app/.claude/skills/m3-expressive/shapes-type.md +48 -0
- package/templates/m3e-app/.claude/skills/m3-expressive/sources.md +53 -0
- package/templates/m3e-app/.claude/skills/module-architecture/SKILL.md +65 -0
- package/templates/m3e-app/.claude/skills/module-architecture/file-templates.md +178 -0
- package/templates/m3e-app/.claude/skills/reactive-data/SKILL.md +90 -0
- package/templates/m3e-app/.claude/skills/reactive-data/testing.md +55 -0
- package/templates/m3e-app/.env.example +32 -0
- package/templates/m3e-app/CLAUDE.md +93 -0
- package/templates/m3e-app/auto-imports.d.ts +851 -0
- package/templates/m3e-app/capacitor.config.ts +44 -0
- package/templates/m3e-app/components.d.ts +252 -0
- package/templates/m3e-app/index.html +16 -0
- package/templates/m3e-app/package.json +107 -0
- package/templates/m3e-app/src/App.vue +129 -0
- package/templates/m3e-app/src/app/pragmas.config.ts +35 -0
- package/templates/m3e-app/src/app/scroll.config.ts +20 -0
- package/templates/m3e-app/src/app/storage.config.ts +65 -0
- package/templates/m3e-app/src/app/tabs.ts +33 -0
- package/templates/m3e-app/src/app/theme.config.ts +51 -0
- package/templates/m3e-app/src/assets/css/app.css +18 -0
- package/templates/m3e-app/src/assets/css/base.css +53 -0
- package/templates/m3e-app/src/assets/css/layout/container-transform.css +113 -0
- package/templates/m3e-app/src/assets/css/layout/shell.css +69 -0
- package/templates/m3e-app/src/assets/css/layout/transitions.css +121 -0
- package/templates/m3e-app/src/assets/css/m3e.css +6 -0
- package/templates/m3e-app/src/assets/css/theme/framework7.css +18 -0
- package/templates/m3e-app/src/assets/css/theme/tailwind.css +365 -0
- package/templates/m3e-app/src/domains/benchmark/benchmark.dataset.ts +209 -0
- package/templates/m3e-app/src/domains/benchmark/benchmark.suite.ts +662 -0
- package/templates/m3e-app/src/domains/sales/sales.repository.ts +458 -0
- package/templates/m3e-app/src/env.d.ts +40 -0
- package/templates/m3e-app/src/locales/ar.json +1260 -0
- package/templates/m3e-app/src/locales/en.json +1260 -0
- package/templates/m3e-app/src/locales/fr.json +1260 -0
- package/templates/m3e-app/src/main.ts +41 -0
- package/templates/m3e-app/src/modules/demo/components/DemoBenchmark.vue +119 -0
- package/templates/m3e-app/src/modules/demo/components/DemoBusLog.vue +63 -0
- package/templates/m3e-app/src/modules/demo/components/DemoCreateOrderSheet.vue +203 -0
- package/templates/m3e-app/src/modules/demo/components/DemoMetricsSheet.vue +75 -0
- package/templates/m3e-app/src/modules/demo/components/DemoOrderList.vue +75 -0
- package/templates/m3e-app/src/modules/demo/components/DemoPipelineBenchmark.vue +65 -0
- package/templates/m3e-app/src/modules/demo/components/DemoStatCards.vue +71 -0
- package/templates/m3e-app/src/modules/demo/composables/useBenchmark.ts +139 -0
- package/templates/m3e-app/src/modules/demo/composables/useOrderStatus.ts +64 -0
- package/templates/m3e-app/src/modules/demo/composables/useReactiveDemo.ts +171 -0
- package/templates/m3e-app/src/modules/demo/router/routes/demo.routes.ts +22 -0
- package/templates/m3e-app/src/modules/demo/views/DemoView.vue +210 -0
- package/templates/m3e-app/src/modules/demo/views/OrderDetailView.vue +136 -0
- package/templates/m3e-app/src/modules/demo/views/OrderSearchView.vue +74 -0
- package/templates/m3e-app/src/modules/gallery/components/GalleryBlock.vue +16 -0
- package/templates/m3e-app/src/modules/gallery/components/GalleryCarouselTile.vue +71 -0
- package/templates/m3e-app/src/modules/gallery/components/GalleryCustomerForm.vue +76 -0
- package/templates/m3e-app/src/modules/gallery/components/GalleryProofOfDelivery.vue +69 -0
- package/templates/m3e-app/src/modules/gallery/components/agenda/AgendaDay.vue +106 -0
- package/templates/m3e-app/src/modules/gallery/components/agenda/AgendaMonth.vue +40 -0
- package/templates/m3e-app/src/modules/gallery/components/agenda/AgendaVisitList.vue +45 -0
- package/templates/m3e-app/src/modules/gallery/components/chat/ChatAttachSheet.vue +106 -0
- package/templates/m3e-app/src/modules/gallery/components/chat/ChatContactPicker.vue +85 -0
- package/templates/m3e-app/src/modules/gallery/components/chat/ChatInviteComposer.vue +131 -0
- package/templates/m3e-app/src/modules/gallery/components/chat/ChatPollComposer.vue +148 -0
- package/templates/m3e-app/src/modules/gallery/components/chat/ChatRecentPhotos.vue +62 -0
- package/templates/m3e-app/src/modules/gallery/components/inputs/InputsAccount.vue +79 -0
- package/templates/m3e-app/src/modules/gallery/components/inputs/InputsTags.vue +53 -0
- package/templates/m3e-app/src/modules/gallery/components/inputs/InputsVerification.vue +73 -0
- package/templates/m3e-app/src/modules/gallery/components/inputs/PasswordStrength.vue +37 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryButtons.vue +128 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryCarousels.vue +236 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryCharts.vue +84 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryFabs.vue +70 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryInputs.vue +219 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryNavigation.vue +141 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryOverlays.vue +368 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryPickers.vue +175 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryProgress.vue +118 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryScale.vue +103 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GallerySelection.vue +219 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryShapes.vue +48 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GallerySurfaces.vue +213 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryTables.vue +185 -0
- package/templates/m3e-app/src/modules/gallery/components/surfaces/SurfacesContainerTransform.vue +46 -0
- package/templates/m3e-app/src/modules/gallery/composables/chatTeam.ts +18 -0
- package/templates/m3e-app/src/modules/gallery/composables/composeKind.ts +2 -0
- package/templates/m3e-app/src/modules/gallery/composables/currentPosition.ts +35 -0
- package/templates/m3e-app/src/modules/gallery/composables/demoOrders.ts +29 -0
- package/templates/m3e-app/src/modules/gallery/composables/featuredStories.ts +73 -0
- package/templates/m3e-app/src/modules/gallery/composables/fieldFormats.ts +36 -0
- package/templates/m3e-app/src/modules/gallery/composables/openExternal.ts +11 -0
- package/templates/m3e-app/src/modules/gallery/composables/routePlan.ts +90 -0
- package/templates/m3e-app/src/modules/gallery/composables/useAgenda.ts +61 -0
- package/templates/m3e-app/src/modules/gallery/composables/useChatCustomers.ts +31 -0
- package/templates/m3e-app/src/modules/gallery/composables/useChatDemo.ts +299 -0
- package/templates/m3e-app/src/modules/gallery/composables/useGallerySections.ts +230 -0
- package/templates/m3e-app/src/modules/gallery/composables/useLocalAttachments.ts +71 -0
- package/templates/m3e-app/src/modules/gallery/composables/useLocationShare.ts +38 -0
- package/templates/m3e-app/src/modules/gallery/composables/usePhotoScenes.ts +82 -0
- package/templates/m3e-app/src/modules/gallery/composables/useTreeDemo.ts +67 -0
- package/templates/m3e-app/src/modules/gallery/composables/wilayas.ts +67 -0
- package/templates/m3e-app/src/modules/gallery/router/routes/gallery.routes.ts +52 -0
- package/templates/m3e-app/src/modules/gallery/views/GalleryAgendaView.vue +117 -0
- package/templates/m3e-app/src/modules/gallery/views/GalleryChatView.vue +292 -0
- package/templates/m3e-app/src/modules/gallery/views/GalleryContactsView.vue +77 -0
- package/templates/m3e-app/src/modules/gallery/views/GalleryFeaturedView.vue +60 -0
- package/templates/m3e-app/src/modules/gallery/views/GalleryLoginView.vue +135 -0
- package/templates/m3e-app/src/modules/gallery/views/GalleryOnboardingView.vue +116 -0
- package/templates/m3e-app/src/modules/gallery/views/GallerySectionView.vue +27 -0
- package/templates/m3e-app/src/modules/gallery/views/GalleryTabsView.vue +135 -0
- package/templates/m3e-app/src/modules/gallery/views/GalleryView.vue +50 -0
- package/templates/m3e-app/src/modules/home/components/HomeHero.vue +57 -0
- package/templates/m3e-app/src/modules/home/composables/useHomeFeatures.ts +128 -0
- package/templates/m3e-app/src/modules/home/composables/useOpenFeature.ts +22 -0
- package/templates/m3e-app/src/modules/home/router/routes/home.routes.ts +21 -0
- package/templates/m3e-app/src/modules/home/views/FeatureDetailView.vue +66 -0
- package/templates/m3e-app/src/modules/home/views/HomeView.vue +47 -0
- package/templates/m3e-app/src/modules/settings/components/RolePalette.vue +38 -0
- package/templates/m3e-app/src/modules/settings/components/SeedSwatches.vue +41 -0
- package/templates/m3e-app/src/modules/settings/components/SettingsChoice.vue +43 -0
- package/templates/m3e-app/src/modules/settings/components/StudioPreview.vue +52 -0
- package/templates/m3e-app/src/modules/settings/router/routes/settings.routes.ts +17 -0
- package/templates/m3e-app/src/modules/settings/types.ts +7 -0
- package/templates/m3e-app/src/modules/settings/views/ColorStudioView.vue +166 -0
- package/templates/m3e-app/src/modules/settings/views/SettingsView.vue +157 -0
- package/templates/m3e-app/src/plugins/bootstrapError.ts +62 -0
- package/templates/m3e-app/src/plugins/capacitor/index.ts +14 -0
- package/templates/m3e-app/src/plugins/capacitor/useAndroidBackButton.ts +36 -0
- package/templates/m3e-app/src/plugins/capacitor/useKeyboard.ts +48 -0
- package/templates/m3e-app/src/plugins/capacitor/useSplashScreen.ts +43 -0
- package/templates/m3e-app/src/plugins/capacitor/useStatusBar.ts +16 -0
- package/templates/m3e-app/src/plugins/framework7.plugin.ts +36 -0
- package/templates/m3e-app/src/plugins/i18n.plugin.ts +87 -0
- package/templates/m3e-app/src/plugins/m3e.plugin.ts +22 -0
- package/templates/m3e-app/src/plugins/seed.plugin.ts +7 -0
- package/templates/m3e-app/src/plugins/sqlite.plugin.ts +32 -0
- package/templates/m3e-app/src/router/global/global.routes.ts +12 -0
- package/templates/m3e-app/src/router/index.ts +20 -0
- package/templates/m3e-app/src/shared/components/error/404.vue +13 -0
- package/templates/m3e-app/src/shared/components/layout/EmptyState.vue +30 -0
- package/templates/m3e-app/src/shared/components/layout/SectionHeader.vue +10 -0
- package/templates/m3e-app/src/shared/components/page/AppPage.vue +104 -0
- package/templates/m3e-app/src/shared/composables/navigation/useActiveTab.ts +39 -0
- package/templates/m3e-app/src/shared/composables/navigation/useContainerTransform.ts +117 -0
- package/templates/m3e-app/src/shared/composables/navigation/useNavigationGuard.ts +48 -0
- package/templates/m3e-app/src/shared/composables/navigation/useNavigationVisibility.ts +64 -0
- package/templates/m3e-app/src/shared/composables/navigation/useViewRouter.ts +25 -0
- package/templates/m3e-app/src/shared/composables/navigation/useWindowClass.ts +22 -0
- package/templates/m3e-app/src/shared/composables/theme/useThemeSettings.ts +155 -0
- package/templates/m3e-app/src/shared/database/candidates/capacitorSqlite.ts +36 -0
- package/templates/m3e-app/src/shared/database/candidates/index.ts +4 -0
- package/templates/m3e-app/src/shared/database/candidates/opfsSahPool.ts +25 -0
- package/templates/m3e-app/src/shared/database/candidates/types.ts +56 -0
- package/templates/m3e-app/src/shared/database/candidates/waSqlite.ts +56 -0
- package/templates/m3e-app/src/shared/database/database.ts +219 -0
- package/templates/m3e-app/src/shared/database/index.ts +3 -0
- package/templates/m3e-app/src/shared/database/migrations.ts +223 -0
- package/templates/m3e-app/src/shared/database/opfs.worker.ts +5 -0
- package/templates/m3e-app/src/shared/database/queries.ts +26 -0
- package/templates/m3e-app/src/shared/database/schema.ts +153 -0
- package/templates/m3e-app/src/shared/database/storage.ts +64 -0
- package/templates/m3e-app/src/shared/database/wa.worker.ts +5 -0
- package/templates/m3e-app/src/shared/utils/lazyRoute.ts +41 -0
- package/templates/m3e-app/src/shared/utils/resolvers/resolvers.ts +42 -0
- package/templates/m3e-app/src/shared/utils/textDirection.ts +22 -0
- package/templates/m3e-app/src/shared/utils/theme/themeSettings.ts +59 -0
- package/templates/m3e-app/src/shared/utils/tone.ts +8 -0
- package/templates/m3e-app/tests/benchmark.suite.test.ts +104 -0
- package/templates/m3e-app/tests/containerTransform.test.ts +58 -0
- package/templates/m3e-app/tests/fieldFormats.test.ts +25 -0
- package/templates/m3e-app/tests/lazyRoute.test.ts +30 -0
- package/templates/m3e-app/tests/locales.test.ts +73 -0
- package/templates/m3e-app/tests/migrations.test.ts +74 -0
- package/templates/m3e-app/tests/navigationGuard.test.ts +82 -0
- package/templates/m3e-app/tests/openDatabase.test.ts +54 -0
- package/templates/m3e-app/tests/sales.repository.test.ts +329 -0
- package/templates/m3e-app/tests/storage.test.ts +117 -0
- package/templates/m3e-app/tests/textDirection.test.ts +11 -0
- package/templates/m3e-app/tooling/linkedPackages.ts +112 -0
- package/templates/m3e-app/tsconfig.json +21 -0
- 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.
|