@vmz/ui 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +8 -0
  2. package/contracts/token-requirements.v0.json +2 -6
  3. package/package.json +18 -2
  4. package/presets/web-surface.v0.json +24 -10
  5. package/src/components/Accordion.vmz +88 -89
  6. package/src/components/Alert.vmz +64 -63
  7. package/src/components/AppShell.vmz +64 -45
  8. package/src/components/Autocomplete.vmz +152 -151
  9. package/src/components/Avatar.vmz +16 -0
  10. package/src/components/Badge.vmz +46 -46
  11. package/src/components/Breadcrumb.vmz +52 -48
  12. package/src/components/BulkActions.vmz +37 -37
  13. package/src/components/Button.vmz +90 -75
  14. package/src/components/Callout.vmz +61 -61
  15. package/src/components/Card.vmz +58 -51
  16. package/src/components/Checkbox.vmz +56 -53
  17. package/src/components/CodeBlock.vmz +51 -51
  18. package/src/components/Collapse.vmz +20 -0
  19. package/src/components/ConsoleShell.vmz +134 -99
  20. package/src/components/Content.vmz +11 -0
  21. package/src/components/DataTable.vmz +197 -196
  22. package/src/components/DatePicker.vmz +613 -536
  23. package/src/components/Dialog.vmz +365 -384
  24. package/src/components/Divider.vmz +19 -0
  25. package/src/components/Drawer.vmz +361 -378
  26. package/src/components/Dropdown.vmz +22 -0
  27. package/src/components/Empty.vmz +36 -36
  28. package/src/components/Field.vmz +69 -40
  29. package/src/components/FilterBar.vmz +15 -15
  30. package/src/components/Flex.vmz +12 -0
  31. package/src/components/Footer.vmz +11 -0
  32. package/src/components/Form.vmz +56 -58
  33. package/src/components/FormItem.vmz +59 -59
  34. package/src/components/Grid.vmz +12 -0
  35. package/src/components/Header.vmz +11 -0
  36. package/src/components/Icon.vmz +86 -0
  37. package/src/components/Layout.vmz +11 -0
  38. package/src/components/Link.vmz +33 -33
  39. package/src/components/List.vmz +96 -96
  40. package/src/components/Menu.vmz +229 -240
  41. package/src/components/Notification.vmz +62 -62
  42. package/src/components/Pagination.vmz +63 -65
  43. package/src/components/Popover.vmz +202 -219
  44. package/src/components/Progress.vmz +18 -0
  45. package/src/components/Prose.vmz +61 -61
  46. package/src/components/QueryForm.vmz +26 -27
  47. package/src/components/RadioGroup.vmz +116 -113
  48. package/src/components/Result.vmz +63 -63
  49. package/src/components/Segmented.vmz +29 -0
  50. package/src/components/Select.vmz +462 -290
  51. package/src/components/Sider.vmz +6 -0
  52. package/src/components/Skeleton.vmz +58 -58
  53. package/src/components/Space.vmz +15 -0
  54. package/src/components/Spinner.vmz +29 -29
  55. package/src/components/Steps.vmz +86 -86
  56. package/src/components/Switch.vmz +86 -88
  57. package/src/components/Table.vmz +116 -114
  58. package/src/components/Tabs.vmz +113 -112
  59. package/src/components/Tag.vmz +8 -0
  60. package/src/components/TextArea.vmz +76 -49
  61. package/src/components/Timeline.vmz +55 -50
  62. package/src/components/Toc.vmz +64 -60
  63. package/src/components/Tooltip.vmz +51 -51
  64. package/src/components/Tree.vmz +184 -190
  65. package/src/components/Typography.vmz +9 -0
  66. package/src/components/Upload.vmz +1130 -1151
package/README.md CHANGED
@@ -41,3 +41,11 @@ pnpm verify -- official-dogfood
41
41
  Still open relative to Browser Production Profile v1: `browser-production` aggregate core gaps,
42
42
  `@vmz/ui-data-grid` deep capabilities, Upload cross-page session / multi-file parallel chunks,
43
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.
@@ -241,8 +241,6 @@
241
241
  "stableHint": "ui.Dialog",
242
242
  "tokens": [
243
243
  "action.primary.background",
244
- "action.primary.foreground",
245
- "action.primary.active",
246
244
  "focus.ring",
247
245
  "motion.overlay.duration",
248
246
  "motion.overlay.easing"
@@ -286,14 +284,14 @@
286
284
  "Tabs": {
287
285
  "source": "src/components/Tabs.vmz",
288
286
  "stableHint": "ui.Tabs",
289
- "tokens": ["action.primary.background", "action.primary.foreground", "focus.ring"],
287
+ "tokens": ["action.primary.background", "focus.ring"],
290
288
  "surfaces": ["WebSurface"],
291
289
  "contracts": ["tablist", "keyboard-roving", "live-selected-prop"]
292
290
  },
293
291
  "Menu": {
294
292
  "source": "src/components/Menu.vmz",
295
293
  "stableHint": "ui.Menu",
296
- "tokens": ["action.primary.background", "action.primary.foreground", "action.primary.active", "focus.ring"],
294
+ "tokens": ["action.primary.background", "focus.ring"],
297
295
  "surfaces": ["WebSurface"],
298
296
  "contracts": ["overlay-ownership", "escape-dismiss", "focus-restore", "outside-dismiss", "live-open-prop"]
299
297
  },
@@ -302,8 +300,6 @@
302
300
  "stableHint": "ui.Drawer",
303
301
  "tokens": [
304
302
  "action.primary.background",
305
- "action.primary.foreground",
306
- "action.primary.active",
307
303
  "focus.ring",
308
304
  "motion.overlay.duration",
309
305
  "motion.overlay.easing"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vmz/ui",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "description": "VMZ UI — official UI framework (component contracts + semantic token requirements). Not a theme. Not a plugin.",
6
6
  "license": "MIT",
@@ -52,7 +52,23 @@
52
52
  "./Accordion": "./src/components/Accordion.vmz",
53
53
  "./Steps": "./src/components/Steps.vmz",
54
54
  "./List": "./src/components/List.vmz",
55
- "./Tree": "./src/components/Tree.vmz"
55
+ "./Tree": "./src/components/Tree.vmz",
56
+ "./Typography": "./src/components/Typography.vmz",
57
+ "./Space": "./src/components/Space.vmz",
58
+ "./Flex": "./src/components/Flex.vmz",
59
+ "./Grid": "./src/components/Grid.vmz",
60
+ "./Divider": "./src/components/Divider.vmz",
61
+ "./Tag": "./src/components/Tag.vmz",
62
+ "./Avatar": "./src/components/Avatar.vmz",
63
+ "./Dropdown": "./src/components/Dropdown.vmz",
64
+ "./Collapse": "./src/components/Collapse.vmz",
65
+ "./Progress": "./src/components/Progress.vmz",
66
+ "./Segmented": "./src/components/Segmented.vmz",
67
+ "./Layout": "./src/components/Layout.vmz",
68
+ "./Header": "./src/components/Header.vmz",
69
+ "./Sider": "./src/components/Sider.vmz",
70
+ "./Content": "./src/components/Content.vmz",
71
+ "./Footer": "./src/components/Footer.vmz"
56
72
  },
57
73
  "files": [
58
74
  "src",
@@ -3,21 +3,34 @@
3
3
  "id": "web-surface",
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
+ "foundation": {
7
+ "radius": { "sm": "2px", "md": "6px", "lg": "8px" },
8
+ "controlHeight": { "sm": "1.5rem", "middle": "2rem", "lg": "2.5rem" },
9
+ "layoutMaxWidth": "75rem",
10
+ "surfacePage": "#F5F5F5",
11
+ "surfaceFill": "#FAFAFA",
12
+ "textTertiary": "#8C8C8C",
13
+ "lineDefault": "#D9D9D9",
14
+ "lineSubtle": "#F0F0F0",
15
+ "shadowCard": "none",
16
+ "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)"
18
+ },
6
19
  "density": {
7
20
  "control": {
8
- "padding-y": "0.55rem",
9
- "padding-x": "0.95rem",
10
- "gap": "0.75rem"
21
+ "padding-y": "0.25rem",
22
+ "padding-x": "0.75rem",
23
+ "gap": "0.5rem"
11
24
  },
12
25
  "compact": {
13
- "padding-y": "0.35rem",
14
- "padding-x": "0.65rem",
15
- "gap": "0.45rem"
26
+ "padding-y": "0.125rem",
27
+ "padding-x": "0.5rem",
28
+ "gap": "0.35rem"
16
29
  },
17
30
  "dense": {
18
- "padding-y": "0.2rem",
19
- "padding-x": "0.45rem",
20
- "gap": "0.3rem"
31
+ "padding-y": "0",
32
+ "padding-x": "0.35rem",
33
+ "gap": "0.25rem"
21
34
  }
22
35
  },
23
36
  "activation": {
@@ -30,5 +43,6 @@
30
43
  "default": "application tokens base",
31
44
  "dark": "themes/dark.json overlay",
32
45
  "high-contrast": "themes/high-contrast.json overlay — stronger ink/line/focus"
33
- }
46
+ },
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."
34
48
  }
@@ -1,112 +1,111 @@
1
1
  <template>
2
2
  <div class="vmz-ui-accordion" data-vmz-ui="accordion" aria-label={label}>
3
- <div each={items} as="item" key={item.id} class="vmz-ui-accordion__item" data-vmz-accordion={item.id}>
4
- <h3 class="vmz-ui-accordion__heading">
5
- <button
6
- type="button"
7
- class="vmz-ui-accordion__trigger"
8
- id={item.triggerId}
9
- data-vmz-accordion-trigger={item.id}
10
- aria-expanded={openId === item.id ? 'true' : 'false'}
11
- aria-controls={item.panelId}
12
- onClick={() => this.toggle(item.id)}
3
+ <div each={items} as="item" key={item.id} class="vmz-ui-accordion__item" data-vmz-accordion={item.id}>
4
+ <h3 class="vmz-ui-accordion__heading">
5
+ <button
6
+ type="button"
7
+ class="vmz-ui-accordion__trigger"
8
+ id={item.triggerId}
9
+ data-vmz-accordion-trigger={item.id}
10
+ aria-expanded={openId === item.id ? 'true' : 'false'}
11
+ aria-controls={item.panelId}
12
+ onClick={() => this.toggle(item.id)}
13
+ >
14
+ {item.title}
15
+ </button>
16
+ </h3>
17
+ <div
18
+ if={openId === item.id}
19
+ id={item.panelId}
20
+ class="vmz-ui-accordion__panel"
21
+ role="region"
22
+ data-vmz-accordion-panel={item.id}
23
+ aria-labelledby={item.triggerId}
13
24
  >
14
- {item.title}
15
- </button>
16
- </h3>
17
- <div
18
- if={openId === item.id}
19
- id={item.panelId}
20
- class="vmz-ui-accordion__panel"
21
- role="region"
22
- data-vmz-accordion-panel={item.id}
23
- aria-labelledby={item.triggerId}
24
- >
25
- {item.body}
25
+ {item.body}
26
+ </div>
26
27
  </div>
27
28
  </div>
28
- </div>
29
29
  </template>
30
30
 
31
31
  <style>
32
- .vmz-ui-accordion {
33
- display: flex;
34
- flex-direction: column;
35
- gap: 0.35rem;
36
- max-width: 36rem;
37
- border: 1px solid var(--vmz-line-default);
38
- border-radius: 2px;
39
- background: var(--vmz-surface-paper);
40
- color: var(--vmz-text-ink);
41
- }
32
+ .vmz-ui-accordion {
33
+ display: flex;
34
+ flex-direction: column;
35
+ gap: 0.35rem;
36
+ max-width: 36rem;
37
+ border: 1px solid var(--vmz-line-default);
38
+ border-radius: var(--vmz-radius-md, 6px);
39
+ background: var(--vmz-surface-paper);
40
+ color: var(--vmz-text-ink);
41
+ }
42
42
 
43
- .vmz-ui-accordion__item {
44
- border-bottom: 1px solid var(--vmz-line-default);
45
- }
43
+ .vmz-ui-accordion__item {
44
+ border-bottom: 1px solid var(--vmz-line-default);
45
+ }
46
46
 
47
- .vmz-ui-accordion__item:last-child {
48
- border-bottom: none;
49
- }
47
+ .vmz-ui-accordion__item:last-child {
48
+ border-bottom: none;
49
+ }
50
50
 
51
- .vmz-ui-accordion__heading {
52
- margin: 0;
53
- font: inherit;
54
- }
51
+ .vmz-ui-accordion__heading {
52
+ margin: 0;
53
+ font: inherit;
54
+ }
55
55
 
56
- .vmz-ui-accordion__trigger {
57
- display: block;
58
- width: 100%;
59
- text-align: start;
60
- font: inherit;
61
- font-weight: 600;
62
- padding: var(--vmz-density-control-padding-y) var(--vmz-density-control-padding-x);
63
- border: 0;
64
- background: transparent;
65
- color: inherit;
66
- cursor: pointer;
67
- outline: none;
68
- }
56
+ .vmz-ui-accordion__trigger {
57
+ display: block;
58
+ width: 100%;
59
+ text-align: start;
60
+ font: inherit;
61
+ font-weight: 600;
62
+ padding: var(--vmz-density-control-padding-y) var(--vmz-density-control-padding-x);
63
+ border: 0;
64
+ background: transparent;
65
+ color: inherit;
66
+ cursor: pointer;
67
+ outline: none;
68
+ }
69
69
 
70
- .vmz-ui-accordion__trigger:hover {
71
- background: color-mix(in srgb, var(--vmz-surface-mist) 85%, transparent);
72
- }
70
+ .vmz-ui-accordion__trigger:hover {
71
+ background: color-mix(in srgb, var(--vmz-surface-mist) 85%, transparent);
72
+ }
73
73
 
74
- .vmz-ui-accordion__trigger:focus-visible {
75
- box-shadow: inset 0 0 0 2px var(--vmz-focus-ring);
76
- }
74
+ .vmz-ui-accordion__trigger:focus-visible {
75
+ box-shadow: inset 0 0 0 2px var(--vmz-focus-ring);
76
+ }
77
77
 
78
- .vmz-ui-accordion__trigger[aria-expanded='true'] {
79
- color: var(--vmz-action-primary-active);
80
- }
78
+ .vmz-ui-accordion__trigger[aria-expanded='true'] {
79
+ color: var(--vmz-action-primary-active);
80
+ }
81
81
 
82
- .vmz-ui-accordion__panel {
83
- padding: 0 var(--vmz-density-control-padding-x) var(--vmz-density-control-padding-y);
84
- color: var(--vmz-text-steel);
85
- line-height: 1.5;
86
- }
82
+ .vmz-ui-accordion__panel {
83
+ padding: 0 var(--vmz-density-control-padding-x) var(--vmz-density-control-padding-y);
84
+ color: var(--vmz-text-steel);
85
+ line-height: 1.5;
86
+ }
87
87
  </style>
88
88
 
89
89
  <script client>
90
90
  /**
91
- * VMZ UI — Accordion.
92
- * Exclusive open: parent owns `openId`. Toggle same id closes.
93
- * Panel body lives on items (no per-panel slot runtime).
94
- */
91
+ * VMZ UI — Accordion.
92
+ * Exclusive open: parent owns `openId`. Toggle same id closes.
93
+ * Panel body lives on items (no per-panel slot runtime).
94
+ */
95
95
  export default class Accordion {
96
- public label: string = 'Accordion';
97
- public openId: string = '';
98
- /** @type {{ id: string, title: string, body: string, triggerId: string, panelId: string }[]} */
99
- public items: {
100
- id: string;
101
- title: string;
102
- body: string;
103
- triggerId: string;
104
- panelId: string;
105
- }[] = [];
106
- public onToggle: ((id: string) => void) | null = null;
107
-
108
- toggle(id) {
109
- if (typeof this.onToggle === 'function') this.onToggle(id);
110
- }
96
+ public label: string = "Accordion";
97
+ public openId: string = "";
98
+ /** @type {{ id: string, title: string, body: string, triggerId: string, panelId: string }[]} */
99
+ public items: {
100
+ id: string;
101
+ title: string;
102
+ body: string;
103
+ triggerId: string;
104
+ panelId: string;
105
+ }[] = [];
106
+ public onToggle: ((id: string) => void) | null = null;
107
+ toggle(id) {
108
+ if (typeof this.onToggle === "function") this.onToggle(id);
109
+ }
111
110
  }
112
111
  </script>
@@ -1,84 +1,85 @@
1
1
  <template>
2
2
  <div
3
- class="vmz-ui-alert"
4
- data-vmz-ui="alert"
5
- data-tone={tone}
6
- role={roleName}
7
- >
8
- <p if={title} class="vmz-ui-alert__title">{title}</p>
9
- <p if={description} class="vmz-ui-alert__description">{description}</p>
10
- <div class="vmz-ui-alert__actions">
11
- <slot />
3
+ class="vmz-ui-alert"
4
+ data-vmz-ui="alert"
5
+ data-tone={tone}
6
+ role={roleName}
7
+ >
8
+ <p if={title} class="vmz-ui-alert__title">{title}</p>
9
+ <p if={description} class="vmz-ui-alert__description">{description}</p>
10
+ <div class="vmz-ui-alert__actions">
11
+ <slot />
12
+ </div>
12
13
  </div>
13
- </div>
14
14
  </template>
15
15
 
16
16
  <style>
17
- .vmz-ui-alert {
18
- display: flex;
19
- flex-direction: column;
20
- gap: var(--vmz-density-control-gap);
21
- padding: var(--vmz-density-control-padding-y) var(--vmz-density-control-padding-x);
22
- border: 1px solid var(--vmz-status-info-accent);
23
- border-left-width: 4px;
24
- border-radius: 2px;
25
- background: color-mix(in srgb, var(--vmz-status-info-accent) 12%, transparent);
26
- color: var(--vmz-status-info-foreground);
27
- }
17
+ .vmz-ui-alert {
18
+ display: flex;
19
+ flex-direction: column;
20
+ gap: var(--vmz-density-control-gap);
21
+ padding: var(--vmz-density-control-padding-y) var(--vmz-density-control-padding-x);
22
+ border: 1px solid var(--vmz-status-info-accent);
23
+ border-left-width: 1px;
24
+ border-radius: var(--vmz-radius-md, 6px);
25
+ background: color-mix(in srgb, var(--vmz-status-info-accent) 12%, transparent);
26
+ color: var(--vmz-status-info-foreground);
27
+ }
28
28
 
29
- .vmz-ui-alert[data-tone='info'] {
30
- border-color: var(--vmz-status-info-accent);
31
- background: color-mix(in srgb, var(--vmz-status-info-accent) 12%, transparent);
32
- color: var(--vmz-status-info-foreground);
33
- }
29
+ .vmz-ui-alert[data-tone='info'] {
30
+ border-color: var(--vmz-status-info-accent);
31
+ background: color-mix(in srgb, var(--vmz-status-info-accent) 12%, transparent);
32
+ color: var(--vmz-status-info-foreground);
33
+ }
34
34
 
35
- .vmz-ui-alert[data-tone='success'] {
36
- border-color: var(--vmz-status-success-accent);
37
- background: color-mix(in srgb, var(--vmz-status-success-accent) 12%, transparent);
38
- color: var(--vmz-status-success-foreground);
39
- }
35
+ .vmz-ui-alert[data-tone='success'] {
36
+ border-color: var(--vmz-status-success-accent);
37
+ background: color-mix(in srgb, var(--vmz-status-success-accent) 12%, transparent);
38
+ color: var(--vmz-status-success-foreground);
39
+ }
40
40
 
41
- .vmz-ui-alert[data-tone='warning'] {
42
- border-color: var(--vmz-status-warning-accent);
43
- background: color-mix(in srgb, var(--vmz-status-warning-accent) 12%, transparent);
44
- color: var(--vmz-status-warning-foreground);
45
- }
41
+ .vmz-ui-alert[data-tone='warning'] {
42
+ border-color: var(--vmz-status-warning-accent);
43
+ background: color-mix(in srgb, var(--vmz-status-warning-accent) 12%, transparent);
44
+ color: var(--vmz-status-warning-foreground);
45
+ }
46
46
 
47
- .vmz-ui-alert[data-tone='danger'] {
48
- border-color: var(--vmz-status-danger-accent);
49
- background: color-mix(in srgb, var(--vmz-status-danger-accent) 12%, transparent);
50
- color: var(--vmz-status-danger-foreground);
51
- }
47
+ .vmz-ui-alert[data-tone='danger'] {
48
+ border-color: var(--vmz-status-danger-accent);
49
+ background: color-mix(in srgb, var(--vmz-status-danger-accent) 12%, transparent);
50
+ color: var(--vmz-status-danger-foreground);
51
+ }
52
52
 
53
- :where([data-density='compact']) .vmz-ui-alert {
54
- gap: var(--vmz-density-compact-gap);
55
- padding: var(--vmz-density-compact-padding-y) var(--vmz-density-compact-padding-x);
56
- }
53
+ :where([data-density='compact']) .vmz-ui-alert {
54
+ gap: var(--vmz-density-compact-gap);
55
+ padding: var(--vmz-density-compact-padding-y) var(--vmz-density-compact-padding-x);
56
+ }
57
57
 
58
- .vmz-ui-alert__title {
59
- margin: 0;
60
- font-weight: 700;
61
- }
58
+ .vmz-ui-alert__title {
59
+ margin: 0;
60
+ font-size: 0.875rem;
61
+ font-weight: 600;
62
+ }
62
63
 
63
- .vmz-ui-alert__description {
64
- margin: 0;
65
- opacity: 0.9;
66
- }
64
+ .vmz-ui-alert__description {
65
+ margin: 0;
66
+ opacity: 0.9;
67
+ }
67
68
 
68
- .vmz-ui-alert__actions:empty {
69
- display: none;
70
- }
69
+ .vmz-ui-alert__actions:empty {
70
+ display: none;
71
+ }
71
72
  </style>
72
73
 
73
74
  <script client>
74
75
  /**
75
- * VMZ UI — Alert (UI4 status tone matrix).
76
- * tone → status.{info|success|warning|danger}.* tokens from Style Theme.
77
- */
76
+ * VMZ UI — Alert (UI4 status tone matrix).
77
+ * tone → status.{info|success|warning|danger}.* tokens from Style Theme.
78
+ */
78
79
  export default class Alert {
79
- public title: string = '';
80
- public description: string = '';
81
- public tone: string = 'info';
82
- public roleName: string = 'status';
80
+ public title: string = "";
81
+ public description: string = "";
82
+ public tone: string = "info";
83
+ public roleName: string = "status";
83
84
  }
84
85
  </script>
@@ -1,47 +1,52 @@
1
1
  <template>
2
2
  <div class="vmz-ui-app-shell" data-vmz-ui="app-shell">
3
- <header class="vmz-ui-app-shell__header" data-vmz-shell="header">
4
- <a class="vmz-ui-app-shell__brand" href={homeHref}>{brand}</a>
5
- <nav class="vmz-ui-app-shell__nav" aria-label={navLabel}>
6
- <a each={nav} as="item" key={item.id} href={item.href} data-vmz-nav={item.id}>
7
- {item.title}
8
- </a>
9
- </nav>
10
- </header>
11
- <main class="vmz-ui-app-shell__main" data-vmz-shell="main">
12
- <slot />
13
- </main>
14
- </div>
3
+ <header class="vmz-ui-app-shell__header" data-vmz-shell="header"><div class="vmz-ui-app-shell__header-inner">
4
+ <a class="vmz-ui-app-shell__brand" href={homeHref}>{brand}</a>
5
+ <nav class="vmz-ui-app-shell__nav" aria-label={navLabel}>
6
+ <a each={nav} as="item" key={item.id} href={item.href} data-vmz-nav={item.id}>
7
+ {item.title}
8
+ </a>
9
+ </nav>
10
+ </div></header>
11
+ <main class="vmz-ui-app-shell__main" data-vmz-shell="main">
12
+ <slot />
13
+ </main>
14
+ </div>
15
15
  </template>
16
16
 
17
17
  <style>
18
- .vmz-ui-app-shell {
19
- display: flex;
20
- flex-direction: column;
21
- min-height: 100%;
22
- color: var(--vmz-text-ink);
23
- background: transparent;
24
- }
18
+ .vmz-ui-app-shell {
19
+ display: flex;
20
+ flex-direction: column;
21
+ min-height: 100%;
22
+ color: var(--vmz-text-ink);
23
+ background: transparent;
24
+ }
25
25
 
26
26
  .vmz-ui-app-shell__header {
27
+ position: sticky;
28
+ top: 0;
29
+ z-index: 30;
30
+ border-bottom: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
31
+ background: var(--vmz-surface-paper);
32
+ }
33
+ .vmz-ui-app-shell__header-inner {
34
+ width: min(var(--vmz-layout-max-width, 75rem), calc(100% - 2rem));
35
+ min-height: 3rem;
36
+ margin: 0 auto;
27
37
  display: flex;
28
- flex-wrap: wrap;
29
38
  align-items: center;
30
39
  justify-content: space-between;
31
- gap: 0.75rem 1.25rem;
32
- padding: 0.9rem clamp(1rem, 3vw, 1.75rem);
33
- border-bottom: 1px solid var(--vmz-line-default);
34
- box-shadow: inset 0 -2px 0 var(--vmz-action-primary-background);
35
- background: color-mix(in srgb, var(--vmz-surface-paper) 92%, transparent);
40
+ gap: 1.5rem;
36
41
  }
37
42
 
38
43
  .vmz-ui-app-shell__brand {
39
- font-family: var(--vmz-font-display);
40
- font-weight: 700;
41
- font-size: 1.2rem;
42
- letter-spacing: 0.03em;
44
+ font-family: var(--vmz-font-sans);
45
+ font-weight: 600;
46
+ font-size: 1.125rem;
47
+ letter-spacing: 0;
43
48
  text-decoration: none;
44
- color: var(--vmz-action-primary-background);
49
+ color: var(--vmz-text-ink);
45
50
  }
46
51
 
47
52
  .vmz-ui-app-shell__brand:focus-visible {
@@ -52,18 +57,22 @@
52
57
  .vmz-ui-app-shell__nav {
53
58
  display: flex;
54
59
  flex-wrap: wrap;
55
- gap: 0.55rem 1rem;
60
+ gap: 0.25rem 0.15rem;
56
61
  }
57
62
 
58
63
  .vmz-ui-app-shell__nav a {
59
64
  font: inherit;
60
- font-weight: 600;
65
+ font-size: 0.875rem;
66
+ font-weight: 400;
61
67
  text-decoration: none;
62
- color: var(--vmz-text-steel);
68
+ color: var(--vmz-text-ink);
69
+ padding: 0.25rem 0.75rem;
70
+ border-radius: var(--vmz-radius-md, 6px);
63
71
  }
64
72
 
65
73
  .vmz-ui-app-shell__nav a:hover {
66
- color: var(--vmz-action-primary-active);
74
+ color: var(--vmz-action-primary-background);
75
+ background: var(--vmz-surface-mist, #fafafa);
67
76
  }
68
77
 
69
78
  .vmz-ui-app-shell__nav a:focus-visible {
@@ -73,25 +82,35 @@
73
82
 
74
83
  .vmz-ui-app-shell__main {
75
84
  flex: 1;
76
- width: min(68rem, calc(100% - 2rem));
85
+ width: min(var(--vmz-layout-max-width, 75rem), calc(100% - 2rem));
77
86
  margin: 0 auto;
78
- padding: 1.5rem 1rem 2.5rem;
87
+ padding: 1.5rem 0 2.5rem;
79
88
  display: flex;
80
89
  flex-direction: column;
81
- gap: 1.5rem;
90
+ gap: 1rem;
82
91
  }
92
+ @media (max-width: 720px) {
93
+ .vmz-ui-app-shell__header-inner { min-height: 3.5rem; align-items: flex-start; flex-direction: column; gap: .35rem; padding: .7rem 0; }
94
+ .vmz-ui-app-shell__nav { width: 100%; overflow-x: auto; flex-wrap: nowrap; padding-bottom: .2rem; }
95
+ .vmz-ui-app-shell__nav a { white-space: nowrap; }
96
+ .vmz-ui-app-shell__main { padding-top: 1.25rem; }
97
+ }
83
98
  </style>
84
99
 
85
100
  <script client>
86
101
  /**
87
- * VMZ UI — AppShell (Commercial composition).
88
- * Landmark header/nav + main slot. Parent supplies nav items; no private theme.
89
- */
102
+ * VMZ UI — AppShell (Commercial composition).
103
+ * Landmark header/nav + main slot. Parent supplies nav items; no private theme.
104
+ */
90
105
  export default class AppShell {
91
- public brand: string = 'VMZ';
92
- public homeHref: string = '/';
93
- public navLabel: string = 'Primary';
94
- /** @type {{ id: string, title: string, href: string }[]} */
95
- public nav: { id: string; title: string; href: string }[] = [];
106
+ public brand: string = "VMZ";
107
+ public homeHref: string = "/";
108
+ public navLabel: string = "Primary";
109
+ /** @type {{ id: string, title: string, href: string }[]} */
110
+ public nav: {
111
+ id: string;
112
+ title: string;
113
+ href: string;
114
+ }[] = [];
96
115
  }
97
116
  </script>