@vmz/ui 0.3.5 → 0.3.6
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 +51 -51
- package/package.json +2 -2
- package/presets/web-surface.v0.json +19 -7
- package/src/components/Accordion.vmz +94 -94
- package/src/components/AppShell.vmz +77 -33
- package/src/components/Autocomplete.vmz +154 -154
- package/src/components/Badge.vmz +70 -70
- package/src/components/Breadcrumb.vmz +72 -72
- package/src/components/BulkActions.vmz +51 -51
- package/src/components/Button.vmz +211 -192
- package/src/components/Callout.vmz +77 -77
- package/src/components/Card.vmz +120 -106
- package/src/components/Checkbox.vmz +55 -55
- package/src/components/CodeBlock.vmz +77 -18
- package/src/components/ConsoleShell.vmz +251 -251
- package/src/components/DataTable.vmz +194 -194
- package/src/components/DatePicker.vmz +674 -674
- package/src/components/Dialog.vmz +10 -7
- package/src/components/Drawer.vmz +427 -427
- package/src/components/Dropdown.vmz +135 -16
- package/src/components/Empty.vmz +54 -54
- package/src/components/Field.vmz +115 -115
- package/src/components/FormItem.vmz +79 -79
- package/src/components/Link.vmz +34 -4
- package/src/components/List.vmz +102 -102
- package/src/components/Menu.vmz +243 -243
- package/src/components/Pagination.vmz +69 -69
- package/src/components/Popover.vmz +223 -223
- package/src/components/Prose.vmz +81 -81
- package/src/components/RadioGroup.vmz +124 -124
- package/src/components/Segmented.vmz +51 -51
- package/src/components/Select.vmz +533 -533
- package/src/components/Steps.vmz +103 -103
- package/src/components/Switch.vmz +98 -98
- package/src/components/Table.vmz +165 -133
- package/src/components/Tabs.vmz +110 -110
- package/src/components/Tag.vmz +43 -40
- package/src/components/TextArea.vmz +118 -118
- package/src/components/Timeline.vmz +76 -76
- package/src/components/Toc.vmz +74 -74
- package/src/components/Tooltip.vmz +58 -58
- package/src/components/Tree.vmz +191 -191
- package/src/components/Upload.vmz +1289 -1289
package/README.md
CHANGED
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
# @vmz/ui — VMZ UI
|
|
2
|
-
|
|
3
|
-
Official **VMZ UI** framework package.
|
|
4
|
-
|
|
5
|
-
```text
|
|
6
|
-
VMZ Design ≠ VMZ UI
|
|
7
|
-
/designs ≠ this package
|
|
8
|
-
@vmz/ui ≠ theme truth source
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
- Declares **semantic token requirements** (see `contracts/`).
|
|
12
|
-
- Ships ordinary `.vmz` under **`src/components/`** (same hard convention as apps) — **not** `@vmz/plugin-*`, no
|
|
13
|
-
`componentsRoot` config.
|
|
14
|
-
- **Never** hardcodes brand hex (`#176BFF`, etc.). Applications provide values via `/designs`.
|
|
15
|
-
|
|
16
|
-
## Preview `0.0.4`
|
|
17
|
-
|
|
18
|
-
This is a **preview** release. It is **not** `production-ready` and **not** a mature / production UI.
|
|
19
|
-
|
|
20
|
-
Headline surface for this cut:
|
|
21
|
-
|
|
22
|
-
- Commercial / Console / Document-Product composition on official homepage
|
|
23
|
-
- Form depth, Structure, Overlay stacking, DataTable (ordinary HTML table — not `@vmz/ui-data-grid`)
|
|
24
|
-
- DatePicker owned calendar overlay (not native `input[type=date]` popup)
|
|
25
|
-
- Upload multipart binary (parent-owned FormData → `#server` File/Blob; progress/cancel)
|
|
26
|
-
- Density / RTL / preset materialize
|
|
27
|
-
- Motion continuity + interrupt/cancel
|
|
28
|
-
- Motion compiler IR thin gate (`pnpm verify -- motion-ir`)
|
|
29
|
-
- UI7 conformance pack (`@vmz/ui/conformance` + `pnpm verify -- ui7`)
|
|
30
|
-
- `@vmz/ui-data-grid` thin gate (virtualization + pinned column — not tree/group/pivot/edit)
|
|
31
|
-
|
|
32
|
-
Verify:
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
pnpm verify -- ui-automation
|
|
36
|
-
pnpm verify -- ui7
|
|
37
|
-
pnpm verify -- ui-data-grid
|
|
38
|
-
pnpm verify -- official-homepage
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Still open relative to Browser Production Profile v1: `browser-production` aggregate core gaps,
|
|
42
|
-
`@vmz/ui-data-grid` deep capabilities, Upload cross-page session / multi-file parallel chunks,
|
|
43
|
-
`@vmz/test` Browser Host U0–U1, sibling `vmz-panel` product app.
|
|
44
|
-
|
|
45
|
-
## Commercial default surface
|
|
46
|
-
|
|
47
|
-
Default web surface targets modern commercial console density: flat 32px-class controls,
|
|
48
|
-
quiet borders, restrained radii, layered page/paper surfaces without Bootstrap-era inset
|
|
49
|
-
fields, accent bars, or card lift. Applications replace every semantic value through `/designs`.
|
|
50
|
-
Additional general-purpose components include layout primitives, typography, segmented controls,
|
|
51
|
-
tags, avatars, dropdowns, collapse panels and progress indicators.
|
|
1
|
+
# @vmz/ui — VMZ UI
|
|
2
|
+
|
|
3
|
+
Official **VMZ UI** framework package.
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
VMZ Design ≠ VMZ UI
|
|
7
|
+
/designs ≠ this package
|
|
8
|
+
@vmz/ui ≠ theme truth source
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- Declares **semantic token requirements** (see `contracts/`).
|
|
12
|
+
- Ships ordinary `.vmz` under **`src/components/`** (same hard convention as apps) — **not** `@vmz/plugin-*`, no
|
|
13
|
+
`componentsRoot` config.
|
|
14
|
+
- **Never** hardcodes brand hex (`#176BFF`, etc.). Applications provide values via `/designs`.
|
|
15
|
+
|
|
16
|
+
## Preview `0.0.4`
|
|
17
|
+
|
|
18
|
+
This is a **preview** release. It is **not** `production-ready` and **not** a mature / production UI.
|
|
19
|
+
|
|
20
|
+
Headline surface for this cut:
|
|
21
|
+
|
|
22
|
+
- Commercial / Console / Document-Product composition on official homepage
|
|
23
|
+
- Form depth, Structure, Overlay stacking, DataTable (ordinary HTML table — not `@vmz/ui-data-grid`)
|
|
24
|
+
- DatePicker owned calendar overlay (not native `input[type=date]` popup)
|
|
25
|
+
- Upload multipart binary (parent-owned FormData → `#server` File/Blob; progress/cancel)
|
|
26
|
+
- Density / RTL / preset materialize
|
|
27
|
+
- Motion continuity + interrupt/cancel
|
|
28
|
+
- Motion compiler IR thin gate (`pnpm verify -- motion-ir`)
|
|
29
|
+
- UI7 conformance pack (`@vmz/ui/conformance` + `pnpm verify -- ui7`)
|
|
30
|
+
- `@vmz/ui-data-grid` thin gate (virtualization + pinned column — not tree/group/pivot/edit)
|
|
31
|
+
|
|
32
|
+
Verify:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pnpm verify -- ui-automation
|
|
36
|
+
pnpm verify -- ui7
|
|
37
|
+
pnpm verify -- ui-data-grid
|
|
38
|
+
pnpm verify -- official-homepage
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Still open relative to Browser Production Profile v1: `browser-production` aggregate core gaps,
|
|
42
|
+
`@vmz/ui-data-grid` deep capabilities, Upload cross-page session / multi-file parallel chunks,
|
|
43
|
+
`@vmz/test` Browser Host U0–U1, sibling `vmz-panel` product app.
|
|
44
|
+
|
|
45
|
+
## Commercial default surface
|
|
46
|
+
|
|
47
|
+
Default web surface targets modern commercial console density: flat 32px-class controls,
|
|
48
|
+
quiet borders, restrained radii, layered page/paper surfaces without Bootstrap-era inset
|
|
49
|
+
fields, accent bars, or card lift. Applications replace every semantic value through `/designs`.
|
|
50
|
+
Additional general-purpose components include layout primitives, typography, segmented controls,
|
|
51
|
+
tags, avatars, dropdowns, collapse panels and progress indicators.
|
package/package.json
CHANGED
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"license": "MIT",
|
|
83
83
|
"name": "@vmz/ui",
|
|
84
84
|
"peerDependencies": {
|
|
85
|
-
"@vmz/ui-icons": "0.3.
|
|
85
|
+
"@vmz/ui-icons": "0.3.6"
|
|
86
86
|
},
|
|
87
87
|
"peerDependenciesMeta": {
|
|
88
88
|
"@vmz/ui-icons": {
|
|
@@ -98,5 +98,5 @@
|
|
|
98
98
|
"url": "git+https://github.com/doki-land/vmz-framework.git"
|
|
99
99
|
},
|
|
100
100
|
"type": "module",
|
|
101
|
-
"version": "0.3.
|
|
101
|
+
"version": "0.3.6"
|
|
102
102
|
}
|
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
"package": "@vmz/ui",
|
|
5
5
|
"materialize": "Copy or resolve into application /designs Style Theme (tokens/*.json + themes/*.json). Preset is not a second runtime theme.",
|
|
6
6
|
"foundation": {
|
|
7
|
-
"radius": {
|
|
7
|
+
"radius": {
|
|
8
|
+
"xs": "2px",
|
|
9
|
+
"sm": "4px",
|
|
10
|
+
"md": "6px",
|
|
11
|
+
"lg": "8px",
|
|
12
|
+
"pill": "999px"
|
|
13
|
+
},
|
|
8
14
|
"controlHeight": { "sm": "1.5rem", "middle": "2rem", "lg": "2.5rem" },
|
|
9
15
|
"layoutMaxWidth": "75rem",
|
|
10
16
|
"surfacePage": "#F5F5F5",
|
|
@@ -14,22 +20,28 @@
|
|
|
14
20
|
"lineSubtle": "#F0F0F0",
|
|
15
21
|
"shadowCard": "none",
|
|
16
22
|
"shadowCardHover": "0 1px 2px -2px rgba(0,0,0,.08), 0 3px 6px 0 rgba(0,0,0,.06), 0 5px 12px 4px rgba(0,0,0,.04)",
|
|
17
|
-
"shadowPopup": "0 6px 16px 0 rgba(0,0,0,.08), 0 3px 6px -4px rgba(0,0,0,.12), 0 9px 28px 8px rgba(0,0,0,.05)"
|
|
23
|
+
"shadowPopup": "0 6px 16px 0 rgba(0,0,0,.08), 0 3px 6px -4px rgba(0,0,0,.12), 0 9px 28px 8px rgba(0,0,0,.05)",
|
|
24
|
+
"elevation": {
|
|
25
|
+
"note": "Floating layers only. Cards stay flat per visualIntent; interactive surfaces lift on hover, never at rest.",
|
|
26
|
+
"float-sm": "0 1px 2px 0 rgba(0,0,0,.03), 0 1px 6px -1px rgba(0,0,0,.02), 0 2px 4px 0 rgba(0,0,0,.02)",
|
|
27
|
+
"float-md": "0 6px 16px 0 rgba(0,0,0,.08), 0 3px 6px -4px rgba(0,0,0,.12), 0 9px 28px 8px rgba(0,0,0,.05)",
|
|
28
|
+
"float-lg": "0 6px 16px 0 rgba(0,0,0,.08), 0 3px 6px -4px rgba(0,0,0,.12), 0 9px 28px 8px rgba(0,0,0,.05), 0 12px 48px 16px rgba(0,0,0,.03)"
|
|
29
|
+
}
|
|
18
30
|
},
|
|
19
31
|
"density": {
|
|
20
32
|
"control": {
|
|
21
33
|
"padding-y": "0.25rem",
|
|
22
|
-
"padding-x": "0.
|
|
34
|
+
"padding-x": "0.9375rem",
|
|
23
35
|
"gap": "0.5rem"
|
|
24
36
|
},
|
|
25
37
|
"compact": {
|
|
26
38
|
"padding-y": "0.125rem",
|
|
27
|
-
"padding-x": "0.
|
|
28
|
-
"gap": "0.
|
|
39
|
+
"padding-x": "0.625rem",
|
|
40
|
+
"gap": "0.375rem"
|
|
29
41
|
},
|
|
30
42
|
"dense": {
|
|
31
43
|
"padding-y": "0",
|
|
32
|
-
"padding-x": "0.
|
|
44
|
+
"padding-x": "0.4375rem",
|
|
33
45
|
"gap": "0.25rem"
|
|
34
46
|
}
|
|
35
47
|
},
|
|
@@ -44,5 +56,5 @@
|
|
|
44
56
|
"dark": "themes/dark.json overlay",
|
|
45
57
|
"high-contrast": "themes/high-contrast.json overlay — stronger ink/line/focus"
|
|
46
58
|
},
|
|
47
|
-
"visualIntent": "Modern commercial product chrome: neutral structure borders (never brand outline on every box), flat cards, soft focus rings, quiet page fill. Brand color is for actions/selection — not scaffolding."
|
|
59
|
+
"visualIntent": "Modern commercial product chrome: neutral structure borders (never brand outline on every box), flat cards, soft focus rings, quiet page fill. Brand color is for actions/selection — not scaffolding. Elevation is reserved for floating layers and hover lift; nothing casts a resting shadow. Motion eases, never jumps: control transitions use a decelerate curve, overlays use a fast-in curve, and reduced-motion is honored."
|
|
48
60
|
}
|
|
@@ -1,94 +1,94 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div data-vmz-ui="accordion" :aria-label="label" class="vmz-ui-accordion">
|
|
3
|
-
<div v-for="item in items" :key="item.id" :data-vmz-accordion="item.id" class="vmz-ui-accordion__item">
|
|
4
|
-
<h3 class="vmz-ui-accordion__heading">
|
|
5
|
-
<button type="button" :id="item.triggerId" :data-vmz-accordion-trigger="item.id" :aria-expanded="openId === item.id ? 'true' : 'false'" :aria-controls="item.panelId" @click="(() => toggle(item.id))" class="vmz-ui-accordion__trigger">
|
|
6
|
-
{{ item.title }}
|
|
7
|
-
</button>
|
|
8
|
-
</h3>
|
|
9
|
-
<div v-if="openId === item.id" :id="item.panelId" role="region" :data-vmz-accordion-panel="item.id" :aria-labelledby="item.triggerId" class="vmz-ui-accordion__panel">
|
|
10
|
-
{{ item.body }}
|
|
11
|
-
</div>
|
|
12
|
-
</div>
|
|
13
|
-
</div>
|
|
14
|
-
</template>
|
|
15
|
-
<style>
|
|
16
|
-
.vmz-ui-accordion {
|
|
17
|
-
display: flex;
|
|
18
|
-
flex-direction: column;
|
|
19
|
-
gap: 0.
|
|
20
|
-
max-width: 36rem;
|
|
21
|
-
border: 1px solid var(--vmz-line-default);
|
|
22
|
-
border-radius: var(--vmz-radius-md, 6px);
|
|
23
|
-
background: var(--vmz-surface-paper);
|
|
24
|
-
color: var(--vmz-text-ink);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
.vmz-ui-accordion__item {
|
|
28
|
-
border-bottom: 1px solid var(--vmz-line-default);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
.vmz-ui-accordion__item:last-child {
|
|
32
|
-
border-bottom: none;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.vmz-ui-accordion__heading {
|
|
36
|
-
margin: 0;
|
|
37
|
-
font: inherit;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.vmz-ui-accordion__trigger {
|
|
41
|
-
display: block;
|
|
42
|
-
width: 100%;
|
|
43
|
-
text-align: start;
|
|
44
|
-
font: inherit;
|
|
45
|
-
font-weight: 600;
|
|
46
|
-
padding: var(--vmz-density-control-padding-y) var(--vmz-density-control-padding-x);
|
|
47
|
-
border: 0;
|
|
48
|
-
background: transparent;
|
|
49
|
-
color: inherit;
|
|
50
|
-
cursor: pointer;
|
|
51
|
-
outline: none;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
.vmz-ui-accordion__trigger:hover {
|
|
55
|
-
background: color-mix(in srgb, var(--vmz-surface-mist) 85%, transparent);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.vmz-ui-accordion__trigger:focus-visible {
|
|
59
|
-
box-shadow: inset 0 0 0
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.vmz-ui-accordion__trigger[aria-expanded="true"] {
|
|
63
|
-
color: var(--vmz-action-primary-active);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.vmz-ui-accordion__panel {
|
|
67
|
-
padding: 0 var(--vmz-density-control-padding-x) var(--vmz-density-control-padding-y);
|
|
68
|
-
color: var(--vmz-text-steel);
|
|
69
|
-
line-height: 1.5;
|
|
70
|
-
}
|
|
71
|
-
</style>
|
|
72
|
-
<script client>
|
|
73
|
-
/**
|
|
74
|
-
* VMZ UI — Accordion.
|
|
75
|
-
* Exclusive open: parent owns `openId`. Toggle same id closes.
|
|
76
|
-
* Panel body lives on items (no per-panel slot runtime).
|
|
77
|
-
*/
|
|
78
|
-
export default class Accordion {
|
|
79
|
-
public label: string = "Accordion";
|
|
80
|
-
public openId: string = "";
|
|
81
|
-
/** @type {{ id: string, title: string, body: string, triggerId: string, panelId: string }[]} */
|
|
82
|
-
public items: {
|
|
83
|
-
id: string;
|
|
84
|
-
title: string;
|
|
85
|
-
body: string;
|
|
86
|
-
triggerId: string;
|
|
87
|
-
panelId: string;
|
|
88
|
-
}[] = [];
|
|
89
|
-
public onToggle: ((id: string) => void) | null = null;
|
|
90
|
-
toggle(id) {
|
|
91
|
-
if (typeof this.onToggle === "function") this.onToggle(id);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
</script>
|
|
1
|
+
<template>
|
|
2
|
+
<div data-vmz-ui="accordion" :aria-label="label" class="vmz-ui-accordion">
|
|
3
|
+
<div v-for="item in items" :key="item.id" :data-vmz-accordion="item.id" class="vmz-ui-accordion__item">
|
|
4
|
+
<h3 class="vmz-ui-accordion__heading">
|
|
5
|
+
<button type="button" :id="item.triggerId" :data-vmz-accordion-trigger="item.id" :aria-expanded="openId === item.id ? 'true' : 'false'" :aria-controls="item.panelId" @click="(() => toggle(item.id))" class="vmz-ui-accordion__trigger">
|
|
6
|
+
{{ item.title }}
|
|
7
|
+
</button>
|
|
8
|
+
</h3>
|
|
9
|
+
<div v-if="openId === item.id" :id="item.panelId" role="region" :data-vmz-accordion-panel="item.id" :aria-labelledby="item.triggerId" class="vmz-ui-accordion__panel">
|
|
10
|
+
{{ item.body }}
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
<style>
|
|
16
|
+
.vmz-ui-accordion {
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
gap: 0.375rem;
|
|
20
|
+
max-width: 36rem;
|
|
21
|
+
border: 1px solid var(--vmz-line-default);
|
|
22
|
+
border-radius: var(--vmz-radius-md, 6px);
|
|
23
|
+
background: var(--vmz-surface-paper);
|
|
24
|
+
color: var(--vmz-text-ink);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.vmz-ui-accordion__item {
|
|
28
|
+
border-bottom: 1px solid var(--vmz-line-default);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.vmz-ui-accordion__item:last-child {
|
|
32
|
+
border-bottom: none;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.vmz-ui-accordion__heading {
|
|
36
|
+
margin: 0;
|
|
37
|
+
font: inherit;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.vmz-ui-accordion__trigger {
|
|
41
|
+
display: block;
|
|
42
|
+
width: 100%;
|
|
43
|
+
text-align: start;
|
|
44
|
+
font: inherit;
|
|
45
|
+
font-weight: 600;
|
|
46
|
+
padding: var(--vmz-density-control-padding-y) var(--vmz-density-control-padding-x);
|
|
47
|
+
border: 0;
|
|
48
|
+
background: transparent;
|
|
49
|
+
color: inherit;
|
|
50
|
+
cursor: pointer;
|
|
51
|
+
outline: none;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.vmz-ui-accordion__trigger:hover {
|
|
55
|
+
background: color-mix(in srgb, var(--vmz-surface-mist) 85%, transparent);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.vmz-ui-accordion__trigger:focus-visible {
|
|
59
|
+
box-shadow: inset 0 0 0 3px var(--vmz-focus-ring);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.vmz-ui-accordion__trigger[aria-expanded="true"] {
|
|
63
|
+
color: var(--vmz-action-primary-active);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.vmz-ui-accordion__panel {
|
|
67
|
+
padding: 0 var(--vmz-density-control-padding-x) var(--vmz-density-control-padding-y);
|
|
68
|
+
color: var(--vmz-text-steel);
|
|
69
|
+
line-height: 1.5;
|
|
70
|
+
}
|
|
71
|
+
</style>
|
|
72
|
+
<script client>
|
|
73
|
+
/**
|
|
74
|
+
* VMZ UI — Accordion.
|
|
75
|
+
* Exclusive open: parent owns `openId`. Toggle same id closes.
|
|
76
|
+
* Panel body lives on items (no per-panel slot runtime).
|
|
77
|
+
*/
|
|
78
|
+
export default class Accordion {
|
|
79
|
+
public label: string = "Accordion";
|
|
80
|
+
public openId: string = "";
|
|
81
|
+
/** @type {{ id: string, title: string, body: string, triggerId: string, panelId: string }[]} */
|
|
82
|
+
public items: {
|
|
83
|
+
id: string;
|
|
84
|
+
title: string;
|
|
85
|
+
body: string;
|
|
86
|
+
triggerId: string;
|
|
87
|
+
panelId: string;
|
|
88
|
+
}[] = [];
|
|
89
|
+
public onToggle: ((id: string) => void) | null = null;
|
|
90
|
+
toggle(id) {
|
|
91
|
+
if (typeof this.onToggle === "function") this.onToggle(id);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
</script>
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
{{ brand }}
|
|
7
7
|
</a>
|
|
8
8
|
<nav :aria-label="navLabel" class="vmz-ui-app-shell__nav">
|
|
9
|
-
<a v-for="item in nav" :key="item.id" :href="item.href" :data-vmz-nav="item.id">
|
|
10
|
-
{{ item.title }}
|
|
11
|
-
</a>
|
|
9
|
+
<a v-for="item in nav" :key="item.id" :href="item.href" :data-vmz-nav="item.id" :data-active="item.id === activeId ? 'true' : 'false'" :aria-current="item.id === activeId ? 'page' : null" class="vmz-ui-app-shell__nav-item">{{ item.title }}</a>
|
|
12
10
|
</nav>
|
|
11
|
+
<div class="vmz-ui-app-shell__actions">
|
|
12
|
+
<slot name="actions" />
|
|
13
|
+
</div>
|
|
13
14
|
</div>
|
|
14
15
|
</header>
|
|
15
16
|
<main data-vmz-shell="main" class="vmz-ui-app-shell__main">
|
|
@@ -31,83 +32,124 @@
|
|
|
31
32
|
top: 0;
|
|
32
33
|
z-index: 30;
|
|
33
34
|
border-bottom: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
|
|
34
|
-
background: var(--vmz-surface-paper);
|
|
35
|
+
background: color-mix(in srgb, var(--vmz-surface-paper) 88%, transparent);
|
|
36
|
+
backdrop-filter: saturate(180%) blur(0.5rem);
|
|
37
|
+
-webkit-backdrop-filter: saturate(180%) blur(0.5rem);
|
|
35
38
|
}
|
|
39
|
+
|
|
36
40
|
.vmz-ui-app-shell__header-inner {
|
|
37
41
|
width: min(var(--vmz-layout-max-width, 75rem), calc(100% - 2rem));
|
|
38
|
-
min-height:
|
|
42
|
+
min-height: 3.5rem;
|
|
39
43
|
margin: 0 auto;
|
|
40
44
|
display: flex;
|
|
41
45
|
align-items: center;
|
|
42
|
-
justify-content: space-between;
|
|
43
46
|
gap: 1.5rem;
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
.vmz-ui-app-shell__brand {
|
|
50
|
+
flex: none;
|
|
51
|
+
color: var(--vmz-text-ink);
|
|
47
52
|
font-family: var(--vmz-font-sans);
|
|
48
|
-
font-
|
|
49
|
-
font-
|
|
50
|
-
letter-spacing: 0;
|
|
53
|
+
font-size: 1.0625rem;
|
|
54
|
+
font-weight: 650;
|
|
55
|
+
letter-spacing: -0.01em;
|
|
51
56
|
text-decoration: none;
|
|
52
|
-
|
|
57
|
+
border-radius: var(--vmz-radius-sm, 4px);
|
|
58
|
+
outline: none;
|
|
59
|
+
transition: color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.vmz-ui-app-shell__brand:hover {
|
|
63
|
+
color: var(--vmz-action-primary-background);
|
|
53
64
|
}
|
|
54
65
|
|
|
55
66
|
.vmz-ui-app-shell__brand:focus-visible {
|
|
56
|
-
|
|
57
|
-
box-shadow: 0 0 0 2px var(--vmz-focus-ring);
|
|
67
|
+
box-shadow: 0 0 0 3px var(--vmz-focus-ring);
|
|
58
68
|
}
|
|
59
69
|
|
|
60
70
|
.vmz-ui-app-shell__nav {
|
|
61
71
|
display: flex;
|
|
72
|
+
flex: 1 1 auto;
|
|
62
73
|
flex-wrap: wrap;
|
|
63
|
-
gap: 0.
|
|
74
|
+
gap: 0.125rem;
|
|
75
|
+
min-width: 0;
|
|
64
76
|
}
|
|
65
77
|
|
|
66
|
-
.vmz-ui-app-shell__nav
|
|
78
|
+
.vmz-ui-app-shell__nav-item {
|
|
79
|
+
position: relative;
|
|
80
|
+
padding: 0.375rem 0.75rem;
|
|
81
|
+
border-radius: var(--vmz-radius-sm, 4px);
|
|
82
|
+
color: var(--vmz-text-steel);
|
|
67
83
|
font: inherit;
|
|
68
84
|
font-size: 0.875rem;
|
|
69
|
-
font-weight:
|
|
85
|
+
font-weight: 500;
|
|
86
|
+
line-height: 1.4286;
|
|
70
87
|
text-decoration: none;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
88
|
+
white-space: nowrap;
|
|
89
|
+
outline: none;
|
|
90
|
+
transition:
|
|
91
|
+
color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1)),
|
|
92
|
+
background-color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1));
|
|
74
93
|
}
|
|
75
94
|
|
|
76
|
-
.vmz-ui-app-shell__nav
|
|
77
|
-
color: var(--vmz-
|
|
95
|
+
.vmz-ui-app-shell__nav-item:hover {
|
|
96
|
+
color: var(--vmz-text-ink);
|
|
78
97
|
background: var(--vmz-surface-mist, #fafafa);
|
|
79
98
|
}
|
|
80
99
|
|
|
81
|
-
.vmz-ui-app-shell__nav
|
|
82
|
-
|
|
83
|
-
|
|
100
|
+
.vmz-ui-app-shell__nav-item:focus-visible {
|
|
101
|
+
box-shadow: 0 0 0 3px var(--vmz-focus-ring);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Current section: ink emphasis plus a hairline marker, no brand fill. */
|
|
105
|
+
.vmz-ui-app-shell__nav-item[data-active="true"] {
|
|
106
|
+
color: var(--vmz-text-ink);
|
|
107
|
+
font-weight: 600;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.vmz-ui-app-shell__nav-item[data-active="true"]::after {
|
|
111
|
+
content: "";
|
|
112
|
+
position: absolute;
|
|
113
|
+
inset: auto 0.75rem 0;
|
|
114
|
+
height: 2px;
|
|
115
|
+
border-radius: var(--vmz-radius-pill, 999px);
|
|
116
|
+
background: var(--vmz-action-primary-background);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.vmz-ui-app-shell__actions {
|
|
120
|
+
flex: none;
|
|
121
|
+
display: flex;
|
|
122
|
+
align-items: center;
|
|
123
|
+
gap: 0.5rem;
|
|
124
|
+
margin-left: auto;
|
|
84
125
|
}
|
|
85
126
|
|
|
86
127
|
.vmz-ui-app-shell__main {
|
|
87
128
|
flex: 1;
|
|
88
129
|
width: min(var(--vmz-layout-max-width, 75rem), calc(100% - 2rem));
|
|
89
130
|
margin: 0 auto;
|
|
90
|
-
padding: 1.
|
|
131
|
+
padding: 1.75rem 0 3rem;
|
|
91
132
|
display: flex;
|
|
92
133
|
flex-direction: column;
|
|
93
134
|
gap: 1rem;
|
|
94
135
|
}
|
|
136
|
+
|
|
95
137
|
@media (max-width: 720px) {
|
|
96
138
|
.vmz-ui-app-shell__header-inner {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
gap: 0.35rem;
|
|
101
|
-
padding: 0.7rem 0;
|
|
139
|
+
flex-wrap: wrap;
|
|
140
|
+
gap: 0.5rem 0.75rem;
|
|
141
|
+
padding: 0.625rem 0;
|
|
102
142
|
}
|
|
103
143
|
.vmz-ui-app-shell__nav {
|
|
104
|
-
|
|
144
|
+
order: 3;
|
|
145
|
+
flex-basis: 100%;
|
|
105
146
|
overflow-x: auto;
|
|
106
147
|
flex-wrap: nowrap;
|
|
107
|
-
padding-bottom: 0.
|
|
148
|
+
padding-bottom: 0.125rem;
|
|
149
|
+
scrollbar-width: none;
|
|
108
150
|
}
|
|
109
|
-
.vmz-ui-app-shell__nav
|
|
110
|
-
|
|
151
|
+
.vmz-ui-app-shell__nav::-webkit-scrollbar {
|
|
152
|
+
display: none;
|
|
111
153
|
}
|
|
112
154
|
.vmz-ui-app-shell__main {
|
|
113
155
|
padding-top: 1.25rem;
|
|
@@ -123,6 +165,8 @@ export default class AppShell {
|
|
|
123
165
|
public brand: string = "VMZ";
|
|
124
166
|
public homeHref: string = "/";
|
|
125
167
|
public navLabel: string = "Primary";
|
|
168
|
+
/** Id of the nav item representing the current page, for `aria-current` + marker. */
|
|
169
|
+
public activeId: string = "";
|
|
126
170
|
/** @type {{ id: string, title: string, href: string }[]} */
|
|
127
171
|
public nav: {
|
|
128
172
|
id: string;
|