@adia-ai/web-components 0.6.33 → 0.6.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (391) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/color/index.js +1 -1
  3. package/components/accordion/accordion-item.yaml +2 -2
  4. package/components/accordion/accordion.css +2 -2
  5. package/components/accordion/accordion.js +1 -1
  6. package/components/action-list/action-item.yaml +2 -2
  7. package/components/action-list/action-list.css +2 -2
  8. package/components/action-list/action-list.js +1 -1
  9. package/components/agent-artifact/{class.js → agent-artifact.class.js} +1 -1
  10. package/components/agent-artifact/agent-artifact.css +31 -31
  11. package/components/agent-artifact/agent-artifact.js +1 -1
  12. package/components/agent-feedback-bar/agent-feedback-bar.css +10 -10
  13. package/components/agent-feedback-bar/agent-feedback-bar.js +1 -1
  14. package/components/agent-questions/agent-questions.css +57 -57
  15. package/components/agent-questions/agent-questions.js +1 -1
  16. package/components/agent-reasoning/agent-reasoning.css +62 -62
  17. package/components/agent-reasoning/agent-reasoning.js +1 -1
  18. package/components/agent-suggestions/agent-suggestions.css +4 -4
  19. package/components/agent-suggestions/agent-suggestions.js +1 -1
  20. package/components/agent-trace/agent-trace.css +53 -53
  21. package/components/alert/alert.a2ui.json +64 -1
  22. package/components/alert/{class.js → alert.class.js} +189 -2
  23. package/components/alert/alert.css +119 -41
  24. package/components/alert/alert.d.ts +14 -0
  25. package/components/alert/alert.js +1 -1
  26. package/components/alert/alert.test.js +184 -0
  27. package/components/alert/alert.yaml +114 -1
  28. package/components/avatar/avatar-group.yaml +2 -2
  29. package/components/avatar/avatar.css +27 -27
  30. package/components/avatar/avatar.js +1 -1
  31. package/components/badge/badge.css +27 -27
  32. package/components/badge/badge.js +1 -1
  33. package/components/block/block.css +16 -16
  34. package/components/block/block.js +1 -1
  35. package/components/breadcrumb/breadcrumb.css +23 -23
  36. package/components/breadcrumb/breadcrumb.js +1 -1
  37. package/components/button/button.css +101 -91
  38. package/components/button/button.js +1 -1
  39. package/components/calendar-grid/calendar-grid.a2ui.json +146 -0
  40. package/components/calendar-grid/calendar-grid.class.js +326 -0
  41. package/components/calendar-grid/calendar-grid.css +246 -0
  42. package/components/calendar-grid/calendar-grid.d.ts +41 -0
  43. package/components/calendar-grid/calendar-grid.js +17 -0
  44. package/components/calendar-grid/calendar-grid.yaml +136 -0
  45. package/components/calendar-picker/calendar-picker.css +139 -139
  46. package/components/calendar-picker/calendar-picker.js +1 -1
  47. package/components/canvas/canvas.css +12 -12
  48. package/components/card/card.css +83 -83
  49. package/components/card/card.js +1 -1
  50. package/components/chart/chart.css +224 -224
  51. package/components/chart/chart.js +1 -1
  52. package/components/chart-legend/chart-legend.css +26 -26
  53. package/components/chart-legend/chart-legend.js +1 -1
  54. package/components/chat-thread/chat-input.a2ui.json +1 -1
  55. package/components/chat-thread/chat-input.js +6 -1
  56. package/components/chat-thread/chat-input.yaml +4 -1
  57. package/components/chat-thread/chat-thread.js +1 -1
  58. package/components/check/check.css +40 -40
  59. package/components/check/check.js +1 -1
  60. package/components/code/code.css +125 -125
  61. package/components/code/code.js +1 -1
  62. package/components/col/col.css +15 -15
  63. package/components/col/col.js +1 -1
  64. package/components/color-input/color-input.js +1 -1
  65. package/components/color-picker/color-picker.css +55 -55
  66. package/components/color-picker/color-picker.js +1 -1
  67. package/components/combobox/combobox.a2ui.json +363 -0
  68. package/components/combobox/combobox.class.js +861 -0
  69. package/components/combobox/combobox.css +244 -0
  70. package/components/combobox/combobox.d.ts +113 -0
  71. package/components/combobox/combobox.examples.md +59 -0
  72. package/components/combobox/combobox.js +17 -0
  73. package/components/combobox/combobox.test.js +181 -0
  74. package/components/combobox/combobox.yaml +369 -0
  75. package/components/command/command.css +90 -90
  76. package/components/command/command.js +1 -1
  77. package/components/date-range-picker/date-range-picker.a2ui.json +300 -0
  78. package/components/date-range-picker/date-range-picker.class.js +791 -0
  79. package/components/date-range-picker/date-range-picker.css +224 -0
  80. package/components/date-range-picker/date-range-picker.d.ts +82 -0
  81. package/components/date-range-picker/date-range-picker.examples.md +37 -0
  82. package/components/date-range-picker/date-range-picker.js +17 -0
  83. package/components/date-range-picker/date-range-picker.test.js +387 -0
  84. package/components/date-range-picker/date-range-picker.yaml +285 -0
  85. package/components/datetime-picker/datetime-picker.a2ui.json +334 -0
  86. package/components/datetime-picker/datetime-picker.class.js +706 -0
  87. package/components/datetime-picker/datetime-picker.css +150 -0
  88. package/components/datetime-picker/datetime-picker.d.ts +86 -0
  89. package/components/datetime-picker/datetime-picker.examples.md +46 -0
  90. package/components/datetime-picker/datetime-picker.js +17 -0
  91. package/components/datetime-picker/datetime-picker.test.js +454 -0
  92. package/components/datetime-picker/datetime-picker.yaml +332 -0
  93. package/components/demo-toggle/demo-toggle.css +27 -27
  94. package/components/demo-toggle/demo-toggle.js +1 -1
  95. package/components/description-list/description-list.css +18 -18
  96. package/components/description-list/description-list.js +1 -1
  97. package/components/divider/divider.css +24 -24
  98. package/components/divider/divider.js +1 -1
  99. package/components/drawer/drawer.js +1 -1
  100. package/components/embed/embed.css +6 -6
  101. package/components/embed/embed.js +1 -1
  102. package/components/empty-state/empty-state.css +27 -27
  103. package/components/empty-state/empty-state.js +1 -1
  104. package/components/feed/feed.css +12 -12
  105. package/components/feed/feed.js +1 -1
  106. package/components/field/field.css +28 -28
  107. package/components/field/field.js +1 -1
  108. package/components/field/field.test.js +1 -1
  109. package/components/fields/fields.css +5 -5
  110. package/components/fields/fields.js +1 -1
  111. package/components/grid/grid.css +5 -5
  112. package/components/grid/grid.js +1 -1
  113. package/components/heatmap/heatmap.css +63 -63
  114. package/components/heatmap/heatmap.js +1 -1
  115. package/components/icon/icon.css +12 -12
  116. package/components/icon/icon.js +1 -1
  117. package/components/image/image.css +14 -14
  118. package/components/image/image.js +1 -1
  119. package/components/index.js +11 -0
  120. package/components/inline-message/inline-message.a2ui.json +143 -0
  121. package/components/inline-message/inline-message.class.js +169 -0
  122. package/components/inline-message/inline-message.css +75 -0
  123. package/components/inline-message/inline-message.d.ts +31 -0
  124. package/components/inline-message/inline-message.examples.md +19 -0
  125. package/components/inline-message/inline-message.js +17 -0
  126. package/components/inline-message/inline-message.test.js +203 -0
  127. package/components/inline-message/inline-message.yaml +205 -0
  128. package/components/input/input.css +67 -67
  129. package/components/input/input.js +1 -1
  130. package/components/input/input.yaml +5 -4
  131. package/components/inspector/inspector.css +6 -6
  132. package/components/inspector/inspector.js +1 -1
  133. package/components/integration-card/integration-card.a2ui.json +268 -0
  134. package/components/integration-card/integration-card.class.js +410 -0
  135. package/components/integration-card/integration-card.css +169 -0
  136. package/components/integration-card/integration-card.d.ts +63 -0
  137. package/components/integration-card/integration-card.examples.md +41 -0
  138. package/components/integration-card/integration-card.js +17 -0
  139. package/components/integration-card/integration-card.test.js +306 -0
  140. package/components/integration-card/integration-card.yaml +280 -0
  141. package/components/kbd/kbd.css +32 -32
  142. package/components/kbd/kbd.js +1 -1
  143. package/components/link/link.css +12 -12
  144. package/components/link/link.js +1 -1
  145. package/components/list/list-item.yaml +2 -2
  146. package/components/list/list.css +8 -8
  147. package/components/list/list.js +1 -1
  148. package/components/list-window/list-window.a2ui.json +277 -0
  149. package/components/list-window/list-window.class.js +688 -0
  150. package/components/list-window/list-window.css +124 -0
  151. package/components/list-window/list-window.d.ts +84 -0
  152. package/components/list-window/list-window.examples.md +73 -0
  153. package/components/list-window/list-window.js +17 -0
  154. package/components/list-window/list-window.test.js +303 -0
  155. package/components/list-window/list-window.yaml +270 -0
  156. package/components/loading-overlay/loading-overlay.a2ui.json +176 -0
  157. package/components/loading-overlay/loading-overlay.class.js +203 -0
  158. package/components/loading-overlay/loading-overlay.css +81 -0
  159. package/components/loading-overlay/loading-overlay.d.ts +24 -0
  160. package/components/loading-overlay/loading-overlay.examples.md +50 -0
  161. package/components/loading-overlay/loading-overlay.js +17 -0
  162. package/components/loading-overlay/loading-overlay.test.js +257 -0
  163. package/components/loading-overlay/loading-overlay.yaml +260 -0
  164. package/components/menu/menu-divider.yaml +1 -1
  165. package/components/menu/menu-item.yaml +1 -1
  166. package/components/menu/menu.a2ui.json +3 -0
  167. package/components/menu/menu.css +8 -8
  168. package/components/menu/menu.js +1 -1
  169. package/components/menu/menu.yaml +7 -0
  170. package/components/modal/{class.js → modal.class.js} +12 -1
  171. package/components/modal/modal.css +54 -44
  172. package/components/modal/modal.js +1 -1
  173. package/components/nav/nav.css +40 -40
  174. package/components/nav/nav.js +1 -1
  175. package/components/nav-group/nav-group.css +52 -52
  176. package/components/nav-group/nav-group.js +1 -1
  177. package/components/nav-item/nav-item.css +44 -44
  178. package/components/nav-item/nav-item.js +1 -1
  179. package/components/noodles/noodles.css +31 -31
  180. package/components/noodles/noodles.js +1 -1
  181. package/components/option-card/option-card.css +69 -69
  182. package/components/option-card/option-card.js +1 -1
  183. package/components/otp-input/otp-input.css +30 -30
  184. package/components/otp-input/otp-input.js +1 -1
  185. package/components/page/page.css +18 -18
  186. package/components/page/page.js +1 -1
  187. package/components/pagination/pagination.css +61 -61
  188. package/components/pagination/pagination.js +1 -1
  189. package/components/pane/pane.css +57 -57
  190. package/components/pane/pane.js +1 -1
  191. package/components/pipeline-status/pipeline-status.css +65 -65
  192. package/components/pipeline-status/pipeline-status.js +1 -1
  193. package/components/popover/popover.a2ui.json +8 -1
  194. package/components/popover/popover.css +17 -17
  195. package/components/popover/popover.js +1 -1
  196. package/components/popover/popover.yaml +14 -1
  197. package/components/progress/progress.css +23 -23
  198. package/components/progress/progress.js +1 -1
  199. package/components/progress-row/progress-row.css +17 -17
  200. package/components/progress-row/progress-row.js +1 -1
  201. package/components/radio/radio.css +39 -39
  202. package/components/radio/radio.js +1 -1
  203. package/components/range/range.css +55 -55
  204. package/components/range/range.js +1 -1
  205. package/components/rating/rating.css +28 -28
  206. package/components/rating/rating.js +1 -1
  207. package/components/richtext/richtext.css +133 -133
  208. package/components/richtext/richtext.js +1 -1
  209. package/components/row/row.css +19 -19
  210. package/components/row/row.js +1 -1
  211. package/components/search/search.css +5 -5
  212. package/components/search/search.js +1 -1
  213. package/components/segment/segment.css +24 -24
  214. package/components/segment/segment.js +1 -1
  215. package/components/segmented/segmented.css +25 -25
  216. package/components/segmented/segmented.js +1 -1
  217. package/components/select/select.a2ui.json +58 -4
  218. package/components/select/{class.js → select.class.js} +415 -6
  219. package/components/select/select.css +242 -84
  220. package/components/select/select.d.ts +31 -1
  221. package/components/select/select.js +1 -1
  222. package/components/select/select.test.js +202 -0
  223. package/components/select/select.yaml +126 -5
  224. package/components/skeleton/skeleton.css +14 -14
  225. package/components/skeleton/skeleton.js +1 -1
  226. package/components/slider/slider.css +46 -46
  227. package/components/slider/slider.js +1 -1
  228. package/components/spinner/spinner.a2ui.json +198 -0
  229. package/components/spinner/spinner.class.js +99 -0
  230. package/components/spinner/spinner.css +221 -0
  231. package/components/spinner/spinner.d.ts +26 -0
  232. package/components/spinner/spinner.examples.md +26 -0
  233. package/components/spinner/spinner.js +17 -0
  234. package/components/spinner/spinner.test.js +272 -0
  235. package/components/spinner/spinner.yaml +238 -0
  236. package/components/stack/stack.css +11 -11
  237. package/components/stack/stack.js +1 -1
  238. package/components/stat/stat.css +25 -25
  239. package/components/step-progress/step-progress.css +20 -20
  240. package/components/step-progress/step-progress.js +1 -1
  241. package/components/stepper/stepper-item.yaml +1 -1
  242. package/components/stepper/stepper.css +29 -29
  243. package/components/stepper/stepper.js +1 -1
  244. package/components/stream/stream.css +12 -12
  245. package/components/stream/stream.js +1 -1
  246. package/components/swatch/swatch.css +68 -68
  247. package/components/swatch/swatch.js +1 -1
  248. package/components/swiper/swiper.css +57 -57
  249. package/components/swiper/swiper.js +1 -1
  250. package/components/switch/switch.css +52 -52
  251. package/components/switch/switch.js +1 -1
  252. package/components/table/table.css +163 -163
  253. package/components/table/table.js +1 -1
  254. package/components/table-toolbar/{class.js → table-toolbar.class.js} +1 -1
  255. package/components/table-toolbar/table-toolbar.css +32 -32
  256. package/components/table-toolbar/table-toolbar.js +1 -1
  257. package/components/tabs/tab.yaml +2 -2
  258. package/components/tabs/tabs.css +51 -51
  259. package/components/tabs/tabs.js +1 -1
  260. package/components/tag/tag.css +48 -48
  261. package/components/tag/tag.js +1 -1
  262. package/components/tags-input/tags-input.a2ui.json +337 -0
  263. package/components/tags-input/tags-input.class.js +776 -0
  264. package/components/tags-input/tags-input.css +201 -0
  265. package/components/tags-input/tags-input.d.ts +120 -0
  266. package/components/tags-input/tags-input.examples.md +92 -0
  267. package/components/tags-input/tags-input.js +17 -0
  268. package/components/tags-input/tags-input.test.js +368 -0
  269. package/components/tags-input/tags-input.yaml +367 -0
  270. package/components/text/text.css +44 -44
  271. package/components/text/text.js +1 -1
  272. package/components/textarea/textarea.a2ui.json +1 -1
  273. package/components/textarea/textarea.css +46 -46
  274. package/components/textarea/textarea.js +1 -1
  275. package/components/textarea/textarea.yaml +11 -8
  276. package/components/time-picker/time-picker.a2ui.json +267 -0
  277. package/components/time-picker/time-picker.class.js +693 -0
  278. package/components/time-picker/time-picker.css +122 -0
  279. package/components/time-picker/time-picker.d.ts +75 -0
  280. package/components/time-picker/time-picker.examples.md +35 -0
  281. package/components/time-picker/time-picker.js +17 -0
  282. package/components/time-picker/time-picker.test.js +287 -0
  283. package/components/time-picker/time-picker.yaml +256 -0
  284. package/components/timeline/timeline-item.yaml +2 -2
  285. package/components/timeline/{class.js → timeline.class.js} +1 -1
  286. package/components/timeline/timeline.css +50 -50
  287. package/components/timeline/timeline.js +1 -1
  288. package/components/toast/toast.css +58 -58
  289. package/components/toast/toast.js +1 -1
  290. package/components/toggle-group/toggle-group.css +6 -6
  291. package/components/toggle-group/toggle-group.js +1 -1
  292. package/components/toggle-group/toggle-option.yaml +1 -1
  293. package/components/toggle-scheme/toggle-scheme.css +2 -2
  294. package/components/toggle-scheme/toggle-scheme.js +1 -1
  295. package/components/toolbar/toolbar-group.yaml +1 -1
  296. package/components/toolbar/toolbar.css +17 -17
  297. package/components/toolbar/toolbar.js +1 -1
  298. package/components/tooltip/tooltip.css +2 -2
  299. package/components/tooltip/tooltip.js +1 -1
  300. package/components/tree/tree-item.yaml +1 -1
  301. package/components/tree/tree.css +37 -37
  302. package/components/tree/tree.js +1 -1
  303. package/components/upload/upload.css +49 -49
  304. package/components/upload/upload.js +1 -1
  305. package/dist/web-components.min.css +1 -1
  306. package/dist/web-components.min.js +146 -87
  307. package/package.json +3 -3
  308. package/styles/components.css +11 -0
  309. /package/components/accordion/{class.js → accordion.class.js} +0 -0
  310. /package/components/action-list/{class.js → action-list.class.js} +0 -0
  311. /package/components/agent-feedback-bar/{class.js → agent-feedback-bar.class.js} +0 -0
  312. /package/components/agent-questions/{class.js → agent-questions.class.js} +0 -0
  313. /package/components/agent-reasoning/{class.js → agent-reasoning.class.js} +0 -0
  314. /package/components/agent-suggestions/{class.js → agent-suggestions.class.js} +0 -0
  315. /package/components/avatar/{class.js → avatar.class.js} +0 -0
  316. /package/components/badge/{class.js → badge.class.js} +0 -0
  317. /package/components/block/{class.js → block.class.js} +0 -0
  318. /package/components/breadcrumb/{class.js → breadcrumb.class.js} +0 -0
  319. /package/components/button/{class.js → button.class.js} +0 -0
  320. /package/components/calendar-picker/{class.js → calendar-picker.class.js} +0 -0
  321. /package/components/card/{class.js → card.class.js} +0 -0
  322. /package/components/chart/{class.js → chart.class.js} +0 -0
  323. /package/components/chart-legend/{class.js → chart-legend.class.js} +0 -0
  324. /package/components/chat-thread/{class.js → chat-thread.class.js} +0 -0
  325. /package/components/check/{class.js → check.class.js} +0 -0
  326. /package/components/code/{class.js → code.class.js} +0 -0
  327. /package/components/col/{class.js → col.class.js} +0 -0
  328. /package/components/color-input/{class.js → color-input.class.js} +0 -0
  329. /package/components/color-picker/{class.js → color-picker.class.js} +0 -0
  330. /package/components/command/{class.js → command.class.js} +0 -0
  331. /package/components/demo-toggle/{class.js → demo-toggle.class.js} +0 -0
  332. /package/components/description-list/{class.js → description-list.class.js} +0 -0
  333. /package/components/divider/{class.js → divider.class.js} +0 -0
  334. /package/components/drawer/{class.js → drawer.class.js} +0 -0
  335. /package/components/embed/{class.js → embed.class.js} +0 -0
  336. /package/components/empty-state/{class.js → empty-state.class.js} +0 -0
  337. /package/components/feed/{class.js → feed.class.js} +0 -0
  338. /package/components/field/{class.js → field.class.js} +0 -0
  339. /package/components/fields/{class.js → fields.class.js} +0 -0
  340. /package/components/grid/{class.js → grid.class.js} +0 -0
  341. /package/components/heatmap/{class.js → heatmap.class.js} +0 -0
  342. /package/components/icon/{class.js → icon.class.js} +0 -0
  343. /package/components/image/{class.js → image.class.js} +0 -0
  344. /package/components/input/{class.js → input.class.js} +0 -0
  345. /package/components/inspector/{class.js → inspector.class.js} +0 -0
  346. /package/components/kbd/{class.js → kbd.class.js} +0 -0
  347. /package/components/link/{class.js → link.class.js} +0 -0
  348. /package/components/list/{class.js → list.class.js} +0 -0
  349. /package/components/menu/{class.js → menu.class.js} +0 -0
  350. /package/components/nav/{class.js → nav.class.js} +0 -0
  351. /package/components/nav-group/{class.js → nav-group.class.js} +0 -0
  352. /package/components/nav-item/{class.js → nav-item.class.js} +0 -0
  353. /package/components/noodles/{class.js → noodles.class.js} +0 -0
  354. /package/components/option-card/{class.js → option-card.class.js} +0 -0
  355. /package/components/otp-input/{class.js → otp-input.class.js} +0 -0
  356. /package/components/page/{class.js → page.class.js} +0 -0
  357. /package/components/pagination/{class.js → pagination.class.js} +0 -0
  358. /package/components/pane/{class.js → pane.class.js} +0 -0
  359. /package/components/pipeline-status/{class.js → pipeline-status.class.js} +0 -0
  360. /package/components/popover/{class.js → popover.class.js} +0 -0
  361. /package/components/progress/{class.js → progress.class.js} +0 -0
  362. /package/components/progress-row/{class.js → progress-row.class.js} +0 -0
  363. /package/components/radio/{class.js → radio.class.js} +0 -0
  364. /package/components/range/{class.js → range.class.js} +0 -0
  365. /package/components/rating/{class.js → rating.class.js} +0 -0
  366. /package/components/richtext/{class.js → richtext.class.js} +0 -0
  367. /package/components/row/{class.js → row.class.js} +0 -0
  368. /package/components/search/{class.js → search.class.js} +0 -0
  369. /package/components/segment/{class.js → segment.class.js} +0 -0
  370. /package/components/segmented/{class.js → segmented.class.js} +0 -0
  371. /package/components/skeleton/{class.js → skeleton.class.js} +0 -0
  372. /package/components/slider/{class.js → slider.class.js} +0 -0
  373. /package/components/stack/{class.js → stack.class.js} +0 -0
  374. /package/components/step-progress/{class.js → step-progress.class.js} +0 -0
  375. /package/components/stepper/{class.js → stepper.class.js} +0 -0
  376. /package/components/stream/{class.js → stream.class.js} +0 -0
  377. /package/components/swatch/{class.js → swatch.class.js} +0 -0
  378. /package/components/swiper/{class.js → swiper.class.js} +0 -0
  379. /package/components/switch/{class.js → switch.class.js} +0 -0
  380. /package/components/table/{class.js → table.class.js} +0 -0
  381. /package/components/tabs/{class.js → tabs.class.js} +0 -0
  382. /package/components/tag/{class.js → tag.class.js} +0 -0
  383. /package/components/text/{class.js → text.class.js} +0 -0
  384. /package/components/textarea/{class.js → textarea.class.js} +0 -0
  385. /package/components/toast/{class.js → toast.class.js} +0 -0
  386. /package/components/toggle-group/{class.js → toggle-group.class.js} +0 -0
  387. /package/components/toggle-scheme/{class.js → toggle-scheme.class.js} +0 -0
  388. /package/components/toolbar/{class.js → toolbar.class.js} +0 -0
  389. /package/components/tooltip/{class.js → tooltip.class.js} +0 -0
  390. /package/components/tree/{class.js → tree.class.js} +0 -0
  391. /package/components/upload/{class.js → upload.class.js} +0 -0
@@ -23,6 +23,16 @@
23
23
  "type": "string",
24
24
  "default": ""
25
25
  },
26
+ "amount": {
27
+ "description": "Dunning mode only — the unpaid amount as a decimal major-unit number (`24.50`, not minor units like `2450`). Formatted via `Intl.NumberFormat` using [currency] and [lang]. Ignored when [pattern] is not `dunning`.",
28
+ "type": "number",
29
+ "default": 0
30
+ },
31
+ "cardLast4": {
32
+ "description": "Dunning mode only — last 4 digits of the declined card. Rendered as \"ending 4242\". Ignored when [pattern] is not `dunning`. Attribute spelling: `card-last4`.",
33
+ "type": "string",
34
+ "default": ""
35
+ },
26
36
  "closable": {
27
37
  "description": "Whether a close button is displayed. Alias [dismissible] is also accepted (same semantics, different spelling — the corpus and many libraries use both; both map to the same state).",
28
38
  "type": "boolean",
@@ -31,16 +41,47 @@
31
41
  "component": {
32
42
  "const": "Alert"
33
43
  },
44
+ "currency": {
45
+ "description": "Dunning mode only — ISO 4217 currency code driving the `Intl.NumberFormat` style=\"currency\" rendering. Defaults to `USD`.",
46
+ "type": "string",
47
+ "default": "USD"
48
+ },
34
49
  "dismissible": {
35
50
  "description": "Public alias for [closable] — same semantics. Both attributes render the close button. Use whichever spelling matches your authoring style.",
36
51
  "type": "boolean",
37
52
  "default": false
38
53
  },
54
+ "dueAt": {
55
+ "description": "Dunning mode only — ISO 8601 due / failed-at timestamp. Formatted via `Intl.DateTimeFormat`. Ignored when [pattern] is not `dunning`. Attribute spelling: `due-at`.",
56
+ "type": "string",
57
+ "default": ""
58
+ },
39
59
  "icon": {
40
60
  "description": "Icon identifier displayed before the message content",
41
61
  "type": "string",
42
62
  "default": ""
43
63
  },
64
+ "pattern": {
65
+ "description": "Domain pattern mode. Default is empty (standard alert). Setting `pattern=\"dunning\"` switches to billing dunning render mode — stamps a formatted amount + due-date + decline-reason from props and re-dispatches descendant `[data-dunning-action]` button clicks as a unified `dunning-action` event. Spec: SPEC-006.",
66
+ "type": "string",
67
+ "enum": [
68
+ "",
69
+ "dunning"
70
+ ],
71
+ "default": ""
72
+ },
73
+ "reason": {
74
+ "description": "Dunning mode only — decline reason. Drives the leading icon (declined → `x-circle`, expired → `clock`, insufficient → `wallet`, network → `wifi-slash`). One of: `declined`, `expired`, `insufficient`, `network`, or empty.",
75
+ "type": "string",
76
+ "enum": [
77
+ "",
78
+ "declined",
79
+ "expired",
80
+ "insufficient",
81
+ "network"
82
+ ],
83
+ "default": ""
84
+ },
44
85
  "text": {
45
86
  "description": "Single-line alert message. For two-line \"headline + body\" alerts, use [title] + [description] instead. For rich content (links, formatting), use the [slot=\"content\"] slot.",
46
87
  "type": "string",
@@ -66,12 +107,26 @@
66
107
  ],
67
108
  "unevaluatedProperties": false,
68
109
  "x-adiaui": {
69
- "anti_patterns": [],
110
+ "anti_patterns": [
111
+ {
112
+ "fix": "<alert-ui pattern=\"dunning\" variant=\"danger\">",
113
+ "why": "Dunning is always a failure surface; info mis-signals severity.",
114
+ "wrong": "<alert-ui pattern=\"dunning\" variant=\"info\">"
115
+ },
116
+ {
117
+ "fix": "<alert-ui pattern=\"dunning\" amount=\"24.50\" currency=\"USD\" due-at=\"2026-05-20\">",
118
+ "why": "Amount baked into title defeats Intl formatting + telemetry.",
119
+ "wrong": "<alert-ui pattern=\"dunning\" title=\"Payment failed $24.50\">"
120
+ }
121
+ ],
70
122
  "category": "container",
71
123
  "composes": [],
72
124
  "events": {
73
125
  "close": {
74
126
  "description": "Fired when the close button is clicked"
127
+ },
128
+ "dunning-action": {
129
+ "description": "Dunning pattern only — fired when a descendant button with `[data-dunning-action]` is clicked. Detail shape `{ action, amount, currency, dueAt }`; the action string is the value of the `data-dunning-action` attribute (typically `\"update\"` or `\"retry\"`)."
75
130
  }
76
131
  },
77
132
  "examples": [
@@ -84,6 +139,11 @@
84
139
  "description": "Card with error alert and a retry button for error state display.",
85
140
  "a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"Card\",\n \"children\": [\n \"sec\",\n \"ftr\"\n ]\n },\n {\n \"id\": \"sec\",\n \"component\": \"Section\",\n \"children\": [\n \"alert\"\n ]\n },\n {\n \"id\": \"alert\",\n \"component\": \"Alert\",\n \"variant\": \"error\",\n \"title\": \"Something went wrong\",\n \"description\": \"We encountered an error while loading the data. Please try again.\"\n },\n {\n \"id\": \"ftr\",\n \"component\": \"Footer\",\n \"children\": [\n \"retry\"\n ]\n },\n {\n \"id\": \"retry\",\n \"component\": \"Button\",\n \"text\": \"Retry\",\n \"icon\": \"refresh\",\n \"variant\": \"primary\"\n }\n]",
86
141
  "name": "error-state"
142
+ },
143
+ {
144
+ "description": "Billing dunning banner — declined card with retry + update-card actions (SPEC-006).",
145
+ "a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"Alert\",\n \"pattern\": \"dunning\",\n \"variant\": \"danger\",\n \"amount\": 24.5,\n \"currency\": \"USD\",\n \"dueAt\": \"2026-05-20T00:00:00Z\",\n \"cardLast4\": \"4242\",\n \"reason\": \"declined\",\n \"children\": [\"btn-update\", \"btn-retry\"]\n },\n {\n \"id\": \"btn-update\",\n \"component\": \"Button\",\n \"text\": \"Update payment method\",\n \"variant\": \"primary\",\n \"slot\": \"actions\"\n },\n {\n \"id\": \"btn-retry\",\n \"component\": \"Button\",\n \"text\": \"Retry charge\",\n \"variant\": \"outline\",\n \"slot\": \"actions\"\n }\n]",
146
+ "name": "dunning-payment-failed"
87
147
  }
88
148
  ],
89
149
  "keywords": [
@@ -112,6 +172,9 @@
112
172
  "action": {
113
173
  "description": "Optional trailing action button (e.g. \"Refresh\", \"Status page\"). Right-aligned in the flex layout. See system-banners pattern for examples."
114
174
  },
175
+ "actions": {
176
+ "description": "Dunning pattern only — action button stack (typically two `<button-ui>` with `data-dunning-action=\"update\"` and `data-dunning-action=\"retry\"`). Sits below the formatted message in the col layout when `pattern=\"dunning\"`."
177
+ },
115
178
  "close": {
116
179
  "description": "Close button. Stamped automatically when `closable` is set; the stamped element is `<button-ui slot=\"close\" icon=\"x\" variant=\"ghost\" size=\"sm\">`. Override by passing a custom `slot=\"close\"` button."
117
180
  },
@@ -50,6 +50,17 @@ export class UIAlert extends UIElement {
50
50
  closable: { type: Boolean, default: false, reflect: true },
51
51
  dismissible: { type: Boolean, default: false, reflect: true },
52
52
  icon: { type: String, default: '', reflect: true },
53
+ /* SPEC-006 dunning pattern \u2014 billing payment-failed mode. When
54
+ `pattern="dunning"`, the alert stamps an Intl-formatted amount +
55
+ due-date + decline-reason from the props below and re-dispatches
56
+ descendant `[data-dunning-action]` button clicks as a unified
57
+ `dunning-action` event. See SPEC-006 for the full rationale. */
58
+ pattern: { type: String, default: '', reflect: true },
59
+ amount: { type: Number, default: 0, reflect: true },
60
+ currency: { type: String, default: 'USD', reflect: true },
61
+ dueAt: { type: String, default: '', reflect: true, attribute: 'due-at' },
62
+ cardLast4: { type: String, default: '', reflect: true, attribute: 'card-last4' },
63
+ reason: { type: String, default: '', reflect: true },
53
64
  };
54
65
 
55
66
  static parts = {
@@ -62,6 +73,15 @@ export class UIAlert extends UIElement {
62
73
  close: '<button-ui slot="close" icon="x" variant="ghost" size="sm" aria-label="Close"></button-ui>',
63
74
  };
64
75
 
76
+ /* SPEC-006 \u2014 decline-reason \u2192 leading icon map. Used when
77
+ `pattern="dunning"` and no explicit [icon] override is set. */
78
+ static #DUNNING_ICONS = {
79
+ declined: 'x-circle',
80
+ expired: 'clock',
81
+ insufficient: 'wallet',
82
+ network: 'wifi-slash',
83
+ };
84
+
65
85
  static template = () => null;
66
86
 
67
87
  /**
@@ -100,7 +120,28 @@ export class UIAlert extends UIElement {
100
120
  }
101
121
 
102
122
  #onPress = (e) => {
103
- if (e.target.closest('[slot="close"]')) this.#close();
123
+ if (e.target.closest('[slot="close"]')) {
124
+ this.#close();
125
+ return;
126
+ }
127
+ // SPEC-006 — dunning action delegation. Re-dispatch as a unified
128
+ // `dunning-action` event so downstream telemetry only listens once.
129
+ if (this.pattern === 'dunning') {
130
+ const trigger = e.target.closest('[data-dunning-action]');
131
+ if (trigger && this.contains(trigger)) {
132
+ const action = trigger.getAttribute('data-dunning-action') || '';
133
+ this.dispatchEvent(new CustomEvent('dunning-action', {
134
+ bubbles: true,
135
+ composed: true,
136
+ detail: {
137
+ action,
138
+ amount: this.amount,
139
+ currency: this.currency,
140
+ dueAt: this.dueAt,
141
+ },
142
+ }));
143
+ }
144
+ }
104
145
  };
105
146
 
106
147
  connected() {
@@ -108,7 +149,7 @@ export class UIAlert extends UIElement {
108
149
  this.#updateRole();
109
150
 
110
151
  // Stamp default DOM if nothing was provided
111
- if (this.icon) this.ensure('leading');
152
+ if (this.icon || (this.pattern === 'dunning' && this.reason)) this.ensure('leading');
112
153
  this.ensure('content');
113
154
  if (this.closable) this.ensure('close');
114
155
 
@@ -120,6 +161,15 @@ export class UIAlert extends UIElement {
120
161
  }
121
162
 
122
163
  render() {
164
+ // SPEC-006 — dunning pattern: branch into a separate render path
165
+ // that stamps Intl-formatted amount + due-date + reason into the
166
+ // content slot. The standard alert render path below is skipped.
167
+ if (this.pattern === 'dunning') {
168
+ this.#renderDunning();
169
+ this.#updateRole();
170
+ return;
171
+ }
172
+
123
173
  // Icon
124
174
  if (this.icon) {
125
175
  const leading = this.ensure('leading');
@@ -182,6 +232,143 @@ export class UIAlert extends UIElement {
182
232
  this.#updateRole();
183
233
  }
184
234
 
235
+ /**
236
+ * SPEC-006 — dunning render path.
237
+ *
238
+ * Stamps the content slot with a bolded title (per-reason copy) +
239
+ * a metadata line ("Card ending 4242 · declined May 20, 2026") +
240
+ * an Intl-formatted amount. The leading icon is selected from
241
+ * `#DUNNING_ICONS` keyed by the [reason] attribute (or [icon] if
242
+ * the consumer explicitly overrode).
243
+ *
244
+ * Re-runs on every render — formatting is stable + idempotent so a
245
+ * re-stamp produces the same DOM. We rebuild the content subtree
246
+ * each render to keep the auto-stamp markers from drifting between
247
+ * the dunning path and the standard path if [pattern] is toggled
248
+ * at runtime.
249
+ */
250
+ #renderDunning() {
251
+ // Leading icon — explicit [icon] beats the reason map
252
+ const iconName = this.icon || UIAlert.#DUNNING_ICONS[this.reason] || 'x-circle';
253
+ const leading = this.ensure('leading');
254
+ if (leading) leading.setAttribute('name', iconName);
255
+
256
+ // Content
257
+ const content = this.ensure('content');
258
+ if (!content) return;
259
+ content.setAttribute('data-alert-auto', 'dunning');
260
+ content.replaceChildren();
261
+
262
+ // Title line — explicit [title] beats the per-reason default
263
+ const titleText = this.title || this.#defaultDunningTitle();
264
+ if (titleText) {
265
+ const strong = document.createElement('strong');
266
+ strong.setAttribute('data-dunning-title', '');
267
+ strong.textContent = titleText;
268
+ content.appendChild(strong);
269
+ }
270
+
271
+ // Body line — formatted amount + metadata. We put amount first
272
+ // (it's the most-scanned datum) followed by the meta string.
273
+ const amountText = this.#formatAmount();
274
+ const metaText = this.#dunningMeta();
275
+ if (amountText || metaText) {
276
+ content.appendChild(document.createTextNode(' '));
277
+ if (amountText) {
278
+ const amt = document.createElement('span');
279
+ amt.setAttribute('data-dunning-amount', '');
280
+ amt.textContent = amountText;
281
+ content.appendChild(amt);
282
+ }
283
+ if (amountText && metaText) {
284
+ content.appendChild(document.createTextNode(' '));
285
+ }
286
+ if (metaText) {
287
+ const meta = document.createElement('span');
288
+ meta.setAttribute('data-dunning-meta', '');
289
+ meta.textContent = metaText;
290
+ content.appendChild(meta);
291
+ }
292
+ }
293
+
294
+ // Composed accessible name — title + amount + meta in DOM order
295
+ const aria = [titleText, amountText, metaText].filter(Boolean).join('. ');
296
+ if (aria) this.setAttribute('aria-label', aria);
297
+
298
+ // Close button — dismissible-on-dunning is advisory-not-blocked
299
+ // (SPEC-006 OD-002 lean A). Pass through.
300
+ if (this.closable || this.dismissible) {
301
+ this.ensure('close');
302
+ } else {
303
+ this.drop('close');
304
+ }
305
+ }
306
+
307
+ #defaultDunningTitle() {
308
+ switch (this.reason) {
309
+ case 'expired': return 'Card expired';
310
+ case 'insufficient': return 'Insufficient funds';
311
+ case 'network': return 'Network error';
312
+ case 'declined': return 'Payment failed';
313
+ default: return this.variant === 'warning' ? 'Payment due soon' : 'Payment failed';
314
+ }
315
+ }
316
+
317
+ #formatAmount() {
318
+ if (!this.amount && this.amount !== 0) return '';
319
+ if (this.amount === 0) return '';
320
+ try {
321
+ const lang = this.getAttribute('lang') || this.lang || undefined;
322
+ return new Intl.NumberFormat(lang, {
323
+ style: 'currency',
324
+ currency: this.currency || 'USD',
325
+ }).format(this.amount);
326
+ } catch {
327
+ // Fallback if Intl rejects the currency code
328
+ return `${this.currency || ''} ${this.amount}`.trim();
329
+ }
330
+ }
331
+
332
+ #formatDueDate() {
333
+ if (!this.dueAt) return '';
334
+ const d = new Date(this.dueAt);
335
+ if (Number.isNaN(d.getTime())) return '';
336
+ try {
337
+ const lang = this.getAttribute('lang') || this.lang || undefined;
338
+ return new Intl.DateTimeFormat(lang, {
339
+ year: 'numeric',
340
+ month: 'short',
341
+ day: 'numeric',
342
+ }).format(d);
343
+ } catch {
344
+ return d.toDateString();
345
+ }
346
+ }
347
+
348
+ #dunningMeta() {
349
+ const parts = [];
350
+ if (this.cardLast4) parts.push(`Card ending ${this.cardLast4}`);
351
+ if (this.reason && this.reason !== 'declined') {
352
+ parts.push(this.#reasonLabel());
353
+ }
354
+ const date = this.#formatDueDate();
355
+ if (date) {
356
+ const prefix = this.variant === 'warning' ? 'due' : 'failed';
357
+ parts.push(`${prefix} ${date}`);
358
+ }
359
+ return parts.join(' · ');
360
+ }
361
+
362
+ #reasonLabel() {
363
+ switch (this.reason) {
364
+ case 'expired': return 'expired';
365
+ case 'insufficient': return 'insufficient funds';
366
+ case 'network': return 'network error';
367
+ case 'declined': return 'declined';
368
+ default: return '';
369
+ }
370
+ }
371
+
185
372
  #updateRole() {
186
373
  const role = (this.variant === 'danger' || this.variant === 'warning') ? 'alert' : 'status';
187
374
  this.setAttribute('role', role);
@@ -1,20 +1,26 @@
1
1
  @scope (alert-ui) {
2
2
  :where(:scope) {
3
3
  /* ── Colors ── */
4
- --alert-bg: var(--a-bg-muted);
5
- --alert-fg: var(--a-fg);
6
- --alert-border: var(--a-border-subtle);
7
- --alert-icon-fg: var(--a-fg-muted);
4
+ --alert-bg-default: var(--a-bg-muted);
5
+ --alert-fg-default: var(--a-fg);
6
+ --alert-border-default: var(--a-border-subtle);
7
+ --alert-icon-fg-default: var(--a-fg-muted);
8
8
 
9
9
  /* ── Layout ── */
10
- --alert-radius: var(--a-radius-md);
11
- --alert-px: var(--a-space-3);
12
- --alert-py: var(--a-space-2);
13
- --alert-gap: var(--a-space-2);
10
+ --alert-radius-default: var(--a-radius-md);
11
+ --alert-px-default: var(--a-space-3);
12
+ --alert-py-default: var(--a-space-2);
13
+ --alert-gap-default: var(--a-space-2);
14
14
 
15
15
  /* ── Typography ── */
16
- --alert-font: var(--a-ui-size);
17
- --alert-line-height: var(--a-ui-line-height, 1.5);
16
+ --alert-font-default: var(--a-ui-size);
17
+ --alert-line-height-default: var(--a-ui-line-height, 1.5);
18
+
19
+ /* ── SPEC-006 dunning pattern tokens ── */
20
+ --alert-amount-font-default: var(--a-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
21
+ --alert-meta-fg-default: var(--a-fg-muted);
22
+ --alert-actions-gap-default: var(--a-space-2);
23
+ --alert-actions-mt-default: var(--a-space-2);
18
24
  }
19
25
 
20
26
  :scope {
@@ -26,43 +32,43 @@
26
32
  text on multi-line alerts. The leading slot's own height = body
27
33
  line-height (below) means the icon optically centers on line 1. */
28
34
  align-items: start;
29
- gap: var(--alert-gap);
30
- padding: var(--alert-py) var(--alert-px);
31
- border: 1px solid var(--alert-border);
32
- border-radius: var(--alert-radius);
33
- font-size: var(--alert-font);
34
- line-height: var(--alert-line-height);
35
- color: var(--alert-fg);
36
- background: var(--alert-bg);
35
+ gap: var(--alert-gap, var(--alert-gap-default));
36
+ padding: var(--alert-py, var(--alert-py-default)) var(--alert-px, var(--alert-px-default));
37
+ border: 1px solid var(--alert-border, var(--alert-border-default));
38
+ border-radius: var(--alert-radius, var(--alert-radius-default));
39
+ font-size: var(--alert-font, var(--alert-font-default));
40
+ line-height: var(--alert-line-height, var(--alert-line-height-default));
41
+ color: var(--alert-fg, var(--alert-fg-default));
42
+ background: var(--alert-bg, var(--alert-bg-default));
37
43
  }
38
44
 
39
45
  /* ── Variants ── */
40
46
  :scope[variant="info"] {
41
- --alert-bg: var(--a-info-muted);
42
- --alert-fg: var(--a-info-text);
43
- --alert-border: var(--a-info-border-subtle);
44
- --alert-icon-fg: var(--a-info-strong);
47
+ --alert-bg-default: var(--a-info-muted);
48
+ --alert-fg-default: var(--a-info-text);
49
+ --alert-border-default: var(--a-info-border-subtle);
50
+ --alert-icon-fg-default: var(--a-info-strong);
45
51
  }
46
52
 
47
53
  :scope[variant="success"] {
48
- --alert-bg: var(--a-success-muted);
49
- --alert-fg: var(--a-success-text);
50
- --alert-border: var(--a-success-border-subtle);
51
- --alert-icon-fg: var(--a-success-strong);
54
+ --alert-bg-default: var(--a-success-muted);
55
+ --alert-fg-default: var(--a-success-text);
56
+ --alert-border-default: var(--a-success-border-subtle);
57
+ --alert-icon-fg-default: var(--a-success-strong);
52
58
  }
53
59
 
54
60
  :scope[variant="warning"] {
55
- --alert-bg: var(--a-warning-muted);
56
- --alert-fg: var(--a-warning-text);
57
- --alert-border: var(--a-warning-border-subtle);
58
- --alert-icon-fg: var(--a-warning-strong);
61
+ --alert-bg-default: var(--a-warning-muted);
62
+ --alert-fg-default: var(--a-warning-text);
63
+ --alert-border-default: var(--a-warning-border-subtle);
64
+ --alert-icon-fg-default: var(--a-warning-strong);
59
65
  }
60
66
 
61
67
  :scope[variant="danger"] {
62
- --alert-bg: var(--a-danger-muted);
63
- --alert-fg: var(--a-danger-text);
64
- --alert-border: var(--a-danger-border-subtle);
65
- --alert-icon-fg: var(--a-danger-strong);
68
+ --alert-bg-default: var(--a-danger-muted);
69
+ --alert-fg-default: var(--a-danger-text);
70
+ --alert-border-default: var(--a-danger-border-subtle);
71
+ --alert-icon-fg-default: var(--a-danger-strong);
66
72
  }
67
73
 
68
74
  /* `muted` and `neutral` are semantic aliases of the base — same tokens
@@ -72,21 +78,21 @@
72
78
  than carrying tonal weight. */
73
79
  :scope[variant="muted"],
74
80
  :scope[variant="neutral"] {
75
- --alert-bg: var(--a-bg-muted);
76
- --alert-fg: var(--a-fg);
77
- --alert-border: var(--a-border-subtle);
78
- --alert-icon-fg: var(--a-fg-muted);
81
+ --alert-bg-default: var(--a-bg-muted);
82
+ --alert-fg-default: var(--a-fg);
83
+ --alert-border-default: var(--a-border-subtle);
84
+ --alert-icon-fg-default: var(--a-fg-muted);
79
85
  }
80
86
 
81
87
  /* ── Slots ── */
82
88
  :scope [slot="leading"] {
83
89
  flex-shrink: 0;
84
- color: var(--alert-icon-fg);
90
+ color: var(--alert-icon-fg, var(--alert-icon-fg-default));
85
91
  /* Box height = one line of body so the icon optical-centers on the
86
92
  first line under `align-items: start`. */
87
93
  display: inline-flex;
88
94
  align-items: center;
89
- min-height: calc(var(--alert-font) * var(--alert-line-height));
95
+ min-height: calc(var(--alert-font, var(--alert-font-default)) * var(--alert-line-height, var(--alert-line-height-default)));
90
96
  /* `ensure()` appends the leading-slot icon to the host, which
91
97
  puts it after any consumer-provided content in DOM order.
92
98
  Force it to the visual lead via flex `order` so the icon
@@ -113,6 +119,78 @@
113
119
  sit on the same first-line baseline. */
114
120
  :scope [slot="close"] {
115
121
  flex-shrink: 0;
116
- min-height: calc(var(--alert-font) * var(--alert-line-height));
122
+ min-height: calc(var(--alert-font, var(--alert-font-default)) * var(--alert-line-height, var(--alert-line-height-default)));
123
+ }
124
+
125
+ /* ──────────────────────────────────────────────────────────────
126
+ SPEC-006 — Dunning / Payment-Failed pattern
127
+ ──────────────────────────────────────────────────────────────
128
+ `pattern="dunning"` switches the alert into a billing notice
129
+ layout. The default alert is a single-row flex container; the
130
+ dunning mode keeps the leading icon and close button in a
131
+ "header" row but stacks the content + actions vertically below.
132
+
133
+ We achieve this by switching the host to `flex-wrap: wrap` and
134
+ assigning `order` per slot. The standard alert children authored
135
+ in arbitrary DOM order (buttons before content is the common
136
+ consumer mistake) are visually re-ordered by these rules so the
137
+ final layout always reads icon → content → actions → close. */
138
+
139
+ :scope[pattern="dunning"] {
140
+ flex-wrap: wrap;
141
+ row-gap: var(--alert-actions-mt, var(--alert-actions-mt-default));
142
+ /* Use align-items: flex-start so the leading icon hugs the top
143
+ of the message regardless of how tall the content + actions
144
+ column grows. */
145
+ align-items: flex-start;
146
+ }
147
+
148
+ /* Visual order — icon first, then content, then actions, then
149
+ close. Source DOM order is irrelevant under these rules. The
150
+ `order` integers leave room (10/20/30/40) for future expansion
151
+ without renumbering. */
152
+ :scope[pattern="dunning"] [slot="leading"] { order: 10; }
153
+ :scope[pattern="dunning"] [slot="content"] { order: 20; }
154
+ :scope[pattern="dunning"] [slot="actions"] { order: 30; }
155
+ :scope[pattern="dunning"] [slot="close"] { order: 40; }
156
+
157
+ :scope[pattern="dunning"] [data-dunning-amount] {
158
+ font-family: var(--alert-amount-font, var(--alert-amount-font-default));
159
+ font-variant-numeric: tabular-nums;
160
+ font-weight: 600;
161
+ /* Inherit color from the variant; tabular-nums + monospace make
162
+ the amount the scannable focal datum. */
163
+ }
164
+
165
+ :scope[pattern="dunning"] [data-dunning-meta] {
166
+ color: var(--alert-meta-fg, var(--alert-meta-fg-default));
167
+ font-size: calc(var(--alert-font, var(--alert-font-default)) * 0.9375);
168
+ }
169
+
170
+ :scope[pattern="dunning"] [data-dunning-title] {
171
+ display: inline-block;
172
+ margin-inline-end: var(--a-space-1, 0.25rem);
173
+ }
174
+
175
+ /* Actions slot — sits below the content as a row of buttons.
176
+ `flex-basis: 100%` combined with the `order: 30` rule pushes
177
+ the actions onto their own row after the content row. The
178
+ padding-inline-start indents the buttons under the message
179
+ column (so they left-align with the body text, not the leading
180
+ icon). */
181
+ :scope[pattern="dunning"] [slot="actions"] {
182
+ flex-basis: 100%;
183
+ display: flex;
184
+ flex-wrap: wrap;
185
+ gap: var(--alert-actions-gap, var(--alert-actions-gap-default));
186
+ padding-inline-start: calc(var(--alert-font, var(--alert-font-default)) + var(--alert-gap, var(--alert-gap-default)));
187
+ }
188
+
189
+ /* Lenient — when a bare button-ui carries data-dunning-action but
190
+ no explicit slot=actions, give it the same visual order as the
191
+ actions group so authoring oversights don't break layout. */
192
+ :scope[pattern="dunning"] > [data-dunning-action]:not([slot]) {
193
+ order: 30;
194
+ flex-basis: 100%;
117
195
  }
118
196
  }
@@ -21,18 +21,31 @@ inline upgrade prompts.
21
21
  import { UIElement } from '../../core/element.js';
22
22
 
23
23
  export type AlertCloseEvent = CustomEvent<unknown>;
24
+ export type AlertDunningActionEvent = CustomEvent<unknown>;
24
25
 
25
26
  export class UIAlert extends UIElement {
26
27
  /** Bold headline rendered as the first line of the alert content. Pair with [description] for the canonical "banner" pattern (headline + body). When [title] or [description] is set, the [text] prop is ignored. */
27
28
  title: string;
28
29
  /** Body text rendered as the second line of the alert content, below [title]. May be used alone (without [title]) for a single muted-body message. */
29
30
  description: string;
31
+ /** Dunning mode only — the unpaid amount as a decimal major-unit number (`24.50`, not minor units like `2450`). Formatted via `Intl.NumberFormat` using [currency] and [lang]. Ignored when [pattern] is not `dunning`. */
32
+ amount: number;
33
+ /** Dunning mode only — last 4 digits of the declined card. Rendered as "ending 4242". Ignored when [pattern] is not `dunning`. Attribute spelling: `card-last4`. */
34
+ cardLast4: string;
30
35
  /** Whether a close button is displayed. Alias [dismissible] is also accepted (same semantics, different spelling — the corpus and many libraries use both; both map to the same state). */
31
36
  closable: boolean;
37
+ /** Dunning mode only — ISO 4217 currency code driving the `Intl.NumberFormat` style="currency" rendering. Defaults to `USD`. */
38
+ currency: string;
32
39
  /** Public alias for [closable] — same semantics. Both attributes render the close button. Use whichever spelling matches your authoring style. */
33
40
  dismissible: boolean;
41
+ /** Dunning mode only — ISO 8601 due / failed-at timestamp. Formatted via `Intl.DateTimeFormat`. Ignored when [pattern] is not `dunning`. Attribute spelling: `due-at`. */
42
+ dueAt: string;
34
43
  /** Icon identifier displayed before the message content */
35
44
  icon: string;
45
+ /** Domain pattern mode. Default is empty (standard alert). Setting `pattern="dunning"` switches to billing dunning render mode — stamps a formatted amount + due-date + decline-reason from props and re-dispatches descendant `[data-dunning-action]` button clicks as a unified `dunning-action` event. Spec: SPEC-006. */
46
+ pattern: '' | 'dunning';
47
+ /** Dunning mode only — decline reason. Drives the leading icon (declined → `x-circle`, expired → `clock`, insufficient → `wallet`, network → `wifi-slash`). One of: `declined`, `expired`, `insufficient`, `network`, or empty. */
48
+ reason: '' | 'declined' | 'expired' | 'insufficient' | 'network';
36
49
  /** Single-line alert message. For two-line "headline + body" alerts, use [title] + [description] instead. For rich content (links, formatting), use the [slot="content"] slot. */
37
50
  text: string;
38
51
  /** Semantic color variant. */
@@ -44,4 +57,5 @@ export class UIAlert extends UIElement {
44
57
  options?: boolean | AddEventListenerOptions,
45
58
  ): void;
46
59
  addEventListener(type: 'close', listener: (ev: AlertCloseEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
60
+ addEventListener(type: 'dunning-action', listener: (ev: AlertDunningActionEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
47
61
  }
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  import { defineIfFree } from '../../core/register.js';
13
- import { UIAlert } from './class.js';
13
+ import { UIAlert } from './alert.class.js';
14
14
 
15
15
  defineIfFree('alert-ui', UIAlert);
16
16