@adia-ai/a2ui-corpus 0.6.33 → 0.6.35

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 (245) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/catalog-a2ui_0_9.json +9706 -3544
  3. package/catalog-a2ui_0_9_rules.txt +231 -4
  4. package/chunk-embeddings.json +1 -1
  5. package/chunks/_index.json +32 -4
  6. package/chunks/accordion-settings.json +1 -1
  7. package/chunks/agent-activity-feed.json +1 -1
  8. package/chunks/agent-canvas-shell.json +1 -1
  9. package/chunks/agent-command-palette-search.json +1 -1
  10. package/chunks/agent-reasoning-collapsed.json +1 -1
  11. package/chunks/agent-tool-call-result.json +1 -1
  12. package/chunks/agent-trace-timeline.json +1 -1
  13. package/chunks/ai-streaming-response.json +1 -1
  14. package/chunks/alert-banner.json +1 -1
  15. package/chunks/auth-account-deleted-actions.json +1 -1
  16. package/chunks/auth-account-deleted.json +1 -1
  17. package/chunks/auth-card-content.json +1 -1
  18. package/chunks/auth-card-header.json +1 -1
  19. package/chunks/auth-email-entry.json +1 -1
  20. package/chunks/auth-email-verify-confirm.json +1 -1
  21. package/chunks/auth-email-verify.json +1 -1
  22. package/chunks/auth-forbidden-actions.json +1 -1
  23. package/chunks/auth-forbidden.json +1 -1
  24. package/chunks/auth-invite-actions.json +1 -1
  25. package/chunks/auth-invite-team-card.json +1 -1
  26. package/chunks/auth-link-expired-form.json +1 -1
  27. package/chunks/auth-link-expired.json +1 -1
  28. package/chunks/auth-locked-account.json +1 -1
  29. package/chunks/auth-locked-recovery-options.json +1 -1
  30. package/chunks/auth-mfa-enrollment-submit.json +1 -1
  31. package/chunks/auth-mfa-enrollment.json +1 -1
  32. package/chunks/auth-mfa-fallback-actions.json +1 -1
  33. package/chunks/auth-mfa-recovery.json +1 -1
  34. package/chunks/auth-new-password-form.json +1 -1
  35. package/chunks/auth-new-password.json +1 -1
  36. package/chunks/auth-oauth-fallback-actions.json +1 -1
  37. package/chunks/auth-oauth-interstitial.json +1 -1
  38. package/chunks/auth-password-challenge.json +1 -1
  39. package/chunks/auth-password-reset-form.json +1 -1
  40. package/chunks/auth-password-reset.json +1 -1
  41. package/chunks/auth-profile-form.json +1 -1
  42. package/chunks/auth-profile-setup.json +1 -1
  43. package/chunks/auth-reset-sent.json +1 -1
  44. package/chunks/auth-session-expired-actions.json +1 -1
  45. package/chunks/auth-session-expired.json +1 -1
  46. package/chunks/auth-signin-card-email.json +1 -1
  47. package/chunks/auth-signin-card-magic-link.json +1 -1
  48. package/chunks/auth-signin-card-mfa.json +1 -1
  49. package/chunks/auth-signin-card-otp.json +1 -1
  50. package/chunks/auth-signin-card-password.json +1 -1
  51. package/chunks/auth-signin-card-recovery.json +1 -1
  52. package/chunks/auth-signout-actions.json +1 -1
  53. package/chunks/auth-signout.json +1 -1
  54. package/chunks/auth-signup-email-entry.json +1 -1
  55. package/chunks/auth-signup-entry.json +1 -1
  56. package/chunks/auth-signup-social-auths.json +1 -1
  57. package/chunks/auth-signup-verify.json +1 -1
  58. package/chunks/auth-social-auths.json +1 -1
  59. package/chunks/auth-sso-providers.json +1 -1
  60. package/chunks/auth-sso-required.json +1 -1
  61. package/chunks/auth-team-invite.json +1 -1
  62. package/chunks/avatar-group-overflow.json +1 -1
  63. package/chunks/breadcrumb-nav.json +1 -1
  64. package/chunks/calendar-month-view.json +1 -1
  65. package/chunks/card-header-with-description.json +1 -1
  66. package/chunks/chart-with-filter-pills.json +1 -1
  67. package/chunks/chat-page-shell.json +1 -1
  68. package/chunks/chat-streaming-surface.json +1 -1
  69. package/chunks/color-picker-swatches.json +1 -1
  70. package/chunks/command-palette.json +1 -1
  71. package/chunks/commerce-pricing-tiers.json +1 -1
  72. package/chunks/comparison-table.json +1 -1
  73. package/chunks/conversion-funnel-6step.json +1 -1
  74. package/chunks/dashboard-acquisition-panel.json +1 -1
  75. package/chunks/dashboard-admin-page.json +1 -1
  76. package/chunks/dashboard-audience-kpis.json +1 -1
  77. package/chunks/dashboard-audience-panel.json +1 -1
  78. package/chunks/dashboard-behavior-panel.json +1 -1
  79. package/chunks/dashboard-chart-recent.json +1 -1
  80. package/chunks/dashboard-cohort-retention.json +1 -1
  81. package/chunks/dashboard-conversion-panel.json +1 -1
  82. package/chunks/dashboard-country-list.json +1 -1
  83. package/chunks/dashboard-filter-bar.json +1 -1
  84. package/chunks/dashboard-funnel.json +1 -1
  85. package/chunks/dashboard-kpi-grid.json +1 -1
  86. package/chunks/dashboard-notifications-feed.json +1 -1
  87. package/chunks/dashboard-notifications-panel.json +1 -1
  88. package/chunks/dashboard-overview-panel.json +1 -1
  89. package/chunks/dashboard-page-header.json +1 -1
  90. package/chunks/dashboard-pages-table.json +1 -1
  91. package/chunks/dashboard-quick-actions.json +1 -1
  92. package/chunks/dashboard-reports-panel.json +1 -1
  93. package/chunks/dashboard-reports-table.json +1 -1
  94. package/chunks/dashboard-spark-cards.json +1 -1
  95. package/chunks/dashboard-storage-card.json +1 -1
  96. package/chunks/dashboard-tabs.json +1 -1
  97. package/chunks/dashboard-team-actions-storage.json +1 -1
  98. package/chunks/dashboard-team-list.json +1 -1
  99. package/chunks/dashboard-traffic-channels.json +1 -1
  100. package/chunks/dashboard-transactions-table.json +1 -1
  101. package/chunks/date-time-picker-form.json +1 -1
  102. package/chunks/destructive-confirm-modal.json +1 -1
  103. package/chunks/divider-text-label.json +1 -1
  104. package/chunks/doc-editor-shell.json +1 -1
  105. package/chunks/drawer-2fa-key.json +1 -1
  106. package/chunks/drawer-2fa-sms.json +1 -1
  107. package/chunks/drawer-2fa-totp.json +1 -1
  108. package/chunks/drawer-cancel-sub.json +1 -1
  109. package/chunks/drawer-change-plan.json +1 -1
  110. package/chunks/drawer-custom-roles.json +1 -1
  111. package/chunks/drawer-data-start.json +1 -1
  112. package/chunks/drawer-delete-account.json +1 -1
  113. package/chunks/drawer-delete-workspace.json +1 -1
  114. package/chunks/drawer-discord.json +1 -1
  115. package/chunks/drawer-figma.json +1 -1
  116. package/chunks/drawer-first-dashboard.json +1 -1
  117. package/chunks/drawer-gcal.json +1 -1
  118. package/chunks/drawer-github.json +1 -1
  119. package/chunks/drawer-invite.json +1 -1
  120. package/chunks/drawer-payment-method.json +1 -1
  121. package/chunks/drawer-report.json +1 -1
  122. package/chunks/drawer-revoke-session.json +1 -1
  123. package/chunks/drawer-role.json +1 -1
  124. package/chunks/drawer-slack.json +1 -1
  125. package/chunks/drawer-smtp.json +1 -1
  126. package/chunks/drawer-source.json +1 -1
  127. package/chunks/drawer-transaction.json +1 -1
  128. package/chunks/editor-code-pane.json +1 -1
  129. package/chunks/editor-page-shell.json +1 -1
  130. package/chunks/editor-preview-pane.json +1 -1
  131. package/chunks/empty-state-danger.json +1 -1
  132. package/chunks/empty-state-minimal.json +1 -1
  133. package/chunks/empty-state-warning.json +1 -1
  134. package/chunks/empty-state.json +1 -1
  135. package/chunks/error-404-actions.json +1 -1
  136. package/chunks/error-404.json +1 -1
  137. package/chunks/error-500-actions.json +1 -1
  138. package/chunks/error-500.json +1 -1
  139. package/chunks/error-maintenance-actions.json +1 -1
  140. package/chunks/error-maintenance.json +1 -1
  141. package/chunks/error-page-shell.json +1 -1
  142. package/chunks/faq-accordion.json +1 -1
  143. package/chunks/file-upload-dnd.json +1 -1
  144. package/chunks/footer-multi-column.json +1 -1
  145. package/chunks/footer-primary-only.json +1 -1
  146. package/chunks/form-page-shell.json +1 -1
  147. package/chunks/gallery-page-shell.json +1 -1
  148. package/chunks/hero-section-split.json +1 -1
  149. package/chunks/icon-text-row.json +1 -1
  150. package/chunks/image-carousel.json +1 -1
  151. package/chunks/image-tile.json +1 -1
  152. package/chunks/image-upload-preview.json +1 -1
  153. package/chunks/inventory-list-stock.json +1 -1
  154. package/chunks/kanban-board-3col.json +1 -1
  155. package/chunks/kanban-page-shell.json +1 -1
  156. package/chunks/kbd-shortcuts.json +1 -1
  157. package/chunks/labeled-textarea.json +1 -1
  158. package/chunks/leaderboard-table.json +1 -1
  159. package/chunks/linked-record-row.json +1 -1
  160. package/chunks/marketing-hero-cta.json +1 -1
  161. package/chunks/marketing-page-shell.json +1 -1
  162. package/chunks/masonry-gallery.json +1 -1
  163. package/chunks/member-edit-drawer.json +1 -1
  164. package/chunks/metadata-description-list.json +1 -1
  165. package/chunks/multi-step-wizard.json +1 -1
  166. package/chunks/notification-toast-row.json +1 -1
  167. package/chunks/onb-completion.json +1 -1
  168. package/chunks/onb-extension-install.json +1 -1
  169. package/chunks/onb-hero-welcome.json +1 -1
  170. package/chunks/onb-import-picker.json +1 -1
  171. package/chunks/onb-mobile-handoff.json +1 -1
  172. package/chunks/onb-notification-prefs.json +1 -1
  173. package/chunks/onb-persona-picker.json +1 -1
  174. package/chunks/onb-provider-tiles.json +1 -1
  175. package/chunks/onb-settings-review.json +1 -1
  176. package/chunks/onb-step-footer.json +1 -1
  177. package/chunks/onb-step-header.json +1 -1
  178. package/chunks/onb-step-progress.json +1 -1
  179. package/chunks/onb-step-shell.json +1 -1
  180. package/chunks/onb-story-pane.json +1 -1
  181. package/chunks/onb-tutorial-steps.json +1 -1
  182. package/chunks/pagination-controls.json +1 -1
  183. package/chunks/playground-a2ui.json +1 -1
  184. package/chunks/playground-app-shell.json +1 -1
  185. package/chunks/playground-chat.json +1 -1
  186. package/chunks/playground-construct-canvas.json +1 -1
  187. package/chunks/playground-css-channel.json +355 -0
  188. package/chunks/playground-gen-ui.json +1 -1
  189. package/chunks/playground-render-preview.json +1 -1
  190. package/chunks/playground-streams-bridge.json +1 -1
  191. package/chunks/playground-table-toolbar.json +1 -1
  192. package/chunks/popover-with-content.json +1 -1
  193. package/chunks/progress-tracker-milestones.json +1 -1
  194. package/chunks/real-time-metrics-dashboard.json +1 -1
  195. package/chunks/reg-address-form.json +1 -1
  196. package/chunks/reg-billing-card.json +1 -1
  197. package/chunks/reg-brand-scrape.json +1 -1
  198. package/chunks/reg-departments-toggle.json +1 -1
  199. package/chunks/reg-extended-profile.json +1 -1
  200. package/chunks/reg-final-done.json +1 -1
  201. package/chunks/reg-helpdesk-grid.json +1 -1
  202. package/chunks/reg-import-picker.json +1 -1
  203. package/chunks/reg-integrations-grid.json +1 -1
  204. package/chunks/reg-invite-form.json +1 -1
  205. package/chunks/reg-legal-entity.json +1 -1
  206. package/chunks/reg-org-chart-review.json +1 -1
  207. package/chunks/reg-profile-identity.json +1 -1
  208. package/chunks/reg-step-footer.json +1 -1
  209. package/chunks/reg-step-header.json +1 -1
  210. package/chunks/reg-step-progress.json +1 -1
  211. package/chunks/reg-step-shell.json +1 -1
  212. package/chunks/reg-story-pane.json +1 -1
  213. package/chunks/reg-success-summary.json +1 -1
  214. package/chunks/reg-team-size.json +1 -1
  215. package/chunks/reg-usecase-picker.json +1 -1
  216. package/chunks/reg-workspace-name.json +1 -1
  217. package/chunks/responsive-grid-pattern.json +1 -1
  218. package/chunks/responsive-kpi-grid.json +1 -1
  219. package/chunks/responsive-row-pattern.json +1 -1
  220. package/chunks/responsive-wrap-at-pattern.json +1 -1
  221. package/chunks/search-with-filters.json +1 -1
  222. package/chunks/section-with-stack.json +1 -1
  223. package/chunks/select-multiple-preselected.json +1 -1
  224. package/chunks/settings-admin-page.json +1 -1
  225. package/chunks/settings-appearance.json +1 -1
  226. package/chunks/settings-billing-plan.json +1 -1
  227. package/chunks/settings-general-form.json +1 -1
  228. package/chunks/settings-integrations.json +1 -1
  229. package/chunks/settings-members-invite.json +1 -1
  230. package/chunks/settings-notifications.json +1 -1
  231. package/chunks/settings-page-shell.json +1 -1
  232. package/chunks/settings-profile-security.json +1 -1
  233. package/chunks/sidebar-collapsible-nav.json +1 -1
  234. package/chunks/slider-range-controls.json +1 -1
  235. package/chunks/tabs-with-panels.json +1 -1
  236. package/chunks/testimonial-grid.json +1 -1
  237. package/chunks/text-card.json +1 -1
  238. package/chunks/timeline-events.json +1 -1
  239. package/chunks/toolbar-icons.json +1 -1
  240. package/chunks/toolbar-tooltips.json +1 -1
  241. package/chunks/user-identity-row.json +1 -1
  242. package/chunks/user-profile-card.json +1 -1
  243. package/chunks/users-table-badge.json +1 -1
  244. package/chunks/video-player-controls.json +1 -1
  245. package/package.json +1 -1
@@ -64,6 +64,9 @@
64
64
  - Inline alert/banner for status messages within a content region. Severity via variant (info, success, warn, error).
65
65
  - For ephemeral toast notifications use <toast-ui> (or post to <feed-ui>); alert-ui is persistent inline.
66
66
  - For modal-style critical alerts use <modal-ui> with alert content.
67
+ - Billing dunning / payment-failed notices use pattern="dunning" + amount + currency + dueAt props, NOT inlined into title/description strings.
68
+ - pattern="dunning" SHOULD use variant="danger" (default) or variant="warning" (grace period); never variant="info" or "success".
69
+ - When pattern="dunning", slot at least one button-ui in slot="actions" with data-dunning-action ("update" or "retry").
67
70
 
68
71
  ## Aside
69
72
  - Use <aside-ui> as a slot stub inside an IN-PAGE primitive container parent (<card-ui>, <drawer-ui>, <modal-ui>, <page-ui>) for two-column layouts with a semantic side region. It ships no behavior; the parent reads [collapsible] and [width] via @scope. Typical contents: <list-ui> / <tree-ui> / <nav-ui variant="section">.
@@ -104,6 +107,10 @@
104
107
  - For navigation (route-change) use <nav-item-ui> or anchor; button-ui is for actions only.
105
108
  - For toggleable on/off state use <switch-ui>; for multi-select clusters use <toggle-group-ui> + <toggle-option-ui>.
106
109
 
110
+ ## CalendarGrid
111
+ - Use <CalendarGrid> only as a substrate primitive composed inside a higher-level component (date-range picker, datetime picker, custom date affordance). For a full single-date input, use <CalendarPicker> — it adds a trigger button, popover surface, and form-association.
112
+ - <CalendarGrid> is NOT form-associated. Its emitted `change` event is the sole signal to the parent — the parent owns the canonical value + form participation.
113
+
107
114
  ## CalendarPicker
108
115
  - Form-associated date input. Trigger button + popover calendar grid; emits ISO date string via change events.
109
116
  - Use for single-date input. For date ranges compose two pickers or use a dedicated range component.
@@ -173,12 +180,34 @@
173
180
  - For simple color swatches (read-only display) use <swatch-ui>; for hex/rgb text input use <color-input-ui>.
174
181
  - Output format defaults to oklch(); set format= to override (hex, rgb, hsl).
175
182
 
183
+ ## Combobox
184
+ - Use <combobox-ui> for typeahead-filterable single-select with a constrained-choice value model. `value` MUST be one of `options[].value` unless `[free-text]` is set. For ≤ 4 options, use <segmented-ui> or <radio-ui> instead.
185
+ - For free-form text entry with suggestions, use <autocomplete-input-ui> (SPEC-035) — combobox is constrained-choice. For button-first dropdowns where the trigger should be closed by default, use <select-ui searchable>.
186
+ - Compose options via native <option> / <optgroup> children, OR set `.options` programmatically as an array of `{value, label, disabled?}` (grouped form: `{label, options:[…]}`). Setting `value` to a string not in `options` is invalid mid-state (free-text=false) and the validator should reject it.
187
+ - `[creatable]` implies `[free-text]` and adds the "Create '{value}'" footer affordance + `create` event. Consumer wires the create event to a backend flow.
188
+ - Multi-select goes through <multi-select-ui> (SPEC-040), not combobox. Combobox is single-select.
189
+
176
190
  ## Command
177
191
  - <command-ui> is the searchable PALETTE primitive (input + option list). For Cmd+K palettes, wrap it in <admin-command> at the shell tier — admin-command owns the native <dialog>, focus management, and the Cmd+K / Ctrl+K shortcut listener. command-ui is content-only and does NOT own the dialog or shortcut. Do NOT hand-roll a <dialog> + Cmd+K key listener.
178
192
  - Author items as native <option value data-icon data-shortcut> elements inside <optgroup label="…"> for grouped sections. The `select` event's detail.category mirrors the parent optgroup's label. Detail = { value, label, category }.
179
193
  - Decision rule vs adjacent surfaces. Use <menu-ui> for small NON-searchable popover menus (≤10 actions, triggered by a button). Use <modal-ui> for generic centered dialogs. Reach for <command-ui> only when you need a searchable, keyboard-navigable list of commands or destinations.
180
194
  - command-ui MAY render inline (no dialog) for embedded search panels, but the canonical AdiaUI admin pattern is exactly one <command-ui placeholder="…"> as the sole child of <admin-command> inside <admin-shell>. See site/index.html and playgrounds/admin-shell/ for production references.
181
195
 
196
+ ## DateRangePicker
197
+ - DateRangePicker.value MUST be `{from, to}` with both ISO 8601 dates, OR null. Either side null is invalid mid-state and the validator should reject it (use `input` event for partial state).
198
+ - DateRangePicker.value.to MUST be `>=` value.from lexicographically. Reversed ranges trigger `invalid` and do NOT commit.
199
+ - When `comparison: true`, both `value` AND `compareValue` MUST be set on commit. If only one is set, the form participation emits the set one and omits the other.
200
+ - presets array entries each require both `label` (string) and `range` (`{from, to}`). Empty preset arrays are valid (rail renders empty).
201
+ - Use DateRangePicker for date ranges. Do NOT compose two adjacent `<calendar-picker-ui>` instances + JS synchronization — that is the pattern this primitive replaces.
202
+
203
+ ## DatetimePicker
204
+ - `DatetimePicker.value` MUST be ISO 8601 datetime (`YYYY-MM-DDTHH:mm` or `YYYY-MM-DDTHH:mm:ss`) OR empty string. Date-only or time-only strings fire `invalid`.
205
+ - `precision: "second"` requires the value to include seconds when set; missing seconds are coerced to `:00` on commit.
206
+ - `min` and `max` MUST be parseable ISO 8601 datetimes if non-empty. If `value` falls outside, `invalid` fires and the value does not commit.
207
+ - `hour-cycle` overrides the locale-derived cycle in the time pane. Set explicitly when the surface needs a specific cycle (cron editors, log queries, system surfaces).
208
+ - Use DatetimePicker for combined date+time. Do NOT compose `<calendar-picker-ui>` + a free-form `<input-ui>` manually as an alternative — that is the pattern this primitive replaces.
209
+ - Per ADR-0025 NEVER wrap a native `<input type="datetime-local">` — the calendar pane + time pane composition + ElementInternals together provide form participation.
210
+
182
211
  ## DemoToggle
183
212
  - Demo-page-only — toggles between live and code views in component documentation surfaces.
184
213
  - Do not use in apps/ — restrict to packages/web-components/components/*/<name>.html demo pages and docs surfaces.
@@ -270,12 +299,18 @@
270
299
  - Set aspect-ratio attribute to lock dimensions and prevent layout shift during load.
271
300
  - For decorative-only images, set alt='' and aria-hidden='true' so screen readers skip them.
272
301
 
302
+ ## InlineMessage
303
+ - In-flow annotation under a form input (validation feedback, hint copy, inline confirmation). Severity via [variant] (info, success, warning, danger).
304
+ - For overlay / banner-style notices use <alert-ui> instead; for transient toasts use <toast-ui>.
305
+ - Place inside <field-ui>, <col-ui>, or <row-ui>; never as a page-level banner.
306
+ - Do not nest a focusable child (button, link with action) — InlineMessage is non-interactive annotation.
307
+
273
308
  ## Input
274
309
  - <input-ui> is the canonical single-line text input. The host IS the contenteditable surface — NEVER wrap a native <input>. The sole exception is type="password", which internally uses a real <input type="password"> for masking (per ADR-0025).
275
310
  - Wrap <input-ui> in <field-ui label="…" hint="…" error="…"> for the canonical stacked label / hint / error chrome. The inline [label] / [hint] / [error] props are also supported on the primitive for compact use.
276
311
  - Form participation is implicit via UIFormElement. Set [name] for FormData submission; [required] / [disabled] / [readonly] reflect; listen for `change` (blur or Enter commit) and `input` (per keystroke). `submit` event fires when Enter commits the value (used by <chat-composer>'s `composer-submit` forwarding).
277
312
  - For numeric input use [type="number"] with [min] [max] [step] [precision] [prefix] / [suffix] — this stamps a contenteditable surface + <button-ui> / <icon-ui> stepper column with ARIA spinbutton semantics. Read `el.valueAsNumber` for the parsed Number. Never substitute a native <input type="number">.
278
- - Inside <chat-composer>, the canonical inner input is <chat-input-ui submit-on-enter> (chat variant subclass — owns the submit-on-enter contract explicitly). The plain <input-ui> primitive already fires `submit` on Enter, but only chat-input-ui reflects the [submit-on-enter] attribute.
313
+ - Inside <chat-composer>, the canonical inner input is <chat-input-ui> (chat variant subclass — adds the send button + model picker + paste-to-attach plumbing). The plain <input-ui> primitive ALSO fires a bubbling `submit` event on Enter (unconditional, no opt-in attribute); <chat-input-ui> simply builds on that semantic.
279
314
 
280
315
  ## Inspector
281
316
  - Developer-tools pane for A2UI runtime state — composes <tabs-ui> + <code-ui> internally.
@@ -283,6 +318,13 @@
283
318
  - Use only in dev/debug surfaces; not for product UI.
284
319
  - Place in a right-pane inside <editor-shell-ui>'s editor-sidebar slot for editor-style inspector layouts.
285
320
 
321
+ ## IntegrationCard
322
+ - Use <integration-card-ui> for one tile in an integrations grid. Set `provider` and `name` — both are required for analytics keys and the accessible name. Set `description` so users don't have to look the integration up externally.
323
+ - `status` MUST be one of `available | connected | error | pending | coming-soon`. The button label and variant are DERIVED from `status` — never slot a <button-ui> directly in the card body. Use the `actions` slot for an overflow <menu-ui> with secondary actions like Reauthenticate or Disconnect.
324
+ - When `status="error"`, set `error-message` — otherwise the user sees a Retry button with no context. The message renders below the description in danger-text color.
325
+ - `logo` accepts a URL (containing `/`, renders as <img>) or a registered icon name (renders as <icon-ui>). Don't mix — one provider gets one logo source.
326
+ - Group integration cards in <grid-ui columns="auto-fit"> or use <integrations-page-ui> (SPEC-063) for the canonical Settings-panel grid. Never nest <card-ui> around an <integration-card-ui> — it IS a card variant.
327
+
286
328
  ## Kbd
287
329
  - Inline-only — content from innerHTML, typically one or two key labels.
288
330
  - In menu items, use the kbd= attribute on <menu-item-ui> instead of nesting <kbd-ui> directly.
@@ -293,6 +335,16 @@
293
335
  - When wrapping action affordances that visually mimic links (e.g. 'Forgot password?' that triggers a reset flow), prefer `<button-ui variant="ghost">` over a fake `<link-ui>` — the affordance is semantically a button, just visually understated.
294
336
  - For inline-sentence affordances ('I agree to the [Terms] and [Privacy]'), nest `<link-ui>` directly inside `<text-ui>` so it inherits the paragraph's font / size / line-height.
295
337
 
338
+ ## ListWindow
339
+ - ListWindow.items MUST be an array of plain objects OR scalars. Functions / DOM nodes / Promises are invalid.
340
+ - ListWindow.render cannot be expressed in A2UI JSON — declarative authoring MUST use a <template> child (or default <list-item-ui> for objects with a text field).
341
+ - When items.length > 200, the validator SHOULD recommend ListWindow over List to keep the DOM tractable.
342
+ - ListWindow MUST have a defined height (via parent layout or style="height:..."). An unbounded-height windowed list defeats the windowing math.
343
+ - ListWindow.item-size SHOULD be set when item heights are known and constant — the fast-path is significantly cheaper.
344
+ - Do NOT nest ListWindow inside another scroll container; double-scroll containers break the IntersectionObserver math. Use one scroll boundary.
345
+ - Do NOT use ListWindow for short lists (< 50 items). The windowing overhead exceeds the cost of rendering all rows. Use List for short lists.
346
+ - Do NOT use for tabular data — that is Table with virtualized rows.
347
+
296
348
  ## ListItem
297
349
  - Child of <list-ui> — one row of generic-list content.
298
350
  - For navigation lists use <nav-item-ui> inside <nav-ui>; for menu items use <menu-item-ui>; for tree rows use <tree-item-ui>.
@@ -303,6 +355,15 @@
303
355
  - For interactive selection lists use <nav-ui> (single-select navigation) or <menu-ui> (action menu); list-ui is content display.
304
356
  - For data-grid / sortable / sticky-header needs use <table-ui> instead.
305
357
 
358
+ ## LoadingOverlay
359
+ - <LoadingOverlay> MUST be placed inside a sized container with content (Card, Section, Table body, Chart). It absolutely positions against the nearest positioned ancestor. The parent's CSS must include `position: relative` (or any non-static positioning) — the component does not mutate parent layout styles.
360
+ - Toggle <LoadingOverlay active> from consumer code while async work is in flight. The overlay applies aria-busy="true" to its parent while active; on dismiss, both inert and aria-busy are released.
361
+ - The [delay] grace window (default 200ms) suppresses paint on fast-resolving loads. For server-rendered "definitely-slow" states (>1s known wait) consider [delay="0"] for immediate feedback.
362
+ - Do NOT use as a page-level / viewport loader — use a dedicated route-loader pattern. <LoadingOverlay> is container-scoped.
363
+ - Do NOT nest <LoadingOverlay> inside <Modal> or <Drawer> — those primitives own their own busy state. When they support it, pass [loading] to those directly.
364
+ - Do NOT use <LoadingOverlay> to disable a form during submit — use <Button type="submit" loading> for the submit affordance.
365
+ - The default slot accepts any busy indicator. When empty, a centered <Spinner size="lg"> is auto-stamped. Slot a <Skeleton> stack for placeholder-shaped loading; slot <Progress value=…> when the wait is determinate.
366
+
306
367
  ## MenuDivider
307
368
  - <menu-divider-ui> MUST be a direct child of <menu-ui>; a raw <hr> will render OUTSIDE the popover because <menu-ui> only hoists <menu-item-ui> and <menu-divider-ui> children via its direct-child selector.
308
369
  - Use to group items by semantic tier — primary actions → secondary → destructive (danger). Avoid leading / trailing dividers and consecutive dividers; they produce visual noise without grouping value.
@@ -463,6 +524,8 @@
463
524
  - For dynamic option lists rendered inside <editor-shell>, set the JSON via the [data-options] attribute — <editor-shell>'s wireSelects() finds select-ui[data-options], JSON.parses the attribute, and assigns `.options` on connect. Useful for static-HTML toolbars where JS hydration would be awkward.
464
525
  - <select-ui> owns its own label / hint / error chrome (via [label] / [hint] / [error] props). Only wrap in <field-ui> when you need to share the field-chrome stack with sibling inputs in the same form row group.
465
526
  - Enable [searchable] for > 10 options; add [free-text] only when unmatched values are valid (tag entry, email-with-suggestion). Use [multiple searchable] for multi-select rather than authoring a separate multi-select primitive.
527
+ - For multi-select, set [multiple] — the trigger automatically renders <tag-ui> chips per selected option; the popover renders checkbox-style option rows where clicks toggle membership without closing. Form value is comma-separated under [name]. There is NO `<multi-select-ui>` tag — that name does not exist.
528
+ - Multi-select bulk controls: [select-all] renders a "Select all" / "Clear" control above the option list; [clearable] adds a clear-all `x` affordance to the trigger. [max-chips] caps the visible chip count and renders "+N more". [min] / [max] gate form validity.
466
529
 
467
530
  ## Skeleton
468
531
  - Use to placeholder content during loading. Shape via CSS sizing (width/height/border-radius); shimmer is automatic.
@@ -474,6 +537,13 @@
474
537
  - For two-handle range selection use <range-ui> instead.
475
538
  - Step attribute controls increments; show-value enables an inline value label.
476
539
 
540
+ ## Spinner
541
+ - Use <Spinner> for INDETERMINATE loading where the duration is unknown. For determinate progress (a known fraction complete), use <Progress> (linear) instead. For known-shape placeholder loading, use <Skeleton>.
542
+ - When a Spinner is inside a Button, set tone="current" so it matches the button label color, and disable the button while the operation is in progress.
543
+ - When overriding [label], use a present-progressive verb form ("Loading", "Saving", "Uploading"). Never use "Spin" or "Wait" — they describe the visual, not the operation.
544
+ - Do not nest <Spinner> inside <Skeleton>; they are siblings (two different loading idioms), not parent/child.
545
+ - Do not stack multiple sibling <Spinner>s in the same viewport region. Use one parent-level Spinner instead — multiple spinners add visual noise without extra information.
546
+
477
547
  ## Stack
478
548
  - Overlay/layer stacking container — children occupy the same area, stacked on the z-axis.
479
549
  - Use for overlapping content (image + overlay, badge-over-avatar, drop-shadow stacks).
@@ -554,6 +624,14 @@
554
624
  - Use [size="sm"] for inline-with-text contexts (doc page headers, table cells, badges next to titles); [size="md"] (default) for filter-bar chips and standalone tag rows.
555
625
  - Group multiple tags inside a <row-ui gap="2"> — never stack them vertically; vertical lists of dismissable items are an <action-list-ui> use case, not <tag-ui>.
556
626
 
627
+ ## TagsInput
628
+ - Use <tags-input-ui> for OPEN-SET free-form token entry — labels, keywords, email recipients, comma-separated lists. For CLOSED option sets (pick N from a fixed list), use <select-ui multiple> (SPEC-040) — that primitive gates the value against `options[]`.
629
+ - `TagsInput.value` MUST be a string array. Pass `["a","b"]`, not the comma-joined string `"a,b"`. The host parses string-form `value` attributes as JSON; non-array shapes throw `invalid`.
630
+ - `delimiter: "enter"` disables in-line character commits; only the Enter key (or programmatic `addToken`) commits. Use this when the token grammar legitimately includes the default `,`.
631
+ - `unique: true` (default) silently coalesces accidental duplicate adds. Do NOT emit `invalid` for those — the contract specifies silent dedup for ergonomic typing.
632
+ - `validateFn` is a JS property, NOT serializable in A2UI JSON. Wire validators post-mount via DOM scripting; A2UI authors should not expect to declare a validator in the JSON payload.
633
+ - Chips are rendered automatically from `value`. Do NOT slot individual <tag-ui> children manually — that decouples the rendered DOM from the form value and breaks Backspace removal.
634
+
557
635
  ## Text
558
636
  - Use for typographic content with semantic role (heading, body, label, caption). Variant attribute sets the role.
559
637
  - For inline-flow rich content with multiple paragraphs, use <richtext-ui> instead.
@@ -564,7 +642,15 @@
564
642
  - Wrap <textarea-ui> in <field-ui label="…" hint="…" error="…"> for the canonical labeled stack. The inline [label] / [hint] / [error] props are also supported on the primitive for compact use.
565
643
  - Form participation is implicit via UIFormElement. Set [name] for FormData submission; [required] / [disabled] / [readonly] reflect; listen for `change` (on blur after value change) and `input` (per keystroke).
566
644
  - Use [rows] to set initial height (default 3) and [resize] (vertical | horizontal | both | none; default vertical) to control user resize. Never substitute a native <textarea> just to get rows / resize.
567
- - Enter inserts a newline <textarea-ui> does NOT emit a `submit` event. For Enter-to-send multi-line composers, use <chat-input-ui submit-on-enter> inside <chat-composer>, not textarea-ui.
645
+ - Enter (without Shift) dispatches a bubbling `submit` event; Shift+Enter inserts a newline. This is unconditional there is no opt-in/opt-out attribute. For chat composer surfaces wrap inside <chat-composer> + <chat-input-ui> (which adds the send button + model picker + paste-to-attach plumbing on top of the same Enter→submit semantics).
646
+
647
+ ## TimePicker
648
+ - `<time-picker-ui>` is the canonical standalone time-of-day picker. Per ADR-0025 NEVER wrap a native `<input type="time">` — segments are contenteditable spans + ElementInternals provides form participation.
649
+ - `value` MUST be ISO 8601 time `HH:mm` or `HH:mm:ss` (24-hour), or empty string. Localized formats (e.g. "9:30 AM") are not accepted; display formatting is derived from `hour-cycle` regardless of how the value is stored.
650
+ - `step` is in seconds. 60 = minute precision (default); 900 = 15-minute precision (meeting-time common); 1 = second precision (requires `precision="second"`).
651
+ - `precision="second"` exposes the seconds segment AND emits `HH:mm:ss`. Default `precision="minute"` emits `HH:mm`.
652
+ - `hour-cycle` overrides locale-derived behavior. Set explicitly (`h12` / `h23`) when the surface needs a specific cycle (cron editors, log queries, system surfaces).
653
+ - For datetime selection use `<datetime-picker-ui>` (SPEC-038) — it composes this primitive as its time pane.
568
654
 
569
655
  ## TimelineItem
570
656
  - Child of <timeline-ui> — one chronological event with timestamp + content + optional icon dot.
@@ -632,8 +718,57 @@
632
718
  - Multiple attribute enables multi-file selection; accept= constrains file types.
633
719
  - For agent chat attachments use <chat-composer-ui>'s built-in upload affordance instead.
634
720
 
721
+ ## BillingOverview
722
+ - BillingOverview.account is REQUIRED. The composite is meaningless without an account snapshot; emitting BillingOverview with null account and no data-stream-src renders the empty state.
723
+ - account.status MUST be one of `active` | `trialing` | `past_due` | `canceled` | `paused`. Unknown values render neutral chrome.
724
+ - account.dunning is REQUIRED when account.status is `past_due` (otherwise the dunning banner cannot render). Shape: `{amount, currency, dueAt, cardLast4?, reason?}`. The composite stamps `<alert-ui pattern="dunning">` per SPEC-006.
725
+ - account.plans is REQUIRED when [variant] is `full` (otherwise the plan-picker section has nothing to render). Omit `plans` when [variant] is `compact` or `enterprise` to skip the plan-picker section.
726
+ - account.paymentMethods is forwarded verbatim to <payment-method-list-ui>; account.invoices is forwarded verbatim to <invoice-history-ui>. Their shapes are owned by SPEC-010 and SPEC-008 respectively.
727
+ - BillingOverview MUST NOT be nested inside Modal or Drawer. The dashboard is a route, not a modal — Modal traps focus + clips on long content; this surface is designed for full-page layout.
728
+ - Wire one listener to `account-action` instead of subscribing to every child primitive's event. The composite normalises plan-action / payment-action / invoice-action / dunning-action into the single bubbling event.
729
+
730
+ ## InvoiceDetail
731
+ - InvoiceDetail MUST set `invoice` OR `data-stream-src`. Neither produces the empty state but cannot be the submission state for a billing surface.
732
+ - invoice.lines[] MUST contain at least one row with `description`, `qty`, `unitAmount`, `amount`. Empty lines arrays render the lines table empty-state row, not the host empty state.
733
+ - invoice.total MUST equal subtotal + tax − (discount or 0). The composite renders the host-supplied total verbatim; consumers are authoritative on rounding rules per locale (SPEC-007 OD-001).
734
+ - invoice.status MUST be one of draft / open / paid / past-due / void. Unknown statuses render the default badge variant + a one-shot console.warn.
735
+ - InvoiceDetail MUST NOT be nested inside Modal or Drawer. Invoices are routes, not modals — Modal traps focus + clips on long content; the composite is designed for full-page layout. Use `<a href="/invoices/{number}">` instead.
736
+ - Slot ALL header actions explicitly via `slot="header-actions"` — the composite NEVER stamps a default toolbar (SPEC-007 OD-002). Action sets vary across products; default is too opinionated.
737
+
738
+ ## InvoiceHistory
739
+ - InvoiceHistory.invoices MUST be a non-empty array on commit. Empty arrays render the empty-state (no rows).
740
+ - invoices[].status MUST be one of "draft" | "open" | "paid" | "past-due" | "void". Unknown values fall back to a neutral badge variant.
741
+ - InvoiceHistory MUST NOT receive a `columns` prop — columns are owned by the composite. Consumers needing custom columns should compose <table-ui> directly with their own column array.
742
+ - `hrefPattern` is a string template with `{number}` interpolation. Consumers needing dynamic resolution (query-strings, function- based routes) should preventDefault on `invoice-row-click` and route themselves.
743
+ - Pair with `InvoiceDetail` (SPEC-007) at the route resolved by `hrefPattern` so the row-click destination exists.
744
+
745
+ ## PaymentMethodForm
746
+ - PaymentMethodForm SHOULD be wrapped in a <Form> so its form-value (the resolved token) is delivered on submission. Standalone use requires the consumer wire `tokenize()` from a custom submit handler.
747
+ - PaymentMethodForm MUST NOT receive a `value` prop carrying raw card digits. The form value is the tokenized output; raw card digits live only inside the form's sub-fields and are never mirrored to `value`.
748
+ - PaymentMethodForm SHOULD only be used in test / demo / non-PCI surfaces. Production card-capture flows MUST tokenize via a real payment processor (Stripe Elements, Braintree, Adyen) and consume only the resulting token — this primitive is the form chrome + structured validation, not a tokenization provider.
749
+ - Wrapping PaymentMethodForm inside <Field> duplicates the label and corrupts the grid layout. PaymentMethodForm IS a field group; the host carries `aria-label` directly.
750
+ - `countries` SHOULD list every ISO 3166-1 alpha-2 code your product accepts. Empty arrays fall back to a default short list (US, CA, GB, DE, FR, AU) suitable for early-stage demos only — production deployments should set this explicitly.
751
+
752
+ ## PaymentMethodList
753
+ - PaymentMethodList.methods MUST be an array. Empty array is valid and renders the empty-state.
754
+ - methods[].id MUST be unique within the list. Duplicate ids cause row collision at diff time.
755
+ - Exactly one record in `methods` MAY have `default: true`. If none does, the primitive marks the first row as default on connect.
756
+ - methods[].brand SHOULD be from the enumerated set (visa | mastercard | amex | discover | jcb | unionpay | diners | paypal | apple-pay | google-pay | ach | sepa). Unknown brands fall back to a generic credit-card mark.
757
+ - methods[].type MUST be `card`, `bank`, or `wallet`. Drives the brand-icon fallback when `brand` is unknown.
758
+ - `value` wins on conflict with methods[].default — if both are set and disagree, value is the source of truth and the primitive emits `change` to reconcile.
759
+ - Mutations are events, not props. Wire `change` / `add` / `remove` / `select` to your billing API; the primitive does NOT write to the underlying data source.
760
+
761
+ ## PlanPicker
762
+ - PlanPicker.plans MUST be a non-empty array on commit. Empty arrays render the empty-state but cannot be the submission state.
763
+ - At most ONE plans[] record may carry `recommended: true`. Two recommended plans are visually ambiguous and defeat the treatment's purpose.
764
+ - `current` MUST match a plans[].id or be empty. Unknown ids fail silently — no anchor row renders, no current-state contextualization on siblings.
765
+ - plans[].prices.monthly is REQUIRED on every record. annual is optional; if absent and cycle="annual" the card renders the monthly price with the annual cycle suffix and no `note`.
766
+ - `cycle` MUST be `monthly` or `annual`. Other values are coerced to `monthly` and a one-shot console.warn fires.
767
+ - Use `layout="list"` for in-app settings panels where the picker sits inside a <card-ui> and width is constrained. Use the default `layout="grid"` for marketing pricing pages.
768
+ - Wire `select` (not `change`) to the actual commit handler — change fires on selection without commit (keyboard focus traversal, programmatic value-set); select fires when the user clicks the CTA inside a card.
769
+
635
770
  ## ChatComposer
636
- - chat-composer is the bespoke replacement for legacy <chat-input-ui data-chat-input> inside <chat-shell>. Place an inner <chat-input-ui submit-on-enter> as the primary input.
771
+ - chat-composer is the bespoke replacement for legacy <chat-input-ui data-chat-input> inside <chat-shell>. Place an inner <chat-input-ui> as the primary input — it emits `submit` on Enter without Shift (unconditional; delegated from the inner textarea).
637
772
  - The host listens for 'composer-submit' on the composer (not on the inner input). The event detail mirrors the inner submit event so existing handlers Just Work.
638
773
  - Default slot holds a single <chat-input-ui> child; trailing/attach/leading slots host action buttons (send, attach, model picker).
639
774
  - For non-chat input surfaces (forms, prompts, search) use <chat-input-ui> directly without the composer wrapper.
@@ -649,7 +784,7 @@
649
784
  - For admin-shell-style chrome bars inside chat-shell, use <admin-topbar> instead — chat-header is for in-chat metadata only.
650
785
 
651
786
  ## ChatShell
652
- - chat-shell takes bespoke chat-* children only. The canonical composition is <chat-thread> (with optional first-child <chat-empty>) followed by <chat-composer> wrapping a <chat-input-ui submit-on-enter>. Add <chat-header> / <chat-sidebar> / <chat-status> as needed.
787
+ - chat-shell takes bespoke chat-* children only. The canonical composition is <chat-thread> (with optional first-child <chat-empty>) followed by <chat-composer> wrapping a <chat-input-ui>. Add <chat-header> / <chat-sidebar> / <chat-status> as needed.
653
788
  - Don't nest col-ui / row-ui or generic layout primitives directly inside chat-shell — the shell's CSS reads child tag selectors to lay them out. Generic layout goes inside the bespoke children.
654
789
  - The shell listens for 'composer-submit' on <chat-composer> (not on the inner input). Streaming state is reflected on this host and propagates to <chat-thread>[streaming] + <chat-composer>[disabled] automatically — don't toggle child attributes manually.
655
790
  - Legacy data-attribute shapes were retired in v0.4.0 per ADR-0024. Do not author <section data-chat-messages>, <chat-input-ui data-chat-input>, <empty-state-ui data-chat-empty>, or <header data-chat-name> inside chat-shell.
@@ -671,6 +806,53 @@
671
806
  - Different from primitive <chat-thread-ui>: chat-thread (no -ui suffix) is the module-tier shell-aware version with scroll-on-new-message + load-more + empty-state coordination.
672
807
  - Hosts message blocks (typically agent/user message rows) as default-slot children; <chat-empty> goes as first child for the empty state.
673
808
 
809
+ ## DashboardLayout
810
+ - DashboardLayout MUST receive children via the four named slots
811
+ (toolbar / kpis / charts / table) plus the optional aside.
812
+ Children without a [slot] attribute will not render in any band.
813
+
814
+ - DashboardLayout[kpi-columns] MUST be 2-6. Out-of-range values
815
+ fall back to the default 4. Eight cards in a row produces a
816
+ KPI card narrower than --dashboard-layout-kpi-min.
817
+
818
+ - DashboardLayout[chart-split] MUST be one of the documented
819
+ enum strings — "" (full-width) / 2 / 2:1 / 3:2 / 3:1.
820
+
821
+ - A DashboardLayout SHOULD sit inside AdminPageBody for the
822
+ canonical chrome stack. Top-level placement is allowed for
823
+ surface-only demos but not the production shape.
824
+
825
+ - A DashboardLayout MUST NOT contain another DashboardLayout as
826
+ a descendant. Two parametric-density containers fight over the
827
+ --a-density cascade. For nested dashboards use sibling
828
+ DashboardLayouts switched by Tabs.
829
+
830
+
831
+ ## DateRangeSelector
832
+ - DateRangeSelector MUST sit inside a toolbar context — typically a
833
+ Row inside DashboardLayout.slot=toolbar, or directly inside
834
+ AdminPageHeader.slot=action. Do NOT place it as a freestanding
835
+ block primitive at page top-level.
836
+
837
+ - ONLY ONE DateRangeSelector with broadcast="document" per page.
838
+ Secondary instances on the same page MUST use broadcast="self"
839
+ or broadcast="none". Two document-broadcasters race for the
840
+ data-range-* attributes on <html> and produce flicker.
841
+
842
+ - `presets` MUST be a comma-separated subset of the documented value
843
+ enum (today, 7d, 30d, 90d, qtd, ytd, custom). Unknown keys are
844
+ ignored at runtime; the chip-row renders only recognized keys.
845
+
846
+ - When value="custom", from/to MUST be ISO-8601 dates supplied
847
+ either declaratively (from + to attributes) or imperatively via
848
+ setRange("custom", from, to). Without a custom from/to, the
849
+ composite falls back to the prior resolved range.
850
+
851
+ - For form participation, set [name]. Without [name], the composite
852
+ emits range-change events only and does NOT participate in form
853
+ submission. With [name], FormData carries the value "{from}:{to}".
854
+
855
+
674
856
  ## EditorCanvasEmpty
675
857
  - editor-canvas-empty is the bespoke empty-state slot for <editor-canvas>. Place as the first child of <editor-canvas>; visibility is automatic via the [empty] reflected attribute.
676
858
  - CSS-only — no JS module needed in shell HTML imports.
@@ -710,6 +892,21 @@
710
892
  - Different from <admin-topbar> (admin-shell chrome) — editor-toolbar has [full-screen] state + editor-specific [data-toolbar-action] event bubbling.
711
893
  - Place full-screen toggle buttons inside the toolbar with [data-toolbar-action="toggle-full-screen"]; host reflects [full-screen] up to <editor-shell>.
712
894
 
895
+ ## ConfirmDialog
896
+ - ConfirmDialog is for NON-destructive yes/no questions ("Save changes?", "Switch theme?", "Apply preset?"). For irreversible destructive operations (delete, drop, deploy to prod) use AlertDialog instead — its danger chrome + role="alertdialog" + typed-name speed-bump are the WAI-APG pattern for that case.
897
+ - Default focus lands on Cancel by default — this is intentional (WAI guidance: the least-destructive action is the default). Do NOT add autofocus to the confirm button or override the focus model.
898
+ - The confirm button is primary-toned by default. Use [confirm-variant="ghost"] only when both options are roughly equivalent in weight (e.g. "Apply preset" vs "Keep current" where neither is strictly preferred).
899
+ - Do NOT model the cancel option as a "Discard" action — if cancel means "lose data", the operation is destructive and belongs in AlertDialog. ConfirmDialog's cancel is a no-op return-to-prior- state semantically.
900
+ - Reflect dialog visibility via the [open] boolean attribute on the ConfirmDialog host (open=true / open=false). Do NOT toggle [hidden], CSS display, or wrap in a sibling visibility container — the inner <modal-ui> owns the native <dialog> lifecycle.
901
+
902
+ ## OnboardingChecklist
903
+ - OnboardingChecklist MUST have a non-empty `items` array. Empty items renders an empty card.
904
+ - Each item MUST have a unique `id` within the list — used as the storage key + event detail.
905
+ - `storageKey` SHOULD be namespaced per product (e.g. `myapp:onboarding:v1`) to avoid cross-app collisions.
906
+ - Items SHOULD number 3 to 8. Fewer than 3 is not worth a checklist; more than 8 should split into stages.
907
+ - 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.
908
+ - Do not nest two <onboarding-checklist-ui> with the same `storageKey` — they will clobber each other.
909
+
713
910
  ## A2UIRoot
714
911
  - Mount point for an A2UI-rendered composition. Hosts the runtime-emitted DOM tree.
715
912
  - Different from <gen-root> (which is for the generative-UI lane with LLM-driven streaming).
@@ -720,6 +917,36 @@
720
917
  - Hosts <chat-thread-ui>, <canvas-ui>, and <inspector-ui> children via named slots — chat slot for the conversation lane, canvas slot for the artifact lane, inspector slot for the dev-tools lane.
721
918
  - Mode attribute (chat-only|split|canvas-only) controls layout; transitions are CSS-animated.
722
919
 
920
+ ## IntegrationsPage
921
+ - Use <integrations-page-ui> for the canonical Settings >
922
+ Integrations grid. Supply the `integrations` array — the
923
+ composite owns the search + grouping + grid breakpoints +
924
+ empty state.
925
+
926
+ - `integrations` MUST be a non-empty array OR the page renders
927
+ the empty-state. Each item MUST satisfy the SPEC-062 card
928
+ prop contract (provider + name required).
929
+
930
+ - Do NOT slot <integration-card-ui> children directly — the page
931
+ generates cards from the `integrations` prop. Slotted children
932
+ are ignored.
933
+
934
+ - Do NOT nest <integrations-page-ui> inside another
935
+ <integrations-page-ui>.
936
+
937
+ - For one-off integration tile groups (e.g., a single recommended
938
+ provider on a marketing page) use <integration-card-ui> directly
939
+ inside <grid-ui>; reach for the page composite only when search
940
+ + grouping + empty-state semantics are needed.
941
+
942
+
943
+ ## NotificationPreferences
944
+ - NotificationPreferences.channels MUST be non-empty. A matrix with zero columns renders nothing meaningful — author at least one channel record.
945
+ - NotificationPreferences.preferences[].channels keys MUST match the channels[].key set. Mismatched keys silently render the cell as `false` (off) — they do NOT raise an error, but the user can never toggle them into existence either.
946
+ - The composite is fully CONTROLLED — toggling a cell does NOT mutate `preferences`. The host MUST listen for `change` (and `bulk-toggle` for the column master), apply the change to its own state, then re-pass the new `preferences` array. Failing to do this leaves the toggle visually flipping back to its prior state on the next render.
947
+ - For binary on/off cell toggles, use <check-ui> semantics — NOT <switch-ui>. The column-header master IS a <switch-ui> (because it's a "setting" — flip the whole column), but each cell is a multi-select-style checkbox.
948
+ - Use `group-by="group"` only when `preferences[]` records carry a stable `group` field. Mixing grouped + ungrouped rows in the same matrix produces inconsistent section heights.
949
+
723
950
  ## AdminCommand
724
951
  - admin-command wraps a native <dialog>; the inner <command-ui> is the actual palette. Keyboard shortcut defaults to both Cmd+K (mac) and Ctrl+K (other) — the AdiaUI convention.
725
952
  - Place admin-command as a direct child of admin-shell, NOT inside a sidebar or main column. The host coordinates triggers ([data-command-trigger]) by reaching across siblings.