@jskit-ai/agent-docs 0.1.273 → 0.1.275
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/guide/agent/app-extras/assistant-conversation.md +7 -1
- package/guide/agent/app-extras/assistant-voice.md +5 -1
- package/guide/agent/app-setup/material-3-migration.md +180 -0
- package/guide/agent/app-setup/upgrading-jskit.md +5 -0
- package/guide/agent/framework/material-3.md +316 -73
- package/guide/agent/framework/ui-operations.md +18 -35
- package/guide/agent/framework/ui-pass.md +155 -0
- package/guide/agent/index.md +3 -0
- package/package.json +1 -1
- package/patterns/feature-package/example/booking-engine/package.json +1 -1
- package/patterns/minimal-foundation/example/package.json +5 -4
- package/patterns/minimal-foundation/example/src/main.js +5 -11
- package/patterns/minimal-foundation/example/src/pages/home/index.vue +5 -18
- package/patterns/minimal-foundation/example/src/theme/index.js +9 -0
- package/patterns/minimal-foundation/example/src/views/NotFound.vue +5 -4
- package/patterns/shell-foundation/example/package.json +6 -6
- package/patterns/shell-foundation/example/packages/main/package.json +1 -1
- package/patterns/shell-foundation/example/src/main.js +5 -11
- package/patterns/shell-foundation/example/src/pages/home/index.vue +21 -57
- package/patterns/shell-foundation/example/src/pages/home/settings/general/index.vue +24 -19
- package/patterns/shell-foundation/example/src/pages/home/settings.vue +27 -84
- package/patterns/shell-foundation/example/src/theme/index.js +9 -0
- package/patterns/shell-foundation/example/src/views/NotFound.vue +9 -7
- package/reference/autogen/PATTERN_INDEX.md +37 -37
- package/reference/autogen/README.md +1 -0
- package/reference/autogen/packages/agent-docs.md +8 -4
- package/reference/autogen/packages/assistant-core.md +3 -0
- package/reference/autogen/packages/console-web.md +5 -0
- package/reference/autogen/packages/http-web.md +29 -2
- package/reference/autogen/packages/kernel.md +2 -1
- package/reference/autogen/packages/material-web.md +54 -0
- package/reference/autogen/packages/shell-web.md +121 -9
- package/reference/autogen/tooling/jskit-catalog.md +81 -0
- package/reference/autogen/tooling/testUtils.md +7 -0
- package/skills/jskit/SKILL.md +11 -6
- package/skills/jskit/references/heuristics/INDEX.md +1 -1
- package/skills/jskit/references/heuristics/ui-contract.md +10 -7
- package/skills/jskit/references/heuristics/ui-testing.md +5 -3
- package/skills/jskit/references/material-3.md +315 -72
- package/skills/jskit/references/pattern-index.md +37 -37
- package/skills/jskit/references/patterns/app/minimal-foundation/example/package.json +5 -4
- package/skills/jskit/references/patterns/app/minimal-foundation/example/src/main.js +5 -11
- package/skills/jskit/references/patterns/app/minimal-foundation/example/src/pages/home/index.vue +5 -18
- package/skills/jskit/references/patterns/app/minimal-foundation/example/src/theme/index.js +9 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/src/views/NotFound.vue +5 -4
- package/skills/jskit/references/patterns/app/shell-foundation/example/package.json +6 -6
- package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/package.json +1 -1
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/main.js +5 -11
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home/index.vue +21 -57
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home/settings/general/index.vue +24 -19
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home/settings.vue +27 -84
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/theme/index.js +9 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/views/NotFound.vue +9 -7
- package/skills/jskit/references/patterns/assistant/assistant-surface/example/src/placement.js +1 -1
- package/skills/jskit/references/patterns/auth/accounts-hub/example/package.json +2 -2
- package/skills/jskit/references/patterns/auth/oidc-sign-in/example/package.json +2 -2
- package/skills/jskit/references/patterns/auth/supabase-auth/example/package.json +1 -1
- package/skills/jskit/references/patterns/connectors/calendar-cli/example/package.json +4 -4
- package/skills/jskit/references/patterns/console/console-surface/PATTERN.md +4 -2
- package/skills/jskit/references/patterns/console/console-surface/example/src/pages/console/index.vue +10 -40
- package/skills/jskit/references/patterns/console/console-surface/example/src/pages/console/settings/index.vue +3 -11
- package/skills/jskit/references/patterns/console/console-surface/example/src/pages/console/settings.vue +24 -72
- package/skills/jskit/references/patterns/console/console-surface/example/src/placement.js +28 -0
- package/skills/jskit/references/patterns/crud/crud-screen-set/PATTERN.md +13 -0
- package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookEditPage.vue +2 -0
- package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookFormFields.vue +0 -6
- package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookListPage.vue +5 -2
- package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookNewPage.vue +2 -0
- package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookViewPage.vue +5 -9
- package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/package.json +2 -2
- package/skills/jskit/references/patterns/database/mysql-application/example/package.json +1 -1
- package/skills/jskit/references/patterns/database/postgres-application/example/package.json +1 -1
- package/skills/jskit/references/patterns/realtime/realtime-application/example/package.json +2 -2
- package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/package.json +1 -1
- package/skills/jskit/references/patterns/shell/application-shell/PATTERN.md +3 -1
- package/skills/jskit/references/patterns/shell/application-shell/example/expected-existing/src/pages/home/index.vue +5 -18
- package/skills/jskit/references/patterns/shell/application-shell/example/expected-existing/src/views/NotFound.vue +14 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home/index.vue +21 -57
- package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home/settings/general/index.vue +24 -19
- package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home/settings.vue +27 -84
- package/skills/jskit/references/patterns/shell/application-shell/example/src/views/NotFound.vue +15 -0
- package/skills/jskit/references/patterns/ui/page-and-placement/example/src/components/SyncStatusElement.vue +2 -2
- package/skills/jskit/references/patterns/ui/page-and-placement/example/src/pages/home/reports/activity.vue +1 -1
- package/skills/jskit/references/patterns/ui/page-and-placement/example/src/pages/home/reports/overview.vue +1 -1
- package/skills/jskit/references/patterns/ui/page-and-placement/example/src/pages/home/reports.vue +6 -3
- package/skills/jskit/references/patterns/ui/page-and-placement/example/src/placement.js +1 -1
- package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsNotificationsSection.vue +2 -8
- package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsPreferencesSection.vue +2 -8
- package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsProfileSection.vue +5 -11
- package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsSignInMethodsSection.vue +3 -9
- package/skills/jskit/references/patterns/users/account-settings/example/src/pages/account/index.vue +7 -0
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/package.json +2 -2
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/package.json +3 -3
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/packages/main/src/client/components/AccountPendingInvitesCue.vue +1 -2
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/components/WorkspaceNotFoundCard.vue +5 -6
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/pages/admin/workspace/settings/index.vue +5 -12
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/pages/admin/workspace/settings.vue +24 -72
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/surfaces/admin/index.vue +3 -4
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/surfaces/app/index.vue +3 -3
- package/skills/jskit/references/ui-operations.md +18 -35
- package/skills/jskit/references/ui-pass.md +150 -0
- package/patterns/shell-foundation/example/src/pages/home/settings/index.vue +0 -7
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home/settings/index.vue +0 -7
- package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home/settings/index.vue +0 -7
|
@@ -2,51 +2,251 @@
|
|
|
2
2
|
|
|
3
3
|
# Material 3
|
|
4
4
|
|
|
5
|
-
Read completely before any JSKIT Vue/Vuetify UI task.
|
|
5
|
+
Read completely before any JSKIT Vue/Vuetify UI task. For a UI pass over a
|
|
6
|
+
task's changes, a section or the whole app, also read [UI pass](ui-pass.md).
|
|
6
7
|
|
|
7
8
|
## Authority
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
app
|
|
10
|
+
Material 3 (m3.material.io) is the UI contract, and it is exact: "close to
|
|
11
|
+
Material 3" is a failure. A new JSKIT app is exact Material 3 in light and dark,
|
|
12
|
+
on phone, tablet and desktop, with no app CSS. An app chooses a seed colour and
|
|
13
|
+
writes its own screens. If an app has to fight JSKIT to look right, that is a
|
|
14
|
+
JSKIT defect: report the exact JSKIT change, never patch it with app CSS,
|
|
15
|
+
`!important` or a copied package file.
|
|
16
|
+
|
|
17
|
+
JSKIT uses Vue and the installed Vuetify 4 release with its `md3` blueprint.
|
|
12
18
|
Use the installed public APIs rather than adding `@material/web`, another UI
|
|
13
|
-
framework, or a parallel theme/component system.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
on
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
19
|
+
framework, or a parallel theme/component system. The request and Blueprint own
|
|
20
|
+
brand, content, routes, and behavior; Material 3 never authorizes a behavior
|
|
21
|
+
change. An established JSKIT prop or component wins over generic Material advice.
|
|
22
|
+
|
|
23
|
+
## Navigation and Up
|
|
24
|
+
|
|
25
|
+
**MUST, in every JSKIT app (#241). The shell owns the Up arrow and the trail.
|
|
26
|
+
An app declares each inner page's parent, links through JSKIT navigation, and
|
|
27
|
+
writes no back logic of any kind. Plain `jskit check` fails on every rule in
|
|
28
|
+
this section, and nothing excuses one. Never improvise a Back.**
|
|
29
|
+
|
|
30
|
+
### The model
|
|
31
|
+
|
|
32
|
+
**Back** is chronological and can leave the app; the browser provides it (its
|
|
33
|
+
button, gestures, Alt+Left), and the app never draws or imitates it. **Up** is
|
|
34
|
+
the app's own arrow, the leading icon of the top app bar; it moves within the
|
|
35
|
+
app and never leaves it.
|
|
36
|
+
|
|
37
|
+
- **Words:** a *top-level destination* is a main-navigation item (bar, rail or
|
|
38
|
+
drawer): Contacts, Suppliers, Settings. An *inner page* is any other page: a
|
|
39
|
+
record, sub-page, new or edit form, or a wizard step with its own URL. Its
|
|
40
|
+
*parent* is the page it belongs under (supplier → Suppliers, edit supplier →
|
|
41
|
+
that supplier). The *trail* is the in-app path taken in this browser tab.
|
|
42
|
+
- **What the person sees:** one Up arrow on every inner page, also when opened
|
|
43
|
+
directly: icon only (`$arrowBack`), accessible name "Back" (localized), a
|
|
44
|
+
tooltip on pointer devices. None on top-level destinations, and no other
|
|
45
|
+
back control anywhere; the bar title says where the person is. A list-detail
|
|
46
|
+
layout showing the parent beside the page needs no arrow for that step;
|
|
47
|
+
collapsed to one pane, the record pane gets it back.
|
|
48
|
+
- **Where Up goes:** to the previous trail entry while it is valid, so inside
|
|
49
|
+
the app Up works like Back (drill-down, sideways, chains unwinding one step
|
|
50
|
+
at a time); otherwise to the declared parent (shared link, bookmark, new
|
|
51
|
+
tab). An entry whose record was deleted, whose permission was lost, or whose
|
|
52
|
+
surface, workspace or route is gone sends Up to the parent, never an error
|
|
53
|
+
page. Up never leaves the app or opens sign-in.
|
|
54
|
+
- **The trail:** one per tab; a refresh keeps it, a new tab or shared link
|
|
55
|
+
starts empty. Navigating to another page pushes an entry; Save and Cancel on
|
|
56
|
+
a form, redirects, and same-page changes (filters, search, sort, paging, an
|
|
57
|
+
in-page tab, a dialog) replace it. A top-level destination starts a fresh
|
|
58
|
+
trail. Going back restores filters, sort, page, scroll and focus. The
|
|
59
|
+
browser's Back and Forward stay in sync with it; external links never enter.
|
|
60
|
+
|
|
61
|
+
### What an app declares
|
|
62
|
+
|
|
63
|
+
Exactly this, and nothing more:
|
|
64
|
+
|
|
65
|
+
1. **Each inner page declares its parent once,** as route meta `jskit.parent`:
|
|
66
|
+
a static path on the page's own surface, resolved like `usePaths().page()`,
|
|
67
|
+
whose `:param` segments take the current route's values. Another surface's
|
|
68
|
+
page is an object, `{ surface: "home", path: "/" }`. No functions, no route
|
|
69
|
+
names.
|
|
70
|
+
|
|
71
|
+
```vue
|
|
72
|
+
<script setup>
|
|
73
|
+
definePage({ meta: { jskit: { parent: "/suppliers/:supplierId" } } });
|
|
74
|
+
</script>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The same key works in a `<route lang="json">` block. A record names its
|
|
78
|
+
list, an edit page its record, a sub-page its page. Tabs under a layout
|
|
79
|
+
(`settings.vue` rendering `<RouterView>` for `settings/`) declare nothing:
|
|
80
|
+
they inherit the nearest layout's declaration, and moving between them keeps
|
|
81
|
+
the trail.
|
|
82
|
+
2. **Each top-level destination is a main-navigation item** (a `kind: "link"`
|
|
83
|
+
placement with `target: "shell.primary-nav"` or `"shell.secondary-nav"`) and
|
|
84
|
+
declares no parent; nor do its tabs. A shell page with no parent is
|
|
85
|
+
top-level: no arrow, a fresh trail.
|
|
86
|
+
3. **Every in-app link and navigation goes through JSKIT navigation:**
|
|
87
|
+
`:to="paths.page('/suppliers')"` and `router.push(paths.page(...))` with
|
|
88
|
+
`usePaths()`, the CRUD runtime's `resolveViewUrl()`, `resolveEditUrl()` and
|
|
89
|
+
`resolveParams()`, and link placements (`surface` with `scopedSuffix` and
|
|
90
|
+
`unscopedSuffix`; `to` only for a global route such as `/auth/login`). The
|
|
91
|
+
trail follows the router, so there is no special link component.
|
|
92
|
+
4. **A custom new or edit page finishes through the shell:**
|
|
93
|
+
`useShellNavigation()` (`@jskit-ai/shell-web/client/navigation/useShellNavigation`)
|
|
94
|
+
gives `finish(target)` for Save (the target replaces the form in the trail)
|
|
95
|
+
and `finish()` for Cancel (back to where the form was opened, else its
|
|
96
|
+
parent). A page whose record turns out deleted or forbidden calls
|
|
97
|
+
`pageUnavailable()`. JSKIT's CRUD screens already do both.
|
|
98
|
+
5. **A full page load goes through the shell too:** `reloadAppPage(path)`
|
|
99
|
+
(after sign-in or a user switch, to rebuild client state) and
|
|
100
|
+
`leaveApp(url)` (an external site in the same tab, such as a payment page),
|
|
101
|
+
from `@jskit-ai/shell-web/client/navigation/documentNavigation`.
|
|
102
|
+
|
|
103
|
+
### Forbidden in app code
|
|
104
|
+
|
|
105
|
+
| Rule | Forbidden |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `nav/history` | `history.back()`, `history.go()`, `history.pushState()`/`replaceState()`, `router.back()`, `router.go()` |
|
|
108
|
+
| `nav/window-location` | `window.location` assignments, `.href =`, `location.assign()`, `location.replace()` |
|
|
109
|
+
| `nav/raw-link` | a hand-built in-app path in `to`, `href`, `router.push()`, `router.replace()` or a `to:`/`href:` object key outside a placement |
|
|
110
|
+
| `nav/back-control` | any back control but the shell's Up: "Back to X", "← Suppliers", back arrows and navigating chevrons, breadcrumbs, a custom app-bar back button, a back component, a `goBack()`-style handler |
|
|
111
|
+
| `nav/missing-parent` | an inner page of a shell surface without a declared parent |
|
|
112
|
+
| `nav/top-level-parent` | a main-navigation destination that declares a parent |
|
|
113
|
+
| `nav/allow` | any `m3-check: allow nav/...` comment: nothing excuses a navigation rule |
|
|
114
|
+
|
|
115
|
+
### Each rule, right and wrong
|
|
116
|
+
|
|
117
|
+
- **Up follows the trail.** Right: contact → (link) supplier → Up shows **the
|
|
118
|
+
contact**; contact → supplier → product → Up = supplier, Up = contact, Up =
|
|
119
|
+
Contacts. Wrong: the supplier's Up opens the Suppliers list after the person
|
|
120
|
+
came from a contact.
|
|
121
|
+
- **Direct entry goes to the parent.** Right: a supplier opened from a pasted
|
|
122
|
+
link → Up = Suppliers list. Wrong: no arrow, a dead arrow, or an arrow that
|
|
123
|
+
leaves the app.
|
|
124
|
+
- **One arrow, on inner pages only.** Right: the Suppliers list (a drawer item)
|
|
125
|
+
has no arrow; a supplier has one, in the bar. Wrong: an arrow on the
|
|
126
|
+
Suppliers list, a second arrow in a card, or a "Back to suppliers" button.
|
|
127
|
+
- **Declare the parent.** Right: `definePage({ meta: { jskit: { parent:
|
|
128
|
+
"/suppliers" } } })` on the supplier. Wrong: no declaration, a parent on
|
|
129
|
+
Suppliers itself, or a page that computes its own "previous page".
|
|
130
|
+
- **Full loads through the shell.** Right: `reloadAppPage(returnTo)` after
|
|
131
|
+
sign-in; `leaveApp(checkoutUrl)` for the payment page. Wrong:
|
|
132
|
+
`window.location.replace(returnTo)` or `location.assign(checkoutUrl)`.
|
|
133
|
+
- **No history.** Right: the page declares its parent and nothing else.
|
|
134
|
+
Wrong: `@click="router.back()"`, `history.go(-1)`, or a `goBack()` helper.
|
|
135
|
+
- **Links through JSKIT.** Right: `<v-btn :to="records.resolveViewUrl(record)">`
|
|
136
|
+
or `:to="paths.page('/suppliers')"`. Wrong: `to="/home/suppliers"`,
|
|
137
|
+
`` :to="`/home/suppliers/${id}`" ``, `router.push("/home/suppliers")` or
|
|
138
|
+
`window.location.href = url`.
|
|
139
|
+
- **Forms replace themselves.** Right: record → Edit → Save calls
|
|
140
|
+
`finish(recordUrl)`; the record shows, and Up goes where the record's Up went
|
|
141
|
+
before. Wrong: `router.push(recordUrl)` after Save, so Up or Back reopens the
|
|
142
|
+
form.
|
|
143
|
+
|
|
144
|
+
## Foundation
|
|
145
|
+
|
|
146
|
+
- **Theme from one seed:** the app's only theme file is `src/theme/index.js`,
|
|
147
|
+
`export const theme = { seed: "#6750A4", icons: {} }`, and `src/main.js` runs
|
|
148
|
+
`createVuetify(createMaterialVuetifyOptions(theme))` with
|
|
149
|
+
`createMaterialVuetifyOptions` from `@jskit-ai/material-web/client`, importing
|
|
150
|
+
`@jskit-ai/material-web/client/material.css`. JSKIT generates the light and
|
|
151
|
+
dark schemes from the seed at startup (Material Color Utilities
|
|
152
|
+
`SchemeTonalSpot`, every role in kebab-case such as `surface-container-low`,
|
|
153
|
+
plus success, warning and info with their containers), with
|
|
154
|
+
`defaultTheme: "system"` so the person's light, dark or system preference
|
|
155
|
+
applies. The `md3` blueprint and JSKIT's Material 3 Vuetify defaults live in
|
|
156
|
+
material-web; an app keeps no defaults file and never hand-picks a colour. The
|
|
157
|
+
seed is the only hex value.
|
|
158
|
+
- **Type:** Vuetify 4 Material 3 roles (`text-headline-small`,
|
|
159
|
+
`text-title-large`, `text-body-medium`, `text-label-large`). Never the
|
|
160
|
+
Material 2 scale (`text-h5`, `text-body-2`) or a page-local type system.
|
|
161
|
+
Roboto Flex is the font.
|
|
162
|
+
- **Icons:** Material Symbols Rounded through Vuetify `$` aliases: Vuetify's
|
|
163
|
+
standard ones (`$close`, `$menu`, `$delete`, `$prev`, `$next`) and JSKIT's
|
|
164
|
+
camelCase names (`$add`, `$edit`, `$search`, `$moreVert`, `$filterList`,
|
|
165
|
+
`$arrowBack`); `$homeFilled`-style `Filled` variants mark active navigation.
|
|
166
|
+
An app adds an icon under `theme.icons` in `src/theme/index.js` as
|
|
167
|
+
`{ name: "<24x24 path data>" }` from Material Symbols Rounded, then uses
|
|
168
|
+
`$name`. No `mdi-*` strings and no `@mdi/js` imports in app code.
|
|
169
|
+
|
|
170
|
+
## Exact sizes
|
|
171
|
+
|
|
172
|
+
JSKIT ships these exact sizes; an app never re-implements or overrides them,
|
|
173
|
+
and needs no Sass and no `!important` (Vuetify 4 keeps its CSS in cascade
|
|
174
|
+
layers):
|
|
175
|
+
|
|
176
|
+
- 40dp buttons with a 48dp touch target, icon buttons included;
|
|
177
|
+
- 12dp cards;
|
|
178
|
+
- 28dp dialogs and bottom-sheet top corners, square full-screen dialogs;
|
|
179
|
+
- 8dp chips and 16dp FABs;
|
|
180
|
+
- emphasized easing, with reduced motion honoured.
|
|
181
|
+
|
|
182
|
+
So never set `height` or `min-height` on a button, or a radius on a card,
|
|
183
|
+
dialog, chip or FAB. A missing size is a JSKIT defect.
|
|
184
|
+
|
|
185
|
+
## The top app bar carries the page
|
|
186
|
+
|
|
187
|
+
- **Title:** route meta `jskit.title` (plain text), or
|
|
188
|
+
`useShellPageTitle(title)` (`@jskit-ai/shell-web/client/composables/useShellPageTitle`)
|
|
189
|
+
for a runtime title such as the record's name. The shell renders it as the
|
|
190
|
+
app bar's only `h1`; pages add no `h1`, page header, title block or
|
|
191
|
+
standalone heading. A section inside a page uses a title role.
|
|
192
|
+
- **Up** sits in the bar on every inner page (see Navigation and Up). On phones
|
|
193
|
+
it replaces the menu icon.
|
|
194
|
+
- **Actions:** a screen's actions go in the bar inside `ShellPageActions`
|
|
195
|
+
(`@jskit-ai/shell-web/client/components/ShellPageActions`), each a
|
|
196
|
+
`ShellPageAction` with `icon`, `label` (also its accessible name and compact
|
|
197
|
+
tooltip), and `primary`, `to`, `replace`, `disabled`, `loading` or `color`.
|
|
198
|
+
On compact widths every action shows only its icon except the single
|
|
199
|
+
`primary` one.
|
|
200
|
+
- **Window size classes:** compact under 600 (navigation bar), medium 600–839
|
|
201
|
+
(navigation rail), expanded 840–1199 and large from 1200 (drawer). Read them
|
|
202
|
+
from `useShellWindowSizeClass()`
|
|
203
|
+
(`@jskit-ai/shell-web/client/composables/useShellWindowSizeClass`), never from
|
|
204
|
+
copied breakpoint numbers.
|
|
205
|
+
|
|
206
|
+
## Colour
|
|
207
|
+
|
|
208
|
+
- Use semantic roles through Vuetify props, classes and
|
|
209
|
+
`rgb(var(--v-theme-<role>))`; never a raw colour value in a component.
|
|
210
|
+
- Status chips use `color="success"` (or `warning`, `info`, `error`) or the
|
|
211
|
+
container roles (`success-container` with `on-success-container`), and each
|
|
212
|
+
status meaning has its own hue. Never communicate status, selection, validation, or permission through
|
|
213
|
+
color alone.
|
|
214
|
+
- Surfaces are separated by tone (`surface-container-lowest` to
|
|
215
|
+
`-highest`), not borders: no `variant="outlined"` cards and no `border` on
|
|
216
|
+
cards or sheets. A VCard default `color` becomes the text colour on outlined
|
|
217
|
+
and tonal cards.
|
|
218
|
+
- Keep WCAG AA contrast in both schemes. Use elevation `0` through `5` and
|
|
219
|
+
component variants instead of arbitrary shadows.
|
|
220
|
+
|
|
221
|
+
## Lists, records and forms
|
|
222
|
+
|
|
223
|
+
- **List:** an M3 search bar, then one row of filter chips, each opening its own
|
|
224
|
+
control; never a permanent panel of outlined fields. A card opens its record
|
|
225
|
+
when tapped (a stretched title link); a table row opens on click. Row actions
|
|
226
|
+
are trailing icon buttons (edit, a `$moreVert` overflow menu, a chevron to
|
|
227
|
+
open), never "View" or "Edit" text buttons on every row. "New" is an extended
|
|
228
|
+
FAB on compact and medium widths and a filled bar button on expanded. Empty
|
|
229
|
+
and error states carry an icon.
|
|
230
|
+
- **A list never scrolls sideways, at any width.** The list's own width (a
|
|
231
|
+
container query, not the window) chooses cards or table. Cards flow into a
|
|
232
|
+
grid; tables use a fixed layout with an ellipsis on long values, and fold
|
|
233
|
+
low-priority columns into a stronger column's second line. Lists show figures
|
|
234
|
+
("120 kg", tabular and right-aligned) and names, never ids.
|
|
235
|
+
- **Record:** a header (avatar, title, status chips) and value-first detail
|
|
236
|
+
lists, never a grid of label/value text. Edit sits in the bar; the record's
|
|
237
|
+
next workflow step is the one filled button, shown in context rather than a
|
|
238
|
+
row of mostly disabled actions.
|
|
239
|
+
- **Form:** Cancel and Save sit in the bar. Fields use `hideDetails: "auto"`.
|
|
240
|
+
Nothing moves under a finger: a multi-select chip field keeps its input width
|
|
241
|
+
when it loses focus. A pick-one-of-N choice is a segmented button with
|
|
242
|
+
`role="radiogroup"` and `role="radio"`/`aria-checked` buttons.
|
|
243
|
+
- **Dialogs:** plain `v-dialog` and `v-bottom-sheet`; JSKIT gives them their
|
|
244
|
+
Material 3 shapes. A full-screen dialog (compact and medium) puts close, title
|
|
245
|
+
and its confirm action in a top bar, never at the bottom where a snackbar
|
|
246
|
+
covers them.
|
|
247
|
+
- **Menus and badges:** a menu's list has `role="menu"` and its items
|
|
248
|
+
`role="menuitem"`, with `aria-disabled` on unavailable ones; a badge has a
|
|
249
|
+
`label` such as "3 batches".
|
|
50
250
|
|
|
51
251
|
## Components, state, and feedback
|
|
52
252
|
|
|
@@ -57,8 +257,7 @@ Do not replace a shared screen to restyle it. Use its public seams.
|
|
|
57
257
|
icon-only actions without relying on color; give icon controls accessible
|
|
58
258
|
names and fields meaningful labels, errors, and autocomplete behavior.
|
|
59
259
|
- Preserve visible focus, logical tab order, keyboard operation, and at least
|
|
60
|
-
48 CSS-pixel interactive targets
|
|
61
|
-
another contract.
|
|
260
|
+
48 CSS-pixel interactive targets.
|
|
62
261
|
- Represent loading, empty, error, retry, disabled, selected, hover, focus,
|
|
63
262
|
pressed, and success states through shared JSKIT behavior. All user-visible
|
|
64
263
|
loading uses Material skeletons that reserve the final content geometry;
|
|
@@ -70,18 +269,69 @@ Do not replace a shared screen to restyle it. Use its public seams.
|
|
|
70
269
|
field. A user-triggered command uses JSKIT's shared action
|
|
71
270
|
feedback/snackbar path. Never insert a transient command-error alert above
|
|
72
271
|
page content where it shifts the working layout.
|
|
73
|
-
- Use established transitions and honor reduced motion. Do not claim
|
|
74
|
-
unsupported Material 3 Expressive parity.
|
|
75
272
|
|
|
76
273
|
## Adaptive layout
|
|
77
274
|
|
|
78
|
-
Start compact, then
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
275
|
+
Start compact, then check medium and expanded. Let navigation, tables, filters,
|
|
276
|
+
supporting content, and action groups change presentation; do not merely shrink
|
|
277
|
+
an expanded screen. Prevent overflow, clipped labels, overlapping actions,
|
|
278
|
+
duplicate navigation, and unreachable dialogs. Persist screen context such as
|
|
279
|
+
filters, tab and page in the route when it must survive navigation.
|
|
280
|
+
|
|
281
|
+
## The conformance check
|
|
282
|
+
|
|
283
|
+
Two commands read application source (no server, no browser) and print each
|
|
284
|
+
finding with its file, line, rule and the section of this page:
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
npx jskit check # in every app's verify: fails on any nav/* rule
|
|
288
|
+
npx jskit check m3 # the whole app: src/ and packages/
|
|
289
|
+
npx jskit check m3 src/pages/home/books # a section
|
|
290
|
+
npx jskit check m3 $(git diff --name-only --diff-filter=d main) # changed files
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Plain `jskit check` fails on the [navigation rules](#forbidden-in-app-code);
|
|
294
|
+
fix every one, because nothing excuses them. `jskit check m3` lists those and
|
|
295
|
+
the visual rules below. The visual rules are a report until every JSKIT app has
|
|
296
|
+
been converted to them; the JSKIT release after that makes plain `jskit check`,
|
|
297
|
+
and so every app's verify, fail on them too:
|
|
298
|
+
|
|
299
|
+
| Rule | Finds |
|
|
300
|
+
|---|---|
|
|
301
|
+
| `m3/duplicate-h1` | a second `h1`, or an in-page `h1` in an app that uses the JSKIT shell |
|
|
302
|
+
| `m3/raw-hex` | a hex colour outside `src/theme/` |
|
|
303
|
+
| `m3/dark-scheme` | no dark scheme, or the theme pinned to light |
|
|
304
|
+
| `m3/outlined-card` | `variant="outlined"` or `border` on a card or sheet |
|
|
305
|
+
| `m3/mdi-icon` | `mdi-*` icon strings and `@mdi/js` imports |
|
|
306
|
+
| `m3/fixed-button-height` | `height`/`min-height` on buttons, in props, styles or CSS |
|
|
307
|
+
|
|
308
|
+
Tests, fixtures, server code and migrations are not checked. A clean check is
|
|
309
|
+
necessary, not sufficient: it cannot see layout, spacing or contrast. A
|
|
310
|
+
[UI pass](ui-pass.md) is the guard for those, because it looks at every
|
|
311
|
+
affected screen at three widths in light and dark; an editor that runs one
|
|
312
|
+
automatically after each plan keeps that guard on every change.
|
|
313
|
+
|
|
314
|
+
### Exceptions
|
|
315
|
+
|
|
316
|
+
Take the approved route first; none needs an exception:
|
|
317
|
+
|
|
318
|
+
- **Brand or partner colours** are theme custom colours, used by name:
|
|
319
|
+
`colors: { partner: "#0A7D3E" }` in `src/theme/index.js`, then
|
|
320
|
+
`<v-chip color="partner">`.
|
|
321
|
+
- **Chart and data colours** come from the theme: `const theme = useTheme()`,
|
|
322
|
+
then `["primary", "tertiary"].map((role) => theme.current.value.colors[role])`.
|
|
323
|
+
- **A third-party widget** lives in one wrapper component, such as
|
|
324
|
+
`src/components/RouteMap.vue`, that hands it theme colours; pages use only
|
|
325
|
+
the wrapper.
|
|
326
|
+
|
|
327
|
+
What is left may be excused one line at a time, on its line or the line
|
|
328
|
+
before: `<!-- m3-check: allow m3/raw-hex - the partner's contractual logo colour -->`
|
|
329
|
+
(`// m3-check: ...` in scripts, `/* ... */` in CSS). **Never add one on your
|
|
330
|
+
own:** ask the person first, naming the rule and the reason. A comment with no
|
|
331
|
+
reason (`m3/allow-reason`) or an unknown rule (`m3/allow-rule`), and any
|
|
332
|
+
`m3-check: allow nav/...` (`nav/allow`), is itself a finding; nothing turns the
|
|
333
|
+
check off for a file, a folder or the app. `jskit check m3` lists every
|
|
334
|
+
exception with its file, line and reason, and their total.
|
|
85
335
|
|
|
86
336
|
## Material 3 audit
|
|
87
337
|
|
|
@@ -89,29 +339,22 @@ Run this behavior-preserving audit over every affected screen:
|
|
|
89
339
|
|
|
90
340
|
1. Identify the owning JSKIT surface, shared screen, shell, placement, theme,
|
|
91
341
|
and Vuetify components before judging local markup.
|
|
92
|
-
2.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
342
|
+
2. Check navigation and Up first, as must-fix: plain `jskit check` passes;
|
|
343
|
+
every inner page has a parent and one Up arrow in the bar; main-menu
|
|
344
|
+
destinations have none; no inner link is hand-built; nothing writes back
|
|
345
|
+
logic.
|
|
346
|
+
3. Find parallel components, duplicated state chrome, page-local themes, raw
|
|
347
|
+
visual constants, CSS against Vuetify internals, nested generic cards,
|
|
348
|
+
borders instead of tones, Material 2 typography, and desktop-only layout.
|
|
349
|
+
4. Check hierarchy, token use, component purpose, responsive presentation,
|
|
98
350
|
interaction states, keyboard/focus behavior, labels, targets, contrast, and
|
|
99
|
-
reduced motion.
|
|
100
|
-
4. Audit Back on every affected page using [Back navigation](ui-operations.md#back-navigation):
|
|
101
|
-
verify its presence or justified omission, actual previous-page destination,
|
|
102
|
-
safe direct-entry fallback, and retained origin context. Inspect handlers and
|
|
103
|
-
route bindings; a hardcoded parent/list link is an explicit exception, not
|
|
104
|
-
the default. Check label, accessible name, consistent leading placement,
|
|
105
|
-
keyboard order, responsive reachability, duplicates, and distinction from
|
|
106
|
-
Cancel, Close, or Previous step. Report misleading navigation as a finding;
|
|
107
|
-
fix behavior only within the requested scope, otherwise report it separately
|
|
108
|
-
from behavior-preserving cleanup.
|
|
351
|
+
reduced motion against the rules above.
|
|
109
352
|
5. Correct issues at the narrowest established owner. Consolidate only truly
|
|
110
353
|
repeated policy; avoid pass-through wrappers and speculative abstractions.
|
|
111
|
-
6. Run
|
|
112
|
-
|
|
354
|
+
6. Run `jskit check` and `jskit check m3` on the affected files, then focused
|
|
355
|
+
tests and Playwright at compact, medium, and expanded widths in light and
|
|
356
|
+
dark (`shellBackButton(page)` and `pressShellBack(page)` from
|
|
357
|
+
`@jskit-ai/shell-web/test/shellNavigation` drive Up).
|
|
113
358
|
|
|
114
359
|
Do not declare Material 3 compliance from visual resemblance alone. Report any
|
|
115
360
|
unverified responsive, theme, accessibility, interaction, or browser behavior.
|
|
116
|
-
For a missing or version-sensitive API, verify the installed Vuetify major and
|
|
117
|
-
official docs; implement Material intent through supported JSKIT/Vuetify seams.
|