@zeyos/zx 4.2.0 → 4.3.0

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 (52) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +5 -0
  3. package/dist/types/components/calendar/calendar.d.ts +19 -2
  4. package/dist/types/components/dialog/dialog.d.ts +6 -1
  5. package/dist/types/components/dropdown/dropdown.d.ts +5 -0
  6. package/dist/types/components/launcher/launcher.d.ts +5 -0
  7. package/dist/types/components/message/message.d.ts +9 -1
  8. package/dist/types/components/modal/modal.d.ts +44 -7
  9. package/dist/types/components/sheet/sheet.d.ts +22 -12
  10. package/dist/types/core/overlay-host.d.ts +12 -0
  11. package/dist/zx-zeyos.esm.js +138 -32
  12. package/dist/zx-zeyos.esm.min.js +1 -1
  13. package/dist/zx.css +259 -132
  14. package/dist/zx.esm.js +277 -82
  15. package/dist/zx.esm.min.js +3 -3
  16. package/dist/zx.global.js +277 -82
  17. package/dist/zx.global.min.js +3 -3
  18. package/dist/zx.min.css +1 -1
  19. package/docs/api.json +36 -0
  20. package/docs/llms.md +37 -18
  21. package/docs/llms.txt +1 -1
  22. package/package.json +1 -1
  23. package/src/components/account-menu/account-menu.css +1 -1
  24. package/src/components/app-rail/app-rail.css +1 -1
  25. package/src/components/calendar/calendar.css +66 -24
  26. package/src/components/calendar/calendar.js +114 -19
  27. package/src/components/context-menu/context-menu.css +2 -2
  28. package/src/components/context-menu/context-menu.js +5 -1
  29. package/src/components/date-picker/date-picker.css +18 -11
  30. package/src/components/datebox/date-range-box.css +4 -2
  31. package/src/components/datebox/datebox.css +4 -2
  32. package/src/components/dialog/dialog.css +4 -3
  33. package/src/components/dialog/dialog.js +2 -1
  34. package/src/components/dropdown/dropdown.css +4 -2
  35. package/src/components/dropdown/dropdown.js +7 -2
  36. package/src/components/launcher/launcher.css +10 -8
  37. package/src/components/launcher/launcher.js +9 -2
  38. package/src/components/menu-button/menu-button.css +2 -2
  39. package/src/components/message/message.css +7 -16
  40. package/src/components/message/message.js +17 -11
  41. package/src/components/modal/modal.css +6 -4
  42. package/src/components/modal/modal.js +99 -25
  43. package/src/components/select/select.css +9 -7
  44. package/src/components/sheet/sheet.css +25 -7
  45. package/src/components/sheet/sheet.js +43 -23
  46. package/src/components/table/table.css +5 -3
  47. package/src/components/tag-picker/tag-picker.css +5 -3
  48. package/src/components/tooltip/tooltip.css +4 -2
  49. package/src/components/tooltip/tooltip.js +4 -1
  50. package/src/core/overlay-host.js +23 -0
  51. package/styles/tokens/dark.css +4 -0
  52. package/styles/tokens/semantic.css +83 -8
@@ -29,7 +29,7 @@
29
29
 
30
30
  .zx-context-menu__item:hover,
31
31
  .zx-context-menu__item:focus-visible {
32
- background: var(--zx-color-bg-hover);
32
+ background: var(--zx-color-overlay-hover);
33
33
  }
34
34
 
35
35
  .zx-context-menu__item[data-danger="true"] {
@@ -50,5 +50,5 @@
50
50
  .zx-context-menu__separator {
51
51
  block-size: 1px;
52
52
  margin: var(--zx-space-1) var(--zx-space-2);
53
- background: var(--zx-color-border);
53
+ background: var(--zx-color-overlay-divider);
54
54
  }
@@ -82,7 +82,11 @@ export class ContextMenu extends Component {
82
82
  this.#target = resolved;
83
83
  document.body.append(this.el);
84
84
 
85
- this.#dropdown = new Dropdown(this.el, { openOn: 'manual', placement: 'bottom-start' });
85
+ this.#dropdown = new Dropdown(this.el, {
86
+ openOn: 'manual',
87
+ placement: 'bottom-start',
88
+ scope: resolved
89
+ });
86
90
  this.#panel = this.#dropdown.getPanel();
87
91
  this.#panel.classList.add('zx-context-menu');
88
92
  this.#panel.setAttribute('role', 'menu');
@@ -31,14 +31,14 @@
31
31
  place-items: center;
32
32
  min-inline-size: var(--zx-control-height);
33
33
  padding: 0;
34
- border-color: var(--zx-color-bg-surface);
35
- background: inherit;
34
+ border-color: transparent;
35
+ background: transparent;
36
36
  }
37
37
 
38
38
  .zx-date-picker__heading {
39
39
  overflow: hidden;
40
- border-color: var(--zx-color-bg-surface);
41
- background: inherit;
40
+ border-color: transparent;
41
+ background: transparent;
42
42
  font-weight: 650;
43
43
  text-overflow: ellipsis;
44
44
  text-wrap: nowrap;
@@ -76,9 +76,9 @@
76
76
  min-inline-size: 0;
77
77
  min-block-size: var(--zx-control-height);
78
78
  padding: 0;
79
- border-color: var(--zx-color-bg-surface);
79
+ border-color: transparent;
80
80
  border-radius: var(--zx-radius-md);
81
- background: inherit;
81
+ background: transparent;
82
82
  font-variant-numeric: tabular-nums;
83
83
  }
84
84
 
@@ -125,10 +125,12 @@
125
125
  inset: var(--zx-space-3);
126
126
  border: 1px solid var(--zx-color-overlay-border);
127
127
  border-radius: var(--zx-radius-md);
128
- background: var(--zx-color-overlay-surface);
128
+ background-color: var(--zx-color-overlay-surface);
129
+ background-image: var(--zx-overlay-surface-image);
130
+ background-clip: padding-box;
129
131
  -webkit-backdrop-filter: var(--zx-glass-filter-overlay);
130
132
  backdrop-filter: var(--zx-glass-filter-overlay);
131
- box-shadow: var(--zx-glass-raised-shadow);
133
+ box-shadow: var(--zx-glass-overlay-shadow);
132
134
  padding: var(--zx-space-3);
133
135
  }
134
136
 
@@ -157,11 +159,16 @@
157
159
  .zx-month-picker__month {
158
160
  min-inline-size: 0;
159
161
  padding-inline: var(--zx-space-1);
160
- border-color: var(--zx-color-bg-surface);
161
- background: inherit;
162
+ border-color: transparent;
163
+ background: transparent;
164
+ }
165
+
166
+ .zx-date-picker__quick-month[aria-selected="true"] {
167
+ background: var(--zx-color-overlay-selected);
168
+ color: var(--zx-color-accent);
169
+ font-weight: 700;
162
170
  }
163
171
 
164
- .zx-date-picker__quick-month[aria-selected="true"],
165
172
  .zx-month-picker__month[aria-selected="true"] {
166
173
  background: var(--zx-color-bg-selected);
167
174
  color: var(--zx-color-accent);
@@ -124,10 +124,12 @@
124
124
  .zx-date-range-box__popover .zx-date-range-picker__surface {
125
125
  max-inline-size: none;
126
126
  border-color: var(--zx-color-overlay-border);
127
- background: var(--zx-color-overlay-surface);
127
+ background-color: var(--zx-color-overlay-surface);
128
+ background-image: var(--zx-overlay-surface-image);
129
+ background-clip: padding-box;
128
130
  -webkit-backdrop-filter: var(--zx-glass-filter-overlay);
129
131
  backdrop-filter: var(--zx-glass-filter-overlay);
130
- box-shadow: var(--zx-glass-raised-shadow);
132
+ box-shadow: var(--zx-glass-overlay-shadow);
131
133
  }
132
134
 
133
135
  @media (prefers-reduced-transparency: reduce) {
@@ -104,10 +104,12 @@
104
104
 
105
105
  .zx-datebox__popover .zx-date-picker__surface {
106
106
  border-color: var(--zx-color-overlay-border);
107
- background: var(--zx-color-overlay-surface);
107
+ background-color: var(--zx-color-overlay-surface);
108
+ background-image: var(--zx-overlay-surface-image);
109
+ background-clip: padding-box;
108
110
  -webkit-backdrop-filter: var(--zx-glass-filter-overlay);
109
111
  backdrop-filter: var(--zx-glass-filter-overlay);
110
- box-shadow: var(--zx-glass-raised-shadow);
112
+ box-shadow: var(--zx-glass-overlay-shadow);
111
113
  }
112
114
 
113
115
  @media (prefers-reduced-transparency: reduce) {
@@ -3,7 +3,8 @@
3
3
  padding: 0;
4
4
  }
5
5
 
6
- .zx-dialog[open] {
6
+ .zx-dialog[open],
7
+ .zx-dialog:popover-open {
7
8
  display: grid;
8
9
  }
9
10
 
@@ -17,7 +18,7 @@
17
18
 
18
19
  .zx-dialog__header {
19
20
  justify-content: space-between;
20
- border-block-end: 1px solid var(--zx-color-border);
21
+ border-block-end: 1px solid var(--zx-color-overlay-divider);
21
22
  }
22
23
 
23
24
  .zx-dialog__title {
@@ -40,7 +41,7 @@
40
41
 
41
42
  .zx-dialog__footer {
42
43
  justify-content: flex-end;
43
- border-block-start: 1px solid var(--zx-color-border);
44
+ border-block-start: 1px solid var(--zx-color-overlay-divider);
44
45
  }
45
46
 
46
47
  .zx-dialog__footer[hidden] {
@@ -22,6 +22,7 @@ const FOCUSABLE_SELECTOR = [
22
22
  * @property {DialogButton[]} [buttons=[]] Footer buttons.
23
23
  * @property {boolean} [closable=true] Whether Escape and the header button may close the dialog.
24
24
  * @property {Node|string|number|{toElement: () => Node|null}|null} [content=null] Body content.
25
+ * @property {Element|string|null} [scope=null] Element whose nearest Zx theme scope owns the overlay; defaults to the opener.
25
26
  * @property {(event: CustomEvent<Record<string, never>>) => void} [onopen] Open event listener.
26
27
  * @property {(event: CustomEvent<{result: unknown}>) => void} [onclose] Close event listener.
27
28
  * @property {(event: CustomEvent<Record<string, never>>) => void} [oncancel] Cancel event listener.
@@ -89,7 +90,7 @@ export class Dialog extends Modal {
89
90
  #opener = null;
90
91
 
91
92
  /**
92
- * Creates a structured dialog appended to `document.body`.
93
+ * Creates a structured dialog in the configured theme scope, or at document level when unscoped.
93
94
  * @param {Element|string|null} [_target=null] Ignored; Dialog always owns its root.
94
95
  * @param {DialogOptions} [options={}] Dialog options.
95
96
  */
@@ -4,10 +4,12 @@
4
4
  overflow: auto;
5
5
  border: 1px solid var(--zx-color-overlay-border);
6
6
  border-radius: var(--zx-radius-md);
7
- background: var(--zx-color-overlay-surface);
7
+ background-color: var(--zx-color-overlay-surface);
8
+ background-image: var(--zx-overlay-surface-image);
9
+ background-clip: padding-box;
8
10
  -webkit-backdrop-filter: var(--zx-glass-filter-overlay);
9
11
  backdrop-filter: var(--zx-glass-filter-overlay);
10
- box-shadow: var(--zx-glass-raised-shadow);
12
+ box-shadow: var(--zx-glass-overlay-shadow);
11
13
  color: var(--zx-color-text);
12
14
  padding: var(--zx-space-3);
13
15
  }
@@ -1,5 +1,6 @@
1
1
  import { Component } from '../../core/component.js';
2
2
  import { h, resolveElement } from '../../core/dom.js';
3
+ import { overlayHost } from '../../core/overlay-host.js';
3
4
  import { position } from '../../core/position.js';
4
5
  import { uid } from '../../core/util.js';
5
6
 
@@ -14,6 +15,7 @@ const HANDLE_ESCAPE_DISMISS = Symbol('zxDropdownEscapeDismiss');
14
15
  * @property {boolean} [matchWidth=false] Whether the panel matches the anchor width.
15
16
  * @property {'click'|'manual'} [openOn='click'] Trigger behavior.
16
17
  * @property {boolean} [closeOnSelect=false] Whether a click within the panel closes it.
18
+ * @property {Element|string|null} [scope=null] Element whose nearest Zx theme scope owns the panel; defaults to the anchor.
17
19
  * @property {(event: CustomEvent<Record<string, never>>) => void} [onopen] Open event listener.
18
20
  * @property {(event: CustomEvent<Record<string, never>>) => void} [onclose] Close event listener.
19
21
  */
@@ -34,7 +36,8 @@ export class Dropdown extends Component {
34
36
  offset: 4,
35
37
  matchWidth: false,
36
38
  openOn: 'click',
37
- closeOnSelect: false
39
+ closeOnSelect: false,
40
+ scope: null
38
41
  };
39
42
 
40
43
  /** @type {Element} */
@@ -55,6 +58,7 @@ export class Dropdown extends Component {
55
58
  if (!resolved) throw new TypeError('Dropdown anchor could not be resolved');
56
59
  super(null, options);
57
60
  this.#anchor = resolved;
61
+ overlayHost(this.options.scope ?? resolved).append(this.el);
58
62
  this.#rememberAnchorAttribute('aria-expanded');
59
63
  this.#rememberAnchorAttribute('aria-controls');
60
64
  this.#anchor.setAttribute('aria-expanded', 'false');
@@ -79,7 +83,6 @@ export class Dropdown extends Component {
79
83
  popover: 'manual'
80
84
  });
81
85
  panel.dataset.state = 'closed';
82
- document.body.append(panel);
83
86
  return panel;
84
87
  }
85
88
 
@@ -90,6 +93,8 @@ export class Dropdown extends Component {
90
93
  */
91
94
  open() {
92
95
  if (this.isOpen() || this.#destroyed) return this;
96
+ const host = overlayHost(this.options.scope ?? this.#anchor);
97
+ if (this.el.parentElement !== host) host.append(this.el);
93
98
  this.#position = position(this.#anchor, this.el, {
94
99
  placement: this.options.placement,
95
100
  offset: Number(this.options.offset),
@@ -3,10 +3,12 @@
3
3
  max-block-size: min(720px, calc(100vh - (2 * var(--zx-space-6))));
4
4
  border: 1px solid var(--zx-color-overlay-border);
5
5
  border-radius: var(--zx-radius-xl);
6
- background: var(--zx-color-overlay-panel);
6
+ background-color: var(--zx-color-overlay-panel);
7
+ background-image: var(--zx-overlay-panel-image);
8
+ background-clip: padding-box;
7
9
  -webkit-backdrop-filter: var(--zx-glass-filter-panel);
8
10
  backdrop-filter: var(--zx-glass-filter-panel);
9
- box-shadow: var(--zx-glass-raised-shadow);
11
+ box-shadow: var(--zx-glass-panel-shadow);
10
12
  color: var(--zx-color-text);
11
13
  padding: 0;
12
14
  }
@@ -22,7 +24,7 @@
22
24
  }
23
25
 
24
26
  .zx-launcher::backdrop {
25
- background: var(--zx-color-bg-backdrop);
27
+ background: var(--zx-color-overlay-backdrop);
26
28
  }
27
29
 
28
30
  .zx-launcher__surface {
@@ -36,7 +38,7 @@
36
38
  grid-template-columns: auto minmax(0, 1fr) auto auto;
37
39
  align-items: center;
38
40
  gap: var(--zx-space-3);
39
- border-block-end: 1px solid var(--zx-color-border);
41
+ border-block-end: 1px solid var(--zx-color-overlay-divider);
40
42
  padding: var(--zx-space-3) var(--zx-space-4);
41
43
  }
42
44
 
@@ -129,11 +131,11 @@
129
131
 
130
132
  .zx-launcher__option[aria-selected="true"],
131
133
  .zx-launcher__option:hover {
132
- background: var(--zx-color-bg-hover);
134
+ background: var(--zx-color-overlay-hover);
133
135
  }
134
136
 
135
137
  .zx-launcher__option[aria-current="page"] {
136
- background: var(--zx-color-bg-selected);
138
+ background: var(--zx-color-overlay-selected);
137
139
  color: var(--zx-color-accent);
138
140
  }
139
141
 
@@ -148,7 +150,7 @@
148
150
  }
149
151
 
150
152
  .zx-launcher__option[data-kind="application"][aria-current="page"] {
151
- background: var(--zx-color-bg-selected);
153
+ background: var(--zx-color-overlay-selected);
152
154
  }
153
155
 
154
156
  .zx-launcher__option[data-kind="application"] .zx-launcher__icon {
@@ -187,7 +189,7 @@
187
189
  justify-content: end;
188
190
  gap: var(--zx-space-4);
189
191
  padding: var(--zx-space-2) var(--zx-space-4);
190
- border-block-start: 1px solid var(--zx-color-border);
192
+ border-block-start: 1px solid var(--zx-color-overlay-divider);
191
193
  color: var(--zx-color-text-muted);
192
194
  font-size: var(--zx-text-xs);
193
195
  }
@@ -1,6 +1,7 @@
1
1
  import { Component } from '../../core/component.js';
2
2
  import { h, safeHref } from '../../core/dom.js';
3
3
  import { icon } from '../../core/icons.js';
4
+ import { overlayHost } from '../../core/overlay-host.js';
4
5
  import { uid } from '../../core/util.js';
5
6
 
6
7
  /**
@@ -49,6 +50,7 @@ import { uid } from '../../core/util.js';
49
50
  * @property {string} [loadingText='Searching…'] Loading-state text.
50
51
  * @property {'mod+k'|false} [shortcut='mod+k'] Optional global keyboard shortcut.
51
52
  * @property {false|{move?: string, open?: string, close?: string}} [hints] Visible keyboard hints, or false.
53
+ * @property {Element|string|null} [scope=null] Element whose nearest Zx theme scope owns an internally created launcher; defaults to the opener.
52
54
  * @property {(event: CustomEvent<LauncherSelectDetail>) => void} [onselect] Preventable selection listener.
53
55
  * @property {(event: CustomEvent<{query: string}>) => void} [onquery] Query-change listener.
54
56
  * @property {(event: CustomEvent<Record<string, never>>) => void} [onopen] Open listener.
@@ -85,7 +87,8 @@ export class Launcher extends Component {
85
87
  emptyText: 'No results',
86
88
  loadingText: 'Searching…',
87
89
  shortcut: 'mod+k',
88
- hints: { move: 'Move', open: 'Open', close: 'Close' }
90
+ hints: { move: 'Move', open: 'Open', close: 'Close' },
91
+ scope: null
89
92
  };
90
93
 
91
94
  /**
@@ -172,7 +175,7 @@ export class Launcher extends Component {
172
175
  results,
173
176
  footer));
174
177
  dialog.dataset.state = 'closed';
175
- if (this._createdRoot) document.body.append(dialog);
178
+ if (this._createdRoot) overlayHost(this.options.scope).append(dialog);
176
179
 
177
180
  this.listen(input, 'input', () => {
178
181
  this._query = /** @type {HTMLInputElement} */ (input).value;
@@ -212,6 +215,10 @@ export class Launcher extends Component {
212
215
  /** Opens the launcher and focuses/selects its query. @returns {this} @fires Launcher#open */
213
216
  open() {
214
217
  if (this.isOpen() || this._destroyed) return this;
218
+ if (this._createdRoot) {
219
+ const host = overlayHost(this.options.scope);
220
+ if (this.el.parentElement !== host) host.append(this.el);
221
+ }
215
222
  this.el.showModal();
216
223
  this.el.dataset.state = 'open';
217
224
  this.refs.input.setAttribute('aria-expanded', 'true');
@@ -24,7 +24,7 @@
24
24
 
25
25
  .zx-menu-button__item:hover,
26
26
  .zx-menu-button__item:focus-visible {
27
- background: var(--zx-color-bg-hover);
27
+ background: var(--zx-color-overlay-hover);
28
28
  }
29
29
 
30
30
  .zx-menu-button__item[data-danger="true"] {
@@ -45,5 +45,5 @@
45
45
  .zx-menu-button__separator {
46
46
  block-size: 1px;
47
47
  margin: var(--zx-space-1) var(--zx-space-2);
48
- background: var(--zx-color-border);
48
+ background: var(--zx-color-overlay-divider);
49
49
  }
@@ -55,24 +55,15 @@
55
55
  background: var(--zx-color-danger-bg);
56
56
  }
57
57
 
58
- @supports (backdrop-filter: blur(1px)) {
58
+ @supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
59
59
  .zx-message__toast {
60
- background: color-mix(in srgb, var(--zx-color-info-bg) 34%, var(--zx-color-overlay-surface));
60
+ border-color: color-mix(in srgb, var(--zx-message-kind) 28%, var(--zx-color-overlay-border));
61
+ background-color: var(--zx-color-overlay-surface);
62
+ background-image: var(--zx-overlay-toast-image);
63
+ background-clip: padding-box;
61
64
  -webkit-backdrop-filter: var(--zx-glass-filter-overlay);
62
65
  backdrop-filter: var(--zx-glass-filter-overlay);
63
- box-shadow: inset 0 0 0 1px var(--zx-message-kind), var(--zx-glass-raised-shadow);
64
- }
65
-
66
- .zx-message__toast[data-kind="success"] {
67
- background: color-mix(in srgb, var(--zx-color-success-bg) 34%, var(--zx-color-overlay-surface));
68
- }
69
-
70
- .zx-message__toast[data-kind="warning"] {
71
- background: color-mix(in srgb, var(--zx-color-warning-bg) 34%, var(--zx-color-overlay-surface));
72
- }
73
-
74
- .zx-message__toast[data-kind="error"] {
75
- background: color-mix(in srgb, var(--zx-color-danger-bg) 34%, var(--zx-color-overlay-surface));
66
+ box-shadow: var(--zx-glass-overlay-shadow);
76
67
  }
77
68
  }
78
69
 
@@ -111,7 +102,7 @@
111
102
  block-size: 4px;
112
103
  margin-block-start: var(--zx-space-2);
113
104
  border-radius: var(--zx-radius-full);
114
- background: var(--zx-color-border);
105
+ background: var(--zx-color-overlay-divider);
115
106
  }
116
107
 
117
108
  .zx-message__progress-fill {
@@ -1,6 +1,7 @@
1
1
  import { Component } from '../../core/component.js';
2
2
  import { h } from '../../core/dom.js';
3
3
  import { icon } from '../../core/icons.js';
4
+ import { overlayHost } from '../../core/overlay-host.js';
4
5
 
5
6
  /** @typedef {'info'|'success'|'warning'|'error'} MessageKind */
6
7
 
@@ -9,6 +10,7 @@ import { icon } from '../../core/icons.js';
9
10
  * @property {MessageKind|'danger'} [kind='info'] Message intent (`danger` aliases `error`).
10
11
  * @property {number} [timeout=4000] Auto-dismiss delay in milliseconds; zero persists.
11
12
  * @property {boolean} [closable=true] Whether to render a close button.
13
+ * @property {Element|string|null} [scope=null] Element whose nearest Zx theme scope owns a floating toast.
12
14
  */
13
15
 
14
16
  /**
@@ -133,8 +135,8 @@ export class Message extends Component {
133
135
  maxVisible: 5
134
136
  };
135
137
 
136
- /** @type {Message|null} */
137
- static #floating = null;
138
+ /** @type {WeakMap<Element, Message>} */
139
+ static #floating = new WeakMap();
138
140
 
139
141
  /** @returns {HTMLElement} */
140
142
  render() {
@@ -217,16 +219,17 @@ export class Message extends Component {
217
219
  * @returns {MessageHandle}
218
220
  */
219
221
  static show(msg, options = {}) {
220
- return this.#floatingRegion().show(msg, options);
222
+ return this.#floatingRegion(options.scope).show(msg, options);
221
223
  }
222
224
 
223
225
  /**
224
226
  * Shows a floating progress status.
225
227
  * @param {string} text Initial status text.
228
+ * @param {{scope?: Element|string|null}} [options={}] Floating-region scope.
226
229
  * @returns {ProgressHandle}
227
230
  */
228
- static progress(text) {
229
- return this.#floatingRegion()._progress(text);
231
+ static progress(text, options = {}) {
232
+ return this.#floatingRegion(options.scope)._progress(text);
230
233
  }
231
234
 
232
235
  /** @returns {void} */
@@ -414,19 +417,22 @@ export class Message extends Component {
414
417
  this._queue.remove(entry);
415
418
  }
416
419
 
417
- /** @returns {Message} */
418
- static #floatingRegion() {
419
- if (this.#floating) return this.#floating;
420
+ /** @param {Element|string|null|undefined} scope @returns {Message} */
421
+ static #floatingRegion(scope) {
422
+ const host = overlayHost(scope ?? null);
423
+ const existing = this.#floating.get(host);
424
+ if (existing) return existing;
420
425
  const region = h('div', {
421
426
  class: 'zx-message-region',
422
427
  popover: 'manual',
423
428
  role: 'status',
424
429
  ariaLive: 'polite'
425
430
  });
426
- document.body.append(region);
427
- this.#floating = new Message(region);
431
+ host.append(region);
432
+ const floating = new Message(region);
433
+ this.#floating.set(host, floating);
428
434
  if (typeof region.showPopover === 'function') region.showPopover();
429
- return this.#floating;
435
+ return floating;
430
436
  }
431
437
  }
432
438
 
@@ -4,10 +4,12 @@
4
4
  overflow: auto;
5
5
  border: 1px solid var(--zx-color-overlay-border);
6
6
  border-radius: var(--zx-radius-lg);
7
- background: var(--zx-color-overlay-panel);
7
+ background-color: var(--zx-color-overlay-panel);
8
+ background-image: var(--zx-overlay-panel-image);
9
+ background-clip: padding-box;
8
10
  -webkit-backdrop-filter: var(--zx-glass-filter-panel);
9
11
  backdrop-filter: var(--zx-glass-filter-panel);
10
- box-shadow: var(--zx-glass-raised-shadow);
12
+ box-shadow: var(--zx-glass-panel-shadow);
11
13
  color: var(--zx-color-text);
12
14
  padding: 0;
13
15
  }
@@ -23,7 +25,7 @@
23
25
  }
24
26
 
25
27
  .zx-modal::backdrop {
26
- background: var(--zx-color-bg-backdrop);
28
+ background: var(--zx-color-overlay-backdrop);
27
29
  }
28
30
 
29
31
  .zx-modal__content {
@@ -34,7 +36,7 @@
34
36
  * `:modal` rather than `[open]`: only a dialog opened with `showModal()` blocks the page, and a
35
37
  * non-modal one (Sheet's `modal: false`) must leave the page scrollable behind it.
36
38
  */
37
- body:has(> .zx-modal:modal) {
39
+ body:has(.zx-modal:modal) {
38
40
  overflow: hidden;
39
41
  overscroll-behavior: none;
40
42
  }