@adia-ai/web-modules 0.8.35 → 0.8.37

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 (89) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +3 -3
  3. package/agent-admin/admin-roster/admin-roster.js +1 -1
  4. package/agent-admin/admin-settings/admin-settings.js +1 -1
  5. package/agent-admin/agent-admin/agent-admin.js +1 -1
  6. package/billing/billing-overview/billing-overview.a2ui.json +281 -0
  7. package/billing/billing-overview/billing-overview.class.js +838 -0
  8. package/billing/billing-overview/billing-overview.css +223 -0
  9. package/billing/billing-overview/billing-overview.js +16 -0
  10. package/billing/billing-overview/billing-overview.yaml +441 -0
  11. package/billing/index.js +21 -0
  12. package/billing/invoice-detail/invoice-detail.a2ui.json +276 -0
  13. package/billing/invoice-detail/invoice-detail.class.js +711 -0
  14. package/billing/invoice-detail/invoice-detail.css +305 -0
  15. package/billing/invoice-detail/invoice-detail.js +16 -0
  16. package/billing/invoice-detail/invoice-detail.yaml +411 -0
  17. package/billing/invoice-history/invoice-history.a2ui.json +315 -0
  18. package/billing/invoice-history/invoice-history.class.js +672 -0
  19. package/billing/invoice-history/invoice-history.css +130 -0
  20. package/billing/invoice-history/invoice-history.js +16 -0
  21. package/billing/invoice-history/invoice-history.yaml +398 -0
  22. package/billing/payment-method-form/payment-method-form.a2ui.json +310 -0
  23. package/billing/payment-method-form/payment-method-form.class.js +886 -0
  24. package/billing/payment-method-form/payment-method-form.css +149 -0
  25. package/billing/payment-method-form/payment-method-form.js +16 -0
  26. package/billing/payment-method-form/payment-method-form.yaml +430 -0
  27. package/billing/payment-method-list/payment-method-list.a2ui.json +283 -0
  28. package/billing/payment-method-list/payment-method-list.class.js +705 -0
  29. package/billing/payment-method-list/payment-method-list.css +128 -0
  30. package/billing/payment-method-list/payment-method-list.js +16 -0
  31. package/billing/payment-method-list/payment-method-list.yaml +355 -0
  32. package/billing/plan-picker/plan-picker.a2ui.json +349 -0
  33. package/billing/plan-picker/plan-picker.class.js +635 -0
  34. package/billing/plan-picker/plan-picker.css +227 -0
  35. package/billing/plan-picker/plan-picker.js +16 -0
  36. package/billing/plan-picker/plan-picker.yaml +453 -0
  37. package/chat/README.md +1 -1
  38. package/chat/chat-composer/chat-composer.js +2 -2
  39. package/chat/chat-empty/chat-empty.js +2 -2
  40. package/chat/chat-shell/chat-shell.js +4 -4
  41. package/chat/chat-sidebar/chat-sidebar.js +2 -2
  42. package/chat/chat-surfaces/chat-surfaces.js +2 -2
  43. package/chat/chat-thread/chat-thread.js +2 -2
  44. package/dashboard/dashboard-layout/dashboard-layout.a2ui.json +254 -0
  45. package/dashboard/dashboard-layout/dashboard-layout.css +190 -0
  46. package/dashboard/dashboard-layout/dashboard-layout.js +289 -0
  47. package/dashboard/dashboard-layout/dashboard-layout.yaml +374 -0
  48. package/dashboard/date-range-selector/date-range-selector.a2ui.json +236 -0
  49. package/dashboard/date-range-selector/date-range-selector.css +84 -0
  50. package/dashboard/date-range-selector/date-range-selector.js +686 -0
  51. package/dashboard/date-range-selector/date-range-selector.yaml +358 -0
  52. package/dashboard/index.js +14 -0
  53. package/dist/everything.min.js +1 -1
  54. package/editor/editor-canvas/editor-canvas.js +2 -2
  55. package/editor/editor-shell/editor-shell.js +2 -2
  56. package/editor/editor-sidebar/editor-sidebar.js +2 -2
  57. package/editor/editor-toolbar/editor-toolbar.js +2 -2
  58. package/feedback/confirm-dialog/confirm-dialog.a2ui.json +206 -0
  59. package/feedback/confirm-dialog/confirm-dialog.css +157 -0
  60. package/feedback/confirm-dialog/confirm-dialog.js +383 -0
  61. package/feedback/confirm-dialog/confirm-dialog.yaml +297 -0
  62. package/feedback/index.js +10 -0
  63. package/form/form-popover/form-popover.js +1 -1
  64. package/generative/index.d.ts +1 -1
  65. package/generative/index.js +2 -2
  66. package/onboarding/index.js +1 -0
  67. package/onboarding/onboarding-checklist/onboarding-checklist.a2ui.json +263 -0
  68. package/onboarding/onboarding-checklist/onboarding-checklist.css +157 -0
  69. package/onboarding/onboarding-checklist/onboarding-checklist.js +428 -0
  70. package/onboarding/onboarding-checklist/onboarding-checklist.yaml +236 -0
  71. package/package.json +99 -6
  72. package/runtime/a2ui-root/a2ui-root.js +3 -3
  73. package/runtime/gen-root/gen-root.js +5 -5
  74. package/settings/index.js +12 -0
  75. package/settings/integrations-page/integrations-page.a2ui.json +274 -0
  76. package/settings/integrations-page/integrations-page.css +177 -0
  77. package/settings/integrations-page/integrations-page.d.ts +77 -0
  78. package/settings/integrations-page/integrations-page.js +413 -0
  79. package/settings/integrations-page/integrations-page.yaml +317 -0
  80. package/settings/notification-preferences/notification-preferences.a2ui.json +245 -0
  81. package/settings/notification-preferences/notification-preferences.css +204 -0
  82. package/settings/notification-preferences/notification-preferences.js +622 -0
  83. package/settings/notification-preferences/notification-preferences.yaml +358 -0
  84. package/shell/admin-command/admin-command.js +2 -2
  85. package/shell/admin-shell/admin-shell.js +2 -2
  86. package/shell/admin-sidebar/admin-sidebar.js +2 -2
  87. package/shell/embed-shell/embed-shell.js +3 -3
  88. package/simple/simple-shell/simple-shell.js +1 -1
  89. package/theme/theme-panel/theme-panel.js +2 -2
@@ -0,0 +1,428 @@
1
+ /**
2
+ * <onboarding-checklist-ui
3
+ * storage-key="acme:onboarding:v1"
4
+ * items='[{"id":"profile","label":"Complete your profile","href":"/settings/profile"}, …]'>
5
+ * <button-ui slot="footer" variant="ghost" text="Hide for now"></button-ui>
6
+ * </onboarding-checklist-ui>
7
+ *
8
+ * Module-tier onboarding checklist — composes <progress-ui> + <list-ui> +
9
+ * <list-item-ui> + <check-ui> + <button-ui>. Data-driven (items prop),
10
+ * persistent (storage-key), auto-dismissing on completion.
11
+ *
12
+ * Per ADR-0027 — composites that programmatically create primitives do
13
+ * NOT auto-import those primitives. The consumer (app shell or page)
14
+ * must explicitly import <progress-ui>, <list-ui>, <check-ui>,
15
+ * <button-ui> in addition to this module.
16
+ */
17
+
18
+ import { UIElement, html } from '@adia-ai/web-components/core/element';
19
+
20
+ class UIOnboardingChecklist extends UIElement {
21
+ static properties = {
22
+ items: { type: Array, default: [] },
23
+ 'storage-key': { type: String, default: '', reflect: true },
24
+ storage: { type: String, default: 'local', reflect: true },
25
+ 'dismiss-on-complete':{ type: Boolean, default: false, reflect: true },
26
+ collapsible: { type: Boolean, default: true, reflect: true },
27
+ collapsed: { type: Boolean, default: false, reflect: true },
28
+ value: { type: Object, default: {} },
29
+ title: { type: String, default: 'Get started', reflect: true },
30
+ };
31
+
32
+ // Data-driven internal DOM; we render via tagged template literal so
33
+ // the child count can change as `items` changes.
34
+ static template = (host) => host._renderTemplate();
35
+
36
+ // ── lifecycle ────────────────────────────────────────────────
37
+
38
+ #headerId = `obc-h-${Math.random().toString(36).slice(2, 8)}`;
39
+ #liveId = `obc-l-${Math.random().toString(36).slice(2, 8)}`;
40
+ #valueHostControlled = false;
41
+ #lastEmittedComplete = false;
42
+ #boundChange = null;
43
+ #boundClick = null;
44
+
45
+ // ── attribute parsing ────────────────────────────────────────
46
+ //
47
+ // `items` + `value` accept JSON strings via the HTML attribute path
48
+ // (the canonical authoring shape for a2ui-generated markup). The
49
+ // default UIElement `attributeChangedCallback` would assign the
50
+ // string verbatim to the property — breaking the array/object shape.
51
+ // Override to parse JSON before assignment for those two props.
52
+
53
+ attributeChangedCallback(name, oldVal, newVal) {
54
+ if (name === 'items' || name === 'value') {
55
+ if (newVal == null) {
56
+ this[name] = name === 'items' ? [] : {};
57
+ } else {
58
+ try {
59
+ const parsed = JSON.parse(newVal);
60
+ this[name] = parsed;
61
+ } catch {
62
+ this[name] = name === 'items' ? [] : {};
63
+ }
64
+ }
65
+ return;
66
+ }
67
+ super.attributeChangedCallback(name, oldVal, newVal);
68
+ }
69
+
70
+ connected() {
71
+ this.setAttribute('role', 'region');
72
+ this.setAttribute('aria-labelledby', this.#headerId);
73
+
74
+ // Late-parse JSON attributes that were present at construct time
75
+ // (UIElement reads the attribute as a String, so we re-parse here).
76
+ if (this.hasAttribute('items') && !Array.isArray(this.items)) {
77
+ try { this.items = JSON.parse(this.getAttribute('items')); }
78
+ catch { this.items = []; }
79
+ }
80
+ if (this.hasAttribute('value') && (typeof this.value !== 'object' || Array.isArray(this.value))) {
81
+ try { this.value = JSON.parse(this.getAttribute('value')); }
82
+ catch { this.value = {}; }
83
+ }
84
+
85
+ // Determine whether host is supplying `value` (host-controlled mode).
86
+ // If host set the property before connect, treat as host-controlled
87
+ // and skip storage hydration.
88
+ this.#valueHostControlled = this.value && Object.keys(this.value).length > 0;
89
+ if (!this.#valueHostControlled) {
90
+ const hydrated = this.#readStorage();
91
+ if (hydrated) this.value = hydrated;
92
+ }
93
+
94
+ this.#boundChange = this.#onChange.bind(this);
95
+ this.#boundClick = this.#onClick.bind(this);
96
+ this.addEventListener('change', this.#boundChange);
97
+ this.addEventListener('click', this.#boundClick);
98
+
99
+ this.#lastEmittedComplete = this.#isAllDone();
100
+ this.#syncCompletionAttrs();
101
+ }
102
+
103
+ disconnected() {
104
+ if (this.#boundChange) this.removeEventListener('change', this.#boundChange);
105
+ if (this.#boundClick) this.removeEventListener('click', this.#boundClick);
106
+ this.#boundChange = null;
107
+ this.#boundClick = null;
108
+
109
+ // Persist final state on detach if storage-key is set.
110
+ this.#writeStorage();
111
+ }
112
+
113
+ // ── render ───────────────────────────────────────────────────
114
+
115
+ render() {
116
+ // Sync per-item completion attributes after each stamp (the
117
+ // template engine handles primary structure; this keeps reflected
118
+ // attrs + aria correct after items/value changes).
119
+ this.#syncCompletionAttrs();
120
+ this.#announceProgress();
121
+ this.#maybeEmitComplete();
122
+ }
123
+
124
+ _renderTemplate() {
125
+ const safeItems = Array.isArray(this.items) ? this.items : [];
126
+ const { done, total, percent } = this.#progressSnapshot();
127
+ const collapseLabel = this.collapsed ? 'Expand' : 'Collapse';
128
+ const collapseIcon = this.collapsed ? 'chevron-down' : 'chevron-up';
129
+ const showCollapse = !!this.collapsible;
130
+ const headerTitle = this.title || 'Get started';
131
+ const allDone = total > 0 && done === total;
132
+
133
+ // The body content (progress + items + footer) is hidden via CSS
134
+ // when [collapsed] is set; we keep the nodes mounted so collapsing
135
+ // is a paint-only operation and focus inside the item list is
136
+ // preserved across collapse/expand toggles.
137
+
138
+ return html`
139
+ <div data-part="header">
140
+ <div data-part="title" id=${this.#headerId}>${headerTitle}</div>
141
+ ${showCollapse
142
+ ? html`<button-ui
143
+ data-part="collapse-toggle"
144
+ variant="ghost"
145
+ size="sm"
146
+ .icon=${collapseIcon}
147
+ aria-expanded=${String(!this.collapsed)}
148
+ aria-controls=${this.#liveId}
149
+ aria-label=${collapseLabel}
150
+ @click=${(e) => this.#onCollapseToggle(e)}
151
+ ></button-ui>`
152
+ : null}
153
+ </div>
154
+ <progress-ui
155
+ data-part="progress"
156
+ .value=${percent}
157
+ aria-label="Onboarding progress"
158
+ ></progress-ui>
159
+ <div
160
+ data-part="meter"
161
+ id=${this.#liveId}
162
+ aria-live="polite"
163
+ aria-atomic="true"
164
+ >${total > 0
165
+ ? (allDone ? 'All steps complete' : `${done} of ${total} steps complete`)
166
+ : ''}</div>
167
+ <list-ui data-part="items">
168
+ ${safeItems.map((item) => this.#renderItem(item))}
169
+ </list-ui>
170
+ ${allDone && !this['dismiss-on-complete']
171
+ ? html`<div data-part="complete">
172
+ <slot name="complete"></slot>
173
+ <button-ui
174
+ data-part="dismiss"
175
+ variant="primary"
176
+ size="sm"
177
+ text="Done"
178
+ @click=${() => this.#dismiss('user')}
179
+ ></button-ui>
180
+ </div>`
181
+ : null}
182
+ <div data-part="footer"><slot name="footer"></slot></div>
183
+ `;
184
+ }
185
+
186
+ #renderItem(item) {
187
+ const id = String(item?.id ?? '');
188
+ const label = String(item?.label ?? '');
189
+ const description = String(item?.description ?? '');
190
+ const href = String(item?.href ?? '');
191
+ const done = this.#isItemDone(item);
192
+ return html`
193
+ <list-item-ui data-item-id=${id} data-done=${String(done)}>
194
+ <check-ui
195
+ slot="icon"
196
+ data-step-check
197
+ data-step-id=${id}
198
+ .checked=${done}
199
+ aria-label=${label}
200
+ ></check-ui>
201
+ <span slot="text">${label}</span>
202
+ ${description
203
+ ? html`<span slot="description">${description}</span>`
204
+ : null}
205
+ ${href
206
+ ? html`<button-ui
207
+ slot="action"
208
+ data-step-action
209
+ data-step-id=${id}
210
+ data-href=${href}
211
+ variant="ghost"
212
+ icon-trailing="arrow-right"
213
+ text="Open"
214
+ ></button-ui>`
215
+ : null}
216
+ </list-item-ui>
217
+ `;
218
+ }
219
+
220
+ // ── event delegation ─────────────────────────────────────────
221
+
222
+ #onChange(e) {
223
+ const target = e.target;
224
+ if (!target || !target.closest) return;
225
+ const check = target.closest('[data-step-check]');
226
+ if (!check || !this.contains(check)) return;
227
+
228
+ const id = check.getAttribute('data-step-id') || '';
229
+ if (!id) return;
230
+
231
+ const wasDone = this.#isItemDoneById(id);
232
+ const nowDone = !!check.checked;
233
+ if (wasDone === nowDone) return;
234
+
235
+ this.#setItemDone(id, nowDone, { emit: nowDone });
236
+ }
237
+
238
+ #onClick(e) {
239
+ const target = e.target;
240
+ if (!target || !target.closest) return;
241
+ const action = target.closest('[data-step-action]');
242
+ if (!action || !this.contains(action)) return;
243
+
244
+ const id = action.getAttribute('data-step-id') || '';
245
+ const href = action.getAttribute('data-href') || '';
246
+ if (!id) return;
247
+
248
+ this.dispatchEvent(new CustomEvent('onboarding-step-action', {
249
+ bubbles: true,
250
+ detail: { id, href },
251
+ }));
252
+ // Per spec OD-001 — clicking the CTA does NOT auto-complete.
253
+ }
254
+
255
+ #onCollapseToggle(e) {
256
+ e?.stopPropagation?.();
257
+ const next = !this.collapsed;
258
+ this.collapsed = next;
259
+ if (next) {
260
+ this.dispatchEvent(new CustomEvent('onboarding-dismiss', {
261
+ bubbles: true,
262
+ detail: { reason: 'collapsed' },
263
+ }));
264
+ }
265
+ }
266
+
267
+ // ── state helpers ────────────────────────────────────────────
268
+
269
+ #isItemDone(item) {
270
+ if (!item) return false;
271
+ const id = item.id;
272
+ if (id != null && Object.prototype.hasOwnProperty.call(this.value || {}, id)) {
273
+ return !!this.value[id];
274
+ }
275
+ return !!item.done;
276
+ }
277
+
278
+ #isItemDoneById(id) {
279
+ const safeItems = Array.isArray(this.items) ? this.items : [];
280
+ const it = safeItems.find((i) => i && String(i.id) === String(id));
281
+ return this.#isItemDone(it);
282
+ }
283
+
284
+ #setItemDone(id, done, { emit = true } = {}) {
285
+ const next = { ...(this.value || {}), [id]: !!done };
286
+ this.value = next;
287
+ this.#writeStorage();
288
+ if (emit && done) {
289
+ this.dispatchEvent(new CustomEvent('onboarding-step-complete', {
290
+ bubbles: true,
291
+ detail: { id, value: next },
292
+ }));
293
+ }
294
+ }
295
+
296
+ #progressSnapshot() {
297
+ const safeItems = Array.isArray(this.items) ? this.items : [];
298
+ const total = safeItems.length;
299
+ let done = 0;
300
+ for (const it of safeItems) if (this.#isItemDone(it)) done++;
301
+ const percent = total === 0 ? 0 : Math.round((done / total) * 100);
302
+ return { done, total, percent };
303
+ }
304
+
305
+ #isAllDone() {
306
+ const { done, total } = this.#progressSnapshot();
307
+ return total > 0 && done === total;
308
+ }
309
+
310
+ #syncCompletionAttrs() {
311
+ if (this.#isAllDone()) this.setAttribute('data-complete', '');
312
+ else this.removeAttribute('data-complete');
313
+ }
314
+
315
+ #maybeEmitComplete() {
316
+ const isDone = this.#isAllDone();
317
+ if (isDone && !this.#lastEmittedComplete) {
318
+ this.#lastEmittedComplete = true;
319
+ this.dispatchEvent(new CustomEvent('onboarding-complete', {
320
+ bubbles: true,
321
+ detail: { value: this.value },
322
+ }));
323
+ // dismiss-on-complete now defaults FALSE — by default the widget stays
324
+ // and surfaces a "Dismiss" button so the user controls when it goes away
325
+ // (per operator: a button rather than the widget silently vanishing).
326
+ // Auto-dismiss is opt-in via the [dismiss-on-complete] attribute.
327
+ if (this['dismiss-on-complete']) this.#dismiss('complete');
328
+ } else if (!isDone) {
329
+ this.#lastEmittedComplete = false;
330
+ }
331
+ }
332
+
333
+ /** Hide the checklist + announce dismissal. Triggered by the user's Dismiss
334
+ * button on completion, or automatically when [dismiss-on-complete] is set. */
335
+ #dismiss(reason = 'user') {
336
+ this.setAttribute('hidden', '');
337
+ this.dispatchEvent(new CustomEvent('onboarding-dismiss', {
338
+ bubbles: true,
339
+ detail: { reason },
340
+ }));
341
+ }
342
+
343
+ #announceProgress() {
344
+ // The live region's textContent is set declaratively in the
345
+ // template; nothing else to do here — the polite live region
346
+ // re-reads on text change.
347
+ }
348
+
349
+ // ── storage ──────────────────────────────────────────────────
350
+
351
+ #storageArea() {
352
+ if (this.#valueHostControlled) return null;
353
+ if (this.storage === 'none') return null;
354
+ if (!this['storage-key']) return null;
355
+ if (typeof window === 'undefined') return null;
356
+ try {
357
+ return this.storage === 'session' ? window.sessionStorage : window.localStorage;
358
+ } catch {
359
+ return null;
360
+ }
361
+ }
362
+
363
+ #readStorage() {
364
+ const area = this.#storageArea();
365
+ if (!area) return null;
366
+ try {
367
+ const raw = area.getItem(this['storage-key']);
368
+ if (!raw) return null;
369
+ const parsed = JSON.parse(raw);
370
+ return (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) ? parsed : null;
371
+ } catch {
372
+ return null;
373
+ }
374
+ }
375
+
376
+ #writeStorage() {
377
+ const area = this.#storageArea();
378
+ if (!area) return;
379
+ try {
380
+ area.setItem(this['storage-key'], JSON.stringify(this.value || {}));
381
+ } catch {
382
+ /* quota / privacy mode — silently ignore */
383
+ }
384
+ }
385
+
386
+ // ── public methods ───────────────────────────────────────────
387
+
388
+ /**
389
+ * Mark an item as done (idempotent). Fires `onboarding-step-complete`
390
+ * if the item transitions from undone → done.
391
+ */
392
+ complete(id) {
393
+ if (id == null) return;
394
+ const key = String(id);
395
+ const was = this.#isItemDoneById(key);
396
+ if (was) return;
397
+ this.#setItemDone(key, true, { emit: true });
398
+ }
399
+
400
+ /**
401
+ * Reset one item (or all items, when id is omitted) to undone. Wipes
402
+ * the storage entry when resetting all.
403
+ */
404
+ reset(id) {
405
+ if (id == null) {
406
+ this.value = {};
407
+ const area = this.#storageArea();
408
+ try { area?.removeItem(this['storage-key']); } catch { /* ignore */ }
409
+ this.#lastEmittedComplete = false;
410
+ this.removeAttribute('hidden');
411
+ return;
412
+ }
413
+ const key = String(id);
414
+ if (!Object.prototype.hasOwnProperty.call(this.value || {}, key)) return;
415
+ this.#setItemDone(key, false, { emit: false });
416
+ }
417
+
418
+ /** Snapshot of progress numbers. */
419
+ progress() {
420
+ return this.#progressSnapshot();
421
+ }
422
+ }
423
+
424
+ if (!customElements.get('onboarding-checklist-ui')) {
425
+ customElements.define('onboarding-checklist-ui', UIOnboardingChecklist);
426
+ }
427
+
428
+ export { UIOnboardingChecklist };
@@ -0,0 +1,236 @@
1
+ # Edit this file; run `npm run build:components` to regenerate a2ui.json.
2
+ $schema: ../../../../scripts/schemas/component.yaml.schema.json
3
+ name: UIOnboardingChecklist
4
+ tag: onboarding-checklist-ui
5
+ status: stable
6
+ component: OnboardingChecklist
7
+ category: feedback
8
+ version: 1
9
+ description: |
10
+ Module-tier onboarding checklist — a progressive setup-step widget
11
+ ("Getting started" card seen in Stripe / Linear / Vercel dashboards).
12
+ Composes <progress-ui> (top fill bar), <list-ui> + <list-item-ui>
13
+ (the step rows), <check-ui> (per-item completion checkbox), and
14
+ optional <button-ui> (per-item action CTA).
15
+
16
+ Owns the data model (items: array of {id, label, description, href,
17
+ done}), the persistence contract (storage-key writes a JSON blob to
18
+ localStorage / sessionStorage), the auto-computed progress display,
19
+ and the dismiss-on-complete behavior. Items are data, not slotted
20
+ markup — host wiring uses stable per-item ids, not DOM positions.
21
+
22
+ Use for the "card of optional setup tasks" case; use <step-progress-ui>
23
+ for horizontal multi-screen wizards where the user is locked into
24
+ linear progression.
25
+
26
+ props:
27
+ items:
28
+ description: Step list — each item is { id, label, description?, href?, done? }. Required, non-empty.
29
+ type: array
30
+ default: []
31
+ required: true
32
+ dynamic: true
33
+ storage-key:
34
+ description: localStorage key for per-item completion persistence. Empty string disables storage (ephemeral).
35
+ type: string
36
+ default: ""
37
+ reflect: true
38
+ storage:
39
+ description: Persistence backend when storage-key is set.
40
+ type: string
41
+ default: local
42
+ enum: [local, session, none]
43
+ reflect: true
44
+ dismiss-on-complete:
45
+ description: Auto-hide the widget when all items are done.
46
+ type: boolean
47
+ default: true
48
+ reflect: true
49
+ collapsible:
50
+ description: Show a top-right collapse toggle.
51
+ type: boolean
52
+ default: true
53
+ reflect: true
54
+ collapsed:
55
+ description: Initial / current collapsed state — hides the item list, keeps progress + header visible.
56
+ type: boolean
57
+ default: false
58
+ reflect: true
59
+ dynamic: true
60
+ value:
61
+ description: "Completion map mapping item id to boolean. When set, host owns state and storage attributes are ignored."
62
+ type: object
63
+ default: {}
64
+ dynamic: true
65
+ title:
66
+ description: Header label above the progress bar. Defaults to "Get started".
67
+ type: string
68
+ default: "Get started"
69
+ reflect: true
70
+
71
+ events:
72
+ onboarding-step-complete:
73
+ description: Fired when a step transitions from undone to done.
74
+ detail:
75
+ id:
76
+ type: string
77
+ description: The id of the step that just completed.
78
+ value:
79
+ type: object
80
+ description: Snapshot of the full completion map after the change.
81
+ onboarding-step-action:
82
+ description: Fired when the user clicks an item's action CTA.
83
+ detail:
84
+ id:
85
+ type: string
86
+ description: The id of the step whose CTA was clicked.
87
+ href:
88
+ type: string
89
+ description: The href of the CTA (may be empty when host wires the click elsewhere).
90
+ onboarding-complete:
91
+ description: Fired when all items become done.
92
+ detail:
93
+ value:
94
+ type: object
95
+ description: The completion map at the moment of full completion.
96
+ onboarding-dismiss:
97
+ description: Fired when the widget collapses or auto-dismisses on completion.
98
+ detail:
99
+ reason:
100
+ type: string
101
+ description: Why the widget was dismissed.
102
+ enum: [collapsed, complete]
103
+
104
+ slots:
105
+ header:
106
+ description: Custom title region above the progress bar. Overrides the default [title] attribute label.
107
+ description:
108
+ description: >-
109
+ Optional subtitle / supporting prose rendered beneath the header.
110
+ Renders at body-subtle typography. Use for richer markup than the
111
+ plain [description] attribute string.
112
+ footer:
113
+ description: Optional footer content (e.g. "Skip for now" link / button).
114
+ complete:
115
+ description: Content shown when all items are done and dismiss-on-complete is false.
116
+
117
+ states:
118
+ - name: idle
119
+ description: Some items incomplete; widget visible and expanded.
120
+ - name: collapsed
121
+ description: Item list hidden; header + progress visible.
122
+ attribute: collapsed
123
+ - name: complete
124
+ description: All items done. Behavior depends on dismiss-on-complete.
125
+ attribute: data-complete
126
+ - name: dismissed
127
+ description: Widget removed from layout after collapse or auto-dismiss.
128
+ attribute: hidden
129
+
130
+ traits: []
131
+
132
+ tokens:
133
+ --onboarding-checklist-bg:
134
+ description: Card background. Defaults to elevated canvas tier.
135
+ default: var(--a-canvas-1)
136
+ --onboarding-checklist-fg:
137
+ description: Primary foreground color.
138
+ default: var(--a-fg)
139
+ --onboarding-checklist-fg-muted:
140
+ description: Secondary foreground color for descriptions + meta.
141
+ default: var(--a-fg-muted)
142
+ --onboarding-checklist-radius:
143
+ description: Outer card radius.
144
+ default: var(--a-radius-lg)
145
+ --onboarding-checklist-px:
146
+ description: Inline (left/right) padding.
147
+ default: var(--a-space-4)
148
+ --onboarding-checklist-py:
149
+ description: Block (top/bottom) padding.
150
+ default: var(--a-space-4)
151
+ --onboarding-checklist-gap:
152
+ description: Gap between header / progress / items / footer.
153
+ default: var(--a-space-3)
154
+ --onboarding-checklist-progress-fill:
155
+ description: Progress-bar fill color (forwarded to inner <progress-ui> via --progress-fill).
156
+ default: var(--md-sys-color-primary)
157
+ --onboarding-checklist-shadow:
158
+ description: Card elevation.
159
+ default: var(--a-shadow-sm)
160
+ --onboarding-checklist-border:
161
+ description: Outer border.
162
+ default: 1px solid var(--a-border-subtle)
163
+
164
+ a2ui:
165
+ rules:
166
+ - rule: 'OnboardingChecklist MUST have a non-empty `items` array. Empty items renders an empty card.'
167
+ reason: 'Without steps the widget has nothing to display.'
168
+ - rule: 'Each item MUST have a unique `id` within the list — used as the storage key + event detail.'
169
+ reason: 'Stable per-item identifier; completion cannot be persisted otherwise.'
170
+ - rule: '`storageKey` SHOULD be namespaced per product (e.g. `myapp:onboarding:v1`) to avoid cross-app collisions.'
171
+ reason: 'localStorage is a shared global key-space across the origin.'
172
+ - rule: 'Items SHOULD number 3 to 8. Fewer than 3 is not worth a checklist; more than 8 should split into stages.'
173
+ reason: 'Empirical product-design heuristic — readable progress, no overwhelm.'
174
+ - rule: 'Use for the "card of optional setup tasks" case. For horizontal multi-screen wizards use <step-progress-ui>; for generic to-do lists compose <list-ui> + <check-ui> directly.'
175
+ reason: 'Onboarding-specific semantics (dismiss-on-complete, persistent storage) leak into generic lists.'
176
+ - rule: 'Do not nest two <onboarding-checklist-ui> with the same `storageKey` — they will clobber each other.'
177
+ reason: 'Single storage slot per key.'
178
+
179
+ anti_patterns:
180
+ - wrong: |
181
+ { "component": "OnboardingChecklist", "items": [] }
182
+ why: |
183
+ Empty checklist — renders an empty card.
184
+ fix: |
185
+ { "component": "OnboardingChecklist", "items": [{ "id": "x", "label": "Step 1" }] }
186
+ - wrong: |
187
+ { "component": "OnboardingChecklist", "items": [
188
+ { "label": "Step A" }, { "label": "Step B" }
189
+ ] }
190
+ why: |
191
+ Items missing `id`. The component cannot persist completion
192
+ without stable per-item identifiers.
193
+ fix: |
194
+ { "component": "OnboardingChecklist", "items": [
195
+ { "id": "a", "label": "Step A" }, { "id": "b", "label": "Step B" }
196
+ ] }
197
+
198
+ examples:
199
+ - name: basic-checklist
200
+ description: Three-step dashboard setup card.
201
+ a2ui: |
202
+ [
203
+ {
204
+ "id": "onb-1",
205
+ "component": "OnboardingChecklist",
206
+ "storageKey": "acme:onboarding:v1",
207
+ "items": [
208
+ { "id": "profile", "label": "Complete your profile", "description": "Add a photo and bio", "href": "/settings/profile" },
209
+ { "id": "team", "label": "Invite your team", "description": "Add at least one teammate", "href": "/settings/team" },
210
+ { "id": "connect", "label": "Connect a data source", "description": "Hook up the first integration", "href": "/integrations" }
211
+ ]
212
+ }
213
+ ]
214
+
215
+ keywords:
216
+ - onboarding
217
+ - checklist
218
+ - getting-started
219
+ - setup
220
+ - steps
221
+ - progress
222
+ - getting started
223
+
224
+ synonyms:
225
+ onboarding: [getting-started, setup, welcome, kickoff]
226
+ checklist: [todo, task-list, steps]
227
+ getting-started: [onboarding, setup, welcome]
228
+ setup: [onboarding, getting-started, configuration]
229
+
230
+ related:
231
+ - Check
232
+ - List
233
+ - ListItem
234
+ - Progress
235
+ - Button
236
+ - StepProgress