@adia-ai/web-components 0.8.31 → 0.8.33

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 (164) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/MIGRATION.md +136 -0
  3. package/components/accordion/accordion-item.a2ui.json +8 -0
  4. package/components/accordion/accordion-item.yaml +6 -0
  5. package/components/accordion/accordion.css +3 -1
  6. package/components/accordion/accordion.d.ts +2 -2
  7. package/components/action-list/action-list.class.js +6 -2
  8. package/components/action-list/action-list.d.ts +1 -1
  9. package/components/agent-artifact/agent-artifact.d.ts +1 -1
  10. package/components/agent-feedback-bar/agent-feedback-bar.d.ts +2 -2
  11. package/components/agent-questions/agent-questions.d.ts +1 -1
  12. package/components/agent-reasoning/agent-reasoning.d.ts +3 -3
  13. package/components/agent-suggestions/agent-suggestions.d.ts +1 -1
  14. package/components/agent-trace/agent-trace.d.ts +1 -1
  15. package/components/alert/alert.a2ui.json +3 -5
  16. package/components/alert/alert.class.js +3 -3
  17. package/components/alert/alert.css +5 -12
  18. package/components/alert/alert.d.ts +4 -4
  19. package/components/alert/alert.yaml +6 -5
  20. package/components/anchor-bar/anchor-bar.d.ts +2 -2
  21. package/components/avatar/avatar.class.js +0 -1
  22. package/components/avatar/avatar.css +16 -2
  23. package/components/badge/badge.a2ui.json +6 -9
  24. package/components/badge/badge.class.js +4 -4
  25. package/components/badge/badge.css +134 -74
  26. package/components/badge/badge.d.ts +34 -12
  27. package/components/badge/badge.examples.md +1 -1
  28. package/components/badge/badge.yaml +37 -16
  29. package/components/breadcrumb/breadcrumb.css +15 -3
  30. package/components/button/button.a2ui.json +1 -1
  31. package/components/button/button.css +4 -2
  32. package/components/button/button.d.ts +3 -3
  33. package/components/button/button.yaml +1 -1
  34. package/components/calendar-grid/calendar-grid.d.ts +2 -2
  35. package/components/card/card.css +21 -0
  36. package/components/card/card.yaml +10 -1
  37. package/components/chart/chart.class.js +8 -4
  38. package/components/chart/chart.d.ts +4 -4
  39. package/components/chart-legend/chart-legend.d.ts +1 -1
  40. package/components/chat-thread/chat-input.css +1 -1
  41. package/components/chat-thread/chat-thread.d.ts +2 -2
  42. package/components/check/check.css +3 -3
  43. package/components/command/command.d.ts +2 -2
  44. package/components/context-menu/context-menu.d.ts +3 -3
  45. package/components/datetime-picker/datetime-picker.d.ts +5 -5
  46. package/components/demo-toggle/demo-toggle.d.ts +1 -1
  47. package/components/drawer/drawer.d.ts +2 -2
  48. package/components/heatmap/heatmap.d.ts +5 -5
  49. package/components/index.js +1 -0
  50. package/components/inline-edit/inline-edit.d.ts +4 -4
  51. package/components/input/input.a2ui.json +4 -1
  52. package/components/input/input.css +18 -0
  53. package/components/input/input.yaml +11 -1
  54. package/components/integration-card/integration-card.class.js +11 -2
  55. package/components/integration-card/integration-card.d.ts +4 -4
  56. package/components/link/link.d.ts +1 -1
  57. package/components/list/list.class.js +22 -7
  58. package/components/list/list.d.ts +1 -1
  59. package/components/list-window/list-window.d.ts +5 -5
  60. package/components/mark/mark.a2ui.json +6 -7
  61. package/components/mark/mark.class.js +1 -1
  62. package/components/mark/mark.css +7 -4
  63. package/components/mark/mark.d.ts +2 -2
  64. package/components/mark/mark.examples.md +0 -4
  65. package/components/mark/mark.yaml +16 -6
  66. package/components/menu/menu.css +2 -2
  67. package/components/menu/menu.d.ts +1 -1
  68. package/components/modal/modal.d.ts +1 -1
  69. package/components/nav/nav.css +8 -0
  70. package/components/nav/nav.d.ts +1 -1
  71. package/components/nav-group/nav-group.css +58 -9
  72. package/components/nav-group/nav-group.d.ts +1 -1
  73. package/components/nav-item/nav-item.css +7 -4
  74. package/components/nav-item/nav-item.d.ts +1 -1
  75. package/components/noodles/noodles.d.ts +3 -3
  76. package/components/option-card/option-card.css +1 -1
  77. package/components/option-card/option-card.examples.md +1 -1
  78. package/components/pagination/pagination.d.ts +1 -1
  79. package/components/pane/pane.d.ts +1 -1
  80. package/components/password-strength/password-strength.d.ts +1 -1
  81. package/components/popover/popover.a2ui.json +5 -0
  82. package/components/popover/popover.class.js +9 -4
  83. package/components/popover/popover.css +5 -2
  84. package/components/popover/popover.yaml +8 -0
  85. package/components/preview/preview.examples.md +1 -1
  86. package/components/radio/radio.a2ui.json +4 -3
  87. package/components/radio/radio.css +2 -2
  88. package/components/radio/radio.yaml +7 -14
  89. package/components/radio-group/radio-group.a2ui.json +118 -0
  90. package/components/radio-group/radio-group.class.js +75 -0
  91. package/components/radio-group/radio-group.css +31 -0
  92. package/components/radio-group/radio-group.d.ts +20 -0
  93. package/components/radio-group/radio-group.examples.md +37 -0
  94. package/components/radio-group/radio-group.js +17 -0
  95. package/components/radio-group/radio-group.yaml +114 -0
  96. package/components/stat/stat.css +12 -8
  97. package/components/stat/stat.js +66 -1
  98. package/components/stat/stat.yaml +3 -0
  99. package/components/stepper/stepper-item.a2ui.json +5 -0
  100. package/components/stepper/stepper-item.yaml +11 -0
  101. package/components/stepper/stepper.a2ui.json +38 -3
  102. package/components/stepper/stepper.class.js +199 -2
  103. package/components/stepper/stepper.css +30 -0
  104. package/components/stepper/stepper.d.ts +28 -1
  105. package/components/stepper/stepper.yaml +76 -2
  106. package/components/stream/stream.d.ts +3 -3
  107. package/components/swatch/swatch.d.ts +1 -1
  108. package/components/swiper/swiper.d.ts +3 -3
  109. package/components/table/cell-types.js +12 -1
  110. package/components/table/table.a2ui.json +2 -2
  111. package/components/table/table.d.ts +7 -7
  112. package/components/table/table.yaml +9 -1
  113. package/components/table-toolbar/table-toolbar.class.js +1 -1
  114. package/components/table-toolbar/table-toolbar.d.ts +4 -4
  115. package/components/tabs/tabs.d.ts +1 -1
  116. package/components/tag/tag.a2ui.json +5 -5
  117. package/components/tag/tag.class.js +1 -1
  118. package/components/tag/tag.css +149 -71
  119. package/components/tag/tag.d.ts +21 -11
  120. package/components/tag/tag.examples.md +4 -4
  121. package/components/tag/tag.yaml +35 -13
  122. package/components/tags-input/tags-input.d.ts +4 -4
  123. package/components/timeline/timeline.d.ts +1 -1
  124. package/components/toast/toast.a2ui.json +2 -4
  125. package/components/toast/toast.css +0 -12
  126. package/components/toast/toast.yaml +3 -4
  127. package/components/toc/toc.d.ts +1 -1
  128. package/components/toggle-group/toggle-group.d.ts +1 -1
  129. package/components/toggle-scheme/toggle-scheme.d.ts +1 -1
  130. package/components/tour/tour.d.ts +4 -4
  131. package/components/tree/tree.d.ts +1 -1
  132. package/core/icons-phosphor.js +129 -71
  133. package/core/provider.js +84 -23
  134. package/dist/host.min.css +1 -1
  135. package/dist/host.sheet.js +1 -1
  136. package/dist/theme-provider.min.js +1 -1
  137. package/dist/web-components.min.css +1 -1
  138. package/dist/web-components.min.js +90 -88
  139. package/dist/web-components.sheet.js +1 -1
  140. package/package.json +1 -1
  141. package/patterns/admin-keys-and-export/admin-keys-and-export.examples.html +1 -1
  142. package/patterns/agent-memory/agent-memory.examples.html +5 -5
  143. package/patterns/agent-prompt-library/agent-prompt-library.examples.html +2 -2
  144. package/patterns/audit-log/audit-log.examples.js +12 -5
  145. package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.js +4 -1
  146. package/patterns/changelog-feed/changelog-feed.examples.html +1 -1
  147. package/patterns/comments-and-collaboration/comments-and-collaboration.examples.html +1 -1
  148. package/patterns/conversion-funnel/conversion-funnel.examples.html +1 -1
  149. package/patterns/diff-review/diff-review.examples.html +9 -9
  150. package/patterns/filter-bar/filter-bar.examples.html +12 -8
  151. package/patterns/filter-bar/filter-bar.examples.js +4 -1
  152. package/patterns/marketing-engagement/marketing-engagement.examples.html +1 -1
  153. package/patterns/notifications-bell-and-digest/notifications-bell-and-digest.examples.html +1 -1
  154. package/patterns/permissions-matrix/permissions-matrix.examples.html +1 -1
  155. package/patterns/permissions-sharing/permissions-sharing.examples.html +1 -1
  156. package/patterns/profile-public-and-verification/profile-public-and-verification.examples.html +2 -2
  157. package/patterns/record-detail-drawer/record-detail-drawer.examples.html +1 -1
  158. package/patterns/retention-cohort/retention-cohort.examples.html +1 -1
  159. package/patterns/retention-cohort/retention-cohort.examples.js +4 -1
  160. package/patterns/system-banners/system-banners.examples.html +3 -3
  161. package/styles/api/sizing-fallback.css +32 -0
  162. package/styles/api/sizing.css +7 -3
  163. package/styles/components.css +1 -0
  164. package/styles/tokens.css +1 -0
@@ -14,6 +14,22 @@ description: |
14
14
  event. Use badge-ui for status flags (Beta / New / Deprecated),
15
15
  notification counts, and unread indicators; use tag-ui for filter
16
16
  chips or user-managed labels.
17
+
18
+ REGISTER (gh issue 845; weight revised gh#866): badge and tag share
19
+ the same geometry family — pill radius (`--a-radius-full`) and
20
+ padding token rhythm are identical, so shape is NOT how you tell
21
+ them apart. Typography is: badge is UPPERCASE, mono (kbd-ui's
22
+ `--a-font-family-code` stack), medium weight, wide-tracked
23
+ (`--a-font-tracking-wider`, 0.06em), tabular numerals for counts.
24
+ Tag stays proportional sentence-case, also weight 500 — weight no
25
+ longer distinguishes the two (gh#866); case + tracking + mono carry
26
+ the register split now. Neither component exposes a prop that
27
+ crosses this line; the registers cannot be mixed à la carte.
28
+ Remaining distinctions: badge is non-interactive and carries the
29
+ semantic-color voice (status tones ARE the message, solid by
30
+ default — gh#864); tag is interactive/removable, muted/tinted by
31
+ default for family variants (gh#864), solid by default for the
32
+ family-less variant (gh#794, unaffected by gh#864).
17
33
  # Per ADR-0027 — primitives that programmatically create other primitives
18
34
  # do NOT auto-import them. Consumer (or demo shell) must explicitly import.
19
35
  composes:
@@ -35,7 +51,7 @@ props:
35
51
  status:
36
52
  description: Semantic status shorthand. Auto-maps to variant and sets text.
37
53
  `new` → success "New"; `updated` → info "Updated"; `deprecated` → warning
38
- "Deprecated"; `removed` → danger "Removed"; `beta` → accent "Beta".
54
+ "Deprecated"; `removed` → danger "Removed"; `beta` → primary "Beta".
39
55
  Use `variant` + `text` directly for custom chips.
40
56
  type: string
41
57
  default: ""
@@ -60,30 +76,33 @@ props:
60
76
  default: default
61
77
  enum:
62
78
  - default
63
- - accent
64
79
  - info
65
80
  - success
66
81
  - warning
67
82
  - danger
68
83
  - primary
69
- - muted
70
- - neutral
71
84
  tone:
72
85
  description: |
73
- Fill style — orthogonal to [variant]. Badge defaults to `muted`
74
- (quiet metadata is the primitive's identity counts, IDs, status
75
- pills in dense rows). Three values:
76
- - `muted` (default) — tinted bg + scheme-paired text. Same as
77
- the existing family-variant rules.
78
- - `solid` saturated bg + on-strong text. Use for hero badges
79
- where the badge IS the state (e.g. a single inline error). The
80
- existing `primary` variant is a shortcut for accent + solid.
86
+ Fill style — orthogonal to [variant]. Badge defaults to `solid`
87
+ (gh#864 badge and tag were swapped relative to their reported
88
+ default formulas; this restores badge's own). Three values:
89
+ - `solid` (default) — saturated bg + on-strong text. The badge
90
+ IS the state (status stamp). `primary` is badge's sole
91
+ brand-emphasis variant (`accent` retired, gh#874).
92
+ - `muted` tinted bg + scheme-paired text. Use for quiet
93
+ metadata counts, IDs, status pills in dense rows. Family-tinted
94
+ `muted` (e.g. `variant="info" tone="muted"`) shares `<tag-ui>`'s
95
+ `[tone="muted"]` shape; family-less `tone="muted"` does NOT
96
+ (gh#873) — it renders the stronger `neutral-low` fill, while
97
+ `<tag-ui>`'s family-less muted stays on the lighter
98
+ `--a-bg-muted` pair (gh#778 dense-list rationale).
81
99
  - `outline` — transparent bg + family-colored border + family-
82
100
  colored text. Lightest visual weight; good in dense data rows.
83
- Vocabulary mirrors `<tag-ui>` (which defaults to solid, given its
84
- different role as filter / autocomplete chip).
101
+ Vocabulary mirrors `<tag-ui>` (which now defaults to `muted` for
102
+ its family variants, gh#864 — different role as filter /
103
+ autocomplete chip).
85
104
  type: string
86
- default: muted
105
+ default: solid
87
106
  enum:
88
107
  - muted
89
108
  - solid
@@ -100,6 +119,8 @@ a2ui:
100
119
  rules:
101
120
  - rule: 'Use for small status/count labels attached to another element (notification counts, status pills, version tags).'
102
121
  reason: 'Different from <tag-ui> which is for free-standing taxonomy/filter labels.'
122
+ - rule: 'A bare Badge (no [tone]) renders a solid, saturated status stamp. Set [tone="muted"] for the quieter tinted look on dense metadata rows.'
123
+ reason: 'Badge defaults to solid (gh#864); does not need [tone] set explicitly to read as a status stamp.'
103
124
  - rule: 'Place adjacent to or absolutely-positioned over the badged element, not inside it.'
104
125
  reason: 'Light-DOM positioning; do not nest inside the badged element''s slot.'
105
126
  - rule: 'For dismissable taxonomy labels (filter chips, selected items in multi-select), use <tag-ui> instead.'
@@ -130,7 +151,7 @@ examples:
130
151
  "component": "Badge",
131
152
  "icon": "dot",
132
153
  "text": "Organic",
133
- "variant": "accent",
154
+ "variant": "primary",
134
155
  "size": "sm"
135
156
  },
136
157
  {
@@ -42,10 +42,17 @@
42
42
  user-select: none;
43
43
  }
44
44
 
45
- /* Items */
45
+ /* Items — plain-text crumbs stay a normal inline-block box so
46
+ `text-overflow: ellipsis` actually paints: per the CSS Overflow
47
+ spec, the ellipsis glyph never renders on a flex container's own
48
+ box, only on a block box with a single fragmented inline
49
+ formatting context. Icon-leading crumbs opt back into inline-flex
50
+ below (scoped via :has(), see the icon-leading rule) so icon
51
+ baseline-alignment survives without reinstating the incompatible
52
+ flex+ellipsis combination on text-only items. */
46
53
  [data-item] {
47
- display: inline-flex;
48
- align-items: center;
54
+ display: inline-block;
55
+ vertical-align: middle;
49
56
  overflow: hidden;
50
57
  text-overflow: ellipsis;
51
58
  white-space: nowrap;
@@ -53,6 +60,11 @@
53
60
  color: var(--breadcrumb-fg);
54
61
  }
55
62
 
63
+ [data-item]:has(> icon-ui) {
64
+ display: inline-flex;
65
+ align-items: center;
66
+ }
67
+
56
68
  /* Links */
57
69
  a[data-item] {
58
70
  text-decoration: none;
@@ -27,7 +27,6 @@
27
27
  "type": "string",
28
28
  "enum": [
29
29
  "default",
30
- "accent",
31
30
  "info",
32
31
  "success",
33
32
  "warning",
@@ -83,6 +82,7 @@
83
82
  "enum": [
84
83
  "default",
85
84
  "solid",
85
+ "tonal",
86
86
  "outline",
87
87
  "ghost",
88
88
  "primary"
@@ -252,8 +252,10 @@ button-ui[variant="ghost"][color="warning"]:not([disabled]):hover { --button-f
252
252
  /* ── Color (semantic axis) ──
253
253
  `[color]` carries semantic intent; `variant` carries visual style.
254
254
  Together they compose: `<button-ui variant="solid" color="danger">`
255
- vs `<button-ui variant="outline" color="danger">`.
256
- `variant="danger"` is a deprecated alias migrated to color via JS. */
255
+ vs `<button-ui variant="outline" color="danger">`. (Corrected,
256
+ variant-color-tone LLD §2: no `variant="danger"` `color` alias
257
+ JS exists in button.class.js — the prior comment claiming one was
258
+ stale.) */
257
259
  :scope[color="danger"] {
258
260
  --button-bg: var(--button-bg-danger);
259
261
  --button-fg: var(--button-fg-danger);
@@ -18,7 +18,7 @@ export class UIButton extends UIElement {
18
18
  /** HTML button type (button, submit, reset) */
19
19
  type: string;
20
20
  /** Semantic intent — composes with [variant]. `<button-ui variant="solid" color="danger">` = filled destructive action; `<button-ui variant="outline" color="success">` = outlined success affordance. */
21
- color: 'default' | 'accent' | 'info' | 'success' | 'warning' | 'danger';
21
+ color: 'default' | 'info' | 'success' | 'warning' | 'danger';
22
22
  /** Disables interaction and prevents click/press events */
23
23
  disabled: boolean;
24
24
  /** Leading Phosphor icon name (rendered before text). */
@@ -33,12 +33,12 @@ export class UIButton extends UIElement {
33
33
  textContent: string;
34
34
  /** Visual style — `solid` (default fill), `tonal` (soft container fill with in-family text — the emphasis tier between solid and outline; default rides the primary family), `outline`, `ghost`. `default` / `primary` are aliases of `solid`. Style is independent of semantic intent — to express destructive / success / info / warning intent, set [color="…"] alongside (`variant="tonal" color="danger"` = the danger family's container fill). There is deliberately no `variant="secondary"`: `secondary` names a color family in this system, and an unset variant already IS the secondary-rank look (compose-constitution); `tonal` is the named tier for a softer-than-solid filled action.
35
35
  For **inline navigation** (Terms of Service, Privacy Policy, footer links, "Sign in" / "Sign up" cross-page affordances) use `<link-ui>` instead — it carries proper `<a href>` semantics, keyboard handling (Enter only, no Space), middle-click open-new-tab, and screen-reader announces "link" instead of "button". Mixing navigation and action affordances under the same primitive is a category error fixed at this junction. */
36
- variant: 'default' | 'solid' | 'outline' | 'ghost' | 'primary';
36
+ variant: 'default' | 'solid' | 'tonal' | 'outline' | 'ghost' | 'primary';
37
37
 
38
+ addEventListener(type: 'press', listener: (ev: ButtonPressEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
38
39
  addEventListener<K extends keyof HTMLElementEventMap>(
39
40
  type: K,
40
41
  listener: (this: UIButton, ev: HTMLElementEventMap[K]) => unknown,
41
42
  options?: boolean | AddEventListenerOptions,
42
43
  ): void;
43
- addEventListener(type: 'press', listener: (ev: ButtonPressEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
44
44
  }
@@ -83,6 +83,7 @@ props:
83
83
  enum:
84
84
  - default
85
85
  - solid
86
+ - tonal
86
87
  - outline
87
88
  - ghost
88
89
  - primary
@@ -95,7 +96,6 @@ props:
95
96
  default: ""
96
97
  enum:
97
98
  - default
98
- - accent
99
99
  - info
100
100
  - success
101
101
  - warning
@@ -33,11 +33,11 @@ export class UICalendarGrid extends UIElement {
33
33
  /** Selected date in ISO format (YYYY-MM-DD). */
34
34
  value: string;
35
35
 
36
+ addEventListener(type: 'change', listener: (ev: CalendarGridChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
37
+ addEventListener(type: 'input', listener: (ev: CalendarGridInputEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
36
38
  addEventListener<K extends keyof HTMLElementEventMap>(
37
39
  type: K,
38
40
  listener: (this: UICalendarGrid, ev: HTMLElementEventMap[K]) => unknown,
39
41
  options?: boolean | AddEventListenerOptions,
40
42
  ): void;
41
- addEventListener(type: 'change', listener: (ev: CalendarGridChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
42
- addEventListener(type: 'input', listener: (ev: CalendarGridInputEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
43
43
  }
@@ -364,6 +364,27 @@
364
364
  min-height: 0;
365
365
  }
366
366
 
367
+ /* Chrome-drop for bleed tables (gh#796) — a table-ui directly inside a
368
+ [bleed] section renders chromeless automatically, matching [raw]'s
369
+ reset (table.css:167-171: background none, box-shadow none,
370
+ border-radius 0). table.yaml's own raw/frameless docs already name
371
+ `<card-ui><section bleed>` as the canonical case for dropping a
372
+ table's chrome (the card owns the frame); 14 existing instances across
373
+ the repo carried neither attribute (gh#796 findings), so defaulting it
374
+ here removes an attribute nobody remembers to add rather than relying
375
+ on every author opting in. An explicit [raw] or [frameless] on the
376
+ instance still wins — `:not([raw]):not([frameless])` only lets this
377
+ rule fire in their absence.
378
+ Scoped to [bleed] sections only, not every card section: a plain
379
+ `<section>` keeps its own padding/margin, so a table's own frame there
380
+ reads as an intentional nested surface (see table.examples.html's own
381
+ demo cards, which use plain sections on purpose), not a doubled edge. */
382
+ & > section[bleed] > table-ui:not([raw]):not([frameless]) {
383
+ background: none;
384
+ box-shadow: none;
385
+ border-radius: 0;
386
+ }
387
+
367
388
  /* ═══════ Footer ═══════
368
389
  Margin-based inset. Flex row for actions.
369
390
  [divider] adds top border.
@@ -132,11 +132,20 @@ a2ui:
132
132
  - The card's <header> grid activates only for DIRECT-child slotted elements. If you need an icon column, place the icon
133
133
  element (avatar-ui, icon-ui) directly in the header with slot="icon" — not inside a wrapper.
134
134
  - 'Heading slot accepts inline badges/metadata: <span slot="heading"><text-ui strong>Title</text-ui><badge-ui text="New"
135
- variant="accent"></badge-ui></span> renders title + badge on one row.'
135
+ variant="primary"></badge-ui></span> renders title + badge on one row.'
136
136
  - Description slot also accepts bare <p> or <small> elements as siblings of the heading — they participate in the grid's
137
137
  row 2 without needing slot="description".
138
138
  - Multiple <section> siblings are allowed and stack vertically. [bleed] on a section removes its margin for edge-to-edge
139
139
  content (tables, charts); [padding] adds a canvas-scrim background for hero regions.
140
+ - >-
141
+ When neither [raw] nor [frameless] is present, a <table-ui> placed
142
+ directly inside a <section bleed> automatically renders chromeless
143
+ (background/box-shadow/border-radius reset; card.css, gh#796): the card
144
+ already owns the frame, so the table doesn't need its own. table.yaml's
145
+ [raw]/[frameless] docs describe the same composition; an explicit [raw] or
146
+ [frameless] on the table still applies and is honored. A plain
147
+ (non-bleed) <section> is unaffected — its own padding keeps a nested
148
+ table's frame legible as an intentional nested surface.
140
149
  - '[grow] on a section makes it fill the card''s remaining height (flex:1 — same semantics as col-ui[grow]/row-ui[grow]).
141
150
  The card becomes a flex column so the section absorbs the leftover space after the header/footer. Requires the card to
142
151
  have a definite height (inline height, grid row track, or flex parent) — a content-sized card has nothing to fill.
@@ -1366,9 +1366,13 @@ export class UIChart extends UIElement {
1366
1366
  const data = this.#data;
1367
1367
  const yKey = this.#yKeys()[0] || this.y;
1368
1368
  const vals = data.map(d => +(d[yKey] ?? 0));
1369
- const maxVal = Math.max(...vals) || 1;
1370
- const minVal = Math.min(...vals);
1371
- const range = maxVal - minVal || 1;
1369
+
1370
+ // Fixed 0-floor domain via niceScale — the same convention every other
1371
+ // renderer in this file uses (#renderBar, #renderLine, #renderScatter,
1372
+ // etc). A flat/single-point series then plots against a real baseline
1373
+ // instead of collapsing to the bottom edge (gh#850).
1374
+ const ticks = niceScale(0, Math.max(...vals), 5);
1375
+ const maxVal = ticks[ticks.length - 1];
1372
1376
 
1373
1377
  // Use the actual container dims so the aspect ratio matches what
1374
1378
  // the CSS allocates (otherwise the fixed 120×32 viewBox gets
@@ -1385,7 +1389,7 @@ export class UIChart extends UIElement {
1385
1389
  const labels = data.map(d => d[this.x] ?? '');
1386
1390
  const points = vals.map((v, i) => ({
1387
1391
  x: step * i,
1388
- y: h - ((v - minVal) / range) * (h - padY * 2) - padY,
1392
+ y: h - (maxVal ? (v / maxVal) * (h - padY * 2) : 0) - padY,
1389
1393
  v,
1390
1394
  label: labels[i],
1391
1395
  }));
@@ -49,13 +49,13 @@ export class UIChart extends UIElement {
49
49
  /** Y-axis key(s), comma-separated for multi-series */
50
50
  y: string;
51
51
 
52
+ addEventListener(type: 'chart-hover', listener: (ev: ChartHoverEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
53
+ addEventListener(type: 'chart-leave', listener: (ev: ChartLeaveEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
54
+ addEventListener(type: 'chart-select', listener: (ev: ChartSelectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
55
+ addEventListener(type: 'legend-update', listener: (ev: ChartLegendUpdateEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
52
56
  addEventListener<K extends keyof HTMLElementEventMap>(
53
57
  type: K,
54
58
  listener: (this: UIChart, ev: HTMLElementEventMap[K]) => unknown,
55
59
  options?: boolean | AddEventListenerOptions,
56
60
  ): void;
57
- addEventListener(type: 'chart-hover', listener: (ev: ChartHoverEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
58
- addEventListener(type: 'chart-leave', listener: (ev: ChartLeaveEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
59
- addEventListener(type: 'chart-select', listener: (ev: ChartSelectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
60
- addEventListener(type: 'legend-update', listener: (ev: ChartLegendUpdateEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
61
61
  }
@@ -37,10 +37,10 @@ export class UIChartLegend extends UIElement {
37
37
  /** When set, rows are non-interactive <span>s (no click, no toggle). */
38
38
  static: boolean;
39
39
 
40
+ addEventListener(type: 'toggle', listener: (ev: ChartLegendToggleEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
40
41
  addEventListener<K extends keyof HTMLElementEventMap>(
41
42
  type: K,
42
43
  listener: (this: UIChartLegend, ev: HTMLElementEventMap[K]) => unknown,
43
44
  options?: boolean | AddEventListenerOptions,
44
45
  ): void;
45
- addEventListener(type: 'toggle', listener: (ev: ChartLegendToggleEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
46
46
  }
@@ -6,7 +6,7 @@
6
6
  Two rules — one on the host (paint the hover affordance), one on the
7
7
  inner textarea-ui's [slot="text"] (suppress the textarea-ui hover
8
8
  bg/border/color so it doesn't compound with the host's). */
9
- chat-input-ui:not([disabled]):hover {
9
+ chat-input-ui:not([disabled]):hover:not(:focus-within) {
10
10
  background: var(--chat-input-bg-hover);
11
11
  border-color: var(--chat-input-border-hover);
12
12
  color: var(--chat-input-fg-hover);
@@ -32,12 +32,12 @@ export class UIChatThread extends UIElement {
32
32
  /** Component property: streaming. */
33
33
  streaming: boolean;
34
34
 
35
+ addEventListener(type: 'submit', listener: (ev: ChatThreadSubmitEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
35
36
  addEventListener<K extends keyof HTMLElementEventMap>(
36
37
  type: K,
37
38
  listener: (this: UIChatThread, ev: HTMLElementEventMap[K]) => unknown,
38
39
  options?: boolean | AddEventListenerOptions,
39
40
  ): void;
40
- addEventListener(type: 'submit', listener: (ev: ChatThreadSubmitEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
41
41
  }
42
42
 
43
43
  export interface ChatInputSubmitEventDetail {
@@ -71,10 +71,10 @@ hides the model picker.
71
71
  /** Textarea placeholder. Defaults to "Type a message...". */
72
72
  placeholder: string;
73
73
 
74
+ addEventListener(type: 'submit', listener: (ev: ChatInputSubmitEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
74
75
  addEventListener<K extends keyof HTMLElementEventMap>(
75
76
  type: K,
76
77
  listener: (this: UIChatInput, ev: HTMLElementEventMap[K]) => unknown,
77
78
  options?: boolean | AddEventListenerOptions,
78
79
  ): void;
79
- addEventListener(type: 'submit', listener: (ev: ChatInputSubmitEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
80
80
  }
@@ -3,12 +3,12 @@
3
3
  For [checked] / [indeterminate], when JS toggles the attribute off,
4
4
  Safari leaves the visual stuck — selectors moved out as plain
5
5
  `tagname[attr]` rules. See docs/BROWSER-COMPAT.md §3a. */
6
- check-ui:not([disabled]):hover [slot="box"] {
6
+ check-ui:not([disabled]):hover:not(:focus-visible) [slot="box"] {
7
7
  border-color: var(--check-border-hover);
8
8
  background: var(--check-bg-hover);
9
9
  }
10
- check-ui[checked]:not([disabled]):hover [slot="box"],
11
- check-ui[indeterminate]:not([disabled]):hover [slot="box"] {
10
+ check-ui[checked]:not([disabled]):hover:not(:focus-visible) [slot="box"],
11
+ check-ui[indeterminate]:not([disabled]):hover:not(:focus-visible) [slot="box"] {
12
12
  background: var(--check-bg-checked-hover);
13
13
  border-color: var(--check-bg-checked-hover);
14
14
  }
@@ -38,11 +38,11 @@ export class UICommand extends UIElement {
38
38
  /** Placeholder text for the search input */
39
39
  placeholder: string;
40
40
 
41
+ addEventListener(type: 'dismiss', listener: (ev: CommandDismissEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
42
+ addEventListener(type: 'select', listener: (ev: CommandSelectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
41
43
  addEventListener<K extends keyof HTMLElementEventMap>(
42
44
  type: K,
43
45
  listener: (this: UICommand, ev: HTMLElementEventMap[K]) => unknown,
44
46
  options?: boolean | AddEventListenerOptions,
45
47
  ): void;
46
- addEventListener(type: 'dismiss', listener: (ev: CommandDismissEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
47
- addEventListener(type: 'select', listener: (ev: CommandSelectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
48
48
  }
@@ -59,12 +59,12 @@ export class UIContextMenu extends UIElement {
59
59
  /** Programmatic open state. Set true to open at target center. */
60
60
  open: boolean;
61
61
 
62
+ addEventListener(type: 'context-menu-close', listener: (ev: ContextMenuCloseEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
63
+ addEventListener(type: 'context-menu-open', listener: (ev: ContextMenuOpenEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
64
+ addEventListener(type: 'context-menu-select', listener: (ev: ContextMenuSelectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
62
65
  addEventListener<K extends keyof HTMLElementEventMap>(
63
66
  type: K,
64
67
  listener: (this: UIContextMenu, ev: HTMLElementEventMap[K]) => unknown,
65
68
  options?: boolean | AddEventListenerOptions,
66
69
  ): void;
67
- addEventListener(type: 'context-menu-close', listener: (ev: ContextMenuCloseEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
68
- addEventListener(type: 'context-menu-open', listener: (ev: ContextMenuOpenEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
69
- addEventListener(type: 'context-menu-select', listener: (ev: ContextMenuSelectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
70
70
  }
@@ -75,14 +75,14 @@ document locale. `h12` forces a 12-hour cycle with an AM/PM
75
75
  /** Current value as ISO 8601 datetime ("YYYY-MM-DDTHH:mm" or "YYYY-MM-DDTHH:mm:ss"). Empty when no value selected. */
76
76
  value: string;
77
77
 
78
- addEventListener<K extends keyof HTMLElementEventMap>(
79
- type: K,
80
- listener: (this: UIDatetimePicker, ev: HTMLElementEventMap[K]) => unknown,
81
- options?: boolean | AddEventListenerOptions,
82
- ): void;
83
78
  addEventListener(type: 'change', listener: (ev: DatetimePickerChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
84
79
  addEventListener(type: 'close', listener: (ev: DatetimePickerCloseEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
85
80
  addEventListener(type: 'input', listener: (ev: DatetimePickerInputEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
86
81
  addEventListener(type: 'invalid', listener: (ev: DatetimePickerInvalidEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
87
82
  addEventListener(type: 'open', listener: (ev: DatetimePickerOpenEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
83
+ addEventListener<K extends keyof HTMLElementEventMap>(
84
+ type: K,
85
+ listener: (this: UIDatetimePicker, ev: HTMLElementEventMap[K]) => unknown,
86
+ options?: boolean | AddEventListenerOptions,
87
+ ): void;
88
88
  }
@@ -29,10 +29,10 @@ export class UIDemoToggle extends UIElement {
29
29
  /** Current toggle state ("on" | "off"). Reflected as data-state on the host. */
30
30
  state: '' | 'on' | 'off';
31
31
 
32
+ addEventListener(type: 'change', listener: (ev: DemoToggleChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
32
33
  addEventListener<K extends keyof HTMLElementEventMap>(
33
34
  type: K,
34
35
  listener: (this: UIDemoToggle, ev: HTMLElementEventMap[K]) => unknown,
35
36
  options?: boolean | AddEventListenerOptions,
36
37
  ): void;
37
- addEventListener(type: 'change', listener: (ev: DemoToggleChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
38
38
  }
@@ -32,11 +32,11 @@ export class UIDrawer extends UIElement {
32
32
  /** Title text displayed in the drawer header */
33
33
  text: string;
34
34
 
35
+ addEventListener(type: 'close', listener: (ev: DrawerCloseEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
36
+ addEventListener(type: 'opened', listener: (ev: DrawerOpenedEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
35
37
  addEventListener<K extends keyof HTMLElementEventMap>(
36
38
  type: K,
37
39
  listener: (this: UIDrawer, ev: HTMLElementEventMap[K]) => unknown,
38
40
  options?: boolean | AddEventListenerOptions,
39
41
  ): void;
40
- addEventListener(type: 'close', listener: (ev: DrawerCloseEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
41
- addEventListener(type: 'opened', listener: (ev: DrawerOpenedEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
42
42
  }
@@ -36,14 +36,14 @@ export class UIHeatmap extends UIElement {
36
36
  /** ISO start date; labels months in day-grid mode */
37
37
  startDate: string;
38
38
 
39
- addEventListener<K extends keyof HTMLElementEventMap>(
40
- type: K,
41
- listener: (this: UIHeatmap, ev: HTMLElementEventMap[K]) => unknown,
42
- options?: boolean | AddEventListenerOptions,
43
- ): void;
44
39
  addEventListener(type: 'cell-click', listener: (ev: HeatmapCellClickEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
45
40
  addEventListener(type: 'cell-hover', listener: (ev: HeatmapCellHoverEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
46
41
  addEventListener(type: 'chart-hover', listener: (ev: HeatmapChartHoverEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
47
42
  addEventListener(type: 'chart-leave', listener: (ev: HeatmapChartLeaveEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
48
43
  addEventListener(type: 'chart-select', listener: (ev: HeatmapChartSelectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
44
+ addEventListener<K extends keyof HTMLElementEventMap>(
45
+ type: K,
46
+ listener: (this: UIHeatmap, ev: HTMLElementEventMap[K]) => unknown,
47
+ options?: boolean | AddEventListenerOptions,
48
+ ): void;
49
49
  }
@@ -24,6 +24,7 @@ export { UIInput } from './input/input.js';
24
24
  export { UITextarea } from './textarea/textarea.js';
25
25
  export { UICheck } from './check/check.js';
26
26
  export { UIRadio } from './radio/radio.js';
27
+ export { UIRadioGroup } from './radio-group/radio-group.js';
27
28
  export { UIOptionCard } from './option-card/option-card.js';
28
29
  export { UIIntegrationCard } from './integration-card/integration-card.js';
29
30
  export { UISwitch } from './switch/switch.js';
@@ -40,13 +40,13 @@ by consumers. Listen to `edit-start` / `edit-end` events instead.
40
40
  /** Hint text shown when the value is empty (inline-edit reads this in the static state). */
41
41
  placeholder: string;
42
42
 
43
+ addEventListener(type: 'cancel', listener: (ev: InlineEditCancelEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
44
+ addEventListener(type: 'change', listener: (ev: InlineEditChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
45
+ addEventListener(type: 'edit-end', listener: (ev: InlineEditEditEndEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
46
+ addEventListener(type: 'edit-start', listener: (ev: InlineEditEditStartEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
43
47
  addEventListener<K extends keyof HTMLElementEventMap>(
44
48
  type: K,
45
49
  listener: (this: UIInlineEdit, ev: HTMLElementEventMap[K]) => unknown,
46
50
  options?: boolean | AddEventListenerOptions,
47
51
  ): void;
48
- addEventListener(type: 'cancel', listener: (ev: InlineEditCancelEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
49
- addEventListener(type: 'change', listener: (ev: InlineEditChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
50
- addEventListener(type: 'edit-end', listener: (ev: InlineEditEditEndEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
51
- addEventListener(type: 'edit-start', listener: (ev: InlineEditEditStartEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
52
52
  }
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://adiaui.dev/a2ui/v0_9/components/Input.json",
4
4
  "title": "Input",
5
- "description": "Text input field with contenteditable surface. Supports prefix/suffix icons, label, form participation, and a `type=\"number\"` mode that renders [+]/[-] stepper buttons, numeric input filtering, and ARIA spinbutton semantics — no native `<input type=\"number\">` under the hood. Password type uses a native `<input>` (only path that still wraps native, for `-webkit-text-security` disc masking).",
5
+ "description": "Text input field with contenteditable surface. Supports prefix/suffix icons, label, form participation, and a `type=\"number\"` mode that renders [+]/[-] stepper buttons, numeric input filtering, and ARIA spinbutton semantics — no native `<input type=\"number\">` under the hood. Password type uses a native `<input>` (only path that still wraps native, for `-webkit-text-security` disc masking). Has a 20ch minimum width (`--input-min-width`, mirrors the native `<input>` baseline) so a bare, placeholder-only instance stays usable inside a flex row (row-ui) with no explicit sizing — explicit `width`/`flex` or CSS can size the host above the floor; values below the floor remain clamped unless `--input-min-width` is lowered.",
6
6
  "type": "object",
7
7
  "allOf": [
8
8
  {
@@ -319,6 +319,9 @@
319
319
  "--input-line-height": {
320
320
  "description": "Override line height"
321
321
  },
322
+ "--input-min-width": {
323
+ "description": "Override the minimum-width floor (20ch, mirrors the native `<input>` baseline). Keeps an empty, placeholder-only instance legible when placed directly in a flex row (row-ui) with no explicit width/flex sizing — the placeholder renders via an out-of-flow ::before that contributes no intrinsic width of its own, so without this floor the host collapses to near-zero as a flex item. Explicit sizing (width/flex) can grow the host above the floor but does not bypass it — set to 0 or another lower value to permit a narrower explicit size."
324
+ },
322
325
  "--input-padding": {
323
326
  "description": "Override base padding (density-scaled)"
324
327
  },
@@ -32,6 +32,11 @@ input-ui:not([disabled]) [slot="field"]:hover [slot="suffix"] {
32
32
  --input-affix-fg: var(--a-ui-text-placeholder);
33
33
  --input-field-gap: var(--a-space-1);
34
34
  --input-label-min-width: 8ch;
35
+ /* Native-<input> baseline (historically ~20 characters). Floors the
36
+ host so an empty, placeholder-only instance stays legible as a
37
+ flex item — see the :scope min-width rule below for the collapse
38
+ mechanism this guards against. */
39
+ --input-min-width: 20ch;
35
40
 
36
41
  /* ── Transitions ── */
37
42
  --input-duration: var(--a-duration-fast);
@@ -53,6 +58,19 @@ input-ui:not([disabled]) [slot="field"]:hover [slot="suffix"] {
53
58
  /* ── Base ── */
54
59
  box-sizing: border-box;
55
60
  display: block;
61
+ /* `display: block` gives no width once this host becomes a flex item
62
+ (row-ui, or any display:flex parent) — flex items size from
63
+ content/flex-basis, not display type. The one child that could
64
+ drive content-based sizing, [slot="text"], is EMPTY when the
65
+ input has no value; its placeholder renders via an out-of-flow
66
+ ::before (deliberately, for caret positioning — see the pseudo
67
+ below) that contributes zero intrinsic width. Without a floor,
68
+ an empty placeholder-only input-ui collapses to near-zero in any
69
+ flex context that doesn't otherwise force a width (gh#781).
70
+ min-width, not width, so explicit width/flex can size the host
71
+ above the floor; values below the floor remain clamped. Override
72
+ via --input-min-width (e.g. 0 to opt back out) or size explicitly. */
73
+ min-width: var(--input-min-width);
56
74
  }
57
75
 
58
76
  /* Inline label — sits inside [slot="field"] as a leading caption,
@@ -8,7 +8,9 @@ version: 1
8
8
  description: Text input field with contenteditable surface. Supports prefix/suffix icons, label, form participation, and a
9
9
  `type="number"` mode that renders [+]/[-] stepper buttons, numeric input filtering, and ARIA spinbutton semantics — no native
10
10
  `<input type="number">` under the hood. Password type uses a native `<input>` (only path that still wraps native, for `-webkit-text-security`
11
- disc masking).
11
+ disc masking). Has a 20ch minimum width (`--input-min-width`, mirrors the native `<input>` baseline) so a bare,
12
+ placeholder-only instance stays usable inside a flex row (row-ui) with no explicit sizing — explicit `width`/`flex`
13
+ or CSS can size the host above the floor; values below the floor remain clamped unless `--input-min-width` is lowered.
12
14
  # Per ADR-0027 — primitives that programmatically create other primitives
13
15
  # do NOT auto-import them. Consumer (or demo shell) must explicitly import.
14
16
  composes:
@@ -230,6 +232,14 @@ tokens:
230
232
  description: Override hover state border color
231
233
  --input-line-height:
232
234
  description: Override line height
235
+ --input-min-width:
236
+ description: Override the minimum-width floor (20ch, mirrors the native `<input>`
237
+ baseline). Keeps an empty, placeholder-only instance legible when placed directly
238
+ in a flex row (row-ui) with no explicit width/flex sizing — the placeholder renders
239
+ via an out-of-flow ::before that contributes no intrinsic width of its own, so
240
+ without this floor the host collapses to near-zero as a flex item. Explicit sizing
241
+ (width/flex) can grow the host above the floor but does not bypass it — set to 0
242
+ or another lower value to permit a narrower explicit size.
233
243
  --input-padding:
234
244
  description: Override base padding (density-scaled)
235
245
  --input-placeholder-color:
@@ -48,11 +48,14 @@ const ACTION_FOR_STATUS = Object.freeze({
48
48
  pending: { event: 'press', emits: '', label: 'Connecting…', variant: 'outline', icon: '' },
49
49
  });
50
50
 
51
+ // `coming-soon` is a family-less badge, tone="muted" — `variant="muted"`
52
+ // retired (variant/color/tone convention cut, gh#873/874); `#renderStatusBadge()`
53
+ // below branches on `variant` vs. absent.
51
54
  const BADGE_FOR_STATUS = Object.freeze({
52
55
  connected: { variant: 'success', text: 'Connected', icon: 'check' },
53
56
  error: { variant: 'danger', text: 'Error', icon: 'warning-circle' },
54
57
  pending: { variant: 'info', text: 'Connecting…', icon: '' },
55
- 'coming-soon':{ variant: 'muted', text: 'Coming soon', icon: '' },
58
+ 'coming-soon':{ text: 'Coming soon', icon: '' },
56
59
  });
57
60
 
58
61
  // Fallback glyph per known provider when no explicit [logo] is supplied, so
@@ -312,7 +315,13 @@ export class UIIntegrationCard extends UIElement {
312
315
  return;
313
316
  }
314
317
  this.#statusEl.hidden = false;
315
- this.#statusEl.setAttribute('variant', cfg.variant);
318
+ if (cfg.variant) {
319
+ this.#statusEl.setAttribute('variant', cfg.variant);
320
+ this.#statusEl.removeAttribute('tone');
321
+ } else {
322
+ this.#statusEl.removeAttribute('variant');
323
+ this.#statusEl.setAttribute('tone', 'muted');
324
+ }
316
325
  this.#statusEl.setAttribute('text', cfg.text);
317
326
  this.#statusEl.setAttribute('size', 'sm');
318
327
  if (cfg.icon) this.#statusEl.setAttribute('icon', cfg.icon);