@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,342 @@
1
+ /* ============================================
2
+ <Button /> — shared chrome and variants
3
+
4
+ Pairs with components/button/index.jsx. Variants are BEM modifiers:
5
+ .btn--primary / .btn--secondary / .btn--outline / .btn--ghost
6
+ .btn--danger / .btn--ghost-accent / .btn--ghost-outline
7
+ .btn--ghost.btn--danger ghost-danger compound (two orthogonal
8
+ modifiers; severity carried by icon
9
+ + label, not color).
10
+
11
+ Sizes are an orthogonal modifier:
12
+ .btn--xs 22px height, 11px text (toolbar chrome on dark surfaces)
13
+ .btn--sm 26px height, 12px text
14
+ .btn--md 32px height, 13px text (default)
15
+ .btn--lg 42px height, 14px text
16
+
17
+ Dark-surface variants (paired with the same neutral chrome but using the
18
+ canvas ink palette + canvas borders) — for buttons that sit on canvas,
19
+ sidebar, composer, or bubble surfaces:
20
+ .btn--outline-canvas hairline border, transparent fill
21
+ .btn--ghost-canvas borderless, soft hover fill
22
+ .btn--ghost-accent-canvas accent-tinted ghost on dark
23
+
24
+ Forced-state helpers (.is-hover / .is-focus / .is-active) preview
25
+ pseudo-class visuals in design canvases without requiring real
26
+ keyboard/mouse focus.
27
+ ============================================ */
28
+
29
+ /* ---------- Shared chrome ---------- */
30
+ .btn {
31
+ appearance: none;
32
+ display: inline-flex;
33
+ align-items: center;
34
+ justify-content: center;
35
+ gap: var(--s-1-5);
36
+ font-family: var(--md-sys-typescale-label-large-font);
37
+ line-height: 1;
38
+ user-select: none;
39
+ white-space: nowrap;
40
+ cursor: pointer;
41
+ transition:
42
+ background var(--md-sys-motion-duration-short3) var(--md-sys-motion-easing-standard),
43
+ color var(--md-sys-motion-duration-short3) var(--md-sys-motion-easing-standard),
44
+ border-color var(--md-sys-motion-duration-short3) var(--md-sys-motion-easing-standard),
45
+ box-shadow var(--md-sys-motion-duration-short3) var(--md-sys-motion-easing-standard);
46
+ }
47
+ .btn svg { flex-shrink: 0; }
48
+ .btn:focus { outline: none; }
49
+ .btn:focus-visible,
50
+ .btn.is-focus {
51
+ outline: none;
52
+ box-shadow: var(--xm-state-focus-ring);
53
+ }
54
+
55
+ /* ---------- Sizes ---------- */
56
+ .btn--xs { padding: 3px 7px; font: 600 11px/1 var(--md-sys-typescale-label-large-font); border-radius: 5px; gap: var(--s-1); }
57
+ .btn--sm { padding: 5px var(--s-2-5); font: 600 12px/1 var(--md-sys-typescale-label-large-font); border-radius: 6px; }
58
+ .btn--md { padding: 9px var(--s-3-5); font: 600 13px/1 var(--md-sys-typescale-label-large-font); border-radius: var(--md-sys-shape-corner-button); }
59
+ .btn--lg { padding: var(--s-3) var(--s-4-5); font: 600 14px/1 var(--md-sys-typescale-label-large-font); border-radius: var(--md-sys-shape-corner-button); }
60
+
61
+ /* ---------- Variants ---------- */
62
+
63
+ /* primary — accent fill, the workhorse CTA. Uses --xm-color-primary-fill
64
+ (a darker coral) rather than --md-sys-color-primary so white-on-fill
65
+ clears WCAG AA; the lighter primary role stays for accent text/border. */
66
+ .btn--primary {
67
+ border: 1px solid var(--xm-color-primary-pressed);
68
+ background: var(--xm-color-primary-fill);
69
+ color: var(--md-sys-color-on-primary);
70
+ box-shadow: var(--md-sys-elevation-level0);
71
+ }
72
+ .btn--primary:hover,
73
+ .btn--primary.is-hover { background: var(--xm-color-primary-pressed); }
74
+ .btn--primary:active,
75
+ .btn--primary.is-active { box-shadow: var(--xm-elevation-pressed); transform: translateY(1px); }
76
+
77
+ /* secondary — solid neutral, used for non-destructive secondary CTAs */
78
+ .btn--secondary {
79
+ border: 1px solid var(--md-sys-color-secondary);
80
+ background: var(--md-sys-color-secondary);
81
+ color: var(--md-sys-color-on-secondary);
82
+ }
83
+ .btn--secondary:hover,
84
+ .btn--secondary.is-hover {
85
+ background: color-mix(in oklab, var(--md-sys-color-on-secondary) var(--md-sys-state-hover-state-layer-opacity), var(--md-sys-color-secondary));
86
+ }
87
+
88
+ /* outline — bordered card, used for cancel / pair-actions */
89
+ .btn--outline {
90
+ border: 1px solid var(--md-sys-color-outline-variant);
91
+ background: var(--md-sys-color-inverse-surface);
92
+ color: var(--md-sys-color-inverse-on-surface);
93
+ }
94
+ .btn--outline:hover,
95
+ .btn--outline.is-hover {
96
+ background: color-mix(in oklab, var(--md-sys-color-inverse-on-surface) var(--md-sys-state-hover-state-layer-opacity), var(--md-sys-color-inverse-surface));
97
+ border-color: var(--md-sys-color-outline);
98
+ }
99
+ .btn--outline:focus-visible,
100
+ .btn--outline.is-focus { border-color: var(--md-sys-color-primary); }
101
+
102
+ /* ghost — transparent, sits on the dark canvas (sidebar, top bar) */
103
+ .btn--ghost {
104
+ border: 1px solid transparent;
105
+ background: transparent;
106
+ color: var(--md-sys-color-on-surface);
107
+ }
108
+ .btn--ghost:hover,
109
+ .btn--ghost.is-hover {
110
+ background: color-mix(in oklab, var(--md-sys-color-on-surface) var(--md-sys-state-hover-state-layer-opacity), transparent);
111
+ color: var(--md-sys-color-on-surface);
112
+ }
113
+ /* ghost selected — used by toggle/tab buttons. Persistent fill + hairline
114
+ to stand out next to inactive siblings in a segmented group. */
115
+ .btn--ghost.is-active {
116
+ background: var(--md-sys-color-surface-container-lowest);
117
+ border-color: var(--md-sys-color-outline);
118
+ color: var(--md-sys-color-on-surface);
119
+ }
120
+
121
+ /* ghost-danger — neutral ghost; severity carried by trash/x icon + label.
122
+ Composes with .btn--ghost: <button class="btn btn--ghost btn--danger"> */
123
+ .btn--ghost.btn--danger {
124
+ color: var(--md-sys-color-on-surface);
125
+ border-color: var(--md-sys-color-outline);
126
+ }
127
+ .btn--ghost.btn--danger:hover,
128
+ .btn--ghost.btn--danger.is-hover {
129
+ background: var(--md-sys-color-surface-container-lowest);
130
+ color: var(--md-sys-color-on-surface);
131
+ }
132
+
133
+ /* ghost-outline — transparent fill + hairline border on card surfaces.
134
+ Sits between .btn--outline (filled card) and .btn--ghost (no chrome):
135
+ the rest border outlines the affordance without lifting it onto a new
136
+ surface. Used when a ghost needs a quiet anchor in busy layouts
137
+ (toolbars on cards, secondary actions next to a primary CTA). */
138
+ .btn--ghost-outline {
139
+ border: 1px solid var(--md-sys-color-outline-variant);
140
+ background: transparent;
141
+ color: var(--md-sys-color-inverse-on-surface);
142
+ }
143
+ .btn--ghost-outline:hover,
144
+ .btn--ghost-outline.is-hover {
145
+ background: color-mix(in oklab, var(--md-sys-color-inverse-on-surface) var(--md-sys-state-hover-state-layer-opacity), var(--md-sys-color-inverse-surface));
146
+ border-color: var(--md-sys-color-outline);
147
+ }
148
+ .btn--ghost-outline:focus-visible,
149
+ .btn--ghost-outline.is-focus { border-color: var(--md-sys-color-primary); }
150
+
151
+ /* ghost-accent — accent-tinted secondary, used by file pills etc. One tone,
152
+ not a stack — written as a single hyphenated modifier per BEM. The resting
153
+ fill is the accent wash over the card; since the card tier is theme-following
154
+ (ADR 0006) that wash is dark in dark theme, so the resting ink is the card's
155
+ inverse-on-surface (light there). On hover the fill becomes the full coral
156
+ primary-container, so the ink steps to on-primary-container (dark). */
157
+ .btn--ghost-accent {
158
+ border-color: color-mix(in oklab, var(--md-sys-color-primary-container) 80%, transparent);
159
+ color: var(--md-sys-color-inverse-on-surface);
160
+ background: color-mix(in oklch, var(--md-sys-color-primary-container) 30%, var(--md-sys-color-inverse-surface));
161
+ }
162
+ .btn--ghost-accent:hover,
163
+ .btn--ghost-accent.is-hover {
164
+ background: var(--md-sys-color-primary-container);
165
+ border-color: var(--md-sys-color-primary);
166
+ color: var(--md-sys-color-on-primary-container);
167
+ }
168
+
169
+ /* outline-canvas — hairline + transparent fill, canvas ink palette.
170
+ Used by artifact "Download" pills inside bubbles, and any outline action
171
+ that sits on canvas / composer / sidebar surfaces. */
172
+ .btn--outline-canvas {
173
+ border: 1px solid color-mix(in oklab, var(--md-sys-color-on-surface) 32%, transparent);
174
+ background: transparent;
175
+ color: var(--md-sys-color-on-surface);
176
+ }
177
+ .btn--outline-canvas:hover,
178
+ .btn--outline-canvas.is-hover {
179
+ background: color-mix(in oklab, var(--md-sys-color-on-surface) 10%, transparent);
180
+ border-color: var(--xm-color-on-surface-soft);
181
+ }
182
+ .btn--outline-canvas:focus-visible,
183
+ .btn--outline-canvas.is-focus { border-color: var(--md-sys-color-primary); }
184
+
185
+ /* ghost-canvas — borderless toolbar chrome on dark surfaces. Mirrors
186
+ .btn--ghost but uses the canvas ink palette and the composer/canvas
187
+ hover fill so it works consistently across composer, bubble actions,
188
+ and chat hover toolbars. */
189
+ .btn--ghost-canvas {
190
+ border: 1px solid transparent;
191
+ background: transparent;
192
+ color: var(--md-sys-color-on-surface);
193
+ }
194
+ .btn--ghost-canvas:hover,
195
+ .btn--ghost-canvas.is-hover {
196
+ background: color-mix(in oklab, var(--md-sys-color-on-surface) var(--md-sys-state-hover-state-layer-opacity), transparent);
197
+ color: var(--md-sys-color-on-surface);
198
+ }
199
+ .btn--ghost-canvas.is-active {
200
+ background: var(--md-sys-color-surface-container-lowest);
201
+ border-color: var(--md-sys-color-outline);
202
+ color: var(--md-sys-color-on-surface);
203
+ }
204
+
205
+ /* ghost-accent-canvas — accent-tinted ghost on dark surfaces. */
206
+ .btn--ghost-accent-canvas {
207
+ border: 1px solid transparent;
208
+ background: transparent;
209
+ color: var(--md-sys-color-primary);
210
+ }
211
+ .btn--ghost-accent-canvas:hover,
212
+ .btn--ghost-accent-canvas.is-hover {
213
+ background: color-mix(in oklab, var(--md-sys-color-primary) 14%, transparent);
214
+ color: var(--xm-color-primary-pressed);
215
+ }
216
+
217
+ /* danger — neutral solid; destructive intent expressed by trash icon + label.
218
+ Rides the surface family (surface-container-high), so the ink is on-surface
219
+ (rule 7a). Hover darkens within the surface ramp rather than jumping to the
220
+ card tier, keeping the ink anchored to one surface family. */
221
+ .btn--danger {
222
+ border: 1px solid var(--md-sys-color-outline);
223
+ background: var(--md-sys-color-surface-container-high);
224
+ color: var(--md-sys-color-on-surface);
225
+ }
226
+ .btn--danger:hover,
227
+ .btn--danger.is-hover {
228
+ background: var(--md-sys-color-surface-container-highest);
229
+ border-color: var(--md-sys-color-on-surface);
230
+ }
231
+ .btn--danger:focus-visible,
232
+ .btn--danger.is-focus {
233
+ box-shadow: var(--xm-state-focus-ring);
234
+ }
235
+
236
+ /* ---------- Icon-only ---------- */
237
+ /* Square chip variant. Composes with any tone: pass an iconLeft and
238
+ no children, or use .btn__icon directly. Sizes are square. */
239
+ .btn--icon-only {
240
+ padding: 0;
241
+ gap: 0;
242
+ }
243
+ .btn--icon-only.btn--xs { width: 22px; height: 22px; }
244
+ .btn--icon-only.btn--sm { width: 28px; height: 28px; }
245
+ .btn--icon-only.btn--md { width: 32px; height: 32px; }
246
+ .btn--icon-only.btn--lg { width: 38px; height: 38px; }
247
+
248
+ /* ---------- Full-width ---------- */
249
+ /* Stretches the button to fill its container. The label hugs the leading
250
+ edge (next to icon-left) and `margin-inline-end: auto` pushes any
251
+ icon-right slot (e.g. a kbd hint) to the trailing edge. */
252
+ .btn--full-width {
253
+ width: 100%;
254
+ }
255
+ .btn--full-width .btn__label {
256
+ margin-inline-end: auto;
257
+ text-align: start;
258
+ }
259
+
260
+ /* ---------- Disabled / loading ----------
261
+ No blanket opacity: dimming the whole control fades ink AND surface
262
+ together and drops the label below WCAG AA (POLICIES 7a). Instead each
263
+ variant swaps to a validated disabled ink (filled variants also flatten
264
+ to the disabled container) so the label stays legible. */
265
+ .btn[disabled],
266
+ .btn[aria-busy="true"],
267
+ .btn.is-disabled {
268
+ cursor: not-allowed;
269
+ box-shadow: none;
270
+ }
271
+ /* filled variants — flatten fill to the disabled container + muted ink */
272
+ .btn--primary[disabled], .btn--primary.is-disabled,
273
+ .btn--secondary[disabled], .btn--secondary.is-disabled,
274
+ .btn--danger[disabled], .btn--danger.is-disabled {
275
+ background: var(--xm-state-disabled-container);
276
+ border-color: var(--md-sys-color-outline-variant);
277
+ color: var(--xm-color-on-surface-disabled);
278
+ }
279
+ /* surface-ink variants (ghost / outline-canvas / ghost-canvas on the canvas) */
280
+ .btn--ghost[disabled], .btn--ghost.is-disabled,
281
+ .btn--ghost-canvas[disabled], .btn--ghost-canvas.is-disabled,
282
+ .btn--ghost-accent-canvas[disabled], .btn--ghost-accent-canvas.is-disabled,
283
+ .btn--outline-canvas[disabled], .btn--outline-canvas.is-disabled {
284
+ color: var(--xm-color-on-surface-disabled);
285
+ }
286
+ /* card-ink variants (outline / ghost-outline / ghost-accent on the card tier) */
287
+ .btn--outline[disabled], .btn--outline.is-disabled,
288
+ .btn--ghost-outline[disabled], .btn--ghost-outline.is-disabled,
289
+ .btn--ghost-accent[disabled], .btn--ghost-accent.is-disabled {
290
+ color: var(--xm-color-inverse-on-surface-disabled);
291
+ }
292
+ .btn[disabled]:hover,
293
+ .btn[aria-busy="true"]:hover,
294
+ .btn.is-disabled:hover {
295
+ filter: none;
296
+ transform: none;
297
+ }
298
+ /* Disabled :hover is a no-op — hold the disabled fill/ink set above so
299
+ hovering a disabled control doesn't restore the enabled (and contrast-
300
+ failing) appearance. */
301
+ .btn--primary[disabled]:hover, .btn--primary.is-disabled:hover,
302
+ .btn--secondary[disabled]:hover, .btn--secondary.is-disabled:hover,
303
+ .btn--danger[disabled]:hover, .btn--danger.is-disabled:hover {
304
+ background: var(--xm-state-disabled-container);
305
+ border-color: var(--md-sys-color-outline-variant);
306
+ color: var(--xm-color-on-surface-disabled);
307
+ }
308
+ .btn--ghost[disabled]:hover, .btn--ghost.is-disabled:hover,
309
+ .btn--ghost-canvas[disabled]:hover, .btn--ghost-canvas.is-disabled:hover,
310
+ .btn--outline-canvas[disabled]:hover, .btn--outline-canvas.is-disabled:hover,
311
+ .btn--ghost-accent-canvas[disabled]:hover, .btn--ghost-accent-canvas.is-disabled:hover {
312
+ background: transparent;
313
+ color: var(--xm-color-on-surface-disabled);
314
+ }
315
+ .btn--outline[disabled]:hover, .btn--outline.is-disabled:hover,
316
+ .btn--ghost-outline[disabled]:hover, .btn--ghost-outline.is-disabled:hover,
317
+ .btn--ghost-accent[disabled]:hover, .btn--ghost-accent.is-disabled:hover {
318
+ background: var(--md-sys-color-inverse-surface);
319
+ border-color: var(--md-sys-color-outline-variant);
320
+ color: var(--xm-color-inverse-on-surface-disabled);
321
+ }
322
+
323
+ /* ---------- Inner slots ---------- */
324
+ .btn__icon { display: inline-flex; align-items: center; }
325
+ .btn__label { display: inline-block; }
326
+
327
+ /* ---------- Loading spinner ---------- */
328
+ .btn__spinner {
329
+ display: inline-block;
330
+ width: 12px;
331
+ height: 12px;
332
+ border: 1.6px solid currentColor;
333
+ border-top-color: transparent;
334
+ border-right-color: transparent;
335
+ border-radius: 50%;
336
+ animation: btn-spin 0.7s linear infinite;
337
+ flex-shrink: 0;
338
+ opacity: 0.85;
339
+ }
340
+ @keyframes btn-spin {
341
+ to { transform: rotate(360deg); }
342
+ }
@@ -0,0 +1,32 @@
1
+ import { LitElement } from "lit";
2
+ import type { TemplateResult } from "lit";
3
+ type ButtonVariant = "primary" | "secondary" | "outline" | "ghost" | "ghost-accent" | "ghost-outline" | "outline-canvas" | "ghost-canvas" | "ghost-accent-canvas";
4
+ type ButtonSize = "xs" | "sm" | "md" | "lg";
5
+ type ButtonType = "button" | "submit" | "reset";
6
+ type ForceState = "hover" | "active" | "focus" | null;
7
+ declare class XmButton extends LitElement {
8
+ static shadowRootOptions: ShadowRootInit;
9
+ variant: ButtonVariant;
10
+ size: ButtonSize;
11
+ iconOnly: boolean;
12
+ fullWidth: boolean;
13
+ /** Persistent active/selected state — used by toggle/tab buttons. Reuses
14
+ the same .is-active visual the design canvases trigger via forceState. */
15
+ selected: boolean;
16
+ type: ButtonType;
17
+ disabled: boolean;
18
+ loading: boolean;
19
+ forceState: ForceState;
20
+ private _mo;
21
+ render(): TemplateResult;
22
+ private _slotEmpty;
23
+ private _onSlot;
24
+ connectedCallback(): void;
25
+ disconnectedCallback(): void;
26
+ }
27
+ declare global {
28
+ interface HTMLElementTagNameMap {
29
+ "xm-button": XmButton;
30
+ }
31
+ }
32
+ export {};
@@ -0,0 +1,202 @@
1
+ /*
2
+ button/index.ts — Lit port of components/button/index.jsx.
3
+
4
+ <xm-button> — unified button primitive for the chat surface.
5
+
6
+ Authoring:
7
+ <xm-button variant="primary" size="md">
8
+ <svg slot="icon-left">…</svg>
9
+ Send message
10
+ <svg slot="icon-right">…</svg>
11
+ </xm-button>
12
+
13
+ This component uses **shadow DOM** because Lit's <slot> projection
14
+ only works there. The existing components/button/index.css is loaded
15
+ inside the shadow root via <link>, which gives identical visuals to
16
+ the React version while allowing native slot composition. The design
17
+ tokens (--c-*, --t-*, --r-*, --e-*, --m-*) cascade through the shadow
18
+ boundary as inherited CSS custom properties, so theme switching keeps
19
+ working without any extra wiring.
20
+
21
+ Properties:
22
+ variant primary | secondary | outline | ghost | ghost-accent
23
+ | ghost-outline | outline-canvas | ghost-canvas
24
+ | ghost-accent-canvas
25
+ size xs | sm | md | lg (default md)
26
+ iconOnly boolean — square chip; only the default slot is shown
27
+ type button | submit | reset
28
+ disabled boolean
29
+ loading boolean — disables and swaps content for a spinner
30
+ forceState null | hover | active | focus — preview pseudo-classes
31
+ */
32
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
33
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
34
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
35
+ 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;
36
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
37
+ };
38
+ import { LitElement, html, nothing } from "lit";
39
+ import { customElement, property } from "lit/decorators.js";
40
+ const VARIANT_CLASS = {
41
+ primary: "btn--primary",
42
+ secondary: "btn--secondary",
43
+ outline: "btn--outline",
44
+ ghost: "btn--ghost",
45
+ "ghost-accent": "btn--ghost-accent",
46
+ "ghost-outline": "btn--ghost-outline",
47
+ "outline-canvas": "btn--outline-canvas",
48
+ "ghost-canvas": "btn--ghost-canvas",
49
+ "ghost-accent-canvas": "btn--ghost-accent-canvas",
50
+ };
51
+ // Resolve CSS path relative to this module so the previews can sit at
52
+ // any depth without the component caring (e.g. lit/preview/buttons.html
53
+ // → ../components/button/index.css).
54
+ //
55
+ // Paths are computed from the *built* file location:
56
+ // `lit/build/components/button/index.js` — 3 levels of `..` to reach
57
+ // `lit/`, then into `components/`. The .ts source under
58
+ // `lit/components/button/` is never loaded by the browser; only the
59
+ // tsc-emitted .js is.
60
+ const BTN_CSS = new URL("../button/index.css", import.meta.url).href;
61
+ const PRIMITIVES_CSS = new URL("../primitives/index.css", import.meta.url).href;
62
+ let XmButton = class XmButton extends LitElement {
63
+ constructor() {
64
+ super(...arguments);
65
+ this.variant = "primary";
66
+ this.size = "md";
67
+ this.iconOnly = false;
68
+ this.fullWidth = false;
69
+ /** Persistent active/selected state — used by toggle/tab buttons. Reuses
70
+ the same .is-active visual the design canvases trigger via forceState. */
71
+ this.selected = false;
72
+ this.type = "button";
73
+ this.disabled = false;
74
+ this.loading = false;
75
+ this.forceState = null;
76
+ this._mo = null;
77
+ this._onSlot = () => {
78
+ this.requestUpdate();
79
+ };
80
+ }
81
+ // Forward focus to the inner native <button>. Required for Enter/Space
82
+ // to fire a click when the host is focused via tabindex (used by toggle
83
+ // and tab buttons that receive focus on the host element).
84
+ static { this.shadowRootOptions = {
85
+ ...LitElement.shadowRootOptions,
86
+ delegatesFocus: true,
87
+ }; }
88
+ render() {
89
+ const variantClass = VARIANT_CLASS[this.variant] ?? VARIANT_CLASS.primary;
90
+ const sizeClass = `btn--${this.size}`;
91
+ const iconOnlyClass = this.iconOnly ? "btn--icon-only" : "";
92
+ const fullWidthClass = this.fullWidth ? "btn--full-width" : "";
93
+ const stateClass = this.selected ? "is-active" :
94
+ this.forceState === "hover" ? "is-hover" :
95
+ this.forceState === "active" ? "is-active" :
96
+ this.forceState === "focus" ? "is-focus" : "";
97
+ const cls = `btn ${variantClass} ${sizeClass} ${iconOnlyClass} ${fullWidthClass} ${stateClass}`
98
+ .replace(/\s+/g, " ")
99
+ .trim();
100
+ const isDisabled = this.disabled || this.loading;
101
+ let body;
102
+ if (this.loading) {
103
+ body = html `<span class="ds-spinner btn__spinner"
104
+ role="status" aria-label="Loading"
105
+ style="width:14px;height:14px"></span>`;
106
+ }
107
+ else if (this.iconOnly) {
108
+ body = html `<slot></slot>`;
109
+ }
110
+ else {
111
+ body = html `
112
+ <span class="btn__icon btn__icon--left ${this._slotEmpty("icon-left") ? "is-empty" : ""}"
113
+ ><slot name="icon-left" @slotchange=${this._onSlot}></slot></span>
114
+ <span class="btn__label ${this._slotEmpty(null) ? "is-empty" : ""}"
115
+ ><slot @slotchange=${this._onSlot}></slot></span>
116
+ <span class="btn__icon btn__icon--right ${this._slotEmpty("icon-right") ? "is-empty" : ""}"
117
+ ><slot name="icon-right" @slotchange=${this._onSlot}></slot></span>
118
+ `;
119
+ }
120
+ return html `
121
+ <link rel="stylesheet" href="${PRIMITIVES_CSS}">
122
+ <link rel="stylesheet" href="${BTN_CSS}">
123
+ <style>
124
+ :host { display: inline-block; }
125
+ :host([full-width]) { display: block; }
126
+ :host([hidden]) { display: none; }
127
+ /* Hide empty slot wrappers so the .btn flexbox gap doesn't leave
128
+ phantom space when iconLeft/iconRight isn't authored. Mirrors
129
+ the JSX which simply doesn't render those <span>s. */
130
+ .btn__icon.is-empty,
131
+ .btn__label.is-empty { display: none; }
132
+ </style>
133
+ <button
134
+ type="${this.type}"
135
+ ?disabled=${isDisabled}
136
+ aria-busy=${this.loading ? "true" : nothing}
137
+ class="${cls}"
138
+ >
139
+ ${body}
140
+ </button>
141
+ `;
142
+ }
143
+ _slotEmpty(name) {
144
+ // Read directly from light-DOM children (the authored content).
145
+ for (const node of this.childNodes) {
146
+ if (node.nodeType === Node.ELEMENT_NODE) {
147
+ const slotName = node.getAttribute("slot");
148
+ const target = name || null;
149
+ const matches = (target === null && !slotName) || slotName === target;
150
+ if (matches)
151
+ return false;
152
+ }
153
+ else if (node.nodeType === Node.TEXT_NODE && !name) {
154
+ if (node.textContent?.trim())
155
+ return false;
156
+ }
157
+ }
158
+ return true;
159
+ }
160
+ connectedCallback() {
161
+ super.connectedCallback();
162
+ if (!this._mo) {
163
+ this._mo = new MutationObserver(() => this.requestUpdate());
164
+ this._mo.observe(this, { childList: true, characterData: true, subtree: true });
165
+ }
166
+ }
167
+ disconnectedCallback() {
168
+ super.disconnectedCallback();
169
+ this._mo?.disconnect();
170
+ this._mo = null;
171
+ }
172
+ };
173
+ __decorate([
174
+ property({ type: String })
175
+ ], XmButton.prototype, "variant", void 0);
176
+ __decorate([
177
+ property({ type: String })
178
+ ], XmButton.prototype, "size", void 0);
179
+ __decorate([
180
+ property({ type: Boolean, attribute: "icon-only" })
181
+ ], XmButton.prototype, "iconOnly", void 0);
182
+ __decorate([
183
+ property({ type: Boolean, attribute: "full-width" })
184
+ ], XmButton.prototype, "fullWidth", void 0);
185
+ __decorate([
186
+ property({ type: Boolean })
187
+ ], XmButton.prototype, "selected", void 0);
188
+ __decorate([
189
+ property({ type: String })
190
+ ], XmButton.prototype, "type", void 0);
191
+ __decorate([
192
+ property({ type: Boolean })
193
+ ], XmButton.prototype, "disabled", void 0);
194
+ __decorate([
195
+ property({ type: Boolean })
196
+ ], XmButton.prototype, "loading", void 0);
197
+ __decorate([
198
+ property({ type: String, attribute: "force-state" })
199
+ ], XmButton.prototype, "forceState", void 0);
200
+ XmButton = __decorate([
201
+ customElement("xm-button")
202
+ ], XmButton);
@@ -0,0 +1,99 @@
1
+ /* ============================================
2
+ <xm-card> — general-purpose containment surface.
3
+
4
+ Surface / ink (AD-13; ADR 0006): the CARD-STACK family — background
5
+ var(--md-sys-color-inverse-surface), ink var(--md-sys-color-inverse-on-surface).
6
+ The card tier is theme-following: a lifted dark surface in dark theme, white
7
+ in light; the ink follows (light on the dark card, dark on the white one).
8
+
9
+ Spec (DESIGN components.card): 1px outline-variant border, 14px medium
10
+ corner, level2 elevation (1px brightening top edge + soft 24px drop).
11
+ Header / footer split from the body by a 1px rule — not padding alone.
12
+ Header padding 22px 24px 14px; footer 14px 24px 18px.
13
+
14
+ BEM: block `card`; elements `__header` `__body` `__footer`
15
+ `__footer-content` `__action`; modifiers `--elevation-0..5`, `--xs|sm|md|lg`.
16
+ ============================================ */
17
+
18
+ .card {
19
+ display: flex;
20
+ flex-direction: column;
21
+ box-sizing: border-box;
22
+ background: var(--md-sys-color-inverse-surface);
23
+ color: var(--md-sys-color-inverse-on-surface);
24
+ border: 1px solid var(--md-sys-color-outline-variant);
25
+ border-radius: var(--md-sys-shape-corner-medium);
26
+ box-shadow: var(--md-sys-elevation-level2);
27
+ overflow: hidden;
28
+ }
29
+
30
+ /* ---------- Elevation ---------- */
31
+ .card--elevation-0 { box-shadow: var(--md-sys-elevation-level0); }
32
+ .card--elevation-1 { box-shadow: var(--md-sys-elevation-level1); }
33
+ .card--elevation-2 { box-shadow: var(--md-sys-elevation-level2); }
34
+ .card--elevation-3 { box-shadow: var(--md-sys-elevation-level3); }
35
+ .card--elevation-4 { box-shadow: var(--md-sys-elevation-level4); }
36
+ .card--elevation-5 { box-shadow: var(--md-sys-elevation-level5); }
37
+
38
+ /* ---------- Header ---------- */
39
+ .card__header {
40
+ display: flex;
41
+ align-items: center;
42
+ gap: var(--s-2);
43
+ padding: var(--s-5) var(--s-6) var(--s-3);
44
+ border-bottom: 1px solid var(--md-sys-color-outline-variant);
45
+ font-family: var(--md-sys-typescale-title-medium-font);
46
+ font-size: var(--md-sys-typescale-title-medium-size);
47
+ line-height: var(--md-sys-typescale-title-medium-line-height);
48
+ font-weight: var(--md-sys-typescale-title-medium-weight);
49
+ letter-spacing: var(--md-sys-typescale-title-medium-tracking);
50
+ color: var(--md-sys-color-inverse-on-surface);
51
+ }
52
+ .card__header.is-empty {
53
+ display: none;
54
+ }
55
+
56
+ /* ---------- Body ---------- */
57
+ .card__body {
58
+ flex: 1 1 auto;
59
+ padding: var(--s-5) var(--s-6);
60
+ font-family: var(--md-sys-typescale-body-medium-font);
61
+ font-size: var(--md-sys-typescale-body-medium-size);
62
+ line-height: var(--md-sys-typescale-body-medium-line-height);
63
+ letter-spacing: var(--md-sys-typescale-body-medium-tracking);
64
+ color: var(--md-sys-color-inverse-on-surface);
65
+ }
66
+
67
+ /* ---------- Footer ---------- */
68
+ .card__footer {
69
+ display: flex;
70
+ align-items: center;
71
+ gap: var(--s-3);
72
+ padding: var(--s-3) var(--s-6) var(--s-4);
73
+ border-top: 1px solid var(--md-sys-color-outline-variant);
74
+ }
75
+ .card__footer.is-empty {
76
+ display: none;
77
+ }
78
+ .card__footer-content {
79
+ flex: 1 1 auto;
80
+ min-width: 0;
81
+ font-size: var(--md-sys-typescale-body-small-size);
82
+ line-height: var(--md-sys-typescale-body-small-line-height);
83
+ color: var(--xm-color-inverse-on-surface-muted);
84
+ }
85
+ .card__footer-content.is-empty {
86
+ display: none;
87
+ }
88
+
89
+ /* ---------- Action (singular trailing region in the footer) ---------- */
90
+ .card__action {
91
+ display: inline-flex;
92
+ align-items: center;
93
+ gap: var(--s-2);
94
+ flex-shrink: 0;
95
+ margin-inline-start: auto;
96
+ }
97
+ .card__action.is-empty {
98
+ display: none;
99
+ }