@vmz/ui 0.3.7 → 0.3.8

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 (68) hide show
  1. package/README.md +2 -0
  2. package/contracts/token-requirements.v0.json +58 -1
  3. package/package.json +5 -2
  4. package/presets/web-surface.v0.json +14 -12
  5. package/src/components/Accordion.vmz +49 -16
  6. package/src/components/Alert.vmz +125 -28
  7. package/src/components/AppShell.vmz +121 -56
  8. package/src/components/Autocomplete.vmz +136 -29
  9. package/src/components/Avatar.vmz +20 -12
  10. package/src/components/Badge.vmz +18 -26
  11. package/src/components/Breadcrumb.vmz +28 -14
  12. package/src/components/BulkActions.vmz +13 -8
  13. package/src/components/Button.vmz +68 -44
  14. package/src/components/Callout.vmz +128 -27
  15. package/src/components/Card.vmz +67 -46
  16. package/src/components/Checkbox.vmz +81 -5
  17. package/src/components/CodeBlock.vmz +59 -27
  18. package/src/components/Collapse.vmz +64 -11
  19. package/src/components/ConsoleShell.vmz +217 -63
  20. package/src/components/Content.vmz +15 -1
  21. package/src/components/DataTable.vmz +123 -23
  22. package/src/components/DatePicker.vmz +171 -79
  23. package/src/components/Descriptions.vmz +90 -0
  24. package/src/components/Dialog.vmz +133 -50
  25. package/src/components/Divider.vmz +8 -1
  26. package/src/components/Drawer.vmz +131 -40
  27. package/src/components/Dropdown.vmz +147 -49
  28. package/src/components/Empty.vmz +56 -14
  29. package/src/components/Field.vmz +78 -23
  30. package/src/components/FilterBar.vmz +34 -4
  31. package/src/components/Flex.vmz +3 -1
  32. package/src/components/Footer.vmz +6 -3
  33. package/src/components/Form.vmz +41 -0
  34. package/src/components/FormItem.vmz +2 -3
  35. package/src/components/Grid.vmz +6 -3
  36. package/src/components/Header.vmz +193 -6
  37. package/src/components/Layout.vmz +3 -1
  38. package/src/components/Link.vmz +20 -9
  39. package/src/components/List.vmz +35 -13
  40. package/src/components/Menu.vmz +198 -25
  41. package/src/components/Message.vmz +167 -0
  42. package/src/components/Notification.vmz +132 -37
  43. package/src/components/Pagination.vmz +186 -20
  44. package/src/components/Popconfirm.vmz +287 -0
  45. package/src/components/Popover.vmz +152 -34
  46. package/src/components/Progress.vmz +16 -5
  47. package/src/components/Prose.vmz +131 -32
  48. package/src/components/QueryForm.vmz +31 -9
  49. package/src/components/RadioGroup.vmz +91 -11
  50. package/src/components/Result.vmz +60 -27
  51. package/src/components/Segmented.vmz +46 -11
  52. package/src/components/Select.vmz +174 -87
  53. package/src/components/Sider.vmz +120 -7
  54. package/src/components/Skeleton.vmz +9 -7
  55. package/src/components/Space.vmz +9 -3
  56. package/src/components/Spinner.vmz +5 -4
  57. package/src/components/Steps.vmz +40 -25
  58. package/src/components/Switch.vmz +48 -15
  59. package/src/components/Table.vmz +130 -25
  60. package/src/components/Tabs.vmz +28 -13
  61. package/src/components/Tag.vmz +29 -12
  62. package/src/components/TextArea.vmz +32 -25
  63. package/src/components/Timeline.vmz +65 -30
  64. package/src/components/Toc.vmz +45 -29
  65. package/src/components/Tooltip.vmz +70 -21
  66. package/src/components/Tree.vmz +48 -25
  67. package/src/components/Typography.vmz +23 -8
  68. package/src/components/Upload.vmz +191 -96
@@ -1,18 +1,26 @@
1
1
  <template>
2
2
  <div data-vmz-ui="dialog" :data-vmz-overlay-open="open" class="vmz-ui-dialog-root">
3
- <div v-if="open" data-vmz-overlay="dialog" data-vmz-overlay-owner="dialog" :data-vmz-overlay-stack="stackLevel" class="vmz-ui-dialog">
4
- <button type="button" data-vmz-overlay-layer="backdrop" aria-label="Dismiss dialog" @click="dismiss" class="vmz-ui-dialog__backdrop" />
5
- <div role="dialog" aria-modal="true" :aria-labelledby="titleId" tabindex="-1" data-vmz-overlay-layer="panel" data-vmz-focus="enter" data-vmz-motion="overlay-enter" data-vmz-motion-token="motion.overlay" :onKeyDown="onPanelKeyDown" class="vmz-ui-dialog__panel">
6
- <h2 :id="titleId" class="vmz-ui-dialog__title">
7
- {{ title }}
8
- </h2>
3
+ <div v-if="open" data-vmz-overlay="dialog" data-vmz-overlay-owner="dialog" :data-vmz-overlay-stack="stackLevel" :data-size="size" :data-action-size="actionSize" :data-tone="danger ? 'danger' : 'default'" class="vmz-ui-dialog">
4
+ <button type="button" data-vmz-overlay-layer="backdrop" :aria-label="dismissLabel || closeLabel || label || title" @click="dismiss" class="vmz-ui-dialog__backdrop" />
5
+ <div role="dialog" aria-modal="true" :aria-labelledby="title ? titleId : null" tabindex="-1" data-vmz-overlay-layer="panel" data-vmz-focus="enter" data-vmz-motion="overlay-enter" data-vmz-motion-token="motion.overlay" :onKeyDown="onPanelKeyDown" class="vmz-ui-dialog__panel">
6
+ <header class="vmz-ui-dialog__header">
7
+ <div class="vmz-ui-dialog__heading">
8
+ <h2 v-if="title" :id="titleId" class="vmz-ui-dialog__title">
9
+ {{ title }}
10
+ </h2>
11
+ <p v-if="description" class="vmz-ui-dialog__description">
12
+ {{ description }}
13
+ </p>
14
+ </div>
15
+ <button type="button" data-vmz-focus="close" :aria-label="closeLabel || label || title" @click="dismiss" class="vmz-ui-dialog__close">
16
+ <Icon name="action.close" size="md" decorative />
17
+ </button>
18
+ </header>
9
19
  <div class="vmz-ui-dialog__body">
10
20
  <slot />
11
21
  </div>
12
22
  <div class="vmz-ui-dialog__actions">
13
- <button type="button" data-vmz-focus="close" @click="dismiss" class="vmz-ui-dialog__close">
14
- Close
15
- </button>
23
+ <slot name="actions" />
16
24
  </div>
17
25
  </div>
18
26
  </div>
@@ -29,6 +37,8 @@
29
37
  z-index: 40;
30
38
  display: grid;
31
39
  place-items: center;
40
+ box-sizing: border-box;
41
+ padding: 24px;
32
42
  }
33
43
 
34
44
  .vmz-ui-dialog[data-vmz-overlay-stack="1"] {
@@ -50,7 +60,7 @@
50
60
  padding: 0;
51
61
  margin: 0;
52
62
  cursor: pointer;
53
- background: rgba(0, 0, 0, 0.45);
63
+ background: color-mix(in srgb, var(--vmz-surface-ink) 45%, transparent);
54
64
  animation: vmz-ui-overlay-fade-in var(--vmz-motion-overlay-duration)
55
65
  var(--vmz-motion-overlay-easing);
56
66
  }
@@ -58,11 +68,14 @@
58
68
  .vmz-ui-dialog__panel {
59
69
  position: relative;
60
70
  z-index: 1;
61
- width: min(32rem, calc(100vw - 2rem));
62
- padding: 1.25rem 1.5rem 1.25rem;
63
- border: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
64
- border-radius: var(--vmz-radius-lg, 8px);
65
- background: var(--vmz-surface-paper, #fff);
71
+ display: flex;
72
+ flex-direction: column;
73
+ box-sizing: border-box;
74
+ width: min(520px, 100%);
75
+ max-height: calc(100vh - 48px);
76
+ border: 1px solid var(--vmz-line-subtle);
77
+ border-radius: var(--vmz-radius-lg);
78
+ background: var(--vmz-surface-paper);
66
79
  color: var(--vmz-text-ink);
67
80
  box-shadow: var(--vmz-shadow-popup);
68
81
  outline: none;
@@ -70,6 +83,14 @@
70
83
  var(--vmz-motion-overlay-easing);
71
84
  }
72
85
 
86
+ .vmz-ui-dialog[data-size="sm"] .vmz-ui-dialog__panel {
87
+ width: min(400px, 100%);
88
+ }
89
+
90
+ .vmz-ui-dialog[data-size="lg"] .vmz-ui-dialog__panel {
91
+ width: min(720px, 100%);
92
+ }
93
+
73
94
  .vmz-ui-dialog[data-vmz-motion-adopt="true"] .vmz-ui-dialog__backdrop,
74
95
  .vmz-ui-dialog[data-vmz-motion-adopt="true"] .vmz-ui-dialog__panel {
75
96
  animation: none;
@@ -86,61 +107,107 @@
86
107
  }
87
108
 
88
109
  .vmz-ui-dialog__panel:focus-visible {
89
- box-shadow: 0 0 0 3px var(--vmz-focus-ring);
110
+ box-shadow: 0 0 0 3px var(--vmz-focus-ring), var(--vmz-shadow-popup);
111
+ }
112
+
113
+ .vmz-ui-dialog__header {
114
+ display: flex;
115
+ align-items: flex-start;
116
+ gap: 16px;
117
+ padding: 20px 24px 16px;
118
+ }
119
+
120
+ .vmz-ui-dialog__heading {
121
+ flex: 1 1 auto;
122
+ min-width: 0;
90
123
  }
91
124
 
92
125
  .vmz-ui-dialog__title {
93
- margin: 0 0 0.75rem;
94
- font-size: 1rem;
126
+ margin: 0;
127
+ font-size: 18px;
95
128
  font-weight: 600;
96
- line-height: 1.4;
129
+ line-height: 24px;
97
130
  color: var(--vmz-text-ink);
98
131
  }
99
132
 
100
- .vmz-ui-dialog__body {
101
- margin-bottom: 1rem;
102
- color: var(--vmz-text-ink);
133
+ .vmz-ui-dialog[data-tone="danger"] .vmz-ui-dialog__title {
134
+ color: var(--vmz-status-danger-foreground);
135
+ }
136
+
137
+ .vmz-ui-dialog__description {
138
+ margin: 6px 0 0;
139
+ font-size: 14px;
140
+ line-height: 22px;
141
+ color: var(--vmz-text-tertiary);
103
142
  }
104
143
 
105
144
  .vmz-ui-dialog__close {
106
- font: inherit;
107
- font-size: 0.875rem;
108
- font-weight: 500;
109
- min-height: var(--vmz-control-height, 2rem);
110
- padding: 0.25rem 0.9375rem;
111
- border: 1px solid var(--vmz-line-default);
112
- border-radius: var(--vmz-radius-md, 6px);
145
+ flex: 0 0 auto;
146
+ display: inline-flex;
147
+ align-items: center;
148
+ justify-content: center;
149
+ width: 32px;
150
+ height: 32px;
151
+ margin: -4px -8px 0 0;
152
+ padding: 0;
153
+ border: 0;
154
+ border-radius: var(--vmz-radius-md);
155
+ background: transparent;
156
+ color: var(--vmz-text-steel);
113
157
  cursor: pointer;
114
- background: var(--vmz-surface-paper);
115
- color: var(--vmz-text-ink);
158
+ outline: none;
116
159
  transition:
117
- color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1)),
118
- border-color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1)),
119
- background-color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1));
160
+ color var(--vmz-motion-control-duration) var(--vmz-motion-control-easing),
161
+ background-color var(--vmz-motion-control-duration) var(--vmz-motion-control-easing);
120
162
  }
121
163
 
122
- .vmz-ui-dialog__close[data-variant="primary"] {
123
- background: var(--vmz-action-primary-background);
124
- color: var(--vmz-action-primary-foreground);
125
- border-color: transparent;
164
+ .vmz-ui-dialog__close:hover {
165
+ background: var(--vmz-surface-mist);
166
+ color: var(--vmz-text-ink);
126
167
  }
127
168
 
128
- .vmz-ui-dialog__close[data-variant="primary"]:hover {
129
- background: var(--vmz-action-primary-hover);
130
- border-color: transparent;
169
+ .vmz-ui-dialog__close:focus-visible {
170
+ box-shadow: 0 0 0 3px var(--vmz-focus-ring);
131
171
  }
132
172
 
133
- .vmz-ui-dialog__close[data-variant="primary"]:active {
134
- background: var(--vmz-action-primary-active);
173
+ .vmz-ui-dialog__body {
174
+ flex: 1 1 auto;
175
+ min-height: 0;
176
+ padding: 0 24px 20px;
177
+ overflow: auto;
178
+ font-size: 14px;
179
+ line-height: 22px;
180
+ color: var(--vmz-text-ink);
135
181
  }
136
182
 
137
- .vmz-ui-dialog__close:hover {
183
+ .vmz-ui-dialog__body :where(a) {
184
+ color: var(--vmz-action-primary-background);
185
+ text-underline-offset: 2px;
186
+ }
187
+
188
+ .vmz-ui-dialog__body :where(a):hover {
138
189
  color: var(--vmz-action-primary-hover);
139
- border-color: var(--vmz-action-primary-hover);
140
190
  }
141
191
 
142
- .vmz-ui-dialog__close:focus-visible {
143
- box-shadow: 0 0 0 3px var(--vmz-focus-ring);
192
+ .vmz-ui-dialog__actions {
193
+ display: flex;
194
+ flex-wrap: wrap;
195
+ align-items: center;
196
+ justify-content: flex-end;
197
+ gap: 8px;
198
+ padding: 0 24px 20px;
199
+ }
200
+
201
+ .vmz-ui-dialog__actions:empty {
202
+ display: none;
203
+ }
204
+
205
+ .vmz-ui-dialog__actions :where(button, a) {
206
+ min-height: var(--vmz-control-height, 32px);
207
+ }
208
+
209
+ .vmz-ui-dialog[data-action-size="lg"] .vmz-ui-dialog__actions :where(button, a) {
210
+ min-height: 40px;
144
211
  }
145
212
 
146
213
  @keyframes vmz-ui-overlay-fade-in {
@@ -155,7 +222,7 @@
155
222
  @keyframes vmz-ui-overlay-panel-in {
156
223
  from {
157
224
  opacity: 0;
158
- transform: translateY(0.35rem);
225
+ transform: translateY(8px) scale(0.98);
159
226
  }
160
227
  to {
161
228
  opacity: 1;
@@ -179,16 +246,18 @@
179
246
  }
180
247
  to {
181
248
  opacity: 0;
182
- transform: translateY(0.35rem);
249
+ transform: translateY(8px) scale(0.98);
183
250
  }
184
251
  }
185
252
 
186
253
  @media (prefers-reduced-motion: reduce) {
187
254
  .vmz-ui-dialog__backdrop,
188
255
  .vmz-ui-dialog__panel,
256
+ .vmz-ui-dialog__close,
189
257
  .vmz-ui-dialog[data-vmz-motion-phase="exit"] .vmz-ui-dialog__backdrop,
190
258
  .vmz-ui-dialog[data-vmz-motion-phase="exit"] .vmz-ui-dialog__panel {
191
259
  animation: none;
260
+ transition: none;
192
261
  }
193
262
  }
194
263
  </style>
@@ -197,10 +266,24 @@
197
266
  * VMZ UI — Dialog (UI1).
198
267
  * Live `open` + overlay ownership + focus + Escape/outside dismiss + stackLevel.
199
268
  * SSR/resume adopt skips enter replay; dismiss plays overlay-exit then calls onClose.
269
+ *
270
+ * Appearance props: `size` (sm 400 / md 520 / lg 720), `danger` tone,
271
+ * `description`, `actionSize` (md 32 / lg 40 action buttons).
272
+ * Visible copy is author-supplied; `closeLabel` / `dismissLabel` only name the
273
+ * icon button and backdrop for assistive tech.
200
274
  */
201
275
  export default class Dialog {
202
276
  public open: boolean = false;
203
- public title: string = "Dialog";
277
+ public title: string = "";
278
+ public label: string = "";
279
+ public description: string = "";
280
+ /** sm | md | lg */
281
+ public size: string = "md";
282
+ /** md | lg */
283
+ public actionSize: string = "md";
284
+ public danger: boolean = false;
285
+ public closeLabel: string = "";
286
+ public dismissLabel: string = "";
204
287
  public titleId: string = "vmz-dialog-title";
205
288
  public stackLevel: string = "0";
206
289
  public onClose: (() => void) | null = null;
@@ -9,11 +9,13 @@
9
9
  .vmz-ui-divider {
10
10
  display: flex;
11
11
  align-items: center;
12
- gap: 0.75rem;
12
+ gap: var(--vmz-space-3);
13
13
  width: 100%;
14
14
  color: var(--vmz-text-steel);
15
15
  font-size: 0.875rem;
16
+ line-height: 1.5715;
16
17
  }
18
+
17
19
  .vmz-ui-divider:before,
18
20
  .vmz-ui-divider:after {
19
21
  content: "";
@@ -21,18 +23,23 @@
21
23
  flex: 1;
22
24
  background: var(--vmz-line-default);
23
25
  }
26
+
24
27
  .vmz-ui-divider:not(:has(span)):before {
25
28
  flex: 1;
26
29
  }
30
+
27
31
  .vmz-ui-divider:not(:has(span)):after {
28
32
  display: none;
29
33
  }
34
+
30
35
  .vmz-ui-divider[data-direction="vertical"] {
31
36
  display: inline-block;
32
37
  width: 1px;
33
38
  height: 1em;
39
+ margin: 0 var(--vmz-space-2);
34
40
  background: var(--vmz-line-default);
35
41
  }
42
+
36
43
  .vmz-ui-divider[data-direction="vertical"]:before,
37
44
  .vmz-ui-divider[data-direction="vertical"]:after {
38
45
  display: none;
@@ -1,19 +1,27 @@
1
1
  <template>
2
2
  <div data-vmz-ui="drawer" :data-vmz-overlay-open="open" class="vmz-ui-drawer-root">
3
- <div v-if="open" data-vmz-overlay="drawer" data-vmz-overlay-owner="drawer" :data-vmz-overlay-stack="stackLevel" class="vmz-ui-drawer">
4
- <button type="button" data-vmz-overlay-layer="backdrop" aria-label="Dismiss drawer" @click="dismiss" class="vmz-ui-drawer__backdrop" />
5
- <div role="dialog" aria-modal="true" :aria-labelledby="titleId" tabindex="-1" data-vmz-overlay-layer="panel" data-vmz-focus="enter" data-vmz-motion="overlay-enter" data-vmz-motion-token="motion.overlay" :onKeyDown="onPanelKeyDown" class="vmz-ui-drawer__panel">
6
- <h2 :id="titleId" class="vmz-ui-drawer__title">
7
- {{ title }}
8
- </h2>
3
+ <div v-if="open" data-vmz-overlay="drawer" data-vmz-overlay-owner="drawer" :data-vmz-overlay-stack="stackLevel" :data-size="size" class="vmz-ui-drawer">
4
+ <button type="button" data-vmz-overlay-layer="backdrop" :aria-label="dismissLabel || closeLabel || title" @click="dismiss" class="vmz-ui-drawer__backdrop" />
5
+ <div role="dialog" aria-modal="true" :aria-labelledby="title ? titleId : null" tabindex="-1" data-vmz-overlay-layer="panel" data-vmz-focus="enter" data-vmz-motion="overlay-enter" data-vmz-motion-token="motion.overlay" :onKeyDown="onPanelKeyDown" class="vmz-ui-drawer__panel">
6
+ <header class="vmz-ui-drawer__header">
7
+ <div class="vmz-ui-drawer__heading">
8
+ <h2 v-if="title" :id="titleId" class="vmz-ui-drawer__title">
9
+ {{ title }}
10
+ </h2>
11
+ <p v-if="description" class="vmz-ui-drawer__description">
12
+ {{ description }}
13
+ </p>
14
+ </div>
15
+ <button type="button" data-vmz-focus="close" :aria-label="closeLabel || title" @click="dismiss" class="vmz-ui-drawer__close">
16
+ <Icon name="action.close" size="md" decorative />
17
+ </button>
18
+ </header>
9
19
  <div class="vmz-ui-drawer__body">
10
20
  <slot />
11
21
  </div>
12
- <div class="vmz-ui-drawer__actions">
13
- <button type="button" data-vmz-focus="close" @click="dismiss" class="vmz-ui-drawer__close">
14
- Close
15
- </button>
16
- </div>
22
+ <footer class="vmz-ui-drawer__footer">
23
+ <slot name="footer" />
24
+ </footer>
17
25
  </div>
18
26
  </div>
19
27
  </div>
@@ -48,7 +56,7 @@
48
56
  padding: 0;
49
57
  margin: 0;
50
58
  cursor: pointer;
51
- background: rgba(0, 0, 0, 0.45);
59
+ background: color-mix(in srgb, var(--vmz-surface-ink) 45%, transparent);
52
60
  animation: vmz-ui-drawer-fade-in var(--vmz-motion-overlay-duration)
53
61
  var(--vmz-motion-overlay-easing);
54
62
  }
@@ -59,20 +67,27 @@
59
67
  right: 0;
60
68
  z-index: 1;
61
69
  box-sizing: border-box;
62
- width: min(23.75rem, 100vw);
70
+ display: flex;
71
+ flex-direction: column;
72
+ width: min(420px, 100vw);
63
73
  height: 100%;
64
- padding: 1.25rem 1.5rem;
65
- border-left: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
66
- background: var(--vmz-surface-paper, #fff);
74
+ border-left: 1px solid var(--vmz-line-subtle);
75
+ background: var(--vmz-surface-paper);
67
76
  color: var(--vmz-text-ink);
68
77
  box-shadow: var(--vmz-shadow-popup);
69
78
  outline: none;
70
- display: flex;
71
- flex-direction: column;
72
79
  animation: vmz-ui-drawer-panel-in var(--vmz-motion-overlay-duration)
73
80
  var(--vmz-motion-overlay-easing);
74
81
  }
75
82
 
83
+ .vmz-ui-drawer[data-size="sm"] .vmz-ui-drawer__panel {
84
+ width: min(320px, 100vw);
85
+ }
86
+
87
+ .vmz-ui-drawer[data-size="lg"] .vmz-ui-drawer__panel {
88
+ width: min(560px, 100vw);
89
+ }
90
+
76
91
  .vmz-ui-drawer[data-vmz-motion-adopt="true"] .vmz-ui-drawer__backdrop,
77
92
  .vmz-ui-drawer[data-vmz-motion-adopt="true"] .vmz-ui-drawer__panel {
78
93
  animation: none;
@@ -89,45 +104,103 @@
89
104
  }
90
105
 
91
106
  .vmz-ui-drawer__panel:focus-visible {
92
- box-shadow: 0 0 0 3px var(--vmz-focus-ring);
107
+ box-shadow: 0 0 0 3px var(--vmz-focus-ring), var(--vmz-shadow-popup);
108
+ }
109
+
110
+ .vmz-ui-drawer__header {
111
+ display: flex;
112
+ align-items: flex-start;
113
+ gap: 12px;
114
+ padding: 16px 20px;
115
+ border-bottom: 1px solid var(--vmz-line-subtle);
116
+ }
117
+
118
+ .vmz-ui-drawer__heading {
119
+ flex: 1 1 auto;
120
+ min-width: 0;
93
121
  }
94
122
 
95
123
  .vmz-ui-drawer__title {
96
- margin: 0 0 0.75rem;
97
- font-size: 1rem;
124
+ margin: 0;
125
+ font-size: 16px;
98
126
  font-weight: 600;
127
+ line-height: 24px;
99
128
  color: var(--vmz-text-ink);
100
129
  }
101
130
 
102
- .vmz-ui-drawer__body {
103
- flex: 1;
104
- margin-bottom: 1rem;
105
- overflow: auto;
106
- color: var(--vmz-text-ink);
131
+ .vmz-ui-drawer__description {
132
+ margin: 4px 0 0;
133
+ font-size: 14px;
134
+ line-height: 22px;
135
+ color: var(--vmz-text-tertiary);
107
136
  }
108
137
 
109
138
  .vmz-ui-drawer__close {
110
- font: inherit;
111
- font-size: 0.875rem;
112
- font-weight: 400;
113
- min-height: var(--vmz-control-height, 2rem);
114
- padding: 0.25rem 1rem;
115
- border: 1px solid var(--vmz-line-default);
116
- border-radius: var(--vmz-radius-md, 6px);
139
+ flex: 0 0 auto;
140
+ display: inline-flex;
141
+ align-items: center;
142
+ justify-content: center;
143
+ width: 32px;
144
+ height: 32px;
145
+ margin: -4px -8px -4px 0;
146
+ padding: 0;
147
+ border: 0;
148
+ border-radius: var(--vmz-radius-md);
149
+ background: transparent;
150
+ color: var(--vmz-text-steel);
117
151
  cursor: pointer;
118
- background: var(--vmz-surface-paper, #fff);
119
- color: var(--vmz-text-ink);
152
+ outline: none;
153
+ transition:
154
+ color var(--vmz-motion-control-duration) var(--vmz-motion-control-easing),
155
+ background-color var(--vmz-motion-control-duration) var(--vmz-motion-control-easing);
120
156
  }
121
157
 
122
158
  .vmz-ui-drawer__close:hover {
123
- color: var(--vmz-action-primary-background);
124
- border-color: var(--vmz-action-primary-background);
159
+ background: var(--vmz-surface-mist);
160
+ color: var(--vmz-text-ink);
125
161
  }
126
162
 
127
163
  .vmz-ui-drawer__close:focus-visible {
128
164
  box-shadow: 0 0 0 3px var(--vmz-focus-ring);
129
165
  }
130
166
 
167
+ .vmz-ui-drawer__body {
168
+ flex: 1 1 auto;
169
+ min-height: 0;
170
+ padding: 16px 20px;
171
+ overflow: auto;
172
+ font-size: 14px;
173
+ line-height: 22px;
174
+ color: var(--vmz-text-ink);
175
+ }
176
+
177
+ .vmz-ui-drawer__body :where(a) {
178
+ color: var(--vmz-action-primary-background);
179
+ text-underline-offset: 2px;
180
+ }
181
+
182
+ .vmz-ui-drawer__body :where(a):hover {
183
+ color: var(--vmz-action-primary-hover);
184
+ }
185
+
186
+ .vmz-ui-drawer__footer {
187
+ display: flex;
188
+ flex-wrap: wrap;
189
+ align-items: center;
190
+ justify-content: flex-end;
191
+ gap: 8px;
192
+ padding: 12px 20px;
193
+ border-top: 1px solid var(--vmz-line-subtle);
194
+ }
195
+
196
+ .vmz-ui-drawer__footer:empty {
197
+ display: none;
198
+ }
199
+
200
+ .vmz-ui-drawer__footer :where(button, a) {
201
+ min-height: var(--vmz-control-height, 32px);
202
+ }
203
+
131
204
  @keyframes vmz-ui-drawer-fade-in {
132
205
  from {
133
206
  opacity: 0;
@@ -140,7 +213,7 @@
140
213
  @keyframes vmz-ui-drawer-panel-in {
141
214
  from {
142
215
  opacity: 0;
143
- transform: translateX(0.75rem);
216
+ transform: translateX(16px);
144
217
  }
145
218
  to {
146
219
  opacity: 1;
@@ -164,16 +237,25 @@
164
237
  }
165
238
  to {
166
239
  opacity: 0;
167
- transform: translateX(0.75rem);
240
+ transform: translateX(16px);
241
+ }
242
+ }
243
+
244
+ @media (max-width: 720px) {
245
+ .vmz-ui-drawer__panel {
246
+ width: 100vw;
247
+ border-left: 0;
168
248
  }
169
249
  }
170
250
 
171
251
  @media (prefers-reduced-motion: reduce) {
172
252
  .vmz-ui-drawer__backdrop,
173
253
  .vmz-ui-drawer__panel,
254
+ .vmz-ui-drawer__close,
174
255
  .vmz-ui-drawer[data-vmz-motion-phase="exit"] .vmz-ui-drawer__backdrop,
175
256
  .vmz-ui-drawer[data-vmz-motion-phase="exit"] .vmz-ui-drawer__panel {
176
257
  animation: none;
258
+ transition: none;
177
259
  }
178
260
  }
179
261
  </style>
@@ -182,10 +264,19 @@
182
264
  * VMZ UI — Drawer (UI2).
183
265
  * Same overlay ownership / focus / Escape / stack as Dialog.
184
266
  * SSR/resume adopt skips enter replay; dismiss plays overlay-exit then calls onClose.
267
+ *
268
+ * Appearance props: `size` (sm 320 / md 420 / lg 560), `description`.
269
+ * Visible copy is author-supplied; `closeLabel` / `dismissLabel` only name the
270
+ * icon button and backdrop for assistive tech.
185
271
  */
186
272
  export default class Drawer {
187
273
  public open: boolean = false;
188
- public title: string = "Drawer";
274
+ public title: string = "";
275
+ public description: string = "";
276
+ /** sm | md | lg */
277
+ public size: string = "md";
278
+ public closeLabel: string = "";
279
+ public dismissLabel: string = "";
189
280
  public titleId: string = "vmz-drawer-title";
190
281
  public stackLevel: string = "0";
191
282
  public onClose: (() => void) | null = null;