@adia-ai/web-components 0.7.24 → 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.
- package/CHANGELOG.md +41 -25
- package/README.md +2 -2
- package/USAGE.md +4 -4
- package/components/accordion/accordion-item.a2ui.json +30 -1
- package/components/accordion/accordion-item.yaml +33 -0
- package/components/accordion/accordion.a2ui.json +9 -0
- package/components/accordion/accordion.class.js +53 -3
- package/components/accordion/accordion.css +61 -3
- package/components/accordion/accordion.d.ts +12 -0
- package/components/accordion/accordion.yaml +17 -0
- package/components/code/code.class.js +1 -1
- package/components/drawer/drawer.class.js +2 -2
- package/components/feed/feed.a2ui.json +1 -1
- package/components/feed/feed.class.js +1 -1
- package/components/feed/feed.d.ts +1 -1
- package/components/feed/feed.yaml +2 -2
- package/components/field/field.class.js +1 -1
- package/components/loading-overlay/loading-overlay.class.js +1 -1
- package/components/loading-overlay/loading-overlay.yaml +1 -1
- package/components/modal/modal.class.js +1 -1
- package/components/nav/nav.class.js +1 -1
- package/components/nav/nav.examples.md +4 -4
- package/components/popover/popover.class.js +1 -1
- package/components/spinner/spinner.class.js +1 -1
- package/components/spinner/spinner.yaml +1 -1
- package/components/toast/toast.class.js +1 -1
- package/custom-elements.json +1 -1
- package/dist/theme-provider.min.js +1 -1
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +1 -1
- package/dist/web-components.sheet.js +1 -1
- package/package.json +1 -1
- package/traits/CATEGORIES.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
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
|
+
|
|
3
19
|
## [0.7.24] — 2026-07-01
|
|
4
20
|
|
|
5
21
|
Upstream consumer findings from `adia-pay` against installed `0.7.23` (F1–F6).
|
|
@@ -41,7 +57,7 @@ Upstream consumer findings from `adia-pay` against installed `0.7.23` (F1–F6).
|
|
|
41
57
|
## [0.7.19] — 2026-06-10
|
|
42
58
|
|
|
43
59
|
### Fixed
|
|
44
|
-
- **`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.
|
|
45
61
|
|
|
46
62
|
## [0.7.18] — 2026-06-09
|
|
47
63
|
|
|
@@ -99,7 +115,7 @@ Upstream consumer findings from `adia-pay` against installed `0.7.23` (F1–F6).
|
|
|
99
115
|
|
|
100
116
|
### Changed
|
|
101
117
|
|
|
102
|
-
- **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
|
|
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`.
|
|
103
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.
|
|
104
120
|
|
|
105
121
|
### Fixed
|
|
@@ -211,7 +227,7 @@ Upstream consumer findings from `adia-pay` against installed `0.7.23` (F1–F6).
|
|
|
211
227
|
|
|
212
228
|
### Changed
|
|
213
229
|
|
|
214
|
-
- **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;
|
|
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).
|
|
215
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).
|
|
216
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.
|
|
217
233
|
|
|
@@ -336,7 +352,7 @@ Upstream consumer findings from `adia-pay` against installed `0.7.23` (F1–F6).
|
|
|
336
352
|
### Changed — global styles assigned to `@layer` cascade layers (ADR-0038)
|
|
337
353
|
|
|
338
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).
|
|
339
|
-
- **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 +
|
|
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`.
|
|
340
356
|
|
|
341
357
|
### Added — `<select-ui>` per-option `icon` / `avatar`, reflected in the trigger
|
|
342
358
|
|
|
@@ -344,7 +360,7 @@ Upstream consumer findings from `adia-pay` against installed `0.7.23` (F1–F6).
|
|
|
344
360
|
|
|
345
361
|
### Changed — block-level display by default + universal `[inline]` attribute (ADR-0037)
|
|
346
362
|
|
|
347
|
-
- 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 +
|
|
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.
|
|
348
364
|
|
|
349
365
|
### Changed — `chevron` → `caret` terminology sweep (FEEDBACK-90, ADR-0036 follow-up)
|
|
350
366
|
|
|
@@ -1118,11 +1134,11 @@ Three pre-v0.4.0 `patterns/*.examples.html` files used legacy markup retired by
|
|
|
1118
1134
|
## [0.6.15] — 2026-05-21
|
|
1119
1135
|
|
|
1120
1136
|
### Fixed
|
|
1121
|
-
- **`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
|
|
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`).
|
|
1122
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).
|
|
1123
1139
|
|
|
1124
1140
|
### Docs
|
|
1125
|
-
- **Quick-start + migration guide examples corrected (FB-09).**
|
|
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.
|
|
1126
1142
|
|
|
1127
1143
|
### Verification
|
|
1128
1144
|
- `verify:traits` 56/56 clean.
|
|
@@ -1356,7 +1372,7 @@ Files: `slider.css`, `slider.class.js`, `slider.examples.html`, `slider.yaml`, `
|
|
|
1356
1372
|
|
|
1357
1373
|
## [0.5.15] - 2026-05-16
|
|
1358
1374
|
|
|
1359
|
-
> **Scope note:** §325 + §326 were originally planned as a separate v0.5.16 cycle (see retired
|
|
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.
|
|
1360
1376
|
|
|
1361
1377
|
### v0.5.15 §326 — `<swatch-ui>` `[slot="chrome"]` lost in template contexts (FB-37; P1)
|
|
1362
1378
|
|
|
@@ -2763,7 +2779,7 @@ Also fixed `code.yaml`: removed a phantom `copy` event declaration (no runtime e
|
|
|
2763
2779
|
|
|
2764
2780
|
### Audit summary — hand-authored `.d.ts` cleanliness as of v0.4.9
|
|
2765
2781
|
|
|
2766
|
-
After §90 + §91b sweeps, **all 17 form-bearing primitives' hand-authored `.d.ts` files are runtime-matched.** Audit recipe captured in
|
|
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.
|
|
2767
2783
|
|
|
2768
2784
|
### Documentation — USAGE.md "Derived reactive bindings" subsection (§91d, FEEDBACK-04 #3)
|
|
2769
2785
|
|
|
@@ -3213,7 +3229,7 @@ Companion to the corpus simplification arc in `@adia-ai/a2ui-{runtime,compose,co
|
|
|
3213
3229
|
|
|
3214
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.
|
|
3215
3231
|
|
|
3216
|
-
See root [CHANGELOG.md `[Unreleased]`](../../CHANGELOG.md) for the cross-cutting arc narrative + [docs/journal/2026/05/2026-05-12.md](
|
|
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.
|
|
3217
3233
|
## [0.4.3] - 2026-05-11
|
|
3218
3234
|
|
|
3219
3235
|
### Added
|
|
@@ -3229,7 +3245,7 @@ See root [CHANGELOG.md `[Unreleased]`](../../CHANGELOG.md) for the cross-cutting
|
|
|
3229
3245
|
|
|
3230
3246
|
### Lockstep
|
|
3231
3247
|
|
|
3232
|
-
9-package coordinated PATCH cut to v0.4.3 (per [
|
|
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.
|
|
3233
3249
|
## [0.4.2] - 2026-05-11
|
|
3234
3250
|
|
|
3235
3251
|
### Changed
|
|
@@ -3266,7 +3282,7 @@ See root [CHANGELOG.md `[Unreleased]`](../../CHANGELOG.md) for the cross-cutting
|
|
|
3266
3282
|
|
|
3267
3283
|
### Lockstep
|
|
3268
3284
|
|
|
3269
|
-
9-package coordinated PATCH cut to v0.4.2 (per [
|
|
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.
|
|
3270
3286
|
## [0.4.1] - 2026-05-10
|
|
3271
3287
|
|
|
3272
3288
|
### Ride-along (no source changes)
|
|
@@ -3298,7 +3314,7 @@ Lockstep version bump only — source byte-identical to v0.3.4. Internal `@adia-
|
|
|
3298
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.
|
|
3299
3315
|
## [0.3.3] - 2026-05-07
|
|
3300
3316
|
|
|
3301
|
-
**Lockstep cut.** All 9 published `@adia-ai/*` packages now share version `0.3.3`, governed by [
|
|
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).
|
|
3302
3318
|
|
|
3303
3319
|
### Changed
|
|
3304
3320
|
|
|
@@ -3311,7 +3327,7 @@ Lockstep version bump only — source byte-identical to v0.3.3. Internal `@adia-
|
|
|
3311
3327
|
## [0.3.2] - 2026-05-06
|
|
3312
3328
|
|
|
3313
3329
|
**9-package lockstep patch cut to v0.3.2.** All lockstep members share
|
|
3314
|
-
one version per [
|
|
3330
|
+
one version per [`.claude/docs/specs/package-architecture.md` § 15](../../.claude/docs/specs/package-architecture.md#15-versioning-policy).
|
|
3315
3331
|
Internal `@adia-ai/*` dep ranges unchanged at `^0.3.0`.
|
|
3316
3332
|
|
|
3317
3333
|
### No source changes
|
|
@@ -3324,7 +3340,7 @@ version only.
|
|
|
3324
3340
|
- `version`: `0.3.1` → `0.3.2`.
|
|
3325
3341
|
## [0.3.1] - 2026-05-06
|
|
3326
3342
|
|
|
3327
|
-
**9-package lockstep patch cut + folder-per-trait restructure.** All 9 published `@adia-ai/*` packages bump 0.3.0 → 0.3.1 per [
|
|
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).
|
|
3328
3344
|
|
|
3329
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.
|
|
3330
3346
|
|
|
@@ -3345,7 +3361,7 @@ Symmetry with `components/<name>/` (already folder-per for months) — easier to
|
|
|
3345
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).
|
|
3346
3362
|
## [0.3.0] - 2026-05-05
|
|
3347
3363
|
|
|
3348
|
-
**9-package lockstep cut.** All 9 published `@adia-ai/*` packages bump 0.2.5 → 0.3.0 per [
|
|
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`.
|
|
3349
3365
|
|
|
3350
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).
|
|
3351
3367
|
|
|
@@ -3367,7 +3383,7 @@ The following directories saw significant in-repo changes between v0.2.5 and v0.
|
|
|
3367
3383
|
- `patterns/` — no source changes since v0.2.5.
|
|
3368
3384
|
## [0.2.5] - 2026-05-04
|
|
3369
3385
|
|
|
3370
|
-
**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 [
|
|
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**.
|
|
3371
3387
|
|
|
3372
3388
|
### Added
|
|
3373
3389
|
|
|
@@ -3389,7 +3405,7 @@ The following directories saw significant in-repo changes between v0.2.5 and v0.
|
|
|
3389
3405
|
- `styles/components.css` adds `@import "../components/fields/fields.css"`.
|
|
3390
3406
|
## [0.2.4] - 2026-05-04
|
|
3391
3407
|
|
|
3392
|
-
**Lockstep cut + Tier 4 capability adds + 2 architectural rewrites.** All 8 published `@adia-ai/*` packages bump 0.2.3 → 0.2.4 per [
|
|
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.
|
|
3393
3409
|
|
|
3394
3410
|
### Changed
|
|
3395
3411
|
|
|
@@ -3399,8 +3415,8 @@ The following directories saw significant in-repo changes between v0.2.5 and v0.
|
|
|
3399
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.
|
|
3400
3416
|
|
|
3401
3417
|
The substantive content of the next cut, surfaced from the
|
|
3402
|
-
2026-05-04 trait-library lift initiative (
|
|
3403
|
-
|
|
3418
|
+
2026-05-04 trait-library lift initiative (`.claude/docs/PLAN.md`,
|
|
3419
|
+
`.claude/docs/reports/traits-creative-director-audit-2026-05-04.md`).
|
|
3404
3420
|
**No BREAKING changes.** Every public tag, prop, event, and
|
|
3405
3421
|
declarative `traits=` attribute from v0.2.3 still works.
|
|
3406
3422
|
|
|
@@ -3653,7 +3669,7 @@ warnings on every page that referenced these:
|
|
|
3653
3669
|
|
|
3654
3670
|
**Lockstep cut + accumulated fixes.** All 8 published `@adia-ai/*`
|
|
3655
3671
|
packages bump 0.2.2 → 0.2.3 per
|
|
3656
|
-
[
|
|
3672
|
+
[`.claude/docs/specs/package-architecture.md` § 15](../../.claude/docs/specs/package-architecture.md#15-versioning-policy).
|
|
3657
3673
|
Patch cut — no breaking changes. The substantive content is
|
|
3658
3674
|
`web-components`-only; the other 7 packages have no source change.
|
|
3659
3675
|
|
|
@@ -3707,7 +3723,7 @@ edge instead of leaving the column reading as dead width. Per-row
|
|
|
3707
3723
|
---
|
|
3708
3724
|
## [0.2.2] - 2026-05-02
|
|
3709
3725
|
|
|
3710
|
-
**Lockstep cut + trait coverage 100% + `<traits-host>` wrapper.** All 8 published `@adia-ai/*` packages bump 0.2.1 → 0.2.2 per [
|
|
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.
|
|
3711
3727
|
|
|
3712
3728
|
### Changed
|
|
3713
3729
|
|
|
@@ -3726,7 +3742,7 @@ Follow-up landing on top of the v0.2.1 trait-system work — closes the two foll
|
|
|
3726
3742
|
---
|
|
3727
3743
|
## [0.2.1] - 2026-05-02
|
|
3728
3744
|
|
|
3729
|
-
**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 [
|
|
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.
|
|
3730
3746
|
|
|
3731
3747
|
### Changed
|
|
3732
3748
|
|
|
@@ -3818,8 +3834,8 @@ transition rather than snapping. No JS changes; no API changes.
|
|
|
3818
3834
|
## [0.2.0] - 2026-05-02
|
|
3819
3835
|
|
|
3820
3836
|
**Lockstep cut.** All 8 published `@adia-ai/*` packages now share one
|
|
3821
|
-
version, governed by [
|
|
3822
|
-
(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)
|
|
3823
3839
|
and enforced by `npm run check:lockstep` + the
|
|
3824
3840
|
`lockstep-versioning` job in
|
|
3825
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: [
|
|
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
|
@@ -466,7 +466,7 @@ export class UIModal extends UIElement {
|
|
|
466
466
|
}
|
|
467
467
|
```
|
|
468
468
|
|
|
469
|
-
Reference: [
|
|
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.
|
|
470
470
|
|
|
471
471
|
---
|
|
472
472
|
|
|
@@ -1631,8 +1631,8 @@ If your design needs label-on-tile (e.g. Tokens Studio's C1.3 hand-rolled `<div
|
|
|
1631
1631
|
- [`README.md`](./README.md) — package overview + authoring guide
|
|
1632
1632
|
- [Component yaml contracts](./components/) — every primitive's complete API (props, events, slots, examples)
|
|
1633
1633
|
- [Live demos](https://ui-kit.exe.xyz/site/components/) — every primitive in action
|
|
1634
|
-
- [
|
|
1635
|
-
- [
|
|
1636
|
-
- [
|
|
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
|
|
1637
1637
|
|
|
1638
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": [
|
|
@@ -38,6 +38,12 @@ export class UIAccordion extends UIElement {
|
|
|
38
38
|
|
|
39
39
|
static properties = {
|
|
40
40
|
multiple: { type: Boolean, default: false, reflect: true },
|
|
41
|
+
// `reflect: true` is load-bearing: the CSS ancestor selector
|
|
42
|
+
// (accordion.css `@scope (accordion-item-ui)`) matches
|
|
43
|
+
// `accordion-ui[variant="contained"]`. Default 'flat' ⇒ absent-attribute
|
|
44
|
+
// = current behavior. No render/JS logic keys off it — inheritance of
|
|
45
|
+
// the contained posture into child items is pure CSS.
|
|
46
|
+
variant: { type: String, default: 'flat', reflect: true }, // 'flat' | 'contained'
|
|
41
47
|
};
|
|
42
48
|
|
|
43
49
|
static template = () => null;
|
|
@@ -70,6 +76,10 @@ export class UIAccordionItem extends UIElement {
|
|
|
70
76
|
static properties = {
|
|
71
77
|
text: { type: String, default: '', reflect: true },
|
|
72
78
|
open: { type: Boolean, default: false, reflect: true },
|
|
79
|
+
// Per-item posture override against the host accordion-ui's [variant]
|
|
80
|
+
// (see UIAccordion.variant above). Default 'flat'; CSS-only, pure
|
|
81
|
+
// token re-point in contained (accordion.css).
|
|
82
|
+
variant: { type: String, default: 'flat', reflect: true }, // 'flat' | 'contained'
|
|
73
83
|
};
|
|
74
84
|
|
|
75
85
|
static template = () => null;
|
|
@@ -77,13 +87,24 @@ export class UIAccordionItem extends UIElement {
|
|
|
77
87
|
#bound = false;
|
|
78
88
|
|
|
79
89
|
connected() {
|
|
80
|
-
// Stamp header if not present
|
|
81
|
-
|
|
90
|
+
// Stamp header if not present; otherwise restore the disclosure caret
|
|
91
|
+
// (FN-1) — a consumer's custom [slot="header"] previously suppressed it
|
|
92
|
+
// entirely because the caret only ever got stamped alongside the header.
|
|
93
|
+
const existingHeader = this.querySelector('[slot="header"]');
|
|
94
|
+
if (!existingHeader) {
|
|
82
95
|
const header = document.createElement('div');
|
|
83
96
|
header.setAttribute('slot', 'header');
|
|
97
|
+
// SPEC-R8: the auto-stamped header was mouse-only (no tabindex/keydown).
|
|
98
|
+
// Make it keyboard-operable; a consumer-authored header keeps owning
|
|
99
|
+
// its own semantics (data-autofocusable marks the header WE own).
|
|
100
|
+
header.setAttribute('tabindex', '0');
|
|
101
|
+
header.setAttribute('role', 'button');
|
|
102
|
+
header.setAttribute('aria-expanded', 'false');
|
|
103
|
+
header.dataset.autofocusable = '';
|
|
84
104
|
|
|
85
105
|
const label = document.createElement('span');
|
|
86
106
|
label.setAttribute('slot', 'header-text');
|
|
107
|
+
label.dataset.autolabel = ''; // marks THIS label as prop-driven (FN-2)
|
|
87
108
|
header.appendChild(label);
|
|
88
109
|
|
|
89
110
|
const caret = document.createElement('icon-ui');
|
|
@@ -92,6 +113,13 @@ export class UIAccordionItem extends UIElement {
|
|
|
92
113
|
header.appendChild(caret);
|
|
93
114
|
|
|
94
115
|
this.prepend(header);
|
|
116
|
+
} else if (!existingHeader.querySelector('[slot="caret"]')) {
|
|
117
|
+
// Scope to the HEADER, not the whole item — a caret placed in
|
|
118
|
+
// [slot="body"] must not suppress this (Finding 10).
|
|
119
|
+
const caret = document.createElement('icon-ui');
|
|
120
|
+
caret.setAttribute('name', 'caret-down');
|
|
121
|
+
caret.setAttribute('slot', 'caret');
|
|
122
|
+
existingHeader.appendChild(caret); // restore the disclosure signifier
|
|
95
123
|
}
|
|
96
124
|
|
|
97
125
|
// Stamp content wrapper if not present
|
|
@@ -112,17 +140,26 @@ export class UIAccordionItem extends UIElement {
|
|
|
112
140
|
if (!this.#bound) {
|
|
113
141
|
this.#bound = true;
|
|
114
142
|
this.addEventListener('click', this.#onClick);
|
|
143
|
+
this.addEventListener('keydown', this.#onKeydown);
|
|
115
144
|
}
|
|
116
145
|
}
|
|
117
146
|
|
|
118
147
|
disconnected() {
|
|
119
148
|
this.removeEventListener('click', this.#onClick);
|
|
149
|
+
this.removeEventListener('keydown', this.#onKeydown);
|
|
120
150
|
this.#bound = false;
|
|
121
151
|
}
|
|
122
152
|
|
|
123
153
|
render() {
|
|
124
|
-
|
|
154
|
+
// Only the auto-stamped label follows [text] — an authored
|
|
155
|
+
// [slot="header-text"] (no data-autolabel marker) is never touched (FN-2).
|
|
156
|
+
const label = this.querySelector('[slot="header-text"][data-autolabel]');
|
|
125
157
|
if (label) label.textContent = this.text;
|
|
158
|
+
|
|
159
|
+
// Keep aria-expanded in sync on the auto-stamped header — `open` is a
|
|
160
|
+
// reactive prop, so render() re-runs on every toggle (SPEC-R8).
|
|
161
|
+
const hdr = this.querySelector('[slot="header"][data-autofocusable]');
|
|
162
|
+
if (hdr) hdr.setAttribute('aria-expanded', String(this.open));
|
|
126
163
|
}
|
|
127
164
|
|
|
128
165
|
#onClick = (e) => {
|
|
@@ -140,4 +177,17 @@ export class UIAccordionItem extends UIElement {
|
|
|
140
177
|
detail: { open: this.open },
|
|
141
178
|
}));
|
|
142
179
|
};
|
|
180
|
+
|
|
181
|
+
// SPEC-R8: Enter/Space on the auto-stamped header toggles it. Scoped to
|
|
182
|
+
// OUR stamped header only (data-autofocusable) — never a consumer control
|
|
183
|
+
// inside a custom header. Routes through header.click() → #onClick, so
|
|
184
|
+
// there is exactly one toggle code path (DRY).
|
|
185
|
+
#onKeydown = (e) => {
|
|
186
|
+
const header = this.querySelector('[slot="header"][data-autofocusable]');
|
|
187
|
+
if (!header || e.target !== header) return;
|
|
188
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
189
|
+
e.preventDefault(); // Space: no page scroll
|
|
190
|
+
header.click();
|
|
191
|
+
}
|
|
192
|
+
};
|
|
143
193
|
}
|