@adia-ai/web-components 0.6.34 → 0.6.36

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 (280) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/color/index.js +1 -1
  3. package/components/accordion/accordion-item.yaml +2 -2
  4. package/components/accordion/accordion.js +1 -1
  5. package/components/action-list/action-item.yaml +2 -2
  6. package/components/action-list/action-list.js +1 -1
  7. package/components/agent-artifact/{class.js → agent-artifact.class.js} +1 -1
  8. package/components/agent-artifact/agent-artifact.js +1 -1
  9. package/components/agent-feedback-bar/agent-feedback-bar.js +1 -1
  10. package/components/agent-questions/agent-questions.js +1 -1
  11. package/components/agent-reasoning/agent-reasoning.js +1 -1
  12. package/components/agent-suggestions/agent-suggestions.js +1 -1
  13. package/components/alert/alert.a2ui.json +64 -1
  14. package/components/alert/{class.js → alert.class.js} +189 -2
  15. package/components/alert/alert.css +78 -0
  16. package/components/alert/alert.d.ts +14 -0
  17. package/components/alert/alert.js +1 -1
  18. package/components/alert/alert.test.js +184 -0
  19. package/components/alert/alert.yaml +114 -1
  20. package/components/avatar/avatar-group.yaml +2 -2
  21. package/components/avatar/avatar.js +1 -1
  22. package/components/badge/badge.js +1 -1
  23. package/components/block/block.js +1 -1
  24. package/components/breadcrumb/breadcrumb.js +1 -1
  25. package/components/button/button.js +1 -1
  26. package/components/calendar-grid/calendar-grid.a2ui.json +10 -0
  27. package/components/calendar-grid/{class.js → calendar-grid.class.js} +30 -4
  28. package/components/calendar-grid/calendar-grid.css +20 -0
  29. package/components/calendar-grid/calendar-grid.d.ts +4 -0
  30. package/components/calendar-grid/calendar-grid.js +1 -1
  31. package/components/calendar-grid/calendar-grid.yaml +20 -0
  32. package/components/calendar-picker/calendar-picker.js +1 -1
  33. package/components/card/card.js +1 -1
  34. package/components/chart/chart.js +1 -1
  35. package/components/chart-legend/chart-legend.js +1 -1
  36. package/components/chat-thread/chat-input.a2ui.json +1 -1
  37. package/components/chat-thread/chat-input.js +6 -1
  38. package/components/chat-thread/chat-input.yaml +4 -1
  39. package/components/chat-thread/chat-thread.js +1 -1
  40. package/components/check/check.js +1 -1
  41. package/components/code/code.js +1 -1
  42. package/components/col/col.js +1 -1
  43. package/components/color-input/color-input.js +1 -1
  44. package/components/color-picker/color-picker.js +1 -1
  45. package/components/combobox/combobox.css +12 -0
  46. package/components/combobox/combobox.js +1 -1
  47. package/components/command/command.js +1 -1
  48. package/components/date-range-picker/{class.js → date-range-picker.class.js} +19 -3
  49. package/components/date-range-picker/date-range-picker.css +55 -6
  50. package/components/date-range-picker/date-range-picker.js +1 -1
  51. package/components/datetime-picker/{class.js → datetime-picker.class.js} +1 -1
  52. package/components/datetime-picker/datetime-picker.css +7 -1
  53. package/components/datetime-picker/datetime-picker.js +1 -1
  54. package/components/demo-toggle/demo-toggle.js +1 -1
  55. package/components/description-list/description-list.js +1 -1
  56. package/components/divider/divider.js +1 -1
  57. package/components/drawer/drawer.js +1 -1
  58. package/components/embed/embed.js +1 -1
  59. package/components/empty-state/empty-state.js +1 -1
  60. package/components/feed/feed.js +1 -1
  61. package/components/field/field.js +1 -1
  62. package/components/field/field.test.js +1 -1
  63. package/components/fields/fields.js +1 -1
  64. package/components/grid/grid.js +1 -1
  65. package/components/heatmap/heatmap.js +1 -1
  66. package/components/icon/icon.js +1 -1
  67. package/components/image/image.js +1 -1
  68. package/components/index.js +3 -0
  69. package/components/inline-message/inline-message.a2ui.json +143 -0
  70. package/components/inline-message/inline-message.class.js +169 -0
  71. package/components/inline-message/inline-message.css +75 -0
  72. package/components/inline-message/inline-message.d.ts +31 -0
  73. package/components/inline-message/inline-message.examples.md +19 -0
  74. package/components/inline-message/inline-message.js +17 -0
  75. package/components/inline-message/inline-message.test.js +203 -0
  76. package/components/inline-message/inline-message.yaml +205 -0
  77. package/components/input/input.css +16 -2
  78. package/components/input/input.js +1 -1
  79. package/components/input/input.test.js +40 -0
  80. package/components/input/input.yaml +5 -4
  81. package/components/inspector/inspector.js +1 -1
  82. package/components/integration-card/integration-card.js +1 -1
  83. package/components/kbd/kbd.js +1 -1
  84. package/components/link/link.js +1 -1
  85. package/components/list/list-item.yaml +2 -2
  86. package/components/list/list.js +1 -1
  87. package/components/list-window/list-window.js +1 -1
  88. package/components/loading-overlay/loading-overlay.a2ui.json +176 -0
  89. package/components/loading-overlay/loading-overlay.class.js +203 -0
  90. package/components/loading-overlay/loading-overlay.css +81 -0
  91. package/components/loading-overlay/loading-overlay.d.ts +24 -0
  92. package/components/loading-overlay/loading-overlay.examples.md +50 -0
  93. package/components/loading-overlay/loading-overlay.js +17 -0
  94. package/components/loading-overlay/loading-overlay.test.js +257 -0
  95. package/components/loading-overlay/loading-overlay.yaml +260 -0
  96. package/components/menu/menu-divider.yaml +1 -1
  97. package/components/menu/menu-item.yaml +1 -1
  98. package/components/menu/menu.a2ui.json +3 -0
  99. package/components/menu/menu.js +1 -1
  100. package/components/menu/menu.yaml +7 -0
  101. package/components/modal/{class.js → modal.class.js} +12 -1
  102. package/components/modal/modal.css +11 -1
  103. package/components/modal/modal.js +1 -1
  104. package/components/nav/nav.js +1 -1
  105. package/components/nav-group/nav-group.js +1 -1
  106. package/components/nav-item/nav-item.js +1 -1
  107. package/components/noodles/noodles.js +1 -1
  108. package/components/option-card/option-card.js +1 -1
  109. package/components/otp-input/otp-input.js +1 -1
  110. package/components/page/page.js +1 -1
  111. package/components/pagination/pagination.js +1 -1
  112. package/components/pane/pane.js +1 -1
  113. package/components/pipeline-status/pipeline-status.js +1 -1
  114. package/components/popover/popover.a2ui.json +8 -1
  115. package/components/popover/popover.js +1 -1
  116. package/components/popover/popover.yaml +14 -1
  117. package/components/progress/progress.js +1 -1
  118. package/components/progress-row/progress-row.js +1 -1
  119. package/components/radio/radio.js +1 -1
  120. package/components/range/range.js +1 -1
  121. package/components/rating/rating.js +1 -1
  122. package/components/richtext/richtext.js +1 -1
  123. package/components/row/row.js +1 -1
  124. package/components/search/{class.js → search.class.js} +2 -0
  125. package/components/search/search.js +1 -1
  126. package/components/segment/segment.js +1 -1
  127. package/components/segmented/segmented.js +1 -1
  128. package/components/select/select.a2ui.json +58 -4
  129. package/components/select/{class.js → select.class.js} +415 -6
  130. package/components/select/select.css +158 -0
  131. package/components/select/select.d.ts +31 -1
  132. package/components/select/select.js +1 -1
  133. package/components/select/select.test.js +202 -0
  134. package/components/select/select.yaml +126 -5
  135. package/components/skeleton/skeleton.js +1 -1
  136. package/components/slider/slider.js +1 -1
  137. package/components/spinner/spinner.a2ui.json +3 -2
  138. package/components/spinner/{class.js → spinner.class.js} +33 -3
  139. package/components/spinner/spinner.css +91 -35
  140. package/components/spinner/spinner.d.ts +2 -2
  141. package/components/spinner/spinner.js +1 -1
  142. package/components/spinner/spinner.test.js +49 -11
  143. package/components/spinner/spinner.yaml +9 -1
  144. package/components/stack/stack.js +1 -1
  145. package/components/step-progress/step-progress.js +1 -1
  146. package/components/stepper/stepper-item.yaml +1 -1
  147. package/components/stepper/stepper.js +1 -1
  148. package/components/stream/stream.js +1 -1
  149. package/components/swatch/swatch.js +1 -1
  150. package/components/swiper/swiper.js +1 -1
  151. package/components/switch/switch.js +1 -1
  152. package/components/table/table.css +1 -1
  153. package/components/table/table.js +1 -1
  154. package/components/table-toolbar/{class.js → table-toolbar.class.js} +2 -1
  155. package/components/table-toolbar/table-toolbar.js +1 -1
  156. package/components/tabs/tab.yaml +2 -2
  157. package/components/tabs/tabs.js +1 -1
  158. package/components/tag/tag.a2ui.json +9 -0
  159. package/components/tag/{class.js → tag.class.js} +8 -1
  160. package/components/tag/tag.css +84 -20
  161. package/components/tag/tag.js +1 -1
  162. package/components/tag/tag.test.js +75 -1
  163. package/components/tag/tag.yaml +14 -0
  164. package/components/tags-input/tags-input.a2ui.json +337 -0
  165. package/components/tags-input/tags-input.class.js +783 -0
  166. package/components/tags-input/tags-input.css +210 -0
  167. package/components/tags-input/tags-input.d.ts +120 -0
  168. package/components/tags-input/tags-input.examples.md +92 -0
  169. package/components/tags-input/tags-input.js +17 -0
  170. package/components/tags-input/tags-input.test.js +368 -0
  171. package/components/tags-input/tags-input.yaml +367 -0
  172. package/components/text/text.js +1 -1
  173. package/components/textarea/textarea.a2ui.json +1 -1
  174. package/components/textarea/textarea.css +10 -1
  175. package/components/textarea/textarea.js +1 -1
  176. package/components/textarea/textarea.yaml +11 -8
  177. package/components/time-picker/time-picker.js +1 -1
  178. package/components/timeline/timeline-item.yaml +2 -2
  179. package/components/timeline/{class.js → timeline.class.js} +1 -1
  180. package/components/timeline/timeline.js +1 -1
  181. package/components/toast/toast.js +1 -1
  182. package/components/toggle-group/toggle-group.js +1 -1
  183. package/components/toggle-group/toggle-option.yaml +1 -1
  184. package/components/toggle-scheme/toggle-scheme.js +1 -1
  185. package/components/toolbar/toolbar-group.yaml +1 -1
  186. package/components/toolbar/toolbar.js +1 -1
  187. package/components/tooltip/tooltip.js +1 -1
  188. package/components/tree/tree-item.yaml +1 -1
  189. package/components/tree/tree.js +1 -1
  190. package/components/upload/upload.js +1 -1
  191. package/core/provider.js +19 -2
  192. package/dist/web-components.min.css +1 -1
  193. package/dist/web-components.min.js +112 -90
  194. package/package.json +3 -3
  195. package/styles/components.css +3 -0
  196. /package/components/accordion/{class.js → accordion.class.js} +0 -0
  197. /package/components/action-list/{class.js → action-list.class.js} +0 -0
  198. /package/components/agent-feedback-bar/{class.js → agent-feedback-bar.class.js} +0 -0
  199. /package/components/agent-questions/{class.js → agent-questions.class.js} +0 -0
  200. /package/components/agent-reasoning/{class.js → agent-reasoning.class.js} +0 -0
  201. /package/components/agent-suggestions/{class.js → agent-suggestions.class.js} +0 -0
  202. /package/components/avatar/{class.js → avatar.class.js} +0 -0
  203. /package/components/badge/{class.js → badge.class.js} +0 -0
  204. /package/components/block/{class.js → block.class.js} +0 -0
  205. /package/components/breadcrumb/{class.js → breadcrumb.class.js} +0 -0
  206. /package/components/button/{class.js → button.class.js} +0 -0
  207. /package/components/calendar-picker/{class.js → calendar-picker.class.js} +0 -0
  208. /package/components/card/{class.js → card.class.js} +0 -0
  209. /package/components/chart/{class.js → chart.class.js} +0 -0
  210. /package/components/chart-legend/{class.js → chart-legend.class.js} +0 -0
  211. /package/components/chat-thread/{class.js → chat-thread.class.js} +0 -0
  212. /package/components/check/{class.js → check.class.js} +0 -0
  213. /package/components/code/{class.js → code.class.js} +0 -0
  214. /package/components/col/{class.js → col.class.js} +0 -0
  215. /package/components/color-input/{class.js → color-input.class.js} +0 -0
  216. /package/components/color-picker/{class.js → color-picker.class.js} +0 -0
  217. /package/components/combobox/{class.js → combobox.class.js} +0 -0
  218. /package/components/command/{class.js → command.class.js} +0 -0
  219. /package/components/demo-toggle/{class.js → demo-toggle.class.js} +0 -0
  220. /package/components/description-list/{class.js → description-list.class.js} +0 -0
  221. /package/components/divider/{class.js → divider.class.js} +0 -0
  222. /package/components/drawer/{class.js → drawer.class.js} +0 -0
  223. /package/components/embed/{class.js → embed.class.js} +0 -0
  224. /package/components/empty-state/{class.js → empty-state.class.js} +0 -0
  225. /package/components/feed/{class.js → feed.class.js} +0 -0
  226. /package/components/field/{class.js → field.class.js} +0 -0
  227. /package/components/fields/{class.js → fields.class.js} +0 -0
  228. /package/components/grid/{class.js → grid.class.js} +0 -0
  229. /package/components/heatmap/{class.js → heatmap.class.js} +0 -0
  230. /package/components/icon/{class.js → icon.class.js} +0 -0
  231. /package/components/image/{class.js → image.class.js} +0 -0
  232. /package/components/input/{class.js → input.class.js} +0 -0
  233. /package/components/inspector/{class.js → inspector.class.js} +0 -0
  234. /package/components/integration-card/{class.js → integration-card.class.js} +0 -0
  235. /package/components/kbd/{class.js → kbd.class.js} +0 -0
  236. /package/components/link/{class.js → link.class.js} +0 -0
  237. /package/components/list/{class.js → list.class.js} +0 -0
  238. /package/components/list-window/{class.js → list-window.class.js} +0 -0
  239. /package/components/menu/{class.js → menu.class.js} +0 -0
  240. /package/components/nav/{class.js → nav.class.js} +0 -0
  241. /package/components/nav-group/{class.js → nav-group.class.js} +0 -0
  242. /package/components/nav-item/{class.js → nav-item.class.js} +0 -0
  243. /package/components/noodles/{class.js → noodles.class.js} +0 -0
  244. /package/components/option-card/{class.js → option-card.class.js} +0 -0
  245. /package/components/otp-input/{class.js → otp-input.class.js} +0 -0
  246. /package/components/page/{class.js → page.class.js} +0 -0
  247. /package/components/pagination/{class.js → pagination.class.js} +0 -0
  248. /package/components/pane/{class.js → pane.class.js} +0 -0
  249. /package/components/pipeline-status/{class.js → pipeline-status.class.js} +0 -0
  250. /package/components/popover/{class.js → popover.class.js} +0 -0
  251. /package/components/progress/{class.js → progress.class.js} +0 -0
  252. /package/components/progress-row/{class.js → progress-row.class.js} +0 -0
  253. /package/components/radio/{class.js → radio.class.js} +0 -0
  254. /package/components/range/{class.js → range.class.js} +0 -0
  255. /package/components/rating/{class.js → rating.class.js} +0 -0
  256. /package/components/richtext/{class.js → richtext.class.js} +0 -0
  257. /package/components/row/{class.js → row.class.js} +0 -0
  258. /package/components/segment/{class.js → segment.class.js} +0 -0
  259. /package/components/segmented/{class.js → segmented.class.js} +0 -0
  260. /package/components/skeleton/{class.js → skeleton.class.js} +0 -0
  261. /package/components/slider/{class.js → slider.class.js} +0 -0
  262. /package/components/stack/{class.js → stack.class.js} +0 -0
  263. /package/components/step-progress/{class.js → step-progress.class.js} +0 -0
  264. /package/components/stepper/{class.js → stepper.class.js} +0 -0
  265. /package/components/stream/{class.js → stream.class.js} +0 -0
  266. /package/components/swatch/{class.js → swatch.class.js} +0 -0
  267. /package/components/swiper/{class.js → swiper.class.js} +0 -0
  268. /package/components/switch/{class.js → switch.class.js} +0 -0
  269. /package/components/table/{class.js → table.class.js} +0 -0
  270. /package/components/tabs/{class.js → tabs.class.js} +0 -0
  271. /package/components/text/{class.js → text.class.js} +0 -0
  272. /package/components/textarea/{class.js → textarea.class.js} +0 -0
  273. /package/components/time-picker/{class.js → time-picker.class.js} +0 -0
  274. /package/components/toast/{class.js → toast.class.js} +0 -0
  275. /package/components/toggle-group/{class.js → toggle-group.class.js} +0 -0
  276. /package/components/toggle-scheme/{class.js → toggle-scheme.class.js} +0 -0
  277. /package/components/toolbar/{class.js → toolbar.class.js} +0 -0
  278. /package/components/tooltip/{class.js → tooltip.class.js} +0 -0
  279. /package/components/tree/{class.js → tree.class.js} +0 -0
  280. /package/components/upload/{class.js → upload.class.js} +0 -0
@@ -27,11 +27,21 @@ tag-ui[removable]:not([disabled]):hover {
27
27
  --tag-duration-default: var(--a-duration-fast);
28
28
  --tag-easing-default: var(--a-easing);
29
29
 
30
- /* ── Dismiss ── */
30
+ /* ── Dismiss ──
31
+ Rest = inherit the host's text color at reduced opacity (so the X
32
+ reads as quieter chrome than the label, but tracks every variant
33
+ — info/success/danger get a near-white X on saturated bg; warning
34
+ gets a dark-brown X on bright amber; default gets a fg-muted X on
35
+ quiet chrome). Hover restores full opacity + drops a translucent
36
+ overlay in the same color family for the affordance.
37
+ Pre-v0.6.36 used `--a-fg-muted` / `--a-fg` directly, which gave
38
+ a neutral-grey X on saturated solid pills — the X disappeared
39
+ against the variant bg. */
31
40
  --tag-dismiss-radius-default: var(--a-radius-full);
32
- --tag-dismiss-fg-default: var(--a-fg-muted);
33
- --tag-dismiss-fg-hover-default: var(--a-fg);
34
- --tag-dismiss-bg-hover-default: var(--a-bg-muted);
41
+ --tag-dismiss-fg-default: currentColor;
42
+ --tag-dismiss-opacity-default: 0.85;
43
+ --tag-dismiss-opacity-hover-default: 1;
44
+ --tag-dismiss-bg-hover-default: color-mix(in oklch, currentColor 18%, transparent);
35
45
  text-align: start; /* §text-align-reset — blocks inheritance from centered ancestors */
36
46
  }
37
47
 
@@ -64,37 +74,81 @@ tag-ui[removable]:not([disabled]):hover {
64
74
  content: attr(text);
65
75
  }
66
76
 
67
- /* ── Variants ── */
77
+ /* ── Variants ──
78
+ Default tone is `solid` for the four family variants:
79
+ `--a-{family}-bg` (saturated surface) + `--a-{family}-fg` (on-strong
80
+ text, near-white in both schemes by design). Reads as a status pill
81
+ where the chip IS the state. Opt out per-tag via [tone="muted"] for
82
+ metadata-chip surfaces in dense lists. */
68
83
  :scope[variant="info"] {
69
- --tag-bg-default: var(--a-info-muted);
70
- --tag-fg-default: var(--a-info-bg);
84
+ --tag-bg-default: var(--a-info-bg);
85
+ --tag-fg-default: var(--a-info-fg);
71
86
  }
72
87
 
73
88
  :scope[variant="success"] {
74
- --tag-bg-default: var(--a-success-muted);
75
- --tag-fg-default: var(--a-success-bg);
89
+ --tag-bg-default: var(--a-success-bg);
90
+ --tag-fg-default: var(--a-success-fg);
76
91
  }
77
92
 
93
+ /* `--a-warning-bg` (= --a-warning-strong / -50) is too dark to read
94
+ against `--a-warning-fg` (= -text-strong / -10-shade), giving a
95
+ muddy brown-on-brown chip. Per the semantics.css comment "warning
96
+ is light-colored — text on warning fills should be dark", the
97
+ intent is bright-amber-bg + dark-text. The literal `-20-tint`
98
+ step is bright in BOTH schemes (independent of light-dark swap),
99
+ so warning solid keeps the same caution-tape look across modes.
100
+ The other family variants stay on `-bg` + `-fg` because their
101
+ -text-strong is `-05-tint` (near-white) which contrasts fine
102
+ against their saturated `-strong` bg. */
78
103
  :scope[variant="warning"] {
79
- --tag-bg-default: var(--a-warning-muted);
80
- --tag-fg-default: var(--a-warning-bg);
104
+ --tag-bg-default: var(--a-warning-20-tint);
105
+ --tag-fg-default: var(--a-warning-fg);
81
106
  }
82
107
 
83
108
  :scope[variant="danger"] {
84
- --tag-bg-default: var(--a-danger-muted);
85
- --tag-fg-default: var(--a-danger-bg);
109
+ --tag-bg-default: var(--a-danger-bg);
110
+ --tag-fg-default: var(--a-danger-fg);
86
111
  }
87
112
 
88
- /* `default` is a semantic alias of the base same tokens as the
89
- unstyled `:scope`, declared explicitly so the yaml enum and the
90
- CSS contract agree. Most consumers omit `variant` and inherit the
91
- base; the explicit selector lets `<tag-ui variant="default">`
92
- render identically without falling through to base. */
113
+ /* `default` (no family) stays as quiet chromea stark fg/bg-inverse
114
+ would be too loud for the no-variant case. Opt in to the inverse
115
+ stamp via `[tone="solid"]` when you want a high-contrast neutral pill. */
93
116
  :scope[variant="default"] {
94
117
  --tag-bg-default: var(--a-bg-muted);
95
118
  --tag-fg-default: var(--a-fg);
96
119
  }
97
120
 
121
+ /* ── Tone modifier — orthogonal to [variant] ──
122
+ `[tone="muted"]` opts each family variant OUT of the solid default
123
+ into the canonical muted pair: `--a-{family}-muted` (tinted surface)
124
+ + `--a-{family}-text` (scheme-flipping text). Same shape <badge-ui>
125
+ uses as its default. Use on metadata chips in dense lists where the
126
+ saturated default would compete for attention. */
127
+ :scope[tone="muted"][variant="info"] {
128
+ --tag-bg-default: var(--a-info-muted);
129
+ --tag-fg-default: var(--a-info-text);
130
+ }
131
+ :scope[tone="muted"][variant="success"] {
132
+ --tag-bg-default: var(--a-success-muted);
133
+ --tag-fg-default: var(--a-success-text);
134
+ }
135
+ :scope[tone="muted"][variant="warning"] {
136
+ --tag-bg-default: var(--a-warning-muted);
137
+ --tag-fg-default: var(--a-warning-text);
138
+ }
139
+ :scope[tone="muted"][variant="danger"] {
140
+ --tag-bg-default: var(--a-danger-muted);
141
+ --tag-fg-default: var(--a-danger-text);
142
+ }
143
+
144
+ /* `[tone="solid"]` on the neutral default (or no variant) inverts the
145
+ chrome — solid fg-color bg with bg-color text. High-contrast neutral
146
+ stamp. Explicit opt-in; the variant-less default stays quiet chrome. */
147
+ :scope[tone="solid"]:not([variant="info"]):not([variant="success"]):not([variant="warning"]):not([variant="danger"]) {
148
+ --tag-bg-default: var(--a-fg);
149
+ --tag-fg-default: var(--a-bg);
150
+ }
151
+
98
152
  /* Size handled by universal [size] attribute system. */
99
153
 
100
154
  /* hover rule moved outside @scope — see Safari 17.x bug note at top. */
@@ -105,6 +159,15 @@ tag-ui[removable]:not([disabled]):hover {
105
159
  box-shadow: var(--tag-focus-ring, var(--tag-focus-ring-default));
106
160
  }
107
161
 
162
+ /* ── Slotted icons (leading) ──
163
+ Icons placed inside the tag (e.g. `<icon-ui name="check">`) inherit
164
+ the host's text color so legend / status chips read as a single
165
+ color-coded unit. Mirrors `<badge-ui>`'s convention. */
166
+ :scope > icon-ui {
167
+ color: currentColor;
168
+ flex-shrink: 0;
169
+ }
170
+
108
171
  /* ── Dismiss button ── */
109
172
  [slot="dismiss"] {
110
173
  display: inline-flex;
@@ -118,13 +181,14 @@ tag-ui[removable]:not([disabled]):hover {
118
181
  cursor: pointer;
119
182
  border-radius: var(--tag-dismiss-radius, var(--tag-dismiss-radius-default));
120
183
  color: var(--tag-dismiss-fg, var(--tag-dismiss-fg-default));
184
+ opacity: var(--tag-dismiss-opacity, var(--tag-dismiss-opacity-default));
121
185
  --a-icon-size: 0.875rem;
122
186
  order: 1; /* push dismiss to end so layout reads [text] [×] */
123
- transition: color var(--tag-duration, var(--tag-duration-default)) var(--tag-easing, var(--tag-easing-default)), background var(--tag-duration, var(--tag-duration-default)) var(--tag-easing, var(--tag-easing-default));
187
+ transition: opacity var(--tag-duration, var(--tag-duration-default)) var(--tag-easing, var(--tag-easing-default)), background var(--tag-duration, var(--tag-duration-default)) var(--tag-easing, var(--tag-easing-default));
124
188
  }
125
189
 
126
190
  [slot="dismiss"]:hover {
127
- color: var(--tag-dismiss-fg-hover, var(--tag-dismiss-fg-hover-default));
191
+ opacity: var(--tag-dismiss-opacity-hover, var(--tag-dismiss-opacity-hover-default));
128
192
  background: var(--tag-dismiss-bg-hover, var(--tag-dismiss-bg-hover-default));
129
193
  }
130
194
 
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  import { defineIfFree } from '../../core/register.js';
13
- import { UITag } from './class.js';
13
+ import { UITag } from './tag.class.js';
14
14
 
15
15
  defineIfFree('tag-ui', UITag);
16
16
 
@@ -41,6 +41,80 @@ describe('tag-ui — content: attr(text) gating', () => {
41
41
  // [icon] + [dismiss] slot rendering and for proper :scope[text]
42
42
  // gap rendering when text IS set.
43
43
  expect(TAG_CSS).toMatch(/:scope\s*\{[^}]*display:\s*inline-flex/);
44
- expect(TAG_CSS).toMatch(/:scope\s*\{[^}]*gap:\s*var\(--tag-gap\)/);
44
+ // Post OD-5 sweep: tokens read via `var(--prop, var(--prop-default))`
45
+ // chain so consumer-named overrides AND --a-* surface overrides both work.
46
+ expect(TAG_CSS).toMatch(/:scope\s*\{[^}]*gap:\s*var\(--tag-gap,\s*var\(--tag-gap-default\)\)/);
47
+ });
48
+ });
49
+
50
+ // ── Tone defaults (solid by default for families; muted as opt-out) ──
51
+ //
52
+ // Default tone is `solid` for family variants — the base `:scope[variant=…]`
53
+ // rules use the saturated-fill pair `--a-{family}-bg` + `--a-{family}-fg`
54
+ // (the chip IS the state). `[tone="muted"]` opts back into the canonical
55
+ // muted pair `--a-{family}-muted` + `--a-{family}-text` (same look as
56
+ // <badge-ui>'s default). The `default` (no-family) variant stays quiet
57
+ // chrome unless `[tone="solid"]` is set explicitly (then it inverts
58
+ // to fg/bg for a high-contrast neutral stamp).
59
+
60
+ describe('tag-ui — variant defaults to solid fill', () => {
61
+ it.each([
62
+ ['info'],
63
+ ['success'],
64
+ ['danger'],
65
+ ])('[variant="%s"] base rule uses --a-{family}-bg + --a-{family}-fg', (family) => {
66
+ const block = new RegExp(
67
+ `:scope\\[variant="${family}"\\]\\s*\\{[^}]*` +
68
+ `--tag-bg-default:\\s*var\\(--a-${family}-bg\\)[^}]*` +
69
+ `--tag-fg-default:\\s*var\\(--a-${family}-fg\\)`,
70
+ 's'
71
+ );
72
+ expect(TAG_CSS).toMatch(block);
73
+ });
74
+
75
+ it('[variant="warning"] uses the literal --a-warning-20-tint bg (NOT --a-warning-bg)', () => {
76
+ // The token-system convention `-bg` + `-fg` collapses for warning
77
+ // because `--a-warning-fg` is dark (`-10-shade`) but `--a-warning-bg`
78
+ // (= `-strong` = `-50`) is mid-tone amber — dark-on-mid is muddy.
79
+ // The literal `-20-tint` step is bright in both schemes, restoring
80
+ // the caution-tape look the semantics.css comment intends.
81
+ expect(TAG_CSS).toMatch(
82
+ /:scope\[variant="warning"\]\s*\{[^}]*--tag-bg-default:\s*var\(--a-warning-20-tint\)[^}]*--tag-fg-default:\s*var\(--a-warning-fg\)/s
83
+ );
84
+ // Negative — guard against accidental revert to the muddy pair.
85
+ expect(TAG_CSS).not.toMatch(
86
+ /:scope\[variant="warning"\]\s*\{[^}]*--tag-bg-default:\s*var\(--a-warning-bg\)/s
87
+ );
88
+ });
89
+ });
90
+
91
+ describe('tag-ui — [tone="muted"] opts out to canonical muted pair', () => {
92
+ it.each([
93
+ ['info'],
94
+ ['success'],
95
+ ['warning'],
96
+ ['danger'],
97
+ ])('[tone="muted"][variant="%s"] uses --a-{family}-muted + --a-{family}-text', (family) => {
98
+ const block = new RegExp(
99
+ `:scope\\[tone="muted"\\]\\[variant="${family}"\\]\\s*\\{[^}]*` +
100
+ `--tag-bg-default:\\s*var\\(--a-${family}-muted\\)[^}]*` +
101
+ `--tag-fg-default:\\s*var\\(--a-${family}-text\\)`,
102
+ 's'
103
+ );
104
+ expect(TAG_CSS).toMatch(block);
105
+ });
106
+ });
107
+
108
+ describe('tag-ui — neutral default + explicit solid stamp', () => {
109
+ it('[variant="default"] stays quiet chrome (--a-bg-muted + --a-fg)', () => {
110
+ expect(TAG_CSS).toMatch(
111
+ /:scope\[variant="default"\]\s*\{[^}]*--tag-bg-default:\s*var\(--a-bg-muted\)[^}]*--tag-fg-default:\s*var\(--a-fg\)/s
112
+ );
113
+ });
114
+
115
+ it('[tone="solid"] without a family inverts to fg/bg high-contrast stamp', () => {
116
+ expect(TAG_CSS).toMatch(
117
+ /:scope\[tone="solid"\]:not\(\[variant="info"\]\)[\s\S]*?--tag-bg-default:\s*var\(--a-fg\)[\s\S]*?--tag-fg-default:\s*var\(--a-bg\)/
118
+ );
45
119
  });
46
120
  });
@@ -51,6 +51,20 @@ props:
51
51
  - success
52
52
  - warning
53
53
  - danger
54
+ tone:
55
+ description: |
56
+ Fill style — orthogonal to [variant]. Default (`solid`) for family
57
+ variants is a saturated bg with on-strong (near-white) text — the
58
+ chip IS the state. Opt-out via `tone="muted"` for a tinted bg with
59
+ scheme-paired text (matches <badge-ui>'s default look) when the
60
+ chip is metadata in a dense list rather than a status stamp. The
61
+ `default` variant stays quiet chrome regardless of tone unless
62
+ `tone="solid"` is set explicitly (high-contrast neutral inverse).
63
+ type: string
64
+ default: solid
65
+ enum:
66
+ - solid
67
+ - muted
54
68
  events:
55
69
  remove:
56
70
  description: Fired when the dismiss button is activated.
@@ -0,0 +1,337 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://adiaui.dev/a2ui/v0_9/components/TagsInput.json",
4
+ "title": "TagsInput",
5
+ "description": "Free-form token input — type a value, press Enter (or the configured\ndelimiter) to commit it as a chip; Backspace from the empty inline\ninput removes the last chip. Distinct from <select-ui multiple>\n(SPEC-040), which is constrained to a fixed option list — tags-input\nis for OPEN sets (labels, email recipients, keyword inputs). Per\nADR-0025 the inline editor is a contenteditable surface (NO native\n`<input>` wrap). Form-bearing via UIFormElement + ElementInternals:\nthe form value is a JSON-serialized string array under `[name]`.\nEach rendered chip is a `<tag-ui removable>`; the `remove` event\ndelegates back to the host. Optional autocompletion: when\n`.suggestions` is non-empty, a popover renders below the field and\nthe host announces as a combobox per WAI-APG.\n",
6
+ "type": "object",
7
+ "allOf": [
8
+ {
9
+ "$ref": "common_types.json#/$defs/ComponentCommon"
10
+ },
11
+ {
12
+ "$ref": "common_types.json#/$defs/CatalogComponentCommon"
13
+ }
14
+ ],
15
+ "properties": {
16
+ "required": {
17
+ "description": "Required for form validation. Empty array fails `:invalid`.",
18
+ "type": "boolean",
19
+ "default": false
20
+ },
21
+ "component": {
22
+ "const": "TagsInput"
23
+ },
24
+ "delimiter": {
25
+ "description": "Inline character that commits the typed value as a chip. The\nsentinel `enter` disables in-line commit — only the Enter key\n(or programmatic `addToken`) commits. Default is `,`.",
26
+ "type": "string",
27
+ "default": ","
28
+ },
29
+ "disabled": {
30
+ "description": "Block all interaction; dim the field.",
31
+ "type": "boolean",
32
+ "default": false
33
+ },
34
+ "max": {
35
+ "description": "Maximum number of tokens. `0` (default) means unlimited.",
36
+ "type": "number",
37
+ "default": 0
38
+ },
39
+ "maxLength": {
40
+ "description": "Maximum per-token character count. `0` (default) means unlimited.",
41
+ "type": "number",
42
+ "default": 0
43
+ },
44
+ "min": {
45
+ "description": "Minimum required tokens for form validity.",
46
+ "type": "number",
47
+ "default": 0
48
+ },
49
+ "minLength": {
50
+ "description": "Minimum per-token character count after `transform` is applied.",
51
+ "type": "number",
52
+ "default": 1
53
+ },
54
+ "name": {
55
+ "description": "Form field name. Serializes the token list as JSON under this key.",
56
+ "type": "string",
57
+ "default": ""
58
+ },
59
+ "pasteSplit": {
60
+ "description": "Regex character-class fragment matched against pasted text to\nsplit it into multiple tokens. Default `,\\n` splits on commas\nand newlines. Use `\"\"` to disable paste-splitting entirely.",
61
+ "type": "string",
62
+ "default": ",\n"
63
+ },
64
+ "placeholder": {
65
+ "description": "Placeholder text for the inline input when no chips are committed.",
66
+ "type": "string",
67
+ "default": "Add tag…"
68
+ },
69
+ "readonly": {
70
+ "description": "Block edits; allow inspection (chips render without remove buttons).",
71
+ "type": "boolean",
72
+ "default": false
73
+ },
74
+ "size": {
75
+ "description": "Sizing scale via universal `[size]` attribute system. Matches\nInput + Combobox sizing tokens so a TagsInput rendered alongside\neither feels coherent in a form row.",
76
+ "type": "string",
77
+ "enum": [
78
+ "sm",
79
+ "md",
80
+ "lg"
81
+ ],
82
+ "default": "md"
83
+ },
84
+ "suggestions": {
85
+ "description": "Optional autocomplete strings rendered in a popover below the\nfield. Suggestions are hints, not gates — the typed value still\nwins on commit. JS property only (array reflection skipped).",
86
+ "$ref": "common_types.json#/$defs/DynamicStringList"
87
+ },
88
+ "transform": {
89
+ "description": "Normalize tokens on commit. `lowercase` lowercases, `trim` strips\nleading + trailing whitespace, `strip-spaces` removes all\nwhitespace, `\"\"` (default) preserves the typed value.",
90
+ "type": "string",
91
+ "enum": [
92
+ "",
93
+ "lowercase",
94
+ "trim",
95
+ "strip-spaces"
96
+ ],
97
+ "default": ""
98
+ },
99
+ "unique": {
100
+ "description": "Reject duplicate tokens silently (no `invalid` event for accidental dups).",
101
+ "type": "boolean",
102
+ "default": true
103
+ },
104
+ "validateFn": {
105
+ "description": "Per-token validator. Sync `(value, index) => boolean` rejects\nfalse synchronously; async `(value, index) => Promise<boolean>`\nflips the host into `[validating]` while pending. JS property\nonly — not serializable in A2UI JSON; wire post-mount.",
106
+ "type": "object",
107
+ "default": null
108
+ },
109
+ "value": {
110
+ "description": "Current token list (string array). Setting `.value = ['a','b']`\nreplaces the rendered chips. Reflection skipped — array values\ndo not round-trip through attribute strings.",
111
+ "$ref": "common_types.json#/$defs/DynamicStringList"
112
+ }
113
+ },
114
+ "required": [
115
+ "component"
116
+ ],
117
+ "unevaluatedProperties": false,
118
+ "x-adiaui": {
119
+ "anti_patterns": [
120
+ {
121
+ "fix": "{\"component\": \"Select\", \"multiple\": true, \"options\": [{\"id\": \"a\", \"label\": \"A\"}]}\n",
122
+ "why": "TagsInput does NOT accept `options`. It is the OPEN-set primitive — any\ntyped string is valid. For closed option lists, use the multi-select\nprimitive with `multiple: true`.\n",
123
+ "wrong": "{\"component\": \"TagsInput\", \"options\": [{\"id\": \"a\", \"label\": \"A\"}]}\n"
124
+ },
125
+ {
126
+ "fix": "{\"component\": \"TagsInput\", \"value\": [\"bug\", \"high-priority\"]}\n",
127
+ "why": "Comma-separated string is invalid for `value`. The contract requires\na string array.\n",
128
+ "wrong": "{\"component\": \"TagsInput\", \"value\": \"bug,high-priority\"}\n"
129
+ },
130
+ {
131
+ "fix": "{\"component\": \"TagsInput\", \"value\": [\"Tag 1\"]}\n",
132
+ "why": "Chips are rendered automatically from `value`. Slotting Tag children\ndecouples the rendered DOM from the form value and breaks Backspace\nremoval + the `change` event payload.\n",
133
+ "wrong": "{\"component\": \"TagsInput\", \"children\": [\n {\"component\": \"Tag\", \"text\": \"Tag 1\"}\n]}\n"
134
+ }
135
+ ],
136
+ "category": "input",
137
+ "composes": [
138
+ "tag-ui",
139
+ "icon-ui"
140
+ ],
141
+ "events": {
142
+ "change": {
143
+ "description": "Fired after `value` changes (token added or removed).",
144
+ "detail": {
145
+ "added": {
146
+ "description": "Tokens added by this change (`[]` when only removals occurred).",
147
+ "type": "array"
148
+ },
149
+ "removed": {
150
+ "description": "Tokens removed by this change (`[]` when only additions occurred).",
151
+ "type": "array"
152
+ },
153
+ "value": {
154
+ "description": "Current token list (string array).",
155
+ "type": "array"
156
+ }
157
+ }
158
+ },
159
+ "commit": {
160
+ "description": "Fired immediately before a token is committed. Call\n`event.preventDefault()` to reject the candidate token.",
161
+ "detail": {
162
+ "accepted": {
163
+ "description": "True unless the consumer calls `preventDefault()`.",
164
+ "type": "boolean"
165
+ },
166
+ "value": {
167
+ "description": "Candidate token text (post-transform).",
168
+ "type": "string"
169
+ }
170
+ }
171
+ },
172
+ "input": {
173
+ "description": "Fired on each keystroke in the inline input (pre-commit).",
174
+ "detail": {
175
+ "query": {
176
+ "description": "Current typed text in the inline input.",
177
+ "type": "string"
178
+ }
179
+ }
180
+ },
181
+ "invalid": {
182
+ "description": "Fired when a candidate token is rejected. Reasons cover all\nbuilt-in validators and the user-supplied `validateFn`.",
183
+ "detail": {
184
+ "reason": {
185
+ "description": "One of `duplicate` / `too-short` / `too-long` / `validator` / `max`.",
186
+ "type": "string"
187
+ },
188
+ "value": {
189
+ "description": "Rejected token text.",
190
+ "type": "string"
191
+ }
192
+ }
193
+ }
194
+ },
195
+ "examples": [
196
+ {
197
+ "description": "Label input with lowercase normalization + max 10 tags + 2 pre-committed values.",
198
+ "a2ui": "[\n {\"id\": \"root\", \"component\": \"Card\", \"children\": [\"sec\"]},\n {\"id\": \"sec\", \"component\": \"Section\", \"children\": [\"field\"]},\n {\n \"id\": \"field\",\n \"component\": \"Field\",\n \"label\": \"Labels\",\n \"children\": [\"tags\"]\n },\n {\n \"id\": \"tags\",\n \"component\": \"TagsInput\",\n \"name\": \"labels\",\n \"placeholder\": \"Add a label, press Enter\",\n \"transform\": \"lowercase\",\n \"max\": 10,\n \"value\": [\"bug\", \"high-priority\"]\n }\n]",
199
+ "name": "canonical-labels"
200
+ },
201
+ {
202
+ "description": "Recipient picker with Enter-only commit (commas are legal in display names).",
203
+ "a2ui": "[\n {\n \"id\": \"to\",\n \"component\": \"TagsInput\",\n \"name\": \"to\",\n \"placeholder\": \"Email…\",\n \"delimiter\": \"enter\",\n \"transform\": \"trim\"\n }\n]",
204
+ "name": "email-recipients"
205
+ }
206
+ ],
207
+ "keywords": [
208
+ "tags-input",
209
+ "tag-input",
210
+ "token-input",
211
+ "chips-input",
212
+ "chip-input",
213
+ "labels",
214
+ "recipients",
215
+ "keywords",
216
+ "tags",
217
+ "email-input",
218
+ "multi-value-input"
219
+ ],
220
+ "name": "UITagsInput",
221
+ "related": [
222
+ "tag",
223
+ "select",
224
+ "combobox",
225
+ "field",
226
+ "input"
227
+ ],
228
+ "slots": {
229
+ "chip": {
230
+ "description": "Custom chip template — receives `{value, index}` per token.\nWhen absent, each token renders as a default\n`<tag-ui removable text=\"<value>\">`."
231
+ }
232
+ },
233
+ "states": [
234
+ {
235
+ "description": "No tokens; no typed text.",
236
+ "name": "idle"
237
+ },
238
+ {
239
+ "description": "One or more tokens committed.",
240
+ "attribute": "populated",
241
+ "name": "populated"
242
+ },
243
+ {
244
+ "description": "User is typing into the inline input.",
245
+ "attribute": "editing",
246
+ "name": "editing"
247
+ },
248
+ {
249
+ "description": "Suggestion popover is open.",
250
+ "attribute": "suggesting",
251
+ "name": "suggesting"
252
+ },
253
+ {
254
+ "description": "Async `validateFn` is in flight.",
255
+ "attribute": "validating",
256
+ "name": "validating"
257
+ },
258
+ {
259
+ "description": "Non-interactive; dimmed.",
260
+ "attribute": "disabled",
261
+ "name": "disabled"
262
+ },
263
+ {
264
+ "description": "Read-only; chips render without remove affordance.",
265
+ "attribute": "readonly",
266
+ "name": "readonly"
267
+ }
268
+ ],
269
+ "status": "experimental",
270
+ "synonyms": {
271
+ "chips-input": [
272
+ "tags-input",
273
+ "chip-input",
274
+ "token-input"
275
+ ],
276
+ "labels": [
277
+ "tags",
278
+ "tag-input",
279
+ "labels-input"
280
+ ],
281
+ "recipients": [
282
+ "email-input",
283
+ "tags-input",
284
+ "to-field"
285
+ ],
286
+ "tags-input": [
287
+ "tag-input",
288
+ "chip-input",
289
+ "token-input",
290
+ "tokenizer"
291
+ ],
292
+ "token-input": [
293
+ "tags-input",
294
+ "chip-input",
295
+ "tokenizer"
296
+ ]
297
+ },
298
+ "tag": "tags-input-ui",
299
+ "tokens": {
300
+ "--tags-input-bg": {
301
+ "description": "Host background color."
302
+ },
303
+ "--tags-input-border": {
304
+ "description": "Host border color (idle)."
305
+ },
306
+ "--tags-input-border-hover": {
307
+ "description": "Host border color on hover."
308
+ },
309
+ "--tags-input-chip-bg-invalid": {
310
+ "description": "Chip background color when a chip is marked invalid (validator rejection echo)."
311
+ },
312
+ "--tags-input-fg": {
313
+ "description": "Host foreground (typed-text color)."
314
+ },
315
+ "--tags-input-focus-ring": {
316
+ "description": "Focus-ring box-shadow when the host is focus-within."
317
+ },
318
+ "--tags-input-gap": {
319
+ "description": "Gap between chips and the inline input."
320
+ },
321
+ "--tags-input-placeholder-fg": {
322
+ "description": "Placeholder color for the empty inline input."
323
+ },
324
+ "--tags-input-px": {
325
+ "description": "Horizontal padding inside the chip-cluster wrapper."
326
+ },
327
+ "--tags-input-py": {
328
+ "description": "Vertical padding inside the chip-cluster wrapper."
329
+ },
330
+ "--tags-input-radius": {
331
+ "description": "Host border radius."
332
+ }
333
+ },
334
+ "traits": [],
335
+ "version": 1
336
+ }
337
+ }