@xmesh/system-design 0.0.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 (175) hide show
  1. package/README.md +472 -0
  2. package/assets/brand-lockup-dark.svg +9 -0
  3. package/assets/brand-lockup-light.svg +9 -0
  4. package/assets/brand-mark.svg +9 -0
  5. package/colors_and_type.css +11 -0
  6. package/dist/lit/components/alert/index.css +201 -0
  7. package/dist/lit/components/alert/index.d.ts +25 -0
  8. package/dist/lit/components/alert/index.js +191 -0
  9. package/dist/lit/components/app-bar/index.css +80 -0
  10. package/dist/lit/components/app-bar/index.d.ts +19 -0
  11. package/dist/lit/components/app-bar/index.js +120 -0
  12. package/dist/lit/components/artifact/index.css +166 -0
  13. package/dist/lit/components/artifact/index.d.ts +37 -0
  14. package/dist/lit/components/artifact/index.js +294 -0
  15. package/dist/lit/components/autocomplete/index.css +171 -0
  16. package/dist/lit/components/autocomplete/index.d.ts +47 -0
  17. package/dist/lit/components/autocomplete/index.js +404 -0
  18. package/dist/lit/components/avatar/index.css +62 -0
  19. package/dist/lit/components/avatar/index.d.ts +19 -0
  20. package/dist/lit/components/avatar/index.js +112 -0
  21. package/dist/lit/components/avatar-group/index.css +60 -0
  22. package/dist/lit/components/avatar-group/index.d.ts +19 -0
  23. package/dist/lit/components/avatar-group/index.js +97 -0
  24. package/dist/lit/components/badge/index.css +72 -0
  25. package/dist/lit/components/badge/index.d.ts +18 -0
  26. package/dist/lit/components/badge/index.js +115 -0
  27. package/dist/lit/components/brand-mark/index.css +109 -0
  28. package/dist/lit/components/brand-mark/index.d.ts +24 -0
  29. package/dist/lit/components/brand-mark/index.js +116 -0
  30. package/dist/lit/components/breadcrumbs/index.css +91 -0
  31. package/dist/lit/components/breadcrumbs/index.d.ts +19 -0
  32. package/dist/lit/components/breadcrumbs/index.js +104 -0
  33. package/dist/lit/components/bubble/index.css +182 -0
  34. package/dist/lit/components/bubble/index.d.ts +72 -0
  35. package/dist/lit/components/bubble/index.js +617 -0
  36. package/dist/lit/components/button/index.css +342 -0
  37. package/dist/lit/components/button/index.d.ts +32 -0
  38. package/dist/lit/components/button/index.js +202 -0
  39. package/dist/lit/components/card/index.css +99 -0
  40. package/dist/lit/components/card/index.d.ts +20 -0
  41. package/dist/lit/components/card/index.js +133 -0
  42. package/dist/lit/components/chat/index.css +292 -0
  43. package/dist/lit/components/chat/index.d.ts +74 -0
  44. package/dist/lit/components/chat/index.js +589 -0
  45. package/dist/lit/components/checkbox/index.css +126 -0
  46. package/dist/lit/components/checkbox/index.d.ts +21 -0
  47. package/dist/lit/components/checkbox/index.js +138 -0
  48. package/dist/lit/components/chip/index.css +145 -0
  49. package/dist/lit/components/chip/index.d.ts +30 -0
  50. package/dist/lit/components/chip/index.js +230 -0
  51. package/dist/lit/components/chip-group/index.css +19 -0
  52. package/dist/lit/components/chip-group/index.d.ts +24 -0
  53. package/dist/lit/components/chip-group/index.js +171 -0
  54. package/dist/lit/components/code/index.css +42 -0
  55. package/dist/lit/components/code/index.d.ts +12 -0
  56. package/dist/lit/components/code/index.js +68 -0
  57. package/dist/lit/components/composer/index.css +548 -0
  58. package/dist/lit/components/composer/index.d.ts +67 -0
  59. package/dist/lit/components/composer/index.js +713 -0
  60. package/dist/lit/components/data-table/index.css +166 -0
  61. package/dist/lit/components/data-table/index.d.ts +55 -0
  62. package/dist/lit/components/data-table/index.js +390 -0
  63. package/dist/lit/components/dialog/index.css +124 -0
  64. package/dist/lit/components/dialog/index.d.ts +24 -0
  65. package/dist/lit/components/dialog/index.js +199 -0
  66. package/dist/lit/components/divider/index.css +27 -0
  67. package/dist/lit/components/divider/index.d.ts +13 -0
  68. package/dist/lit/components/divider/index.js +67 -0
  69. package/dist/lit/components/empty-state/index.css +69 -0
  70. package/dist/lit/components/empty-state/index.d.ts +21 -0
  71. package/dist/lit/components/empty-state/index.js +123 -0
  72. package/dist/lit/components/expansion-panel/index.css +120 -0
  73. package/dist/lit/components/expansion-panel/index.d.ts +22 -0
  74. package/dist/lit/components/expansion-panel/index.js +174 -0
  75. package/dist/lit/components/field/index.css +223 -0
  76. package/dist/lit/components/field/index.d.ts +106 -0
  77. package/dist/lit/components/field/index.js +388 -0
  78. package/dist/lit/components/file-input/index.css +257 -0
  79. package/dist/lit/components/file-input/index.d.ts +30 -0
  80. package/dist/lit/components/file-input/index.js +298 -0
  81. package/dist/lit/components/form/index.css +29 -0
  82. package/dist/lit/components/form/index.d.ts +38 -0
  83. package/dist/lit/components/form/index.js +192 -0
  84. package/dist/lit/components/grid/index.css +53 -0
  85. package/dist/lit/components/grid/index.d.ts +14 -0
  86. package/dist/lit/components/grid/index.js +82 -0
  87. package/dist/lit/components/kbd/index.css +35 -0
  88. package/dist/lit/components/kbd/index.d.ts +11 -0
  89. package/dist/lit/components/kbd/index.js +43 -0
  90. package/dist/lit/components/list/index.css +15 -0
  91. package/dist/lit/components/list/index.d.ts +28 -0
  92. package/dist/lit/components/list/index.js +188 -0
  93. package/dist/lit/components/list-item/index.css +119 -0
  94. package/dist/lit/components/list-item/index.d.ts +20 -0
  95. package/dist/lit/components/list-item/index.js +127 -0
  96. package/dist/lit/components/menu/index.css +94 -0
  97. package/dist/lit/components/menu/index.d.ts +47 -0
  98. package/dist/lit/components/menu/index.js +386 -0
  99. package/dist/lit/components/navigation-drawer/index.css +114 -0
  100. package/dist/lit/components/navigation-drawer/index.d.ts +29 -0
  101. package/dist/lit/components/navigation-drawer/index.js +218 -0
  102. package/dist/lit/components/overlay/index.css +171 -0
  103. package/dist/lit/components/overlay/index.d.ts +65 -0
  104. package/dist/lit/components/overlay/index.js +566 -0
  105. package/dist/lit/components/pagination/index.css +102 -0
  106. package/dist/lit/components/pagination/index.d.ts +22 -0
  107. package/dist/lit/components/pagination/index.js +184 -0
  108. package/dist/lit/components/primitives/index.css +504 -0
  109. package/dist/lit/components/primitives/index.d.ts +25 -0
  110. package/dist/lit/components/primitives/index.js +283 -0
  111. package/dist/lit/components/progress/index.css +143 -0
  112. package/dist/lit/components/progress/index.d.ts +23 -0
  113. package/dist/lit/components/progress/index.js +180 -0
  114. package/dist/lit/components/radio-group/index.css +178 -0
  115. package/dist/lit/components/radio-group/index.d.ts +35 -0
  116. package/dist/lit/components/radio-group/index.js +292 -0
  117. package/dist/lit/components/select/index.css +151 -0
  118. package/dist/lit/components/select/index.d.ts +50 -0
  119. package/dist/lit/components/select/index.js +390 -0
  120. package/dist/lit/components/sidebar-item/index.css +133 -0
  121. package/dist/lit/components/sidebar-item/index.d.ts +20 -0
  122. package/dist/lit/components/sidebar-item/index.js +105 -0
  123. package/dist/lit/components/skeleton/index.css +81 -0
  124. package/dist/lit/components/skeleton/index.d.ts +19 -0
  125. package/dist/lit/components/skeleton/index.js +119 -0
  126. package/dist/lit/components/slider/index.css +171 -0
  127. package/dist/lit/components/slider/index.d.ts +36 -0
  128. package/dist/lit/components/slider/index.js +302 -0
  129. package/dist/lit/components/snackbar/index.css +279 -0
  130. package/dist/lit/components/snackbar/index.d.ts +33 -0
  131. package/dist/lit/components/snackbar/index.js +195 -0
  132. package/dist/lit/components/stack/index.css +41 -0
  133. package/dist/lit/components/stack/index.d.ts +20 -0
  134. package/dist/lit/components/stack/index.js +103 -0
  135. package/dist/lit/components/switch/index.css +126 -0
  136. package/dist/lit/components/switch/index.d.ts +17 -0
  137. package/dist/lit/components/switch/index.js +116 -0
  138. package/dist/lit/components/table/index.css +85 -0
  139. package/dist/lit/components/table/index.d.ts +25 -0
  140. package/dist/lit/components/table/index.js +139 -0
  141. package/dist/lit/components/tabs/index.css +116 -0
  142. package/dist/lit/components/tabs/index.d.ts +49 -0
  143. package/dist/lit/components/tabs/index.js +320 -0
  144. package/dist/lit/components/text-field/index.css +90 -0
  145. package/dist/lit/components/text-field/index.d.ts +17 -0
  146. package/dist/lit/components/text-field/index.js +101 -0
  147. package/dist/lit/components/textarea/index.css +55 -0
  148. package/dist/lit/components/textarea/index.d.ts +26 -0
  149. package/dist/lit/components/textarea/index.js +124 -0
  150. package/dist/lit/components/tooltip/index.css +37 -0
  151. package/dist/lit/components/tooltip/index.d.ts +31 -0
  152. package/dist/lit/components/tooltip/index.js +196 -0
  153. package/dist/lit/components/validation/index.css +386 -0
  154. package/dist/lit/components/validation/index.d.ts +45 -0
  155. package/dist/lit/components/validation/index.js +318 -0
  156. package/dist/lit/index.d.ts +50 -0
  157. package/dist/lit/index.js +59 -0
  158. package/package.json +81 -0
  159. package/styles/README.md +346 -0
  160. package/styles/_elevation.css +24 -0
  161. package/styles/_fonts.css +6 -0
  162. package/styles/_layout.css +37 -0
  163. package/styles/_primitives.css +154 -0
  164. package/styles/_scroll.css +75 -0
  165. package/styles/_semantic.css +146 -0
  166. package/styles/_space.css +61 -0
  167. package/styles/_type.css +139 -0
  168. package/styles/_xmesh-extensions.css +232 -0
  169. package/styles/index.css +44 -0
  170. package/styles/md3/_color.css +102 -0
  171. package/styles/md3/_elevation.css +26 -0
  172. package/styles/md3/_motion.css +35 -0
  173. package/styles/md3/_shape.css +22 -0
  174. package/styles/md3/_state.css +22 -0
  175. package/styles/md3/_type.css +111 -0
@@ -0,0 +1,218 @@
1
+ /*
2
+ navigation-drawer/index.ts — <xm-navigation-drawer>, the side navigation panel.
3
+
4
+ <xm-navigation-drawer> — an edge-anchored navigation container that hosts
5
+ xm-sidebar-item children. Belongs to the INVERSE-SURFACE family (the card
6
+ stack), carries the directional drawer shadow, and supports modal (focus-
7
+ trapped, scrim, inert background) and persistent / collapsed-rail modes.
8
+
9
+ Authoring:
10
+ <xm-navigation-drawer open modal>
11
+ <xm-sidebar-item title="Overview" active></xm-sidebar-item>
12
+ <xm-sidebar-item title="Activity"></xm-sidebar-item>
13
+ </xm-navigation-drawer>
14
+ el.addEventListener("close", () => (el.open = false));
15
+
16
+ Properties:
17
+ open boolean (reflected) — declarative open state
18
+ collapsed boolean (reflected) — icon-rail mode (persistent)
19
+ modal boolean (reflected) — trap focus + inert background + scrim
20
+ label string — accessible name for the drawer region
21
+
22
+ Events:
23
+ close bare component dismiss signal (bubbles + composed, no detail).
24
+
25
+ ── Overlay composition (AD-5 / AD-5a — the decision) ──────────────────────
26
+ xm-overlay's modal mode is a CENTERED card (margin:auto, max-width 560px); it
27
+ has no edge-placement mode, and AD-12 forbids restyling its shadow root. So
28
+ the drawer renders its OWN native <dialog> in EDGE layout and reuses the
29
+ IDENTICAL platform-native modal contract that xm-overlay itself codifies:
30
+ • showModal() → focus-trap + background `inert`, for free (AD-5)
31
+ • <dialog>::backdrop → the scrim
32
+ • opener capture + .focus() restore on close (NFR-14)
33
+ • innermost-only Esc: the native top layer routes Esc to the topmost modal;
34
+ we handle `cancel`, preventDefault, close, dispatch `close`, and
35
+ stopPropagation so one keypress never closes an enclosing layer (AD-5a)
36
+ • dialog tier: a modal <dialog> rides the native top layer above every
37
+ popover by spec — exactly what `dialog` being the highest tier means, so
38
+ no z-index is invented (AD-5a)
39
+ This is the same mechanism xm-overlay's own modal path uses — there is no
40
+ divergent focus-trap or positioning dependency (NFR-16). Persistent / rail
41
+ mode renders an <aside> with no showModal().
42
+
43
+ Shadow DOM; Lit from lit; sibling CSS via the built-
44
+ file-relative new URL(...). BEM block `nav-drawer` (differs from the element
45
+ name). Hosts light-DOM xm-sidebar-item children unchanged (AD-12); the panel
46
+ remaps the surface role tokens so those items read on inverse-surface.
47
+ */
48
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
49
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
50
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
51
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
52
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
53
+ };
54
+ import { LitElement, html, nothing } from "lit";
55
+ import { customElement, property, query } from "lit/decorators.js";
56
+ // Resolve CSS relative to the *built* file:
57
+ // lit/build/components/navigation-drawer/index.js → ../...
58
+ const NAV_DRAWER_CSS = new URL("../navigation-drawer/index.css", import.meta.url).href;
59
+ let XmNavigationDrawer = class XmNavigationDrawer extends LitElement {
60
+ constructor() {
61
+ super(...arguments);
62
+ this.open = false;
63
+ this.collapsed = false;
64
+ this.modal = false;
65
+ this.label = "Navigation";
66
+ this._restoreFocusTo = null;
67
+ this._wasOpen = false;
68
+ // Native <dialog> emits `cancel` on Esc. We own the close (and its focus
69
+ // restore + event) and stopPropagation so one Esc never closes an enclosing
70
+ // layer — innermost-only (AD-5a).
71
+ this._onDialogCancel = (event) => {
72
+ event.preventDefault();
73
+ event.stopPropagation();
74
+ this.open = false;
75
+ };
76
+ this._onDialogClose = () => {
77
+ // Reached if the dialog closed outside our API; sync state.
78
+ if (this._wasOpen) {
79
+ this._wasOpen = false;
80
+ this._restoreFocus();
81
+ this.open = false;
82
+ this._emitClose();
83
+ }
84
+ };
85
+ // Backdrop click closes the modal drawer.
86
+ this._onDialogClick = (event) => {
87
+ if (event.target === this._dialog) {
88
+ this.open = false;
89
+ }
90
+ };
91
+ }
92
+ static { this.shadowRootOptions = {
93
+ ...LitElement.shadowRootOptions,
94
+ delegatesFocus: true,
95
+ }; }
96
+ render() {
97
+ const cls = [
98
+ "nav-drawer",
99
+ this.open ? "nav-drawer--open" : "",
100
+ this.collapsed ? "nav-drawer--collapsed" : "",
101
+ this.modal ? "nav-drawer--modal" : "nav-drawer--persistent",
102
+ ]
103
+ .filter(Boolean)
104
+ .join(" ");
105
+ const panel = html `
106
+ <nav class="nav-drawer__panel" aria-label=${this.label}>
107
+ <slot></slot>
108
+ </nav>
109
+ `;
110
+ return html `
111
+ <link rel="stylesheet" href="${NAV_DRAWER_CSS}" />
112
+ <style>
113
+ :host {
114
+ display: contents;
115
+ }
116
+ :host([hidden]) {
117
+ display: none;
118
+ }
119
+ </style>
120
+ ${this.modal
121
+ ? html `
122
+ <dialog
123
+ class="${cls} nav-drawer__dialog"
124
+ aria-label=${this.label}
125
+ @cancel=${this._onDialogCancel}
126
+ @close=${this._onDialogClose}
127
+ @click=${this._onDialogClick}
128
+ >
129
+ ${panel}
130
+ </dialog>
131
+ `
132
+ : html `
133
+ <aside class="${cls}" aria-hidden=${this.open ? nothing : "true"}>
134
+ ${panel}
135
+ </aside>
136
+ `}
137
+ `;
138
+ }
139
+ updated(changed) {
140
+ if (changed.has("open") || changed.has("modal")) {
141
+ if (this.modal) {
142
+ if (this.open && !this._wasOpen)
143
+ this._activateModal();
144
+ else if (!this.open && this._wasOpen)
145
+ this._deactivateModal();
146
+ }
147
+ else if (!this.open && this._wasOpen) {
148
+ // Switched out of modal while open, or closed persistently.
149
+ this._wasOpen = false;
150
+ }
151
+ else if (this.open) {
152
+ this._wasOpen = true;
153
+ }
154
+ }
155
+ }
156
+ disconnectedCallback() {
157
+ super.disconnectedCallback();
158
+ if (this._wasOpen)
159
+ this._deactivateModal(false);
160
+ }
161
+ // ── Modal activation (native <dialog> contract) ─────────────────────
162
+ _activateModal() {
163
+ this._wasOpen = true;
164
+ this._restoreFocusTo = this._deepActiveElement();
165
+ this.updateComplete.then(() => {
166
+ const dlg = this._dialog;
167
+ if (dlg && !dlg.open && this.open && this.modal) {
168
+ dlg.showModal(); // focus-trap + background inert, for free (AD-5)
169
+ }
170
+ });
171
+ }
172
+ _deactivateModal(dispatch = true) {
173
+ this._wasOpen = false;
174
+ const dlg = this._dialog;
175
+ if (dlg?.open)
176
+ dlg.close();
177
+ this._restoreFocus();
178
+ if (dispatch)
179
+ this._emitClose();
180
+ }
181
+ // ── close event ─────────────────────────────────────────────────────
182
+ _emitClose() {
183
+ this.dispatchEvent(new CustomEvent("close", { bubbles: true, composed: true }));
184
+ }
185
+ // ── Focus restore (cross-shadow-root) ───────────────────────────────
186
+ _restoreFocus() {
187
+ const target = this._restoreFocusTo;
188
+ this._restoreFocusTo = null;
189
+ if (target && typeof target.focus === "function" && target.isConnected) {
190
+ target.focus();
191
+ }
192
+ }
193
+ _deepActiveElement() {
194
+ let active = document.activeElement;
195
+ while (active?.shadowRoot?.activeElement) {
196
+ active = active.shadowRoot.activeElement;
197
+ }
198
+ return active instanceof HTMLElement ? active : null;
199
+ }
200
+ };
201
+ __decorate([
202
+ property({ type: Boolean, reflect: true })
203
+ ], XmNavigationDrawer.prototype, "open", void 0);
204
+ __decorate([
205
+ property({ type: Boolean, reflect: true })
206
+ ], XmNavigationDrawer.prototype, "collapsed", void 0);
207
+ __decorate([
208
+ property({ type: Boolean, reflect: true })
209
+ ], XmNavigationDrawer.prototype, "modal", void 0);
210
+ __decorate([
211
+ property({ type: String })
212
+ ], XmNavigationDrawer.prototype, "label", void 0);
213
+ __decorate([
214
+ query(".nav-drawer__dialog")
215
+ ], XmNavigationDrawer.prototype, "_dialog", void 0);
216
+ XmNavigationDrawer = __decorate([
217
+ customElement("xm-navigation-drawer")
218
+ ], XmNavigationDrawer);
@@ -0,0 +1,171 @@
1
+ /* ============================================
2
+ <xm-overlay> — platform-native overlay foundation.
3
+
4
+ The single surface that xm-dialog / xm-menu / xm-tooltip / xm-select /
5
+ xm-autocomplete compose. Modal mode is a native <dialog> (its ::backdrop
6
+ is the scrim); non-modal mode is a Popover-API element in the top layer,
7
+ anchored with CSS anchor() / @position-try.
8
+
9
+ Surface is the card tier — inverse-surface fill + inverse-on-surface ink
10
+ (AD-13). Elevation level3 for popovers/menus, level4–5 for modals
11
+ (DESIGN.md). Scrim is var(--md-sys-color-scrim) with alpha at this use
12
+ site. No blur / backdrop-filter; no gradient.
13
+
14
+ BEM block: `overlay`. Registered in scripts/check-bem.sh STRICT_BLOCKS.
15
+ Modifiers: --modal / --non-modal · --tier-tooltip / --tier-menu /
16
+ --tier-dialog. Elements: __dialog (native <dialog>), __popover (popover
17
+ element), __surface (the card chrome), __body (content region).
18
+ ============================================ */
19
+
20
+ /* ---------- Native <dialog> (modal) ---------- */
21
+ /* The <dialog> element is the positioning + top-layer host; its visible card
22
+ is .overlay__surface inside it. The dialog box itself is transparent so the
23
+ surface owns the chrome. */
24
+ .overlay__dialog {
25
+ margin: auto;
26
+ padding: 0;
27
+ max-width: min(92vw, 560px);
28
+ max-height: 86vh;
29
+ border: none;
30
+ background: transparent;
31
+ color: var(--md-sys-color-inverse-on-surface);
32
+ overflow: visible;
33
+ }
34
+
35
+ /* The scrim — flat token-alpha fill (DESIGN.md: scrim is pure black, alpha
36
+ applied at the callsite). No blur. */
37
+ .overlay__dialog::backdrop {
38
+ background: color-mix(in oklab, var(--md-sys-color-scrim) 48%, transparent);
39
+ animation: overlay-fade var(--md-sys-motion-duration-short4)
40
+ var(--md-sys-motion-easing-standard);
41
+ }
42
+
43
+ /* ---------- Popover (non-modal, anchored) ---------- */
44
+ .overlay__popover {
45
+ margin: 0;
46
+ padding: 0;
47
+ border: none;
48
+ background: transparent;
49
+ color: var(--md-sys-color-inverse-on-surface);
50
+ overflow: visible;
51
+ /* Top-layer popover, positioned against the viewport. anchor() resolves
52
+ against the per-instance position-anchor set inline. @position-try flips
53
+ the popover when it would overflow the viewport. */
54
+ position: fixed;
55
+ position-try-fallbacks: flip-block, flip-inline;
56
+ }
57
+
58
+ /* Native anchor() path — used when the anchor element lives in the SAME tree
59
+ scope as the popover (anchor-name is tree-scoped). data-placement is set by
60
+ the element at open time; inset: auto resets so only the chosen edges bind. */
61
+ .overlay__popover[data-placement^="bottom"] {
62
+ inset: auto;
63
+ top: anchor(bottom);
64
+ left: anchor(left);
65
+ margin-block-start: var(--s-1);
66
+ }
67
+ .overlay__popover[data-placement^="top"] {
68
+ inset: auto;
69
+ bottom: anchor(top);
70
+ left: anchor(left);
71
+ margin-block-end: var(--s-1);
72
+ }
73
+ .overlay__popover[data-placement^="right"] {
74
+ inset: auto;
75
+ left: anchor(right);
76
+ top: anchor(top);
77
+ margin-inline-start: var(--s-1);
78
+ }
79
+ .overlay__popover[data-placement^="left"] {
80
+ inset: auto;
81
+ right: anchor(left);
82
+ top: anchor(top);
83
+ margin-inline-end: var(--s-1);
84
+ }
85
+
86
+ /* Cross-root fallback path — when the consumer's trigger is in ANOTHER shadow
87
+ root, CSS anchor-name is NOT visible to this popover (tree-scoped). The
88
+ element computes a fixed top/left from the trigger's bounding rect (no
89
+ dependency) and pins them via these custom properties; anchor() is skipped.
90
+ This is the built-in, dependency-free fallback — Floating UI (the heavier
91
+ escape hatch) is only for deep/virtual cases, never the common cross-root
92
+ one. */
93
+ .overlay__popover.overlay__popover--pinned {
94
+ inset: auto;
95
+ top: var(--xm-overlay-pin-top, 0);
96
+ left: var(--xm-overlay-pin-left, 0);
97
+ position-try-fallbacks: none;
98
+ }
99
+
100
+ /* ---------- The card surface (shared) ---------- */
101
+ .overlay__surface {
102
+ display: flex;
103
+ flex-direction: column;
104
+ min-width: 0;
105
+ box-sizing: border-box;
106
+ border: 1px solid var(--md-sys-color-outline-variant);
107
+ border-radius: var(--md-sys-shape-corner-large);
108
+ background: var(--md-sys-color-inverse-surface);
109
+ color: var(--md-sys-color-inverse-on-surface);
110
+ font:
111
+ var(--md-sys-typescale-body-large-weight)
112
+ var(--md-sys-typescale-body-large-size) /
113
+ var(--md-sys-typescale-body-large-line-height)
114
+ var(--md-sys-typescale-body-large-font);
115
+ }
116
+
117
+ /* Elevation per tier: dialogs lift highest (level5), menus/select-listbox
118
+ sit at popover height (level3), tooltips are the quietest layer (level3
119
+ too — they never cover a menu, but read as a raised chip). */
120
+ .overlay--modal .overlay__surface {
121
+ box-shadow: var(--md-sys-elevation-level5);
122
+ min-width: min(86vw, 320px);
123
+ }
124
+ .overlay--non-modal.overlay--tier-menu .overlay__surface {
125
+ box-shadow: var(--md-sys-elevation-level3);
126
+ min-width: 180px;
127
+ }
128
+ .overlay--non-modal.overlay--tier-dialog .overlay__surface {
129
+ box-shadow: var(--md-sys-elevation-level3);
130
+ }
131
+ .overlay--tier-tooltip .overlay__surface {
132
+ box-shadow: var(--md-sys-elevation-level3);
133
+ border-radius: var(--md-sys-shape-corner-small);
134
+ max-width: 260px;
135
+ }
136
+
137
+ /* ---------- Content region ---------- */
138
+ .overlay__body {
139
+ flex: 1;
140
+ min-width: 0;
141
+ min-height: 0;
142
+ overflow: auto;
143
+ padding: var(--s-5);
144
+ }
145
+ .overlay--tier-tooltip .overlay__body,
146
+ .overlay--tier-menu .overlay__body {
147
+ padding: var(--s-2);
148
+ }
149
+
150
+ /* ---------- Open transition (short4, standard) ---------- */
151
+ @keyframes overlay-fade {
152
+ from {
153
+ opacity: 0;
154
+ }
155
+ to {
156
+ opacity: 1;
157
+ }
158
+ }
159
+ .overlay__dialog[open],
160
+ .overlay__popover:popover-open {
161
+ animation: overlay-fade var(--md-sys-motion-duration-short4)
162
+ var(--md-sys-motion-easing-standard);
163
+ }
164
+
165
+ @media (prefers-reduced-motion: reduce) {
166
+ .overlay__dialog[open],
167
+ .overlay__popover:popover-open,
168
+ .overlay__dialog::backdrop {
169
+ animation: none;
170
+ }
171
+ }
@@ -0,0 +1,65 @@
1
+ import { LitElement } from "lit";
2
+ import type { PropertyValues, TemplateResult } from "lit";
3
+ export type OverlayTier = "tooltip" | "menu" | "dialog";
4
+ export type OverlayMode = "modal" | "non-modal";
5
+ export type OverlayPlacement = "top" | "bottom" | "left" | "right" | "top-start" | "top-end" | "bottom-start" | "bottom-end" | "left-start" | "left-end" | "right-start" | "right-end";
6
+ export type OverlayCloseReason = "escape" | "backdrop" | "api" | "ancestor-closed";
7
+ export interface OverlayCloseDetail {
8
+ tier: OverlayTier;
9
+ mode: OverlayMode;
10
+ reason: OverlayCloseReason;
11
+ }
12
+ export interface OverlayOpenDetail {
13
+ tier: OverlayTier;
14
+ mode: OverlayMode;
15
+ }
16
+ export declare class XmOverlay extends LitElement {
17
+ static shadowRootOptions: ShadowRootInit;
18
+ open: boolean;
19
+ mode: OverlayMode;
20
+ tier: OverlayTier;
21
+ placement: OverlayPlacement;
22
+ label: string;
23
+ /** Trigger element to anchor a non-modal overlay against (CSS anchor()).
24
+ A property, not an attribute — it is an element reference, possibly from
25
+ another shadow root. */
26
+ anchor: HTMLElement | null;
27
+ /** Element focus is restored to on close. Defaults to `anchor`, else the
28
+ document's deepest activeElement at open time. */
29
+ opener: HTMLElement | null;
30
+ private _dialog;
31
+ private _popover;
32
+ private readonly _id;
33
+ private readonly _anchorName;
34
+ private _restoreFocusTo;
35
+ private _scrollLocked;
36
+ private _wasOpen;
37
+ private _activationSeq;
38
+ render(): TemplateResult;
39
+ protected updated(changed: PropertyValues<this>): void;
40
+ disconnectedCallback(): void;
41
+ show(): void;
42
+ hide(reason?: OverlayCloseReason): void;
43
+ toggle(): void;
44
+ private _activate;
45
+ private _deactivate;
46
+ private _onKeydown;
47
+ private _onDialogCancel;
48
+ private _onDialogClose;
49
+ private _onDialogClick;
50
+ private _applyAnchor;
51
+ private _sameTreeScope;
52
+ private _reposition;
53
+ private _pinToAnchor;
54
+ private _lockScroll;
55
+ private _unlockScroll;
56
+ private _restoreFocus;
57
+ private _deepActiveElement;
58
+ private _ownsOpener;
59
+ private _tierZVar;
60
+ }
61
+ declare global {
62
+ interface HTMLElementTagNameMap {
63
+ "xm-overlay": XmOverlay;
64
+ }
65
+ }