@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,195 @@
1
+ /*
2
+ snackbar/index.ts — Lit port of components/snackbar/index.jsx.
3
+
4
+ <xm-snackbar> — backend-error surface that lives at the bottom of
5
+ the chat pane. Three flavors (auth | server | generic) plus a
6
+ separate `static` mode for preview cards (no entry animation).
7
+
8
+ Properties:
9
+ kind "auth" | "server" | "generic" (default "generic")
10
+ code optional code string ("401", "503", "ERR")
11
+ title required heading string
12
+ message required body string
13
+ primaryLabel if present, primary button is rendered
14
+ secondaryLabel defaults to "Dismiss"; the button only renders when
15
+ a `dismiss` listener wants to receive the event
16
+ showDismiss boolean — render the secondary/dismiss button
17
+ static boolean — preview-only mode, suppresses animations
18
+
19
+ Events:
20
+ snackbar-primary — primary action clicked (Retry / Open settings)
21
+ snackbar-dismiss — secondary/dismiss action clicked
22
+
23
+ Light DOM. components/snackbar/index.css and components/primitives/index.css
24
+ must be loaded in the host page.
25
+
26
+ SNACK_PRESETS is also exported so previews can spread a preset onto
27
+ the element via JS.
28
+ */
29
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
30
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
31
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
32
+ 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;
33
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
34
+ };
35
+ import { LitElement, html, svg, nothing } from "lit";
36
+ import { customElement, property } from "lit/decorators.js";
37
+ export const SNACK_PRESETS = {
38
+ auth: {
39
+ kind: "auth",
40
+ code: "401",
41
+ title: "Access denied",
42
+ message: "Your API key was rejected. Open settings to update or replace the key.",
43
+ primaryLabel: "Open settings",
44
+ secondaryLabel: "Dismiss",
45
+ },
46
+ server: {
47
+ kind: "server",
48
+ code: "503",
49
+ title: "Server error",
50
+ message: "The model service is temporarily unavailable. Your message wasn't sent.",
51
+ primaryLabel: "Retry",
52
+ secondaryLabel: "Dismiss",
53
+ },
54
+ generic: {
55
+ kind: "generic",
56
+ code: "ERR",
57
+ title: "Something went wrong",
58
+ message: "We couldn't reach the backend. Check your connection and try again.",
59
+ primaryLabel: "Retry",
60
+ secondaryLabel: "Dismiss",
61
+ },
62
+ };
63
+ const SHELL = (size, strokeWidth, paths) => svg `
64
+ <svg width="${size}" height="${size}" viewBox="0 0 24 24"
65
+ fill="none" stroke="currentColor" stroke-width="${strokeWidth}"
66
+ stroke-linecap="round" stroke-linejoin="round" class="ds-icon">
67
+ ${paths}
68
+ </svg>
69
+ `;
70
+ const KIND_ICONS = {
71
+ auth: () => SHELL(18, 1.8, svg `
72
+ <rect x="4" y="10" width="16" height="11" rx="2" />
73
+ <path d="M8 10V7a4 4 0 0 1 8 0v3" />
74
+ `),
75
+ server: () => SHELL(18, 1.8, svg `
76
+ <rect x="3" y="4" width="18" height="7" rx="1.5" />
77
+ <rect x="3" y="13" width="18" height="7" rx="1.5" />
78
+ <path d="M7 7.5h.01M7 16.5h.01" />
79
+ <path d="M16 18l4-4M20 18l-4-4" />
80
+ `),
81
+ generic: () => SHELL(18, 1.8, svg `
82
+ <path d="M12 3l10 18H2L12 3z" />
83
+ <path d="M12 10v5M12 18.5v.01" />
84
+ `),
85
+ };
86
+ const RETRY_ICON = () => SHELL(13, 2, svg `
87
+ <path d="M3 12a9 9 0 0 1 15.5-6.3L21 8" />
88
+ <path d="M21 3v5h-5" />
89
+ <path d="M21 12a9 9 0 0 1-15.5 6.3L3 16" />
90
+ <path d="M3 21v-5h5" />
91
+ `);
92
+ let XmSnackbar = class XmSnackbar extends LitElement {
93
+ constructor() {
94
+ super(...arguments);
95
+ this.kind = "generic";
96
+ this.code = "";
97
+ this.title = "";
98
+ this.message = "";
99
+ this.primaryLabel = "";
100
+ this.secondaryLabel = "Dismiss";
101
+ this.showDismiss = false;
102
+ this.static = false;
103
+ this._onPrimary = () => {
104
+ this.dispatchEvent(new CustomEvent("snackbar-primary", { bubbles: true, composed: true }));
105
+ };
106
+ this._onDismiss = () => {
107
+ this.dispatchEvent(new CustomEvent("snackbar-dismiss", { bubbles: true, composed: true }));
108
+ };
109
+ }
110
+ createRenderRoot() {
111
+ return this;
112
+ }
113
+ connectedCallback() {
114
+ super.connectedCallback();
115
+ // Make the host transparent to flex/grid layout so the inner .snackbar
116
+ // is what the parent measures — matches the React tree where
117
+ // <Snackbar> renders the <div class="snackbar"> directly.
118
+ if (!this.style.display)
119
+ this.style.display = "contents";
120
+ }
121
+ render() {
122
+ const kind = KIND_ICONS[this.kind] ? this.kind : "generic";
123
+ const kindIcon = KIND_ICONS[kind];
124
+ const cls = `snackbar snackbar--${kind} ${this.static ? "snackbar--static" : ""}`
125
+ .replace(/\s+/g, " ").trim();
126
+ return html `
127
+ <div class="${cls}" role="alert" aria-live="assertive">
128
+ <span class="snackbar__icon" aria-hidden="true">${kindIcon()}</span>
129
+
130
+ <div class="snackbar__body">
131
+ <div class="snackbar__head">
132
+ ${this.code
133
+ ? html `<span class="snackbar__code mono">${this.code}</span>`
134
+ : nothing}
135
+ <span class="snackbar__title">${this.title}</span>
136
+ </div>
137
+ <div class="snackbar__msg">${this.message}</div>
138
+ </div>
139
+
140
+ <div class="snackbar__actions">
141
+ ${this.primaryLabel
142
+ ? html `
143
+ <button
144
+ type="button"
145
+ class="snackbar__btn snackbar__btn--primary"
146
+ @click=${this._onPrimary}
147
+ >
148
+ ${kind !== "auth" ? RETRY_ICON() : nothing}
149
+ <span>${this.primaryLabel}</span>
150
+ </button>
151
+ `
152
+ : nothing}
153
+ ${this.showDismiss && this.secondaryLabel
154
+ ? html `
155
+ <button
156
+ type="button"
157
+ class="snackbar__btn snackbar__btn--ghost"
158
+ @click=${this._onDismiss}
159
+ >
160
+ ${this.secondaryLabel}
161
+ </button>
162
+ `
163
+ : nothing}
164
+ </div>
165
+ </div>
166
+ `;
167
+ }
168
+ };
169
+ __decorate([
170
+ property({ type: String })
171
+ ], XmSnackbar.prototype, "kind", void 0);
172
+ __decorate([
173
+ property({ type: String })
174
+ ], XmSnackbar.prototype, "code", void 0);
175
+ __decorate([
176
+ property({ type: String })
177
+ ], XmSnackbar.prototype, "title", void 0);
178
+ __decorate([
179
+ property({ type: String })
180
+ ], XmSnackbar.prototype, "message", void 0);
181
+ __decorate([
182
+ property({ type: String, attribute: "primary-label" })
183
+ ], XmSnackbar.prototype, "primaryLabel", void 0);
184
+ __decorate([
185
+ property({ type: String, attribute: "secondary-label" })
186
+ ], XmSnackbar.prototype, "secondaryLabel", void 0);
187
+ __decorate([
188
+ property({ type: Boolean, attribute: "show-dismiss" })
189
+ ], XmSnackbar.prototype, "showDismiss", void 0);
190
+ __decorate([
191
+ property({ type: Boolean })
192
+ ], XmSnackbar.prototype, "static", void 0);
193
+ XmSnackbar = __decorate([
194
+ customElement("xm-snackbar")
195
+ ], XmSnackbar);
@@ -0,0 +1,41 @@
1
+ /* ============================================
2
+ <xm-stack> — 1-D flex layout (vertical default).
3
+
4
+ The flex-stack primitive behind forms, sidebars, and action rows.
5
+ Slotted children are flex items (the <slot> is layout-transparent).
6
+ Gutter via gap="…" → --xm-gutter-* (the --s-N-aliased layout tier).
7
+
8
+ BEM block `stack`; modifiers --vertical / --horizontal, --gap-*,
9
+ --align-*, --justify-*. Registered in scripts/check-bem.sh
10
+ STRICT_BLOCKS.
11
+ ============================================ */
12
+
13
+ .stack {
14
+ display: flex;
15
+ gap: var(--xm-gutter-md);
16
+ /* Inherited ink so currentColor in slotted content reads on the desk
17
+ surface (AD-13) — also satisfies the --md-sys-* token gate. */
18
+ color: var(--md-sys-color-on-surface);
19
+ }
20
+
21
+ .stack--vertical { flex-direction: column; }
22
+ .stack--horizontal { flex-direction: row; flex-wrap: nowrap; }
23
+
24
+ :host([wrap]) .stack--horizontal { flex-wrap: wrap; }
25
+
26
+ .stack--gap-none { gap: var(--xm-gutter-none); }
27
+ .stack--gap-xs { gap: var(--xm-gutter-xs); }
28
+ .stack--gap-sm { gap: var(--xm-gutter-sm); }
29
+ .stack--gap-md { gap: var(--xm-gutter-md); }
30
+ .stack--gap-lg { gap: var(--xm-gutter-lg); }
31
+ .stack--gap-xl { gap: var(--xm-gutter-xl); }
32
+
33
+ .stack--align-start { align-items: flex-start; }
34
+ .stack--align-center { align-items: center; }
35
+ .stack--align-end { align-items: flex-end; }
36
+ .stack--align-stretch { align-items: stretch; }
37
+
38
+ .stack--justify-start { justify-content: flex-start; }
39
+ .stack--justify-center { justify-content: center; }
40
+ .stack--justify-end { justify-content: flex-end; }
41
+ .stack--justify-between { justify-content: space-between; }
@@ -0,0 +1,20 @@
1
+ import { LitElement } from "lit";
2
+ import type { TemplateResult } from "lit";
3
+ type StackDirection = "vertical" | "horizontal";
4
+ type StackGap = "none" | "xs" | "sm" | "md" | "lg" | "xl";
5
+ type StackAlign = "start" | "center" | "end" | "stretch";
6
+ type StackJustify = "start" | "center" | "end" | "between";
7
+ declare class XmStack extends LitElement {
8
+ direction: StackDirection;
9
+ gap: StackGap;
10
+ align: StackAlign;
11
+ justify: StackJustify;
12
+ wrap: boolean;
13
+ render(): TemplateResult;
14
+ }
15
+ declare global {
16
+ interface HTMLElementTagNameMap {
17
+ "xm-stack": XmStack;
18
+ }
19
+ }
20
+ export {};
@@ -0,0 +1,103 @@
1
+ /*
2
+ stack/index.ts — <xm-stack>, the 1-D flex layout primitive.
3
+
4
+ <xm-stack> — the flex-stack behind forms, sidebars, and action rows (which
5
+ today each re-implement `display:flex; flex-direction:column; gap:--s-N`
6
+ inline). Slotted children are flex items: the <slot> is layout-transparent,
7
+ so a child lays out as if it were a direct child of the stack container.
8
+
9
+ Authoring:
10
+ <xm-stack gap="md"> vertical stack (default)
11
+ <xm-field>…</xm-field>
12
+ <xm-field>…</xm-field>
13
+ </xm-stack>
14
+
15
+ <xm-stack direction="horizontal" justify="end" gap="sm"> action row
16
+ <xm-button variant="ghost">Cancel</xm-button>
17
+ <xm-button variant="primary">Save</xm-button>
18
+ </xm-stack>
19
+
20
+ Properties:
21
+ direction "vertical" | "horizontal" (default "vertical")
22
+ gap "none" | "xs" | "sm" | "md" | "lg" | "xl" (default "md")
23
+ → --xm-gutter-* (aliases the --s-N scale)
24
+ align "start" | "center" | "end" | "stretch" (default "stretch")
25
+ cross-axis align-items
26
+ justify "start" | "center" | "end" | "between" (default "start")
27
+ main-axis justify-content
28
+ wrap boolean — flex-wrap on horizontal stacks (default false)
29
+
30
+ Shadow DOM; Lit from lit; sibling CSS via the
31
+ built-file-relative new URL(...). BEM block `stack`.
32
+ */
33
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
34
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
35
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
36
+ 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;
37
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
38
+ };
39
+ import { LitElement, html } from "lit";
40
+ import { customElement, property } from "lit/decorators.js";
41
+ // Resolve CSS relative to the *built* file:
42
+ // lit/build/components/stack/index.js → ../stack/index.css.
43
+ const STACK_CSS = new URL("../stack/index.css", import.meta.url).href;
44
+ const DIRECTIONS = ["vertical", "horizontal"];
45
+ const GAPS = ["none", "xs", "sm", "md", "lg", "xl"];
46
+ const ALIGNS = ["start", "center", "end", "stretch"];
47
+ const JUSTIFIES = ["start", "center", "end", "between"];
48
+ let XmStack = class XmStack extends LitElement {
49
+ constructor() {
50
+ super(...arguments);
51
+ this.direction = "vertical";
52
+ this.gap = "md";
53
+ this.align = "stretch";
54
+ this.justify = "start";
55
+ this.wrap = false;
56
+ }
57
+ render() {
58
+ const direction = DIRECTIONS.includes(this.direction)
59
+ ? this.direction
60
+ : "vertical";
61
+ const gap = GAPS.includes(this.gap) ? this.gap : "md";
62
+ const align = ALIGNS.includes(this.align)
63
+ ? this.align
64
+ : "stretch";
65
+ const justify = JUSTIFIES.includes(this.justify)
66
+ ? this.justify
67
+ : "start";
68
+ return html `
69
+ <link rel="stylesheet" href="${STACK_CSS}" />
70
+ <style>
71
+ :host {
72
+ display: block;
73
+ }
74
+ :host([hidden]) {
75
+ display: none;
76
+ }
77
+ </style>
78
+ <div
79
+ class="stack stack--${direction} stack--gap-${gap} stack--align-${align} stack--justify-${justify}"
80
+ >
81
+ <slot></slot>
82
+ </div>
83
+ `;
84
+ }
85
+ };
86
+ __decorate([
87
+ property({ type: String, reflect: true })
88
+ ], XmStack.prototype, "direction", void 0);
89
+ __decorate([
90
+ property({ type: String, reflect: true })
91
+ ], XmStack.prototype, "gap", void 0);
92
+ __decorate([
93
+ property({ type: String, reflect: true })
94
+ ], XmStack.prototype, "align", void 0);
95
+ __decorate([
96
+ property({ type: String, reflect: true })
97
+ ], XmStack.prototype, "justify", void 0);
98
+ __decorate([
99
+ property({ type: Boolean, reflect: true })
100
+ ], XmStack.prototype, "wrap", void 0);
101
+ XmStack = __decorate([
102
+ customElement("xm-stack")
103
+ ], XmStack);
@@ -0,0 +1,126 @@
1
+ /* ============================================
2
+ xm-switch — binary on/off toggle on XmField (Story 2.7).
3
+
4
+ Like xm-checkbox, this is a TOGGLE field: the track sits BESIDE the label, not
5
+ above it on the bordered text-field chrome. State machinery (disabled / focus /
6
+ form-association / change) is inherited from XmField; this file styles only the
7
+ track, the thumb, and the label row.
8
+
9
+ Surface & ink (AD-13): the switch rides the inverse-surface card tier shared by
10
+ every XmField sibling — the label is inverse-on-surface ink. The OFF-track is a
11
+ muted neutral (--xm-switch-track-off, AD-10 extension); the thumb is the MD3
12
+ outline ink (legible on the off-track). When ON, the track fills with the
13
+ single coral accent (--md-sys-color-primary) and the thumb flips to on-primary
14
+ ink. Coral = on, never severity (AD-11).
15
+
16
+ Motion (NFR-19 / UX-DR10): the thumb slides on translateX only — short4
17
+ standard easing, NO bounce / spring / scale.
18
+
19
+ BEM block: `switch`. Registered in scripts/check-bem.sh STRICT_BLOCKS.
20
+ (Distinct from the legacy `ds-switch` CSS block.)
21
+ ============================================ */
22
+
23
+ .switch {
24
+ display: inline-flex;
25
+ flex-direction: column;
26
+ gap: var(--s-1);
27
+ }
28
+
29
+ /* ---------- Control row — track + label, the focusable target ---------- */
30
+ .switch__control {
31
+ display: inline-flex;
32
+ align-items: center;
33
+ gap: var(--s-2);
34
+ cursor: pointer;
35
+ user-select: none;
36
+ outline: none;
37
+ }
38
+
39
+ /* ---------- Track ---------- */
40
+ .switch__track {
41
+ position: relative;
42
+ display: inline-flex;
43
+ align-items: center;
44
+ width: 36px;
45
+ height: 20px;
46
+ flex-shrink: 0;
47
+ box-sizing: border-box;
48
+ padding: var(--s-0-5);
49
+ border-radius: var(--md-sys-shape-corner-full);
50
+ background: var(--xm-switch-track-off);
51
+ transition:
52
+ background var(--md-sys-motion-duration-short4) var(--md-sys-motion-easing-standard),
53
+ box-shadow var(--md-sys-motion-duration-short3) var(--md-sys-motion-easing-standard);
54
+ }
55
+
56
+ /* Focus ring — the canonical 3px coral halo on the track. */
57
+ .switch__control:focus-visible .switch__track {
58
+ box-shadow: var(--xm-state-focus-ring);
59
+ }
60
+
61
+ /* ---------- Thumb — slides on translateX only ---------- */
62
+ .switch__thumb {
63
+ width: 16px;
64
+ height: 16px;
65
+ border-radius: var(--md-sys-shape-corner-full);
66
+ background: var(--md-sys-color-outline);
67
+ box-shadow: var(--xm-switch-thumb-shadow);
68
+ transform: translateX(0);
69
+ transition: transform var(--md-sys-motion-duration-short4)
70
+ var(--md-sys-motion-easing-standard),
71
+ background var(--md-sys-motion-duration-short4)
72
+ var(--md-sys-motion-easing-standard);
73
+ }
74
+
75
+ /* ---------- On — coral track, thumb slides + flips to on-primary ink ---------- */
76
+ .switch--on .switch__track {
77
+ background: var(--md-sys-color-primary);
78
+ }
79
+ .switch--on .switch__thumb {
80
+ transform: translateX(16px);
81
+ background: var(--md-sys-color-on-primary);
82
+ }
83
+
84
+ /* ---------- Label ---------- */
85
+ .switch__label {
86
+ color: var(--md-sys-color-inverse-on-surface);
87
+ font:
88
+ var(--md-sys-typescale-body-medium-weight)
89
+ var(--md-sys-typescale-body-medium-size) /
90
+ var(--md-sys-typescale-body-medium-line-height)
91
+ var(--md-sys-typescale-body-medium-font);
92
+ letter-spacing: 0;
93
+ }
94
+ /* Collapse the label gap when no label is slotted. */
95
+ .switch__label:empty {
96
+ display: none;
97
+ }
98
+
99
+ /* ---------- Disabled — shared reduced emphasis, no pointer ---------- */
100
+ .switch--disabled .switch__control {
101
+ cursor: not-allowed;
102
+ }
103
+ .switch--disabled .switch__track {
104
+ opacity: 0.45;
105
+ box-shadow: none;
106
+ }
107
+ .switch--disabled .switch__label {
108
+ color: var(--xm-color-inverse-on-surface-disabled);
109
+ }
110
+
111
+ /* ---------- Helper / error message row ----------
112
+ Severity is copy, not color — the error keeps the helper ink (rule 3a). */
113
+ .switch__message {
114
+ padding-left: calc(var(--s-9) + var(--s-2));
115
+ font:
116
+ var(--md-sys-typescale-body-small-weight)
117
+ var(--md-sys-typescale-body-small-size) /
118
+ var(--md-sys-typescale-body-small-line-height)
119
+ var(--md-sys-typescale-body-small-font);
120
+ }
121
+ .switch__message--helper {
122
+ color: var(--xm-color-inverse-on-surface-muted);
123
+ }
124
+ .switch__message--error {
125
+ color: var(--md-sys-color-inverse-on-surface);
126
+ }
@@ -0,0 +1,17 @@
1
+ import type { TemplateResult } from "lit";
2
+ import { XmField } from "../field/index.js";
3
+ export declare class XmSwitch extends XmField {
4
+ /** A switch submits checked-state ("on"/null), not text — declare it a toggle
5
+ so the form value is correct from first paint, before any user interaction
6
+ (the base seeds `_checked` from the `checked` attribute and `isToggle`
7
+ routes it through the toggle form-value branch). */
8
+ protected get isToggle(): boolean;
9
+ private _toggleSwitch;
10
+ private _onKeydown;
11
+ render(): TemplateResult;
12
+ }
13
+ declare global {
14
+ interface HTMLElementTagNameMap {
15
+ "xm-switch": XmSwitch;
16
+ }
17
+ }
@@ -0,0 +1,116 @@
1
+ /*
2
+ switch/index.ts — <xm-switch>, a binary on/off toggle on XmField (Story 2.7).
3
+
4
+ Shares the checkbox's toggle machinery: it subclasses XmField and overrides
5
+ render() for an inline track+thumb+label row (the bordered text-field chrome is
6
+ wrong for a compact toggle), while reusing ALL of the base's state machinery —
7
+ effectiveDisabled / nonInteractive, emitToggle (which owns live _checked state,
8
+ the form value, and the bubbling+composed `change` with detail.checked), the
9
+ controlAria hooks, and the helper/error message row. Nothing about disabled /
10
+ focus / form-association is re-implemented (AD-7).
11
+
12
+ The on-track is the single coral accent (--md-sys-color-primary); the thumb
13
+ slides on translateX only — short3–short4 standard easing, NO bounce / spring /
14
+ scale (NFR-19 / UX-DR10). Coral signals on, never severity (AD-11).
15
+
16
+ Authoring:
17
+ <xm-switch checked>Email notifications</xm-switch>
18
+ <xm-switch disabled>Locked setting</xm-switch>
19
+
20
+ Keyboard: Space toggles when focused; label/track click toggles. role="switch"
21
+ + aria-checked. Shadow DOM. Lit is a bare `import` (peer dep).
22
+ */
23
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
24
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
25
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
26
+ 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;
27
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
28
+ };
29
+ import { html, nothing } from "lit";
30
+ import { customElement } from "lit/decorators.js";
31
+ import { XmField } from "../field/index.js";
32
+ const SWITCH_CSS = new URL("../switch/index.css", import.meta.url).href;
33
+ const PRIMITIVES_CSS = new URL("../primitives/index.css", import.meta.url).href;
34
+ let XmSwitch = class XmSwitch extends XmField {
35
+ constructor() {
36
+ super(...arguments);
37
+ this._toggleSwitch = () => {
38
+ if (this.nonInteractive)
39
+ return;
40
+ this.emitToggle(!this._checked);
41
+ };
42
+ this._onKeydown = (event) => {
43
+ if (event.key === " " || event.key === "Spacebar") {
44
+ event.preventDefault();
45
+ this._toggleSwitch();
46
+ }
47
+ };
48
+ }
49
+ /** A switch submits checked-state ("on"/null), not text — declare it a toggle
50
+ so the form value is correct from first paint, before any user interaction
51
+ (the base seeds `_checked` from the `checked` attribute and `isToggle`
52
+ routes it through the toggle form-value branch). */
53
+ get isToggle() {
54
+ return true;
55
+ }
56
+ render() {
57
+ const cls = [
58
+ "switch",
59
+ this._checked ? "switch--on" : "",
60
+ this.effectiveDisabled ? "switch--disabled" : "",
61
+ this.isError ? "switch--error" : "",
62
+ ]
63
+ .filter(Boolean)
64
+ .join(" ");
65
+ const helperText = this.isError ? this.error : this.helper;
66
+ const ctrl = this.controlAria;
67
+ return html `
68
+ <link rel="stylesheet" href="${PRIMITIVES_CSS}" />
69
+ <link rel="stylesheet" href="${SWITCH_CSS}" />
70
+ <style>
71
+ :host {
72
+ display: inline-block;
73
+ }
74
+ :host([hidden]) {
75
+ display: none;
76
+ }
77
+ </style>
78
+ <div class="${cls}">
79
+ <div
80
+ class="switch__control"
81
+ id="${ctrl.id}"
82
+ role="switch"
83
+ aria-checked="${this._checked ? "true" : "false"}"
84
+ aria-describedby="${helperText ? ctrl.describedBy : nothing}"
85
+ aria-invalid="${ctrl.invalid ?? nothing}"
86
+ aria-required="${ctrl.required ?? nothing}"
87
+ aria-disabled="${this.effectiveDisabled ? "true" : nothing}"
88
+ tabindex="${this.effectiveDisabled ? -1 : 0}"
89
+ @click=${this._toggleSwitch}
90
+ @keydown=${this._onKeydown}
91
+ >
92
+ <span class="switch__track" aria-hidden="true">
93
+ <span class="switch__thumb"></span>
94
+ </span>
95
+ <span class="switch__label"><slot></slot></span>
96
+ </div>
97
+
98
+ ${helperText
99
+ ? html `<div
100
+ class="switch__message ${this.isError
101
+ ? "switch__message--error"
102
+ : "switch__message--helper"}"
103
+ id="${ctrl.describedBy}"
104
+ role=${this.isError ? "alert" : nothing}
105
+ >
106
+ ${helperText}
107
+ </div>`
108
+ : nothing}
109
+ </div>
110
+ `;
111
+ }
112
+ };
113
+ XmSwitch = __decorate([
114
+ customElement("xm-switch")
115
+ ], XmSwitch);
116
+ export { XmSwitch };