@adia-ai/web-components 0.8.39 → 0.8.41

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 (424) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/MIGRATION.md +1269 -3
  3. package/README.md +25 -30
  4. package/USAGE.md +26 -0
  5. package/bin/audit-template-children.mjs +556 -0
  6. package/components/accordion/accordion-item.a2ui.json +4 -0
  7. package/components/accordion/accordion-item.yaml +2 -0
  8. package/components/accordion/accordion.a2ui.json +4 -0
  9. package/components/accordion/accordion.yaml +2 -0
  10. package/components/action-list/action-item.a2ui.json +24 -2
  11. package/components/action-list/action-item.yaml +28 -3
  12. package/components/action-list/action-list.a2ui.json +6 -1
  13. package/components/action-list/action-list.class.js +52 -11
  14. package/components/action-list/action-list.css +43 -3
  15. package/components/action-list/action-list.d.ts +4 -2
  16. package/components/action-list/action-list.yaml +5 -0
  17. package/components/adia-mark/adia-mark.a2ui.json +1 -0
  18. package/components/adia-wordmark/adia-wordmark.a2ui.json +1 -0
  19. package/components/agent-artifact/agent-artifact.a2ui.json +1 -0
  20. package/components/agent-artifact/agent-artifact.css +1 -1
  21. package/components/agent-feedback-bar/agent-feedback-bar.a2ui.json +6 -5
  22. package/components/agent-feedback-bar/agent-feedback-bar.yaml +6 -2
  23. package/components/agent-questions/agent-questions.a2ui.json +1 -0
  24. package/components/agent-reasoning/agent-reasoning.a2ui.json +6 -5
  25. package/components/agent-reasoning/agent-reasoning.css +2 -2
  26. package/components/agent-reasoning/agent-reasoning.yaml +6 -2
  27. package/components/agent-suggestions/agent-suggestions.a2ui.json +11 -2
  28. package/components/agent-suggestions/agent-suggestions.class.js +2 -1
  29. package/components/agent-suggestions/agent-suggestions.d.ts +4 -4
  30. package/components/agent-suggestions/agent-suggestions.yaml +4 -2
  31. package/components/agent-trace/agent-trace.a2ui.json +6 -5
  32. package/components/agent-trace/agent-trace.css +3 -3
  33. package/components/agent-trace/agent-trace.yaml +6 -2
  34. package/components/alert/alert.a2ui.json +1 -0
  35. package/components/alert/alert.css +2 -2
  36. package/components/anchor-bar/anchor-bar.a2ui.json +1 -0
  37. package/components/aside/aside.a2ui.json +1 -0
  38. package/components/avatar/avatar-group.a2ui.json +7 -1
  39. package/components/avatar/avatar-group.yaml +1 -0
  40. package/components/avatar/avatar.a2ui.json +12 -11
  41. package/components/avatar/avatar.css +1 -1
  42. package/components/avatar/avatar.d.ts +1 -1
  43. package/components/avatar/avatar.yaml +4 -4
  44. package/components/badge/badge.a2ui.json +1 -0
  45. package/components/badge/badge.css +1 -1
  46. package/components/block/block.a2ui.json +1 -0
  47. package/components/blockquote/blockquote.a2ui.json +1 -0
  48. package/components/breadcrumb/breadcrumb.a2ui.json +1 -0
  49. package/components/button/button.a2ui.json +1 -3
  50. package/components/button/button.css +6 -5
  51. package/components/button/button.yaml +1 -3
  52. package/components/calendar-grid/calendar-grid.a2ui.json +1 -0
  53. package/components/calendar-grid/calendar-grid.class.js +2 -1
  54. package/components/calendar-picker/calendar-picker.a2ui.json +143 -24
  55. package/components/calendar-picker/calendar-picker.class.js +490 -197
  56. package/components/calendar-picker/calendar-picker.css +121 -172
  57. package/components/calendar-picker/calendar-picker.yaml +225 -47
  58. package/components/canvas/canvas.a2ui.json +6 -5
  59. package/components/canvas/canvas.d.ts +37 -0
  60. package/components/canvas/canvas.js +72 -3
  61. package/components/canvas/canvas.yaml +6 -2
  62. package/components/card/card.a2ui.json +25 -2
  63. package/components/card/card.class.js +3 -0
  64. package/components/card/card.css +34 -4
  65. package/components/card/card.d.ts +2 -2
  66. package/components/card/card.examples.md +3 -3
  67. package/components/card/card.yaml +28 -4
  68. package/components/chart/chart.a2ui.json +13 -8
  69. package/components/chart/chart.class.js +10 -10
  70. package/components/chart/chart.css +12 -10
  71. package/components/chart/chart.d.ts +2 -2
  72. package/components/chart/chart.yaml +8 -5
  73. package/components/chart-legend/chart-legend.a2ui.json +1 -0
  74. package/components/chat-thread/chat-input.a2ui.json +1 -0
  75. package/components/chat-thread/chat-thread.a2ui.json +10 -9
  76. package/components/chat-thread/chat-thread.css +1 -1
  77. package/components/chat-thread/chat-thread.yaml +13 -20
  78. package/components/check/check.a2ui.json +10 -9
  79. package/components/check/check.yaml +4 -4
  80. package/components/code/code.a2ui.json +1 -0
  81. package/components/code/code.css +6 -6
  82. package/components/col/col.a2ui.json +1 -0
  83. package/components/color-input/color-input.a2ui.json +1 -0
  84. package/components/color-picker/color-picker.a2ui.json +1 -0
  85. package/components/combobox/combobox.a2ui.json +3 -2
  86. package/components/combobox/combobox.css +2 -2
  87. package/components/combobox/combobox.yaml +2 -2
  88. package/components/command/command.a2ui.json +12 -10
  89. package/components/command/command.class.js +3 -2
  90. package/components/command/command.css +3 -3
  91. package/components/command/command.yaml +17 -4
  92. package/components/context-menu/context-menu.a2ui.json +1 -0
  93. package/components/date-range-picker/date-range-picker.a2ui.json +2 -1
  94. package/components/date-range-picker/date-range-picker.css +1 -1
  95. package/components/date-range-picker/date-range-picker.yaml +1 -1
  96. package/components/datetime-picker/datetime-picker.a2ui.json +15 -108
  97. package/components/datetime-picker/datetime-picker.class.js +37 -690
  98. package/components/datetime-picker/datetime-picker.css +11 -183
  99. package/components/datetime-picker/datetime-picker.d.ts +4 -5
  100. package/components/datetime-picker/datetime-picker.yaml +44 -135
  101. package/components/demo-toggle/demo-toggle.a2ui.json +1 -0
  102. package/components/demo-toggle/demo-toggle.css +1 -1
  103. package/components/description-list/description-list.a2ui.json +1 -0
  104. package/components/description-list/description-list.css +2 -2
  105. package/components/display-field/display-field.a2ui.json +1 -0
  106. package/components/display-field/display-field.css +3 -3
  107. package/components/divider/divider.a2ui.json +1 -0
  108. package/components/divider/divider.css +1 -1
  109. package/components/drawer/drawer.a2ui.json +1 -3
  110. package/components/drawer/drawer.css +3 -3
  111. package/components/drawer/drawer.yaml +0 -2
  112. package/components/drilldown/drilldown.a2ui.json +22 -20
  113. package/components/drilldown/drilldown.class.js +4 -3
  114. package/components/drilldown/drilldown.css +8 -8
  115. package/components/drilldown/drilldown.yaml +40 -9
  116. package/components/embed/embed.a2ui.json +6 -5
  117. package/components/embed/embed.yaml +2 -2
  118. package/components/empty-state/empty-state.a2ui.json +1 -0
  119. package/components/empty-state/empty-state.css +2 -2
  120. package/components/feed/feed-item.a2ui.json +25 -5
  121. package/components/feed/feed-item.yaml +28 -7
  122. package/components/feed/feed.a2ui.json +9 -2
  123. package/components/feed/feed.class.js +19 -12
  124. package/components/feed/feed.css +8 -8
  125. package/components/feed/feed.yaml +14 -1
  126. package/components/field/field.a2ui.json +1 -0
  127. package/components/field/field.css +1 -1
  128. package/components/field/field.examples.md +1 -1
  129. package/components/fields/fields.a2ui.json +1 -0
  130. package/components/fields/fields.examples.md +1 -1
  131. package/components/footer/footer.a2ui.json +1 -0
  132. package/components/frame/frame.a2ui.json +1 -0
  133. package/components/grid/grid.a2ui.json +1 -0
  134. package/components/header/header.a2ui.json +3 -2
  135. package/components/header/header.yaml +3 -3
  136. package/components/heatmap/heatmap.a2ui.json +2 -1
  137. package/components/heatmap/heatmap.class.js +1 -1
  138. package/components/heatmap/heatmap.css +6 -6
  139. package/components/heatmap/heatmap.yaml +1 -1
  140. package/components/icon/icon.a2ui.json +1 -0
  141. package/components/image/image.a2ui.json +28 -14
  142. package/components/image/image.class.js +38 -23
  143. package/components/image/image.css +18 -8
  144. package/components/image/image.d.ts +2 -2
  145. package/components/image/image.examples.md +3 -3
  146. package/components/image/image.yaml +23 -7
  147. package/components/inline-edit/inline-edit.a2ui.json +1 -0
  148. package/components/inline-message/inline-message.a2ui.json +3 -1
  149. package/components/inline-message/inline-message.class.js +3 -1
  150. package/components/inline-message/inline-message.css +1 -1
  151. package/components/inline-message/inline-message.d.ts +2 -2
  152. package/components/inline-message/inline-message.yaml +2 -1
  153. package/components/input/input.a2ui.json +66 -16
  154. package/components/input/input.class.js +64 -7
  155. package/components/input/input.css +19 -18
  156. package/components/input/input.d.ts +9 -4
  157. package/components/input/input.yaml +83 -10
  158. package/components/inspector/inspector.a2ui.json +1 -0
  159. package/components/integration-card/integration-card.a2ui.json +1 -0
  160. package/components/integration-card/integration-card.css +4 -4
  161. package/components/kbd/kbd.a2ui.json +1 -0
  162. package/components/kbd/kbd.css +1 -1
  163. package/components/link/link.a2ui.json +2 -1
  164. package/components/link/link.yaml +1 -1
  165. package/components/list/list-item.a2ui.json +9 -0
  166. package/components/list/list-item.yaml +12 -0
  167. package/components/list/list.a2ui.json +9 -3
  168. package/components/list/list.class.js +14 -1
  169. package/components/list/list.css +14 -3
  170. package/components/list/list.d.ts +7 -5
  171. package/components/list/list.yaml +16 -6
  172. package/components/list-window/list-window.a2ui.json +1 -0
  173. package/components/list-window/list-window.class.js +2 -1
  174. package/components/loading-overlay/loading-overlay.a2ui.json +1 -0
  175. package/components/mark/mark.a2ui.json +2 -1
  176. package/components/mark/mark.yaml +1 -1
  177. package/components/menu/menu-divider.a2ui.json +1 -0
  178. package/components/menu/menu-item.a2ui.json +3 -1
  179. package/components/menu/menu-item.yaml +6 -0
  180. package/components/menu/menu-label.a2ui.json +1 -0
  181. package/components/menu/menu.a2ui.json +3 -2
  182. package/components/menu/menu.class.js +2 -2
  183. package/components/menu/menu.d.ts +2 -2
  184. package/components/menu/menu.yaml +3 -3
  185. package/components/modal/modal.a2ui.json +1 -0
  186. package/components/nav/nav.a2ui.json +1 -0
  187. package/components/nav/nav.class.js +14 -3
  188. package/components/nav/nav.css +2 -2
  189. package/components/nav-group/nav-group.a2ui.json +1 -0
  190. package/components/nav-group/nav-group.class.js +3 -1
  191. package/components/nav-group/nav-group.css +3 -3
  192. package/components/nav-item/nav-item.a2ui.json +1 -0
  193. package/components/nav-item/nav-item.css +2 -2
  194. package/components/noodles/noodles.a2ui.json +1 -0
  195. package/components/noodles/noodles.class.js +5 -1
  196. package/components/number-format/number-format.a2ui.json +1 -0
  197. package/components/option-card/option-card.a2ui.json +1 -0
  198. package/components/option-card/option-card.css +1 -1
  199. package/components/otp-input/otp-input.a2ui.json +3 -2
  200. package/components/otp-input/otp-input.css +1 -1
  201. package/components/otp-input/otp-input.yaml +9 -2
  202. package/components/page/page.a2ui.json +13 -0
  203. package/components/page/page.css +2 -2
  204. package/components/page/page.d.ts +1 -1
  205. package/components/page/page.yaml +1 -0
  206. package/components/pagination/pagination.a2ui.json +1 -0
  207. package/components/pagination/pagination.class.js +3 -2
  208. package/components/pagination/pagination.css +1 -1
  209. package/components/pane/pane.a2ui.json +6 -0
  210. package/components/pane/pane.class.js +3 -3
  211. package/components/pane/pane.css +3 -3
  212. package/components/pane/pane.yaml +3 -0
  213. package/components/password-strength/password-strength.a2ui.json +1 -0
  214. package/components/pipeline-status/pipeline-status.a2ui.json +1 -0
  215. package/components/popover/popover.a2ui.json +7 -6
  216. package/components/popover/popover.class.js +2 -2
  217. package/components/popover/popover.d.ts +2 -2
  218. package/components/popover/popover.yaml +4 -4
  219. package/components/preview/preview.a2ui.json +1 -0
  220. package/components/preview/preview.class.js +5 -1
  221. package/components/progress/progress.a2ui.json +47 -24
  222. package/components/progress/progress.class.js +66 -11
  223. package/components/progress/progress.css +44 -26
  224. package/components/progress/progress.d.ts +7 -5
  225. package/components/progress/progress.examples.md +2 -2
  226. package/components/progress/progress.yaml +235 -53
  227. package/components/progress-row/progress-row.a2ui.json +16 -12
  228. package/components/progress-row/progress-row.css +2 -2
  229. package/components/progress-row/progress-row.d.ts +2 -2
  230. package/components/progress-row/progress-row.examples.md +2 -14
  231. package/components/progress-row/progress-row.yaml +28 -36
  232. package/components/qr-code/qr-code.a2ui.json +13 -12
  233. package/components/qr-code/qr-code.class.js +7 -7
  234. package/components/qr-code/qr-code.css +1 -1
  235. package/components/qr-code/qr-code.d.ts +10 -7
  236. package/components/qr-code/qr-code.yaml +18 -12
  237. package/components/radio/radio.a2ui.json +8 -7
  238. package/components/radio/radio.class.js +8 -0
  239. package/components/radio/radio.yaml +3 -3
  240. package/components/radio-group/radio-group.a2ui.json +5 -3
  241. package/components/radio-group/radio-group.class.js +11 -0
  242. package/components/radio-group/radio-group.yaml +7 -1
  243. package/components/range/range.a2ui.json +6 -0
  244. package/components/range/range.class.js +2 -2
  245. package/components/range/range.css +3 -3
  246. package/components/range/range.yaml +3 -0
  247. package/components/rating/rating.a2ui.json +1 -0
  248. package/components/rating/rating.css +1 -1
  249. package/components/relative-time/relative-time.a2ui.json +1 -0
  250. package/components/richtext/richtext.a2ui.json +2 -1
  251. package/components/richtext/richtext.css +3 -3
  252. package/components/richtext/richtext.yaml +1 -1
  253. package/components/row/row.a2ui.json +1 -0
  254. package/components/search/search.a2ui.json +1 -0
  255. package/components/search/search.class.js +4 -1
  256. package/components/section/section.a2ui.json +1 -0
  257. package/components/segment/segment.a2ui.json +21 -10
  258. package/components/segment/segment.css +20 -4
  259. package/components/segment/segment.d.ts +2 -1
  260. package/components/segment/segment.yaml +20 -13
  261. package/components/segmented/segmented.a2ui.json +36 -7
  262. package/components/segmented/segmented.class.js +113 -22
  263. package/components/segmented/segmented.css +3 -0
  264. package/components/segmented/segmented.d.ts +3 -1
  265. package/components/segmented/segmented.yaml +80 -8
  266. package/components/select/select.a2ui.json +18 -13
  267. package/components/select/select.class.js +61 -37
  268. package/components/select/select.css +18 -16
  269. package/components/select/select.yaml +18 -7
  270. package/components/skeleton/skeleton.a2ui.json +8 -7
  271. package/components/skeleton/skeleton.class.js +8 -7
  272. package/components/skeleton/skeleton.css +21 -6
  273. package/components/skeleton/skeleton.d.ts +2 -2
  274. package/components/skeleton/skeleton.examples.md +2 -2
  275. package/components/skeleton/skeleton.yaml +3 -2
  276. package/components/skip-nav/skip-nav.a2ui.json +1 -0
  277. package/components/slider/slider.a2ui.json +6 -0
  278. package/components/slider/slider.class.js +20 -3
  279. package/components/slider/slider.css +2 -2
  280. package/components/slider/slider.yaml +3 -0
  281. package/components/spinner/spinner.a2ui.json +1 -0
  282. package/components/spinner/spinner.css +1 -1
  283. package/components/stack/stack.a2ui.json +1 -0
  284. package/components/stat/stat.a2ui.json +1 -0
  285. package/components/stat/stat.css +3 -3
  286. package/components/stat/stat.js +2 -2
  287. package/components/step-progress/step-progress.a2ui.json +1 -0
  288. package/components/step-progress/step-progress.css +1 -1
  289. package/components/step-progress/step-progress.yaml +1 -1
  290. package/components/stepper/stepper-item.a2ui.json +7 -3
  291. package/components/stepper/stepper-item.yaml +15 -5
  292. package/components/stepper/stepper.a2ui.json +5 -1
  293. package/components/stepper/stepper.class.js +31 -16
  294. package/components/stepper/stepper.css +2 -2
  295. package/components/stepper/stepper.d.ts +1 -1
  296. package/components/stepper/stepper.yaml +4 -2
  297. package/components/stream/stream.a2ui.json +1 -0
  298. package/components/swatch/swatch.a2ui.json +1 -0
  299. package/components/swatch/swatch.css +2 -2
  300. package/components/swiper/swiper.a2ui.json +1 -0
  301. package/components/swiper/swiper.class.js +5 -1
  302. package/components/switch/switch.a2ui.json +12 -11
  303. package/components/switch/switch.css +1 -1
  304. package/components/switch/switch.yaml +6 -6
  305. package/components/table/table.a2ui.json +1 -10
  306. package/components/table/table.class.js +18 -16
  307. package/components/table/table.css +17 -18
  308. package/components/table/table.d.ts +0 -2
  309. package/components/table/table.yaml +0 -9
  310. package/components/table-toolbar/table-toolbar.a2ui.json +1 -0
  311. package/components/table-toolbar/table-toolbar.class.js +38 -19
  312. package/components/table-toolbar/table-toolbar.css +2 -2
  313. package/components/tabs/tab.a2ui.json +9 -0
  314. package/components/tabs/tab.js +4 -0
  315. package/components/tabs/tab.yaml +15 -0
  316. package/components/tabs/tabs.a2ui.json +9 -4
  317. package/components/tabs/tabs.class.js +13 -1
  318. package/components/tabs/tabs.css +9 -1
  319. package/components/tabs/tabs.d.ts +2 -0
  320. package/components/tabs/tabs.yaml +9 -2
  321. package/components/tag/tag.a2ui.json +1 -0
  322. package/components/tag/tag.css +2 -2
  323. package/components/tags-input/tags-input.a2ui.json +22 -17
  324. package/components/tags-input/tags-input.class.js +13 -11
  325. package/components/tags-input/tags-input.css +11 -7
  326. package/components/tags-input/tags-input.d.ts +16 -7
  327. package/components/tags-input/tags-input.examples.md +2 -2
  328. package/components/tags-input/tags-input.yaml +25 -8
  329. package/components/text/text.a2ui.json +6 -5
  330. package/components/text/text.css +10 -5
  331. package/components/text/text.d.ts +2 -2
  332. package/components/text/text.yaml +9 -4
  333. package/components/textarea/textarea.a2ui.json +2 -1
  334. package/components/textarea/textarea.css +1 -1
  335. package/components/textarea/textarea.yaml +1 -1
  336. package/components/theme-provider/theme-provider.a2ui.json +2 -1
  337. package/components/theme-provider/theme-provider.d.ts +5 -3
  338. package/components/theme-provider/theme-provider.yaml +5 -3
  339. package/components/time-picker/time-picker.a2ui.json +1 -0
  340. package/components/time-picker/time-picker.class.js +4 -2
  341. package/components/time-picker/time-picker.css +1 -1
  342. package/components/timeline/timeline-item.a2ui.json +26 -3
  343. package/components/timeline/timeline-item.yaml +22 -7
  344. package/components/timeline/timeline.a2ui.json +4 -0
  345. package/components/timeline/timeline.class.js +29 -18
  346. package/components/timeline/timeline.css +4 -4
  347. package/components/timeline/timeline.d.ts +3 -3
  348. package/components/timeline/timeline.yaml +2 -0
  349. package/components/toast/toast.a2ui.json +10 -3
  350. package/components/toast/toast.yaml +20 -13
  351. package/components/toc/toc.a2ui.json +1 -0
  352. package/components/toc/toc.css +1 -1
  353. package/components/toggle-group/toggle-group.a2ui.json +7 -3
  354. package/components/toggle-group/toggle-group.class.js +43 -12
  355. package/components/toggle-group/toggle-group.css +7 -4
  356. package/components/toggle-group/toggle-group.d.ts +3 -4
  357. package/components/toggle-group/toggle-group.yaml +22 -16
  358. package/components/toggle-group/toggle-option.a2ui.json +8 -4
  359. package/components/toggle-group/toggle-option.yaml +15 -13
  360. package/components/toggle-scheme/toggle-scheme.a2ui.json +2 -1
  361. package/components/toggle-scheme/toggle-scheme.yaml +1 -1
  362. package/components/toolbar/toolbar-group.a2ui.json +1 -0
  363. package/components/toolbar/toolbar.a2ui.json +1 -0
  364. package/components/toolbar/toolbar.class.js +3 -0
  365. package/components/tooltip/tooltip.a2ui.json +1 -0
  366. package/components/tooltip/tooltip.css +1 -1
  367. package/components/tour/tour-step.a2ui.json +4 -0
  368. package/components/tour/tour-step.yaml +2 -0
  369. package/components/tour/tour.a2ui.json +4 -0
  370. package/components/tour/tour.css +2 -2
  371. package/components/tour/tour.yaml +2 -0
  372. package/components/tree/tree-item.a2ui.json +5 -0
  373. package/components/tree/tree-item.yaml +3 -0
  374. package/components/tree/tree.a2ui.json +4 -0
  375. package/components/tree/tree.class.js +2 -1
  376. package/components/tree/tree.css +1 -1
  377. package/components/tree/tree.yaml +2 -0
  378. package/components/upload/upload.a2ui.json +8 -1
  379. package/components/upload/upload.class.js +35 -5
  380. package/components/upload/upload.css +28 -0
  381. package/components/upload/upload.d.ts +16 -0
  382. package/components/upload/upload.yaml +27 -7
  383. package/components/visually-hidden/visually-hidden.a2ui.json +1 -0
  384. package/core/dom.d.ts +11 -0
  385. package/core/dom.js +34 -0
  386. package/core/form.js +7 -0
  387. package/core/index.d.ts +1 -0
  388. package/core/index.js +1 -0
  389. package/core/responsive.js +16 -4
  390. package/core/template.js +2 -1
  391. package/custom-elements.json +442 -502
  392. package/dist/host.min.css +1 -1
  393. package/dist/host.sheet.js +1 -1
  394. package/dist/theme-provider.min.js +5 -5
  395. package/dist/web-components.min.css +1 -1
  396. package/dist/web-components.min.js +91 -104
  397. package/dist/web-components.sheet.js +1 -1
  398. package/package.json +4 -2
  399. package/patterns/conversion-funnel/conversion-funnel.examples.html +15 -15
  400. package/patterns/inline-dialog/inline-dialog.examples.html +2 -2
  401. package/patterns/inline-dialog/inline-dialog.html +4 -0
  402. package/patterns/timezone-selector/timezone-selector.examples.html +1 -1
  403. package/patterns/timezone-selector/timezone-selector.html +2 -2
  404. package/styles/api/text.css +7 -0
  405. package/styles/type/scale.css +6 -7
  406. package/traits/_catalog.json +129 -41
  407. package/traits/anchor-positioning/anchor-positioning.js +1 -0
  408. package/traits/announcer/announcer.js +1 -0
  409. package/traits/arrow-grid-nav/arrow-grid-nav.js +1 -0
  410. package/traits/attention-pulse/attention-pulse.js +1 -0
  411. package/traits/count-up/count-up.js +1 -0
  412. package/traits/define.js +78 -0
  413. package/traits/focus-restore/focus-restore.js +2 -1
  414. package/traits/haptic-feedback/haptic-feedback.js +1 -0
  415. package/traits/input-mask/input-mask.js +1 -0
  416. package/traits/intersection-observer/intersection-observer.js +1 -0
  417. package/traits/layout-animation/layout-animation.js +1 -0
  418. package/traits/noise-texture/noise-texture.js +1 -0
  419. package/traits/resize-observer/resize-observer.js +1 -0
  420. package/traits/scroll-progress/scroll-progress.js +1 -0
  421. package/traits/snap-to-grid/snap-to-grid.js +1 -0
  422. package/traits/sound-feedback/sound-feedback.js +1 -0
  423. package/traits/spring-animate/spring-animate.js +1 -0
  424. package/traits/validation/validation.js +1 -0
package/MIGRATION.md CHANGED
@@ -41,7 +41,7 @@ Version cuts and migration scope:
41
41
  - **Recommended consumer migration:** if you were using `@adia-ai/web-modules/runtime/a2ui-root/a2ui-root.js` + `@adia-ai/web-components/components/canvas/canvas.js` for generative UI surfaces, collapse to a single `import '@adia-ai/web-modules/generative'`. Both old paths still work.
42
42
  - See [§ v0.6.7 section below](#migrating-to-adia-aiv067-2026-05-19).
43
43
 
44
- - **Upcoming (variant/color/tone convention cut)** — **BREAKING:** `accent` is retired as an enum value on `badge-ui` (`variant`), `tag-ui` (`variant`), and `button-ui` (`color`) — `primary` is the brand-emphasis value on badge/tag (identical rendering; pure rename). Badge-ui's `variant` becomes family-only: `variant="muted"` / `variant="neutral"` are removed — both map to `tone="muted"` (the fill-style axis they always belonged on). Badge's family-less `tone="muted"` also re-tokens to the `--md-sys-color-neutral-low` fill (a visibly stronger tint — no markup change). The same one-axis rule then reaches **alert-ui** (`variant="muted"` / `"neutral"` removed — they were exact aliases of the default, so just drop the attribute; zero visual change) and **mark-ui** (`variant="muted"` removed with no enum successor — a neutral highlight is now a `--mark-bg`/`--mark-fg` token override); neither gains a `tone` axis. `accent` on OTHER components (icon-ui, text-ui, chart-ui, …) is unchanged. Pre-1.0 PATCH cadence. See [§ Upcoming — variant/color/tone convention cut](#upcoming-variant-color-tone-convention-cut).
44
+ - **Shipped in `0.8.33` (variant/color/tone convention cut, ADR-0044)** — **BREAKING:** `accent` is retired as an enum value on `badge-ui` (`variant`), `tag-ui` (`variant`), and `button-ui` (`color`) — `primary` is the brand-emphasis value on badge/tag (identical rendering; pure rename). Badge-ui's `variant` becomes family-only: `variant="muted"` / `variant="neutral"` are removed — both map to `tone="muted"` (the fill-style axis they always belonged on). Badge's family-less `tone="muted"` also re-tokens to the `--md-sys-color-neutral-low` fill (a visibly stronger tint — no markup change). The same one-axis rule then reaches **alert-ui** (`variant="muted"` / `"neutral"` removed — they were exact aliases of the default, so just drop the attribute; zero visual change) and **mark-ui** (`variant="muted"` removed with no enum successor — a neutral highlight is now a `--mark-bg`/`--mark-fg` token override); neither gains a `tone` axis. `accent` on OTHER components (icon-ui, text-ui, chart-ui, …) is unchanged. See [§ Shipped in `0.8.33` — variant/color/tone convention cut](#upcoming-variant-color-tone-convention-cut).
45
45
 
46
46
  - **Upcoming (`<theme-provider>` cut)** — **BREAKING:** the token-theme attribute is renamed `data-theme` → `theme` repo-wide. Replace `<html data-theme="ocean">` → `<html theme="ocean">` and any `[data-theme="…"]` selectors → `[theme="…"]`. Mechanical: a word-boundary find-replace of `data-theme` → `theme` (preserve compound attrs like `data-theme-slug` / `data-themed`). Pre-1.0 PATCH cadence. Also new: the **`<theme-provider>`** element adopts the AdiaUI foundation into the document from anywhere (no `<head>` stylesheet link), with opt-in `theme="…"` (named preset) + `scale="verse|prose"` (typographic register) attributes that fetch their layer on demand.
47
47
  - **`theme` ≠ `scheme` — don't conflate the two axes.** This rename is purely the *theme* axis (a named preset: ocean / forest / slate / …). A light/dark *mode* is the orthogonal *scheme* axis — it belongs on `color-scheme` / `data-scheme`, **never** on `[theme]`. If you'd encoded a mode as `data-theme="dark"`, that was a category error: after the rename it is a no-op on *both* axes. Move it to the scheme axis — `<html style="color-scheme: dark">`, `data-scheme`, or `<toggle-scheme-ui>`. (Surfaced by color-app, 2026-06-07.)
@@ -54,6 +54,34 @@ Version cuts and migration scope:
54
54
 
55
55
  - **`0.8.37`** — PATCH lockstep cut. **BREAKING: the a2ui / gen-ui estate split (ADR-0048).** Six published package names (`a2ui-runtime`, `a2ui-compose`, `a2ui-corpus`, `a2ui-retrieval`, `a2ui-validator`, `a2ui-mcp`) are replaced by four (`a2ui`, `gen-ui`, `gen-ui-mcp`, `a2ui-protocol-mcp`). **A patch-shaped version number does not make this non-breaking** — every old name is renamed. One-cycle re-export shims bridge the gap; the cut AFTER `0.8.37` retires them. See [§ 0.8.37 — the a2ui / gen-ui estate split](#0837--the-a2ui--gen-ui-estate-split-adr-0048).
56
56
 
57
+ - **Shipped in `0.8.40` (attribute-grammar reconciliation, ADR-0054)** — **BREAKING, no deprecation alias:** `table-ui[density]` converges onto the ambient global `[density]` attribute — the component-local `compact`/`standard`/`comfortable` enum is removed. Migration: `density="compact"` stays `density="compact"`; `density="standard"` → drop the attribute (unset is the default); `density="comfortable"` → `density="spacious"`. `chart-ui[radius]` renames to `chart-ui[corner-radius]` (same `Number`/`null` semantics, same `--a-radius` fallback) — a mechanical attribute-name rename, no value-shape change. `swatch-ui[color]` and `noodles-ui[color]` are ratified exceptions and are unaffected. See [§ Shipped in `0.8.40` — attribute-grammar reconciliation](#upcoming-0840-attribute-grammar-reconciliation-adr-0054).
58
+
59
+ - **Shipped in `0.8.40` (`qr-code-ui` quiet-zone rename)** — **BREAKING:** `<qr-code-ui>`'s reflected `[margin]` attribute is renamed `[quiet-zone]` (property `quietZone`) — same type, default (`4`), and reflection; only the name changes, per [ADR-0053](../../docs/ops/adr/adr-0053-no-shadowing-global-attributes.md). `margin` on `qr-code-ui` now means the framework's **global** `[margin]` Scale attribute (box-model spacing), not the QR quiet zone — the two grammars silently collided on the same syntax with unrelated meanings. **No deprecation alias** — keeping `margin` as an alias would preserve the collision the rename exists to remove. Audit existing usages: `<qr-code-ui margin="4">` intending a quiet zone must become `<qr-code-ui quiet-zone="4">`. Shipped in `0.8.40`. See [§ Shipped in `0.8.40` — qr-code-ui quiet-zone rename](#upcoming--qr-code-ui-quiet-zone-rename-adr-0053).
60
+
61
+ - **Shipped in `0.8.40` (leading-scale consolidation)** — **BREAKING, no aliases:** the legacy `--a-leading-*` token family is removed per [ADR-0052](../../docs/ops/adr/adr-0052-leading-scale-consolidation.md) — `--a-font-leading-*` (+ new `--a-font-leading-none: 1` step) is now the single canonical leading scale. Value-nearest replacement table: `none→--a-font-leading-none` (1), `tight→--a-font-leading-snug` (1.2), `snug→--a-font-leading-normal` (1.35), `normal→--a-font-leading-relaxed` (1.5). The same gh#1298 sweep also tokenized every hardcoded `line-height`/`letter-spacing`/`font-weight` literal across `packages/web-components/components/*/*.css` (1.3 and 1.4 both → `--a-font-leading-normal`; 0.05em → `--a-font-tracking-wide`) and added a mechanized `npm run check:typography-tokens` gate. See [§ Shipped in `0.8.40` — leading-scale consolidation](#upcoming--leading-scale-consolidation-adr-0052).
62
+
63
+ - **Shipped in `0.8.40` (`toggle-option-ui` drops `data-selected`)** — **BREAKING, no deprecation alias:** `<toggle-group-ui>`'s legacy `data-selected` parallel stamp on its `<toggle-option-ui>` children is removed per [ADR-0056](../../docs/ops/adr/adr-0056-selection-item-selected-convention.md) — the declared, reflected `selected` prop (shipped additively in `0.8.39`) is now the only current-state surface, matching `<segment-ui>`'s convention. Migration: any `[data-selected]` CSS or script targeting `toggle-option-ui` moves to `[selected]`. See [§ Shipped in `0.8.40` — `toggle-option-ui` drops the legacy `data-selected` stamp](#upcoming--toggle-option-ui-drops-the-legacy-data-selected-stamp-0840-adr-0056).
64
+
65
+ - **Shipped in `0.8.41` (`progress-ui` retires `variant`, gh#1370)** — **BREAKING, no deprecation alias:** `<progress-ui>`'s `variant` prop (`bar` | `spinner`) is removed — progress is bar-only now. The former `variant="spinner"` render mode (a Sanctioned Mode Attribute per the component-token-contract) is gone; `<spinner-ui>` owns circular loading indicators (`arc`/`ring`/`dots`/`knight`). Migration: `<progress-ui variant="spinner">` → `<spinner-ui>` (pick the closest `variant`; `arc` is the nearest visual match to the old spinner render). `<progress-ui variant="bar">` → drop the attribute (bar was already the default). Filed from the catalog-regularity audit's C2 finding (gh#1363) — a 2026-06 merge folded a standalone spinner into progress as `variant="spinner"`, then a richer standalone `spinner-ui` was re-added later, leaving two circular-loading answers in the catalog; this rider un-forks them. See [§ Shipped in `0.8.41` — `progress-ui` retires `variant`](#upcoming--progress-ui-retires-variant-090-gh1370).
66
+
67
+ - **Shipped in `0.8.41` (`input-ui` trims `type` enum, gh#1381)** — **BREAKING, no deprecation alias:** `<input-ui>`'s `type` enum drops `search`/`date`/`datetime-local`/`time`/`month`/`week`/`color` — none ever rendered a native affordance (all seven fell back to a plain contenteditable surface), so the enum was first-pass-VALID but functionally broken generation output. The implemented set is now exactly `text`/`email`/`password`/`number`/`tel`/`url`. Migration: `type="search"` → `<search-ui>`; `type="date"`/`"month"`/`"week"` → `<calendar-picker-ui>` (day granularity only — no dedicated month/week primitive exists); `type="datetime-local"` → `<datetime-picker-ui>`; `type="time"` → `<time-picker-ui>`; `type="color"` → `<color-input-ui>` (or `<color-picker-ui>` directly for a full-surface editor). Filed from the catalog-regularity audit's B10 finding (gh#1363). See [§ Shipped in `0.8.41` — `input-ui` trims `type` enum](#upcoming--input-ui-trims-type-enum-090-gh1381).
68
+
69
+ - **Shipped in `0.8.41` (web-modules `data-*` state converges to bare attributes, gh#1332)** — **BREAKING, no deprecation alias:** two web-modules' declared `states:` entries drop a private `data-*` stamp for a bare state attribute, per the data-* usage sweep's Category A disposition (ADR-0056's rule generalized past `toggle-option-ui`). `<integrations-page-ui>`'s `empty-search`/`empty-data` states (was `data-empty-search`/`data-empty-data`) and `<onboarding-checklist-ui>`'s `complete` state (was `data-complete`) now match their own sibling states' existing bare-attribute convention (`loading`, `collapsed`). Migration: any `[data-empty-search]`/`[data-empty-data]`/`[data-complete]` CSS or script targeting these two modules moves to `[empty-search]`/`[empty-data]`/`[complete]`. See [§ Shipped in `0.8.41` — web-modules data-* state converges to bare attributes](#upcoming--web-modules-data--state-converges-to-bare-attributes-090-gh1332).
70
+
71
+ - **Shipped in `0.8.41` (`action-item-ui` converges onto `menu-item-ui`'s row contract, gh#1374)** — **BREAKING, no deprecation alias, zero known consumer sites.** `<action-item-ui>` gains a `subtitle` prop + `text`/`subtitle`/`trailing` slot overrides, matching `<menu-item-ui>`'s row contract exactly. The unnamed default slot this row previously used for trailing content (kbd shortcut, badge, caret) is retired in favor of an explicit `slot="trailing"` — a monorepo-wide sweep at dispatch time found no consumer relying on it (every `<action-item-ui>` usage is self-closing/attribute-only). Migration: any external consumer slotting unnamed trailing content into `<action-item-ui>` adds `slot="trailing"` to that child. Filed from the catalog-regularity audit's C5 finding (gh#1363) — action-list's container-level "should this become a `list-ui` mode" question was decided as NO (kept as a separate component; see the ticket's Findings for rationale), leaving only this row-level convergence to ship. See [§ Shipped in `0.8.41` — `action-item-ui` converges onto `menu-item-ui`'s row contract](#upcoming--action-item-ui-converges-onto-menu-item-uis-row-contract-090-gh1374).
72
+
73
+ - **Shipped in `0.8.41` (global-attribute-shadowing collision sweep, gh#1335)** — **BREAKING, no deprecation aliases:** of the 16 pre-existing component-local attributes that shadowed the global attribute grammar (surfaced by the `check:attribute-shadowing` gate, ADR-0053/0054's own decision-4 gate), 14 are resolved here — 11 renamed/converged, 3 exempted (an ADR-ratified §11 pass-through, no code change) — plus a 17th collision this same ticket's own follow-up audit surfaced (`dashboard-layout-ui[gap]`, never in the gate's original 16 since its shape wasn't caught by the gate's type-only check), also renamed here, for **12 renames/conversions + 3 exemptions = 15 items resolved of 17 tracked**: `header-ui[padding]` → `[padded]`; `menu-ui`/`popover-ui[gap]` → `[offset]`; `skeleton-ui`/`image-ui[radius]` → `[corner-radius]`; `invoice-detail-ui`/`invoice-history-ui`/`dashboard-layout-ui[density]` converge onto `compact`/`spacious`/unset; `dashboard-layout-ui[gap]` → `[band-gap]`; `text-ui[weight]` `regular` → `normal`; `text-ui[text-align]` converges onto the global physical vocabulary (`start`/`end` → `left`/`right`; `justify` newly supported by the global `[text-align]` grammar too); `tags-input-ui[transform]` → `[normalize]`; `button-ui`/`toggle-scheme-ui`/`text-ui[color]` are newly §11-ratified exemptions (ADR-0044/ADR-0064 — these play the FAMILY axis, a different grammar from the generic descriptive `[color]`, not a foreign-value violation). `qr-code-ui[color]` and `icon-ui[weight]` were left open by this sweep (both genuine §11-exemption candidates with no ratifying ADR at the time, tracked in the gate's own `KNOWN_FINDINGS`) and are since ratified as §11 exemptions by ADR-0070 (gh#1500) — no code change, closing the ledger at 17 of 17. See [§ Shipped in `0.8.41` — global-attribute-shadowing collision sweep](#upcoming--global-attribute-shadowing-collision-sweep-090-gh1335).
74
+
75
+ - **Shipped in `0.8.41` (A2UI catalog `component:` collision — `AdminShell` / `UIChatThread`, gh#1427)** — **BREAKING for hand-authored/generated A2UI documents only — no DOM/JS API or tag-name change.** `admin-shell.yaml` and `embed-shell.yaml` both declared catalog identity `component: AppShell`, so alphabetical build traversal silently dropped `admin-shell`'s L0 catalog entry (embed-shell's fragment won). Fixed: `admin-shell`'s catalog identity is now `AdminShell` (its own established `registry.js` mapping); `embed-shell` keeps `AppShell`. The same build-time collision guard added to catch this also caught `chat-thread-ui`'s primitive yaml still declaring `component: ChatThread`, colliding with the chat web-module's own `ChatThread` — the primitive's catalog identity is now `UIChatThread`. A raw a2ui tree with `{"component": "AppShell"}` meaning the stable admin shell, or `{"component": "ChatThread"}` meaning the standalone primitive, needs updating; the HTML tags (`<admin-shell>`, `<chat-thread-ui>`) are unaffected. See [§ Shipped in `0.8.41` — A2UI catalog `component:` collision](#upcoming--a2ui-catalog-component-collision-adminshell--uichatthread-090-gh1427).
76
+
77
+ - **Shipped in `0.8.41` (`progress-row-ui` deprecated — absorbed into `progress-ui`, gh#1373)** — **Non-breaking, deprecation alias.** `<progress-ui>` gains `label` + `meta` props; a non-empty `label` activates the identical labeled-row layout `<progress-row-ui>` used to own (a new Sanctioned Mode Attribute per the component-token-contract). `<progress-row-ui>` keeps working unchanged (still composes `<progress-ui>` internally) but ships `status: deprecated` and is no longer taught to the A2UI catalog or generated for new work. Migration: `<progress-row-ui label="…" value="…" meta="…">` → `<progress-ui label="…" value="…" meta="…">` — attribute names are identical, a mechanical tag rename. `variant` (the color-family prop on the old component) has **no successor on `<progress-ui>`** — progress's `variant` prop was already retired bar-only in the gh#1370 C2 rider and stayed retired; recolor the fill via the `--progress-fill` token instead (`--progress-fill: var(--a-success-bg)` etc.), consistent with progress's existing token-only convention. Filed from the catalog-regularity audit's C4 finding (gh#1363) — progress's own canonical catalog example already put `label` on a `Progress` node, meaning generation copying that exemplar failed validation before this rider. See [§ Shipped in `0.8.41` — `progress-row-ui` deprecated](#upcoming--progress-row-ui-deprecated--absorbed-into-progress-ui-090-gh1373).
78
+ - **Shipped in `0.8.41` (`segmented-ui` gains `[multiple]`; `toggle-group-ui`/`toggle-option-ui` deprecated, gh#1369)** — **Additive, non-breaking + soft deprecation (no removal).** `<segmented-ui>` gains a `multiple: Boolean` axis (default `false`, positive polarity matching `select-ui[multiple]`): under `[multiple]`, `value` is a comma-separated set (same encoding `toggle-group-ui` and `select-ui[multiple]` already use), the sliding indicator is suppressed, each selected `<segment-ui>` carries its own background instead, and Space/Enter toggles a focused segment rather than arrow-move selecting it. `<toggle-group-ui>` + `<toggle-option-ui>` are marked `status: deprecated` and log a one-time `console.warn` — they keep working exactly as before, no behavior change. **Recommended migration:** `<toggle-group-ui value="…">` + `<toggle-option-ui>` children → `<segmented-ui multiple value="…">` + `<segment-ui>` children (value encoding is identical; `text`/`icon`/`disabled` map directly). Filed from the catalog-regularity audit's C1 finding (gh#1363) — the strongest-ranked consolidation candidate: `toggle-group-ui` already had a `single` mode prop making it segmented, and had zero corpus chunks vs. segmented/segment's 19 each, so multi-select intents retrieved nothing or mis-retrieved single-select chunks. See [§ Shipped in `0.8.41` — `segmented-ui` gains `[multiple]`](#upcoming--segmented-ui-gains-multiple-toggle-group-uitoggle-option-ui-deprecated-090-gh1369).
79
+
80
+ - **Shipped in `0.8.41` (`calendar-picker-ui` absorbs `datetime-picker-ui`, gh#1372)** — **Mostly additive + soft deprecation; two real breaking removals on `calendar-picker-ui` itself.** `<calendar-picker-ui>` gains a `precision` axis (`date`|`minute`|`second`, default `date` — unchanged for every existing consumer) plus `step`/`hour-cycle`/`locale` and the `input`/`open`/`close`/`invalid` events datetime mode needs. `<datetime-picker-ui>` is now `status: deprecated`, implemented as a thin `calendar-picker-ui` subclass (`precision="minute"` by default) — it keeps working unchanged; **recommended (non-breaking) migration:** `<datetime-picker-ui>` → `<calendar-picker-ui precision="minute">`. **Breaking, no deprecation alias:** `<calendar-picker-ui>`'s own `label` prop/slot are removed — wrap in `<field-ui label>` instead, matching every sibling picker (this was `calendar-picker-ui`'s own outlier); and `<datetime-picker-ui>`'s `--datetime-picker-*` CSS tokens are removed — styling now flows through `<calendar-picker-ui>`'s shared `--calendar-picker-*` tokens. Filed from the catalog-regularity audit's C3c/C3d findings (gh#1363). See [§ Shipped in `0.8.41` — `calendar-picker-ui` absorbs `datetime-picker-ui`](#upcoming--calendar-picker-ui-absorbs-datetime-picker-ui-behind-a-widened-precision-axis-090-gh1372).
81
+ - **Shipped in `0.8.41` (primitive-tier `data-*` state converges to bare attributes, gh#1332)** — **BREAKING, no deprecation alias.** The residual primitive-tier round of the [data-* usage sweep](https://github.com/adiahealth/gen-ui-kit/issues/1332) (Category A), following the web-modules round above: `<feed-item-ui>`'s `[data-open]`/`[data-closing]` states and its `[data-queued]` max-queue marker (stamped by the parent `<feed-ui>`'s post logic, carried on the item) converge to bare `[open]`/`[closing]`/`[queued]`; `<slider-ui>` and `<range-ui>`'s `[data-dragging]` converges to bare `[dragging]`; `<pane-ui>`'s `[data-resizing]` converges to bare `[resizing]` (matching admin-sidebar/chat-sidebar's own `[resizing]` convention from the web-modules round); `<chart-ui>`'s `[data-has-data]` converges to bare `[has-data]`; `<select-ui>`'s private `[data-multi-chips]` mirror is dropped outright — its 8 CSS selectors now key off the compound `[multiple]:not([summary-label])`, both already-declared reflected props, no bare successor attribute needed. See [§ Shipped in `0.8.41` — primitive-tier data-* state converges to bare attributes](#upcoming--primitive-tier-data--state-converges-to-bare-attributes-090-gh1332-2).
82
+
83
+ - **Shipped in `0.8.41` (`image-ui` load/error states declared + converge to bare attributes, gh#1464)** — **BREAKING, no deprecation alias.** The final residue of the [data-* usage sweep](https://github.com/adiahealth/gen-ui-kit/issues/1332), deferred out of the residual round above because it needed a new `states:` declaration rather than a bare mapping change: `<image-ui>`'s `[data-loaded]`/`[data-error]` were genuine undeclared host-level state (never yaml-declared at all) — now declared as `loaded`/`error` in `image.yaml`'s `states:` list with bare `attribute:` mappings, matching qr-code-ui's own converged `error` state. Migration: any `[data-loaded]`/`[data-error]` CSS or script targeting `image-ui` moves to `[loaded]`/`[error]`; `image-ui`'s unrelated `[data-overlay]` skeleton-proximity marker (gh#1362) is untouched. A repo-wide consumer sweep (`rg -nU 'data-loaded|data-error'`) found zero external consumers of image-ui's forms specifically, so no deprecation alias was kept. The same ticket also dispositioned the `data-msg-*` validation-message config family (`data-msg-required`/`pattern`/`minlength`/`maxlength`/`min`/`max`/`bad-input`) as **exempt, non-breaking, no migration** — it stays `data-*` by design (component-side config read by `core/form.js`'s shared mixin across 7+ components, not host-reflected state; see `primitive-authoring/references/yaml-contract.md`'s new `data-msg-*` section for the full rationale). See [§ Shipped in `0.8.41` — image-ui load/error states declared + converge to bare attributes](#upcoming--image-ui-loaderror-states-declared--converge-to-bare-attributes-090-gh1464).
84
+
57
85
  Live demos and per-component docs at [ui-kit.exe.xyz/site/](https://ui-kit.exe.xyz/site/). The `adia-ui-migration` skill (in `.agents/skills/`) automates most of the mechanical sweeps in this guide.
58
86
 
59
87
  ---
@@ -65,7 +93,7 @@ cut renames six published packages. Nothing about the rename is patch-shaped
65
93
  except the number, which is an operator ruling (gh#1192, 2026-08-13) about
66
94
  release cadence, not a claim that your imports keep working forever.
67
95
 
68
- The split is by concept, ruled in `docs/adr/adr-0048-a2ui-gen-ui-estate-split.md`:
96
+ The split is by concept, ruled in `docs/ops/adr/adr-0048-a2ui-gen-ui-estate-split.md`:
69
97
 
70
98
  - **`@adia-ai/a2ui` — the protocol.** How one system renders gen-UI canvases
71
99
  in another system, safely: renderer, registry, streams, surface, wiring,
@@ -242,7 +270,8 @@ between a dependency and a peer:
242
270
 
243
271
  ---
244
272
 
245
- ## Upcoming — variant/color/tone convention cut
273
+ <a id="upcoming-variant-color-tone-convention-cut"></a>
274
+ ## Shipped in `0.8.33` — variant/color/tone convention cut (ADR-0044, gh#873/874/875/876/881)
246
275
 
247
276
  **One theme: every color-ish attribute owns exactly one axis.** `variant`/`tone` on badge & tag
248
277
  and `variant`/`color` on button now each carry either a *semantic family* or a *fill style*,
@@ -384,6 +413,1243 @@ npm run check # in-repo consumers; downstream: your app's build + a visual pas
384
413
 
385
414
  ---
386
415
 
416
+ <a id="upcoming-0840-attribute-grammar-reconciliation-adr-0054"></a>
417
+ ## Shipped in `0.8.40` — attribute-grammar reconciliation (ADR-0054)
418
+
419
+ **BREAKING, no deprecation alias.** [ADR-0054](../../docs/ops/adr/adr-0054-attribute-grammar-reconciliation.md)
420
+ resolves two component-local attributes that shadowed the [global attribute
421
+ grammar](../../.claude/docs/specs/attribute-api-system.md) with divergent vocabularies —
422
+ `table-ui[density]` **converges** onto the global mechanism (it already did
423
+ the job), `chart-ui[radius]` **renames** (a numeric px value is a genuinely
424
+ different shape than the global preset enum). Per [ADR-0053](../../docs/ops/adr/adr-0053-no-shadowing-global-attributes.md),
425
+ neither collision is exempted — `swatch-ui[color]` / `noodles-ui[color]` are
426
+ the only two ratified exceptions (documented in
427
+ [§11 of attribute-api-system.md](../../.claude/docs/specs/attribute-api-system.md#11-global-attribute-exemption-list)),
428
+ and neither is touched by this cut.
429
+
430
+ ### `table-ui[density]` converges onto the global grammar
431
+
432
+ The component-local `compact | standard | comfortable` enum is removed —
433
+ `table-ui` no longer reflects a `density` prop or JS property at all.
434
+ `[density]` on `table-ui` is now the ordinary ambient global attribute
435
+ (`compact | spacious`, unset = the middle) — `--table-py`/`--table-px`
436
+ already derive from `--a-space-*` steps, which the global `[density]`
437
+ attribute already scales via `--a-density`.
438
+
439
+ **Value migration** (apply wherever `<table-ui density="…">` appears):
440
+
441
+ | Old value | New value |
442
+ |---|---|
443
+ | `density="compact"` | `density="compact"` (unchanged) |
444
+ | `density="standard"` | drop the attribute entirely (unset is the default) |
445
+ | `density="comfortable"` | `density="spacious"` |
446
+
447
+ ```bash
448
+ # find every table-ui density usage in your tree
449
+ git grep -nE '<table-ui[^>]*\bdensity='
450
+
451
+ # comfortable → spacious (mechanical)
452
+ git grep -lz '<table-ui[^>]*density="comfortable"' | while IFS= read -r -d '' f; do
453
+ perl -i -pe 's/(<table-ui[^>]*density=")comfortable(")/${1}spacious${2}/g' "$f"
454
+ done
455
+
456
+ # standard → drop the attribute (review each hit; not always a safe blind sed
457
+ # across multi-line tags)
458
+ git grep -nE '<table-ui[^>]*density="standard"'
459
+ ```
460
+
461
+ If you read/write a table's `density` as a JS property (`el.density`) or
462
+ persisted it via `table-ui`'s `getState()`/`setState()`, switch to
463
+ `getAttribute('density')`/`setAttribute('density', …)` — the reflected
464
+ property and the `getState()`/`setState()` `density` key are both gone.
465
+ Composites that forward density to an embedded `table-ui` (own component,
466
+ own vocabulary) must translate through the same table above before setting
467
+ the child's attribute — see `invoice-history.class.js`'s `mapDensity()` for
468
+ the pattern.
469
+
470
+ ### `chart-ui[radius]` renames to `chart-ui[corner-radius]`
471
+
472
+ Same `Number` type, same `default: null` (falls back to `--a-radius`), same
473
+ behavior — only the attribute name changes.
474
+
475
+ ```bash
476
+ git grep -nE '<chart-ui[^>]*\bradius='
477
+ git grep -lz '<chart-ui[^>]*radius="' | while IFS= read -r -d '' f; do
478
+ perl -i -pe 's/(<chart-ui[^>]*)\bradius=/${1}corner-radius=/g' "$f"
479
+ done
480
+ ```
481
+
482
+ If you set it via JS, `chart.radius = 8` → `chart.cornerRadius = 8`.
483
+
484
+ ### Verify
485
+
486
+ ```bash
487
+ git grep -nE '<table-ui[^>]*\bdensity="(standard|comfortable)"' # must be 0
488
+ git grep -nE '<chart-ui[^>]*\bradius=' # must be 0
489
+ npm run check
490
+ ```
491
+
492
+ ---
493
+
494
+ <a id="upcoming--qr-code-ui-quiet-zone-rename-adr-0053"></a>
495
+ ## Shipped in `0.8.40` — qr-code-ui quiet-zone rename (ADR-0053)
496
+
497
+ **BREAKING, ships in `0.8.40`.** `<qr-code-ui>`'s reflected `[margin]` attribute is
498
+ renamed `[quiet-zone]` (property `quietZone`) — same `number` type, same default (`4`), same
499
+ reflection; only the name changes.
500
+
501
+ **Why:** the framework's global attribute grammar (`styles/api/*.css`) is a pitch — one
502
+ grammar, inferable: `margin="4"` means the same thing on every element, resolving through one
503
+ Scale mechanism (`--a-margin`). `qr-code-ui` declared its own reflected `margin` meaning the QR
504
+ "quiet zone" in grid cells. Both grammars accepted the identical syntax with no error surfaced,
505
+ so an author reaching for `<qr-code-ui margin="4">` expecting global spacing silently got a
506
+ quiet-zone resize instead, and vice versa. [ADR-0053](../../docs/ops/adr/adr-0053-no-shadowing-global-attributes.md)
507
+ rules that no component-local attribute may shadow a global one; this is the first instance
508
+ resolved under that policy (gh#1299).
509
+
510
+ **No deprecation alias.** Keeping `margin` as an alias on `qr-code-ui` would preserve the exact
511
+ collision the rename exists to remove — `margin` on `qr-code-ui` reverts to meaning what it
512
+ means everywhere: the global Scale attribute.
513
+
514
+ ### Mechanical sweep
515
+
516
+ ```bash
517
+ git grep -nE '<qr-code-ui[^>]*\smargin='
518
+ ```
519
+
520
+ Every hit intending the QR quiet zone becomes `quiet-zone`:
521
+
522
+ ```html
523
+ <!-- before --> <qr-code-ui value="…" margin="4"></qr-code-ui>
524
+ <!-- after --> <qr-code-ui value="…" quiet-zone="4"></qr-code-ui>
525
+ ```
526
+
527
+ **Programmatic setters** (JS render paths — the grep above misses these):
528
+
529
+ ```bash
530
+ git grep -rnE "setAttribute\('margin', |\.margin\s*="
531
+ ```
532
+
533
+ Filter to `qr-code-ui` hosts only — `margin` is a legitimate global attribute on every other
534
+ element, so only rewrite a hit that is actually a `<qr-code-ui>` reference:
535
+ `setAttribute('margin', …)` → `setAttribute('quiet-zone', …)`; `el.margin = …` → `el.quietZone
536
+ = …`.
537
+
538
+ ### Verify
539
+
540
+ ```bash
541
+ git grep -nE '<qr-code-ui[^>]*\smargin=' # must be 0
542
+ npm run check # in-repo consumers; downstream: your app's build
543
+ ```
544
+
545
+ ---
546
+
547
+ <a id="upcoming--leading-scale-consolidation-adr-0052"></a>
548
+ ## Shipped in `0.8.40` — leading-scale consolidation (ADR-0052)
549
+
550
+ **BREAKING, no aliases, ships in `0.8.40`.** [ADR-0052](../../docs/ops/adr/adr-0052-leading-scale-consolidation.md)
551
+ retires the legacy `--a-leading-*` family. `--a-font-leading-*` is now the
552
+ single canonical line-height scale.
553
+
554
+ **Why:** two leading-token families coexisted in `scale.css` with the same
555
+ step names resolving to *different* values (`--a-leading-snug` = `1.3` vs.
556
+ `--a-font-leading-snug` = `1.2`). A component author reaching for a leading
557
+ token had no obviously-correct choice — which is why a corpus-wide sweep
558
+ (gh#1298) found 55 component files reaching for a bare numeric literal
559
+ instead of either family. `--a-font-leading-*` is what the L2 role layer
560
+ already consumes and its shape (tightening as size grows) is the design
561
+ intent on record. One step closes its only gap: `--a-font-leading-none: 1`.
562
+
563
+ **No value-preserving aliases.** `snug`/`normal` name different values
564
+ across the two families — aliasing one name to the other would silently
565
+ restyle every consumer of the alias. Replacement table (value-nearest, not
566
+ name-preserving):
567
+
568
+ | Removed | Value | Replacement | Value |
569
+ |---|---|---|---|
570
+ | `--a-leading-none` | `1` | `--a-font-leading-none` | `1` |
571
+ | `--a-leading-tight` | `1.2` | `--a-font-leading-snug` | `1.2` |
572
+ | `--a-leading-snug` | `1.3` | `--a-font-leading-normal` | `1.35` |
573
+ | `--a-leading-normal` | `1.5` | `--a-font-leading-relaxed` | `1.5` |
574
+
575
+ A surviving `var(--a-leading-*)` reference goes invalid-at-computed-value-time
576
+ → inherit (same failure shape as the `--a-font` alias removal in `0.8.23`).
577
+
578
+ ### The same sweep also tokenized every other hardcoded typography literal
579
+
580
+ Beyond the legacy family, gh#1298's corpus sweep found — and tokenized —
581
+ every bare numeric `line-height`, `em`-literal `letter-spacing`, and numeric
582
+ `font-weight` in `packages/web-components/components/*/*.css`:
583
+
584
+ - **`line-height`** — mapped to the nearest `--a-font-leading-*` step. Both
585
+ `1.3` and `1.4` map to `--a-font-leading-normal` (`1.35`) — no new step was
586
+ minted (at the 12–14px sizes where these values appeared, ±0.05
587
+ line-height is below visual significance). A handful of `1.4` hits on
588
+ genuinely multi-line body/description copy were mapped up to
589
+ `--a-font-leading-relaxed` (`1.5`) instead, case-by-case — see the sweep
590
+ PR description for the enumerated list. `line-height: 0` (the
591
+ icon/line-box-collapse idiom used by `icon.css`, `swatch.css`,
592
+ `qr-code.css`, etc.) is **not** a typographic leading value and was left
593
+ untouched.
594
+ - **`letter-spacing`** — mapped to the nearest `--a-font-tracking-*` step;
595
+ `0.05em` (off-scale) maps to `--a-font-tracking-wide` (`0.04em`), same
596
+ nearest-step logic as the leading family.
597
+ - **`font-weight`** — all hits were already on-scale and map mechanically to
598
+ `--a-weight-*` (`500`→medium, `600`→semibold, `700`→bold).
599
+
600
+ ### A mechanized gate follows the sweep
601
+
602
+ `npm run check:typography-tokens` (wired into the `npm run check` aggregate)
603
+ fails on any bare numeric `line-height`, `em`-literal `letter-spacing`, or
604
+ numeric `font-weight` reappearing in `packages/web-components/components/*/*.css`
605
+ — mirroring the existing brand-chrome/foundation-layer audit gates — so the
606
+ parallel scale (and the literal-reaching it caused) cannot silently
607
+ reaccumulate.
608
+
609
+ ### Mechanical sweep
610
+
611
+ ```bash
612
+ git grep -nE -- '--a-leading-(none|tight|snug|normal)\b'
613
+ ```
614
+
615
+ Apply the replacement table above at each hit — do not blind-`sed`, since
616
+ `snug`/`normal` map to *different* replacement names than their own name
617
+ would suggest.
618
+
619
+ ### Verify
620
+
621
+ ```bash
622
+ git grep -nE -- '--a-leading-(none|tight|snug|normal)\b' # must be 0
623
+ npm run check:typography-tokens # must exit 0
624
+ npm run check
625
+ ```
626
+
627
+ ---
628
+
629
+ <a id="upcoming--toggle-option-ui-drops-the-legacy-data-selected-stamp-0840-adr-0056"></a>
630
+ ## Shipped in `0.8.40` — `toggle-option-ui` drops the legacy `data-selected` stamp (ADR-0056)
631
+
632
+ **BREAKING, no deprecation alias.** [ADR-0056](../../docs/ops/adr/adr-0056-selection-item-selected-convention.md)'s
633
+ breaking half removes `<toggle-group-ui>`'s legacy `data-selected` attribute
634
+ stamp on its `<toggle-option-ui>` children. The declared, reflected `selected`
635
+ prop (shipped additively in `0.8.39`) is now the *only* current-state surface
636
+ — matching `<segment-ui>`'s existing convention.
637
+
638
+ **Migration:** any external CSS or script targeting `[data-selected]` on
639
+ `toggle-option-ui` must move to `[selected]` — same semantics, same timing,
640
+ just the attribute name. `toggle-group.css` itself already styled
641
+ `[selected]`; the removed stamp was a parallel, undocumented hook with no
642
+ declared API surface (invisible to the yaml SoT, the catalog, and the A2UI
643
+ grammar).
644
+
645
+ ```bash
646
+ git grep -n "data-selected" -- '*.css' '*.js' '*.html' | grep -i toggle # must be 0
647
+ npm run check
648
+ ```
649
+
650
+ ---
651
+
652
+ <a id="upcoming--progress-ui-retires-variant-090-gh1370"></a>
653
+ ## Shipped in `0.8.41` — `progress-ui` retires `variant` (gh#1370)
654
+
655
+ **BREAKING, no deprecation alias.** `<progress-ui>`'s `variant` prop
656
+ (`bar` | `spinner`) is removed — progress is bar-only. `<spinner-ui>`
657
+ owns circular loading indicators.
658
+
659
+ **Why:** `packages/web-components/MIGRATION.md:2125,2139` records the
660
+ original standalone spinner being merged INTO progress as
661
+ `variant="spinner"` in the old `@agent-ui-kit` → `@adia-ai` migration —
662
+ then a richer standalone `<spinner-ui>` (`arc`/`ring`/`dots`/`knight`,
663
+ `tone`, `paused`, `label`) was later re-added. Both shipped in the
664
+ catalog, so a "loading" intent had three valid answers instead of two
665
+ disjoint ones. Retiring the render mode from progress un-forks the
666
+ consolidation that was already undone once. Filed from the
667
+ catalog-regularity audit's C2 finding, [gh#1363](https://github.com/adiahealth/gen-ui-kit/issues/1363).
668
+
669
+ **Migration:**
670
+
671
+ | Old | New |
672
+ |---|---|
673
+ | `<progress-ui variant="spinner">` | `<spinner-ui>` (`arc` is the closest visual match to the old rotating-arc spinner render; pick `ring`/`dots`/`knight` if the intent was different) |
674
+ | `<progress-ui variant="bar">` | drop the attribute — `bar` was already the default |
675
+ | `<progress-ui>` (no variant) | unchanged — already bar, already the default |
676
+
677
+ `<spinner-ui>` has no `value` prop — it is indeterminate-only. A
678
+ determinate use of `variant="spinner"` (if any existed) has no direct
679
+ spinner-ui equivalent; keep `<progress-ui>` and drop the `variant`
680
+ attribute instead (renders as a determinate bar).
681
+
682
+ **Find every affected consumer site with the repo's own scanner — not
683
+ ad-hoc grep** (a markup grep also fires on this very guide's worked
684
+ examples above, and misses double-quoted setters, multiline HTML
685
+ attributes, and whitespace variations around `=`):
686
+
687
+ ```bash
688
+ npm run qa:contract-migration-scan -- --base <pre-upgrade-ref> --head <post-upgrade-ref>
689
+ ```
690
+
691
+ `scripts/qa/contract-migration-scan.mjs` (gh#1405, shipped on `main`)
692
+ detects the `variant` removal from `progress.yaml` directly and
693
+ enumerates every markup consumer under `packages/`, `apps/`,
694
+ `playgrounds/`, `site/` — including corpus chunks — read from the two
695
+ git refs, so a dirty working tree never leaks into the report. Its own
696
+ tag-markup matching also fires on this guide's *shipped* copy
697
+ (`packages/web-components/MIGRATION.md`, which lives under `packages/`
698
+ and quotes `variant="spinner"` as a worked example above, not live
699
+ usage) — expect and disregard that one self-hit; any other reported
700
+ site is a real consumer to migrate. The scanner's markup-textual
701
+ matching does not cover JS `setAttribute()`/property-assignment call
702
+ sites (its own documented limitation) — grep those separately:
703
+
704
+ ```bash
705
+ git grep -rnE "setAttribute\('variant', *'(bar|spinner)'\)"
706
+ ```
707
+
708
+ Verify each hit's element is actually a `progress-ui` host before
709
+ rewriting — the same discipline as the variant/color/tone convention
710
+ cut's own sweep above.
711
+
712
+ ### Verify
713
+
714
+ ```bash
715
+ npm run qa:contract-migration-scan -- --base <pre-upgrade-ref> --head <post-upgrade-ref> # only the MIGRATION.md self-hit, no other sites
716
+ git grep -rnE "setAttribute\('variant', *'(bar|spinner)'\)" # review each hit manually
717
+ npm run check
718
+ ```
719
+
720
+ ---
721
+
722
+ <a id="upcoming--input-ui-trims-type-enum-090-gh1381"></a>
723
+ ## Shipped in `0.8.41` — `input-ui` trims `type` enum (gh#1381)
724
+
725
+ **BREAKING, no deprecation alias.** `<input-ui>`'s `type` enum is
726
+ trimmed to the six values it actually implements — `text` | `email` |
727
+ `password` | `number` | `tel` | `url`. `search`, `date`,
728
+ `datetime-local`, `time`, `month`, `week`, and `color` are removed.
729
+
730
+ **Why:** none of the seven removed values ever rendered a native
731
+ affordance — `input.class.js` only special-cases `password` (native
732
+ `<input>` for disc masking) and `number` (contenteditable + stepper
733
+ column); every other `type` value renders identically, a plain
734
+ contenteditable surface with no calendar, clock, swatch, or search
735
+ icon. `type="date"` was first-pass-VALID against the old enum but
736
+ functionally broken generation output — an LLM emitting it produces
737
+ markup that validates yet gives the user a bare text box where a date
738
+ picker was intended. Filed from the catalog-regularity audit's B10
739
+ finding, [gh#1363](https://github.com/adiahealth/gen-ui-kit/issues/1363).
740
+
741
+ **Migration:**
742
+
743
+ | Old | New |
744
+ |---|---|
745
+ | `<input-ui type="search">` | `<search-ui>` (built-in magnifying-glass icon, clear button, debounced `search` event) |
746
+ | `<input-ui type="date">` | `<calendar-picker-ui>` (single-date, day granularity) |
747
+ | `<input-ui type="month">` / `type="week"` | `<calendar-picker-ui>` — no dedicated month/week-granularity primitive exists in the catalog; day granularity is the closest routed primitive |
748
+ | `<input-ui type="datetime-local">` | `<datetime-picker-ui>` |
749
+ | `<input-ui type="time">` | `<time-picker-ui>` |
750
+ | `<input-ui type="color">` | `<color-input-ui>` (compact form-row swatch + popover) — or `<color-picker-ui>` directly for a full-surface editor (Tokens-Studio-style canvas) |
751
+ | `<input-ui>` (no type, or `text`/`email`/`password`/`number`/`tel`/`url`) | unchanged |
752
+
753
+ The routed primitives are NOT drop-in attribute-compatible with
754
+ `input-ui` — each has its own prop surface (e.g. `time-picker-ui`
755
+ expects ISO `HH:mm`/`HH:mm:ss`, `calendar-picker-ui` expects ISO
756
+ `YYYY-MM-DD`, `color-input-ui` emits both `hex` and `oklch` views).
757
+ Re-check `name`/`value`/`required`/`disabled` against the target
758
+ primitive's own yaml rather than assuming a mechanical attribute
759
+ carry-over.
760
+
761
+ **The repo's own scanner does not catch this class of change** —
762
+ `scripts/qa/contract-migration-scan.mjs`'s diff detection is scoped to
763
+ a yaml losing a `props`/`tokens` key or its `tag:` field (its own
764
+ documented v1 scope); narrowing an existing prop's `enum:` list is
765
+ invisible to it, since the `type` prop key itself is untouched. Find
766
+ affected sites with the repo's established multiline-capable sweep
767
+ recipe instead (§14.5's precedent — `git grep` is line-based, so a
768
+ `[^>]*` character class cannot cross a newline and misses a multi-line
769
+ opening tag; `rg -nU` is required, quote-aware, tag-scoped, and
770
+ excludes the historical/generated surfaces that legitimately still
771
+ quote the old markup as a worked example — this guide's own table
772
+ above, its shipped copy, `CHANGELOG*.md`, `dist/`, and `.claude/docs/`
773
+ itself, which also covers the frozen `.claude/docs/specs/*.md`
774
+ planning records):
775
+
776
+ ```bash
777
+ rg -nU '<input-ui[^>]*type="(search|date|datetime-local|time|month|week|color)"' \
778
+ -g '!*CHANGELOG*.md' -g '!**/dist/**' -g '!*.examples.md' -g '!*MIGRATION*' \
779
+ -g '!.claude/docs/**'
780
+ ```
781
+
782
+ Verify each hit's element is actually an `input-ui` host, then migrate
783
+ per the table above. The corpus harvest (`npm run harvest:chunks`)
784
+ independently re-validates every chunk against the updated catalog
785
+ schema and will name any consumer still using a removed `type` value
786
+ as a chunk-validation failure — run it after the yaml edit and before
787
+ opening a PR; it is a second, stricter check this scanner's own
788
+ limitation makes necessary for enum-narrowing changes, but it is NOT a
789
+ substitute for the sweep above — the harvest only validates chunks the
790
+ corpus actually harvests (annotated `data-chunk` regions), so a
791
+ non-harvested consumer (a component's own `.examples.html` demo, a
792
+ JS-composed internal template like `search.class.js`'s own former
793
+ `<input-ui type="search">` stamp) needs the direct sweep to be found at
794
+ all.
795
+
796
+ ### Verify
797
+
798
+ ```bash
799
+ rg -nU '<input-ui[^>]*type="(search|date|datetime-local|time|month|week|color)"' \
800
+ -g '!*CHANGELOG*.md' -g '!**/dist/**' -g '!*.examples.md' -g '!*MIGRATION*' \
801
+ -g '!.claude/docs/**' # must be 0 hits (exit 1, rg's "no match" signal) after migrating real sites
802
+ npm run harvest:chunks # 0 catalog:input validation failures
803
+ npm run check
804
+ ```
805
+
806
+ ---
807
+
808
+ <a id="upcoming--web-modules-data--state-converges-to-bare-attributes-090-gh1332"></a>
809
+ ## Shipped in `0.8.41` — web-modules `data-*` state converges to bare attributes (gh#1332)
810
+
811
+ **BREAKING, no deprecation alias.** Two web-modules' declared `states:`
812
+ entries used a private `data-*` stamp where a sibling state on the SAME
813
+ component already used a bare state attribute — an inconsistency the
814
+ [data-* usage sweep](https://github.com/adiahealth/gen-ui-kit/issues/1332)
815
+ (Category A) generalizes ADR-0056's rule to catch: API-conceptual state a
816
+ consumer/generator can see in the yaml `states:` list belongs on a bare
817
+ attribute, never hidden behind `data-*`.
818
+
819
+ | Component | Old | New |
820
+ |---|---|---|
821
+ | `<integrations-page-ui>` | `[data-empty-search]` | `[empty-search]` |
822
+ | `<integrations-page-ui>` | `[data-empty-data]` | `[empty-data]` |
823
+ | `<onboarding-checklist-ui>` | `[data-complete]` | `[complete]` |
824
+
825
+ **Why:** `integrations-page-ui`'s `states:` list already declares
826
+ `loading` with `attribute: loading` (bare) right next to `empty-search`/
827
+ `empty-data`, which used `attribute: data-empty-search`/
828
+ `data-empty-data` — same kind of declared, documented state, two
829
+ different attribute grammars on one component. `onboarding-checklist-ui`
830
+ has the identical shape: `collapsed` and `dismissed` both reflect bare
831
+ attributes (`collapsed`, `hidden`) while `complete` alone used
832
+ `data-complete`. Neither was ever declared in the component's `props:`
833
+ section — both were computed, read-only states set directly via
834
+ `setAttribute`/`removeAttribute`, styling hooks in the same family as
835
+ `loading`/`collapsed`, just minted with the wrong prefix.
836
+
837
+ **Find every affected consumer site with the repo's own scanner, plus
838
+ the multiline-capable manual sweep** (established in the `input-ui`
839
+ `type`-enum rider above, and again at gh#1414/gh#1420) — a line-based
840
+ `git grep` misses multi-line opening tags, and a bracketed-selector-only
841
+ search misses a JS consumer reading the raw attribute name via
842
+ `getAttribute`/`setAttribute`/`hasAttribute`/`dataset`, not a CSS
843
+ selector:
844
+
845
+ ```bash
846
+ npm run qa:contract-migration-scan -- --base <pre-upgrade-ref> --head <post-upgrade-ref>
847
+ # contract-migration-scan's v1 scope is props:/tokens:/tag: — states: entries
848
+ # are outside it (no props/tag/token key is lost here), so treat its report
849
+ # as a first pass, not the full sweep for THIS migration; the rg sweep below
850
+ # is what actually covers it.
851
+ rg -nU '\[data-empty-search\]|\[data-empty-data\]|\[data-complete\]|data-empty-search|data-empty-data|data-complete' \
852
+ -g '!*CHANGELOG*.md' -g '!**/dist/**' -g '!*.examples.md' -g '!*MIGRATION*' \
853
+ -g '!.claude/docs/**'
854
+ ```
855
+
856
+ The raw-name alternation catches `el.getAttribute('data-empty-search')` /
857
+ `el.dataset.emptySearch` (dataset reads reflect through the raw attribute
858
+ name in the DOM, not the camelCase JS property) alongside the bracketed
859
+ CSS-selector form; the bracketed alternation stays for a selector split
860
+ across a line the raw form's word boundary might otherwise merge with a
861
+ neighboring token. `integrations-page-ui`'s unrelated `data-empty-reason`
862
+ (an internal part-scoped value attribute, not this `states:` entry) is
863
+ intentionally excluded from both alternations — do not add it.
864
+
865
+ **Migrate each verified hit** — review before rewriting; the portable
866
+ form below avoids `sed -i`'s BSD-vs-GNU flag divergence (BSD `sed -i`
867
+ requires an explicit, even empty, backup-suffix argument; GNU `sed -i`
868
+ treats a bare backup-suffix argument as the script itself and errors —
869
+ the two are not drop-in compatible, so a script this guide hands to
870
+ `rg -l | xargs` for consumers on either platform needs a wrapper neither
871
+ flavor chokes on):
872
+
873
+ ```bash
874
+ rg -l '\[data-empty-search\]|\[data-empty-data\]|data-empty-search|data-empty-data' \
875
+ -g '!*CHANGELOG*.md' -g '!**/dist/**' -g '!*MIGRATION*' -g '!.claude/docs/**' \
876
+ | xargs perl -pi -e 's/data-empty-search/empty-search/g; s/data-empty-data/empty-data/g'
877
+ rg -l '\[data-complete\]|data-complete' \
878
+ -g '!*CHANGELOG*.md' -g '!**/dist/**' -g '!*MIGRATION*' -g '!.claude/docs/**' \
879
+ | xargs perl -pi -e 's/data-complete/complete/g'
880
+ ```
881
+
882
+ ### Verify
883
+
884
+ ```bash
885
+ npm run qa:contract-migration-scan -- --base <pre-upgrade-ref> --head <post-upgrade-ref>
886
+ rg -nU '\[data-empty-search\]|\[data-empty-data\]|\[data-complete\]|data-empty-search|data-empty-data|data-complete' \
887
+ -g '!*CHANGELOG*.md' -g '!**/dist/**' -g '!*.examples.md' -g '!*MIGRATION*' \
888
+ -g '!.claude/docs/**' # must be 0 hits (exit 1, rg's "no match" signal) after migrating real sites
889
+ ```
890
+
891
+ <a id="upcoming--primitive-tier-data--state-converges-to-bare-attributes-090-gh1332-2"></a>
892
+ ## Shipped in `0.8.41` — primitive-tier `data-*` state converges to bare attributes (gh#1332-2)
893
+
894
+ **BREAKING, no deprecation alias.** The residual primitive-tier round of
895
+ the [data-* usage sweep](https://github.com/adiahealth/gen-ui-kit/issues/1332)
896
+ (Category A), covering the ~25 primitives left over after the web-modules
897
+ round above. Same rule: API-conceptual state a consumer/generator can see
898
+ in the yaml `states:` list belongs on a bare attribute, never hidden
899
+ behind `data-*`.
900
+
901
+ | Component | Old | New |
902
+ |---|---|---|
903
+ | `<feed-item-ui>` | `[data-open]` | `[open]` |
904
+ | `<feed-item-ui>` | `[data-closing]` | `[closing]` |
905
+ | `<feed-item-ui>` | `[data-queued]` | `[queued]` |
906
+ | `<slider-ui>` | `[data-dragging]` | `[dragging]` |
907
+ | `<range-ui>` | `[data-dragging]` | `[dragging]` |
908
+ | `<pane-ui>` | `[data-resizing]` | `[resizing]` |
909
+ | `<chart-ui>` | `[data-has-data]` | `[has-data]` |
910
+ | `<select-ui>` | `[data-multi-chips]` | *(removed — no successor; see below)* |
911
+ | `<qr-code-ui>` | `[data-error]` | `[error]` |
912
+ | `<table-ui>` | `[data-selected]` (body rows) | `[aria-selected="true"]` |
913
+ | `<pagination-ui>` | `[data-active]` (page buttons) | `[aria-current="page"]` |
914
+ | `<tags-input-ui>` | `[data-empty]` (inline input) | *(removed — key off the host's declared `:not([editing])` inverse)* |
915
+ | `<input-ui>` | `[data-number]` (field div) | *(removed — key off the declared, reflected host `[type="number"]`)* |
916
+
917
+ **Why:** `feed-item-ui`'s open/closing lifecycle and `feed-ui`'s
918
+ above-`[max]` queue marker, `slider-ui`/`range-ui`'s mid-drag flag,
919
+ `pane-ui`'s mid-resize flag, and `chart-ui`'s has-data flag were each set
920
+ directly via `this.setAttribute`/`removeAttribute` on the host with no
921
+ `props:`/`states:` declaration — private stamps for genuine, observable
922
+ lifecycle state. `pane-ui[resizing]` matches the bare `[resizing]`
923
+ convention `admin-sidebar-ui`/`chat-sidebar-ui` already carry (fixed in
924
+ the web-modules round above, PR #1438) rather than minting a new name.
925
+
926
+ `select-ui[data-multi-chips]` is different in kind: it was a pure
927
+ *derived* cache of two attributes ALREADY declared and reflected —
928
+ `[multiple]` and `[summary-label]` — so there is no successor attribute;
929
+ consumer CSS/JS keying off the private mirror should key off the compound
930
+ selector `[multiple]:not([summary-label])` (or the equivalent JS property
931
+ check) directly instead.
932
+
933
+ The residual-round entries follow the same two shapes. `qr-code-ui`'s
934
+ `error` state was already yaml-declared but mapped to `attribute:
935
+ data-error` — the mapping converged to bare `[error]` (the same shape as
936
+ the web-modules round's `integrations-page-ui` converge). `table-ui`'s
937
+ body-row `[data-selected]` and `pagination-ui`'s page-button
938
+ `[data-active]` were private duplicates of an ARIA attribute set on the
939
+ SAME element in the same code path (`aria-selected="true"` /
940
+ `aria-current="page"`) — the ARIA attribute alone now carries the state
941
+ (PR #1438's `admin-roster` `aria-checked` precedent). `tags-input-ui`'s
942
+ inline-input `[data-empty]` was the exact inverse of the host's declared
943
+ `[editing]` state, and `input-ui`'s `[data-number]` field marker was a
944
+ mirror of the declared, reflected `[type="number"]` — both dropped with
945
+ no successor, `select-ui`-style.
946
+
947
+ **Find every affected consumer site:**
948
+
949
+ ```bash
950
+ npm run qa:contract-migration-scan -- --base <pre-upgrade-ref> --head <post-upgrade-ref>
951
+ # props:/tokens:/tag: scope only — none of these are prop/tag/token
952
+ # changes (states: entries + one dropped private mirror), so treat this
953
+ # as a first pass; the rg sweep below is what actually covers it.
954
+ # Attribute forms (HTML/CSS/JS strings) — word-bounded so e.g. `data-opening`
955
+ # or another component's own `data-error` prose doesn't false-positive:
956
+ rg -nU 'data-(open|closing|queued|dragging|resizing|has-data|multi-chips|selected|active|empty|error|number)\b' \
957
+ -g '!*CHANGELOG*.md' -g '!**/dist/**' -g '!*.examples.md' -g '!*MIGRATION*' \
958
+ -g '!.claude/docs/**' -g '!*.a2ui.json' -g '!custom-elements.json' -g '!catalog*.json'
959
+ # dataset.* property forms (JS) — the camelCase twins of the same attributes:
960
+ rg -nU 'dataset\.(open|closing|queued|dragging|resizing|hasData|multiChips|selected|active|empty|error|number)\b' \
961
+ -g '!**/dist/**' -g '!*.a2ui.json' -g '!custom-elements.json' -g '!catalog*.json'
962
+ ```
963
+
964
+ Review each hit before rewriting — `data-open`/`data-closing` also appear
965
+ verbatim on `modal-ui`/`drawer-ui`'s own INTERNAL `<dialog slot="dialog">`
966
+ anatomy child (never on those hosts, and unrelated to this migration —
967
+ those two already expose a real `open` prop; the private child-level
968
+ stamp is an animation-timing implementation detail, left as-is), and
969
+ `data-dragging` also appears on `swiper-ui`'s internal track child (same
970
+ shape, also left as-is). Only the eight HOST-level attributes in the
971
+ table above moved.
972
+
973
+ **Migrate each verified hit:**
974
+
975
+ ```bash
976
+ # Word-bounded exact tokens only (\b guards partial identifiers like
977
+ # `data-opening`), generated artifacts excluded (regenerate those with the
978
+ # repo's own commands afterwards — never rewrite them in place):
979
+ EXCL=(-g '!*CHANGELOG*.md' -g '!**/dist/**' -g '!*MIGRATION*' -g '!.claude/docs/**'
980
+ -g '!*.a2ui.json' -g '!custom-elements.json' -g '!catalog*.json' -g '!site-a2ui/**'
981
+ -g '!**/corpus/chunks/**')
982
+ rg -l 'data-(open|closing|queued)\b' "${EXCL[@]}" \
983
+ -g '!**/components/modal/**' -g '!**/components/drawer/**' \
984
+ | xargs perl -pi -e 's/\bdata-open\b/open/g; s/\bdata-closing\b/closing/g; s/\bdata-queued\b/queued/g'
985
+ rg -l 'data-dragging\b' "${EXCL[@]}" -g '!**/components/swiper/**' \
986
+ | xargs perl -pi -e 's/\bdata-dragging\b/dragging/g'
987
+ rg -l 'data-resizing\b' "${EXCL[@]}" \
988
+ | xargs perl -pi -e 's/\bdata-resizing\b/resizing/g'
989
+ rg -l 'data-has-data\b' "${EXCL[@]}" \
990
+ | xargs perl -pi -e 's/\bdata-has-data\b/has-data/g'
991
+ rg -l 'data-error\b' "${EXCL[@]}" \
992
+ | xargs perl -pi -e 's/\bdata-error\b/error/g' # qr-code-ui sites only — review hits first, other components keep their own data-error
993
+ # table-ui [data-selected] → [aria-selected="true"], pagination-ui
994
+ # [data-active] → [aria-current="page"], tags-input-ui [data-empty] →
995
+ # :scope:not([editing]) …, input-ui [data-number] → [type="number"] …:
996
+ # ARIA/compound successors — rewrite these selector-by-selector by hand,
997
+ # there is no mechanical 1:1 attribute swap.
998
+ # After all rewrites, regenerate derived artifacts:
999
+ node scripts/build/components.mjs && node scripts/build/site-a2ui.mjs --stale && npm run harvest:chunks
1000
+ # [data-multi-chips] has no successor attribute — rewrite the selector/check
1001
+ # by hand to [multiple]:not([summary-label]) (CSS) or the equivalent
1002
+ # `el.multiple && !el.summaryLabel` (JS), there is no mechanical 1:1 swap.
1003
+ ```
1004
+
1005
+ ### Verify
1006
+
1007
+ ```bash
1008
+ npm run qa:contract-migration-scan -- --base <pre-upgrade-ref> --head <post-upgrade-ref>
1009
+ rg -nU 'data-(open|closing|queued|dragging|resizing|has-data|multi-chips)\b|dataset\.(open|closing|queued|dragging|resizing|hasData|multiChips)\b' \
1010
+ -g '!*CHANGELOG*.md' -g '!**/dist/**' -g '!*.examples.md' -g '!*MIGRATION*' \
1011
+ -g '!.claude/docs/**' -g '!**/components/modal/**' -g '!**/components/drawer/**' \
1012
+ -g '!**/components/swiper/**' -g '!*.a2ui.json' -g '!custom-elements.json' \
1013
+ -g '!catalog*.json' # must be 0 hits (exit 1) after migrating real sites
1014
+ # and, scoped to each owning component's consumers (other components keep
1015
+ # their own same-named markers): qr-code data-error / table data-selected /
1016
+ # pagination data-active / tags-input data-empty / input data-number, plus
1017
+ # dataset.{error,selected,active,empty,number} accesses on those elements.
1018
+ npm run check
1019
+ ```
1020
+
1021
+ `data-grid`/`data-resize` are explicitly OUT of this round — both are
1022
+ genuine, documented public APIs (admin-shell's 2/3-column layout helper;
1023
+ admin-sidebar/chat-sidebar/editor-sidebar's drag handle) that ADR-0060
1024
+ names as can't-move-without-a-migration this pass; they carry over to the
1025
+ `0.9.0` ADR-0053/0056 breaking-rename wave, not this ticket.
1026
+
1027
+ <a id="upcoming--image-ui-loaderror-states-declared--converge-to-bare-attributes-090-gh1464"></a>
1028
+ ## Shipped in `0.8.41` — image-ui load/error states declared + converge to bare attributes (gh#1464)
1029
+
1030
+ **BREAKING, no deprecation alias.** The final residue of the
1031
+ [data-* usage sweep](https://github.com/adiahealth/gen-ui-kit/issues/1332) —
1032
+ deferred out of the residual round above (PR #1463) because, unlike that
1033
+ round's entries, `image-ui`'s load/error attributes were never yaml-declared
1034
+ at all.
1035
+
1036
+ | Component | Old | New |
1037
+ |---|---|---|
1038
+ | `<image-ui>` | `[data-loaded]` | `[loaded]` |
1039
+ | `<image-ui>` | `[data-error]` | `[error]` |
1040
+
1041
+ **Why:** `image.class.js` set `data-loaded`/`data-error` directly via
1042
+ `this.setAttribute`/`removeAttribute` on the host with no `props:`/`states:`
1043
+ declaration — genuine, observable load-lifecycle state (the image's
1044
+ `load`/`error` events, including the gh#999 missed-load-race guard) hidden
1045
+ behind an undeclared private stamp. `image.yaml` now declares both as
1046
+ `states:` entries with bare `attribute:` mappings, matching `qr-code-ui`'s
1047
+ own `error` state (converged to bare `[error]` in the residual round
1048
+ above). `image-ui`'s unrelated `[data-overlay]` skeleton-proximity marker
1049
+ (gh#1362, an implementation-detail stamp on the composed `skeleton-ui`
1050
+ child, not a declared host state) is untouched.
1051
+
1052
+ **`data-msg-*` disposition (same ticket, non-breaking):** the
1053
+ validation-message config family (`data-msg-required`/`pattern`/
1054
+ `minlength`/`maxlength`/`min`/`max`/`bad-input`, read by `core/form.js`'s
1055
+ shared `UIFormElement` mixin across `input`/`select`/`tags-input`/`code`/
1056
+ `date-range-picker`/`datetime-picker`/`payment-method-form`) is **exempt** —
1057
+ it stays `data-*` with no yaml declaration and no attribute change. It is
1058
+ consumer-authored config the component only ever *reads* (never host-
1059
+ reflected state), and the read path is one shared mixin spanning 7+
1060
+ components with no single owning yaml SoT to declare it against. Full
1061
+ rationale: `primitive-authoring/references/yaml-contract.md`'s new
1062
+ `data-msg-*` section (ADR-0060 §Decision 3 routes this exact family here
1063
+ for its converge-or-ratify call). No consumer action needed for this half.
1064
+
1065
+ **Find every affected consumer site:**
1066
+
1067
+ ```bash
1068
+ npm run qa:contract-migration-scan -- --base <pre-upgrade-ref> --head <post-upgrade-ref>
1069
+ # states: entry, not a prop/tag/token change — treat the scan as a first
1070
+ # pass; the rg sweep below is what actually covers it. Word-bounded so a
1071
+ # same-named marker on another component (qr-code-ui's own already-bare
1072
+ # `error`, canvas-ui/agent-reasoning-ui's own `data-error`) doesn't
1073
+ # false-positive — review each hit, only image-ui sites move:
1074
+ rg -nU 'data-(loaded|error)\b' \
1075
+ -g '!*CHANGELOG*.md' -g '!**/dist/**' -g '!*.examples.md' -g '!*MIGRATION*' \
1076
+ -g '!.claude/docs/**' -g '!*.a2ui.json' -g '!custom-elements.json' -g '!catalog*.json'
1077
+ rg -nU 'dataset\.(loaded|error)\b' \
1078
+ -g '!**/dist/**' -g '!*.a2ui.json' -g '!custom-elements.json' -g '!catalog*.json'
1079
+ ```
1080
+
1081
+ **Migrate each verified `image-ui` hit:**
1082
+
1083
+ ```bash
1084
+ rg -l 'data-loaded\b' -g '!**/dist/**' -g '!*MIGRATION*' -g '!.claude/docs/**' \
1085
+ -g '!*.a2ui.json' -g '!custom-elements.json' -g '!catalog*.json' \
1086
+ | xargs perl -pi -e 's/\bdata-loaded\b/loaded/g'
1087
+ # data-error: review hits first — qr-code-ui, canvas-ui, agent-reasoning-ui,
1088
+ # construct-canvas and error-shake keep their OWN data-error/data-error-*
1089
+ # markers unchanged; only image-ui sites move:
1090
+ rg -l 'data-error\b' -g '!**/dist/**' -g '!*MIGRATION*' -g '!.claude/docs/**' \
1091
+ -g '!*.a2ui.json' -g '!custom-elements.json' -g '!catalog*.json' \
1092
+ -g '!**/components/qr-code/**' -g '!**/components/canvas/**' \
1093
+ -g '!**/components/agent-reasoning/**' -g '!**/traits/error-shake/**' \
1094
+ -g '!**/construct-canvas/**' \
1095
+ | xargs perl -pi -e 's/\bdata-error\b/error/g'
1096
+ node scripts/build/components.mjs && node scripts/build/site-a2ui.mjs --stale && npm run harvest:chunks
1097
+ ```
1098
+
1099
+ ### Verify
1100
+
1101
+ ```bash
1102
+ npm run qa:contract-migration-scan -- --base <pre-upgrade-ref> --head <post-upgrade-ref>
1103
+ rg -nU 'data-(loaded|error)\b' -g '!*CHANGELOG*.md' -g '!**/dist/**' \
1104
+ -g '!*.examples.md' -g '!*MIGRATION*' -g '!.claude/docs/**' \
1105
+ -g '!**/components/qr-code/**' -g '!**/components/canvas/**' \
1106
+ -g '!**/components/agent-reasoning/**' -g '!**/traits/error-shake/**' \
1107
+ -g '!**/construct-canvas/**' -g '!*.a2ui.json' -g '!custom-elements.json' \
1108
+ -g '!catalog*.json' # must be 0 hits on image-ui specifically after migrating real sites
1109
+ npm run check
1110
+ ```
1111
+
1112
+ <a id="upcoming--action-item-ui-converges-onto-menu-item-uis-row-contract-090-gh1374"></a>
1113
+ ## Shipped in `0.8.41` — `action-item-ui` converges onto `menu-item-ui`'s row contract (gh#1374)
1114
+
1115
+ **BREAKING, no deprecation alias, zero known consumer sites.**
1116
+ `<action-item-ui>` (the child of `<action-list-ui>`) gains a `subtitle`
1117
+ prop and `text`/`subtitle`/`trailing` slot overrides, converging its row
1118
+ contract exactly onto `<menu-item-ui>`'s (icon/text/subtitle/value/
1119
+ variant/disabled props; icon/text/subtitle/trailing slot overrides). The
1120
+ `variant` enum (`default`/`danger`) already converged separately in
1121
+ [gh#1417](https://github.com/adiahealth/gen-ui-kit/pull/1417) (#1363 B6)
1122
+ and is unaffected here.
1123
+
1124
+ **Why:** the catalog-regularity audit's C5 finding
1125
+ ([gh#1363](https://github.com/adiahealth/gen-ui-kit/issues/1363)) flagged
1126
+ `action-item-ui` as `menu-item-ui` minus `subtitle`, with no principled
1127
+ reason for the two row contracts to differ — action-items live inline in
1128
+ content surfaces, menu-items live inside `<menu-ui>` popovers, but the row
1129
+ anatomy itself (leading icon, label, optional secondary line, optional
1130
+ trailing shortcut/badge) is identical. Filed as
1131
+ [gh#1374](https://github.com/adiahealth/gen-ui-kit/issues/1374).
1132
+
1133
+ **Container-level decision (also C5):** whether `<list-ui>` should absorb
1134
+ `<action-list-ui>` as a fire-and-forget `action` mode was decided **NO** —
1135
+ the two are kept as separate components. `<list-ui selectable>` holds a
1136
+ persisted single selection (`selectedKey`/`aria-selected`, roving
1137
+ tabindex); `<action-list-ui>` always fires and never holds selection
1138
+ state — genuinely different interaction contracts, not one mode axis, and
1139
+ no existing implementation of an "action" mode exists on `list-ui` to
1140
+ converge onto (unlike the segmented/toggle-group case, C1, where the mode
1141
+ already existed under a different name). `list.yaml` and `action-list.yaml`
1142
+ each carry an a2ui rule cross-routing to the other so the decision is
1143
+ discoverable at generation time.
1144
+
1145
+ **Migration:**
1146
+
1147
+ | Old | New |
1148
+ |---|---|
1149
+ | `<action-item-ui icon="…" text="…" value="…">` | unchanged |
1150
+ | `<action-item-ui>` with an unnamed (default-slot) trailing child (e.g. a `<kbd>` shortcut hint) | add `slot="trailing"` to that child |
1151
+ | Anything reading `subtitle` | new, additive — no migration |
1152
+
1153
+ A monorepo-wide sweep at dispatch time (`rg -nU` across every
1154
+ `<action-item-ui>...</action-item-ui>` span, source + corpus chunks +
1155
+ examples, dist/generated excluded) found **zero** consumers with slotted
1156
+ children of any kind — every usage in this repo is
1157
+ self-closing/attribute-only. The default-slot retirement is therefore a
1158
+ contract change with no repo-internal migration to perform; it is
1159
+ documented here for any external consumer that had adopted the unnamed
1160
+ slot.
1161
+
1162
+ **Find every affected consumer site:**
1163
+
1164
+ ```bash
1165
+ npm run qa:contract-migration-scan -- --base <pre-upgrade-ref> --head <post-upgrade-ref>
1166
+ rg -nU '<action-item-ui[^>]*>[\s\S]*?</action-item-ui>' \
1167
+ -g '!**/dist/**' -g '!*.a2ui.json' -g '!*MIGRATION*' -g '!.claude/docs/**'
1168
+ ```
1169
+
1170
+ Any non-self-closing `<action-item-ui>` hit with slotted content that
1171
+ isn't `slot="icon"` needs `slot="trailing"` added.
1172
+ <a id="upcoming--global-attribute-shadowing-collision-sweep-090-gh1335"></a>
1173
+ ## Shipped in `0.8.41` — global-attribute-shadowing collision sweep (gh#1335)
1174
+
1175
+ **BREAKING, no deprecation aliases.** gh#1327's build of the ADR-0053/0054
1176
+ `check:attribute-shadowing` gate surfaced 16 pre-existing component-local
1177
+ attributes that shadowed the global attribute grammar with divergent
1178
+ semantics — the same collision class ADR-0053/0054 fixed for
1179
+ `qr-code-ui[margin]`, `table-ui[density]`, and `chart-ui[radius]`, but never
1180
+ itself audited. gh#1335 also surfaced a 17th collision of its own, during
1181
+ this same build's follow-up audit (`dashboard-layout-ui[gap]` — never in the
1182
+ gate's original 16, since its shape wasn't caught by the gate's type-only
1183
+ check for the `gap`/`padding`/`margin` Scale roster). Of the 17 total tracked
1184
+ here, **15 are resolved** (12 renamed/converged — the original 11 plus the
1185
+ 17th — and 3 exempted); **2 remain open** (both from the original 16 — see
1186
+ below).
1187
+
1188
+ ### Renames / conversions (12 — 11 from the original 16 + the 17th collision)
1189
+
1190
+ | Component | Old | New | Notes |
1191
+ |---|---|---|---|
1192
+ | `header-ui` | `[padding]` (boolean) | `[padded]` | same boolean semantics; collided with the global Scale `[padding]`. |
1193
+ | `menu-ui` | `[gap]` (raw px) | `[offset]` | same numeric semantics; matches `popover-ui`'s own rename — both wrap the same `anchorPopover()` helper. |
1194
+ | `popover-ui` | `[gap]` (raw px) | `[offset]` | same numeric semantics; yaml already described it as "pixel offset". |
1195
+ | `skeleton-ui` | `[radius]` (enum) | `[corner-radius]` | same enum (`none/sm/md/lg/full`); mirrors `chart-ui`'s own `[corner-radius]` fix (ADR-0054). |
1196
+ | `image-ui` | `[radius]` (enum) | `[corner-radius]` | same enum (`sm/md/lg/full`); same rename as `skeleton-ui` above. |
1197
+ | `invoice-detail-ui` | `[density]` (`compact`/`standard`) | `[density]` (`compact`/`spacious`/unset) | `standard` retired (never a real forwarded state — the JS already treated it as unset); `spacious` gained for parity with the global grammar. |
1198
+ | `invoice-history-ui` | `[density]` (`compact`/`standard`/`comfortable`) | `[density]` (`compact`/`spacious`/unset) | `standard` retired, `comfortable` → `spacious` (a real behavioral remap, this component's `comfortable` genuinely meant the same as `spacious`). |
1199
+ | `dashboard-layout-ui` | `[density]` includes `comfortable` | `[density]` drops `comfortable` | **not** a `comfortable`→`spacious` remap — verified as dead code: `comfortable` set only `--a-density:1` with no gap-token override, functionally identical to the unset `""` state, not to `spacious` (`--a-density:1.15` + wider gap tokens). Converges onto `""` (the closest matching real behavior). |
1200
+ | `dashboard-layout-ui` | `[gap]` (numeric `1`-`6`) | `[band-gap]` | kept namespaced, not converged onto the global `[gap]` Scale — it sets a component-local token that never composed with the ambient `[size]`→`--a-gap` cascade, and covers a narrower range (1-6 vs. the global 0-16); a genuinely distinct, band-scoped concept. |
1201
+ | `text-ui` | `[weight]` includes `regular` | `[weight]` uses `normal` | spelling convergence onto the global Enum·semantic `[weight]` vocabulary. |
1202
+ | `text-ui` | `[text-align]` (`start`/`center`/`end`/`justify`) | `[text-align]` (`left`/`center`/`right`/`justify`) | converges onto the global physical vocabulary; `justify` is a genuine physical (non-directional) value and is now supported by the global `[text-align]` grammar itself (`packages/web-components/styles/api/text.css`), not just `text-ui`'s own local rule. |
1203
+ | `tags-input-ui` | `[transform]` (`lowercase`/`trim`/`strip-spaces`) | `[normalize]` | value-commit normalization is a different concept from the global CSS `[transform]` (uppercase/lowercase/capitalize/none display styling), not merely a colliding vocabulary. |
1204
+
1205
+ ### New §11 exemptions (3, no code change)
1206
+
1207
+ `button-ui[color]`, `toggle-scheme-ui[color]`, and `text-ui[color]` are
1208
+ newly ratified `.claude/docs/specs/attribute-api-system.md` §11 rows —
1209
+ `[color]` on these three plays the ADR-0044/ADR-0064 FAMILY axis (a
1210
+ different grammar from the generic descriptive `[color]` the rest of the
1211
+ catalog uses), not a foreign value against that generic vocabulary. No
1212
+ markup changes — these were always valid, just undocumented as deliberate.
1213
+
1214
+ ### Left open (2) — since ratified as §11 exemptions (ADR-0070, no code change)
1215
+
1216
+ Both were tracked in the gate's own `KNOWN_FINDINGS`, not fixed by this
1217
+ sweep — self-certifying an exemption with no ADR citation is exactly what
1218
+ §11's own contract forbids. ADR-0070 (gh#1500) has since ratified both as
1219
+ §11 rows, the same way ADR-0044/ADR-0064 closed the `[color]` rows above:
1220
+
1221
+ - **`qr-code-ui[color]`** — a free-form CSS color string, structurally
1222
+ identical to the ratified `swatch-ui`/`noodles-ui[color]` §11 exemption
1223
+ ("the component's entire subject is a color").
1224
+ - **`icon-ui[weight]`** — Phosphor icon-render-style vocabulary
1225
+ (`thin/light/regular/bold/fill/duotone`), a different concept entirely
1226
+ from font-weight — same "different concept" shape as the other ratified
1227
+ exemptions.
1228
+
1229
+ No markup changes — like the three ADR-0044/ADR-0064 exemptions, these were
1230
+ always valid, just undocumented as deliberate.
1231
+
1232
+ ### Migration
1233
+
1234
+ | Old | New |
1235
+ |---|---|
1236
+ | `<header-ui padding>` | `<header-ui padded>` |
1237
+ | `<menu-ui gap="N">` | `<menu-ui offset="N">` |
1238
+ | `<popover-ui gap="N">` | `<popover-ui offset="N">` |
1239
+ | `<skeleton-ui radius="X">` | `<skeleton-ui corner-radius="X">` |
1240
+ | `<image-ui radius="X">` | `<image-ui corner-radius="X">` |
1241
+ | `<invoice-detail-ui density="standard">` | drop the attribute (unset is the default) |
1242
+ | `<invoice-history-ui density="standard">` | drop the attribute |
1243
+ | `<invoice-history-ui density="comfortable">` | `<invoice-history-ui density="spacious">` |
1244
+ | `<dashboard-layout-ui density="comfortable">` | drop the attribute (was already behaviorally equivalent to unset) |
1245
+ | `<dashboard-layout-ui gap="N">` | `<dashboard-layout-ui band-gap="N">` |
1246
+ | `<text-ui weight="regular">` | `<text-ui weight="normal">` |
1247
+ | `<text-ui text-align="start">` | `<text-ui text-align="left">` |
1248
+ | `<text-ui text-align="end">` | `<text-ui text-align="right">` |
1249
+ | `<tags-input-ui transform="…">` | `<tags-input-ui normalize="…">` |
1250
+
1251
+ **Find every affected consumer site with the repo's own scanner — not
1252
+ ad-hoc grep:**
1253
+
1254
+ ```bash
1255
+ npm run qa:contract-migration-scan -- --base <pre-upgrade-ref> --head <post-upgrade-ref>
1256
+ ```
1257
+
1258
+ The scanner's markup-textual matching does not cover JS
1259
+ `setAttribute()`/property-assignment call sites at all, or the
1260
+ enum-value-only renames (`weight="regular"→"normal"`,
1261
+ `text-align="start/end"→"left/right"`, `density="standard"/"comfortable"`) —
1262
+ the latter are value changes on an attribute whose NAME didn't change,
1263
+ outside the scanner's rename-detection shape. Sweep both classes with a
1264
+ tag-scoped `rg -nU`:
1265
+
1266
+ **Enum-value-only renames (markup):**
1267
+
1268
+ ```bash
1269
+ rg -nU '<text-ui[^>]*weight="regular"'
1270
+ rg -nU '<text-ui[^>]*text-align="(start|end)"'
1271
+ rg -nU '<(invoice-detail-ui|invoice-history-ui)[^>]*density="(standard|comfortable)"'
1272
+ rg -nU '<dashboard-layout-ui[^>]*density="comfortable"'
1273
+ ```
1274
+
1275
+ **JS `setAttribute()` / property-assignment call sites (for every renamed
1276
+ attribute — `padding`, `gap`, `radius`, `transform`, plus the enum-only
1277
+ renames above by attribute name):**
1278
+
1279
+ ```bash
1280
+ rg -nU "setAttribute\('(padding|gap|radius|transform)', *['\"]" -g '*.js' -g '*.ts'
1281
+ rg -nU '\.(padding|gap|radius|transform)\s*=' -g '*.js' -g '*.ts'
1282
+ rg -nU "setAttribute\('(weight|text-align|density)', *['\"](regular|start|end|standard|comfortable)['\"]" -g '*.js' -g '*.ts'
1283
+ ```
1284
+
1285
+ The `\.(padding|gap|radius|transform)\s*=` property-assignment pattern is
1286
+ necessarily broad (these are common English words used by unrelated
1287
+ objects/properties too) — review each hit's receiver is actually a
1288
+ `header-ui`/`menu-ui`/`popover-ui`/`skeleton-ui`/`image-ui`/
1289
+ `dashboard-layout-ui`/`tags-input-ui` element before rewriting, the same
1290
+ discipline the `progress-ui` migration entry's own `setAttribute()`
1291
+ sweep above already documents.
1292
+
1293
+ ### Verify
1294
+
1295
+ ```bash
1296
+ node scripts/build/components.mjs --verify # sidecars clean
1297
+ npm run qa:contract-migration-scan -- --base <pre-upgrade-ref> --head <post-upgrade-ref>
1298
+ npm run qa:contract-migration-scan -- --base <pre-upgrade-ref> --head <post-upgrade-ref>
1299
+ node scripts/audit/check-attribute-shadowing.mjs --strict # 0 untracked violations, exactly 2 known findings
1300
+ npm run check
1301
+ ```
1302
+
1303
+ ---
1304
+
1305
+
1306
+ <a id="upcoming--segmented-ui-gains-multiple-toggle-group-uitoggle-option-ui-deprecated-090-gh1369"></a>
1307
+ ## Shipped in `0.8.41` — `segmented-ui` gains `[multiple]`; `toggle-group-ui`/`toggle-option-ui` deprecated (gh#1369)
1308
+
1309
+ **Additive, non-breaking — plus a soft deprecation with no removal.**
1310
+ `<segmented-ui>` gains a `multiple: Boolean` axis (default `false`).
1311
+ `<toggle-group-ui>` and `<toggle-option-ui>` are marked
1312
+ `status: deprecated` and log a one-time `console.warn` on connect; **both
1313
+ keep working exactly as before** — this is a recommended migration, not
1314
+ a forced break.
1315
+
1316
+ **Why:** the catalog-regularity audit's C1 finding —
1317
+ [gh#1363](https://github.com/adiahealth/gen-ui-kit/issues/1363), its
1318
+ top-ranked candidate. `<toggle-group-ui>` already carried a `single`
1319
+ mode prop that made it behave like segmented (`toggle-group.yaml`);
1320
+ four components played one concept. `toggle-group-ui`/`toggle-option-ui`
1321
+ had **zero** corpus chunk files (segmented/segment: 19 each), so
1322
+ multi-select intents (filter chips, day-of-week pickers) retrieved
1323
+ nothing for toggle-group or mis-retrieved single-select segmented
1324
+ chunks. Merging the multi-select axis into segmented gives multi-select
1325
+ intents the same 19-chunk retrieval mass single-select already has, and
1326
+ removes the Segmented-vs-ToggleGroup decision rule that was taught
1327
+ redundantly across four separate a2ui rules.
1328
+
1329
+ **New API:** under `[multiple]`, `<segmented-ui>`'s `value` becomes a
1330
+ comma-separated set of selected `<segment-ui>` values — the same
1331
+ encoding `<toggle-group-ui>` and `<select-ui multiple>` already use.
1332
+ The sliding indicator (single-select's animated pill) is suppressed;
1333
+ each selected segment carries its own background instead
1334
+ (`--segment-bg-selected`). Keyboard model changes under `[multiple]`
1335
+ only: arrow keys move focus without selecting (a checkbox-toolbar
1336
+ model, `role="group"` / children `role="checkbox"`), Space/Enter
1337
+ toggles the focused segment. Single-select (`[multiple]` absent) is
1338
+ byte-identical to before — radiogroup model, arrow-move selects,
1339
+ sliding indicator.
1340
+
1341
+ **Migration:**
1342
+
1343
+ | Old | New |
1344
+ |---|---|
1345
+ | `<toggle-group-ui value="bold,italic">` + `<toggle-option-ui value="bold" text="Bold" icon="…">` children | `<segmented-ui multiple value="bold,italic">` + `<segment-ui value="bold" text="Bold" icon="…">` children — `value`/`text`/`icon`/`disabled` map directly, same comma-separated `value` encoding |
1346
+ | `<toggle-group-ui single value="…">` (toggle-group's own single-select mode prop) | `<segmented-ui value="…">` (no `[multiple]`) — segmented's native single-select mode, not a mode flag on the multi-select component |
1347
+ | `toggle-option-ui[selected]` CSS | `segment-ui[selected]` under a `segmented-ui[multiple]` ancestor |
1348
+
1349
+ **Behavioral gap: `[single]`'s clear-on-reclick has no segmented-ui
1350
+ equivalent.** `<toggle-group-ui single>` clears its selection (empty
1351
+ `value`) when the already-selected option is activated again
1352
+ (`toggle-group.class.js`'s `#toggle()`: `if (selected.has(optVal))
1353
+ selected.clear()`). `<segmented-ui>` single-select does not — it's
1354
+ standard radiogroup semantics, exactly one segment stays selected once
1355
+ a value is set; clicking the already-selected segment again is a
1356
+ no-op. `allow-empty` does **not** cover this gap either — it only
1357
+ suppresses the connect-time self-select of the first segment when the
1358
+ initial `value` is falsy, it has no effect on later clicks. Most
1359
+ `[single]` consumers don't rely on the clear affordance (it wasn't
1360
+ advertised as the toggle-group contract's headline behavior) — audit
1361
+ before assuming it's needed.
1362
+
1363
+ **If a `[single]` consumer relies on the clear-on-reclick affordance**,
1364
+ the migration has no built-in replacement — wire it yourself, and the
1365
+ listener phase matters. `segmented-ui`'s own click handler runs on the
1366
+ **bubble** phase (registered in `connected()`); a same-phase consumer
1367
+ listener that reads `el.value` to decide "was this a re-click" is
1368
+ **registration-order-fragile**: a bubble listener attached AFTER
1369
+ `connected()` already ran (the realistic case —
1370
+ `document.querySelector('segmented-ui').addEventListener(...)` on an
1371
+ already-mounted page) reads `el.value` **after** segmented-ui's own
1372
+ handler already overwrote it with the just-clicked value, so the check
1373
+ is true on every click, not just re-clicks — it would incorrectly
1374
+ clear a selection of a *different* segment too. Snapshot the
1375
+ pre-click value in a **capture-phase** listener instead — capture
1376
+ always runs before bubble, independent of registration order — then
1377
+ apply the clear in a microtask so it lands after segmented-ui's own
1378
+ bubble handler (and its `change` dispatch) have already committed the
1379
+ reselect:
1380
+
1381
+ ```js
1382
+ el.addEventListener(
1383
+ 'click',
1384
+ (e) => {
1385
+ const seg = e.target.closest('segment-ui');
1386
+ if (!seg) return;
1387
+ const clickedValue = seg.value || seg.getAttribute('value') || '';
1388
+ const wasAlreadySelected = clickedValue === el.value; // pre-click snapshot
1389
+ if (wasAlreadySelected) {
1390
+ queueMicrotask(() => {
1391
+ el.value = '';
1392
+ // segmented-ui already dispatched one `change` for the reselect;
1393
+ // re-dispatch so consumers see the final (cleared) state too.
1394
+ el.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: '' } }));
1395
+ });
1396
+ }
1397
+ },
1398
+ { capture: true },
1399
+ );
1400
+ ```
1401
+
1402
+ **Find every affected consumer site with the repo's own scanner, plus
1403
+ the multiline-capable manual sweep** (established in the `input-ui`
1404
+ `type`-enum rider above, and again for this exact tag-rename shape at
1405
+ gh#1414) — a line-based `git grep` misses multi-line opening tags:
1406
+
1407
+ ```bash
1408
+ npm run qa:contract-migration-scan -- --base <pre-upgrade-ref> --head <post-upgrade-ref>
1409
+ rg -nU '<toggle-group-ui[^>]*>|<toggle-option-ui[^>]*>' \
1410
+ -g '!*CHANGELOG*.md' -g '!**/dist/**' -g '!*.examples.md' -g '!*MIGRATION*' \
1411
+ -g '!.claude/docs/**'
1412
+ ```
1413
+
1414
+ Both tags keep working — a positive hit is a candidate to migrate on
1415
+ your own schedule, not a broken build. Verify each hit's element is
1416
+ actually a `toggle-group-ui`/`toggle-option-ui` host before rewriting.
1417
+
1418
+ ### Verify
1419
+
1420
+ ```bash
1421
+ npm run qa:contract-migration-scan -- --base <pre-upgrade-ref> --head <post-upgrade-ref>
1422
+ rg -nU '<toggle-group-ui[^>]*>|<toggle-option-ui[^>]*>' \
1423
+ -g '!*CHANGELOG*.md' -g '!**/dist/**' -g '!*.examples.md' -g '!*MIGRATION*' \
1424
+ -g '!.claude/docs/**' # any hit is a migration candidate, not a failure — both tags still work
1425
+ npm run check
1426
+ ```
1427
+
1428
+ ---
1429
+
1430
+ <a id="upcoming--calendar-picker-ui-absorbs-datetime-picker-ui-behind-a-widened-precision-axis-090-gh1372"></a>
1431
+ ## Shipped in `0.8.41` — `calendar-picker-ui` absorbs `datetime-picker-ui` behind a widened `precision` axis (gh#1372)
1432
+
1433
+ **Mostly additive + soft deprecation; two real breaking removals on `calendar-picker-ui` itself.**
1434
+ `<calendar-picker-ui>` gains a `precision` axis (`date` | `minute` | `second`,
1435
+ default `date` — unchanged behavior for every existing consumer that never
1436
+ set it) plus the props/events it needs when a time pane is on:
1437
+ `step`/`hour-cycle`/`locale` (active only when `precision` isn't `date`),
1438
+ and the `input`/`open`/`close`/`invalid` events (previously only `change`
1439
+ existed). `<datetime-picker-ui>` is now marked `status: deprecated` and
1440
+ implemented as a thin `UICalendarPicker` subclass (`precision="minute"` by
1441
+ default) rather than an independent primitive — it keeps working
1442
+ unchanged. Filed from the catalog-regularity audit's C3c finding,
1443
+ [gh#1363](https://github.com/adiahealth/gen-ui-kit/issues/1363) (C3d
1444
+ sibling-contract convergence folded into the same build).
1445
+
1446
+ **Why:** `datetime-picker-ui` was structurally `calendar-picker-ui` (a
1447
+ trigger + popover) plus a composed `<time-picker-ui>` pane — same
1448
+ `format`/`placement`/`open` contract, same ISO-string value shape both
1449
+ ways. Two near-identical implementations of the same trigger/popover/
1450
+ anchor/focus-trap machinery is exactly the maintenance-cost shape
1451
+ gh#1363 was auditing for, and `datetime-picker-ui` was still
1452
+ `status: experimental` with zero corpus chunks — "the cheap moment
1453
+ closes at stabilization." `calendar-picker-ui[precision]` also closes two
1454
+ sibling-contract gaps `calendar-picker-ui` had relative to its own
1455
+ siblings (`readonly` with real behavior, not just an inherited-but-unwired
1456
+ property; `name`/`min`/`max` documented `reflect: true`, matching what the
1457
+ class already did).
1458
+
1459
+ **Breaking removals on `calendar-picker-ui`:**
1460
+
1461
+ | Old | New |
1462
+ |---|---|
1463
+ | `<calendar-picker-ui label="…">` | `<field-ui label="…"><calendar-picker-ui></calendar-picker-ui></field-ui>` — matches every sibling picker (`date-range-picker-ui`, `datetime-picker-ui`, `time-picker-ui` never had a first-class `label`; this was `calendar-picker-ui`'s own outlier) |
1464
+ | `slot="label"` child on `<calendar-picker-ui>` | drop it — same rationale; the slot was never wired to any rendering either |
1465
+
1466
+ **Breaking token removals on `datetime-picker-ui`** (it owns no CSS custom
1467
+ properties of its own anymore — everything routes through
1468
+ `calendar-picker-ui`'s `--calendar-picker-*` tokens, which the two tags
1469
+ now share):
1470
+
1471
+ | Old token | New token |
1472
+ |---|---|
1473
+ | `--datetime-picker-popover-bg` | `--calendar-picker-popover-bg` |
1474
+ | `--datetime-picker-popover-border` | `--calendar-picker-popover-border` |
1475
+ | `--datetime-picker-popover-radius` | `--calendar-picker-popover-radius` |
1476
+ | `--datetime-picker-popover-shadow` | `--calendar-picker-popover-shadow` |
1477
+ | `--datetime-picker-popover-padding` | `--calendar-picker-popover-padding` |
1478
+ | `--datetime-picker-popover-gap` | `--calendar-picker-popover-gap` |
1479
+ | `--datetime-picker-divider` | `--calendar-picker-divider` |
1480
+ | `--datetime-picker-duration` | `--calendar-picker-duration` |
1481
+ | `--datetime-picker-easing` | `--calendar-picker-easing` |
1482
+ | `--datetime-picker-bg`, `--datetime-picker-fg` | *removed, no replacement* — the host paints no background of its own; text color inherits |
1483
+ | `--datetime-picker-trigger-min-width`, `--datetime-picker-px`, `--datetime-picker-py` | *removed, no replacement* — the trigger sizes to content; set width/padding via ordinary CSS on the host if a fixed size is wanted |
1484
+
1485
+ **Recommended migration (non-breaking, on your own schedule):**
1486
+ `<datetime-picker-ui name="…">` → `<calendar-picker-ui precision="minute" name="…">`
1487
+ (or `precision="second"` for the former second-precision mode) — every
1488
+ other attribute name is unchanged.
1489
+
1490
+ **Find every affected consumer site with the repo's own scanner:**
1491
+
1492
+ ```bash
1493
+ npm run qa:contract-migration-scan -- --base <pre-upgrade-ref> --head <post-upgrade-ref> \
1494
+ --roots packages,apps,playgrounds,site,site-a2ui
1495
+ ```
1496
+
1497
+ The `--roots` addition is required here — the scanner's own default roots
1498
+ miss `site-a2ui/` (ADR-0063's own consumer-scan note applies equally to
1499
+ this rider). The removed `label` prop/slot has no reported sites in this
1500
+ repo as of the fold landing; the `--datetime-picker-*` token findings are
1501
+ expected to fire on `datetime-picker.examples.html`'s own old→new
1502
+ reference table above (that's documentation explaining the rename, not a
1503
+ live override — disregard that one self-hit). `<datetime-picker-ui>` tag
1504
+ usage itself is a soft-deprecation candidate, not a scanner finding (the
1505
+ tag isn't removed):
1506
+
1507
+ ```bash
1508
+ rg -nU '<datetime-picker-ui\b[^>]*>' \
1509
+ -g '!*CHANGELOG*.md' -g '!**/dist/**' -g '!*.examples.md' -g '!*MIGRATION*' \
1510
+ -g '!.claude/docs/**' # any hit is a migration candidate, not a failure — the tag still works
1511
+ ```
1512
+
1513
+ ### Verify
1514
+
1515
+ ```bash
1516
+ npm run qa:contract-migration-scan -- --base <pre-upgrade-ref> --head <post-upgrade-ref> --roots packages,apps,playgrounds,site,site-a2ui
1517
+ rg -nU '<calendar-picker-ui\b[^>]*\blabel=' -g '!.claude/docs/**' # must be 0
1518
+ npm run check
1519
+ ```
1520
+
1521
+ ---
1522
+
1523
+ <a id="upcoming--a2ui-catalog-component-collision-adminshell--uichatthread-090-gh1427"></a>
1524
+ ## Shipped in `0.8.41` — A2UI catalog `component:` collision: `AdminShell` / `UIChatThread` (gh#1427)
1525
+
1526
+ **BREAKING for hand-authored or generated A2UI documents only — no DOM/JS API
1527
+ change.** `<admin-shell>` and `<chat-thread-ui>` keep their existing tag
1528
+ names, props, and events unchanged. What changed is the **A2UI catalog's**
1529
+ internal `component:` identifier — the value that appears in a rendered
1530
+ a2ui JSON tree's `"component"` field and the L0 catalog's own top-level key.
1531
+
1532
+ **Why:** `packages/web-modules/shell/admin-shell/admin-shell.yaml` and
1533
+ `packages/web-modules/shell/embed-shell/embed-shell.yaml` both declared
1534
+ `component: AppShell`. `scripts/build/components.mjs` used that field as the
1535
+ catalog's own key with no collision check — alphabetical directory
1536
+ traversal processed `admin-shell` first, so `embed-shell`'s fragment
1537
+ silently overwrote it: the L0 catalog's `components.AppShell` entry held
1538
+ **embed-shell's** (experimental) contract, and `admin-shell` (stable, the
1539
+ more heavily used shell) had **no L0 catalog schema entry at all** — no
1540
+ build error, no warning. Fixed by giving `admin-shell` its own catalog
1541
+ identity, `AdminShell`, matching the existing `registry.js` mapping
1542
+ (`['AdminShell', 'admin-shell']`) that had already anticipated this;
1543
+ `embed-shell` keeps `AppShell` (its own established, intentional mapping).
1544
+ `scripts/build/components.mjs` also gained a build-time collision guard — a
1545
+ duplicate `component:` value across yaml SoTs now throws instead of
1546
+ silently overwriting, which caught a second, independent instance of the
1547
+ identical defect: `packages/web-components/components/chat-thread/
1548
+ chat-thread.yaml` (the primitive, tag `chat-thread-ui`) still declared
1549
+ `component: ChatThread`, colliding with the chat web-module's own
1550
+ `ChatThread` (tag `chat-thread`, `registry.js`'s `['ChatThread',
1551
+ 'chat-thread']` mapping). Renamed the primitive's catalog identity to
1552
+ `UIChatThread` (matching its own `name:` field), added the matching
1553
+ `registry.js` entry.
1554
+
1555
+ **Migration:**
1556
+
1557
+ | Old (a2ui JSON) | New (a2ui JSON) | HTML tag (unchanged) |
1558
+ |---|---|---|
1559
+ | `{"component": "AppShell", ...}` intending the **stable admin shell** | `{"component": "AdminShell", ...}` | `<admin-shell>` |
1560
+ | `{"component": "AppShell", ...}` intending the **experimental embed shell** | unchanged — still `{"component": "AppShell", ...}` | `<embed-shell>` |
1561
+ | `{"component": "ChatThread", ...}` intending the **primitive** scroll container (used OUTSIDE a `<chat-shell>`) | `{"component": "UIChatThread", ...}` | `<chat-thread-ui>` |
1562
+ | `{"component": "ChatThread", ...}` intending the **chat-shell module child** | unchanged — still `{"component": "ChatThread", ...}` | `<chat-thread>` |
1563
+
1564
+ Only a consumer that hand-authors or generates raw A2UI trees referencing
1565
+ these `component` values directly is affected — component authoring via
1566
+ the HTML tags themselves (`<admin-shell>`, `<chat-thread-ui>`) needs no
1567
+ change. Any pre-fix generation that produced `"component": "AppShell"`
1568
+ meaning admin-shell was already silently wrong (it validated against
1569
+ embed-shell's schema instead) — this migration corrects, not breaks, that
1570
+ path.
1571
+
1572
+ ### Verify
1573
+
1574
+ ```bash
1575
+ rg -nU '"component"\s*:\s*"(AppShell|ChatThread)"' \
1576
+ -g '!**/dist/**' -g '!*MIGRATION*' -g '!.claude/docs/**' -g '!**/corpus/chunks/**'
1577
+ npm run check
1578
+ ```
1579
+
1580
+ ---
1581
+
1582
+ <a id="upcoming--progress-row-ui-deprecated--absorbed-into-progress-ui-090-gh1373"></a>
1583
+ ## Shipped in `0.8.41` — `progress-row-ui` deprecated — absorbed into `progress-ui` (gh#1373)
1584
+
1585
+ **Non-breaking, deprecation alias.** `<progress-ui>` gains a `label` +
1586
+ `meta` prop pair. A non-empty `label` switches `<progress-ui>` into the
1587
+ same labeled-row layout `<progress-row-ui>` renders — label + bar +
1588
+ optional right-aligned `meta` text — a new Sanctioned Mode Attribute
1589
+ (`.claude/docs/specs/component-token-contract.md`'s Modes table).
1590
+ `<progress-row-ui>` itself is unchanged at the implementation level
1591
+ (still composes `<progress-ui>` internally, exact same runtime
1592
+ behavior) but ships `status: deprecated` (ADR-0057's vocabulary) — the
1593
+ site badges it, and the A2UI catalog no longer teaches or generates it.
1594
+
1595
+ **Why:** filed from the catalog-regularity audit's C4 finding
1596
+ ([gh#1363](https://github.com/adiahealth/gen-ui-kit/issues/1363)) —
1597
+ `progress-row.yaml:10-15` is a thin label+meta wrapper over
1598
+ `progress-ui`, and the catalog already believed they were one
1599
+ component: `progress.yaml`'s own canonical `dashboard` example put
1600
+ `label` on a `Progress` node — a prop only `ProgressRow` had — meaning
1601
+ generation copying the catalog's own exemplar failed validation before
1602
+ this rider.
1603
+
1604
+ **Migration:**
1605
+
1606
+ | Old | New |
1607
+ |---|---|
1608
+ | `<progress-row-ui label="…" value="…" meta="…">` | `<progress-ui label="…" value="…" meta="…">` — attribute names unchanged, a mechanical tag rename |
1609
+ | `<progress-row-ui label="…">` (no meta) | `<progress-ui label="…">` |
1610
+ | `<progress-row-ui variant="success\|warning\|danger">` | **no successor** — see below |
1611
+ | `<progress-row-ui>` (unmodified) | keeps working; not required to migrate, but no longer the taught/generated shape |
1612
+
1613
+ **`variant` (the row's color-family prop) has no successor on
1614
+ `<progress-ui>`.** Progress's own `variant` prop was already retired
1615
+ bar-only in the C2 rider ([gh#1370](https://github.com/adiahealth/gen-ui-kit/issues/1370))
1616
+ and stays retired — this rider does not reopen it, since none of the
1617
+ repo's own authored consumers used `progress-row-ui[variant]` (a
1618
+ `qa:contract-migration-scan` + `rg` sweep of every real usage site
1619
+ found zero). To recolor the fill in row layout, override the
1620
+ `--progress-fill` token directly — `--progress-fill:
1621
+ var(--a-success-bg)`, `--progress-fill: var(--a-warning-bg)`, or
1622
+ `--progress-fill: var(--a-danger-bg)` — the same token-only convention
1623
+ progress's yaml has documented since before this change ("there is no
1624
+ variant-as-color, use the token").
1625
+
1626
+ **Find every affected consumer site:**
1627
+
1628
+ ```bash
1629
+ npm run qa:contract-migration-scan -- --base <pre-upgrade-ref> --head <post-upgrade-ref>
1630
+ rg -nU '<progress-row-ui[^>]*>' \
1631
+ -g '!*CHANGELOG*.md' -g '!**/dist/**' -g '!*.examples.md' -g '!*MIGRATION*' \
1632
+ -g '!.claude/docs/**'
1633
+ ```
1634
+
1635
+ The scanner's diff detection is scoped to a yaml losing a `props`/
1636
+ `tokens` key or its `tag:` field (its own documented v1 scope) — this
1637
+ change is additive on `progress.yaml` and non-breaking on
1638
+ `progress-row.yaml`, so expect it to report no contract break; the
1639
+ `rg` sweep is the one that finds real markup sites to migrate (not
1640
+ required — `progress-row-ui` keeps working — but recommended so new
1641
+ edits to a touched file land on the current shape).
1642
+
1643
+ ### Verify
1644
+
1645
+ ```bash
1646
+ npm run qa:contract-migration-scan -- --base <pre-upgrade-ref> --head <post-upgrade-ref>
1647
+ npm run harvest:chunks # re-validates every chunk against the updated catalog
1648
+ npm run check
1649
+ ```
1650
+
1651
+ ---
1652
+
387
1653
  ## Migrating to `@adia-ai/*@v0.8.23` (2026-07-30)
388
1654
 
389
1655
  - **Removed: the `--a-font` alias** (pre-ADR-0035 name, DEPRECATED since the 0.8.x line). Any surviving `font-family: var(--a-font)` goes invalid-at-computed-value-time → `inherit` → UA **serif** — the visible symptom is Times-faced labels on an otherwise-correct page.