@adia-ai/web-components 0.8.22 → 0.8.24

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 (106) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/MIGRATION.md +9 -0
  3. package/components/agent-questions/agent-questions.yaml +0 -2
  4. package/components/agent-trace/agent-trace.js +4 -1
  5. package/components/alert/alert.a2ui.json +1 -1
  6. package/components/alert/alert.class.js +5 -0
  7. package/components/alert/alert.css +44 -5
  8. package/components/alert/alert.yaml +10 -4
  9. package/components/calendar-grid/calendar-grid.yaml +3 -0
  10. package/components/canvas/canvas.js +4 -1
  11. package/components/card/card.css +12 -0
  12. package/components/chat-thread/chat-input.js +4 -1
  13. package/components/code/code.css +29 -26
  14. package/components/combobox/combobox.class.js +1 -1
  15. package/components/combobox/combobox.yaml +0 -1
  16. package/components/date-range-picker/date-range-picker.class.js +1 -1
  17. package/components/date-range-picker/date-range-picker.yaml +0 -1
  18. package/components/datetime-picker/datetime-picker.class.js +1 -1
  19. package/components/datetime-picker/datetime-picker.yaml +0 -1
  20. package/components/description-list/description-list.css +9 -0
  21. package/components/drawer/drawer.class.js +5 -0
  22. package/components/drawer/drawer.css +16 -0
  23. package/components/drawer/drawer.yaml +2 -0
  24. package/components/feed/feed.class.js +5 -0
  25. package/components/feed/feed.yaml +2 -0
  26. package/components/field/field.css +4 -1
  27. package/components/input/input.class.js +5 -0
  28. package/components/input/input.yaml +3 -0
  29. package/components/list/list.css +25 -5
  30. package/components/modal/modal.class.js +5 -0
  31. package/components/modal/modal.yaml +2 -0
  32. package/components/nav-group/nav-group.class.js +50 -2
  33. package/components/nav-group/nav-group.css +11 -2
  34. package/components/nav-group/nav-group.yaml +2 -0
  35. package/components/pagination/pagination.yaml +3 -0
  36. package/components/progress-row/progress-row.class.js +10 -2
  37. package/components/richtext/richtext.css +7 -0
  38. package/components/select/select.a2ui.json +5 -0
  39. package/components/select/select.class.js +27 -8
  40. package/components/select/select.yaml +13 -1
  41. package/components/stat/stat.js +4 -1
  42. package/components/stepper/stepper.css +25 -20
  43. package/components/swiper/swiper.class.js +5 -0
  44. package/components/swiper/swiper.yaml +3 -0
  45. package/components/table/table.a2ui.json +15 -1
  46. package/components/table/table.class.js +14 -1
  47. package/components/table/table.css +68 -3
  48. package/components/table/table.d.ts +5 -1
  49. package/components/table/table.yaml +34 -1
  50. package/components/tabs/tab.js +4 -1
  51. package/components/tag/tag.css +15 -9
  52. package/components/toolbar/toolbar.class.js +20 -0
  53. package/components/toolbar/toolbar.yaml +2 -0
  54. package/core/provider.js +5 -1
  55. package/dist/host.min.css +1 -1
  56. package/dist/host.sheet.js +1 -1
  57. package/dist/scale.min.css +1 -1
  58. package/dist/scale.sheet.js +1 -1
  59. package/dist/theme-provider.min.js +2 -2
  60. package/dist/web-components.min.css +1 -1
  61. package/dist/web-components.min.js +93 -93
  62. package/dist/web-components.sheet.js +1 -1
  63. package/package.json +1 -1
  64. package/patterns/access-requests/access-requests.examples.html +12 -3
  65. package/patterns/admin-keys-and-export/admin-keys-and-export.examples.html +23 -8
  66. package/patterns/agent-cost/agent-cost.examples.html +12 -3
  67. package/patterns/agent-memory/agent-memory.examples.html +12 -3
  68. package/patterns/agent-prompt-library/agent-prompt-library.examples.html +12 -3
  69. package/patterns/agent-tool-call/agent-tool-call.examples.html +16 -4
  70. package/patterns/alert-dialog/alert-dialog.examples.html +12 -3
  71. package/patterns/app-nav/app-nav.examples.html +36 -9
  72. package/patterns/approvals/approvals.examples.html +14 -10
  73. package/patterns/audit-log/audit-log.examples.html +5 -2
  74. package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.html +210 -32
  75. package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.js +83 -4
  76. package/patterns/bulk-action-toolbar/bulk-action-toolbar.html +68 -0
  77. package/patterns/changelog-feed/changelog-feed.examples.html +7 -4
  78. package/patterns/chart-in-card/chart-in-card.examples.html +28 -7
  79. package/patterns/comments-and-collaboration/comments-and-collaboration.examples.html +24 -9
  80. package/patterns/conversion-funnel/conversion-funnel.examples.html +24 -15
  81. package/patterns/data-tables-inline-edit-and-tree/data-tables-inline-edit-and-tree.examples.html +17 -8
  82. package/patterns/diff-review/diff-review.examples.html +12 -3
  83. package/patterns/export-flow/export-flow.examples.html +12 -3
  84. package/patterns/filter-bar/filter-bar.examples.html +13 -4
  85. package/patterns/form-drawer/form-drawer.examples.html +15 -6
  86. package/patterns/form-system/form-system.examples.html +4 -1
  87. package/patterns/forms-conditional-and-autocomplete/forms-conditional-and-autocomplete.examples.html +12 -3
  88. package/patterns/inline-dialog/inline-dialog.examples.html +12 -3
  89. package/patterns/kanban-board/kanban-board.examples.html +4 -1
  90. package/patterns/marketing-engagement/marketing-engagement.examples.html +32 -14
  91. package/patterns/notifications-bell-and-digest/notifications-bell-and-digest.examples.html +13 -4
  92. package/patterns/permissions-matrix/permissions-matrix.examples.html +9 -3
  93. package/patterns/permissions-role-picker/permissions-role-picker.examples.html +8 -2
  94. package/patterns/permissions-sharing/permissions-sharing.examples.html +12 -3
  95. package/patterns/profile-public-and-verification/profile-public-and-verification.examples.html +18 -6
  96. package/patterns/record-detail-drawer/record-detail-drawer.examples.html +10 -4
  97. package/patterns/retention-cohort/retention-cohort.examples.html +15 -6
  98. package/patterns/search-discovery/search-discovery.examples.html +16 -4
  99. package/patterns/system-banners/system-banners.examples.html +54 -48
  100. package/patterns/timezone-selector/timezone-selector.examples.html +8 -2
  101. package/patterns/timezone-selector/timezone-selector.examples.js +3 -1
  102. package/patterns/wizard-drawer/wizard-drawer.examples.html +25 -23
  103. package/patterns/wizard-drawer/wizard-drawer.examples.js +12 -1
  104. package/styles/foundation/radius.css +2 -2
  105. package/styles/scale.css +2 -2
  106. package/styles/type/scale.css +6 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,64 @@
1
1
  # Changelog — @adia-ai/web-components
2
2
 
3
+ ## [0.8.24] — 2026-07-30
4
+
5
+ ### Fixed
6
+ - **`list-item-ui` two-line items center the leading glyph on the whole item** (gh#558, operator screenshot). The icon was pinned to the title row, hanging 9–18px above the item center on title+description rows — Material's two-line convention centers it. Explicit `grid-row: 1 / span 2` scoped by `:has([slot="description"])` (with no `grid-template-rows` the explicit grid has zero rows, so the old `1 / -1` collapsed to one row — the reason the earlier anatomy comment called them "identical"); single-line items untouched (probe: two-line offset 0/0/0, single-line 24px rows unchanged).
7
+ - **`card-ui` pins a direct `<footer>` to the card bottom** (gh#559, operator screenshot). In an equal-height grid row, a shorter card's CTA floated at content end (Free tier 42px above the edge vs 17px on its siblings) — the buttons were already in footers; the card just had no pinning rule. `:has(> footer)` promotes the card to a flex column with `margin-top: auto` on the footer; footerless cards keep block flow. Probe: 17/17/17.
8
+ - **`requiredIcons` drift cleared across 16 components** (gh#550). Eight primitives that auto-stamp icons (alert/drawer/feed/modal `x`, input steppers `caret-up`/`caret-down`, nav-group `caret-right`, swiper `caret-left`/`caret-right`, toolbar overflow `dots-three`) now declare `static requiredIcons`, mirrored in their yaml SoTs (calendar-grid and pagination gained the missing yaml blocks); four stale declarations pruned (combobox `check`, date-range-picker `arrow-right`, datetime-picker `clock`, select `magnifying-glass` — none ever stamped) and agent-questions' leftover yaml block removed (options render via option-card-ui since gh#276). Checker itself hardened: `//` line comments no longer read as stamp sites (button `plus` false positive) and yaml list entries may carry trailing `#` comments (select's three phantom mismatches). 125/125 components clean.
9
+ - **`toolbar-ui` overflow measurement traps closed** (gh#528). (1) Flex-grow children (spacers, stretched groups) measured at their EXPANDED width, so the row always read "full" and the last action spilled unrecoverably — grow is zeroed for the read and restored. (2) A `display:none` mount committed a spill from a 0-width reading and stranded the bar collapsed — a zero-width guard now leaves everything visible until the ResizeObserver delivers a real size. Browser-verified both ways.
10
+ - **Corpus/demo enum drift swept** (gh#529): 41 uses of never-implemented values (Tag `muted`/`primary`, Text `primary`/`muted`/`xxl`, Card `soft`/`primary`, Badge `subtle`, Chart `muted`, Button `danger`) mapped to their nearest real values across 15 harvest sources — they silently rendered as defaults. `verify:corpus` warnings 138 → 97.
11
+ - **10 standalone example routes actually render** (gh#530): the sitemap pointed iframe-served routes at headless `.contents.html` fragments; thin standalone wrappers added (single-source fetch-inject), annotations repointed, `frame`/`theme-provider` demos wired for phosphor. 108/108 clean. The `check-required-icons` checker also now reads `<comp>.class.js` and all stamp shapes — 20 false positives gone, 19 real findings exposed → gh#550.
12
+
13
+ ### Maintenance
14
+ - **`dist/` bundles rebuilt** in this cut's window (4 file(s)) — regenerated from the source changes described above, not independent edits.
15
+ - **`patterns/` touched in this release window** (7 file(s), e.g. `admin-keys-and-export/admin-keys-and-export.examples.html`) — carried by the entries above.
16
+
17
+ ## [0.8.23] — 2026-07-30
18
+
19
+ ### Removed
20
+ - **BREAKING — the `--a-font` back-compat alias** (operator ruling 2026-07-30; ships in **0.8.23** as an operator-ruled PATCH exception to the removals-are-MINOR convention). The pre-ADR-0035 name is gone from `type/scale.css`; surviving `var(--a-font)` references resolve to UA serif. Consumer sweep is mechanical (`var(--a-font)` → `var(--a-font-family)`); `audit-font-family-floor` Check B and the plugin lint hooks flag any stragglers. MIGRATION GUIDE §v0.8.23.
21
+
22
+ ### Fixed
23
+ - **`richtext-ui` lists inherit the body font spec** (gh#546, operator chat screenshot). `p` was pinned to `--richtext-body-size` while `ul`/`li` carried no font rule and inherited the HOST page's larger base — chat-rendered bullet items read bigger than the paragraph beside them. The body size + leading now sit once on `[data-richtext-body]` and everything inherits (headings/code still override). Probe: p and li both 14px inside an 18px host.
24
+ - **Deployed-site freeze class eliminated** (gh#534, operator repro + live-browser diagnosis). Cold loads of the deployed site could stall the ~250-file raw module graph on one hung fetch, leaving the page as un-upgraded markup — visible, scrollable, zero interactivity, no error. Two-part fix: (1) on https (or `?force-bundle`) the prebuilt everything-bundle loads in PARALLEL with the module graph, so element registration rides one request; every registration is now `defineIfFree` (22 bare `customElements.define` calls guarded across components + modules — the parallel load surfaced each collision); (2) an 8s registration watchdog surfaces a plain-DOM reload prompt instead of an indefinite freeze. Probe-verified: three heavy routes clean under bundle+graph with zero collisions, dev path untouched, and a stalled-graph negative control trips the banner.
25
+ - **A2UI runtime registry gains `ToolbarGroup → toolbar-group-ui`** (gh#535, operator screenshot). The missing entry made toolbar-group an unknown tag, so the docs transpiler Column-wrapped every toolbar group — the floating bulk-action bar rendered as vertical col-ui stacks on its site route. Rows regenerated; surface-verified warm (groups render, bars 67–75px, main thread responsive — the earlier freeze signal was cold-Vite first-compile noise, not a loop).
26
+ - **`table-ui` perimeter is three-sided — no top edge** (gh#537, operator report). A line above the header read as a stray divider under a card edge or empty-state; the header row's own border-bottom is the table's top structure. Sides + bottom keep the standalone silhouette; `[frameless]` unchanged.
27
+ - **`select-ui [searchable]` matches underscores as spaces** (gh#538) — typing "Los Angeles" now finds `America/Los_Angeles`; the timezone-selector pattern also displays humanized (de-underscored) labels while values keep exact IANA ids.
28
+ - **`approvals` pattern: priority tag leads the request context** (gh#539, operator ruling) — the High tag moves from the trailing action cluster to the front of the requester/department/age line; trailing cluster is Reject · Approve only.
29
+ - **`list-item-ui` iconless items no longer indent behind a ghost grid track** (gh#524, operator devtools screenshot). The `auto 1fr` grid charged its `column-gap` beside the empty leading track. The gap now rides the icon's own `margin-inline-end` (and the action's `margin-inline-start`); empty stamped icon spans hide. Surface-verified: Playwright measurement on /site/patterns/search-discovery — text indent 0 on iconless rows.
30
+ - **`select-ui [mark]` implies the no-radio menu treatment** (gh#498 REOPENED — the first fix applied `[no-radio]` to the playground switcher and closed green while the site chrome switcher kept its radios; operator caught it same-day). A mark declares an identity switcher, so the semantic attribute now carries the treatment; `[no-radio]` remains for markless menus. Surface-verified this time: Playwright probe of the site chrome `#workspace-select` — 2 rows, 0 radio indicators. Tests 38 → 39.
31
+ - **Nav selection icon color is one language** (gh#522, operator ruling). A nav-group containing the selected route filled its header glyph (gh#501) in plain label color — reading as unselected beside nav-item's primary accent glyph. The contains-selection state now reflects as `[data-selected-within]` (set by the gh#501 sync, observer-safe), and the header icon paints `--nav-group-icon-fg-selected` (default `--md-sys-color-primary`, one token language with `--nav-item-accent`) whenever the group is selected or contains the selection. Sub-items keep their own accent-bar system. Tests 9 → 10.
32
+ - **`patterns/wizard-drawer` works on the site route** (gh#506, operator report). The open/cancel affordances were inline `onclick` handlers, which the site's A2UI render path does not execute — the drawer could never open on /site/patterns/wizard-drawer, so the whole three-step use case was dead there. All interactivity now lives in the pattern's `examples.js` setup module (which the route does run); verified end-to-end in a real browser: trigger opens → Next/Back swap panels with the stepper → Create closes.
33
+ - **`tag-ui` no longer top-pins in table cells** (gh#503, adia-v2 report — measured −34px off-center). The TKT-0021 guard `align-self: start` acted on the PARENT's cross axis: correct in a col-ui stack, but vertical in every flex-row parent (table cells, row-ui, toolbars). Re-cut as `width: fit-content` — defeats column-stretch identically (stretch applies only at auto cross size), while row parents' `align-items: center` regains vertical control. Tag-only; badge/button never carried the rule.
34
+ - **`alert-ui` variant icons clear legibility on their own tinted surface** (gh#507, operator screenshot). Icon fg moves from `--a-<family>-strong` (an on-SURFACE color, near-invisible against the family's dark-scheme container tint) to the gh#434 gate-proven `--md-sys-color-<family>-on-<family>-container` role (dark leg measured 5.6–9.2:1).
35
+
36
+ - **`nav-group-ui` header glyph fills when a CHILD is selected** (gh#501, operator report). gh#459 ruled "the group CONTAINING the selected route fills" but relied on consumers setting `[selected]` on the group; a MutationObserver (subtree, `[selected]` filter + childList) now makes it self-enforcing — targeted weight sync only, since a full re-render from the observer would loop on its own textContent writes. Symmetric disconnect. Tests 7 → 9.
37
+ - **`progress-row-ui` authored `[slot="meta"]` content survives render** (gh#502). `render()` unconditionally wrote `textContent` from the `[meta]` attr and hid the element when the attr was empty, so authored meta content (text + a trailing delta badge) was wiped — the slot was effectively attr-only. The attr still owns the text when set; absent, authored children are kept and only a genuinely empty meta hides. New test file, 4 passing. The `conversion-funnel` pattern's step-over-step variant now rides its delta badges inside the meta slot instead of a sibling column misaligned against the row.
38
+
39
+ ### Added
40
+ - **`table-ui [frameless]` + the `--table-perimeter` token split** (gh#511, adia-v2 console tables). The 1px perimeter and the row dividers both read `--table-border` (TKT-0026), so a card-composed table always double-bordered — the card's rounded outline plus the table's square frame — and consumers couldn't drop one without the other. The perimeter now reads its own `--table-perimeter` (default: `var(--table-border)` — standalone tables unchanged); `[frameless]` blanks just that token. Pattern rule: inside `card-ui`, set frameless — the container owns the frame. Tests 45 → 49.
41
+ - **`patterns/bulk-action-toolbar`: a floating, bottom-anchored variant** (gh#494, operator ruling — adia-v2 evidence-console + independent kit-side report, ADIA2-7028). Long/paginated tables scroll the top-swap bar out of view exactly when a mid-scroll selection needs it; the floating variant is a card-like elevated bar (`card-ui[elevation="3"]`) that rises `position: sticky` from the bottom of the table's own scroll container — never the browser viewport — driven by `table-ui`'s existing `select` event. Anatomy: `aria-live="polite"` selection count, an optional per-flow controls slot, actions with destructive last, an explicit Clear. Composed entirely from existing primitives (`card-ui` + `toolbar-ui` + `toolbar-group-ui`) — the anchored-surface primitive filed as gh#495 was not needed. The pattern doc now states which variant to pick and why (floating default once a table runs taller than ~one viewport). The top-swap variant stays valid and unchanged in shape; fixed alongside it: a real defect where its trailing action (Delete) silently spilled into an unreachable "more" menu (a flex-grow spacer inflates `toolbar-ui`'s own overflow-width measurement) — both variants now push their action group right via `toolbar-group-ui` + `align="space-between"` instead.
42
+ - **`table-ui [valign]` — host + per-column vertical alignment** (gh#505, adia-v2 evidence-sources/organizations console tables). Every body-cell `align-items` was unconditionally `center`, which floats single-line neighbors (dates, status chips) at a different vertical center than a tall/wrapped primary cell — no way to opt into the Gmail/Linear/Notion "top-align, shared first-line scan anchor" convention. `<table-ui valign="top">` flips body cells to `align-items: flex-start` (default stays `center` — zero behavioral change for existing consumers); `columns[].valign` (or `<col-def valign="…">`) overrides the host per column, e.g. a `valign="center"` checkbox column inside a `valign="top"` table. Header cells and the structural checkbox/expand-toggle columns always stay centered regardless — they're single-line/fixed-size by contract, immune to the new token. New `--table-valign` mode token, added to the Sanctioned Mode Attributes table (`component-token-contract.md`). Tests 31 → 45.
43
+ - **`alert-ui` sectioned content anatomy** (gh#507) — card-ui's rhythm inside the content slot: semantic `<header>`/`<section>`/`<footer>` children of the slotted wrapper, component-owned gaps (`--alert-section-gap`, `--alert-footer-gap`), footer right-aligned with its own step of air. Free-form content is untouched (the column engages only when structural children are present). The system-banners pattern's six banners now compose this way.
44
+ - **`select-ui [no-radio]`** (gh#498, operator report). Opt-out for the single-select radio indicator on menu-shaped switchers — workspace/user identity menus — where the trigger already shows the committed value and a per-row radio reads as redundant. Drops only the dot: rows keep `[data-single-option]` layout and `aria-selected` (trigger + selected-row weight carry the signal); no effect under `[multiple]`. Applied to the admin-shell playground's workspace switcher, the reported surface. Tests 36 → 38.
45
+
46
+ ### Changed
47
+ - **Carded tables adopt `[frameless]`** (gh#500 resolution) — the 11 card-composed `table-ui` instances across six patterns drop their doubled perimeter per the gh#511 rule: inside card-ui, the container owns the frame. Standalone tables unchanged.
48
+ - **Default radius band: 6px–14px** (operator ruling 2026-07-30). `--a-radius-min` 0.25rem → 0.375rem, `--a-radius-max` 1.25rem → 0.875rem in `foundation/radius.css` and its `scale.css` ui-md byte-copy — every `--a-radius-xs/sm/md/lg` step re-clamps into the new band kit-wide. The ui-sm/ui-lg and content registers keep their own authored bands.
49
+ - **`drawer-ui`: a leading `stepper-ui` in the body is sticky wizard chrome** (operator ruling 2026-07-30) — pins just below the sticky header on the panel's own background, padded and bled to the body edges so panels scroll cleanly beneath; `--drawer-sticky-offset` overridable for taller headers.
50
+ - **`stepper-ui` state system re-cut** (operator ruling 2026-07-30, with mock): completed = `primary-container` bg + `primary` glyph, transparent border; current = `primary` bg + `on-primary` glyph, transparent border; next = transparent bg, `on-surface-variant` glyph, `outline-variant` border. Labels: current `on-surface`, completed/next `on-surface-variant` (new `--stepper-item-label-fg-done` token). All existing `--stepper-*` overrides keep working — only default values moved.
51
+ - **`field-ui` labels read at 13-14px** (operator ruling 2026-07-30): `--field-label-size` moves `--a-ui-sm` → `--a-ui-md` (12px → 13px base register; 14px in larger registers) — labels no longer read smaller than the control text beside them.
52
+ - **`description-list-ui [layout="inline"]` collapses responsively** — the two-column property|value grid stacks under 480px instead of squeezing the value column; pairs with the wizard-drawer review step's new settings-style layout.
53
+ - **`patterns/wizard-drawer` restructured** (operator rulings): stepper moves from header chrome to the FIRST content block (`header / content (stepper) / content / footer`); the review step swaps its stacked list for `description-list-ui layout="inline"` — the responsive two-column settings layout. Anatomy sample + rules updated; browser-probe green end-to-end.
54
+ - **`code-ui` color tokens map directly to `--md-sys-color-*` roles** (operator ask 2026-07-30). All 25 color refs (syntax `--code-tok-*`, editor chrome, lint markers) drop the `--a-*` alias hop for the md-sys role each alias already resolved to — value-identical except `--code-tok-function`: brand has no md-sys mirror, so it moves to `--md-sys-color-tertiary-active` (the identity-adjacent family). Layout/typography/motion tokens untouched; per-instance overrides work exactly as before.
55
+ - **`table-ui` sort carets render larger** (gh#496, operator report). The header sort indicator (`caret-up-down` idle, `arrow-up`/`arrow-down` active) moves `--a-icon-size` 0.75em → 0.95em — ~+2px at the heading's `--a-ui-xs` size, em-based so it keeps scaling with the register. `--table-sort-*` overrides untouched.
56
+
57
+ ### Maintenance
58
+ - **`core/` touched in this release window** (1 file(s), e.g. `core/provider.js`) — carried by the entries above.
59
+ - **`dist/` bundles rebuilt** in this cut's window (8 file(s)) — regenerated from the source changes described above, not independent edits.
60
+ - **`styles/` touched in this release window** (3 file(s), e.g. `foundation/radius.css`) — carried by the entries above.
61
+
3
62
  ## [0.8.22] — 2026-07-29
4
63
 
5
64
  ### Fixed
package/MIGRATION.md CHANGED
@@ -46,12 +46,21 @@ Version cuts and migration scope:
46
46
 
47
47
  - **`0.8.0`** — MINOR lockstep cut (2026-07-15). Internal ranges `^0.7.0` → `^0.8.0`. **BREAKING (3 items):** the Material color-token adoption — `--md-sys-color-*` (a static export) is now the color system's source of truth and public API. Removed: the 19 `--a-accent-*` back-compat aliases (→ `--a-primary-*`), the 189 raw per-family `--a-{family}-{0..6}-{tint-|shade-}scrim` primitives (→ the static `--md-sys-color-{family}-scrim-*` ladder), and the `primitives-accent.css` deep-import path (→ `primitives-primary.css`). **Behavioral:** the 8 named `[theme]` presets no longer re-color components (only brand hue + radius/density/shadow knobs still respond). See [§ v0.8.0 section below](#migrating-to-adia-aiv080-2026-07-15).
48
48
 
49
+ - **`0.8.23`** — PATCH lockstep cut (2026-07-30; operator ruling — an operator-ruled PATCH exception to the removals-are-MINOR convention). **BREAKING (1 item):** the `--a-font` back-compat alias is removed. See [§ v0.8.23 section below](#migrating-to-adia-aiv0823-2026-07-30).
50
+
49
51
  - **`0.8.11`** — PATCH lockstep cut (2026-07-23). Internal ranges stay at `^0.8.0`. **BREAKING: the size model.** Every component `size` enum is now exactly `sm | md | lg`; the removed tiers (`xs`, `xl`, icon's `2xl/3xl/4xl/fill`) are served by the new ancestor **`[scale]`** context — six hand-tabled tiers (`ui-sm · ui-md · ui-lg · content-sm · content-md · content-lg`): scale picks the table, `size` picks the row. `[verse]`/`[prose]` are deprecated aliases of `ui-sm`/`content-md` (removed at v1.0). Also new: `<adia-wordmark-ui>`. See [§ v0.8.11 section below](#migrating-to-adia-aiv0811-2026-07-23).
50
52
 
51
53
  Live demos and per-component docs at [ui-kit.exe.xyz/site/](https://ui-kit.exe.xyz/site/). The `adia-ui-migration` skill (in `.agents/skills/`) automates most of the mechanical sweeps in this guide.
52
54
 
53
55
  ---
54
56
 
57
+ ## Migrating to `@adia-ai/*@v0.8.23` (2026-07-30)
58
+
59
+ - **Removed: the `--a-font` alias** (pre-ADR-0035 name, DEPRECATED since the 0.8.x line). Any surviving `font-family: var(--a-font)` goes invalid-at-computed-value-time → `inherit` → UA **serif** — the visible symptom is Times-faced labels on an otherwise-correct page.
60
+ - **Mechanical sweep:** replace `var(--a-font)` → `var(--a-font-family)` — exact match only; `--a-font-family` / `--a-font-*` names are current API and must not be touched. The kit's own sweep used `codemod-demo-host-css.mjs --token-only`, which consumers can run against their tree.
61
+ - **Detection:** `audit-font-family-floor.mjs` Check B flags any `var(--a-font)` usage as CRITICAL; the forge/factory lint hooks flag it at write time.
62
+ - The 0.8.16 font-family floors on text-bearing primitives remain, so kit components stay sans even beside a broken host declaration — only YOUR selectors referencing the dead token regress.
63
+
55
64
  ## Migrating to `@adia-ai/*@v0.8.11` (2026-07-23)
56
65
 
57
66
  **PATCH lockstep cut.** Internal ranges stay at `^0.8.0`. One breaking theme: **the size model contracts to `sm | md | lg` everywhere, and an ancestor `[scale]` attribute takes over everything the extra tiers did.** `styles/scale.css` (new, opt-in like verse/prose; `dist/scale.min.css` + `dist/scale.sheet.js`) defines six explicit tiers — `ui-sm` (≡ old `[verse]`), `ui-md` (≡ base), `ui-lg`, `content-sm`, `content-md` (≡ old `[prose]`), `content-lg` — each a full hand-tabled re-declaration of the `--a-size/-inset/-gap/-radius/-icon/-caret/-toggle` families and all 13 type roles. Scale picks the table, `[size]` picks the row; an unscaled page renders byte-identically to v0.8.10.
@@ -56,8 +56,6 @@ states:
56
56
  description: Default, ready for interaction.
57
57
  traits: []
58
58
  tokens: {}
59
- requiredIcons:
60
- - check
61
59
  a2ui:
62
60
  rules:
63
61
  - rule: 'Multi-choice clarifying-question card emitted by an agent when it needs disambiguation before proceeding.'
@@ -34,6 +34,7 @@
34
34
  * trace-toggle — summary opened/closed
35
35
  */
36
36
 
37
+ import { defineIfFree } from '../../core/register.js';
37
38
  import { UIElement } from '../../core/element.js';
38
39
 
39
40
  class UIAgentTrace extends UIElement {
@@ -220,5 +221,7 @@ function escapeAttr(s) {
220
221
  return String(s).replace(/"/g, '&quot;');
221
222
  }
222
223
 
223
- customElements.define('agent-trace-ui', UIAgentTrace);
224
+ // defineIfFree (gh#534): the prod bundle + module graph load in parallel;
225
+ // second registration must no-op, not throw.
226
+ defineIfFree('agent-trace-ui', UIAgentTrace);
224
227
  export { UIAgentTrace };
@@ -179,7 +179,7 @@
179
179
  "description": "Close button. Stamped automatically when `closable` is set; the stamped element is `<button-ui slot=\"close\" icon=\"x\" variant=\"ghost\" size=\"sm\">`. Override by passing a custom `slot=\"close\"` button."
180
180
  },
181
181
  "content": {
182
- "description": "Alert body. For single-line text use the `text=` attribute (alert.js stamps a `<span slot=\"content\">` automatically). For rich content with title + description, wrap in `<col-ui slot=\"content\">` — the alert root is `display: flex` row, so multiple bare children would lay out side-by-side instead of stacking."
182
+ "description": "Alert body. For single-line text use the `text=` attribute (alert.js stamps a `<span slot=\"content\">` automatically). For structured banners use card-ui's section rhythm INSIDE the content element (gh#507): semantic `<header>` (title), `<section>` (body), and `<footer>` (actions right-aligned with its own step of air) children of the slotted wrapper; the component owns the gaps (`--alert-section-gap`, `--alert-footer-gap`), so spacing balances without per-banner col-ui/row-ui improvisation. Free-form content without those children lays out exactly as before."
183
183
  },
184
184
  "leading": {
185
185
  "description": "Leading icon. Stamped automatically from the `icon=` attribute; consumers can override by passing a custom `<icon-ui slot=\"leading\">`."
@@ -42,6 +42,11 @@ function _warnOnce(key, message) {
42
42
  }
43
43
 
44
44
  export class UIAlert extends UIElement {
45
+ // Phosphor icons this primitive auto-stamps (without consumer markup).
46
+ // Aggregated by installIconLoadersForRegistered() across all defined
47
+ // elements. Audited by check-required-icons.mjs.
48
+ static requiredIcons = ['x'];
49
+
45
50
  static properties = {
46
51
  text: { type: String, default: '', reflect: true },
47
52
  title: { type: String, default: '', reflect: true },
@@ -42,33 +42,38 @@
42
42
  background: var(--alert-bg);
43
43
  }
44
44
 
45
- /* ── Variants ── */
45
+ /* ── Variants ──
46
+ Icon fg = the family's on-container tonal-text role (gh#507): the icon
47
+ sits ON the -container tint, and the gh#434 gate-proven on-container
48
+ stops clear AA on both legs there (dark leg measured 5.6-9.2:1). The
49
+ former --a-<family>-strong is an on-SURFACE color — near-invisible
50
+ against its own family's dark-scheme container tint. */
46
51
  :scope[variant="info"] {
47
52
  --alert-bg: var(--md-sys-color-info-container);
48
53
  --alert-fg: var(--md-sys-color-info-on-surface);
49
54
  --alert-border: var(--md-sys-color-info-outline-variant);
50
- --alert-icon-fg: var(--a-info-strong);
55
+ --alert-icon-fg: var(--md-sys-color-info-on-info-container);
51
56
  }
52
57
 
53
58
  :scope[variant="success"] {
54
59
  --alert-bg: var(--md-sys-color-success-container);
55
60
  --alert-fg: var(--md-sys-color-success-on-surface);
56
61
  --alert-border: var(--md-sys-color-success-outline-variant);
57
- --alert-icon-fg: var(--a-success-strong);
62
+ --alert-icon-fg: var(--md-sys-color-success-on-success-container);
58
63
  }
59
64
 
60
65
  :scope[variant="warning"] {
61
66
  --alert-bg: var(--md-sys-color-warning-container);
62
67
  --alert-fg: var(--md-sys-color-warning-on-surface);
63
68
  --alert-border: var(--md-sys-color-warning-outline-variant);
64
- --alert-icon-fg: var(--a-warning-strong);
69
+ --alert-icon-fg: var(--md-sys-color-warning-on-warning-container);
65
70
  }
66
71
 
67
72
  :scope[variant="danger"] {
68
73
  --alert-bg: var(--md-sys-color-danger-container);
69
74
  --alert-fg: var(--md-sys-color-danger-on-surface);
70
75
  --alert-border: var(--md-sys-color-danger-outline-variant);
71
- --alert-icon-fg: var(--a-danger-strong);
76
+ --alert-icon-fg: var(--md-sys-color-danger-on-danger-container);
72
77
  }
73
78
 
74
79
  /* `muted` and `neutral` are semantic aliases of the base — same tokens
@@ -105,6 +110,40 @@
105
110
  min-width: 0;
106
111
  }
107
112
 
113
+ /* ── Sectioned content (gh#507) — card-ui's header/section/footer rhythm,
114
+ owned by the component so banner spacing stops being per-author col-ui/
115
+ row-ui gap improvisation. Authors write semantic children inside the
116
+ content slot:
117
+ <div slot="content">
118
+ <header>…title…</header>
119
+ <section>…body…</section>
120
+ <footer><button-ui …></footer>
121
+ </div>
122
+ The wrapper columnizes only when structural children are present, so
123
+ existing free-form content is untouched. */
124
+ :scope [slot="content"]:has(> header, & > section, & > footer) {
125
+ display: flex;
126
+ flex-direction: column;
127
+ gap: var(--alert-section-gap, var(--a-space-1));
128
+ }
129
+ :scope [slot="content"] > header,
130
+ :scope [slot="content"] > section,
131
+ :scope [slot="content"] > footer {
132
+ margin: 0;
133
+ padding: 0;
134
+ }
135
+ :scope [slot="content"] > header {
136
+ font-weight: var(--a-weight-medium);
137
+ }
138
+ /* Footer = the action band: right-aligned, its own step of air above so
139
+ a CTA never crowds the body (the live-update banner's reported cram). */
140
+ :scope [slot="content"] > footer {
141
+ display: flex;
142
+ gap: var(--a-space-2);
143
+ justify-content: flex-end;
144
+ margin-top: var(--alert-footer-gap, var(--a-space-1));
145
+ }
146
+
108
147
  /* Trailing action area — optional single button or small button group.
109
148
  flex-shrink:0 keeps it from collapsing; align-self:start anchors it
110
149
  to the first text line under the `align-items:start` root.
@@ -140,10 +140,14 @@ slots:
140
140
  content:
141
141
  description: >-
142
142
  Alert body. For single-line text use the `text=` attribute (alert.js
143
- stamps a `<span slot="content">` automatically). For rich content
144
- with title + description, wrap in `<col-ui slot="content">` — the
145
- alert root is `display: flex` row, so multiple bare children would
146
- lay out side-by-side instead of stacking.
143
+ stamps a `<span slot="content">` automatically). For structured
144
+ banners use card-ui's section rhythm INSIDE the content element
145
+ (gh#507): semantic `<header>` (title), `<section>` (body), and
146
+ `<footer>` (actions — right-aligned with its own step of air)
147
+ children of the slotted wrapper; the component owns the gaps
148
+ (`--alert-section-gap`, `--alert-footer-gap`), so spacing balances
149
+ without per-banner col-ui/row-ui improvisation. Free-form content
150
+ without those children lays out exactly as before.
147
151
  leading:
148
152
  description: >-
149
153
  Leading icon. Stamped automatically from the `icon=` attribute;
@@ -169,6 +173,8 @@ states:
169
173
  description: Default, ready for interaction.
170
174
  traits: []
171
175
  tokens: {}
176
+ requiredIcons:
177
+ - x
172
178
  a2ui:
173
179
  rules:
174
180
  - rule: 'Inline alert/banner for status messages within a content region. Severity via variant (info, success, warn, error).'
@@ -108,6 +108,9 @@ tokens:
108
108
  --calendar-grid-day-today-color:
109
109
  description: Accent color used for the today-indicator dot + numeral tint.
110
110
  default: var(--md-sys-color-primary)
111
+ requiredIcons:
112
+ - caret-left
113
+ - caret-right
111
114
  a2ui:
112
115
  rules:
113
116
  - >-
@@ -1,3 +1,4 @@
1
+ import { defineIfFree } from '../../core/register.js';
1
2
  import { UIElement } from '../../core/element.js';
2
3
  // NOTE: the A2UI renderer (<a2ui-root>) lives in @adia-ai/web-modules and is
3
4
  // loaded lazily + guarded inside #ensureRenderer() (kicked off from connected()).
@@ -243,4 +244,6 @@ export class UICanvas extends UIElement {
243
244
  }
244
245
  }
245
246
 
246
- customElements.define('canvas-ui', UICanvas);
247
+ // defineIfFree (gh#534): the prod bundle + module graph load in parallel;
248
+ // second registration must no-op, not throw.
249
+ defineIfFree('canvas-ui', UICanvas);
@@ -68,6 +68,18 @@
68
68
  flex: 1 1 auto;
69
69
  }
70
70
 
71
+ /* A card with a direct footer pins it to the bottom edge (gh#559):
72
+ in an equal-height grid/flex row, a shorter card's CTA otherwise
73
+ floats at content end while its siblings' sit lower. Scoped via
74
+ :has(> footer) so footerless cards keep plain block flow. */
75
+ :scope:has(> footer) {
76
+ display: flex;
77
+ flex-direction: column;
78
+ }
79
+ :scope > footer {
80
+ margin-top: auto;
81
+ }
82
+
71
83
  /* ═══════ Variants — token-only overrides ═══════ */
72
84
 
73
85
  /* `outline` is a documented alias for `outlined` (yaml). */
@@ -1,3 +1,4 @@
1
+ import { defineIfFree } from '../../core/register.js';
1
2
  import { UIElement } from '../../core/element.js';
2
3
 
3
4
  /**
@@ -272,6 +273,8 @@ class UIChatInput extends UIElement {
272
273
  this.#fileInput = null;
273
274
  }
274
275
  }
275
- customElements.define('chat-input-ui', UIChatInput);
276
+ // defineIfFree (gh#534): the prod bundle + module graph load in parallel;
277
+ // second registration must no-op, not throw.
278
+ defineIfFree('chat-input-ui', UIChatInput);
276
279
 
277
280
  export { UIChatInput };
@@ -13,9 +13,9 @@
13
13
  --code-bg: var(--md-sys-color-neutral-background);
14
14
  --code-fg: var(--md-sys-color-neutral-on-surface);
15
15
  --code-border: var(--md-sys-color-neutral-outline-variant);
16
- --code-header-bg: var(--a-bg-scrim);
17
- --code-header-fg: var(--a-fg-muted);
18
- --code-copy-hover-bg: var(--a-bg-muted);
16
+ --code-header-bg: var(--md-sys-color-neutral-scrim-weakest);
17
+ --code-header-fg: var(--md-sys-color-neutral-on-surface-variant);
18
+ --code-copy-hover-bg: var(--md-sys-color-neutral-container-low);
19
19
  --code-copy-hover-fg: var(--md-sys-color-neutral-on-surface);
20
20
 
21
21
  /* ── Typography ── */
@@ -31,47 +31,50 @@
31
31
  Active only when `[language]` triggers the CM mount. Still safe
32
32
  to emit in read-only mode; unused until `.cm-editor` is in the
33
33
  DOM. See docs/specs/code-editor.md §6. */
34
- --code-gutter-bg: var(--a-bg-subtle);
35
- --code-gutter-fg: var(--a-fg-muted);
34
+ --code-gutter-bg: var(--md-sys-color-neutral-surface);
35
+ --code-gutter-fg: var(--md-sys-color-neutral-on-surface-variant);
36
36
  /* Transparency-mixing in oklab — Safari < 18 has an OKLCH bug where
37
37
  the transparent operand's hue resolves to 0, red-shifting the mix.
38
38
  oklab is hue-agnostic and visually identical in fixed engines. */
39
39
  --code-active-line-bg: color-mix(in oklab, var(--md-sys-color-primary-container) 40%, transparent);
40
40
  --code-selection-bg: color-mix(in oklab, var(--md-sys-color-primary-container) 60%, transparent);
41
41
  --code-selection-match: color-mix(in oklab, var(--md-sys-color-primary-container) 30%, transparent);
42
- --code-cursor: var(--a-primary-strong);
42
+ --code-cursor: var(--md-sys-color-primary-active);
43
43
  --code-focus-ring: var(--a-focus-ring);
44
44
 
45
45
  /* ── Syntax highlight tokens ──
46
46
  Map Lezer/CodeMirror highlight tag names to AdiaUI semantic
47
- tokens. Overridable per-instance by setting `--code-tok-*` on
47
+ tokens mapped DIRECTLY to --md-sys-color-* roles (operator ask
48
+ 2026-07-30; the --a-* alias hop removed). tok-function uses
49
+ tertiary-active: brand has no md-sys mirror, and tertiary is the
50
+ identity-adjacent md-sys family. Overridable per-instance by setting `--code-tok-*` on
48
51
  any ancestor. See docs/specs/code-editor.md §6.3 for the tag →
49
52
  class → role map. */
50
53
  --code-tok-comment: var(--md-sys-color-neutral-on-surface-variant);
51
- --code-tok-keyword: var(--a-primary-strong);
52
- --code-tok-string: var(--a-success-strong);
53
- --code-tok-number: var(--a-info-strong);
54
- --code-tok-boolean: var(--a-info-strong);
54
+ --code-tok-keyword: var(--md-sys-color-primary-active);
55
+ --code-tok-string: var(--md-sys-color-success-active);
56
+ --code-tok-number: var(--md-sys-color-info-active);
57
+ --code-tok-boolean: var(--md-sys-color-info-active);
55
58
  --code-tok-operator: var(--md-sys-color-neutral-on-surface);
56
- --code-tok-punctuation: var(--a-fg-muted);
57
- --code-tok-function: var(--a-brand-strong);
59
+ --code-tok-punctuation: var(--md-sys-color-neutral-on-surface-variant);
60
+ --code-tok-function: var(--md-sys-color-tertiary-active);
58
61
  --code-tok-variable: var(--md-sys-color-neutral-on-surface);
59
- --code-tok-type: var(--a-warning-strong);
62
+ --code-tok-type: var(--md-sys-color-warning-active);
60
63
  --code-tok-property: var(--md-sys-color-neutral-on-surface);
61
- --code-tok-tag: var(--a-primary-strong);
62
- --code-tok-attribute: var(--a-warning-strong);
63
- --code-tok-url: var(--a-info-strong);
64
- --code-tok-invalid: var(--a-danger-strong);
64
+ --code-tok-tag: var(--md-sys-color-primary-active);
65
+ --code-tok-attribute: var(--md-sys-color-warning-active);
66
+ --code-tok-url: var(--md-sys-color-info-active);
67
+ --code-tok-invalid: var(--md-sys-color-danger-active);
65
68
 
66
69
  /* ── Lint markers (Phase 3 — editable JSON) ──
67
70
  `@codemirror/lint` paints gutter markers + diagnostic tooltips
68
71
  + inline underlines. Only fires in editable mode with a linter
69
72
  attached; read-only instances never see these selectors. */
70
- --code-lint-gutter-bg: var(--a-bg-subtle);
71
- --code-lint-error: var(--a-danger-strong);
72
- --code-lint-warning: var(--a-warning-strong);
73
- --code-lint-info: var(--a-info-strong);
74
- --code-lint-tooltip-bg: var(--a-bg-strong); /* §230-bundle (v0.5.9): elevated panel surface. Was orphaned --a-surface-floating. */
73
+ --code-lint-gutter-bg: var(--md-sys-color-neutral-surface);
74
+ --code-lint-error: var(--md-sys-color-danger-active);
75
+ --code-lint-warning: var(--md-sys-color-warning-active);
76
+ --code-lint-info: var(--md-sys-color-info-active);
77
+ --code-lint-tooltip-bg: var(--md-sys-color-neutral-surface-high); /* §230-bundle (v0.5.9): elevated panel surface. Was orphaned --a-surface-floating. */
75
78
  --code-lint-tooltip-fg: var(--md-sys-color-neutral-on-surface);
76
79
  --code-lint-tooltip-border: var(--md-sys-color-neutral-outline-variant);
77
80
  }
@@ -268,13 +271,13 @@
268
271
  pointer-events: none;
269
272
  }
270
273
  :scope[readonly] .cm-editor {
271
- background: var(--a-bg-subtle);
274
+ background: var(--md-sys-color-neutral-surface);
272
275
  }
273
276
  :scope[aria-invalid="true"] {
274
- border-color: var(--a-danger-strong);
277
+ border-color: var(--md-sys-color-danger-active);
275
278
  }
276
279
  :scope[aria-invalid="true"] .cm-focused {
277
- outline: 2px solid var(--a-danger-strong);
280
+ outline: 2px solid var(--md-sys-color-danger-active);
278
281
  outline-offset: -1px;
279
282
  }
280
283
 
@@ -58,7 +58,7 @@ export class UICombobox extends UIFormElement {
58
58
  // base-class deprecation warning that targets inert above-field labels.
59
59
  static labelDeprecated = false;
60
60
 
61
- static requiredIcons = ['caret-down', 'x-circle', 'check'];
61
+ static requiredIcons = ['caret-down', 'x-circle'];
62
62
 
63
63
  static properties = {
64
64
  ...UIFormElement.properties,
@@ -257,7 +257,6 @@ tokens:
257
257
  requiredIcons:
258
258
  - caret-down
259
259
  - x-circle
260
- - check
261
260
  a2ui:
262
261
  rules:
263
262
  - >-
@@ -130,7 +130,7 @@ export class UIDateRangePicker extends UIFormElement {
130
130
  // §154: Phosphor icons this primitive auto-stamps (without consumer
131
131
  // markup). Aggregated by installIconLoadersForRegistered() across all
132
132
  // defined elements. Audited by check-required-icons.mjs.
133
- static requiredIcons = ['calendar', 'caret-down', 'arrow-right'];
133
+ static requiredIcons = ['calendar', 'caret-down'];
134
134
 
135
135
  static get properties() {
136
136
  return {
@@ -195,7 +195,6 @@ tokens:
195
195
  requiredIcons:
196
196
  - calendar
197
197
  - caret-down
198
- - arrow-right
199
198
  a2ui:
200
199
  rules:
201
200
  - rule: 'DateRangePicker.value MUST be `{from, to}` with both ISO 8601 dates, OR null. Either side null is invalid mid-state and the validator should reject it (use `input` event for partial state).'
@@ -122,7 +122,7 @@ export class UIDatetimePicker extends UIFormElement {
122
122
  // §154: Phosphor icons this primitive auto-stamps (without consumer
123
123
  // markup). Aggregated by installIconLoadersForRegistered() across all
124
124
  // defined elements. Audited by check-required-icons.mjs.
125
- static requiredIcons = ['calendar', 'clock', 'caret-down'];
125
+ static requiredIcons = ['calendar', 'caret-down'];
126
126
 
127
127
  static get properties() {
128
128
  return {
@@ -210,7 +210,6 @@ tokens:
210
210
  default: var(--a-easing)
211
211
  requiredIcons:
212
212
  - calendar
213
- - clock
214
213
  - caret-down
215
214
  a2ui:
216
215
  rules:
@@ -54,6 +54,15 @@
54
54
  margin-bottom: var(--description-list-gap-row);
55
55
  }
56
56
 
57
+ /* Responsive collapse (operator ruling 2026-07-30, the wizard-drawer
58
+ review step's settings layout): the two-column property|value grid
59
+ stacks on narrow viewports rather than squeezing the value column. */
60
+ @media (max-width: 480px) {
61
+ :scope[layout="inline"] {
62
+ grid-template-columns: 1fr;
63
+ }
64
+ }
65
+
57
66
  /* Inline: term and desc on one row */
58
67
  :scope[layout="inline"] {
59
68
  grid-template-columns: minmax(var(--description-list-term-min-width), max-content) 1fr;
@@ -60,6 +60,11 @@ export class UIDrawer extends UIElement {
60
60
  // doesn't carry a stale reason. Per FEEDBACK-06 §2.
61
61
  #closeReason = 'programmatic';
62
62
 
63
+ // Phosphor icons this primitive auto-stamps (without consumer markup).
64
+ // Aggregated by installIconLoadersForRegistered() across all defined
65
+ // elements. Audited by check-required-icons.mjs.
66
+ static requiredIcons = ['x'];
67
+
63
68
  static properties = {
64
69
  text: { type: String, default: '', reflect: true },
65
70
  side: { type: String, default: 'right', reflect: true },
@@ -286,6 +286,22 @@ drawer-ui [slot="panel"] > [slot="header"] > :is([slot="action"], [slot="close"]
286
286
  gap: var(--drawer-header-gap);
287
287
  }
288
288
 
289
+ /* ═══════ Sticky progress block (operator ruling 2026-07-30) ═══════
290
+ A stepper-ui LEADING a drawer body is wizard chrome: it pins just below
291
+ the sticky header while the step panels scroll beneath — padded, on the
292
+ panel's own background so content can't ghost through, bled to the body
293
+ edges so the background spans full width. --drawer-sticky-offset
294
+ defaults to a one-line header's height (2×header-pad + one line +
295
+ divider); override for taller headers. */
296
+ drawer-ui [slot="panel"] > [slot="body"] > stepper-ui:first-child {
297
+ position: sticky;
298
+ top: var(--drawer-sticky-offset, calc(2 * var(--drawer-header-pad) + 1lh + 1px));
299
+ z-index: 1;
300
+ background: var(--drawer-bg);
301
+ margin: calc(-1 * var(--drawer-inset)) calc(-1 * var(--drawer-inset)) 0;
302
+ padding: var(--a-space-3) var(--drawer-inset);
303
+ }
304
+
289
305
  /* ═══════ Body (section) ═══════
290
306
  Padded by default. Multiple <section> siblings are allowed — they stack
291
307
  inside the panel's own scroll region (see panel's overflow-y + sticky
@@ -131,6 +131,8 @@ states:
131
131
  description: Default, ready for interaction.
132
132
  traits: []
133
133
  tokens: {}
134
+ requiredIcons:
135
+ - x
134
136
  a2ui:
135
137
  rules:
136
138
  - >-
@@ -43,6 +43,11 @@ import { UIElement, html } from '../../core/element.js';
43
43
  /* ── Container — one per position, mounted into document.body ── */
44
44
 
45
45
  export class UIFeedContainer extends UIElement {
46
+ // Phosphor icons this primitive auto-stamps (without consumer markup).
47
+ // Aggregated by installIconLoadersForRegistered() across all defined
48
+ // elements. Audited by check-required-icons.mjs.
49
+ static requiredIcons = ['x'];
50
+
46
51
  static properties = {
47
52
  position: { type: String, default: 'bottom-right', reflect: true },
48
53
  max: { type: Number, default: 5, reflect: true },
@@ -57,6 +57,8 @@ related:
57
57
  - Toast
58
58
  - Alert
59
59
 
60
+ requiredIcons:
61
+ - x
60
62
  a2ui:
61
63
  rules:
62
64
  - rule: 'Top-layer notification feed channel — singleton per position (top-right, bottom-center, etc.) mounted lazily into document.body.'
@@ -3,7 +3,10 @@
3
3
  /* ── Tokens ── */
4
4
  --field-gap: var(--a-space-2);
5
5
  --field-label-color: var(--md-sys-color-neutral-on-surface);
6
- --field-label-size: var(--a-ui-sm);
6
+ /* --a-ui-md, not -sm (operator ruling 2026-07-30): 13px base register /
7
+ 14px in larger registers — 12px labels read too small beside 13-14px
8
+ control text. */
9
+ --field-label-size: var(--a-ui-md);
7
10
  --field-label-weight: var(--a-weight-medium);
8
11
  --field-required-color: var(--a-danger-strong);
9
12
  --field-trailing-color: var(--md-sys-color-neutral-on-surface-variant);
@@ -66,6 +66,11 @@ export class UIInput extends UIFormElement {
66
66
  // motivated the deprecation.
67
67
  static labelDeprecated = false;
68
68
 
69
+ // Phosphor icons this primitive auto-stamps (without consumer markup).
70
+ // Aggregated by installIconLoadersForRegistered() across all defined
71
+ // elements. Audited by check-required-icons.mjs.
72
+ static requiredIcons = ['caret-up', 'caret-down'];
73
+
69
74
  static properties = {
70
75
  ...UIFormElement.properties,
71
76
  placeholder: { type: String, default: '', reflect: true },
@@ -238,6 +238,9 @@ tokens:
238
238
  description: Override border radius
239
239
  --input-transition:
240
240
  description: Override transition timing
241
+ requiredIcons:
242
+ - caret-up
243
+ - caret-down
241
244
  a2ui:
242
245
  rules:
243
246
  - >-