@vmz/ui 0.3.7 → 0.3.9

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 +249 -69
  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
package/README.md CHANGED
@@ -25,6 +25,8 @@ Headline surface for this cut:
25
25
  - Upload multipart binary (parent-owned FormData → `#server` File/Blob; progress/cancel)
26
26
  - Density / RTL / preset materialize
27
27
  - Motion continuity + interrupt/cancel
28
+ - Form rule validation (`Form#rules` + `values` → `invalid`), destructive `Popconfirm`,
29
+ transient `Message`, read-only `Descriptions`
28
30
  - Motion compiler IR thin gate (`pnpm verify -- motion-ir`)
29
31
  - UI7 conformance pack (`@vmz/ui/conformance` + `pnpm verify -- ui7`)
30
32
  - `@vmz/ui-data-grid` thin gate (virtualization + pinned column — not tree/group/pivot/edit)
@@ -192,7 +192,7 @@
192
192
  "density.control.padding-x"
193
193
  ],
194
194
  "surfaces": ["WebSurface"],
195
- "contracts": ["submit-reset", "error-summary", "no-parallel-form-store", "novalidate"]
195
+ "contracts": ["submit-reset", "error-summary", "no-parallel-form-store", "novalidate", "rule-validation"]
196
196
  },
197
197
  "FormItem": {
198
198
  "source": "src/components/FormItem.vmz",
@@ -326,6 +326,63 @@
326
326
  "overlay-stacking"
327
327
  ]
328
328
  },
329
+ "Popconfirm": {
330
+ "source": "src/components/Popconfirm.vmz",
331
+ "stableHint": "ui.Popconfirm",
332
+ "tokens": [
333
+ "line.subtle",
334
+ "surface.paper",
335
+ "text.ink",
336
+ "text.steel",
337
+ "radius.lg",
338
+ "shadow.popup",
339
+ "status.danger.accent",
340
+ "status.warning.accent",
341
+ "status.info.accent",
342
+ "focus.ring",
343
+ "motion.overlay.duration",
344
+ "motion.overlay.easing"
345
+ ],
346
+ "surfaces": ["WebSurface"],
347
+ "contracts": [
348
+ "destructive-confirm",
349
+ "overlay-ownership",
350
+ "outside-dismiss",
351
+ "focus-enter",
352
+ "focus-restore",
353
+ "escape-dismiss",
354
+ "overlay-stacking"
355
+ ]
356
+ },
357
+ "Message": {
358
+ "source": "src/components/Message.vmz",
359
+ "stableHint": "ui.Message",
360
+ "tokens": [
361
+ "surface.paper",
362
+ "text.ink",
363
+ "text.steel",
364
+ "text.tertiary",
365
+ "line.subtle",
366
+ "radius.lg",
367
+ "shadow.popup",
368
+ "status.info.accent",
369
+ "status.success.accent",
370
+ "status.warning.accent",
371
+ "status.danger.accent",
372
+ "focus.ring",
373
+ "motion.overlay.duration",
374
+ "motion.overlay.easing"
375
+ ],
376
+ "surfaces": ["WebSurface"],
377
+ "contracts": ["transient-feedback", "auto-dismiss", "tone-matrix", "status-tokens"]
378
+ },
379
+ "Descriptions": {
380
+ "source": "src/components/Descriptions.vmz",
381
+ "stableHint": "ui.Descriptions",
382
+ "tokens": ["text.ink", "text.steel", "line.default", "line.subtle", "radius.md"],
383
+ "surfaces": ["WebSurface"],
384
+ "contracts": ["read-only-detail", "column-layout", "no-column-inference"]
385
+ },
329
386
  "AppShell": {
330
387
  "source": "src/components/AppShell.vmz",
331
388
  "stableHint": "ui.AppShell",
package/package.json CHANGED
@@ -19,6 +19,7 @@
19
19
  "./Content": "./src/components/Content.vmz",
20
20
  "./DataTable": "./src/components/DataTable.vmz",
21
21
  "./DatePicker": "./src/components/DatePicker.vmz",
22
+ "./Descriptions": "./src/components/Descriptions.vmz",
22
23
  "./Dialog": "./src/components/Dialog.vmz",
23
24
  "./Divider": "./src/components/Divider.vmz",
24
25
  "./Drawer": "./src/components/Drawer.vmz",
@@ -37,8 +38,10 @@
37
38
  "./Link": "./src/components/Link.vmz",
38
39
  "./List": "./src/components/List.vmz",
39
40
  "./Menu": "./src/components/Menu.vmz",
41
+ "./Message": "./src/components/Message.vmz",
40
42
  "./Notification": "./src/components/Notification.vmz",
41
43
  "./Pagination": "./src/components/Pagination.vmz",
44
+ "./Popconfirm": "./src/components/Popconfirm.vmz",
42
45
  "./Popover": "./src/components/Popover.vmz",
43
46
  "./Progress": "./src/components/Progress.vmz",
44
47
  "./Prose": "./src/components/Prose.vmz",
@@ -82,7 +85,7 @@
82
85
  "license": "MIT",
83
86
  "name": "@vmz/ui",
84
87
  "peerDependencies": {
85
- "@vmz/ui-icons": "0.3.7"
88
+ "@vmz/ui-icons": "0.3.9"
86
89
  },
87
90
  "peerDependenciesMeta": {
88
91
  "@vmz/ui-icons": {
@@ -98,5 +101,5 @@
98
101
  "url": "git+https://github.com/doki-land/vmz-framework.git"
99
102
  },
100
103
  "type": "module",
101
- "version": "0.3.7"
104
+ "version": "0.3.9"
102
105
  }
@@ -9,29 +9,31 @@
9
9
  "sm": "4px",
10
10
  "md": "6px",
11
11
  "lg": "8px",
12
+ "xl": "12px",
12
13
  "pill": "999px"
13
14
  },
14
- "controlHeight": { "sm": "1.5rem", "middle": "2rem", "lg": "2.5rem" },
15
+ "controlHeight": { "sm": "1.5rem", "middle": "2rem", "lg": "2.5rem", "mobile": "2.75rem" },
15
16
  "layoutMaxWidth": "75rem",
16
- "surfacePage": "#F5F5F5",
17
- "surfaceFill": "#FAFAFA",
18
- "textTertiary": "#8C8C8C",
19
- "lineDefault": "#D9D9D9",
20
- "lineSubtle": "#F0F0F0",
17
+ "surfacePage": "#F5F6F8",
18
+ "surfaceFill": "#FAFBFC",
19
+ "textTertiary": "#646A73",
20
+ "textMuted": "#8A9099",
21
+ "lineDefault": "#E5E6EB",
22
+ "lineSubtle": "#F0F1F3",
21
23
  "shadowCard": "none",
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)",
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
+ "shadowCardHover": "none",
25
+ "shadowPopup": "0 12px 32px 0 rgba(15,23,42,.12), 0 2px 8px 0 rgba(15,23,42,.06)",
24
26
  "elevation": {
25
27
  "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)"
28
+ "float-sm": "0 1px 2px 0 rgba(15,23,42,.04), 0 1px 4px -1px rgba(15,23,42,.03)",
29
+ "float-md": "0 4px 12px 0 rgba(15,23,42,.08), 0 1px 3px 0 rgba(15,23,42,.04)",
30
+ "float-lg": "0 12px 32px 0 rgba(15,23,42,.12), 0 2px 8px 0 rgba(15,23,42,.06)"
29
31
  }
30
32
  },
31
33
  "density": {
32
34
  "control": {
33
35
  "padding-y": "0.25rem",
34
- "padding-x": "0.9375rem",
36
+ "padding-x": "0.75rem",
35
37
  "gap": "0.5rem"
36
38
  },
37
39
  "compact": {
@@ -2,8 +2,11 @@
2
2
  <div data-vmz-ui="accordion" :aria-label="label" class="vmz-ui-accordion">
3
3
  <div v-for="item in items" :key="item.id" :data-vmz-accordion="item.id" class="vmz-ui-accordion__item">
4
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 }}
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
+ <span class="vmz-ui-accordion__title">
7
+ {{ item.title }}
8
+ </span>
9
+ <Icon name="action.chevron-down" size="sm" decorative />
7
10
  </button>
8
11
  </h3>
9
12
  <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">
@@ -16,16 +19,16 @@
16
19
  .vmz-ui-accordion {
17
20
  display: flex;
18
21
  flex-direction: column;
19
- gap: 0.375rem;
20
- max-width: 36rem;
22
+ box-sizing: border-box;
21
23
  border: 1px solid var(--vmz-line-default);
22
- border-radius: var(--vmz-radius-md, 6px);
24
+ border-radius: var(--vmz-radius-lg);
23
25
  background: var(--vmz-surface-paper);
24
26
  color: var(--vmz-text-ink);
27
+ overflow: hidden;
25
28
  }
26
29
 
27
30
  .vmz-ui-accordion__item {
28
- border-bottom: 1px solid var(--vmz-line-default);
31
+ border-bottom: 1px solid var(--vmz-line-subtle);
29
32
  }
30
33
 
31
34
  .vmz-ui-accordion__item:last-child {
@@ -38,35 +41,65 @@
38
41
  }
39
42
 
40
43
  .vmz-ui-accordion__trigger {
41
- display: block;
44
+ display: flex;
45
+ align-items: center;
46
+ gap: 12px;
42
47
  width: 100%;
48
+ min-height: 40px;
49
+ box-sizing: border-box;
43
50
  text-align: start;
44
51
  font: inherit;
52
+ font-size: 14px;
45
53
  font-weight: 600;
54
+ line-height: 22px;
46
55
  padding: var(--vmz-density-control-padding-y) var(--vmz-density-control-padding-x);
47
56
  border: 0;
48
57
  background: transparent;
49
- color: inherit;
58
+ color: var(--vmz-text-ink);
50
59
  cursor: pointer;
51
60
  outline: none;
61
+ transition: background-color var(--vmz-motion-control-duration)
62
+ var(--vmz-motion-control-easing);
52
63
  }
53
64
 
54
- .vmz-ui-accordion__trigger:hover {
55
- background: color-mix(in srgb, var(--vmz-surface-mist) 85%, transparent);
65
+ .vmz-ui-accordion__title {
66
+ flex: 1 1 auto;
67
+ min-width: 0;
56
68
  }
57
69
 
58
- .vmz-ui-accordion__trigger:focus-visible {
59
- box-shadow: inset 0 0 0 3px var(--vmz-focus-ring);
70
+ .vmz-ui-accordion__trigger .vmz-ui-icon {
71
+ flex: 0 0 auto;
72
+ color: var(--vmz-text-tertiary);
73
+ transition:
74
+ color var(--vmz-motion-control-duration) var(--vmz-motion-control-easing),
75
+ transform var(--vmz-motion-control-duration) var(--vmz-motion-control-easing);
60
76
  }
61
77
 
62
- .vmz-ui-accordion__trigger[aria-expanded="true"] {
78
+ .vmz-ui-accordion__trigger[aria-expanded="true"] .vmz-ui-icon {
63
79
  color: var(--vmz-action-primary-active);
80
+ transform: rotate(180deg);
81
+ }
82
+
83
+ .vmz-ui-accordion__trigger:hover {
84
+ background: var(--vmz-surface-mist);
85
+ }
86
+
87
+ .vmz-ui-accordion__trigger:focus-visible {
88
+ box-shadow: inset 0 0 0 2px var(--vmz-focus-ring);
64
89
  }
65
90
 
66
91
  .vmz-ui-accordion__panel {
67
- padding: 0 var(--vmz-density-control-padding-x) var(--vmz-density-control-padding-y);
92
+ padding: 0 16px 16px;
93
+ font-size: 14px;
94
+ line-height: 26px;
68
95
  color: var(--vmz-text-steel);
69
- line-height: 1.5;
96
+ }
97
+
98
+ @media (prefers-reduced-motion: reduce) {
99
+ .vmz-ui-accordion__trigger,
100
+ .vmz-ui-accordion__trigger .vmz-ui-icon {
101
+ transition: none;
102
+ }
70
103
  }
71
104
  </style>
72
105
  <script client>
@@ -76,7 +109,7 @@
76
109
  * Panel body lives on items (no per-panel slot runtime).
77
110
  */
78
111
  export default class Accordion {
79
- public label: string = "Accordion";
112
+ public label: string = "";
80
113
  public openId: string = "";
81
114
  /** @type {{ id: string, title: string, body: string, triggerId: string, panelId: string }[]} */
82
115
  public items: {
@@ -1,82 +1,179 @@
1
1
  <template>
2
2
  <div data-vmz-ui="alert" :data-tone="tone" :role="roleName" class="vmz-ui-alert">
3
- <p v-if="title" class="vmz-ui-alert__title">
4
- {{ title }}
5
- </p>
6
- <p v-if="description" class="vmz-ui-alert__description">
7
- {{ description }}
8
- </p>
9
- <div class="vmz-ui-alert__actions">
10
- <slot />
3
+ <span aria-hidden="true" class="vmz-ui-alert__icon">
4
+ <Icon :name="toneIcon()" size="md" decorative />
5
+ </span>
6
+ <div class="vmz-ui-alert__content">
7
+ <p v-if="title" class="vmz-ui-alert__title">
8
+ {{ title }}
9
+ </p>
10
+ <p v-if="description" class="vmz-ui-alert__description">
11
+ {{ description }}
12
+ </p>
13
+ <div class="vmz-ui-alert__actions">
14
+ <slot />
15
+ </div>
11
16
  </div>
17
+ <button v-if="closable" type="button" :aria-label="closeLabel" @click="dismiss" class="vmz-ui-alert__close">
18
+ <Icon name="action.close" size="md" decorative />
19
+ </button>
12
20
  </div>
13
21
  </template>
14
22
  <style>
15
23
  .vmz-ui-alert {
16
24
  display: flex;
17
- flex-direction: column;
18
- gap: var(--vmz-density-control-gap);
19
- padding: var(--vmz-density-control-padding-y) var(--vmz-density-control-padding-x);
20
- border: 1px solid var(--vmz-status-info-accent);
21
- border-left-width: 1px;
22
- border-radius: var(--vmz-radius-md, 6px);
23
- background: color-mix(in srgb, var(--vmz-status-info-accent) 12%, transparent);
25
+ align-items: flex-start;
26
+ gap: var(--vmz-space-2, 0.5rem);
27
+ padding: var(--vmz-space-3, 0.75rem) var(--vmz-space-4, 1rem);
28
+ border: 1px solid var(--vmz-status-info-border, var(--vmz-line-default));
29
+ border-radius: var(--vmz-radius-lg, 8px);
30
+ background: var(--vmz-status-info-background, var(--vmz-surface-mist));
24
31
  color: var(--vmz-status-info-foreground);
25
32
  }
26
33
 
27
34
  .vmz-ui-alert[data-tone="info"] {
28
- border-color: var(--vmz-status-info-accent);
29
- background: color-mix(in srgb, var(--vmz-status-info-accent) 12%, transparent);
35
+ border-color: var(--vmz-status-info-border, var(--vmz-line-default));
36
+ background: var(--vmz-status-info-background, var(--vmz-surface-mist));
30
37
  color: var(--vmz-status-info-foreground);
31
38
  }
32
39
 
33
40
  .vmz-ui-alert[data-tone="success"] {
34
- border-color: var(--vmz-status-success-accent);
35
- background: color-mix(in srgb, var(--vmz-status-success-accent) 12%, transparent);
41
+ border-color: var(--vmz-status-success-border, var(--vmz-line-default));
42
+ background: var(--vmz-status-success-background, var(--vmz-surface-mist));
36
43
  color: var(--vmz-status-success-foreground);
37
44
  }
38
45
 
39
46
  .vmz-ui-alert[data-tone="warning"] {
40
- border-color: var(--vmz-status-warning-accent);
41
- background: color-mix(in srgb, var(--vmz-status-warning-accent) 12%, transparent);
47
+ border-color: var(--vmz-status-warning-border, var(--vmz-line-default));
48
+ background: var(--vmz-status-warning-background, var(--vmz-surface-mist));
42
49
  color: var(--vmz-status-warning-foreground);
43
50
  }
44
51
 
45
52
  .vmz-ui-alert[data-tone="danger"] {
46
- border-color: var(--vmz-status-danger-accent);
47
- background: color-mix(in srgb, var(--vmz-status-danger-accent) 12%, transparent);
53
+ border-color: var(--vmz-status-danger-border, var(--vmz-line-default));
54
+ background: var(--vmz-status-danger-background, var(--vmz-surface-mist));
48
55
  color: var(--vmz-status-danger-foreground);
49
56
  }
50
57
 
51
- :where([data-density="compact"]) .vmz-ui-alert {
52
- gap: var(--vmz-density-compact-gap);
53
- padding: var(--vmz-density-compact-padding-y) var(--vmz-density-compact-padding-x);
58
+ .vmz-ui-alert[data-tone="info"] .vmz-ui-alert__icon {
59
+ color: var(--vmz-status-info-accent);
60
+ }
61
+
62
+ .vmz-ui-alert[data-tone="success"] .vmz-ui-alert__icon {
63
+ color: var(--vmz-status-success-accent);
64
+ }
65
+
66
+ .vmz-ui-alert[data-tone="warning"] .vmz-ui-alert__icon {
67
+ color: var(--vmz-status-warning-accent);
68
+ }
69
+
70
+ .vmz-ui-alert[data-tone="danger"] .vmz-ui-alert__icon {
71
+ color: var(--vmz-status-danger-accent);
72
+ }
73
+
74
+ .vmz-ui-alert__icon {
75
+ flex: 0 0 auto;
76
+ display: inline-flex;
77
+ align-items: center;
78
+ justify-content: center;
79
+ height: 1.5rem;
80
+ }
81
+
82
+ .vmz-ui-alert__content {
83
+ flex: 1 1 auto;
84
+ display: flex;
85
+ flex-direction: column;
86
+ gap: var(--vmz-space-1, 0.25rem);
87
+ min-width: 0;
54
88
  }
55
89
 
56
90
  .vmz-ui-alert__title {
57
91
  margin: 0;
58
92
  font-size: 0.875rem;
59
93
  font-weight: 600;
94
+ line-height: 1.5;
95
+ color: inherit;
60
96
  }
61
97
 
62
98
  .vmz-ui-alert__description {
63
99
  margin: 0;
64
- opacity: 0.9;
100
+ font-size: 0.875rem;
101
+ line-height: 1.55;
102
+ color: var(--vmz-text-steel);
103
+ }
104
+
105
+ .vmz-ui-alert__actions {
106
+ display: flex;
107
+ flex-wrap: wrap;
108
+ gap: var(--vmz-space-2, 0.5rem);
109
+ margin-top: var(--vmz-space-1, 0.25rem);
65
110
  }
66
111
 
67
112
  .vmz-ui-alert__actions:empty {
68
113
  display: none;
69
114
  }
115
+
116
+ .vmz-ui-alert__close {
117
+ flex: 0 0 auto;
118
+ display: inline-flex;
119
+ align-items: center;
120
+ justify-content: center;
121
+ width: 1.5rem;
122
+ height: 1.5rem;
123
+ padding: 0;
124
+ border: 1px solid transparent;
125
+ border-radius: var(--vmz-radius-sm, 4px);
126
+ background: transparent;
127
+ color: var(--vmz-text-tertiary);
128
+ cursor: pointer;
129
+ outline: none;
130
+ transition:
131
+ color var(--vmz-motion-control-duration, 120ms) var(--vmz-motion-control-easing, cubic-bezier(0.2, 0, 0, 1)),
132
+ background-color var(--vmz-motion-control-duration, 120ms) var(--vmz-motion-control-easing, cubic-bezier(0.2, 0, 0, 1));
133
+ }
134
+
135
+ .vmz-ui-alert__close:hover {
136
+ color: var(--vmz-text-ink);
137
+ background: var(--vmz-surface-mist);
138
+ }
139
+
140
+ .vmz-ui-alert__close:focus-visible {
141
+ box-shadow: 0 0 0 3px var(--vmz-focus-ring);
142
+ }
143
+
144
+ :where([data-density="compact"]) .vmz-ui-alert {
145
+ gap: var(--vmz-space-2, 0.5rem);
146
+ padding: var(--vmz-space-2, 0.5rem) var(--vmz-space-3, 0.75rem);
147
+ }
148
+
149
+ :where([data-density="dense"]) .vmz-ui-alert {
150
+ gap: var(--vmz-space-1, 0.25rem);
151
+ padding: var(--vmz-space-1, 0.25rem) var(--vmz-space-2, 0.5rem);
152
+ }
70
153
  </style>
71
154
  <script client>
72
155
  /**
73
156
  * VMZ UI — Alert (UI4 status tone matrix).
74
- * tone → status.{info|success|warning|danger}.* tokens from Style Theme.
157
+ * tone → status.{info|success|warning|danger}.{accent|foreground|background|border}.
75
158
  */
76
159
  export default class Alert {
77
160
  public title: string = "";
78
161
  public description: string = "";
79
162
  public tone: string = "info";
80
163
  public roleName: string = "status";
164
+ public closable: boolean = false;
165
+ public closeLabel: string = "";
166
+ public onClose: (() => void) | null = null;
167
+
168
+ toneIcon() {
169
+ const tone = String(this.tone || "").toLowerCase();
170
+ if (tone === "success" || tone === "warning" || tone === "danger") return `status.${tone}`;
171
+ return "status.info";
172
+ }
173
+
174
+ dismiss() {
175
+ this.emit("close");
176
+ if (typeof this.onClose === "function") this.onClose();
177
+ }
81
178
  }
82
179
  </script>