@adia-ai/web-components 0.6.49 → 0.7.0

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 (113) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/components/action-list/action-list.css +1 -1
  3. package/components/agent-artifact/agent-artifact.class.js +10 -10
  4. package/components/agent-artifact/agent-artifact.css +1 -1
  5. package/components/agent-reasoning/agent-reasoning.class.js +52 -1
  6. package/components/agent-reasoning/agent-reasoning.css +49 -22
  7. package/components/agent-trace/agent-trace.css +10 -10
  8. package/components/agent-trace/agent-trace.js +3 -3
  9. package/components/alert/alert.class.js +8 -1
  10. package/components/alert/alert.css +13 -1
  11. package/components/avatar/avatar.a2ui.json +2 -14
  12. package/components/avatar/avatar.class.js +3 -15
  13. package/components/avatar/avatar.d.ts +2 -4
  14. package/components/avatar/avatar.yaml +1 -18
  15. package/components/breadcrumb/breadcrumb.css +4 -1
  16. package/components/button/button.a2ui.json +3 -0
  17. package/components/button/button.css +14 -3
  18. package/components/button/button.yaml +5 -0
  19. package/components/calendar-grid/calendar-grid.css +1 -1
  20. package/components/calendar-picker/calendar-picker.css +5 -2
  21. package/components/chart/chart.a2ui.json +0 -18
  22. package/components/chart/chart.class.js +8 -50
  23. package/components/chart/chart.css +1 -15
  24. package/components/chart/chart.d.ts +0 -4
  25. package/components/chart/chart.yaml +0 -24
  26. package/components/color-input/color-input.css +4 -1
  27. package/components/combobox/combobox.class.js +11 -0
  28. package/components/combobox/combobox.css +8 -0
  29. package/components/date-range-picker/date-range-picker.class.js +5 -1
  30. package/components/date-range-picker/date-range-picker.css +12 -2
  31. package/components/datetime-picker/datetime-picker.class.js +3 -0
  32. package/components/datetime-picker/datetime-picker.css +16 -2
  33. package/components/empty-state/empty-state.css +11 -4
  34. package/components/field/field.css +17 -6
  35. package/components/grid/grid.a2ui.json +5 -0
  36. package/components/grid/grid.class.js +16 -6
  37. package/components/grid/grid.css +17 -3
  38. package/components/grid/grid.d.ts +2 -0
  39. package/components/grid/grid.yaml +9 -0
  40. package/components/heatmap/heatmap.class.js +9 -3
  41. package/components/heatmap/heatmap.css +19 -2
  42. package/components/image/image.css +4 -1
  43. package/components/input/input.css +1 -1
  44. package/components/integration-card/integration-card.class.js +31 -7
  45. package/components/integration-card/integration-card.test.js +12 -1
  46. package/components/kbd/kbd.a2ui.json +3 -2
  47. package/components/kbd/kbd.css +7 -4
  48. package/components/kbd/kbd.d.ts +2 -2
  49. package/components/kbd/kbd.yaml +2 -1
  50. package/components/list/list.class.js +8 -1
  51. package/components/menu/menu.css +4 -1
  52. package/components/modal/modal.class.js +10 -1
  53. package/components/modal/modal.css +9 -0
  54. package/components/option-card/option-card.a2ui.json +3 -0
  55. package/components/option-card/option-card.css +44 -19
  56. package/components/option-card/option-card.yaml +5 -0
  57. package/components/otp-input/otp-input.css +25 -10
  58. package/components/page/page.css +64 -11
  59. package/components/pagination/pagination.class.js +1 -1
  60. package/components/pagination/pagination.css +9 -1
  61. package/components/pane/pane.a2ui.json +3 -0
  62. package/components/pane/pane.class.js +7 -6
  63. package/components/pane/pane.css +5 -5
  64. package/components/pane/pane.yaml +6 -0
  65. package/components/pipeline-status/pipeline-status.css +6 -0
  66. package/components/popover/popover.css +12 -1
  67. package/components/preview/preview.css +30 -3
  68. package/components/progress-row/progress-row.css +3 -1
  69. package/components/qr-code/qr-code.css +4 -1
  70. package/components/segmented/segmented.css +4 -1
  71. package/components/select/select.a2ui.json +1 -1
  72. package/components/select/select.class.js +63 -7
  73. package/components/select/select.css +18 -0
  74. package/components/select/select.yaml +9 -2
  75. package/components/stack/stack.a2ui.json +12 -1
  76. package/components/stack/stack.d.ts +2 -2
  77. package/components/stack/stack.yaml +13 -1
  78. package/components/stat/stat.a2ui.json +5 -0
  79. package/components/stat/stat.css +55 -0
  80. package/components/stat/stat.d.ts +2 -0
  81. package/components/stat/stat.js +4 -0
  82. package/components/stat/stat.yaml +9 -0
  83. package/components/swiper/swiper.class.js +14 -6
  84. package/components/switch/switch.css +13 -0
  85. package/components/table/table.a2ui.json +2 -2
  86. package/components/table/table.css +13 -1
  87. package/components/table/table.yaml +2 -2
  88. package/components/time-picker/time-picker.css +4 -1
  89. package/components/timeline/timeline.class.js +3 -3
  90. package/components/timeline/timeline.css +23 -5
  91. package/components/toggle-group/toggle-group.css +4 -1
  92. package/components/toggle-scheme/toggle-scheme.css +4 -1
  93. package/components/tree/tree-item.a2ui.json +6 -0
  94. package/components/tree/tree-item.yaml +13 -1
  95. package/components/tree/tree.a2ui.json +3 -3
  96. package/components/tree/tree.class.js +24 -9
  97. package/components/tree/tree.css +8 -8
  98. package/components/tree/tree.test.js +52 -0
  99. package/components/tree/tree.yaml +4 -4
  100. package/dist/web-components.min.css +1 -1
  101. package/dist/web-components.min.js +84 -84
  102. package/package.json +3 -3
  103. package/styles/api/layout.css +7 -0
  104. package/styles/api/text.css +9 -5
  105. package/styles/index.css +11 -2
  106. package/styles/prose.css +8 -0
  107. package/styles/resets.css +5 -5
  108. package/styles/themes.css +8 -1
  109. package/styles/tokens.css +3 -3
  110. package/styles/type/elements.css +73 -0
  111. package/styles/type/roles.css +14 -49
  112. package/styles/type/scale.css +0 -5
  113. package/styles/typography.css +3 -3
@@ -14,8 +14,19 @@
14
14
  ],
15
15
  "properties": {
16
16
  "align": {
17
- "description": "Alignment of stacked items",
17
+ "description": "Alignment of the layered children within the shared cell (maps to grid place-items). `center` (default) plus eight directional keywords.",
18
18
  "type": "string",
19
+ "enum": [
20
+ "center",
21
+ "top-left",
22
+ "top",
23
+ "top-right",
24
+ "left",
25
+ "right",
26
+ "bottom-left",
27
+ "bottom",
28
+ "bottom-right"
29
+ ],
19
30
  "default": "center"
20
31
  },
21
32
  "component": {
@@ -21,6 +21,6 @@ cell.
21
21
  import { UIElement } from '../../core/element.js';
22
22
 
23
23
  export class UIStack extends UIElement {
24
- /** Alignment of stacked items */
25
- align: string;
24
+ /** Alignment of the layered children within the shared cell (maps to grid place-items). `center` (default) plus eight directional keywords. */
25
+ align: 'center' | 'top-left' | 'top' | 'top-right' | 'left' | 'right' | 'bottom-left' | 'bottom' | 'bottom-right';
26
26
  }
@@ -18,9 +18,21 @@ description: |
18
18
  cell.
19
19
  props:
20
20
  align:
21
- description: Alignment of stacked items
21
+ description: >-
22
+ Alignment of the layered children within the shared cell (maps to
23
+ grid place-items). `center` (default) plus eight directional keywords.
22
24
  type: string
23
25
  default: center
26
+ enum:
27
+ - center
28
+ - top-left
29
+ - top
30
+ - top-right
31
+ - left
32
+ - right
33
+ - bottom-left
34
+ - bottom
35
+ - bottom-right
24
36
  events: {}
25
37
  slots:
26
38
  default:
@@ -13,6 +13,11 @@
13
13
  }
14
14
  ],
15
15
  "properties": {
16
+ "bleed": {
17
+ "description": "Horizontal-bleed KPI layout. With a `slot=\"chart\"` child, the value / label / change stack on the left while the chart fills the right column at full height and bleeds to the card's top / right / bottom edges (the horizontal counterpart to a chart in a card `<section bleed>`). No effect without a chart slot.",
18
+ "type": "boolean",
19
+ "default": false
20
+ },
16
21
  "change": {
17
22
  "description": "Change indicator text (e.g. '+12%', '-3%')",
18
23
  "type": "string",
@@ -72,6 +72,61 @@
72
72
  grid-column: 1;
73
73
  }
74
74
 
75
+ /* ── Horizontal-bleed KPI tile ──
76
+ value / label / change stack on the LEFT; the chart fills the RIGHT
77
+ column at full height and bleeds to the card's top / right / bottom
78
+ edges (cancels the card-section inset via negative margins keyed to
79
+ the inherited --card-inset). The compact `slot="chart"` reflow above
80
+ keeps the chart resting on the value baseline; this opt-in spreads it
81
+ into a full-height trajectory panel. Compose inside a card section:
82
+ <card-ui><section>
83
+ <stat-ui bleed value=… label=… change=… trend=…>
84
+ <chart-ui slot="chart" type="area" …></chart-ui>
85
+ </stat-ui>
86
+ </section></card-ui> */
87
+ :scope[bleed]:has([slot="chart"]) {
88
+ grid-template-columns: minmax(0, 1fr) minmax(0, var(--stat-bleed-col, 46%));
89
+ grid-template-areas:
90
+ "label chart"
91
+ "value chart"
92
+ "change chart";
93
+ align-items: start;
94
+ }
95
+ :scope[bleed] [slot="value"],
96
+ :scope[bleed] [slot="change"] {
97
+ grid-column: 1;
98
+ }
99
+ /* Icon shares the label row, pinned to the text column's inner edge. */
100
+ :scope[bleed] [slot="label"] {
101
+ padding-inline-end: var(--a-space-5);
102
+ }
103
+ :scope[bleed] [slot="icon"] {
104
+ grid-area: label;
105
+ justify-self: end;
106
+ align-self: start;
107
+ }
108
+ :scope[bleed] [slot="chart"] {
109
+ grid-area: chart;
110
+ /* VERTICAL bleed via stretch: a stretched grid item's height is
111
+ `track − margins`, so a negative margin-block overflows the track by
112
+ that amount top + bottom. `height:auto` overrides the base
113
+ chart-ui[slot="chart"]{height:100%} so the stretch governs. */
114
+ align-self: stretch;
115
+ height: auto;
116
+ margin-block: calc(-1 * var(--card-inset, var(--card-inset-default, 0px)));
117
+ /* HORIZONTAL bleed via explicit width: chart-ui pins its own
118
+ `width:100%` (chart.css), which beats justify-self:stretch and would
119
+ freeze the box to the track — so a negative margin-inline-end can't
120
+ widen it. Instead size the box to `track + inset` and left-anchor it,
121
+ so the right edge overflows to the card edge while the left keeps the
122
+ column gap. The percentage resolves against the grid track. */
123
+ justify-self: start;
124
+ width: calc(100% + var(--card-inset, var(--card-inset-default, 0px)));
125
+ min-width: 0;
126
+ /* override the inline-chart 4:3 — fill the row span instead */
127
+ aspect-ratio: auto;
128
+ }
129
+
75
130
  /* ── Label (eyebrow) ── */
76
131
  [slot="label"] {
77
132
  grid-area: label;
@@ -13,6 +13,8 @@
13
13
  import { UIElement } from '../../core/element.js';
14
14
 
15
15
  export class UIStat extends UIElement {
16
+ /** Horizontal-bleed KPI layout. With a `slot="chart"` child, the value / label / change stack on the left while the chart fills the right column at full height and bleeds to the card's top / right / bottom edges (the horizontal counterpart to a chart in a card `<section bleed>`). No effect without a chart slot. */
17
+ bleed: boolean;
16
18
  /** Change indicator text (e.g. '+12%', '-3%') */
17
19
  change: string;
18
20
  /** Icon name displayed in the icon slot */
@@ -18,6 +18,10 @@ class UIStat extends UIElement {
18
18
  trend: { type: String, default: '', reflect: true },
19
19
  icon: { type: String, default: '', reflect: true },
20
20
  loading: { type: Boolean, default: false, reflect: true },
21
+ // Horizontal-bleed KPI layout: value/label/change stack on the left, the
22
+ // slot="chart" child fills the right column and bleeds to the card edges.
23
+ // Reflected so the [bleed] CSS selector gates the layout.
24
+ bleed: { type: Boolean, default: false, reflect: true },
21
25
  };
22
26
 
23
27
  static template = () => null;
@@ -49,6 +49,15 @@ props:
49
49
  required: true
50
50
  type: string
51
51
  default: ""
52
+ bleed:
53
+ description: >-
54
+ Horizontal-bleed KPI layout. With a `slot="chart"` child, the value /
55
+ label / change stack on the left while the chart fills the right column
56
+ at full height and bleeds to the card's top / right / bottom edges (the
57
+ horizontal counterpart to a chart in a card `<section bleed>`). No effect
58
+ without a chart slot.
59
+ type: boolean
60
+ default: false
52
61
  events: {}
53
62
  slots:
54
63
  change:
@@ -249,14 +249,22 @@ export class UISwiper extends UIElement {
249
249
  }
250
250
 
251
251
  // ── Snap & page helpers ──
252
- // `--swiper-columns` is set on the host by `[slides-per-view]` and
253
- // cascades to the track; the responsive @container rules set it on
254
- // the track directly. Reading the computed value on the track sees
255
- // both paths.
256
-
252
+ // The column count drives the page/dot math. `[slides-per-view]` (and the
253
+ // responsive @container rules) set `--swiper-columns-default` on the host /
254
+ // track per the component-token contract; consumers may override via the
255
+ // public `--swiper-columns`. We must read the SAME fallback chain the CSS
256
+ // width calc uses — `var(--swiper-columns, var(--swiper-columns-default))` —
257
+ // not just the public token. Reading only `--swiper-columns` (unset unless a
258
+ // consumer overrides) made #getColumns() always fall back to 1, so the dot
259
+ // count was `total - 1 + 1 = total slides` instead of the real page count
260
+ // (bug-29: 5 slides at slides-per-view=3 stamped 5 dots, not 3). Regressed by
261
+ // the 2026-05-24 OD-5 `-default` token sweep, which renamed the CSS side but
262
+ // not this read site.
257
263
  #getColumns() {
258
264
  if (!this.#track) return 1;
259
- const v = getComputedStyle(this.#track).getPropertyValue('--swiper-columns').trim();
265
+ const cs = getComputedStyle(this.#track);
266
+ const v = cs.getPropertyValue('--swiper-columns').trim()
267
+ || cs.getPropertyValue('--swiper-columns-default').trim();
260
268
  const n = parseInt(v, 10);
261
269
  return Number.isFinite(n) && n > 0 ? n : 1;
262
270
  }
@@ -115,6 +115,19 @@ switch-ui[checked] [slot="thumb"] {
115
115
  color: var(--switch-hint-fg, var(--a-fg-muted));
116
116
  line-height: var(--switch-hint-lh, 1.4);
117
117
  }
118
+ /* The hint is documented (yaml + props table) as rendering BELOW the label,
119
+ but the base `inline-flex` row laid the stamped `[slot="hint"]` out as a
120
+ third inline item (beside the label). When a hint is present, let the row
121
+ wrap and force the hint onto its own full-width line, indented under the
122
+ label (past the track + gap). `:has()`-scoped so hint-less switches keep
123
+ the unchanged single-row layout (no long-label side effects); no display-
124
+ model change (stays inline-flex). The hint span is stamped only when
125
+ [hint] is set (switch.class.js), so this never adds a phantom line. */
126
+ :scope:has([slot="hint"]) { flex-wrap: wrap; }
127
+ :scope:has([slot="hint"]) [slot="hint"] {
128
+ flex-basis: 100%;
129
+ padding-inline-start: calc(var(--switch-track-width, var(--switch-track-width-default)) + var(--switch-gap, var(--switch-gap-default)));
130
+ }
118
131
 
119
132
  :scope:focus-visible { outline: none; }
120
133
  :scope:focus-visible [slot="track"] { box-shadow: var(--switch-focus-ring, var(--switch-focus-ring-default)); }
@@ -169,7 +169,7 @@
169
169
  }
170
170
  },
171
171
  "row-collapse": {
172
- "description": "Fired when an already-expanded row's chevron is activated (collapses the row). Mirror of row-expand.",
172
+ "description": "Fired when an already-expanded row's caret is activated (collapses the row). Mirror of row-expand.",
173
173
  "detail": {
174
174
  "index": {
175
175
  "description": "Row index in the underlying data array.",
@@ -182,7 +182,7 @@
182
182
  }
183
183
  },
184
184
  "row-expand": {
185
- "description": "Fired when an expandable row's chevron is activated. detail.index is the row position; detail.row is the row data.",
185
+ "description": "Fired when an expandable row's caret is activated. detail.index is the row position; detail.row is the row data.",
186
186
  "detail": {
187
187
  "index": {
188
188
  "description": "Row index in the underlying data array.",
@@ -323,7 +323,12 @@
323
323
  color: var(--table-fg-hover, var(--table-fg-hover-default));
324
324
  }
325
325
 
326
- [data-selected] {
326
+ /* Selected row — must out-specify the base row rule `[data-body] >
327
+ [role="row"]` (0,2,0) and the hover rule (0,2,1), or the highlight never
328
+ paints. Scoping to `[data-body] > [role="row"][data-selected]` lifts it to
329
+ (0,4,0). (The striped-even override below handles selected+striped, which
330
+ is higher still.) */
331
+ [data-body] > [role="row"][data-selected] {
327
332
  background: var(--table-row-bg-selected, var(--table-row-bg-selected-default));
328
333
  }
329
334
 
@@ -341,6 +346,13 @@
341
346
  background: var(--table-row-bg-hover, var(--table-row-bg-hover-default));
342
347
  }
343
348
 
349
+ /* Selected wins over the stripe — same (0,5,0) specificity as the striped-
350
+ even rule above, placed AFTER it so source order resolves the tie in
351
+ favor of selection (a selected row reads as selected regardless of stripe). */
352
+ :scope[striped] [data-body] > [role="row"][data-selected] {
353
+ background: var(--table-row-bg-selected, var(--table-row-bg-selected-default));
354
+ }
355
+
344
356
  /* ═══════ Selection checkboxes ═══════ */
345
357
 
346
358
  [data-check-col] {
@@ -146,7 +146,7 @@ events:
146
146
  type: number
147
147
  description: New column width in pixels.
148
148
  row-collapse:
149
- description: Fired when an already-expanded row's chevron is activated (collapses the row). Mirror of row-expand.
149
+ description: Fired when an already-expanded row's caret is activated (collapses the row). Mirror of row-expand.
150
150
  detail:
151
151
  index:
152
152
  type: number
@@ -155,7 +155,7 @@ events:
155
155
  type: object
156
156
  description: Row data object.
157
157
  row-expand:
158
- description: Fired when an expandable row's chevron is activated. detail.index is the row position; detail.row is the row data.
158
+ description: Fired when an expandable row's caret is activated. detail.index is the row position; detail.row is the row data.
159
159
  detail:
160
160
  index:
161
161
  type: number
@@ -30,7 +30,7 @@
30
30
  :scope {
31
31
  /* ── Base ── */
32
32
  box-sizing: border-box;
33
- display: inline-flex;
33
+ display: flex;
34
34
  align-items: center;
35
35
  gap: var(--time-picker-gap, var(--time-picker-gap-default));
36
36
  min-height: var(--time-picker-height, var(--time-picker-height-default));
@@ -50,6 +50,9 @@
50
50
  box-shadow var(--time-picker-duration, var(--time-picker-duration-default)) var(--time-picker-easing, var(--time-picker-easing-default));
51
51
  }
52
52
 
53
+ /* Display convention (ADR-0037): block-level by default; [inline] opts back to inline-level. */
54
+ :scope[inline] { display: inline-flex; }
55
+
53
56
  /* ── Segments ── */
54
57
  :scope [data-segment] {
55
58
  display: inline-block;
@@ -87,7 +87,7 @@ export class UITimelineItem extends UIElement {
87
87
  spinner: { type: Boolean, default: false, reflect: true },
88
88
  };
89
89
 
90
- // §205 (v0.5.7): dynamic chevron icons stamped on expanded-state ternary
90
+ // §205 (v0.5.7): dynamic caret icons stamped on expanded-state ternary
91
91
  // (timeline.class.js:167). Per FEEDBACK-16 §1 + §209 slot-11 ternary-walker discovery.
92
92
  // Note: `this.icon` consumer-supplied — not declared here.
93
93
  static requiredIcons = ['caret-down', 'caret-right'];
@@ -134,7 +134,7 @@ export class UITimelineItem extends UIElement {
134
134
  this.querySelector(':scope > [slot="icon"]')?.remove();
135
135
  }
136
136
 
137
- // Outcomes sub-list + toggle chevron
137
+ // Outcomes sub-list + toggle caret
138
138
  let body = this.querySelector(':scope > [slot="outcomes"]');
139
139
  if (this.#outcomes.length > 0) {
140
140
  if (!body) {
@@ -150,7 +150,7 @@ export class UITimelineItem extends UIElement {
150
150
  }
151
151
  body.hidden = !this.#expanded;
152
152
 
153
- // Toggle chevron lives on the row — we stamp a button once
153
+ // Toggle caret lives on the row — we stamp a button once
154
154
  let toggle = this.querySelector(':scope > [data-timeline-toggle]');
155
155
  if (!toggle) {
156
156
  toggle = document.createElement('button');
@@ -39,10 +39,18 @@
39
39
  --timeline-line-fg-default: var(--a-border-subtle);
40
40
  --timeline-line-fg-done-default: var(--a-border);
41
41
 
42
+ /* Marker box — the shared circular footprint the icon marker fills and
43
+ the dot / spinner center within (the column track --timeline-marker-w
44
+ is a touch wider for breathing room). One token sizes the whole marker
45
+ system; consumers (agent-reasoning-ui, chat surfaces) override it to
46
+ scale every marker type coherently. */
47
+ --timeline-marker-box-size-default: 1rem;
48
+
42
49
  /* Icon (replaces dot) */
43
- --timeline-icon-size-default: 1rem;
50
+ --timeline-icon-size-default: var(--timeline-marker-box-size, var(--timeline-marker-box-size-default));
44
51
  --timeline-icon-bg-default: var(--a-bg-muted);
45
52
  --timeline-icon-fg-default: var(--a-fg-subtle);
53
+ --timeline-icon-border-default: var(--a-border-subtle);
46
54
  --timeline-icon-bg-success-default: var(--a-success-muted);
47
55
  --timeline-icon-fg-success-default: var(--a-success-strong);
48
56
  --timeline-icon-bg-accent-default: var(--a-accent-muted);
@@ -85,6 +93,7 @@
85
93
  /* Compact variant (agent-reasoning + chat-adjacent) */
86
94
  :scope[size="sm"] {
87
95
  --timeline-marker-w-default: 1rem;
96
+ --timeline-marker-box-size-default: 0.875rem;
88
97
  --timeline-dot-size-default: 6px;
89
98
  --timeline-gap-row-default: var(--a-space-2);
90
99
  --timeline-label-size-default: var(--a-ui-sm);
@@ -133,9 +142,14 @@ agent-reasoning-ui timeline-ui:not([orientation="horizontal"]),
133
142
  --timeline-item-line-fg: var(--timeline-line-fg, var(--a-border-subtle));
134
143
  --timeline-item-line-fg-done: var(--timeline-line-fg-done, var(--a-border));
135
144
 
136
- --timeline-item-icon-size: var(--timeline-icon-size, 1rem);
145
+ /* Reads the override THEN the inherited -default (set on timeline-ui,
146
+ and shrunk by [size="sm"]) THEN a literal — so the marker box scales
147
+ with both consumer overrides AND the sm compact variant. */
148
+ --timeline-item-marker-box-size: var(--timeline-marker-box-size, var(--timeline-marker-box-size-default, 1rem));
149
+ --timeline-item-icon-size: var(--timeline-icon-size, var(--timeline-item-marker-box-size));
137
150
  --timeline-item-icon-bg: var(--timeline-icon-bg, var(--a-bg-muted));
138
151
  --timeline-item-icon-fg: var(--timeline-icon-fg, var(--a-fg-subtle));
152
+ --timeline-item-icon-border: var(--timeline-icon-border, var(--a-border-subtle));
139
153
  --timeline-item-icon-bg-success: var(--timeline-icon-bg-success, var(--a-success-muted));
140
154
  --timeline-item-icon-fg-success: var(--timeline-icon-fg-success, var(--a-success-strong));
141
155
  --timeline-item-icon-bg-accent: var(--timeline-icon-bg-accent, var(--a-accent-muted));
@@ -245,6 +259,10 @@ agent-reasoning-ui timeline-ui:not([orientation="horizontal"]),
245
259
  border-radius: 50%;
246
260
  background: var(--timeline-item-icon-bg);
247
261
  color: var(--timeline-item-icon-fg);
262
+ /* Subtle ring so the default (no-variant) chip reads as a marker even
263
+ when its muted fill blends with the surrounding surface; the colored
264
+ variants below restate background only, keeping this ring. */
265
+ border: 1px solid var(--timeline-item-icon-border);
248
266
  display: inline-flex;
249
267
  align-items: center;
250
268
  justify-content: center;
@@ -342,12 +360,12 @@ agent-reasoning-ui timeline-ui:not([orientation="horizontal"]),
342
360
  background: var(--timeline-item-subdot-bg);
343
361
  }
344
362
 
345
- /* ═══════ Toggle chevron (outcomes) ═══════ */
363
+ /* ═══════ Toggle caret (outcomes) ═══════ */
346
364
 
347
365
  :scope > [data-timeline-toggle] {
348
366
  position: absolute;
349
367
  inset-inline-end: 0;
350
- /* Place the chevron's icon-center on row 1's optical center.
368
+ /* Place the caret's icon-center on row 1's optical center.
351
369
  The button has --a-space-0-5 vertical padding, so the icon
352
370
  sits that much lower inside the box; we subtract that so the
353
371
  icon (not the box) lines up with the time-slot text center. */
@@ -372,7 +390,7 @@ agent-reasoning-ui timeline-ui:not([orientation="horizontal"]),
372
390
  color: var(--timeline-item-label-fg, var(--a-fg));
373
391
  }
374
392
 
375
- /* Reserve room so the time isn't overlapped by the chevron */
393
+ /* Reserve room so the time isn't overlapped by the caret */
376
394
  :scope:has(> [data-timeline-toggle]) [slot="time"] {
377
395
  margin-inline-end: var(--timeline-item-toggle-time-margin);
378
396
  }
@@ -19,11 +19,14 @@ toggle-option-ui:not([disabled]):hover {
19
19
 
20
20
  :scope {
21
21
  box-sizing: border-box;
22
- display: inline-flex;
22
+ display: flex;
23
23
  border: var(--toggle-group-border-width, var(--toggle-group-border-width-default)) solid var(--toggle-group-border-color, var(--toggle-group-border-color-default));
24
24
  border-radius: var(--toggle-group-radius, var(--toggle-group-radius-default));
25
25
  overflow: hidden;
26
26
  }
27
+
28
+ /* Display convention (ADR-0037): block-level by default; [inline] opts back to inline-level. */
29
+ :scope[inline] { display: inline-flex; }
27
30
  }
28
31
 
29
32
  @scope (toggle-option-ui) {
@@ -4,12 +4,15 @@
4
4
  }
5
5
 
6
6
  :scope {
7
- display: inline-flex;
7
+ display: flex;
8
8
  align-items: center;
9
9
  justify-content: center;
10
10
  line-height: 0;
11
11
  }
12
12
 
13
+ /* Display convention (ADR-0037): block-level by default; [inline] opts back to inline-level. */
14
+ :scope[inline] { display: inline-flex; }
15
+
13
16
  :scope[disabled] {
14
17
  pointer-events: none;
15
18
  }
@@ -65,6 +65,12 @@
65
65
  "Accordion"
66
66
  ],
67
67
  "slots": {
68
+ "actions": {
69
+ "description": "Per-row action buttons (rename / add / overflow menu), right-aligned and hover-revealed. A declarative `slot=\"actions\"` child is adopted into the auto-stamped row (FEEDBACK-89), so it sits inline in the row rather than wrapping below it."
70
+ },
71
+ "caret": {
72
+ "description": "Override slot for the expand/collapse caret. Auto-stamped as `<icon-ui slot=\"caret\" name=\"caret-right\">` (rotates on `[open]`, hidden for leaf nodes). Supply your own `slot=\"caret\"` child to customize — adopt-or-stamp moves a declarative caret into the row."
73
+ },
68
74
  "icon": {
69
75
  "description": "Override the leading [icon] glyph with a custom slotted element (custom icon-ui, folder open/closed glyph, file-type marker). Mutually exclusive with the [icon] attribute — slot child wins."
70
76
  }
@@ -47,6 +47,18 @@ slots:
47
47
  Override the leading [icon] glyph with a custom slotted element (custom
48
48
  icon-ui, folder open/closed glyph, file-type marker). Mutually exclusive
49
49
  with the [icon] attribute — slot child wins.
50
+ caret:
51
+ description: >-
52
+ Override slot for the expand/collapse caret. Auto-stamped as
53
+ `<icon-ui slot="caret" name="caret-right">` (rotates on `[open]`,
54
+ hidden for leaf nodes). Supply your own `slot="caret"` child to
55
+ customize — adopt-or-stamp moves a declarative caret into the row.
56
+ actions:
57
+ description: >-
58
+ Per-row action buttons (rename / add / overflow menu), right-aligned
59
+ and hover-revealed. A declarative `slot="actions"` child is adopted
60
+ into the auto-stamped row (FEEDBACK-89), so it sits inline in the row
61
+ rather than wrapping below it.
50
62
 
51
63
  a2ui:
52
64
  rules:
@@ -72,7 +84,7 @@ a2ui:
72
84
  - >-
73
85
  Nest further <tree-item-ui> in the default slot only — no
74
86
  <list-item-ui>, <nav-item-ui>, or arbitrary content inside a
75
- tree row. The chevron is auto-stamped when the row has nested
87
+ tree row. The caret is auto-stamped when the row has nested
76
88
  tree-item-ui children.
77
89
 
78
90
  keywords:
@@ -126,8 +126,8 @@
126
126
  "--tree-bg-selected": {
127
127
  "description": "Background color when selected"
128
128
  },
129
- "--tree-chevron-size": {
130
- "description": "Size of the collapse chevron icon"
129
+ "--tree-caret-size": {
130
+ "description": "Size of the collapse caret icon"
131
131
  },
132
132
  "--tree-duration": {
133
133
  "description": "Transition duration"
@@ -139,7 +139,7 @@
139
139
  "description": "Primary text color"
140
140
  },
141
141
  "--tree-fg-muted": {
142
- "description": "Muted text color (icons, chevrons)"
142
+ "description": "Muted text color (icons, carets)"
143
143
  },
144
144
  "--tree-focus-ring": {
145
145
  "description": "Focus ring box-shadow"
@@ -293,11 +293,17 @@ export class UITreeItem extends UIElement {
293
293
  row.setAttribute('slot', 'row');
294
294
  row.setAttribute('tabindex', '0');
295
295
 
296
- // Chevron
297
- const chevron = document.createElement('icon-ui');
298
- chevron.setAttribute('slot', 'chevron');
299
- chevron.setAttribute('name', 'caret-right');
300
- row.appendChild(chevron);
296
+ // Caret — adopt a declarative [slot="caret"] child if the consumer supplied
297
+ // one, else stamp the default. (Adopt-or-stamp; same pattern as actions.)
298
+ const declaredCaret = this.querySelector(':scope > [slot="caret"]');
299
+ if (declaredCaret) {
300
+ row.appendChild(declaredCaret);
301
+ } else {
302
+ const caret = document.createElement('icon-ui');
303
+ caret.setAttribute('slot', 'caret');
304
+ caret.setAttribute('name', 'caret-right');
305
+ row.appendChild(caret);
306
+ }
301
307
 
302
308
  // Icon
303
309
  if (this.icon) {
@@ -320,10 +326,19 @@ export class UITreeItem extends UIElement {
320
326
  if (this.badge) badgeEl.textContent = this.badge;
321
327
  row.appendChild(badgeEl);
322
328
 
323
- // Actions slot placeholder
324
- const actions = document.createElement('span');
325
- actions.setAttribute('slot', 'actions');
326
- row.appendChild(actions);
329
+ // Actions — adopt pre-existing declarative [slot="actions"] children into
330
+ // the row (FEEDBACK-89) so per-row action buttons land in the styled,
331
+ // hover-revealed actions area; else stamp an empty placeholder. The host
332
+ // #onClick already excludes [slot="actions"] * from row selection, so
333
+ // adoption is click-safe.
334
+ const declaredActions = this.querySelectorAll(':scope > [slot="actions"]');
335
+ if (declaredActions.length) {
336
+ for (const a of declaredActions) row.appendChild(a);
337
+ } else {
338
+ const actions = document.createElement('span');
339
+ actions.setAttribute('slot', 'actions');
340
+ row.appendChild(actions);
341
+ }
327
342
 
328
343
  this.prepend(row);
329
344
  }
@@ -7,7 +7,7 @@
7
7
  --tree-row-gap-default: var(--a-space-1);
8
8
  --tree-actions-gap-default: var(--a-space-0-5);
9
9
  --tree-indent-default: var(--a-space-4);
10
- --tree-chevron-size-default: var(--a-space-2);
10
+ --tree-caret-size-default: var(--a-space-2);
11
11
  --tree-icon-size-default: var(--a-space-3);
12
12
 
13
13
  /* ── Typography ── */
@@ -47,7 +47,7 @@
47
47
  --tree-item-row-gap: var(--tree-row-gap, var(--a-space-1));
48
48
  --tree-item-actions-gap: var(--tree-actions-gap, var(--a-space-0-5));
49
49
  --tree-item-indent: var(--tree-indent, var(--a-space-4));
50
- --tree-item-chevron-size: var(--tree-chevron-size, var(--a-space-2));
50
+ --tree-item-caret-size: var(--tree-caret-size, var(--a-space-2));
51
51
  --tree-item-icon-size: var(--tree-icon-size, var(--a-space-3));
52
52
 
53
53
  --tree-item-fg: var(--tree-fg, var(--a-fg));
@@ -92,21 +92,21 @@
92
92
 
93
93
  /* :scope[selected] rules moved outside @scope — see tree-ui Safari note at end of file. */
94
94
 
95
- /* ── Chevron ── */
96
- [slot="chevron"] {
97
- --a-icon-size: var(--tree-chevron-size, var(--tree-chevron-size-default));
95
+ /* ── Caret ── */
96
+ [slot="caret"] {
97
+ --a-icon-size: var(--tree-caret-size, var(--tree-caret-size-default));
98
98
  flex-shrink: 0;
99
99
  color: var(--tree-fg-muted, var(--tree-fg-muted-default));
100
100
  transition: transform var(--tree-duration, var(--tree-duration-default)) var(--tree-easing, var(--tree-easing-default));
101
101
  transform: rotate(90deg);
102
102
  }
103
103
 
104
- :scope:not([open]) > [slot="row"] > [slot="chevron"] {
104
+ :scope:not([open]) > [slot="row"] > [slot="caret"] {
105
105
  transform: rotate(0deg);
106
106
  }
107
107
 
108
- /* Hide chevron for leaf nodes */
109
- :scope:not(:has(> tree-item-ui)) > [slot="row"] > [slot="chevron"] {
108
+ /* Hide caret for leaf nodes */
109
+ :scope:not(:has(> tree-item-ui)) > [slot="row"] > [slot="caret"] {
110
110
  visibility: hidden;
111
111
  }
112
112