@adia-ai/web-components 0.7.23 → 0.7.25

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 (46) hide show
  1. package/CHANGELOG.md +56 -25
  2. package/README.md +2 -2
  3. package/USAGE.md +60 -15
  4. package/components/accordion/accordion-item.a2ui.json +30 -1
  5. package/components/accordion/accordion-item.yaml +33 -0
  6. package/components/accordion/accordion.a2ui.json +9 -0
  7. package/components/accordion/accordion.class.js +53 -3
  8. package/components/accordion/accordion.css +61 -3
  9. package/components/accordion/accordion.d.ts +12 -0
  10. package/components/accordion/accordion.yaml +17 -0
  11. package/components/code/code.class.js +1 -1
  12. package/components/display-field/display-field.a2ui.json +185 -0
  13. package/components/display-field/display-field.css +91 -0
  14. package/components/display-field/display-field.d.ts +26 -0
  15. package/components/display-field/display-field.examples.md +44 -0
  16. package/components/display-field/display-field.js +141 -0
  17. package/components/display-field/display-field.test.js +115 -0
  18. package/components/display-field/display-field.yaml +191 -0
  19. package/components/drawer/drawer.class.js +2 -2
  20. package/components/feed/feed.a2ui.json +1 -1
  21. package/components/feed/feed.class.js +1 -1
  22. package/components/feed/feed.d.ts +1 -1
  23. package/components/feed/feed.yaml +2 -2
  24. package/components/field/field.class.js +1 -1
  25. package/components/icon/icon.class.js +34 -1
  26. package/components/index.js +1 -0
  27. package/components/loading-overlay/loading-overlay.class.js +1 -1
  28. package/components/loading-overlay/loading-overlay.yaml +1 -1
  29. package/components/modal/modal.class.js +1 -1
  30. package/components/nav/nav.class.js +1 -1
  31. package/components/nav/nav.examples.md +4 -4
  32. package/components/popover/popover.class.js +1 -1
  33. package/components/spinner/spinner.class.js +1 -1
  34. package/components/spinner/spinner.yaml +1 -1
  35. package/components/table/table.class.js +42 -11
  36. package/components/table/table.test.js +53 -0
  37. package/components/toast/toast.class.js +1 -1
  38. package/core/icons.js +14 -0
  39. package/custom-elements.json +5723 -2865
  40. package/dist/theme-provider.min.js +1 -1
  41. package/dist/web-components.min.css +1 -1
  42. package/dist/web-components.min.js +87 -79
  43. package/dist/web-components.sheet.js +1 -1
  44. package/package.json +3 -2
  45. package/styles/components.css +1 -0
  46. package/traits/CATEGORIES.md +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # Changelog — @adia-ai/web-components
2
2
 
3
+ ## [0.7.25] — 2026-07-03
4
+
5
+ Upstream consumer finding from `adia-pay` (F10, sibling of F1–F9 shipped in 0.7.24) — a token-modeled `variant="flat|contained"` posture for the accordion, plus its two footnote bugs.
6
+
7
+ ### Added
8
+ - **`accordion-ui` / `accordion-item-ui` (`components/accordion/`) — `variant="flat|contained"` posture.** New reflected `variant` attribute on both the host and each item (default `flat`, pixel-unchanged from today — verified via the new `accordion-contained-probe.mjs`). `variant="contained"` on the host gives every child item a bounded surface — background, border, and radius spanning its header + open body, with a divider between them once expanded — the composition for embedding an accordion inside `<card-ui>`; a single item can opt out with its own `variant="flat"` (or opt IN against a flat host with `variant="contained"`). Four new item tokens carry the posture and are consumer-overridable in **both** postures: `--accordion-item-bg`, `--accordion-item-border` (full shorthand, mirrors `--card-border`), `--accordion-item-divider`, `--accordion-item-body-pt` — all default to the flat, invisible value. Contained is a token-only re-point plus three documented layout exceptions (`overflow:hidden` clip, `border-radius:0` on the header pill, and focus-ring relocation from the header onto the item surface) — added to `component-token-contract.md`'s sanctioned mode-attribute table alongside `progress-ui`/`pagination-ui`. Nested accordions stay isolated: a flat accordion inside a contained item's body is unaffected by the host's posture (direct-child `>` combinator, not a descendant selector). New examples: standalone contained, contained inside `card-ui`, and a per-item flat override. (upstream finding F10, ticket AC-1…AC-3/AC-5)
9
+
10
+ ### Fixed
11
+ - **`accordion-item-ui` — the disclosure caret now survives a custom `[slot="header"]`.** Authoring your own header (icon + title + action buttons) previously suppressed the caret entirely — the component only ever stamped one alongside a header it ALSO stamped. `connected()` now stamps a `caret-down` `icon-ui` into any authored header that doesn't already contain a `[slot="caret"]`, scoped to the header element specifically (a caret placed in `[slot="body"]` no longer falsely suppresses it). Author your own `[slot="caret"]` (even empty) to opt out — no new attribute; documented on the `caret` slot in `accordion-item.yaml`. (upstream finding F10 footnote FN-1)
12
+ - **`accordion-item-ui` — `[text]` no longer clobbers an authored `[slot="header-text"]`.** The auto-stamped label is now marked with a `data-autolabel` DOM attribute (survives disconnect→reconnect), and `render()` only drives that marked label — a hand-authored `[slot="header-text"]` inside a custom header is never overwritten. Setting `text` alongside an authored header-text label is now a silent no-op by design (was a silent clobber before). (upstream finding F10 footnote FN-2)
13
+ - **`accordion-item-ui` — the default (auto-stamped) header is now keyboard-operable.** It was mouse-only: no `tabindex`, no keydown handling. The auto-stamped header now carries `tabindex="0"`, `role="button"`, and `aria-expanded` (kept in sync on every toggle); `Enter` and `Space` both toggle it, routed through the existing click handler (`header.click()`) so there is exactly one toggle code path. A consumer-authored header (e.g. `<button slot="header">`) already worked and is unaffected — this closes a pre-existing gap for the default header only, folded into this arc on operator review (an additive a11y improvement, not a regression).
14
+
15
+ ### Maintenance
16
+ - **`dist/` bundles regenerated** (`web-components.min.{css,js}`, `web-components.sheet.js`, `theme-provider.min.js`) to carry the accordion changes above.
17
+ - **Doc-comment path references corrected** (`docs/specs/...` → `.claude/docs/specs/...`) across `patterns/` (2 `.examples.html` + 1 README) and `traits/` (`CATEGORIES.md` + 1 `.examples.html`) — propagated from the repo-wide `docs/` → `.claude/docs/` migration (2026-07-01) on the first full `components.mjs` regen since; no functional change, `styles/type-scale-review.examples.html` likewise.
18
+
19
+ ## [0.7.24] — 2026-07-01
20
+
21
+ Upstream consumer findings from `adia-pay` against installed `0.7.23` (F1–F6).
22
+
23
+ ### Added
24
+ - **`display-field-ui` (`components/display-field/`) — new display-only labeled-value primitive.** Fills a capability gap: a field-shaped read-out for a static or masked value (a card on file, `•••• 4242`, an account id) or a labeled slot that hosts a third-party element (a Stripe Element iframe) under a proper caption. Unlike `field-ui` it wraps **no** interactive control — no focus, no contenteditable, no form participation, no `[for]`/id-minting — so a readonly `input-ui` (which keeps input semantics) is no longer the only option. Props: `label` (required; names the group via `role="group"` + `aria-labelledby`), `value` (static text), `variant` (`default` | `masked` — mono/tabular treatment so `•••• 4242` aligns), `hint` (caption + `aria-describedby`), `icon` (leading glyph). The value comes from the `value` attr **or** an unnamed default-slot child (the foreign-element path) — slot content wins when both are present. Ships yaml + css + js + d.ts + a2ui sidecar + demo (`display-field.examples.html`) + 6 unit tests; registered in the component barrel + `styles/components.css`; `dist/` + `custom-elements.json` (120 → 121 modules) regenerated. Additive (PATCH). (upstream finding F5)
25
+ - **`table-ui` (`components/table/`) — `rowExpandable` per-row expand gate.** `expandable` was host-level: every row got the same `caret-right` toggle and one host-level `expandRenderer` drove all detail panes, so heterogeneous rows (a *succeeded* payment needing no detail vs. a *failed* one that expands to a reason) could not vary the affordance. New optional `rowExpandable(row, index) => boolean` callback gates the caret per row — non-expandable rows keep the reserved expand-column cell (grid alignment never shifts) but show no caret and can't open a detail pane; `toggleExpand()` is guarded to match (it still collapses an already-open row if the predicate later flips, so a row can't get stuck open). `null` (default) preserves the legacy "every row expandable" behavior. Demo: the `rowExpandable` section in `table.examples.html` (only Failed payments expand). Regression: 3 tests in `table.test.js`. Additive — no API removed (PATCH). (upstream finding F6)
26
+
27
+ ### Fixed
28
+ - **`icon-ui` (`components/icon/`) — fails loud when the icon registry was never wired.** Under Vite 8 / rolldown the zero-config `import.meta.glob('/node_modules/@phosphor-icons/…')` path in `icons-phosphor.js` does not transform from inside a published dependency, leaving the registry empty so every `<icon-ui>` renders blank — previously **silently** (a single missing icon warned, but a wholly-unwired registry did not). New `iconRegistryUnwired()` predicate (`core/icons.js`) distinguishes "never wired" (`registry.size === 0 && !registryReady`) from "one bad name"; `icon.class.js` schedules a one-shot, macrotask-deferred `console.error` naming the exact fix (install `@phosphor-icons/core` + call `installIconLoaders` from the app entry) only when a named icon resolves to nothing AND the registry is unwired. The deferral means a correctly-wired app entry is never false-flagged. Documented as the primary bundler path in `USAGE.md` § Icons. (upstream finding F1)
29
+ - **`custom-elements.json` — manifest no longer fabricates nonexistent modules.** `scripts/build/generate-custom-elements-json.mjs` emitted a `javascript-module` + class + `custom-element-definition` for every component yaml by assuming a `components/<dir>/<base>.js` file existed — fabricating e.g. `UIHeader` → `header.js` (the CSS-only `header-ui` has no class file) and pointing co-located children (menu-item-ui, menu-divider-ui, …) at nonexistent per-child `.js` files. The generator now scans the actual `.js` sources for `customElements.define` / `defineIfFree` / `defineIfUndefined` calls, maps each tag to the file that really defines it, groups declarations by real module path, and lists genuinely CSS-only tags (aside/footer/header/section-ui) as skipped. Regenerated manifest: 120 modules / 134 tags, 0 dangling file paths (was 110 modules carrying fabricated paths). (upstream finding F4)
30
+
31
+ ### Changed
32
+ - **`@phosphor-icons/core` is now a direct dependency (`^2.1.1`).** It was referenced by the icon-loader glob but absent from every dependency field, so the documented zero-config icon path could not resolve its SVG assets from a clean install. Added to `dependencies` so the assets resolve; the app still wires the loader glob from its own source graph (see `USAGE.md` § Icons / finding F1). (upstream finding F2)
33
+
3
34
  ## [0.7.23] — 2026-06-30
4
35
 
5
36
  ### Fixed
@@ -26,7 +57,7 @@
26
57
  ## [0.7.19] — 2026-06-10
27
58
 
28
59
  ### Fixed
29
- - **`icon-ui` — glyphs always paint on the standalone import path.** Importing `components/icon` directly (or a demo page loading only `./icon.js`) never loaded `core/icons-phosphor.js`, so the weight-loader map stayed empty and every `getIcon()` returned `''` — **silently** (the not-ready guard suppressed the warning): blank icons, zero console errors. Two fixes: `icon.js` now imports the Phosphor loader as a side-effect (the barrel paths already did), and `icon.class.js` gains a signal-reactive retry (`#rev` bumped by `whenIconRegistryReady`, aborted symmetrically in `disconnected()`) so elements that connect *after* the loaders install — e.g. fetched-and-injected demo markup — re-render once the registry is ready. Production (barrel-served) was never affected; the docs/demo single-component path was. Regression probe: `scripts/dev/icon-paint-probe.mjs`. `dist/web-components.min.js` (and the web-modules kitchen-sink bundle) regenerated to carry the fix.
60
+ - **`icon-ui` — glyphs always paint on the standalone import path.** Importing `components/icon` directly (or a demo page loading only `./icon.js`) never loaded `core/icons-phosphor.js`, so the weight-loader map stayed empty and every `getIcon()` returned `''` — **silently** (the not-ready guard suppressed the warning): blank icons, zero console errors. Two fixes: `icon.js` now imports the Phosphor loader as a side-effect (the barrel paths already did), and `icon.class.js` gains a signal-reactive retry (`#rev` bumped by `whenIconRegistryReady`, aborted symmetrically in `disconnected()`) so elements that connect *after* the loaders install — e.g. fetched-and-injected demo markup — re-render once the registry is ready. Production (barrel-served) was never affected; the .claude/docs/demo single-component path was. Regression probe: `scripts/dev/icon-paint-probe.mjs`. `dist/web-components.min.js` (and the web-modules kitchen-sink bundle) regenerated to carry the fix.
30
61
 
31
62
  ## [0.7.18] — 2026-06-09
32
63
 
@@ -84,7 +115,7 @@
84
115
 
85
116
  ### Changed
86
117
 
87
- - **BREAKING (pre-1.0 PATCH cadence) — the token-theme attribute `data-theme` is renamed `theme`.** `<html data-theme="ocean">` → `<html theme="ocean">`; `[data-theme="…"]` selectors → `[theme="…"]`; the token-root selector `:root, theme-ui, [data-theme]` → `:root, theme-ui, [theme], theme-provider` across the 14 foundation files (so `<theme-provider>` is itself a token-root). Cleaner, and it converges with `<canvas-ui theme="…">`, which already scoped AdiaUI themes the same way. Done as a repo-wide word-boundary sweep — compound attributes (`data-theme-slug`, `data-themes-grid`, `data-themed`, …) are preserved (the sweep also touched the styles/, traits/, and patterns/ trees). **Consumers using `data-theme="…"` must switch to `theme="…"`** — see `docs/MIGRATION GUIDE.md`.
118
+ - **BREAKING (pre-1.0 PATCH cadence) — the token-theme attribute `data-theme` is renamed `theme`.** `<html data-theme="ocean">` → `<html theme="ocean">`; `[data-theme="…"]` selectors → `[theme="…"]`; the token-root selector `:root, theme-ui, [data-theme]` → `:root, theme-ui, [theme], theme-provider` across the 14 foundation files (so `<theme-provider>` is itself a token-root). Cleaner, and it converges with `<canvas-ui theme="…">`, which already scoped AdiaUI themes the same way. Done as a repo-wide word-boundary sweep — compound attributes (`data-theme-slug`, `data-themes-grid`, `data-themed`, …) are preserved (the sweep also touched the styles/, traits/, and patterns/ trees). **Consumers using `data-theme="…"` must switch to `theme="…"`** — see `.claude/docs/MIGRATION GUIDE.md`.
88
119
  - **CDN bundles rebuilt** — `dist/web-components.min.{css,js}` + `dist/host.min.css` regenerated so the `[theme]` rename, `<theme-provider>` registration, and the new register twins reach `@adia-ai/web-components@0.7` CDN consumers.
89
120
 
90
121
  ### Fixed
@@ -196,7 +227,7 @@
196
227
 
197
228
  ### Changed
198
229
 
199
- - **Reverted the OD-5 `-default` token-shadowing layer — component CSS now declares a single un-suffixed token per property** — `var(--card-bg, var(--card-bg-default))` → `var(--card-bg)` across 119 component + module CSS files; variants/states re-point the un-suffixed `--<component>-*` token directly (`:where(:scope)` keeps the base at zero specificity). The 2026-05-24 OD-5 split (internal `--<c>-X-default` + public `--<c>-X` read through a fallback chain) added read-past noise without paying for itself: the one capability it bought — ancestor-surface inheritance of *component-named* tokens — had no real usage (a repo sweep found only on-element attribute hooks + parent→child composition, both of which win by specificity and survive). **Consumer overriding is unchanged** for the four real vectors (`--a-*` upstream · on-element `card-ui { --card-bg: … }` / inline · parent→child composition · `@layer overrides`); the *only* behavior change is that a `--<component>-*` token set on a non-targeting **ancestor** no longer inherits in (theme via `--a-*` or `@layer overrides`). No public component/prop API change. Inverse codemod `scripts/refactor/undo-od5-token-shadowing.mjs` (2-level + 3-level chain collapse, precise pair-derived strip, collision-safe residual rewire); forward `od5-token-shadowing.mjs` retired; `docs/specs/component-token-contract.md` rewritten (§History). Internal: `modal`/`swiper` `getComputedStyle` reads + 5 CSS-string-match tests re-pointed to the un-suffixed names. Verified: suite 1526/1526, `components --verify` clean, bundles fresh, visual QA (composition intact).
230
+ - **Reverted the OD-5 `-default` token-shadowing layer — component CSS now declares a single un-suffixed token per property** — `var(--card-bg, var(--card-bg-default))` → `var(--card-bg)` across 119 component + module CSS files; variants/states re-point the un-suffixed `--<component>-*` token directly (`:where(:scope)` keeps the base at zero specificity). The 2026-05-24 OD-5 split (internal `--<c>-X-default` + public `--<c>-X` read through a fallback chain) added read-past noise without paying for itself: the one capability it bought — ancestor-surface inheritance of *component-named* tokens — had no real usage (a repo sweep found only on-element attribute hooks + parent→child composition, both of which win by specificity and survive). **Consumer overriding is unchanged** for the four real vectors (`--a-*` upstream · on-element `card-ui { --card-bg: … }` / inline · parent→child composition · `@layer overrides`); the *only* behavior change is that a `--<component>-*` token set on a non-targeting **ancestor** no longer inherits in (theme via `--a-*` or `@layer overrides`). No public component/prop API change. Inverse codemod `scripts/refactor/undo-od5-token-shadowing.mjs` (2-level + 3-level chain collapse, precise pair-derived strip, collision-safe residual rewire); forward `od5-token-shadowing.mjs` retired; `.claude/docs/specs/component-token-contract.md` rewritten (§History). Internal: `modal`/`swiper` `getComputedStyle` reads + 5 CSS-string-match tests re-pointed to the un-suffixed names. Verified: suite 1526/1526, `components --verify` clean, bundles fresh, visual QA (composition intact).
200
231
  - **Typographic registers re-scaled — regular (default) + prose (MINOR behavior change)** — the regular register's inset compressed `8/16/24 → 14/16/18` and its gap re-based to a clean `value × --a-gap-k` multiplier (`8/12/16 → 10/12/14`, replacing the `half+half·k` form); `md` (the default tier) is unchanged for both, so only `[size=sm]`/`[size=lg]` containers shift. Prose moved one rung up (inset → 32/40/48, gap → 16/20/24). **Radius is now a register-scaled axis** — each register sets its own `--a-radius-min/max` (verse ≤16px · regular 20px=base · prose ≤28px) feeding the existing `clamp(min, k·max·tier-k, max)` formula. All values verified by `scripts/qa/register-scale-probe.mjs` (9 cells × 5 axes).
201
232
  - **Demo scaffolds migrated to the `--a-font-family` token** — the ~114 `patterns/` + `traits/` demo `.html` scaffolds (plus the per-component demos) swapped their hand-rolled `:where(html, body) { font-family: var(--a-font) }` boilerplate to `var(--a-font-family)`, tracking the font-family-floor token rename; `patterns/search-discovery` additionally dropped a stray inline `max-width` so the results card self-frames. Demo-layer migration only — no component or CSS-API change.
202
233
 
@@ -321,7 +352,7 @@
321
352
  ### Changed — global styles assigned to `@layer` cascade layers (ADR-0038)
322
353
 
323
354
  - `packages/web-components/styles/` now assigns every rule to a named cascade layer — `@layer reset, tokens, elements, components, utilities, context, overrides` — so precedence is **declared once** instead of faked per-rule via specificity/`:where()`. The published bundle + barrel carry the layers; **rule content is unchanged** — only layer membership. Cuts: `resets.css` → `reset`; `colors/` + `foundation/` → `tokens`; `type/elements.css` → `elements`; the 128 scoped components → `components`; `api/{sizing,text,layout}` + `[variant]` → `utilities`; `themes.css` + `prose.css` → `context`; `overrides` declared topmost and **reserved (empty)** for consumer rules. A consumer rule in `@layer overrides` now wins by layer order with no `!important` and no specificity war (proven: a `(0,0,1)` `badge-ui{font-weight:321}` override beats both the component's `@scope` 500 and a `[weight="bold"]` 700).
324
- - **One intentional behavior change (IPC-04).** Utility attributes — `[weight]` `[color]` `[transform]` `[text-align]` `[variant]` — now **override** a component's own `@scope` rule (they sit in `utilities`, above `components`). Previously they lost to component `@scope` by scope-proximity, and api/text.css documented that limitation — now retired. Measured blast radius: **39 component×property honorings across 128 components** (25 `color`, 10 `font-weight`, 3 `text-align`, 1 `text-transform`), zero breakage. Every *other* cut is behavior-neutral (real-browser computed-style probe 0 diffs; token identity byte-identical). The `ul/ol[role="list"]` reset and the `h1–h6` role defaults were relocated into `type/elements.css` (element-default refinements that must win by specificity *within* the `elements` layer). See ADR-0038 + `docs/specs/cascade-layer-architecture.md`.
355
+ - **One intentional behavior change (IPC-04).** Utility attributes — `[weight]` `[color]` `[transform]` `[text-align]` `[variant]` — now **override** a component's own `@scope` rule (they sit in `utilities`, above `components`). Previously they lost to component `@scope` by scope-proximity, and api/text.css documented that limitation — now retired. Measured blast radius: **39 component×property honorings across 128 components** (25 `color`, 10 `font-weight`, 3 `text-align`, 1 `text-transform`), zero breakage. Every *other* cut is behavior-neutral (real-browser computed-style probe 0 diffs; token identity byte-identical). The `ul/ol[role="list"]` reset and the `h1–h6` role defaults were relocated into `type/elements.css` (element-default refinements that must win by specificity *within* the `elements` layer). See ADR-0038 + `.claude/docs/specs/cascade-layer-architecture.md`.
325
356
 
326
357
  ### Added — `<select-ui>` per-option `icon` / `avatar`, reflected in the trigger
327
358
 
@@ -329,7 +360,7 @@
329
360
 
330
361
  ### Changed — block-level display by default + universal `[inline]` attribute (ADR-0037)
331
362
 
332
- - Composite / control / container primitives now default to a **block-level** host `display` (`flex`/`block`/`grid`); the new universal **`[inline]`** boolean opts back to inline-level (`inline-flex`/`inline-block`/`inline-grid`). Flipped 14 primitives: `button` (text → full-width block, icon-only held inline via `:not([text])`), `color-input`, `date-range-picker`, `datetime-picker`, `time-picker`, `breadcrumb`, `pagination`, `segmented`, `toggle-group`, `toggle-scheme`, `menu`, `popover`, `image`, `qr-code`. **Text-flow atoms stay inline** (`text` `link` `mark` `number-format` `relative-time` `inline-edit` `inline-message` `badge` `tag` `chip` `kbd` `icon` `radio` `check` `switch` `avatar` `swatch` `rating` `spinner`). CSS-only — per-component `:scope[inline]` (specificity 0,2,0 reliably overrides `:scope`); no yaml/JS/sidecar (universal-attribute precedent: `[grow]`/`[hidden]`). **Inside `row-ui`/`col-ui`/`grid-ui` the flip is a no-op** (flex/grid items are blockified), so real-composition rendering is unchanged — only standalone-in-block-flow goes full-width. `field`/`fields` carve out `[inline]` for their single-row *layout* mode. `api/layout.css` documents the per-component rationale. See ADR-0037 + `docs/specs/attribute-api-system.md` §5.
363
+ - Composite / control / container primitives now default to a **block-level** host `display` (`flex`/`block`/`grid`); the new universal **`[inline]`** boolean opts back to inline-level (`inline-flex`/`inline-block`/`inline-grid`). Flipped 14 primitives: `button` (text → full-width block, icon-only held inline via `:not([text])`), `color-input`, `date-range-picker`, `datetime-picker`, `time-picker`, `breadcrumb`, `pagination`, `segmented`, `toggle-group`, `toggle-scheme`, `menu`, `popover`, `image`, `qr-code`. **Text-flow atoms stay inline** (`text` `link` `mark` `number-format` `relative-time` `inline-edit` `inline-message` `badge` `tag` `chip` `kbd` `icon` `radio` `check` `switch` `avatar` `swatch` `rating` `spinner`). CSS-only — per-component `:scope[inline]` (specificity 0,2,0 reliably overrides `:scope`); no yaml/JS/sidecar (universal-attribute precedent: `[grow]`/`[hidden]`). **Inside `row-ui`/`col-ui`/`grid-ui` the flip is a no-op** (flex/grid items are blockified), so real-composition rendering is unchanged — only standalone-in-block-flow goes full-width. `field`/`fields` carve out `[inline]` for their single-row *layout* mode. `api/layout.css` documents the per-component rationale. See ADR-0037 + `.claude/docs/specs/attribute-api-system.md` §5.
333
364
 
334
365
  ### Changed — `chevron` → `caret` terminology sweep (FEEDBACK-90, ADR-0036 follow-up)
335
366
 
@@ -1103,11 +1134,11 @@ Three pre-v0.4.0 `patterns/*.examples.html` files used legacy markup retired by
1103
1134
  ## [0.6.15] — 2026-05-21
1104
1135
 
1105
1136
  ### Fixed
1106
- - **`installIconLoaders` / `installIconLoadersForRegistered` no longer silently accept a malformed shape (FB-07).** The previous behavior was `weightModules = { ...EMPTY_WEIGHTS, ...modules }` which spread any top-level keys verbatim. A flat path→svg glob (the documented quick-start shape; see FB-09) had its `/path/foo.svg` keys spread at the top level of `weightModules`, leaving every per-weight slot empty. Every `<icon-ui>` then rendered the icon NAME as visible text in its slot — confusing to debug because the per-icon `warnMissingIcon` console message points at name-resolution issues (Phosphor alias, `registerIcon`, etc.), not at loader-shape issues. The fix shape-detects three cases: (1) per-weight map keyed by `regular`/`thin`/`light`/`bold`/`fill`/`duotone` installs verbatim; (2) flat path→svg map auto-promotes to `{ regular: <flat> }` with a one-time `console.warn` so the caller knows to wrap explicitly (and so bold/fill/etc. aren't silently lost); (3) anything else logs `console.error` with the received key sample and aborts the install (registry stays unchanged). Net: the documented flat shape from `docs/guides/02-quick-start.md` continues to work but emits one diagnostic warn per install; consumers on prior versions writing the flat shape needed to wrap manually to get icons at all. Files: `packages/web-components/core/icons.js` (+59 LOC: `WEIGHT_KEYS` const, `isFlatGlobShape()`, `warnedShapes` WeakSet, three-branch dispatch inside `installIconLoaders`).
1137
+ - **`installIconLoaders` / `installIconLoadersForRegistered` no longer silently accept a malformed shape (FB-07).** The previous behavior was `weightModules = { ...EMPTY_WEIGHTS, ...modules }` which spread any top-level keys verbatim. A flat path→svg glob (the documented quick-start shape; see FB-09) had its `/path/foo.svg` keys spread at the top level of `weightModules`, leaving every per-weight slot empty. Every `<icon-ui>` then rendered the icon NAME as visible text in its slot — confusing to debug because the per-icon `warnMissingIcon` console message points at name-resolution issues (Phosphor alias, `registerIcon`, etc.), not at loader-shape issues. The fix shape-detects three cases: (1) per-weight map keyed by `regular`/`thin`/`light`/`bold`/`fill`/`duotone` installs verbatim; (2) flat path→svg map auto-promotes to `{ regular: <flat> }` with a one-time `console.warn` so the caller knows to wrap explicitly (and so bold/fill/etc. aren't silently lost); (3) anything else logs `console.error` with the received key sample and aborts the install (registry stays unchanged). Net: the documented flat shape from `.claude/docs/guides/02-quick-start.md` continues to work but emits one diagnostic warn per install; consumers on prior versions writing the flat shape needed to wrap manually to get icons at all. Files: `packages/web-components/core/icons.js` (+59 LOC: `WEIGHT_KEYS` const, `isFlatGlobShape()`, `warnedShapes` WeakSet, three-branch dispatch inside `installIconLoaders`).
1107
1138
  - **`core/icons-phosphor.js` no longer silently swallows `import.meta.glob` failures (FB-08).** Two silent-fail paths fixed. (a) **Empty-glob path** — `import.meta.glob('/node_modules/@phosphor-icons/core/assets/<weight>/*.svg', …)` returns `{}` (not an error) when zero files match. This happens when the consumer's Vite root sits below `node_modules`, or with pnpm/yarn-berry virtual paths, or when this module is loaded from a published package directory (the absolute-style glob is resolved against the Vite root, not this file's directory). Previously `hasViteGlob` was set true and `installIconLoaders({ regular: {}, … })` ran anyway, leaving the registry empty with no diagnostic. The fix sums per-weight entry counts after the `try` block; if all six weights have zero entries, emits a `console.warn` with the canonical manual-install snippet. (b) **Caught-error path** — the bare `catch {}` was scoped to handle "no Vite at all" (`ReferenceError` because `import.meta.glob` doesn't exist in static serving), but it also swallowed every OTHER error type (plugin order issues, Vite-config misconfigs, etc.). The catch now logs non-`ReferenceError` failures via `console.warn` before falling back to the manifest fetch. Files: `packages/web-components/core/icons-phosphor.js` (+38 LOC, -2 LOC).
1108
1139
 
1109
1140
  ### Docs
1110
- - **Quick-start + migration guide examples corrected (FB-09).** `docs/guides/02-quick-start.md` lines ~76-81 (boot sequence) and ~126-131 (the dedicated `installIconLoadersForRegistered` section) both showed the flat-glob form passed directly to `installIconLoadersForRegistered`. Both now wrap the glob in `{ regular: … }` to match the canonical contract. New `### Pitfall: flat glob silently auto-promoted (v0.6.15+)` section explains the FB-07 auto-promote behavior and when the per-weight form is strictly required (bold/fill/etc.). `docs/MIGRATION GUIDE.md` lines ~279-282 (the v0.5.3 §154 recommended upgrade example) had the same flat-glob bug — corrected. The skill `adia-ui-kit` v2.9.0 ships the same scaffold-template correction.
1141
+ - **Quick-start + migration guide examples corrected (FB-09).** `.claude/docs/guides/02-quick-start.md` lines ~76-81 (boot sequence) and ~126-131 (the dedicated `installIconLoadersForRegistered` section) both showed the flat-glob form passed directly to `installIconLoadersForRegistered`. Both now wrap the glob in `{ regular: … }` to match the canonical contract. New `### Pitfall: flat glob silently auto-promoted (v0.6.15+)` section explains the FB-07 auto-promote behavior and when the per-weight form is strictly required (bold/fill/etc.). `.claude/docs/MIGRATION GUIDE.md` lines ~279-282 (the v0.5.3 §154 recommended upgrade example) had the same flat-glob bug — corrected. The skill `adia-ui-kit` v2.9.0 ships the same scaffold-template correction.
1111
1142
 
1112
1143
  ### Verification
1113
1144
  - `verify:traits` 56/56 clean.
@@ -1341,7 +1372,7 @@ Files: `slider.css`, `slider.class.js`, `slider.examples.html`, `slider.yaml`, `
1341
1372
 
1342
1373
  ## [0.5.15] - 2026-05-16
1343
1374
 
1344
- > **Scope note:** §325 + §326 were originally planned as a separate v0.5.16 cycle (see retired `docs/plans/0.5.16-release-plan.md`); absorbed into v0.5.15 since both landed within the same release window (same-day commits prior to bump). The peer's v0.5.16 plan-doc explicitly anticipated this absorption option ("Hermes's call per `/lockstep-release`"). v0.5.16 plan-doc marked SHIPPED-IN-V0.5.15.
1375
+ > **Scope note:** §325 + §326 were originally planned as a separate v0.5.16 cycle (see retired `.claude/docs/plans/0.5.16-release-plan.md`); absorbed into v0.5.15 since both landed within the same release window (same-day commits prior to bump). The peer's v0.5.16 plan-doc explicitly anticipated this absorption option ("Hermes's call per `/lockstep-release`"). v0.5.16 plan-doc marked SHIPPED-IN-V0.5.15.
1345
1376
 
1346
1377
  ### v0.5.15 §326 — `<swatch-ui>` `[slot="chrome"]` lost in template contexts (FB-37; P1)
1347
1378
 
@@ -2748,7 +2779,7 @@ Also fixed `code.yaml`: removed a phantom `copy` event declaration (no runtime e
2748
2779
 
2749
2780
  ### Audit summary — hand-authored `.d.ts` cleanliness as of v0.4.9
2750
2781
 
2751
- After §90 + §91b sweeps, **all 17 form-bearing primitives' hand-authored `.d.ts` files are runtime-matched.** Audit recipe captured in `docs/plans/0.4.9-release-plan.md` §91b for future cycles — automatable as `scripts/release/check-form-bearing-dts.mjs` in a later cycle if drift returns.
2782
+ After §90 + §91b sweeps, **all 17 form-bearing primitives' hand-authored `.d.ts` files are runtime-matched.** Audit recipe captured in `.claude/docs/plans/0.4.9-release-plan.md` §91b for future cycles — automatable as `scripts/release/check-form-bearing-dts.mjs` in a later cycle if drift returns.
2752
2783
 
2753
2784
  ### Documentation — USAGE.md "Derived reactive bindings" subsection (§91d, FEEDBACK-04 #3)
2754
2785
 
@@ -3198,7 +3229,7 @@ Companion to the corpus simplification arc in `@adia-ai/a2ui-{runtime,compose,co
3198
3229
 
3199
3230
  Catalog (`packages/a2ui/corpus/catalog-a2ui_0_9.json`) regenerated to pick up every yaml addition. `verify:traits` clean at 56/56; `components --verify` clean at 125 files.
3200
3231
 
3201
- See root [CHANGELOG.md `[Unreleased]`](../../CHANGELOG.md) for the cross-cutting arc narrative + [docs/journal/2026/05/2026-05-12.md](../../docs/journal/2026/05/2026-05-12.md) §§ 43 / 44 / 50 / 51 for per-§ details.
3232
+ See root [CHANGELOG.md `[Unreleased]`](../../CHANGELOG.md) for the cross-cutting arc narrative + [.claude/docs/journal/2026/05/2026-05-12.md](../../.claude/docs/journal/2026/05/2026-05-12.md) §§ 43 / 44 / 50 / 51 for per-§ details.
3202
3233
  ## [0.4.3] - 2026-05-11
3203
3234
 
3204
3235
  ### Added
@@ -3214,7 +3245,7 @@ See root [CHANGELOG.md `[Unreleased]`](../../CHANGELOG.md) for the cross-cutting
3214
3245
 
3215
3246
  ### Lockstep
3216
3247
 
3217
- 9-package coordinated PATCH cut to v0.4.3 (per [`docs/specs/package-architecture.md` § 15](../../docs/specs/package-architecture.md#15-versioning-policy)). Internal `@adia-ai/*` dep ranges stay at `^0.4.0` (patch-cut asymmetry — `^0.4.0` covers `0.4.x` under semver). Ratifies [ADR-0027](../../.brain/adrs/0027-cross-primitive-composition-imports.md) — cross-primitive composition imports are the consumer's responsibility; the primitive's `.js` does NOT side-effect-import composed primitives. Source files touched: `components/input/input.{js,css,yaml,a2ui.json}`. See root [CHANGELOG.md `## [0.4.3]`](../../CHANGELOG.md) for the cut narrative.
3248
+ 9-package coordinated PATCH cut to v0.4.3 (per [`.claude/docs/specs/package-architecture.md` § 15](../../.claude/docs/specs/package-architecture.md#15-versioning-policy)). Internal `@adia-ai/*` dep ranges stay at `^0.4.0` (patch-cut asymmetry — `^0.4.0` covers `0.4.x` under semver). Ratifies [ADR-0027](../../.brain/adrs/0027-cross-primitive-composition-imports.md) — cross-primitive composition imports are the consumer's responsibility; the primitive's `.js` does NOT side-effect-import composed primitives. Source files touched: `components/input/input.{js,css,yaml,a2ui.json}`. See root [CHANGELOG.md `## [0.4.3]`](../../CHANGELOG.md) for the cut narrative.
3218
3249
  ## [0.4.2] - 2026-05-11
3219
3250
 
3220
3251
  ### Changed
@@ -3251,7 +3282,7 @@ See root [CHANGELOG.md `[Unreleased]`](../../CHANGELOG.md) for the cross-cutting
3251
3282
 
3252
3283
  ### Lockstep
3253
3284
 
3254
- 9-package coordinated PATCH cut to v0.4.2 (per [`docs/specs/package-architecture.md` § 15](../../docs/specs/package-architecture.md#15-versioning-policy)). Internal `@adia-ai/*` dep ranges stay at `^0.4.0` (patch-cut asymmetry — `^0.4.0` covers `0.4.x` under semver). ADR-0025 ([`.brain/adrs/0025-no-native-form-controls.md`](../../.brain/adrs/0025-no-native-form-controls.md)) ratified — codifies the "no native or suppressed-native interactive widgets in primitives" principle that the `<input-ui type="number">` rewrite implements; `password` remains the documented exception (physically requires `<input type="password">` for `-webkit-text-security`). See root [CHANGELOG.md `## [0.4.2]`](../../CHANGELOG.md) for the cut narrative.
3285
+ 9-package coordinated PATCH cut to v0.4.2 (per [`.claude/docs/specs/package-architecture.md` § 15](../../.claude/docs/specs/package-architecture.md#15-versioning-policy)). Internal `@adia-ai/*` dep ranges stay at `^0.4.0` (patch-cut asymmetry — `^0.4.0` covers `0.4.x` under semver). ADR-0025 ([`.brain/adrs/0025-no-native-form-controls.md`](../../.brain/adrs/0025-no-native-form-controls.md)) ratified — codifies the "no native or suppressed-native interactive widgets in primitives" principle that the `<input-ui type="number">` rewrite implements; `password` remains the documented exception (physically requires `<input type="password">` for `-webkit-text-security`). See root [CHANGELOG.md `## [0.4.2]`](../../CHANGELOG.md) for the cut narrative.
3255
3286
  ## [0.4.1] - 2026-05-10
3256
3287
 
3257
3288
  ### Ride-along (no source changes)
@@ -3283,7 +3314,7 @@ Lockstep version bump only — source byte-identical to v0.3.4. Internal `@adia-
3283
3314
  Lockstep version bump only — source byte-identical to v0.3.3. Internal `@adia-ai/*` dep ranges remain at `^0.3.0`. See root [CHANGELOG.md `## [0.3.4]`](../../CHANGELOG.md) for the cut narrative.
3284
3315
  ## [0.3.3] - 2026-05-07
3285
3316
 
3286
- **Lockstep cut.** All 9 published `@adia-ai/*` packages now share version `0.3.3`, governed by [`docs/specs/package-architecture.md` § 15](../../docs/specs/package-architecture.md#15-versioning-policy).
3317
+ **Lockstep cut.** All 9 published `@adia-ai/*` packages now share version `0.3.3`, governed by [`.claude/docs/specs/package-architecture.md` § 15](../../.claude/docs/specs/package-architecture.md#15-versioning-policy).
3287
3318
 
3288
3319
  ### Changed
3289
3320
 
@@ -3296,7 +3327,7 @@ Lockstep version bump only — source byte-identical to v0.3.3. Internal `@adia-
3296
3327
  ## [0.3.2] - 2026-05-06
3297
3328
 
3298
3329
  **9-package lockstep patch cut to v0.3.2.** All lockstep members share
3299
- one version per [`docs/specs/package-architecture.md` § 15](../../docs/specs/package-architecture.md#15-versioning-policy).
3330
+ one version per [`.claude/docs/specs/package-architecture.md` § 15](../../.claude/docs/specs/package-architecture.md#15-versioning-policy).
3300
3331
  Internal `@adia-ai/*` dep ranges unchanged at `^0.3.0`.
3301
3332
 
3302
3333
  ### No source changes
@@ -3309,7 +3340,7 @@ version only.
3309
3340
  - `version`: `0.3.1` → `0.3.2`.
3310
3341
  ## [0.3.1] - 2026-05-06
3311
3342
 
3312
- **9-package lockstep patch cut + folder-per-trait restructure.** All 9 published `@adia-ai/*` packages bump 0.3.0 → 0.3.1 per [`docs/specs/package-architecture.md` § 15](../../docs/specs/package-architecture.md#15-versioning-policy). Internal `@adia-ai/*` dep ranges remain at `^0.3.0` (covers `0.3.1` under semver — patch-cut asymmetry).
3343
+ **9-package lockstep patch cut + folder-per-trait restructure.** All 9 published `@adia-ai/*` packages bump 0.3.0 → 0.3.1 per [`.claude/docs/specs/package-architecture.md` § 15](../../.claude/docs/specs/package-architecture.md#15-versioning-policy). Internal `@adia-ai/*` dep ranges remain at `^0.3.0` (covers `0.3.1` under semver — patch-cut asymmetry).
3313
3344
 
3314
3345
  This is a **patch cut on top of v0.3.0, no BREAKING changes for npm consumers.** The barrel API (`@adia-ai/web-components/traits`, `@adia-ai/web-components/components`, etc.) is byte-equivalent to v0.3.0 at the public surface. The change is internal: the trait library moved from a flat `traits/<name>.{html,js,examples.html,examples.js,test.js}` layout to one folder per trait (`traits/<name>/<name>.{...}`), mirroring the existing `components/<name>/` convention.
3315
3346
 
@@ -3330,7 +3361,7 @@ Symmetry with `components/<name>/` (already folder-per for months) — easier to
3330
3361
  - `packages/a2ui/compose/transpiler/transpiler-maps.js` — no change since v0.3.0 (the v0.3.0 entry mentioned this file; correction: that update was part of v0.3.0).
3331
3362
  ## [0.3.0] - 2026-05-05
3332
3363
 
3333
- **9-package lockstep cut.** All 9 published `@adia-ai/*` packages bump 0.2.5 → 0.3.0 per [`docs/specs/package-architecture.md` § 15](../../docs/specs/package-architecture.md#15-versioning-policy). Internal `@adia-ai/*` dep ranges bump `^0.2.0` → `^0.3.0`.
3364
+ **9-package lockstep cut.** All 9 published `@adia-ai/*` packages bump 0.2.5 → 0.3.0 per [`.claude/docs/specs/package-architecture.md` § 15](../../.claude/docs/specs/package-architecture.md#15-versioning-policy). Internal `@adia-ai/*` dep ranges bump `^0.2.0` → `^0.3.0`.
3334
3365
 
3335
3366
  The lockstep policy expanded from 8 to 9 packages with this cut — `@adia-ai/llm` joins as a foundational primitive (extracted from `@adia-ai/a2ui-compose/llm`), and `@adia-ai/a2ui-utils` was renamed to `@adia-ai/a2ui-runtime`. Both are BREAKING for npm consumers (acceptable under pre-1.0 semver).
3336
3367
 
@@ -3352,7 +3383,7 @@ The following directories saw significant in-repo changes between v0.2.5 and v0.
3352
3383
  - `patterns/` — no source changes since v0.2.5.
3353
3384
  ## [0.2.5] - 2026-05-04
3354
3385
 
3355
- **Lockstep cut + new `<fields-ui>` form-grid primitive + `draggable-list-item` last-item drop-zone fix.** All 8 published `@adia-ai/*` packages bump 0.2.4 → 0.2.5 per [`docs/specs/package-architecture.md` § 15](../../docs/specs/package-architecture.md#15-versioning-policy). Patch cut — **no BREAKING changes**.
3386
+ **Lockstep cut + new `<fields-ui>` form-grid primitive + `draggable-list-item` last-item drop-zone fix.** All 8 published `@adia-ai/*` packages bump 0.2.4 → 0.2.5 per [`.claude/docs/specs/package-architecture.md` § 15](../../.claude/docs/specs/package-architecture.md#15-versioning-policy). Patch cut — **no BREAKING changes**.
3356
3387
 
3357
3388
  ### Added
3358
3389
 
@@ -3374,7 +3405,7 @@ The following directories saw significant in-repo changes between v0.2.5 and v0.
3374
3405
  - `styles/components.css` adds `@import "../components/fields/fields.css"`.
3375
3406
  ## [0.2.4] - 2026-05-04
3376
3407
 
3377
- **Lockstep cut + Tier 4 capability adds + 2 architectural rewrites.** All 8 published `@adia-ai/*` packages bump 0.2.3 → 0.2.4 per [`docs/specs/package-architecture.md` § 15](../../docs/specs/package-architecture.md#15-versioning-policy). Patch cut — no breaking changes.
3408
+ **Lockstep cut + Tier 4 capability adds + 2 architectural rewrites.** All 8 published `@adia-ai/*` packages bump 0.2.3 → 0.2.4 per [`.claude/docs/specs/package-architecture.md` § 15](../../.claude/docs/specs/package-architecture.md#15-versioning-policy). Patch cut — no breaking changes.
3378
3409
 
3379
3410
  ### Changed
3380
3411
 
@@ -3384,8 +3415,8 @@ The following directories saw significant in-repo changes between v0.2.5 and v0.
3384
3415
  - `traits/index.js` adds 12 new exports (the 11 Tier-4 traits + droppable from the parallel Tasks UI initiative); `traits/_catalog.json` regenerated to 56 entries; `core/icons.js` extended with ~25 new aliases.
3385
3416
 
3386
3417
  The substantive content of the next cut, surfaced from the
3387
- 2026-05-04 trait-library lift initiative (`docs/PLAN.md`,
3388
- `docs/reports/traits-creative-director-audit-2026-05-04.md`).
3418
+ 2026-05-04 trait-library lift initiative (`.claude/docs/PLAN.md`,
3419
+ `.claude/docs/reports/traits-creative-director-audit-2026-05-04.md`).
3389
3420
  **No BREAKING changes.** Every public tag, prop, event, and
3390
3421
  declarative `traits=` attribute from v0.2.3 still works.
3391
3422
 
@@ -3638,7 +3669,7 @@ warnings on every page that referenced these:
3638
3669
 
3639
3670
  **Lockstep cut + accumulated fixes.** All 8 published `@adia-ai/*`
3640
3671
  packages bump 0.2.2 → 0.2.3 per
3641
- [`docs/specs/package-architecture.md` § 15](../../docs/specs/package-architecture.md#15-versioning-policy).
3672
+ [`.claude/docs/specs/package-architecture.md` § 15](../../.claude/docs/specs/package-architecture.md#15-versioning-policy).
3642
3673
  Patch cut — no breaking changes. The substantive content is
3643
3674
  `web-components`-only; the other 7 packages have no source change.
3644
3675
 
@@ -3692,7 +3723,7 @@ edge instead of leaving the column reading as dead width. Per-row
3692
3723
  ---
3693
3724
  ## [0.2.2] - 2026-05-02
3694
3725
 
3695
- **Lockstep cut + trait coverage 100% + `<traits-host>` wrapper.** All 8 published `@adia-ai/*` packages bump 0.2.1 → 0.2.2 per [`docs/specs/package-architecture.md` § 15](../../docs/specs/package-architecture.md#15-versioning-policy). Patch cut — no breaking changes.
3726
+ **Lockstep cut + trait coverage 100% + `<traits-host>` wrapper.** All 8 published `@adia-ai/*` packages bump 0.2.1 → 0.2.2 per [`.claude/docs/specs/package-architecture.md` § 15](../../.claude/docs/specs/package-architecture.md#15-versioning-policy). Patch cut — no breaking changes.
3696
3727
 
3697
3728
  ### Changed
3698
3729
 
@@ -3711,7 +3742,7 @@ Follow-up landing on top of the v0.2.1 trait-system work — closes the two foll
3711
3742
  ---
3712
3743
  ## [0.2.1] - 2026-05-02
3713
3744
 
3714
- **Lockstep cut + 41-trait library overhaul + `<stat-ui>` ad-blocker rename.** All 8 published `@adia-ai/*` packages bump 0.2.0 → 0.2.1 per [`docs/specs/package-architecture.md` § 15](../../docs/specs/package-architecture.md#15-versioning-policy). Patch cut — no breaking changes.
3745
+ **Lockstep cut + 41-trait library overhaul + `<stat-ui>` ad-blocker rename.** All 8 published `@adia-ai/*` packages bump 0.2.0 → 0.2.1 per [`.claude/docs/specs/package-architecture.md` § 15](../../.claude/docs/specs/package-architecture.md#15-versioning-policy). Patch cut — no breaking changes.
3715
3746
 
3716
3747
  ### Changed
3717
3748
 
@@ -3803,8 +3834,8 @@ transition rather than snapping. No JS changes; no API changes.
3803
3834
  ## [0.2.0] - 2026-05-02
3804
3835
 
3805
3836
  **Lockstep cut.** All 8 published `@adia-ai/*` packages now share one
3806
- version, governed by [`docs/specs/package-architecture.md` § 15
3807
- (Versioning Policy)](../../docs/specs/package-architecture.md#15-versioning-policy)
3837
+ version, governed by [`.claude/docs/specs/package-architecture.md` § 15
3838
+ (Versioning Policy)](../../.claude/docs/specs/package-architecture.md#15-versioning-policy)
3808
3839
  and enforced by `npm run check:lockstep` + the
3809
3840
  `lockstep-versioning` job in
3810
3841
  [`.github/workflows/docs-lint.yml`](../../.github/workflows/docs-lint.yml).
package/README.md CHANGED
@@ -117,7 +117,7 @@ web-components/
117
117
  │ <traits-host> wrapper for raw-HTML declarative
118
118
  │ composition. Generated catalog at _catalog.json
119
119
  │ drives the MCP get_traits tool + per-trait demo
120
- │ pages. Full contract in docs/specs/traits.md.
120
+ │ pages. Full contract in .claude/docs/specs/traits.md.
121
121
 
122
122
  ├── a2ui/ — deprecation shim for one release
123
123
  │ └── index.js Re-exports @adia-ai/a2ui-runtime with a
@@ -247,7 +247,7 @@ class MyPanel extends UIElement {
247
247
 
248
248
  The `el` parameter is the element instance — every signal-backed property is reactively read.
249
249
 
250
- Full authoring contract: [`docs/specs/component-token-contract.md`](../../docs/specs/component-token-contract.md).
250
+ Full authoring contract: [`.claude/docs/specs/component-token-contract.md`](../../.claude/docs/specs/component-token-contract.md).
251
251
  The `adia-ui-authoring` skill encodes the 20 non-negotiable rules.
252
252
 
253
253
  ## Card-n / drawer-ui composition parity
package/USAGE.md CHANGED
@@ -9,17 +9,18 @@ The complete reference for engineers and agents **integrating** AdiaUI into an a
9
9
  ## Contents
10
10
 
11
11
  1. [Install](#install)
12
- 2. [The mental model](#the-mental-model)
13
- 3. [Property reactivity (signal-backed)](#property-reactivity-signal-backed)
14
- 4. [Property binding patterns (templates / framework integration)](#property-binding-patterns)
15
- 5. [Event contract `CustomEvent` with `detail`](#event-contract)
16
- 6. [Form participation — `UIFormElement` + `ElementInternals`](#form-participation)
17
- 7. [Data binding with `data-stream-*`](#data-binding-with-data-stream-)
18
- 8. [Lifecycle `connected` / `render` / `updated` / `disconnected`](#lifecycle)
19
- 9. [Theming, density, size](#theming-density-size)
20
- 10. [Registration auto vs explicit](#registration--auto-vs-explicit)
21
- 11. [TypeScript](#typescript)
22
- 12. [Anti-patterns](#anti-patterns)
12
+ 2. [Icons wiring the registry](#icons--wiring-the-registry)
13
+ 3. [The mental model](#the-mental-model)
14
+ 4. [Property reactivity (signal-backed)](#property-reactivity-signal-backed)
15
+ 5. [Property binding patterns (templates / framework integration)](#property-binding-patterns)
16
+ 6. [Event contract — `CustomEvent` with `detail`](#event-contract)
17
+ 7. [Form participation `UIFormElement` + `ElementInternals`](#form-participation)
18
+ 8. [Data binding with `data-stream-*`](#data-binding-with-data-stream-)
19
+ 9. [Lifecycle `connected` / `render` / `updated` / `disconnected`](#lifecycle)
20
+ 10. [Theming, density, size](#theming-density-size)
21
+ 11. [Registration — auto vs explicit](#registration--auto-vs-explicit)
22
+ 12. [TypeScript](#typescript)
23
+ 13. [Anti-patterns](#anti-patterns)
23
24
 
24
25
  ---
25
26
 
@@ -96,6 +97,50 @@ For the live demo of every component: <https://ui-kit.exe.xyz/site/components/>.
96
97
 
97
98
  ---
98
99
 
100
+ ## Icons — wiring the registry
101
+
102
+ `<icon-ui>` (and every component that renders an icon — `<button-ui icon>`,
103
+ `<badge-ui>`, menus, etc.) draws from an **icon registry** that your app
104
+ must wire **once, from your own entry module**. Out of the box the registry
105
+ is empty; an un-wired registry renders every icon **blank**.
106
+
107
+ **Why the app has to do it.** The zero-config glob in the package's own
108
+ `icons-phosphor.js` uses `import.meta.glob('/node_modules/@phosphor-icons/…')`,
109
+ which is a **build-time macro your bundler rewrites** — and under Vite 8 /
110
+ rolldown that rewrite **does not happen for code imported from inside a
111
+ published dependency**. The glob has to be evaluated in *your* source graph.
112
+ `@phosphor-icons/core` ships as a dependency of this package (so the SVG
113
+ assets resolve), but the glob that reads them must live in your app.
114
+
115
+ **The one-time wiring (do this in your app entry, before first paint):**
116
+
117
+ ```js
118
+ import { installIconLoaders } from '@adia-ai/web-components/core/icons';
119
+
120
+ installIconLoaders({
121
+ regular: import.meta.glob(
122
+ '/node_modules/@phosphor-icons/core/assets/regular/*.svg',
123
+ { query: '?raw', import: 'default', eager: true },
124
+ ),
125
+ // add other weights only if you use them:
126
+ // bold: import.meta.glob('/node_modules/@phosphor-icons/core/assets/bold/*.svg', { query: '?raw', import: 'default', eager: true }),
127
+ // fill: import.meta.glob('/node_modules/@phosphor-icons/core/assets/fill/*.svg', { query: '?raw', import: 'default', eager: true }),
128
+ });
129
+ ```
130
+
131
+ `installIconLoaders` is idempotent, may be called before or after the
132
+ elements connect (icons already on the page repaint when it runs), and
133
+ accepts either the per-weight map above or a flat `path → svg-string` map
134
+ if you source icons some other way (`registerIcon(name, svg)` /
135
+ `registerIcons(map)` are the lower-level primitives).
136
+
137
+ **Fail-loud, not fail-blank.** If any `<icon-ui name="…">` renders and the
138
+ registry was never wired, the component logs a one-shot `console.error`
139
+ naming this exact fix. A single *missing* icon (registry wired, that one
140
+ name absent) warns separately and does not trip the un-wired error.
141
+
142
+ ---
143
+
99
144
  ## The mental model
100
145
 
101
146
  Every AdiaUI primitive is a **standard light-DOM custom element**. There's no shadow DOM, no compiler, no framework — just `customElements.define()` extending a small `UIElement` base class. The base class wires a signal-backed property system on top.
@@ -421,7 +466,7 @@ export class UIModal extends UIElement {
421
466
  }
422
467
  ```
423
468
 
424
- Reference: [`docs/conventions/component-events.md`](../../docs/conventions/component-events.md) for the 1-page convention summary; [`scripts/build/dts-codegen.mjs`](../../scripts/build/dts-codegen.mjs) for the codegen logic that reads `x-adiaui.events`; [`scripts/release/check-form-bearing-dts.mjs`](../../scripts/release/check-form-bearing-dts.mjs) for the drift audit.
469
+ Reference: [`.claude/docs/conventions/component-events.md`](../../.claude/docs/conventions/component-events.md) for the 1-page convention summary; [`scripts/build/dts-codegen.mjs`](../../scripts/build/dts-codegen.mjs) for the codegen logic that reads `x-adiaui.events`; [`scripts/release/check-form-bearing-dts.mjs`](../../scripts/release/check-form-bearing-dts.mjs) for the drift audit.
425
470
 
426
471
  ---
427
472
 
@@ -1586,8 +1631,8 @@ If your design needs label-on-tile (e.g. Tokens Studio's C1.3 hand-rolled `<div
1586
1631
  - [`README.md`](./README.md) — package overview + authoring guide
1587
1632
  - [Component yaml contracts](./components/) — every primitive's complete API (props, events, slots, examples)
1588
1633
  - [Live demos](https://ui-kit.exe.xyz/site/components/) — every primitive in action
1589
- - [`docs/specs/component-token-contract.md`](../../docs/specs/component-token-contract.md) — full authoring contract
1590
- - [`docs/specs/component-implementation-patterns.md`](../../docs/specs/component-implementation-patterns.md) — render strategies
1591
- - [`docs/specs/traits.md`](../../docs/specs/traits.md) — composable behaviors
1634
+ - [`.claude/docs/specs/component-token-contract.md`](../../.claude/docs/specs/component-token-contract.md) — full authoring contract
1635
+ - [`.claude/docs/specs/component-implementation-patterns.md`](../../.claude/docs/specs/component-implementation-patterns.md) — render strategies
1636
+ - [`.claude/docs/specs/traits.md`](../../.claude/docs/specs/traits.md) — composable behaviors
1592
1637
 
1593
1638
  For consumer questions not covered here, file an issue against [adiahealth/gen-ui-kit](https://github.com/adiahealth/gen-ui-kit/issues). This document is the canonical answer — if it doesn't have your answer, it's a doc gap worth fixing.
@@ -24,6 +24,15 @@
24
24
  "text": {
25
25
  "description": "Header text — the clickable label that toggles the section.",
26
26
  "type": "string"
27
+ },
28
+ "variant": {
29
+ "description": "Per-item posture override. Items normally inherit their host\naccordion-ui's [variant] — set this directly on ONE item only to\nopt it out of (`flat`) or into (`contained`) the host's posture.",
30
+ "type": "string",
31
+ "enum": [
32
+ "flat",
33
+ "contained"
34
+ ],
35
+ "default": "flat"
27
36
  }
28
37
  },
29
38
  "required": [
@@ -66,6 +75,9 @@
66
75
  "body": {
67
76
  "description": "The section's collapsible content. Renders below the header; hidden when `[open]` is unset. Author-fills with prose, lists, embedded forms, or any rich markup the panel needs."
68
77
  },
78
+ "caret": {
79
+ "description": "The disclosure indicator icon. Auto-stamped (a caret-down icon-ui)\nwhen absent — under BOTH the default header and an authored\n[slot=\"header\"]. Author your own [slot=\"caret\"] element (even empty)\nto opt out of the auto-stamp; no separate attribute needed."
80
+ },
69
81
  "header": {
70
82
  "description": "Custom header content. By default `[text]` renders as a plain header\nlabel, but a `[slot=\"header\"]` override lets consumers author rich\nheaders (icon + title + action buttons + caret)."
71
83
  }
@@ -81,7 +93,24 @@
81
93
  ]
82
94
  },
83
95
  "tag": "accordion-item-ui",
84
- "tokens": {},
96
+ "tokens": {
97
+ "--accordion-item-bg": {
98
+ "description": "Item surface background.",
99
+ "default": "transparent"
100
+ },
101
+ "--accordion-item-body-pt": {
102
+ "description": "Open body top padding.",
103
+ "default": 0
104
+ },
105
+ "--accordion-item-border": {
106
+ "description": "Item surface border (full shorthand — mirrors --card-border).",
107
+ "default": "none"
108
+ },
109
+ "--accordion-item-divider": {
110
+ "description": "Border between header and open body when expanded.",
111
+ "default": "none"
112
+ }
113
+ },
85
114
  "traits": [],
86
115
  "version": 1
87
116
  }
@@ -26,6 +26,17 @@ props:
26
26
  description: Whether the section is expanded.
27
27
  type: boolean
28
28
  default: false
29
+ variant:
30
+ description: |-
31
+ Per-item posture override. Items normally inherit their host
32
+ accordion-ui's [variant] — set this directly on ONE item only to
33
+ opt it out of (`flat`) or into (`contained`) the host's posture.
34
+ type: string
35
+ default: flat
36
+ enum:
37
+ - flat
38
+ - contained
39
+ reflect: true
29
40
  slots:
30
41
  header:
31
42
  description: |-
@@ -39,6 +50,12 @@ slots:
39
50
  `[slot="actions"]`, or marked `[data-no-toggle]`) are excluded from
40
51
  the toggle-on-click cascade — clicking them fires their own handler
41
52
  without also toggling the section.
53
+ caret:
54
+ description: |-
55
+ The disclosure indicator icon. Auto-stamped (a caret-down icon-ui)
56
+ when absent — under BOTH the default header and an authored
57
+ [slot="header"]. Author your own [slot="caret"] element (even empty)
58
+ to opt out of the auto-stamp; no separate attribute needed.
42
59
  body:
43
60
  description: >-
44
61
  The section's collapsible content. Renders below the header; hidden
@@ -52,6 +69,20 @@ events:
52
69
  type: boolean
53
70
  description: New open state.
54
71
 
72
+ tokens:
73
+ --accordion-item-bg:
74
+ description: Item surface background.
75
+ default: transparent
76
+ --accordion-item-border:
77
+ description: Item surface border (full shorthand — mirrors --card-border).
78
+ default: none
79
+ --accordion-item-divider:
80
+ description: Border between header and open body when expanded.
81
+ default: none
82
+ --accordion-item-body-pt:
83
+ description: Open body top padding.
84
+ default: 0
85
+
55
86
  keywords:
56
87
  - accordion-item
57
88
  - collapsible
@@ -75,3 +106,5 @@ a2ui:
75
106
  reason: 'Built-in cascade; do not wire onclick yourself.'
76
107
  - rule: 'For single-button disclosures (one expanding panel without a list) use <details>/<summary> or compose <button-ui> + <section-ui>.'
77
108
  reason: 'Accordion-item assumes sibling-context.'
109
+ - rule: 'An item inherits its host accordion-ui''s [variant]; set variant="flat" or variant="contained" directly on ONE item to override just that item against the host.'
110
+ reason: 'Per-item posture override.'
@@ -20,6 +20,15 @@
20
20
  "description": "Allow multiple panels to be open simultaneously",
21
21
  "type": "boolean",
22
22
  "default": false
23
+ },
24
+ "variant": {
25
+ "description": "Visual posture, inherited by child accordion-item-ui elements unless a\nchild sets its own [variant]. `flat` (default) renders items with no\nchrome — settings pages, FAQ blocks, anywhere the accordion sits\ndirectly on the page canvas. `contained` gives every item a bounded\nsurface (background + border + radius spanning header and open body,\nplus a divider between them when open) — the composition to reach for\nwhen nesting the accordion inside <card-ui>.",
26
+ "type": "string",
27
+ "enum": [
28
+ "flat",
29
+ "contained"
30
+ ],
31
+ "default": "flat"
23
32
  }
24
33
  },
25
34
  "required": [