@adia-ai/web-components 0.8.34 → 0.8.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 (89) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/components/agent-questions/agent-questions.class.js +0 -1
  3. package/components/badge/badge.css +27 -3
  4. package/components/badge/badge.examples.md +4 -4
  5. package/components/card/card.css +44 -1
  6. package/components/check/check.a2ui.json +25 -11
  7. package/components/check/check.class.js +5 -0
  8. package/components/check/check.css +21 -0
  9. package/components/check/check.d.ts +2 -0
  10. package/components/check/check.examples.md +4 -4
  11. package/components/check/check.yaml +26 -7
  12. package/components/demo-toggle/demo-toggle.css +4 -0
  13. package/components/demo-toggle/demo-toggle.examples.md +4 -4
  14. package/components/feed/feed-item.a2ui.json +3 -2
  15. package/components/feed/feed-item.yaml +7 -1
  16. package/components/feed/feed.css +28 -5
  17. package/components/field/field.examples.md +4 -4
  18. package/components/image/image.class.js +8 -0
  19. package/components/nav-group/nav-group.css +12 -1
  20. package/components/option-card/option-card.a2ui.json +10 -10
  21. package/components/option-card/option-card.css +32 -16
  22. package/components/option-card/option-card.yaml +19 -15
  23. package/components/radio/radio.examples.md +4 -4
  24. package/components/range/range.css +8 -4
  25. package/components/slider/slider.a2ui.json +6 -3
  26. package/components/slider/slider.css +28 -12
  27. package/components/slider/slider.yaml +11 -3
  28. package/components/stat/stat.examples.md +4 -4
  29. package/components/swatch/swatch.css +14 -6
  30. package/components/switch/switch.examples.md +4 -4
  31. package/components/table-toolbar/table-toolbar.css +10 -1
  32. package/components/tabs/tabs.css +10 -4
  33. package/components/tag/tag.class.js +14 -1
  34. package/components/tag/tag.css +63 -5
  35. package/components/text/text.examples.md +4 -4
  36. package/components/timeline/timeline-item.a2ui.json +2 -2
  37. package/components/timeline/timeline-item.yaml +2 -2
  38. package/components/timeline/timeline.css +33 -5
  39. package/components/timeline/timeline.d.ts +2 -2
  40. package/components/toast/toast.css +28 -5
  41. package/custom-elements.json +469 -46
  42. package/dist/theme-provider.min.js +1 -1
  43. package/dist/web-components.min.css +1 -1
  44. package/dist/web-components.min.js +4 -4
  45. package/dist/web-components.sheet.js +1 -1
  46. package/package.json +1 -1
  47. package/patterns/access-requests/access-requests.examples.html +3 -3
  48. package/patterns/admin-keys-and-export/admin-keys-and-export.examples.html +3 -3
  49. package/patterns/admin-shell/admin-shell.examples.html +3 -4
  50. package/patterns/agent-cost/agent-cost.examples.html +3 -3
  51. package/patterns/agent-memory/agent-memory.examples.html +3 -3
  52. package/patterns/agent-prompt-library/agent-prompt-library.examples.html +3 -3
  53. package/patterns/agent-tool-call/agent-tool-call.examples.html +3 -3
  54. package/patterns/alert-dialog/alert-dialog.examples.html +3 -3
  55. package/patterns/app-nav/app-nav.examples.html +4 -4
  56. package/patterns/approvals/approvals.examples.html +3 -3
  57. package/patterns/audit-log/audit-log.examples.html +3 -3
  58. package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.html +19 -9
  59. package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.js +2 -2
  60. package/patterns/changelog-feed/changelog-feed.examples.html +3 -3
  61. package/patterns/chart-in-card/chart-in-card.examples.html +37 -3
  62. package/patterns/chart-in-card/chart-in-card.examples.js +12 -0
  63. package/patterns/chart-in-card/chart-in-card.html +6 -0
  64. package/patterns/chat-shell/chat-shell.examples.html +3 -4
  65. package/patterns/comments-and-collaboration/comments-and-collaboration.examples.html +3 -3
  66. package/patterns/conversion-funnel/conversion-funnel.examples.html +3 -3
  67. package/patterns/data-tables-inline-edit-and-tree/data-tables-inline-edit-and-tree.examples.html +3 -3
  68. package/patterns/diff-review/diff-review.examples.html +3 -3
  69. package/patterns/editor-shell/editor-shell.examples.html +3 -4
  70. package/patterns/export-flow/export-flow.examples.html +3 -3
  71. package/patterns/filter-bar/filter-bar.examples.html +3 -3
  72. package/patterns/form-drawer/form-drawer.examples.html +3 -3
  73. package/patterns/forms-conditional-and-autocomplete/forms-conditional-and-autocomplete.examples.html +3 -3
  74. package/patterns/inline-dialog/inline-dialog.examples.html +3 -3
  75. package/patterns/kanban-board/kanban-board.examples.html +3 -3
  76. package/patterns/layout/layout.examples.html +4 -7
  77. package/patterns/marketing-engagement/marketing-engagement.examples.html +3 -3
  78. package/patterns/notifications-bell-and-digest/notifications-bell-and-digest.examples.html +3 -3
  79. package/patterns/permissions-matrix/permissions-matrix.examples.html +3 -3
  80. package/patterns/permissions-role-picker/permissions-role-picker.examples.html +3 -3
  81. package/patterns/permissions-sharing/permissions-sharing.examples.html +3 -3
  82. package/patterns/profile-public-and-verification/profile-public-and-verification.examples.html +3 -3
  83. package/patterns/record-detail-drawer/record-detail-drawer.examples.html +3 -3
  84. package/patterns/responsive-shell-sidebar/responsive-shell-sidebar.examples.html +3 -3
  85. package/patterns/retention-cohort/retention-cohort.examples.html +3 -3
  86. package/patterns/search-discovery/search-discovery.examples.html +3 -3
  87. package/patterns/system-banners/system-banners.examples.html +3 -3
  88. package/patterns/timezone-selector/timezone-selector.examples.html +3 -3
  89. package/patterns/wizard-drawer/wizard-drawer.examples.html +3 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog — @adia-ai/web-components
2
2
 
3
+ ## [0.8.36] — 2026-08-13
4
+
5
+ ### Maintenance
6
+ - **Lockstep version bump only.** No source changes in this package; bumped to maintain the lockstep version coherence enforced by `scripts/release/check-lockstep.mjs`. Substantive v0.8.36 work shipped in a2ui chain standalone-installable (gh#1186: unpublishable dep removed, 143 packed-import escapes fixed, packed-imports gate + boot probe) + ui-architect coordinator agent + MCP doc consolidation. See `packages/a2ui/mcp/CHANGELOG.md#0836--2026-08-13` for details.
7
+
8
+ ## [0.8.35] — 2026-08-13
9
+
10
+ ### Maintenance
11
+ - **Source fixes this window are carried per-file below; the narrative CHANGELOG entries live with the substantive packages.** Bumped in lockstep per `scripts/release/check-lockstep.mjs`. Headline v0.8.35 work shipped in factory-DX program (scaffold boot+CSS fixes, class-B npm marketplace, consumer CI recipe, DX sweep) + audit-wave plugin hardening + ~20 component/pattern fixes. See `packages/plugins/adia-ui-factory/CHANGELOG.md#0835--2026-08-13` for details.
12
+ - **`components/` touched in this release window** (58 file(s), e.g. `agent-questions/agent-questions.class.js`) — carried by the entries above.
13
+ - **`dist/` bundles rebuilt** in this cut's window (4 file(s)) — regenerated from the source changes described above, not independent edits.
14
+ - **`patterns/` touched in this release window** (43 file(s), e.g. `access-requests/access-requests.examples.html`) — carried by the entries above.
15
+ - **`traits/` touched in this release window** (5 file(s), e.g. `anchor-positioning/anchor-positioning.examples.html`) — carried by the entries above.
16
+
3
17
  ## [0.8.34] — 2026-08-11
4
18
 
5
19
  ### Fixed
@@ -133,7 +133,6 @@ export class UIAgentQuestions extends UIElement {
133
133
  this.#submitEl = document.createElement('button-ui');
134
134
  this.#submitEl.setAttribute('text', this.submitLabel || 'Continue');
135
135
  this.#submitEl.setAttribute('variant', 'primary');
136
- this.#submitEl.setAttribute('size', 'sm');
137
136
  this.#submitEl.setAttribute('disabled', '');
138
137
  if (!this.multi) this.#submitEl.hidden = true;
139
138
  this.#submitEl.addEventListener('press', this.#onSubmit);
@@ -20,8 +20,22 @@
20
20
  only radius moved. */
21
21
  --badge-radius: var(--a-radius-full);
22
22
  --badge-px: var(--a-space-1-5);
23
- --badge-py: var(--a-space-0-5);
23
+ /* gh#1084: block padding flattened toward 1px per tier — the
24
+ --badge-min-h floor (below) now owns the chip's vertical size, so
25
+ py no longer has to carry it. Was --a-space-0-5 (2px); mirrors the
26
+ identical tag-ui change (same rationale, tag.css). */
27
+ --badge-py: var(--a-space-px);
24
28
  --badge-gap: var(--a-space-1);
29
+ /* gh#1084 — min-height floor, decoupled from padding (mirrors
30
+ tags-input-ui's --tags-input-min-h, and tag-ui's own --tag-min-h
31
+ added in the same change). Component-owned fixed rem constants
32
+ (not density-scaled) calibrated to badge's CURRENT rendered
33
+ height at each tier — measured 12/15/20px sm/md/lg before this
34
+ change — so this is a non-regressing addition: shrinking py
35
+ toward 1px no longer shrinks the badge, the floor holds the line. */
36
+ --badge-min-h: 0.9375rem; /* 15px @ d=1 — md */
37
+ --badge-min-h-sm: 0.75rem; /* 12px @ d=1 */
38
+ --badge-min-h-lg: 1.25rem; /* 20px @ d=1 */
25
39
  /* One rung down the --a-ui-* scale (gh#965) — ladder shifts with it
26
40
  (sm=2xs, lg=sm) so tiers stay one rung apart. Mirrors tag-ui. */
27
41
  --badge-font-size: var(--a-ui-xs);
@@ -45,8 +59,11 @@
45
59
  and tag share this md baseline and this exact reasoning). */
46
60
  --badge-px-sm: var(--a-space-1);
47
61
  --badge-px-lg: var(--a-space-2);
62
+ /* gh#1084: py flattened toward 1px/tier now that --badge-min-h
63
+ floors the box — was --a-space-px/--a-space-1 (1px/4px); mirrors
64
+ the identical tag-ui change (tag.css). */
48
65
  --badge-py-sm: var(--a-space-px);
49
- --badge-py-lg: var(--a-space-1);
66
+ --badge-py-lg: var(--a-space-0-5);
50
67
  --badge-font-size-sm: var(--a-ui-2xs);
51
68
  --badge-font-size-lg: var(--a-ui-sm);
52
69
  text-align: start; /* §text-align-reset — blocks inheritance from centered ancestors */
@@ -60,6 +77,10 @@
60
77
  justify-content: center;
61
78
  gap: var(--badge-gap);
62
79
  width: fit-content;
80
+ /* gh#1084: floor, not a fixed size — mirrors tags-input-ui's own
81
+ min-height and tag-ui's identical addition (tag.css). Decouples
82
+ the badge's vertical size from --badge-py. */
83
+ min-height: var(--badge-min-h);
63
84
  padding: var(--badge-py) var(--badge-px);
64
85
  background: var(--badge-bg);
65
86
  color: var(--badge-fg);
@@ -239,15 +260,18 @@
239
260
  --badge-px: var(--badge-px-sm);
240
261
  --badge-py: var(--badge-py-sm);
241
262
  --badge-font-size: var(--badge-font-size-sm);
263
+ --badge-min-h: var(--badge-min-h-sm);
242
264
  }
243
265
  :scope[size="md"] {
244
266
  --badge-px: var(--a-space-1-5);
245
- --badge-py: var(--a-space-0-5);
267
+ --badge-py: var(--a-space-px);
246
268
  --badge-font-size: var(--a-ui-xs);
269
+ --badge-min-h: 0.9375rem; /* 15px @ d=1 — matches the :where(:scope) default */
247
270
  }
248
271
  :scope[size="lg"] {
249
272
  --badge-px: var(--badge-px-lg);
250
273
  --badge-py: var(--badge-py-lg);
251
274
  --badge-font-size: var(--badge-font-size-lg);
275
+ --badge-min-h: var(--badge-min-h-lg);
252
276
  }
253
277
  }
@@ -11,8 +11,8 @@
11
11
  ```html
12
12
  <row-ui gap="6" align="start" wrap>
13
13
  <col-ui gap="2">
14
- <text-ui variant="caption" color="muted">verse</text-ui>
15
- <div verse>
14
+ <text-ui variant="caption" color="muted">scale="ui-sm"</text-ui>
15
+ <div scale="ui-sm">
16
16
  <row-ui gap="2">
17
17
  <badge-ui text="New" variant="success"></badge-ui>
18
18
  <badge-ui text="Beta" variant="info"></badge-ui>
@@ -31,8 +31,8 @@
31
31
  </div>
32
32
  </col-ui>
33
33
  <col-ui gap="2">
34
- <text-ui variant="caption" color="muted">prose</text-ui>
35
- <div prose>
34
+ <text-ui variant="caption" color="muted">scale="content-md"</text-ui>
35
+ <div scale="content-md">
36
36
  <row-ui gap="2">
37
37
  <badge-ui text="New" variant="success"></badge-ui>
38
38
  <badge-ui text="Beta" variant="info"></badge-ui>
@@ -80,6 +80,18 @@
80
80
  margin-top: auto;
81
81
  }
82
82
 
83
+ /* Flex containers never collapse adjacent margins the way block flow
84
+ does (gh#1098) — once :has(> footer) above switches the card to
85
+ flex-column, a plain header followed by a plain section stacks BOTH
86
+ regions' --card-inset margins into a doubled gap instead of the
87
+ single inset block flow would have collapsed them to. The leading
88
+ region's own margin-bottom already supplies that inset; zero the
89
+ trailing region's margin-top. footer is excluded — its margin-top is
90
+ handled above (bottom-pin `auto`) or by [divider] below. */
91
+ :scope:has(> footer) > :is(header, section) + :is(header, section) {
92
+ margin-block-start: 0;
93
+ }
94
+
83
95
  /* ═══════ Variants — token-only overrides ═══════ */
84
96
 
85
97
  /* `outline` is a documented alias for `outlined` (yaml). */
@@ -385,6 +397,30 @@
385
397
  border-radius: 0;
386
398
  }
387
399
 
400
+ /* Guide/value/legend safety inset (gh#1095) — [bleed]'s zero-inset is
401
+ only collision-free for the bare-marks case: a sparkline (its
402
+ renderer never emits axis ticks, gridlines, value labels, or a
403
+ legend — chart.class.js #renderSparkline), or any other chart-ui
404
+ type with BOTH [hide-grid] and [hide-values] set (the author opted
405
+ out of every text element). A chart that still draws guides or
406
+ value text — or is paired with a <chart-legend-ui> — needs
407
+ clearance from the card's frame, or that text collides with the
408
+ card edge and clips under the corner-rounded `overflow: hidden`
409
+ (:scope, top of file). Restore the card's own inset automatically
410
+ for those cases rather than relying on every author to remember —
411
+ the :has() selector's specificity already beats the plain [bleed]
412
+ reset above regardless of source order. Pairing a legend with a
413
+ genuinely full-bleed chart still works — put the legend in its OWN
414
+ section below, wired via chart-legend-ui[for="<chart id>"], which
415
+ never enters this selector (see chart-in-card.examples.html's
416
+ "Full-bleed chart + legend band" example). */
417
+ & > section[bleed]:has(> chart-ui:not([type="sparkline"]):not([hide-grid])),
418
+ & > section[bleed]:has(> chart-ui:not([type="sparkline"]):not([hide-values])),
419
+ & > section[bleed]:has(> chart-legend-ui) {
420
+ margin: var(--card-inset);
421
+ padding: 0;
422
+ }
423
+
388
424
  /* ═══════ Footer ═══════
389
425
  Margin-based inset. Flex row for actions.
390
426
  [divider] adds top border.
@@ -414,8 +450,15 @@
414
450
  justify-content: flex-end;
415
451
  }
416
452
 
453
+ /* Divider clearance is padding-only (matches header[divider]'s
454
+ padding-bottom pattern) — the space above the line already comes from
455
+ the preceding section's own trailing margin. An explicit margin-top
456
+ here on top of that stacked with the footer's base margin instead of
457
+ collapsing into it (footer/section are flex-column siblings once the
458
+ card has a footer — see the :has(> footer) rule above — so adjacent
459
+ margins never collapse), tripling the gap to the divider vs. the
460
+ content gaps around it (gh#1096). */
417
461
  & > footer[divider] {
418
- margin-top: var(--card-inset);
419
462
  padding-top: var(--card-inset);
420
463
  border-top: 1px solid var(--card-divider);
421
464
  }
@@ -46,6 +46,11 @@
46
46
  "type": "string",
47
47
  "default": ""
48
48
  },
49
+ "labelHidden": {
50
+ "description": "When true, [label] still sets the accessible name (aria-label) but the visible text is suppressed via the canonical sr-only technique (gh#1010). Use when a sibling element already shows the same name visually (e.g. a title next to a check-ui used as a list-item icon) and the plain-visible label would paint it twice.",
51
+ "type": "boolean",
52
+ "default": false
53
+ },
49
54
  "name": {
50
55
  "description": "Form field name",
51
56
  "type": "string",
@@ -123,26 +128,35 @@
123
128
  },
124
129
  "tag": "check-ui",
125
130
  "tokens": {
126
- "--check-box-background": {
131
+ "--check-bg": {
127
132
  "description": "Unchecked background"
128
133
  },
129
- "--check-box-border-color": {
130
- "description": "Unchecked border color"
134
+ "--check-bg-checked": {
135
+ "description": "Checked/indeterminate background"
131
136
  },
132
- "--check-box-radius": {
133
- "description": "Checkbox border radius"
137
+ "--check-bg-checked-hover": {
138
+ "description": "Checked/indeterminate background on hover"
134
139
  },
135
- "--check-box-size": {
136
- "description": "Checkbox indicator size"
140
+ "--check-bg-hover": {
141
+ "description": "Unchecked background on hover"
137
142
  },
138
- "--check-checked-background": {
139
- "description": "Checked/indeterminate background"
143
+ "--check-border": {
144
+ "description": "Unchecked border color"
140
145
  },
141
- "--check-checked-border-color": {
146
+ "--check-border-checked": {
142
147
  "description": "Checked/indeterminate border"
143
148
  },
144
- "--check-checked-foreground": {
149
+ "--check-border-hover": {
150
+ "description": "Unchecked border color on hover"
151
+ },
152
+ "--check-fg-checked": {
145
153
  "description": "Checkmark/dash color"
154
+ },
155
+ "--check-radius": {
156
+ "description": "Checkbox border radius"
157
+ },
158
+ "--check-size": {
159
+ "description": "Checkbox indicator size"
146
160
  }
147
161
  },
148
162
  "traits": [],
@@ -26,6 +26,11 @@ export class UICheck extends UIFormElement {
26
26
  checked: { type: Boolean, default: false, reflect: true },
27
27
  indeterminate: { type: Boolean, default: false, reflect: true },
28
28
  label: { type: String, default: '', reflect: true },
29
+ /* gh#1010: keeps [label] as the accessible name (aria-label, set
30
+ below) while suppressing the VISIBLE `::after` text — for
31
+ compositions that already show the name elsewhere (e.g. a
32
+ sibling title) and would otherwise paint it twice. */
33
+ labelHidden: { type: Boolean, default: false, reflect: true, attribute: 'label-hidden' },
29
34
  };
30
35
 
31
36
  static template = () => html`<span slot="box"></span>`;
@@ -122,6 +122,27 @@ check-ui[indeterminate] {
122
122
  /* Label */
123
123
  :scope[label]::after { content: attr(label); }
124
124
 
125
+ /* Visually-hidden label (gh#1010) — [label-hidden] keeps the a11y name
126
+ (aria-label, set from [label] in check.class.js render()) but drops
127
+ the visible text, for compositions that already show the name
128
+ elsewhere. Same canonical sr-only recipe as <visually-hidden-ui>
129
+ (visually-hidden.css) — clip-path does the real hiding; the rest is
130
+ belt-and-suspenders for cross-UA. More specific than the bare
131
+ `[label]::after` rule above, so it wins whenever both are set. */
132
+ :scope[label][label-hidden]::after {
133
+ content: attr(label);
134
+ position: absolute !important;
135
+ width: 1px !important;
136
+ height: 1px !important;
137
+ padding: 0 !important;
138
+ margin: -1px !important;
139
+ overflow: hidden !important;
140
+ clip: rect(0, 0, 0, 0) !important;
141
+ clip-path: inset(100%) !important;
142
+ white-space: nowrap !important;
143
+ border: 0 !important;
144
+ }
145
+
125
146
  /* Focus */
126
147
  :scope:focus-visible [slot="box"] { box-shadow: var(--check-focus-ring); border-color: transparent; }
127
148
 
@@ -20,6 +20,8 @@ export class UICheck extends UIFormElement {
20
20
  /** Indeterminate (mixed) state — visual only; cleared on click. */
21
21
  indeterminate: boolean;
22
22
  label: string;
23
+ /** When true, [label] still sets the accessible name but the visible text is sr-only hidden (gh#1010). */
24
+ labelHidden: boolean;
23
25
 
24
26
  addEventListener<K extends keyof HTMLElementEventMap>(
25
27
  type: K,
@@ -11,8 +11,8 @@
11
11
  ```html
12
12
  <row-ui gap="6" align="start" wrap>
13
13
  <col-ui gap="2">
14
- <text-ui variant="caption" color="muted">verse</text-ui>
15
- <div verse>
14
+ <text-ui variant="caption" color="muted">scale="ui-sm"</text-ui>
15
+ <div scale="ui-sm">
16
16
  <check-ui label="Notify me" checked></check-ui>
17
17
  </div>
18
18
  </col-ui>
@@ -23,8 +23,8 @@
23
23
  </div>
24
24
  </col-ui>
25
25
  <col-ui gap="2">
26
- <text-ui variant="caption" color="muted">prose</text-ui>
27
- <div prose>
26
+ <text-ui variant="caption" color="muted">scale="content-md"</text-ui>
27
+ <div scale="content-md">
28
28
  <check-ui label="Notify me" checked></check-ui>
29
29
  </div>
30
30
  </col-ui>
@@ -48,6 +48,17 @@ props:
48
48
  required: true
49
49
  type: string
50
50
  default: ""
51
+ labelHidden:
52
+ description: >-
53
+ When true, [label] still sets the accessible name (aria-label) but
54
+ the visible text is suppressed via the canonical sr-only technique
55
+ (gh#1010). Use when a sibling element already shows the same name
56
+ visually (e.g. a title next to a check-ui used as a list-item icon)
57
+ and the plain-visible label would paint it twice.
58
+ type: boolean
59
+ default: false
60
+ reflect: true
61
+ attribute: label-hidden
51
62
  value:
52
63
  description: Form value (submitted as 'on' when checked)
53
64
  type: string
@@ -68,23 +79,31 @@ states:
68
79
  attribute: disabled
69
80
  traits: []
70
81
  tokens:
71
- --check-box-background:
82
+ --check-bg:
72
83
  description: Unchecked background
73
- --check-box-border-color:
84
+ --check-border:
74
85
  description: Unchecked border color
75
- --check-box-radius:
86
+ --check-radius:
76
87
  description: Checkbox border radius
77
- --check-box-size:
88
+ --check-size:
78
89
  description: Checkbox indicator size
79
- --check-checked-background:
90
+ --check-bg-checked:
80
91
  description: Checked/indeterminate background
81
- --check-checked-border-color:
92
+ --check-border-checked:
82
93
  description: Checked/indeterminate border
83
- --check-checked-foreground:
94
+ --check-fg-checked:
84
95
  description: Checkmark/dash color
96
+ --check-border-hover:
97
+ description: Unchecked border color on hover
98
+ --check-bg-hover:
99
+ description: Unchecked background on hover
100
+ --check-bg-checked-hover:
101
+ description: Checked/indeterminate background on hover
85
102
  a2ui:
86
103
  rules:
87
104
  - "Self-labeling widget — use the [label] attribute directly; do NOT wrap in <field-ui>. The widget renders its own label inline via CSS attr() pattern. Wrapping breaks the consent-row layout (see field-ui anti_patterns)."
105
+ - rule: 'When check-ui is used as a list-item icon/indicator beside a separate title element carrying the same name (a title column, a list-item-ui slot="text"), set [label-hidden] alongside [label] so the accessible name is preserved without painting the name twice.'
106
+ reason: 'gh#1010 — the visible ::after label and an adjacent title both showed the step name, and the visible labels varied width, producing a ragged title column.'
88
107
  - rule: 'Three states supported: unchecked (default), checked (via [checked]), and indeterminate (via [indeterminate]); form-associated.'
89
108
  reason: 'Per ADR-0025 — host IS the control, no wrapped <input>.'
90
109
  - rule: 'For binary on/off toggles where visual switch metaphor matters, use <switch-ui> instead; check-ui is for multi-select sets and form-acceptance gates.'
@@ -39,6 +39,10 @@
39
39
 
40
40
  /* -- Header bar -- */
41
41
  [slot="bar"] {
42
+ /* ensure('bar') APPENDS the bar after the authored slot content —
43
+ flex order puts it visually first (gh#1075: it rendered at the
44
+ bottom of the card, under the stage, without this). */
45
+ order: -1;
42
46
  width: 100%;
43
47
  padding: var(--demo-toggle-bar-py) var(--demo-toggle-bar-px);
44
48
  background: var(--demo-toggle-bar-bg);
@@ -4,8 +4,8 @@
4
4
 
5
5
  ```html
6
6
  <demo-toggle-ui label-on="With trait" label-off="Without trait" initial="off">
7
- <div slot="off" style="padding:var(--a-space-4);text-align:center;color:var(--a-fg-muted);">Bare surface — no trait attached.</div>
8
- <div slot="on" style="padding:var(--a-space-4);text-align:center;color:var(--md-sys-color-neutral-on-surface);">Same surface, with trait attached.</div>
7
+ <div slot="off" style="text-align:center;color:var(--a-fg-muted);">Bare surface — no trait attached.</div>
8
+ <div slot="on" style="text-align:center;color:var(--md-sys-color-neutral-on-surface);">Same surface, with trait attached.</div>
9
9
  </demo-toggle-ui>
10
10
  ```
11
11
 
@@ -13,7 +13,7 @@
13
13
 
14
14
  ```html
15
15
  <demo-toggle-ui label-on="With shimmer-loading" label-off="Without shimmer-loading">
16
- <div slot="off" style="padding:var(--a-space-4);">Placeholder content (no trait)</div>
17
- <div slot="on" style="padding:var(--a-space-4);">Placeholder content (shimmer active)</div>
16
+ <div slot="off">Placeholder content (no trait)</div>
17
+ <div slot="on">Placeholder content (shimmer active)</div>
18
18
  </demo-toggle-ui>
19
19
  ```
@@ -47,14 +47,15 @@
47
47
  "default": ""
48
48
  },
49
49
  "variant": {
50
- "description": "Semantic variant",
50
+ "description": "Semantic variant — `default | info | success | warning | danger | primary`. `primary` is the brand-emphasis fill (accent-filled bg + on-accent text), ported from toast.css's `primary` variant (gh#1104) now that feed-item-ui is the real paint site for posted toasts.",
51
51
  "type": "string",
52
52
  "enum": [
53
53
  "default",
54
54
  "info",
55
55
  "success",
56
56
  "warning",
57
- "danger"
57
+ "danger",
58
+ "primary"
58
59
  ],
59
60
  "default": "default"
60
61
  }
@@ -25,7 +25,12 @@ props:
25
25
  type: string
26
26
  default: ""
27
27
  variant:
28
- description: Semantic variant
28
+ description: >-
29
+ Semantic variant — `default | info | success | warning | danger |
30
+ primary`. `primary` is the brand-emphasis fill (accent-filled bg +
31
+ on-accent text), ported from toast.css's `primary` variant
32
+ (gh#1104) now that feed-item-ui is the real paint site for
33
+ posted toasts.
29
34
  type: string
30
35
  default: default
31
36
  enum:
@@ -34,6 +39,7 @@ props:
34
39
  - success
35
40
  - warning
36
41
  - danger
42
+ - primary
37
43
  duration:
38
44
  description: Auto-fade timer in ms; null/0 = sticky (requires user input)
39
45
  type: number
@@ -99,7 +99,16 @@ feed-item-ui[data-closing] {
99
99
  /* Item — atomic feed entry. */
100
100
  @scope (feed-item-ui) {
101
101
  :where(:scope) {
102
- --feed-item-bg: var(--md-sys-color-neutral-background);
102
+ /* -container-high (NOT plain -background/-container) — a top-layer
103
+ overlay item needs a tonal step strong enough to read as its own
104
+ elevated surface against arbitrary page content behind it
105
+ (gh#1091). Plain -background is literally the page's own bg
106
+ token, so an un-varianted item had ZERO tonal separation. Every
107
+ family (incl. neutral) has a generated -container-high role in
108
+ colors/material-static.css — the same tier `--a-{family}-subtle`
109
+ aliases to (component-token-contract.md's `-strong` semantics
110
+ note). */
111
+ --feed-item-bg: var(--md-sys-color-neutral-container-high);
103
112
  --feed-item-fg: var(--md-sys-color-neutral-on-surface);
104
113
  --feed-item-border: var(--md-sys-color-neutral-outline-variant);
105
114
  --feed-item-radius: var(--a-radius-md);
@@ -117,10 +126,20 @@ feed-item-ui[data-closing] {
117
126
  NOT --a-{variant}-fg (= --a-{variant}-text-strong, locked for solid
118
127
  --variant-strong fills) — pairing a locked fg with a flipping bg goes
119
128
  low-contrast in one scheme. Mirrors alert.css + toast.css wiring. */
120
- :scope[variant="info"] { --feed-item-fg: var(--md-sys-color-info-on-surface); --feed-item-bg: var(--md-sys-color-info-container); }
121
- :scope[variant="success"] { --feed-item-fg: var(--md-sys-color-success-on-surface); --feed-item-bg: var(--md-sys-color-success-container); }
122
- :scope[variant="warning"] { --feed-item-fg: var(--md-sys-color-warning-on-surface); --feed-item-bg: var(--md-sys-color-warning-container); }
123
- :scope[variant="danger"] { --feed-item-fg: var(--md-sys-color-danger-on-surface); --feed-item-bg: var(--md-sys-color-danger-container); }
129
+ :scope[variant="info"] { --feed-item-fg: var(--md-sys-color-info-on-surface); --feed-item-bg: var(--md-sys-color-info-container-high); }
130
+ :scope[variant="success"] { --feed-item-fg: var(--md-sys-color-success-on-surface); --feed-item-bg: var(--md-sys-color-success-container-high); }
131
+ :scope[variant="warning"] { --feed-item-fg: var(--md-sys-color-warning-on-surface); --feed-item-bg: var(--md-sys-color-warning-container-high); }
132
+ :scope[variant="danger"] { --feed-item-fg: var(--md-sys-color-danger-on-surface); --feed-item-bg: var(--md-sys-color-danger-container-high); }
133
+ /* `primary` — the accent-filled "branded" tone (welcome banners,
134
+ feature-launch nudges), ported from toast.css's `primary` variant
135
+ (gh#1104 — toast.css documented this but its rules never paint;
136
+ feed-item-ui is the real render surface post-migration). No
137
+ border override, matching this block's existing two-property
138
+ shape (unlike toast.css's variant block, which does override
139
+ border). Deliberately NOT the -container-high convention above —
140
+ primary is a stronger/bolder branded solid-fill per gh#1104's own
141
+ acceptance criteria, not a softer semantic-status tone. */
142
+ :scope[variant="primary"] { --feed-item-fg: var(--md-sys-color-primary-on-primary); --feed-item-bg: var(--a-primary-bg); }
124
143
 
125
144
  :scope {
126
145
  box-sizing: border-box;
@@ -143,6 +162,10 @@ feed-item-ui[data-closing] {
143
162
  opacity: 0;
144
163
  transform: translateY(0.5rem);
145
164
  pointer-events: auto;
165
+ /* Elevated-surface separation over busy page content (gh#1091) —
166
+ mirrors loading-overlay.css's [variant="blur"] treatment. */
167
+ backdrop-filter: blur(4px);
168
+ -webkit-backdrop-filter: blur(4px);
146
169
  }
147
170
  :scope [slot="body"] {
148
171
  flex: 1;
@@ -13,8 +13,8 @@
13
13
  ```html
14
14
  <row-ui gap="6" align="start" wrap>
15
15
  <col-ui gap="2">
16
- <text-ui variant="caption" color="muted">verse</text-ui>
17
- <div verse style="width:220px">
16
+ <text-ui variant="caption" color="muted">scale="ui-sm"</text-ui>
17
+ <div scale="ui-sm" style="width:220px">
18
18
  <field-ui label="Email">
19
19
  <input-ui type="email" placeholder="you@example.com"></input-ui>
20
20
  </field-ui>
@@ -29,8 +29,8 @@
29
29
  </div>
30
30
  </col-ui>
31
31
  <col-ui gap="2">
32
- <text-ui variant="caption" color="muted">prose</text-ui>
33
- <div prose style="width:220px">
32
+ <text-ui variant="caption" color="muted">scale="content-md"</text-ui>
33
+ <div scale="content-md" style="width:220px">
34
34
  <field-ui label="Email">
35
35
  <input-ui type="email" placeholder="you@example.com"></input-ui>
36
36
  </field-ui>
@@ -66,6 +66,14 @@ export class UIImage extends UIElement {
66
66
  }
67
67
  this.#img.addEventListener('load', this.#onLoad);
68
68
  this.#img.addEventListener('error', this.#onError);
69
+ // Missed-load guard (gh#999): an image already complete when these
70
+ // listeners attach — a parse-time-loaded authored [slot="image"], or a
71
+ // cache hit racing the upgrade — fired `load` before we were listening.
72
+ // Without this check [data-loaded] never stamps and the reveal
73
+ // (skeleton → img opacity 1) never happens: the permanent blank box
74
+ // gh#988 reported. naturalWidth guards the broken-image case, which
75
+ // fires `error` (still listened) rather than a false reveal.
76
+ if (this.#img.complete && this.#img.naturalWidth > 0) this.#onLoad();
69
77
 
70
78
  this.#applySize();
71
79
  }
@@ -28,6 +28,14 @@
28
28
  --nav-group-bg-selected: var(--a-bg-selected); /* mirrors --nav-item-bg-selected (gh#795) */
29
29
  --nav-group-icon-size: calc(var(--nav-group-row-height) - var(--a-space-2));
30
30
  --nav-group-badge-size: var(--a-ui-sm);
31
+ /* Continuous indent rail color (gh#1016) — was referenced at the
32
+ ::after usage site below but never given a base value here, the
33
+ same empty-var failure class --nav-gap already documents in this
34
+ file: an undefined custom property resolves to the guaranteed-
35
+ invalid value, so `background: var(--nav-group-indent-rail-bg)`
36
+ painted nothing. Same house role accordion, agent- components,
37
+ chart etc. use for subtle structural lines. */
38
+ --nav-group-indent-rail-bg: var(--md-sys-color-neutral-outline-variant);
31
39
 
32
40
  --nav-item-child-height: var(--a-size-sm);
33
41
  --nav-item-child-font: var(--nav-group-font-size);
@@ -57,7 +65,10 @@
57
65
  top: var(--nav-group-row-height);
58
66
  bottom: calc(var(--nav-group-row-height) / 2);
59
67
  width: 1px;
60
- background: var(--nav-group-indent-rail-bg);
68
+ /* Explicit fallback — same defensive shape --nav-gap got above —
69
+ so an ancestor/override cascade that sets this token to an empty
70
+ string still paints a rail instead of silently disappearing. */
71
+ background: var(--nav-group-indent-rail-bg, var(--md-sys-color-neutral-outline-variant));
61
72
  pointer-events: none;
62
73
  }
63
74
  /* Selected-look header treatment keys on [selected] — the CSS half of
@@ -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/OptionCard.json",
4
4
  "title": "OptionCard",
5
- "description": "Selectable card with radio semantics. A \"rich radio\" — single-select one of N where each option carries a heading, optional description, and optional leading icon. Siblings sharing a `name` form a radiogroup. The whole card is the click target; a CSS-rendered radio circle in the top-left signals state. Form-associated, so `name=value` submits with the parent form when checked.",
5
+ "description": "Selectable card with radio semantics. A \"rich radio\" — single-select one of N where each option carries a heading, optional description, and an optional icon at the trailing end of the header row. Siblings sharing a `name` form a radiogroup. The whole card is the click target; a CSS-rendered radio circle in the top-left signals state. Renders on a container-low tonal surface (no outline as the primary boundary); the checked state keeps an accent border + fill so selection stays unmistakable. Form-associated, so `name=value` submits with the parent form when checked.",
6
6
  "type": "object",
7
7
  "allOf": [
8
8
  {
@@ -42,12 +42,12 @@
42
42
  "default": ""
43
43
  },
44
44
  "icon": {
45
- "description": "Optional Phosphor icon name. Stamped as a leading <icon-ui slot=\"icon\"> when set.",
45
+ "description": "Optional Phosphor icon name. Stamped as a trailing <icon-ui slot=\"icon\"> when set.",
46
46
  "type": "string",
47
47
  "default": ""
48
48
  },
49
49
  "layout": {
50
- "description": "Internal layout. `default` puts the indicator on the left and the icon adjacent. `tile` stacks vertically — icon top-left, indicator top-right, heading + description below — for hero pickers (source / role / plan tiles) where the icon is a primary brand cue.",
50
+ "description": "Internal layout. `default` puts the indicator on the left and, when [icon] is set, the icon at the trailing end of the header row alongside the heading. `tile` stacks vertically — icon top-left, indicator top-right, heading + description below — for hero pickers (source / role / plan tiles) where the icon is a primary brand cue.",
51
51
  "type": "string",
52
52
  "enum": [
53
53
  "default",
@@ -171,22 +171,22 @@
171
171
  "tag": "option-card-ui",
172
172
  "tokens": {
173
173
  "--option-card-bg": {
174
- "description": "Default background."
174
+ "description": "Default background (container-low tonal surface, gh#1018)."
175
175
  },
176
176
  "--option-card-bg-checked": {
177
177
  "description": "Background when checked."
178
178
  },
179
179
  "--option-card-bg-hover": {
180
- "description": "Hover background (non-checked)."
180
+ "description": "Hover background (non-checked) — one tonal step up from --option-card-bg so hover stays visibly distinct."
181
181
  },
182
182
  "--option-card-border": {
183
- "description": "Default border color."
183
+ "description": "Default border color (transparent — the tonal bg is the primary boundary, not an outline; gh#1018)."
184
184
  },
185
185
  "--option-card-border-checked": {
186
186
  "description": "Border color when checked."
187
187
  },
188
188
  "--option-card-border-hover": {
189
- "description": "Hover border color (non-checked)."
189
+ "description": "Hover border color (non-checked; transparent by default, matching the no-outline base)."
190
190
  },
191
191
  "--option-card-desc-color": {
192
192
  "description": "Description text color."
@@ -228,13 +228,13 @@
228
228
  "description": "Heading font weight."
229
229
  },
230
230
  "--option-card-icon-color": {
231
- "description": "Leading icon color when the card is not checked."
231
+ "description": "Trailing icon color when the card is not checked."
232
232
  },
233
233
  "--option-card-icon-color-checked": {
234
- "description": "Leading icon color when the card is checked."
234
+ "description": "Trailing icon color when the card is checked."
235
235
  },
236
236
  "--option-card-icon-size": {
237
- "description": "Leading icon size (sets `--a-icon-size` on the slotted icon-ui)."
237
+ "description": "Trailing icon size (sets `--a-icon-size` on the slotted icon-ui)."
238
238
  },
239
239
  "--option-card-padding-block": {
240
240
  "description": "Vertical padding inside the card."