elements-kit 0.20.0 → 0.21.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 (48) hide show
  1. package/dist/await.d.mts +1 -1
  2. package/dist/{custom-elements-DK5VUcWA.d.mts → custom-elements-BGcU6ldL.d.mts} +2 -0
  3. package/dist/custom-elements.d.mts +1 -1
  4. package/dist/for.d.mts +1 -1
  5. package/dist/integrations/react.d.mts +1 -1
  6. package/dist/jsx-runtime/index.d.mts +1 -1
  7. package/dist/signals/index.d.mts +1 -1
  8. package/dist/{types-ncUK9rki.d.mts → types-CUvFIgPa.d.mts} +7 -7
  9. package/dist/ui/badge/badge.css +5 -5
  10. package/dist/ui/button/button.css +15 -15
  11. package/dist/ui/card/card.css +2 -2
  12. package/dist/ui/group/group.css +61 -0
  13. package/dist/ui/otp-input/index.d.mts +132 -0
  14. package/dist/ui/otp-input/index.mjs +328 -0
  15. package/dist/ui/otp-input/otp-input.css +157 -0
  16. package/dist/ui/overlay/index.css +52 -4
  17. package/dist/ui/overlay/index.d.mts +237 -63
  18. package/dist/ui/overlay/index.mjs +658 -66
  19. package/dist/ui/overlay/overlay.css +58 -2
  20. package/dist/ui/styles/index.css +662 -0
  21. package/dist/ui/styles/radius.css +7 -7
  22. package/dist/utilities/active-element.d.mts +1 -1
  23. package/dist/utilities/async.d.mts +1 -1
  24. package/dist/utilities/debounced.d.mts +1 -1
  25. package/dist/utilities/dom-lifecycle.bench.mjs +2 -2
  26. package/dist/utilities/element-rect.d.mts +1 -1
  27. package/dist/utilities/element-scroll.d.mts +1 -1
  28. package/dist/utilities/event-driven.d.mts +1 -1
  29. package/dist/utilities/event-listener.d.mts +1 -1
  30. package/dist/utilities/focus-within.d.mts +1 -1
  31. package/dist/utilities/hover.d.mts +1 -1
  32. package/dist/utilities/interval.d.mts +1 -1
  33. package/dist/utilities/location.d.mts +1 -1
  34. package/dist/utilities/media-devices.d.mts +1 -1
  35. package/dist/utilities/media-player.d.mts +1 -1
  36. package/dist/utilities/media-query.d.mts +1 -1
  37. package/dist/utilities/network.d.mts +1 -1
  38. package/dist/utilities/orientation.d.mts +1 -1
  39. package/dist/utilities/previous.d.mts +1 -1
  40. package/dist/utilities/promise.d.mts +1 -1
  41. package/dist/utilities/routing.d.mts +1 -1
  42. package/dist/utilities/search-params.d.mts +1 -1
  43. package/dist/utilities/storage.d.mts +1 -1
  44. package/dist/utilities/throttled.d.mts +1 -1
  45. package/dist/utilities/timeout.d.mts +1 -1
  46. package/dist/utilities/window-focus.d.mts +1 -1
  47. package/dist/utilities/window-size.d.mts +1 -1
  48. package/package.json +8 -1
@@ -0,0 +1,328 @@
1
+ import { ATTRIBUTES, observedAttributes } from "../../attributes.mjs";
2
+ import { l as computed, u as effect, x as signal } from "../../lib-DYypKhxk.mjs";
3
+ import "../../signals/index.mjs";
4
+ import { on } from "../../utilities/event-listener.mjs";
5
+ import { render } from "../../render.mjs";
6
+ import { isBrowser } from "../../utilities/environment.mjs";
7
+ //#region src/ui/otp-input/otp-input.shadow.css?inline
8
+ var otp_input_shadow_default = ".x-otp-input__field {\n color: #0000;\n caret-color: #0000;\n width: 100%;\n height: 100%;\n font: inherit;\n letter-spacing: inherit;\n pointer-events: auto;\n background: none;\n border: 0;\n outline: none;\n margin: 0;\n padding: 0;\n position: absolute;\n inset: 0;\n}\n\n.x-otp-input__field::selection {\n color: #0000;\n background-color: #0000;\n}\n";
9
+ //#endregion
10
+ //#region src/ui/otp-input/otp-input.ts
11
+ function toggleAttr(el, name, on) {
12
+ if (on) el.setAttribute(name, "");
13
+ else el.removeAttribute(name);
14
+ }
15
+ const SHADOW_SHEET = typeof CSSStyleSheet === "undefined" ? null : new CSSStyleSheet();
16
+ SHADOW_SHEET?.replaceSync(otp_input_shadow_default);
17
+ /**
18
+ * `<x-otp-input>` — segmented one-time-passcode / pin input.
19
+ *
20
+ * A single transparent `<input>` (rendered into the shadow root) overlays the
21
+ * author-composed cells and owns all keyboard / pointer / paste / mobile
22
+ * autofill behavior. The cells (`<x-otp-slot>`, projected through a `<slot>`)
23
+ * are a purely visual layer whose active cell + fake caret are mirrored from
24
+ * the input's selection. Because it is one field there is no roving tabindex
25
+ * and a screen reader perceives a single textbox.
26
+ *
27
+ * Form-associated: submits under the host `name`, participates in
28
+ * reset / restore / disabled, and reports `valueMissing` / `tooShort`.
29
+ *
30
+ * ```html
31
+ * <x-otp-input name="code" maxlength="6" pattern="[0-9]" aria-label="Code">
32
+ * <x-otp-group>
33
+ * <x-otp-slot index="0"></x-otp-slot>
34
+ * <x-otp-slot index="1"></x-otp-slot>
35
+ * <x-otp-slot index="2"></x-otp-slot>
36
+ * </x-otp-group>
37
+ * <x-otp-separator></x-otp-separator>
38
+ * <x-otp-group>
39
+ * <x-otp-slot index="3"></x-otp-slot>
40
+ * <x-otp-slot index="4"></x-otp-slot>
41
+ * <x-otp-slot index="5"></x-otp-slot>
42
+ * </x-otp-group>
43
+ * </x-otp-input>
44
+ * ```
45
+ */
46
+ var XOtpInput = class XOtpInput extends HTMLElement {
47
+ static formAssociated = true;
48
+ static [ATTRIBUTES] = {
49
+ value(value) {
50
+ this.value = value ?? "";
51
+ },
52
+ maxlength(value) {
53
+ this.maxLength = value == null ? 6 : Math.max(1, Number(value) || 6);
54
+ },
55
+ disabled(value) {
56
+ this.disabled = value != null;
57
+ },
58
+ required(value) {
59
+ this.required = value != null;
60
+ },
61
+ pattern(value) {
62
+ this.pattern = value;
63
+ },
64
+ inputmode(value) {
65
+ this.#inputMode(value ?? "text");
66
+ }
67
+ };
68
+ static observedAttributes = observedAttributes(XOtpInput);
69
+ attributeChangedCallback(name, oldValue, newValue) {
70
+ XOtpInput[ATTRIBUTES][name]?.call(this, newValue, oldValue);
71
+ }
72
+ #value = signal("");
73
+ get value() {
74
+ return this.#value();
75
+ }
76
+ set value(v) {
77
+ this.#value(v ?? "");
78
+ }
79
+ #maxLength = signal(6);
80
+ get maxLength() {
81
+ return this.#maxLength();
82
+ }
83
+ set maxLength(v) {
84
+ this.#maxLength(v);
85
+ }
86
+ #disabled = signal(false);
87
+ get disabled() {
88
+ return this.#disabled();
89
+ }
90
+ set disabled(v) {
91
+ this.#disabled(v);
92
+ }
93
+ #required = signal(false);
94
+ get required() {
95
+ return this.#required();
96
+ }
97
+ set required(v) {
98
+ this.#required(v);
99
+ }
100
+ #pattern = signal(null);
101
+ get pattern() {
102
+ return this.#pattern();
103
+ }
104
+ set pattern(v) {
105
+ this.#pattern(v);
106
+ }
107
+ get name() {
108
+ return this.getAttribute("name") ?? "";
109
+ }
110
+ set name(v) {
111
+ if (v == null || v === "") this.removeAttribute("name");
112
+ else this.setAttribute("name", v);
113
+ }
114
+ /** Optional transform applied to pasted text before validation. */
115
+ pasteTransformer = null;
116
+ #internals = typeof this.attachInternals === "function" ? this.attachInternals() : null;
117
+ #shadow = this.attachShadow({ mode: "open" });
118
+ #input = null;
119
+ #unmount = null;
120
+ #focused = signal(false);
121
+ #selStart = signal(null);
122
+ #selEnd = signal(null);
123
+ #inputMode = signal("text");
124
+ #prevValue = "";
125
+ #regex = computed(() => {
126
+ const p = this.pattern;
127
+ if (!p) return null;
128
+ try {
129
+ return new RegExp(p, "u");
130
+ } catch {
131
+ return null;
132
+ }
133
+ });
134
+ connectedCallback() {
135
+ if (SHADOW_SHEET) this.#shadow.adoptedStyleSheets = [SHADOW_SHEET];
136
+ if (!this.#shadow.querySelector("slot")) this.#shadow.append(document.createElement("slot"));
137
+ this.#unmount?.();
138
+ this.#unmount = render(this.#shadow, () => this.#renderField());
139
+ }
140
+ disconnectedCallback() {
141
+ this.#unmount?.();
142
+ this.#unmount = null;
143
+ this.#input = null;
144
+ }
145
+ formResetCallback() {
146
+ this.value = "";
147
+ }
148
+ formStateRestoreCallback(state) {
149
+ this.value = typeof state === "string" ? state : "";
150
+ }
151
+ formDisabledCallback(disabled) {
152
+ this.disabled = disabled;
153
+ }
154
+ get validity() {
155
+ return this.#internals?.validity ?? null;
156
+ }
157
+ /** Build the overlay input and wire all reactive state + listeners. */
158
+ #renderField() {
159
+ const input = document.createElement("input");
160
+ input.className = "x-otp-input__field";
161
+ input.type = "text";
162
+ input.autocomplete = "one-time-code";
163
+ input.setAttribute("autocapitalize", "none");
164
+ input.spellcheck = false;
165
+ this.#input = input;
166
+ effect(() => {
167
+ input.inputMode = this.#inputMode();
168
+ input.maxLength = this.maxLength;
169
+ input.disabled = this.disabled;
170
+ const label = this.getAttribute("aria-label");
171
+ if (label) input.setAttribute("aria-label", label);
172
+ });
173
+ effect(() => {
174
+ const v = this.value;
175
+ if (input.value !== v) input.value = v;
176
+ this.#prevValue = v;
177
+ this.#internals?.setFormValue(v);
178
+ this.#updateValidity();
179
+ });
180
+ effect(() => {
181
+ const value = this.value;
182
+ const focused = this.#focused();
183
+ const s = this.#selStart();
184
+ const e = this.#selEnd();
185
+ const max = this.maxLength;
186
+ const disabled = this.disabled;
187
+ for (const slot of this.querySelectorAll("x-otp-slot")) {
188
+ const i = Number(slot.getAttribute("index")) || 0;
189
+ const char = value[i];
190
+ slot.textContent = char ?? "";
191
+ const hasSel = focused && s !== null && e !== null;
192
+ const caretCell = hasSel && s === e && i === Math.min(s, max - 1);
193
+ const inRange = hasSel && s !== e && i >= s && i < e;
194
+ toggleAttr(slot, "data-active", caretCell);
195
+ toggleAttr(slot, "data-selected", inRange);
196
+ toggleAttr(slot, "data-caret", caretCell && char == null);
197
+ toggleAttr(slot, "data-disabled", disabled);
198
+ }
199
+ });
200
+ on(input, "input", () => this.#commit());
201
+ on(input, "keydown", (e) => this.#onKeydown(e));
202
+ on(input, "focus", () => this.#onFocus());
203
+ on(input, "blur", () => this.#focused(false));
204
+ on(input, "paste", (e) => this.#onPaste(e));
205
+ on(input, "selectionchange", () => this.#syncSelection());
206
+ on(input, "keyup", () => this.#syncSelection());
207
+ on(input, "pointerup", () => this.#syncSelection());
208
+ on(input, "select", () => this.#syncSelection());
209
+ if (this.hasAttribute("autofocus")) input.focus();
210
+ return input;
211
+ }
212
+ #charOk(ch) {
213
+ const r = this.#regex();
214
+ return !r || r.test(ch);
215
+ }
216
+ /** Filter to accepted characters and clamp to `maxLength`. */
217
+ #sanitize(raw) {
218
+ let out = "";
219
+ for (const ch of raw) {
220
+ if (out.length >= this.maxLength) break;
221
+ if (this.#charOk(ch)) out += ch;
222
+ }
223
+ return out;
224
+ }
225
+ #commit() {
226
+ const input = this.#input;
227
+ if (!input) return;
228
+ const next = this.#sanitize(input.value);
229
+ if (input.value !== next) input.value = next;
230
+ const prev = this.#prevValue;
231
+ if (next === prev) {
232
+ this.#syncSelection();
233
+ return;
234
+ }
235
+ this.value = next;
236
+ this.#syncSelection();
237
+ this.dispatchEvent(new Event("input", { bubbles: true }));
238
+ this.dispatchEvent(new Event("change", { bubbles: true }));
239
+ if (prev.length < this.maxLength && next.length === this.maxLength) this.dispatchEvent(new CustomEvent("complete", {
240
+ detail: next,
241
+ bubbles: true
242
+ }));
243
+ }
244
+ #onKeydown(e) {
245
+ if (e.metaKey || e.ctrlKey || e.altKey) return;
246
+ if (e.key.length === 1 && !this.#charOk(e.key)) e.preventDefault();
247
+ }
248
+ #onFocus() {
249
+ this.#focused(true);
250
+ const input = this.#input;
251
+ if (input) {
252
+ const end = input.value.length;
253
+ input.setSelectionRange(end, end);
254
+ }
255
+ this.#syncSelection();
256
+ }
257
+ #onPaste(e) {
258
+ e.preventDefault();
259
+ const input = this.#input;
260
+ if (!input) return;
261
+ const raw = e.clipboardData?.getData("text") ?? "";
262
+ const text = this.pasteTransformer ? this.pasteTransformer(raw) : raw;
263
+ const start = input.selectionStart ?? this.value.length;
264
+ const end = input.selectionEnd ?? start;
265
+ const merged = this.value.slice(0, start) + text + this.value.slice(end);
266
+ const next = this.#sanitize(merged);
267
+ input.value = next;
268
+ const caret = Math.min(next.length, this.maxLength);
269
+ input.setSelectionRange(caret, caret);
270
+ this.#commit();
271
+ }
272
+ #syncSelection() {
273
+ const input = this.#input;
274
+ if (!input) return;
275
+ this.#selStart(input.selectionStart);
276
+ this.#selEnd(input.selectionEnd);
277
+ }
278
+ #updateValidity() {
279
+ const internals = this.#internals;
280
+ if (!internals) return;
281
+ const v = this.value;
282
+ if (this.required && v.length === 0) internals.setValidity({ valueMissing: true }, "Please fill out this field.");
283
+ else if (v.length > 0 && v.length < this.maxLength) internals.setValidity({ tooShort: true }, `Please enter all ${this.maxLength} characters.`);
284
+ else internals.setValidity({});
285
+ }
286
+ };
287
+ if (isBrowser && !customElements.get("x-otp-input")) customElements.define("x-otp-input", XOtpInput);
288
+ //#endregion
289
+ //#region src/ui/otp-input/otp-slot.ts
290
+ /**
291
+ * `<x-otp-slot index="N">` — one visual cell of an `<x-otp-input>`. Purely
292
+ * presentational: `aria-hidden`, no focus, no value of its own. The parent
293
+ * `<x-otp-input>` paints its character and active / caret / disabled state by
294
+ * index; all styling lives in the companion CSS.
295
+ */
296
+ var XOtpSlot = class extends HTMLElement {
297
+ connectedCallback() {
298
+ this.setAttribute("aria-hidden", "true");
299
+ }
300
+ };
301
+ if (isBrowser && !customElements.get("x-otp-slot")) customElements.define("x-otp-slot", XOtpSlot);
302
+ //#endregion
303
+ //#region src/ui/otp-input/otp-group.ts
304
+ /**
305
+ * `<x-otp-group>` — presentational container that joins a run of
306
+ * `<x-otp-slot>`s into one attached group. All layout lives in CSS; this only
307
+ * applies a `group` role so the visual grouping is exposed to assistive tech.
308
+ */
309
+ var XOtpGroup = class extends HTMLElement {
310
+ connectedCallback() {
311
+ if (!this.hasAttribute("role")) this.setAttribute("role", "group");
312
+ }
313
+ };
314
+ if (isBrowser && !customElements.get("x-otp-group")) customElements.define("x-otp-group", XOtpGroup);
315
+ //#endregion
316
+ //#region src/ui/otp-input/otp-separator.ts
317
+ /**
318
+ * `<x-otp-separator>` — decorative divider authored between OTP groups (e.g. a
319
+ * dash between a 3-3 split). Purely visual: `aria-hidden`, glyph drawn in CSS.
320
+ */
321
+ var XOtpSeparator = class extends HTMLElement {
322
+ connectedCallback() {
323
+ this.setAttribute("aria-hidden", "true");
324
+ }
325
+ };
326
+ if (isBrowser && !customElements.get("x-otp-separator")) customElements.define("x-otp-separator", XOtpSeparator);
327
+ //#endregion
328
+ export { XOtpGroup, XOtpInput, XOtpSeparator, XOtpSlot };
@@ -0,0 +1,157 @@
1
+ x-otp-input {
2
+ align-items: center;
3
+ gap: var(--space-3);
4
+ vertical-align: top;
5
+ font-family: var(--default-font-family);
6
+ color: var(--neutral-12);
7
+ --otp-cell-height: var(--space-6);
8
+ --otp-cell-radius: var(--radius-2);
9
+ --otp-cell-font: var(--font-size-3);
10
+ --otp-border-width: 1px;
11
+ --otp-border-color: var(--neutral-a7);
12
+ --otp-focus-color: var(--focus-8);
13
+ --otp-surface: var(--color-surface);
14
+ display: inline-flex;
15
+ position: relative;
16
+ }
17
+
18
+ x-otp-input:where([data-size="1"]) {
19
+ --otp-cell-height: var(--space-5);
20
+ --otp-cell-radius: var(--radius-2);
21
+ --otp-cell-font: var(--font-size-2);
22
+ gap: var(--space-2);
23
+ }
24
+
25
+ x-otp-input:where([data-size="3"]) {
26
+ --otp-cell-height: var(--space-7);
27
+ --otp-cell-radius: var(--radius-3);
28
+ --otp-cell-font: var(--font-size-4);
29
+ }
30
+
31
+ x-otp-group {
32
+ isolation: isolate;
33
+ align-items: stretch;
34
+ display: inline-flex;
35
+ }
36
+
37
+ x-otp-slot {
38
+ z-index: 0;
39
+ box-sizing: border-box;
40
+ height: var(--otp-cell-height);
41
+ min-width: calc(var(--otp-cell-height) * .9);
42
+ padding-inline: calc(var(--otp-cell-height) * .28);
43
+ font-size: var(--otp-cell-font);
44
+ font-weight: var(--font-weight-medium);
45
+ font-variant-numeric: tabular-nums;
46
+ -webkit-user-select: none;
47
+ user-select: none;
48
+ background-color: var(--otp-surface);
49
+ border: var(--otp-border-width) solid var(--otp-border-color);
50
+ border-radius: var(--otp-cell-radius);
51
+ justify-content: center;
52
+ align-items: center;
53
+ line-height: 1;
54
+ display: inline-flex;
55
+ position: relative;
56
+ }
57
+
58
+ x-otp-group > x-otp-slot:not(:last-child) {
59
+ border-start-end-radius: 0;
60
+ border-end-end-radius: 0;
61
+ }
62
+
63
+ x-otp-group > x-otp-slot:not(:first-child) {
64
+ border-inline-start-width: 0;
65
+ border-start-start-radius: 0;
66
+ border-end-start-radius: 0;
67
+ }
68
+
69
+ x-otp-slot:where([data-active]) {
70
+ z-index: 1;
71
+ box-shadow: 0 0 0 2px var(--otp-focus-color);
72
+ border-width: 0;
73
+ }
74
+
75
+ x-otp-slot:where([data-selected]) {
76
+ z-index: 1;
77
+ background-color: var(--accent-a4);
78
+ }
79
+
80
+ x-otp-slot:where([data-disabled]) {
81
+ color: var(--neutral-a8);
82
+ background-color: var(--neutral-a2);
83
+ border-color: var(--neutral-a6);
84
+ }
85
+
86
+ x-otp-slot:where([data-caret]):after {
87
+ content: "";
88
+ background-color: var(--neutral-12);
89
+ width: 1px;
90
+ height: 1.1em;
91
+ animation: 1s step-start infinite x-otp-caret-blink;
92
+ position: absolute;
93
+ top: 50%;
94
+ left: 50%;
95
+ transform: translate(-50%, -50%);
96
+ }
97
+
98
+ @keyframes x-otp-caret-blink {
99
+ 0%, 49% {
100
+ opacity: 1;
101
+ }
102
+
103
+ 50%, 100% {
104
+ opacity: 0;
105
+ }
106
+ }
107
+
108
+ x-otp-separator {
109
+ color: var(--neutral-a8);
110
+ justify-content: center;
111
+ align-items: center;
112
+ display: inline-flex;
113
+ }
114
+
115
+ x-otp-separator:where(:empty):before {
116
+ content: "";
117
+ width: var(--space-3);
118
+ border-radius: var(--radius-thumb);
119
+ background-color: currentColor;
120
+ height: 2px;
121
+ }
122
+
123
+ x-otp-input:where([data-variant="soft"]) {
124
+ --otp-border-width: 0px;
125
+ --otp-focus-color: var(--accent-8);
126
+ }
127
+
128
+ x-otp-input:where([data-variant="soft"]) x-otp-slot {
129
+ background-color: var(--accent-a3);
130
+ color: var(--accent-12);
131
+ }
132
+
133
+ @media (prefers-reduced-motion: reduce) {
134
+ x-otp-slot:where([data-caret]):after {
135
+ animation: none;
136
+ }
137
+ }
138
+
139
+ @media (forced-colors: active) {
140
+ x-otp-slot {
141
+ border-color: buttontext;
142
+ }
143
+
144
+ x-otp-slot:where([data-active]) {
145
+ outline-offset: -2px;
146
+ outline: 2px solid highlight;
147
+ }
148
+
149
+ x-otp-slot:where([data-selected]) {
150
+ color: highlighttext;
151
+ background-color: highlight;
152
+ }
153
+
154
+ x-otp-slot:where([data-caret]):after {
155
+ background-color: canvastext;
156
+ }
157
+ }
@@ -19,14 +19,56 @@
19
19
  .x-overlay {
20
20
  top: var(--_ct);
21
21
  left: var(--_cl);
22
- translate: calc(-50% + var(--_ex, 0px) + var(--overlay-dx, 0px) +
22
+ translate: calc(-50% + var(--overlay-dx, 0px) +
23
23
  clamp(50%,
24
24
  var(--overlay-x, calc(var(--_cw) / 2)),
25
25
  calc(-50% + var(--_cw))))
26
- calc(-50% + var(--_ey, 0px) + var(--overlay-dy, 0px) +
26
+ calc(-50% + var(--overlay-dy, 0px) +
27
27
  clamp(50%,
28
28
  var(--overlay-y, calc(var(--_ch) / 2)),
29
29
  calc(-50% + var(--_ch))));
30
+ transform: translate(var(--_ex, 0px), var(--_ey, 0px));
31
+ bottom: auto;
32
+ right: auto;
33
+ }
34
+
35
+ @supports (anchor-name: --x) and (position-area: block-end) {
36
+ .x-overlay[data-anchor="element"] {
37
+ translate: var(--overlay-dx, 0px) var(--overlay-dy, 0px);
38
+ position-area: var(--overlay-area, block-end);
39
+ position-try-fallbacks: flip-block, flip-inline;
40
+ position-visibility: anchors-visible;
41
+ margin: var(--overlay-gap, var(--space-2));
42
+ top: auto;
43
+ left: auto;
44
+ }
45
+ }
46
+
47
+ .x-overlay-anchor {
48
+ pointer-events: none;
49
+ width: 0;
50
+ height: 0;
51
+ transition: top var(--overlay-duration, .3s)
52
+ var(--overlay-easing, cubic-bezier(.32, .72, 0, 1)),
53
+ left var(--overlay-duration, .3s)
54
+ var(--overlay-easing, cubic-bezier(.32, .72, 0, 1)),
55
+ width var(--overlay-duration, .3s)
56
+ var(--overlay-easing, cubic-bezier(.32, .72, 0, 1)),
57
+ height var(--overlay-duration, .3s)
58
+ var(--overlay-easing, cubic-bezier(.32, .72, 0, 1));
59
+ position: fixed;
60
+ }
61
+
62
+ @supports (anchor-name: --x) and (position-area: block-end) {
63
+ .x-overlay-anchor[data-follow] {
64
+ top: anchor(top);
65
+ left: anchor(left);
66
+ width: anchor-size(width);
67
+ height: anchor-size(height);
68
+ }
69
+ }
70
+
71
+ .x-overlay {
30
72
  --overlay-width: 480px;
31
73
  width: var(--overlay-w, var(--overlay-width));
32
74
  max-width: var(--_cw);
@@ -37,9 +79,10 @@
37
79
  transition: top var(--overlay-duration) var(--overlay-easing),
38
80
  left var(--overlay-duration) var(--overlay-easing),
39
81
  translate var(--overlay-duration) var(--overlay-easing),
82
+ transform var(--overlay-duration) var(--overlay-easing),
40
83
  width var(--overlay-duration) var(--overlay-easing),
41
84
  height var(--overlay-duration) var(--overlay-easing);
42
- grid-template-rows: minmax(0, 1fr);
85
+ flex-direction: column;
43
86
  }
44
87
 
45
88
  .x-overlay:not([open], :popover-open) {
@@ -51,7 +94,12 @@
51
94
  }
52
95
 
53
96
  .x-overlay:where([open], :popover-open) {
54
- display: grid;
97
+ display: flex;
98
+ }
99
+
100
+ .x-overlay > .x-card {
101
+ flex: auto;
102
+ min-height: 0;
55
103
  }
56
104
 
57
105
  @supports not (translate: 0px) {