@adia-ai/web-components 0.8.31 → 0.8.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +66 -0
- package/MIGRATION.md +136 -0
- package/components/accordion/accordion-item.a2ui.json +8 -0
- package/components/accordion/accordion-item.yaml +6 -0
- package/components/accordion/accordion.css +3 -1
- package/components/accordion/accordion.d.ts +2 -2
- package/components/action-list/action-list.class.js +6 -2
- package/components/action-list/action-list.d.ts +1 -1
- package/components/agent-artifact/agent-artifact.d.ts +1 -1
- package/components/agent-feedback-bar/agent-feedback-bar.d.ts +2 -2
- package/components/agent-questions/agent-questions.d.ts +1 -1
- package/components/agent-reasoning/agent-reasoning.d.ts +3 -3
- package/components/agent-suggestions/agent-suggestions.d.ts +1 -1
- package/components/agent-trace/agent-trace.d.ts +1 -1
- package/components/alert/alert.a2ui.json +3 -5
- package/components/alert/alert.class.js +3 -3
- package/components/alert/alert.css +5 -12
- package/components/alert/alert.d.ts +4 -4
- package/components/alert/alert.yaml +6 -5
- package/components/anchor-bar/anchor-bar.d.ts +2 -2
- package/components/avatar/avatar.class.js +0 -1
- package/components/avatar/avatar.css +16 -2
- package/components/badge/badge.a2ui.json +6 -9
- package/components/badge/badge.class.js +4 -4
- package/components/badge/badge.css +134 -74
- package/components/badge/badge.d.ts +34 -12
- package/components/badge/badge.examples.md +1 -1
- package/components/badge/badge.yaml +37 -16
- package/components/breadcrumb/breadcrumb.css +15 -3
- package/components/button/button.a2ui.json +1 -1
- package/components/button/button.css +4 -2
- package/components/button/button.d.ts +3 -3
- package/components/button/button.yaml +1 -1
- package/components/calendar-grid/calendar-grid.d.ts +2 -2
- package/components/card/card.css +21 -0
- package/components/card/card.yaml +10 -1
- package/components/chart/chart.class.js +8 -4
- package/components/chart/chart.d.ts +4 -4
- package/components/chart-legend/chart-legend.d.ts +1 -1
- package/components/chat-thread/chat-input.css +1 -1
- package/components/chat-thread/chat-thread.d.ts +2 -2
- package/components/check/check.css +3 -3
- package/components/command/command.d.ts +2 -2
- package/components/context-menu/context-menu.d.ts +3 -3
- package/components/datetime-picker/datetime-picker.d.ts +5 -5
- package/components/demo-toggle/demo-toggle.d.ts +1 -1
- package/components/drawer/drawer.d.ts +2 -2
- package/components/heatmap/heatmap.d.ts +5 -5
- package/components/index.js +1 -0
- package/components/inline-edit/inline-edit.d.ts +4 -4
- package/components/input/input.a2ui.json +4 -1
- package/components/input/input.css +18 -0
- package/components/input/input.yaml +11 -1
- package/components/integration-card/integration-card.class.js +11 -2
- package/components/integration-card/integration-card.d.ts +4 -4
- package/components/link/link.d.ts +1 -1
- package/components/list/list.class.js +22 -7
- package/components/list/list.d.ts +1 -1
- package/components/list-window/list-window.d.ts +5 -5
- package/components/mark/mark.a2ui.json +6 -7
- package/components/mark/mark.class.js +1 -1
- package/components/mark/mark.css +7 -4
- package/components/mark/mark.d.ts +2 -2
- package/components/mark/mark.examples.md +0 -4
- package/components/mark/mark.yaml +16 -6
- package/components/menu/menu.css +2 -2
- package/components/menu/menu.d.ts +1 -1
- package/components/modal/modal.d.ts +1 -1
- package/components/nav/nav.css +8 -0
- package/components/nav/nav.d.ts +1 -1
- package/components/nav-group/nav-group.css +58 -9
- package/components/nav-group/nav-group.d.ts +1 -1
- package/components/nav-item/nav-item.css +7 -4
- package/components/nav-item/nav-item.d.ts +1 -1
- package/components/noodles/noodles.d.ts +3 -3
- package/components/option-card/option-card.css +1 -1
- package/components/option-card/option-card.examples.md +1 -1
- package/components/pagination/pagination.d.ts +1 -1
- package/components/pane/pane.d.ts +1 -1
- package/components/password-strength/password-strength.d.ts +1 -1
- package/components/popover/popover.a2ui.json +5 -0
- package/components/popover/popover.class.js +9 -4
- package/components/popover/popover.css +5 -2
- package/components/popover/popover.yaml +8 -0
- package/components/preview/preview.examples.md +1 -1
- package/components/radio/radio.a2ui.json +4 -3
- package/components/radio/radio.css +2 -2
- package/components/radio/radio.yaml +7 -14
- package/components/radio-group/radio-group.a2ui.json +118 -0
- package/components/radio-group/radio-group.class.js +75 -0
- package/components/radio-group/radio-group.css +31 -0
- package/components/radio-group/radio-group.d.ts +20 -0
- package/components/radio-group/radio-group.examples.md +37 -0
- package/components/radio-group/radio-group.js +17 -0
- package/components/radio-group/radio-group.yaml +114 -0
- package/components/stat/stat.css +12 -8
- package/components/stat/stat.js +66 -1
- package/components/stat/stat.yaml +3 -0
- package/components/stepper/stepper-item.a2ui.json +5 -0
- package/components/stepper/stepper-item.yaml +11 -0
- package/components/stepper/stepper.a2ui.json +38 -3
- package/components/stepper/stepper.class.js +199 -2
- package/components/stepper/stepper.css +30 -0
- package/components/stepper/stepper.d.ts +28 -1
- package/components/stepper/stepper.yaml +76 -2
- package/components/stream/stream.d.ts +3 -3
- package/components/swatch/swatch.d.ts +1 -1
- package/components/swiper/swiper.d.ts +3 -3
- package/components/table/cell-types.js +12 -1
- package/components/table/table.a2ui.json +2 -2
- package/components/table/table.d.ts +7 -7
- package/components/table/table.yaml +9 -1
- package/components/table-toolbar/table-toolbar.class.js +1 -1
- package/components/table-toolbar/table-toolbar.d.ts +4 -4
- package/components/tabs/tabs.d.ts +1 -1
- package/components/tag/tag.a2ui.json +5 -5
- package/components/tag/tag.class.js +1 -1
- package/components/tag/tag.css +149 -71
- package/components/tag/tag.d.ts +21 -11
- package/components/tag/tag.examples.md +4 -4
- package/components/tag/tag.yaml +35 -13
- package/components/tags-input/tags-input.d.ts +4 -4
- package/components/timeline/timeline.d.ts +1 -1
- package/components/toast/toast.a2ui.json +2 -4
- package/components/toast/toast.css +0 -12
- package/components/toast/toast.yaml +3 -4
- package/components/toc/toc.d.ts +1 -1
- package/components/toggle-group/toggle-group.d.ts +1 -1
- package/components/toggle-scheme/toggle-scheme.d.ts +1 -1
- package/components/tour/tour.d.ts +4 -4
- package/components/tree/tree.d.ts +1 -1
- package/core/icons-phosphor.js +129 -71
- package/core/provider.js +84 -23
- package/dist/host.min.css +1 -1
- package/dist/host.sheet.js +1 -1
- package/dist/theme-provider.min.js +1 -1
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +90 -88
- package/dist/web-components.sheet.js +1 -1
- package/package.json +1 -1
- package/patterns/admin-keys-and-export/admin-keys-and-export.examples.html +1 -1
- package/patterns/agent-memory/agent-memory.examples.html +5 -5
- package/patterns/agent-prompt-library/agent-prompt-library.examples.html +2 -2
- package/patterns/audit-log/audit-log.examples.js +12 -5
- package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.js +4 -1
- package/patterns/changelog-feed/changelog-feed.examples.html +1 -1
- package/patterns/comments-and-collaboration/comments-and-collaboration.examples.html +1 -1
- package/patterns/conversion-funnel/conversion-funnel.examples.html +1 -1
- package/patterns/diff-review/diff-review.examples.html +9 -9
- package/patterns/filter-bar/filter-bar.examples.html +12 -8
- package/patterns/filter-bar/filter-bar.examples.js +4 -1
- package/patterns/marketing-engagement/marketing-engagement.examples.html +1 -1
- package/patterns/notifications-bell-and-digest/notifications-bell-and-digest.examples.html +1 -1
- package/patterns/permissions-matrix/permissions-matrix.examples.html +1 -1
- package/patterns/permissions-sharing/permissions-sharing.examples.html +1 -1
- package/patterns/profile-public-and-verification/profile-public-and-verification.examples.html +2 -2
- package/patterns/record-detail-drawer/record-detail-drawer.examples.html +1 -1
- package/patterns/retention-cohort/retention-cohort.examples.html +1 -1
- package/patterns/retention-cohort/retention-cohort.examples.js +4 -1
- package/patterns/system-banners/system-banners.examples.html +3 -3
- package/styles/api/sizing-fallback.css +32 -0
- package/styles/api/sizing.css +7 -3
- package/styles/components.css +1 -0
- package/styles/tokens.css +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,71 @@
|
|
|
1
1
|
# Changelog — @adia-ai/web-components
|
|
2
2
|
|
|
3
|
+
## [0.8.33] — 2026-08-11
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`accordion-item-ui` completes the body-padding token trio — `--accordion-item-body-px` / `--accordion-item-body-py` join the existing `--accordion-item-body-pt` (gh#852).** The body's horizontal and bottom padding came from `--accordion-item-px`/`--accordion-item-py`, undocumented internals shared with the header — no supported way existed to zero the body's padding (e.g. for a self-inset `table-ui` nested in the body) without also flattening the header. Both new tokens default to the shared header tokens (`--accordion-item-body-px: var(--accordion-item-px)`, `--accordion-item-body-py: var(--accordion-item-py)`), so existing renders are byte-identical; a consumer overriding either now only affects the body, never the header. `accordion-item.yaml`'s `tokens` block documents both alongside `--accordion-item-body-pt`.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **BREAKING — `alert-ui` drops `variant="muted"`/`"neutral"` (zero visual change) and `mark-ui` drops `variant="muted"` (VISIBLE BEHAVIOR CHANGE, no enum successor) — the variant/color/tone convention's last two carriers (gh#881, LLD §14).** Root cause is gh#873's exactly: style-like values sitting on a semantic-family axis. Neither component gains a `tone` prop — the ratified discipline is *at most* one style axis and one family axis, which permits zero, and neither has a second implemented fill style (alert: one container-tint + outline-variant banner; mark: one inline tint — zero CSS, zero call sites, zero corpus demand for a solid/outline form, grep-confirmed; the names `tone` and `muted|solid|outline` stay reserved should demand appear). `alert-ui` → `default·info·success·warning·danger`: the two retired values re-declared the exact four tokens `:where(:scope)` already sets (`--a-bg-muted` / `neutral-on-surface` / `neutral-outline-variant` / `--a-fg-muted`, `alert.css:4-7`), so the deleted rule was a second name for "no variant attribute" — every migration is dropping the attribute, and the family-less banner IS the quiet-chrome look. `mark-ui` → `warning(default)·info·success·danger`: `warning` stays the default and **no** `default` member is added (mark's family-less identity is the yellow marker — the convention its a2ui rules teach and its native-`<mark>` anti-pattern rests on; a `default` value would either alias `warning` or contradict the attribute's own default). Mark's `muted` was the one real render in this cut (`mark.css:35-38`), so it retires with a **token** successor instead of an enum one: `--mark-bg: var(--a-bg-muted); --mark-fg: var(--md-sys-color-neutral-on-surface)` — both tokens already public and unchanged, now documented as the escape hatch in `mark.yaml`'s `variant` description and the demo's note. Also repaired (source-wins, pre-existing drift): `mark.yaml` documented its token defaults as `--a-warning-muted`/`--a-warning-text`, but `mark.css:3-4` has always used `--md-sys-color-warning-container`/`-on-surface`. Call sites: the retiring demo rows in `alert.examples.html`/`mark.examples.html` deleted with their prose, and one real consumer — `system-banners`' beta-announcement banner, whose multi-line `<alert-ui>` tag only a `rg -nU` sweep can see (a line-based `git grep` misses it) — loses the attribute for an identical render. Zero `setAttribute('variant', …)` hits on either host; stale enum lists in `alert.class.js` (docblock + the `variant="error"` hallucination-alias warning text) and `mark.class.js`'s property comment updated. No new bg/fg pair anywhere, so `verify:contrast`'s roster is unchanged. `node scripts/build/components.mjs --verify` clean.
|
|
12
|
+
- **BREAKING + VISIBLE BEHAVIOR CHANGE — `accent` retired as an enum value on `badge-ui`/`tag-ui` (`variant`) and `button-ui` (`color`) — the variant/color/tone convention cut (gh#873, gh#874, gh#875, gh#876).** Root cause: badge/tag were carrying a fill-style value (`accent`, plus badge's `muted`/`neutral`) on their semantic-family `variant` axis instead of the fill-style `tone` axis, and button's `color` enum carried `accent` with zero backing CSS (gh#875's original finding, re-confirmed) — three overlapping vocabularies for what should stay two orthogonal axes. Ratified convention (LLD, coordinator ruling 2026-08-10): every component exposes at most one style axis and one family axis; badge/tag keep `variant` = family, `tone` = style — button's own split (`variant` = style, `color` = family) was already the clean reference the other two migrate toward, not the reverse. Fixes: (1) `badge-ui` — `variant="accent"` → `variant="primary"` (pure rename, identical solid pair; the rename also gains working `tone="muted"`/`tone="outline"` pairings `primary` previously lacked); `variant="muted"`/`"neutral"` retire, merging into `tone="muted"` (badge's family-less `tone="muted"` also re-tokens `--a-bg-muted` → `--md-sys-color-neutral-low` + `-on-surface`, gh#873 — see below). (2) `tag-ui` — gains `primary` in its `variant` enum (new, not previously present) as the `accent`→`primary` rename target — coordinator-ratified sibling parity with badge (gh#372's actual intent: equivalent brand-emphasis capability on both siblings, not the same attribute name); tag's family-less `tone="muted"` is UNCHANGED (stays on `--a-bg-muted`, gh#778 dense-list rationale — badge and tag's family-less muteds are no longer the same shape, `badge.yaml` corrected). (3) `button-ui` — `color` enum drops `accent` (confirmed zero CSS at every step, gh#875 is now superseded and closes); same edit adds the missing `tonal` value to `variant`'s enum (yaml/CSS drift repair — `button.css` already implemented it). `STATUS_MAP.beta` (`badge.class.js`) moves `accent`→`primary` (identical render). Axis-4 JS render-method sweep (`table-toolbar.class.js`, `plan-picker.class.js`'s tag pills, `table/cell-types.js`'s badge cell renderer, `integration-card.class.js`'s status badge) updated to the new attributes, including two reused-DOM-node cases (`table/cell-types.js`, `integration-card.class.js`) where `variant`/`tone` must be explicitly cleared on the branch not taken, not just set on the branch taken — a stale attribute from a prior render otherwise survives. `verify:components --verify` clean.
|
|
13
|
+
- **`--md-sys-color-neutral-low`/`-on-surface` join `verify:contrast`'s roster as `badge-ui`'s new family-less `[tone="muted"]` fill (gh#873).** The literally-requested pair (`neutral-low`/`neutral-high`) measures ~2.95:1 light / ~3.06:1 dark — fails AA at badge's ~12px uppercase text (4.5:1 threshold, not the 3:1 large-text one). Shipped pair honors the requested *background* (the visually distinctive half of the ask) paired with the family's own `-on-surface` role instead: measured 8.19:1 light / 8.46:1 dark — comfortably AA, confirmed by the new roster pair across all 13 palettes (default + 12 named `[theme]` identities, gh#435-style; 1820 pairs total, all pass). Family-tinted `tone="muted"` (info/success/warning/danger/primary) is unchanged. Consequence: badge's and tag's family-less `[tone="muted"]` are no longer the same shape (tag intentionally stays on the lighter `--a-bg-muted`, gh#778 dense-list rationale) — `badge.yaml`'s claim that they matched is corrected in the same edit.
|
|
14
|
+
- **VISIBLE BEHAVIOR CHANGE — `badge-ui`/`tag-ui` default color formulas swapped back to their reported targets (gh#864).** `badge-ui`'s bare (no `[tone]`) family variants (`accent`/`info`/`success`/`warning`/`danger`) had been implementing `tag-ui`'s reported formula — tinted container bg + `on-surface` text — while `tag-ui`'s bare family variants had been implementing `badge-ui`'s — saturated `--a-{family}-bg` + on-{family} text. Root cause: gh#794 flipped `tag-ui`'s bare default to solid a day before gh#845 ratified `badge-ui`'s register, with no cross-check between the two changes. Swapped both back: `badge-ui`'s bare family variants are now solid (`--a-{family}-bg` + `-on-{family}`, opt into the tinted look via `[tone="muted"]`); `tag-ui`'s bare family variants are now the tinted container pair (`-{family}-container` + `-on-surface`, opt into the saturated stamp via `[tone="solid"]`). Neither component's `[tone="outline"]` text color moved — `-{family}-on-surface` stays, a deliberate 2026-07-15 contrast-safety fix for a documented 2.23:1 WCAG failure (`styles/colors/semantics/core.css`), not part of this swap. `tag-ui`'s family-less `default` variant (no `[variant]`) is unaffected — it stays solid unconditionally per gh#794, since that ruling was never about the family-formula swap. `verify:contrast` stays green (1794 pairs, WCAG AA) — the swap only re-points which selector carries which already-passing pair, no new color combination introduced. `badge.yaml`/`tag.yaml`'s `tone` prop schema defaults flip to match (`badge-ui`: `muted` → `solid`; `tag-ui`: `solid` → `muted`); both yamls' descriptions and `badge.test.js`/`tag.test.js`'s color-formula assertions updated to the new shape.
|
|
15
|
+
- **`badge-ui`'s text weight moves semibold (600) → medium (500) — no longer distinguishes it from `tag-ui` (gh#866, revises gh#845).** `--badge-weight` now consumes `--a-weight-medium` instead of `--a-weight-semibold`; `tag-ui`'s text was already weight 500 (hardcoded, not tokenized — pre-existing, unrelated note). Per gh#845's own register ruling, weight was one of four axes (case/tracking/mono/weight) separating badge from tag; with weight now equal, the split holds via the remaining three — badge stays UPPERCASE, mono (`--a-font-family-code`), wide-tracked (`--a-font-tracking-wider`). `badge.test.js`'s weight-pinning assertion (added per gh#845) updated to expect 500; `badge.yaml`'s REGISTER doc note updated to say medium, not semibold.
|
|
16
|
+
- **VISIBLE BEHAVIOR CHANGE — `badge-ui` diverges from `tag-ui` by TYPOGRAPHY, not shape (gh#845).** Operator ruling: tags and badges had converged to near-identical size/shape/type and needed to separate by register instead. `badge-ui` now renders **uppercase**, its own mono stack (kbd-ui's `--a-font-family-code` — MONO TRIAL taken: held up cleanly at small badge sizes in both schemes, screenshot-verified against the admin-dashboard notification/team/table surfaces; the tracked-caps-sans fallback was not needed), semibold weight, wide letter-spacing (`--a-font-tracking-wider`, 0.06em — inside the ruling's +0.04–0.08em band), and `font-variant-numeric: tabular-nums` for counts. Geometry moved the other direction: `--badge-radius` now consumes the same `--a-radius-full` pill token `tag-ui`'s `--tag-radius` uses (was `--a-radius-md`, a squarer chip) — `--badge-px`/`--badge-py` already shared tag's exact padding-token structure pre-845, so shape is no longer a differentiator at all. `tag-ui` is unchanged (stays proportional sentence-case, weight 500, never uppercase/tracked/mono) — it was the geometry reference for this change, not a target. Neither component exposes a prop that lets one adopt the other's typographic register; badge's `[variant]`/`[tone]` (the only attributes the two share) only ever re-point color tokens, never typography — pinned by new `badge.test.js` coverage. Semantic color voice and non-interactivity are unchanged; `verify:contrast` stays green (uppercase small text on semantic fills was re-measured, no regression — color pairs didn't move). Consumers relying on badge's prior squarer/proportional look (any surface treating badge and tag as visually interchangeable) will see a visible diff; no API/props changed.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **`badge-ui`/`tag-ui`'s family-less solid default used the wrong neutral token pair, and badge's own default was still muted (follow-up to gh#864/gh#866, operator ruling 2026-08-10).** Both components' truly family-less default (`tag-ui`: `:where(:scope)` + `[variant="default"]` + `[tone="solid"]:not([variant])`; `badge-ui`: `:where(:scope)`) used `--md-sys-color-neutral-on-surface` (bg) + `-neutral-background` (fg) — ink/page tokens borrowed as a stand-in, not the actual `--md-sys-color-neutral`/`-on-neutral` pair that already exists in the token catalog as the true parallel to `-primary`/`-on-primary` etc. Separately, `badge-ui`'s base tokens were still `--a-bg-muted` + `-on-surface` (quiet chrome) even after gh#864 made every family variant solid by default — the swap only touched variants that HAD a family, missing the family-less base case entirely, so a bare `<badge-ui>` contradicted its own documented "solid by default" register. Both fixed: `tag-ui`'s three solid-neutral rules re-pointed to the correct pair (visually near-identical, same solid intent, correct tokens); `badge-ui`'s base tokens moved from muted to solid `neutral`/`on-neutral`, matching `tag-ui`'s already-solid neutral default and badge's own register — `[tone="muted"]`/`[variant="muted"|"neutral"]` still opt into the quiet-chrome look. Neither component's `[tone="muted"]` fg (`-neutral-on-surface`, correctly ink-on-muted-surface) or `[tone="outline"]` styling moved. `verify:contrast` stays green (1794 pairs, WCAG AA); `tag.test.js`'s three affected assertions updated to match.
|
|
21
|
+
- **`popover-ui`'s panel border token swapped from `outline-variant` to `--a-ui-border` (`outline`), matching `select-ui`'s listbox border (gh#857, item 3 of 3 — closes the issue).** `popover.css`'s `--popover-border` default was the only overlay chrome in the shared trigger/panel family reading `outline-variant` while `select-ui`'s `[slot="listbox"]` (`select.css:277`) reads `outline` via `--a-ui-border` — both panels already share the identical `--md-sys-color-neutral-surface-bright` background per the existing comment's stated intent ("Matches select-ui's own dropdown surface exactly"), so the border now follows the same intent. Confirmed `popover-ui`'s other consumers first: `color-input-ui` (the only other component instantiating `<popover-ui>` in code) and the `filter-bar`/`inline-dialog`/`theme-panel` demo patterns all use the default panel chrome as-is — none override `--popover-border`, and nothing in `popover.yaml` documents a reason to keep `outline-variant`. `date-range-picker-ui`/`datetime-picker-ui`/`calendar-picker-ui`/`tour-ui` are NOT `<popover-ui>` consumers (they build their own native `[popover]` panels with independent `--<component>-popover-border` tokens) and are unaffected.
|
|
22
|
+
- **`chart-ui` sparkline no longer pins flat/single-point series to the bottom of the plot (gh#850).** `#renderSparkline()` derived its vertical domain from the series' own `minVal`/`maxVal` (`range = maxVal - minVal || 1`) — when every value was equal (or there was one point), `range` fell back to the literal constant `1`, and every point mapped to `y = h - padY`, the exact bottom edge. Every other renderer in `chart.class.js` (`#renderBar`, `#renderLine`/`#renderArea`, `#renderScatter`, `#renderMultiLine`, `#renderStackedBar`, `#renderGroupedBar`, `#renderComposed`) already scales against a fixed 0-floor domain via `niceScale(0, Math.max(...vals), 5)`; `#renderSparkline` was the one holdout. Brought in line with that convention instead of inventing a new mechanism — a flat series (e.g. constant `90` for 7 days) now plots against a real 0-floor baseline instead of collapsing to the bottom edge.
|
|
23
|
+
- **Three demo pages authored tags that do not exist (gh#848).** `patterns/diff-review.examples.html` used `<description-term-ui>` / `<description-detail-ui>` inside `<description-list-ui>`, and `patterns/agent-memory.examples.html` used `<form-container-ui>`. Neither tag has a `.yaml`, a `.a2ui.json`, or a `customElements.define` anywhere in `packages/` — they are undefined elements the browser renders as bare inline boxes, so the live demos were subtly wrong too and the A2UI conversion lost the region entirely. Replaced with the real contracts: native `<dt>`/`<dd>` (description-list.yaml: "consumer passes `<dt>`/`<dd>` children inline") and native `<form>` (registry.js §45: "no form-ui component exists"). Registering the invented tags was rejected — that would have been inventing a component to match a typo.
|
|
24
|
+
- **Filter-bar removable chips read as compact/quiet, not oversized pills (gh#841).** Measurement (headless Playwright against the admin-dashboard filter bar) split the three candidate contributors named in the report: `tag-ui`'s geometry tokens (`--tag-px`/`--tag-py`/`--tag-font`) are pixel-identical to v0.8.31 — the size-ladder tokenization (#789) never changed them, and the bare-default removable tag (22px tall) measures *shorter* than the sibling `size="sm"` ghost buttons (24px) it sits beside. The `removable` dismiss button (16×16px) fits inside that box height too — no geometry regression anywhere. The "too big" read is 100% the #794 solid-default: a near-black stamped pill next to transparent ghost buttons reads heavy regardless of matching height. Fixed at the surface+pattern level, not the component: filter chips are now `size="sm" tone="muted"` — the canonical `patterns/filter-bar` demo (both the "Active filters" and "Filter bar + table" sections, plus its anatomy code sample and Rules list) and the admin-dashboard filter bar it was copied from. This corrects the #794 CHANGELOG entry's own call that the saas-admin filter-bar chips were "fine with the new solid default" — they read heavy next to ghost buttons in practice, which #794's static blast-radius audit didn't catch. `tag-ui` itself is unchanged; every other bare/solid tag surface (status pills, single prominent chips) is unaffected.
|
|
25
|
+
|
|
26
|
+
### Maintenance
|
|
27
|
+
- **`dist/` bundles rebuilt** in this cut's window (4 file(s)) — regenerated from the source changes described above, not independent edits.
|
|
28
|
+
- **`traits/` touched in this release window** (15 file(s), e.g. `anchor-positioning/anchor-positioning.examples.html`) — carried by the entries above.
|
|
29
|
+
|
|
30
|
+
## [0.8.32] — 2026-08-09
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- **`router-ui` wires the shipped `view-transition` trait into every route/content swap it performs (gh#815 slice 1).** The trait shipped with zero consumers; `router-ui` is the shell-semantic swap site every consumer (site's docs router, `admin-shell`'s documented content pattern, embedded-app's tab routers) ultimately routes through. Both of its swap paths now ride `this.startTransition(callback)`: the fetched-content path (`#loadContent`'s `innerHTML` replace) and a new `setRoutePath(path)` method for content-less consumers whose own CSS reveals children via `router-ui[data-route-path="…"]` selectors (previously those callers hand-rolled a bare `setAttribute`). Sensible defaults (`--a-duration`/`--a-easing` from `foundation/motion.css`) apply only when a consumer hasn't set its own `data-view-transition-duration`/`-easing`. No reduced-motion or no-API fallback reimplemented here — both are owned by `view-transition.js` and simply inherited. Verified live (Playwright, CDP screencast frame capture — real rasterized pixels, not a DOM proxy) against a throwaway `admin-shell` + `router-ui` fixture: the wired swap cross-fades over ~150–200ms with zero blank/background frames in every captured frame, versus a hard single-frame cut with the raw `setAttribute` path (negative control); the configured `--vt-duration` measured live as `250ms` (the `--a-duration` default) via `getComputedStyle()`.
|
|
35
|
+
PR #819 review (CodeRabbit): a real `document.startViewTransition()` callback runs asynchronously — `startTransition()` returns before it fires — so `#loadContent` now re-checks the nav token INSIDE the callback (never paints a superseded route) and awaits `transition.updateCallbackDone` before importing the route's setup module or emitting `route-loaded` (so setup code runs against the actually-painted DOM, not the previous route's). Verified with a rapid-fire triple-navigate against the real embedded-app (Labs → History → Briefing, no waits between clicks): exactly one panel visible at the end, matching the last click.
|
|
36
|
+
- **New `radio-group-ui` primitive — programmatic question↔group association for radio-ui sets (gh#729).** The canonical questionnaire shape (`radio.yaml`'s own example, shipped in PR #726) rendered a question `Text` plus bare `radio-ui` options sharing `[name=]` with no accessible link between them — no `radiogroup` role, no `aria-labelledby` — because `radio-ui` already supports ancestor `fieldset`/`[role="radiogroup"]` grouping (`radio.class.js:48`) but a plain layout container (`Column`) can't carry a role. Audited existing primitives first (`toggle-group-ui`/`toggle-option-ui`, `segmented-ui`/`segment-ui` — the established group-wrapper + bare-option-child pattern) before building: a composite primitive is the right semantic level, not a new wire-level `role` prop on a general container (which would also cut against the reserved-keys discipline from gh#653/PR #684). `radio-group-ui`'s own `[label]` prop is the group's accessible name — mints a label element, sets `role="radiogroup"` + `aria-labelledby` on the host; exclusivity and arrow-key navigation are unchanged, since `radio-ui`'s existing `closest('fieldset, [role="radiogroup"]')` lookup now resolves to the wrapper instead of falling through to `this.parentElement`. `radio.yaml`'s canonical example and a2ui rules updated to the new shape; registered in `packages/a2ui/runtime/registry.js` and `packages/web-components/components/index.js`/`styles/components.css` barrels.
|
|
37
|
+
- **`stepper-ui` gains an opt-in `interactive` mode — click-to-navigate + roving-tabindex keyboard nav** (gh#711 S1). Primitive audit (per `adia-author`'s mandatory §0 gate) found `stepper-ui` already covers the mock's labeled 3-state (complete/current/upcoming) progression shape — the operator ruling's premise that "none exists among ~126" didn't hold; a duplicate primitive would have violated the audit-before-authoring rule. Extended the existing primitive instead: `[interactive]` (absent by default — a bare stepper stays a read-only progress display) adds `role="button"` + roving `tabindex` + `aria-current="step"` to `<stepper-item-ui>` children, wires click + keyboard (arrow keys / Home / End / Enter / Space, orientation-aware), and fires a `change` event with `{ step }` — the stepper never drives navigation itself, the host reacts. Demo: new "Click-to-navigate" section in `stepper.examples.html`.
|
|
38
|
+
- **`stepper-ui` gains a pre-mutation veto + per-item `disabled` — the two host-controlled gates a review of the above pass asked for** (gh#711 S1). Before `#select()` mutates `step`, a new cancelable `step-request` event fires with `{ step, previous }`; `preventDefault()` vetoes the navigation — no mutation, no `change`, focus/roving-tabindex untouched. A host that never listens for it keeps the exact pre-veto uncontrolled behavior (`change` still fires unconditionally after every landed navigation) — additive, not a breaking change. Separately, `stepper-item-ui` gains a reflected `disabled` boolean (S2's phase-gating mechanism): a disabled step is skipped in the roving tabindex/arrow order, unclickable, and carries `aria-disabled`, independent of its `status`. Demo: new "Veto + phase-gating (disabled)" section showing both gates together — a conditional veto (block skip-ahead jumps) and an unconditional gate (`disabled` lifted once a phase is reached).
|
|
39
|
+
- **`scripts/build/dts-codegen.mjs` emits event-specific `addEventListener` overloads BEFORE the generic `HTMLElementEventMap` fallback**, not after (same review pass). TS overload resolution picks the first matching signature; several custom event names (`change` chief among them) are also literal keys of the built-in `HTMLElementEventMap`, so the generic overload — listed first — silently shadowed every typed custom-event overload it preceded, making them dead code no caller could actually resolve to. Regenerating with the reordered codegen touched every `.d.ts` with a custom `events:` block (58 files across `web-components` + `web-modules`) — reordering only, no type or behavior change, confirmed by a clean `npm run typecheck`.
|
|
40
|
+
|
|
41
|
+
### ⚠ BEHAVIOR CHANGE
|
|
42
|
+
|
|
43
|
+
- **`tag-ui`'s default render is now the `solid` look — bare `<tag-ui text="…">` is a stamped, high-contrast pill, not quiet chrome (gh#794).** Family variants (`accent`/`info`/`success`/`warning`/`danger`) already rendered solid unconditionally; the family-less `default` variant was the one holdout, requiring an explicit `tone="solid"` to get the inverse-stamp look. That asymmetry is gone — `:scope[variant="default"]` now carries the same `--md-sys-color-neutral-on-surface` bg / `--md-sys-color-neutral-background` fg pair the family variants use, so a bare tag with zero attributes renders solid by default. The pre-#794 quiet-chrome look (`--a-bg-muted` bg / neutral-on-surface fg) is still fully reachable — set `tone="muted"` explicitly (mirrors the existing muted opt-out family variants already had). `tone="solid"` remains valid and is now a no-op restatement of the default for the `default` variant.
|
|
44
|
+
**Blast radius** — every in-repo bare `<tag-ui>` call site (no `tone=`) was audited (`apps/`, `site/`, `catalog/`, `playgrounds/`, `packages/`). Family-variant call sites (`variant="info"` etc.) are unaffected — they were already solid. For the family-less `default` variant, four surfaces needed an explicit `tone="muted"` to keep their existing quiet register (citation/footnote strips and dense per-card category labels, where a wall of solid pills reads as noise — gh#778's oversized-pill concern): the gen-ui-feed agent-citations chips (`apps/genui/app/gen-ui-feed/gen-ui-feed.contents.html`), the `v050-agent-blocks` "Sources:" citation chips (`catalog/ui-patterns/v050-agent-blocks/v050-agent-blocks.contents.html`), the kanban-board-3col category tags (`catalog/ui-patterns/app/kanban-board-3col/kanban-board-3col.contents.html`), and the `a2ui-pattern-library` stage-name reference chips (`site/pages/patterns/a2ui-pattern-library.html`). Every other bare-tag surface (patient-visit header chip, registration source/department chips, saas-admin filter-bar removable chips, the embedded-app coding-card status tags, and every component/pattern `.examples.html` demo page showing the primitive's own default) was judged fine with the new solid default — those are single prominent chips or filter/status pills where the chip-IS-the-state look is the intent, not dense chip walls. `tag.yaml`'s `[tone]` prop docs + a2ui rules updated with the new default + the `tone="muted"` escape hatch; `tag.examples.html` demo updated to match.
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- **`avatar-ui`'s icon variant renders off-center when the avatar itself is placed in a container's `slot="icon"` (gh#809).** Light DOM has no shadow boundary and this repo's Light-DOM stance treats `slot=` as decorative metadata (positioning by tag + ancestor + DOM order, AGENTS.md) — `avatar-ui`'s own internal icon child (auto-created from the `icon` attribute) was marked `slot="icon"` for avatar's own CSS, sharing that attribute value with the very common consumer idiom of placing `<avatar-ui slot="icon">` inside a container that also targets `[slot="icon"]` for its own layout (e.g. `list-item-ui`'s icon-column rule, `admin-dashboard`'s Notifications rows being the reported instance). `list-item.css`'s `:scope [slot="icon"] { margin-inline-end: … }` — meant for the outer avatar — leaked onto the nested `icon-ui` too, pushing the flex-centered glyph 6px left of center at `size="sm"` (measured live via Playwright: `deltaX -6px`, initials/image variants unaffected). Fixed at the root rather than with a specificity arms race: the inner `icon-ui` no longer carries `slot="icon"` at all (`avatar.class.js`), and `avatar.css` selects it by tag (`:scope > icon-ui`) — with no shared attribute to hang off, the ancestor collision is structurally impossible rather than merely out-specificity'd. Verified live post-fix at sm/md/lg × circle/square, plus the reported admin-dashboard Notifications tab directly: all icon-variant deltas ≤0.008px (initials/image variants re-measured unchanged).
|
|
49
|
+
- **`stat-ui`'s trend delta renders a real Phosphor arrow icon instead of a CSS `::before` unicode triangle (gh#805).** `stat.css` baked `content: "\25B2"`/`"\25BC"` directly into the `[slot="change"]::before` pseudo-element — a raw glyph outside the icon system entirely (wrong stroke style, no size/weight ladder, invisible to `check-required-icons`). Fixed at the anatomy level, matching the precedent `table-ui`'s sort-direction icon already set (a class-rendered `icon-ui` child, not a CSS-only mask): `stat.js`'s `connected()` now builds a `icon-ui[data-trend-icon]` + text-span pair inside `[slot="change"]` (replacing the old bare-text write), and `render()` sets `name="arrow-up"`/`"arrow-down"` per `[trend]`, hidden for `neutral`. No `slot="icon"` attribute on the new child — that value is reserved for stat's own outer icon region, and an ancestor's `[slot="icon"]` descendant selector would otherwise leak margin/positioning onto it (the gh#809/PR#810 collision shape); spacing rides `[slot="change"]`'s existing flex `gap`, not margin, for the same reason. Color comes for free via `currentColor` (icon.css's default) inheriting the existing `--stat-up-fg`/`--stat-down-fg` rules — zero new raw colors. Sizing: `--a-icon-size: 1em` on the new child, scoped to the `--stat-change-size` font size. `stat.yaml`/`stat.js` gain a `requiredIcons: [arrow-up, arrow-down]` declaration (`check:required-icons` gate). The generated icon+text pair OWNS `[slot="change"]`'s content — same overwrite contract `#valueEl`/`#labelEl` already apply — so `render()` normalizes to exactly `[icon, text]` whenever a consumer-authored slot, an orphaned loading skeleton, or any other deviation is found, instead of appending after it (CodeRabbit review on PR #813: the original version could double-render the delta or leave stale skeleton markup after a disconnect/reconnect cycle). Verified live (headless Playwright, dev server): `stat.html`'s own demo matrix confirms both `arrow-up`/`arrow-down` resolve to a real `<svg>`, change-row height measures 13px identically before/after the fix (no layout shift), the admin-dashboard KPI row (the reported surface) renders proper Phosphor arrows in place of the triangles, and a consumer-populated `[slot="change"]` plus a loading→reconnect cycle both normalize to exactly `[icon, text]` with no duplicate or stale nodes.
|
|
50
|
+
- **`nav-group-ui`'s selected-header background no longer double-marks the selection in the expanded rail (gh#806, PR #797/gh#795 regression).** gh#795's fix added `background: var(--nav-group-bg-selected)` to both `:scope[selected] > [slot="header"]` and `:scope[data-selected-within] > [slot="header"]` unconditionally — correct for the collapsed rail (children `display:none`, the header is the sole selection signal) but wrong expanded: the selected CHILD already paints its own `nav-item-ui[selected]` pill, so the header background painted a second, redundant highlight above it (operator screenshot, `/site/examples/profile-security`, ACCOUNT group header over an already-highlighted Profile & Security child). The background declaration now lives ONLY inside the two collapsed-rail contexts the file already uses — `nav-ui[collapsed] &` and `@container sidebar (max-width: 96px)` (admin-sidebar's own collapse path drives the former; both are covered since the file's other collapsed rules already branch on both) — while the `color` declaration stays unscoped, applying in both modes as before (subtle text-weight change, no double-marking risk). Ruling on a directly-`[selected]` group header in expanded mode: NO background there either — `nav-group.class.js`'s own doc comment on the `selected` prop defines it as "marks the group that CONTAINS the selected route," the same semantic `data-selected-within` carries (JS-derived vs. consumer-set, not a distinct "IS the destination" leaf state) — so both attributes get identical scoped treatment, one selected-state grammar. Verified live (dev server + headless Playwright, `/site/components/admin-shell` and `/site/examples/profile-security`): expanded — header `background-color` is `rgba(0,0,0,0)` for both `[selected]`-direct and `[data-selected-within]` groups while the selected child's pill remains `oklch(0.598 0.0316 225.06 / 0.2)`; collapsed — gh#795's fix is intact and unregressed, header `background-color`/`border-radius` still measure identical to the selected item's (`oklch(0.598 0.0316 225.06 / 0.2)` / `9.324px` both rows, both attribute paths).
|
|
51
|
+
- **`nav-group-ui`'s selected header gets a real container background, matching `nav-item-ui` (gh#795).** `nav-group.css`'s `:scope[selected] > [slot="header"]` rule set only `color` — no `background` declaration existed anywhere in the file (confirmed by grep: zero `--nav-group-bg-selected` hits pre-fix), while `nav-item-ui[selected]` (`nav-item.css:15-18`) paints `background: var(--nav-item-bg-selected)` alongside `color`. The selected group header sat on a transparent fill in BOTH expanded and collapsed rails — not a collapsed-only gap; the collapsed square-footprint contract (`admin-shell.collapsed.css`'s `min-width`/`min-height` floor + the shared `border-radius`) already supplied the box shape, it just had nothing painted into it. New `--nav-group-bg-selected` token (mirrors `--nav-item-bg-selected` → `var(--a-bg-selected)`) and a `background` declaration now apply to both `:scope[selected] > [slot="header"]` and `:scope[data-selected-within] > [slot="header"]` (the group-contains-the-selected-route case, `#syncHeaderWeight`, gh#501) — one selected-state grammar for both row types, matching `nav-item-ui`'s existing unscoped `[selected]` rule that already applies identically in expanded and collapsed contexts. The icon-fill chain (gh#459/#501/#522) was untouched — verified intact in the investigation (`weight="fill"` + correct color already landed; the "outline" look was a contrast illusion from the missing background, not a wiring gap). Verified live (dev server + Playwright): selected `nav-item-ui` and selected `nav-group-ui[slot="header"]` compute the identical `background-color`/`border-radius` in both expanded and collapsed modes; screenshots confirm the group header now reads as a filled selection pill, same as the item rows beside it.
|
|
52
|
+
- **`card-ui > section[bleed] > table-ui` now drops the table's chrome automatically, closing a 14-instance repo-wide bug (gh#796).** `table.yaml`'s own `raw`/`frameless` docs already named `<card-ui><section bleed>` as the canonical case for embedding a table inside a card's own frame, but nothing made it automatic — a card-anatomy investigation (gh#796 findings) found 14 instances across 10 files (`admin-dashboard`, `members`, `billing`, `gen-ui-feed`, four `catalog/ui-patterns` demos, `table-toolbar` playground) carrying neither attribute, plus one dead inline-style workaround (`type-scale-review.examples.html`) that hand-rolled `border`/`border-radius` but missed `box-shadow` entirely. `card.css` now mirrors the existing `chart-ui` bleed auto-grow precedent: `& > section[bleed] > table-ui:not([raw]):not([frameless])` resets background/box-shadow/border-radius to match `[raw]`'s own reset — no markup edit needed on any of the 14 sites. Scoped to `[bleed]` sections only (not every card section): a plain `<section>` keeps its own padding/margin, so a nested table's frame there reads as an intentional nested surface (e.g. `table.examples.html`'s own demo cards), not a doubled edge — confirmed live via headless Playwright against an isolated fixture (standalone table keeps its 3-sided perimeter; `section[bleed]` table goes chromeless; plain-`section` table keeps chrome) and against the admin-dashboard audit-log card. An explicit `[raw]`/`[frameless]` on the table instance still applies and is honored. `card.yaml`/`table.yaml` cross-reference the new automatic behavior. `scripts/audit/audit-card-structure.mjs` (part of `npm run check`) gained a regression net that fails if the CSS rule is ever edited away or narrowed.
|
|
53
|
+
- **`api/sizing.css`'s bare `[size]` font-size fallback no longer out-competes size-aware components' own `font-size` (gh#790).** A layer-order defect, not a specificity one: the fallback shipped bundled into `sizing.css`'s `@import … layer(utilities)`, and ADR-0038's `utilities` layer beats `components` by layer order regardless of selector specificity, so no `:where()`/`:not()` wrap inside `sizing.css` could ever have fixed it — same bug class as gh#651/gh#766 (an unlayered/later-layer rule beating a layered/earlier-layer one unconditionally; restated in ADR-0038's own `resets.css` `ul/ol[role="list"]` lesson). Split the single `[size] { font-size: var(--a-body-size); }` declaration into new `styles/api/sizing-fallback.css`, imported `layer(elements)` — one layer BELOW `components`, the same relationship `type/elements.css`'s `:where(h1)` etc. already have to component/`[variant]` rules — so `size="md"` (the documented default) now renders identically to no attribute on every size-aware component, while `sizing.css`'s `[size="sm"/"lg"]` token-setting rules stay in `utilities` unchanged. Verified live (Playwright, new `scripts/qa/sizing-fallback-probe.mjs`) on `button-ui`, `kbd-ui`, `tag-ui`, `badge-ui`, `input-ui`, `nav-item-ui`: no-attr-vs-`size="md"` computed `font-size`/padding now match on all six (was a 13px→14px flip on `button-ui`/`kbd-ui`/`nav-item-ui`; `tag-ui`/`badge-ui` were already unaffected); `sm`/`lg` still differ from `md` wherever a component implements a size ladder in `font-size` (`tag-ui`/`badge-ui` don't — a pre-existing, unrelated gh#789 design choice, not touched by this fix).
|
|
54
|
+
- **`icons-phosphor.js`'s zero-config auto-install resolves `@phosphor-icons/core` under npm/yarn-hoisted layouts, not just pnpm's nested one — and a genuine zero-icon failure now fails loudly while a working manual setup gets silence (gh#786, third strike after gh#287/gh#340).** The single entry-relative glob `../node_modules/@phosphor-icons/core/…` only holds under pnpm (which populates a package's own local `node_modules`); under npm/yarn-classic workspace hoisting the transitive dep lands as a *sibling* in the shared `node_modules`, so the glob was structurally guaranteed to match zero files and printed an alarming failure-shaped warning on every dev-server load — with no consumer config able to prevent it (`optimizeDeps.exclude`/`server.fs.allow` address different failure modes; path arithmetic isn't configurable). Fixed at the semantic level with a candidate-glob ladder — three static globs probed in order, first non-empty installed: `../node_modules/…` (pnpm-nested, gh#287's fix, pnpm consumers unaffected), `../../../@phosphor-icons/core/…` (npm/yarn-hoisted consumer install, gh#786's layout), `../../../node_modules/…` (npm-shaped monorepo checkout, gh#340's layout); losing candidates transform to `{}` at build time, zero runtime cost. The all-zero case no longer installs empty maps (which marked the registry "ready" and muted `<icon-ui>`'s own unwired diagnostic): the verdict defers one tick — if the consumer's manual registration wired the registry (the reporting consumer's setup) nothing prints at all; if the registry is genuinely unwired, one `console.error` names the three probed layouts and the three remedies. Proven by `scripts/qa/phosphor-glob-layout-probe.mjs` (real `vite build --ssr` against five scaffolded layouts: pnpm-nested, npm-hoisted, repo-npm-shaped, phosphor-missing negative control, phosphor-missing + manual registration) plus `core/icons-phosphor.test.js` (vitest, real glob transform against the live checkout layout).
|
|
55
|
+
- **`tag-ui`/`badge-ui` `size="sm"|"lg"` now actually resizes the pill (gh#779).** Both yamls have long documented `size` as riding "the universal `[size]` attribute system" (the same mechanism `button-ui` reads — `button.css:81-84`), but `tag.css`/`badge.css` hardcoded `--tag-px`/`--tag-py`/`--tag-font` and `--badge-px`/`--badge-py`/`--badge-font-size` with zero `[size]` selectors — `size="sm"` and `size="lg"` rendered pixel-identical to the default. No comment, ADR, or yaml note found for the inertness being deliberate, so the prop was wired rather than removed. NOT a direct alias of `--a-ui-px`/`--a-ui-size` (the tokens `button-ui` reads) — tag/badge's own `md` baseline predates and differs from that system's `md` (6px/12px here vs 8px/13px there); aliasing directly would have inflated every existing default tag/badge, the exact failure mode gh#778 independently reported. Instead each component keeps its own `md` token values unchanged (verified pixel-identical: `--tag-px`/`--badge-px` still resolve to `--a-space-1-5` = 6px @ d=1, font still `--a-ui-sm` = 12px) and gets a component-owned `sm`/`lg` ladder (`--a-space-1`/`--a-space-2` for padding, `--a-ui-xs`/`--a-ui-md` for font-size) applied only under explicit `[size="sm"]`/`[size="lg"]` — no attribute, no change. 8 new tests (`tag.test.js`, new `badge.test.js`) assert the CSS source shape (happy-dom doesn't evaluate `@scope` via `getComputedStyle()` — same constraint documented in `tag.test.js`'s header); confirmed live via headless Playwright against the dev server.
|
|
56
|
+
- **`input-ui` no longer collapses to near-zero width as an empty, placeholder-only flex item (gh#781).** The host is `display: block`, which gives it no width once it becomes a flex-row child (`row-ui` or any `display:flex` parent — a block-level child's "fill available width" behavior doesn't apply once its parent is flex); `[slot="field"]` has no intrinsic width of its own; and the one child that could drive content-based sizing, the empty contenteditable `[slot="text"]`, renders its placeholder via a deliberately out-of-flow `::before` (load-bearing for caret positioning — unchanged) that contributes zero intrinsic width. New `--input-min-width: 20ch` token (mirrors the native `<input>`'s historic intrinsic-width baseline) floors `:scope`'s `min-width`, so an empty `<input-ui placeholder="…">` stays legible in a flex row with no explicit sizing — confirmed live (dev server + Playwright): an unsized instance measures ~176px (20ch at the default font/scale) instead of near-zero, while a `flex: 1` sibling still grows past the floor to fill the row and an explicit `width` above the floor still constrains normally (`--input-min-width` is a floor, not a fixed size — a consumer wanting narrower than 20ch overrides `--input-min-width` down, same model as `step-progress-ui`'s existing `--step-progress-min-width`). Documented in `input.yaml`'s top-level description + `tokens` block; `input.examples.html` gains a flex-row demo artifact + CSS-tokens table row.
|
|
57
|
+
- **`breadcrumb-ui`'s current crumb ellipsizes instead of hard-clipping mid-word (gh#780).** `[data-item]`'s truncation quartet (`overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: var(--breadcrumb-max-width)`) lived alongside `display: inline-flex` — per the CSS Overflow spec, `text-overflow: ellipsis` never paints on a flex container's own box, so `overflow: hidden` still clipped long labels but the `…` glyph never rendered. Item content is a bare text node (or an `icon-ui` child for icon-leading crumbs) directly inside `[data-item]`, not a nested wrapper — rather than restructure the light-DOM child shape, `[data-item]` now defaults to `display: inline-block` (where ellipsis is spec-defined to work) and opts back into `inline-flex` only when an `icon-ui` child is actually present, via `[data-item]:has(> icon-ui)`. Text-only crumbs get the ellipsis fix; icon-leading crumbs keep their flex baseline-alignment unchanged. Verified live against the dev server (headless Playwright): a label exceeding `--breadcrumb-max-width` (12rem) now measures `scrollWidth > clientWidth` with `text-overflow: ellipsis` computed and the `…` glyph visible in a screenshot; icon-leading and overflow-popover examples unaffected.
|
|
58
|
+
- **`nav-group-ui`'s collapsed-rail header is a true square again, matching `nav-item-ui` (gh#767).** Two independent defects, both traced to a live probe on `/site/patterns/admin-sidebar` (`demo-nav-3`, collapsed to a 48px rail): (1) `nav-group.css`'s collapsed-mode `align-self: stretch` (two copies — `nav-ui[collapsed] &` and the duplicate inside `@container sidebar (max-width: 96px)`) fought `admin-shell.collapsed.css`'s FEEDBACK-39 square-footprint contract (`align-items: center` at the `nav-ui` level + a `min-width`/`min-height` floor), stretching the group header to the full rail width (measured 64×30) instead of the 30×30 square `nav-item-ui` already got. Both rules were dead code in expanded mode (redundant with the ambient flex-column `align-items: stretch` default) and actively harmful in collapsed mode under admin-shell — removed; the header now falls through to whatever `align-items` the ambient `nav-ui` sets, same as `nav-item-ui` always did. (2) `nav-item.css` pinned collapsed icon size to a literal `18px`; `nav-group.css` referenced `--nav-group-icon-font-size`, a token defined nowhere in the codebase — guaranteed-invalid per the CSS custom-properties spec, so it silently fell back to `icon.css`'s ambient-font-size fallback instead of a deterministic size, drifting with `[scale]`/`[verse]` tiers. Both now consume one shared `--nav-icon-size-collapsed` token (defined in `nav.css`, the nav family's shared-token home, alongside `--nav-width-collapsed` et al.) in their respective `@container sidebar (max-width: 96px)` blocks. The identical dead-token defect in each component's unconditional (non-collapsed) icon-size rule — `nav-item.css`'s own `--nav-item-icon-font-size` reference — was removed too (same guaranteed-invalid shape, same fallback result, now explicit instead of accidental). Verified via a headless probe against the real dev preview: collapsed `nav-group-ui [slot="header"]` measures 30×30 (was 64×30), both icons compute `--a-icon-size: 18px`; expanded mode unchanged (both rows still stretch full-width; icon sizes still ride the ambient-font fallback identically to before).
|
|
59
|
+
- **`list-item-ui`/`list-ui`/`action-item-ui`/`action-list-ui` adopt a consumer-authored `role` instead of stamping over it (gh#753).** `connected()` on all four set `role` unconditionally — a consumer-authored `role="button"` on a clickable row (the embedded-app population-health views' `cohort.js`/`alert-queue.js`/`followup-debt.js`/`visit-grid.js`, which all author `<list-item-ui role="button">` rows) was silently overwritten to `listitem`/`list` on upgrade. Each now stamps only when `role` is absent (`if (!this.hasAttribute('role')) …`); `disconnected()` never touched `role` on any of the four, so the adopted role already survived disconnect/reconnect with no extra change needed. Estate sweep (`grep setAttribute('role'` across `connected()`) found ~70 other unconditional role stamps; only these four share the exact clobber shape (a generic collection-item/list role on a light-DOM host whose internal query logic is tag-based, not role-based, so overriding the role can't break the primitive's own behavior). The rest stamp a role that IS the primitive's own ARIA-widget contract (e.g. `tabs-ui`'s `tablist`, `tree-ui`'s `tree`, `radio-ui`'s `radio`, `nav-item-ui`'s `link`) — overriding those would break the widget pattern itself, so they're intentionally left unconditional. 8 new tests (`list.test.js`, new `action-list.test.js`, + a `cohort.js` DOM regression test in `apps/embedded-app`).
|
|
60
|
+
- **A non-selectable `list-ui` no longer strips consumer-set `tabindex` off its rows** (gh#746). The `!selectable` branch of `render()` cleared `aria-selected`/`tabindex` from every child unconditionally — cleanup meant for a `selectable` toggle-off, but it also clobbered focusability a CONSUMER manages on its own rows (the patient-visit rail's roving tabindex was silently stripped on the list's connect render). The list now tracks whether it stamped selection chrome and only clears what it stamped; toggling `selectable` off still cleans up exactly as before. 2 new tests.
|
|
61
|
+
- **`stepper-ui` reflects a baked `[disabled]` on first paint and re-renders on a child `[disabled]` flip, with no host poke needed** (gh#736, found in PR #735's review). Two related gaps: (1) during static innerHTML parsing, `stepper-ui`'s own first `render()` can fire while its `stepper-item-ui` children aren't appended yet (confirmed: `render()` sees zero items on that pass) — the parent's `render()` now falls back to `item.hasAttribute('disabled')` alongside the property read, and a scoped `MutationObserver` (childList + subtree, `attributeFilter: ['disabled']` — the `nav-group-ui` gh#501 idiom) re-renders once children actually land, so the baked state is correct before any real paint (microtasks always drain first). (2) a child's `[disabled]` flip after mount now re-renders the parent automatically via the same observer — hosts no longer poke `stepper.render()` manually. Removed the now-unnecessary post-boot `stepper.render()` poke from `apps/embedded-app`'s `adia-embed-labs.js` (marked with the upgrade-race comment from PR #735) — the app works without it, proven by its own vitest suite plus 2 new stepper tests (26 total, was 24).
|
|
62
|
+
- **`chat-input-ui` composer no longer shows a resting border alongside the focus ring when hovered while focused (gh#718).** The Safari-17.x hover workaround (`chat-input-ui:not([disabled]):hover`, moved outside `@scope` for engine compat) picked up an extra type-selector specificity point the scoped `:focus-within` override didn't have — `(0,2,1)` vs `(0,2,0)` — so hover's `border-color` silently beat focus's `border-color: transparent` whenever both states were true (the common case: click into the textarea leaves the pointer sitting on it). Fixed at the contract level by excluding the focused state explicitly: `chat-input-ui:not([disabled]):hover:not(:focus-within)`, rather than relying on a specificity race.
|
|
63
|
+
Swept every component carrying the same Safari-workaround comment (23 files) for the identical latent gap: found and fixed 3 more instances of the same shape — `option-card-ui:not([checked]):not([disabled]):hover`, `radio-ui:not([disabled]):hover [slot="dot"]` (plus its `[checked]` variant), and `check-ui:not([disabled]):hover [slot="box"]` (plus its `[checked]`/`[indeterminate]` variant) — all now exclude `:focus-visible`. Also found and fixed one hover-vs-focus-visible custom-property race in `menu-item-ui[variant="danger"]:hover`, where the `[variant="danger"]` attribute selector tied the hover rule's specificity with `:scope:focus-visible`'s and won the tie-break on type-selector count. `input-ui`/`textarea-ui`'s nested-control pattern was confirmed safe (their hover selector targets a descendant slot with fewer class-selectors than the focus rule, so focus always wins) — verified computationally per file, not assumed from the shared comment.
|
|
64
|
+
|
|
65
|
+
### Maintenance
|
|
66
|
+
|
|
67
|
+
- **`dist/` bundles rebuilt** in this cut's window (6 file(s)) — regenerated from the source changes described above, not independent edits.
|
|
68
|
+
|
|
3
69
|
## [0.8.31] — 2026-08-07
|
|
4
70
|
|
|
5
71
|
### Fixed
|
package/MIGRATION.md
CHANGED
|
@@ -41,6 +41,8 @@ Version cuts and migration scope:
|
|
|
41
41
|
- **Recommended consumer migration:** if you were using `@adia-ai/web-modules/runtime/a2ui-root/a2ui-root.js` + `@adia-ai/web-components/components/canvas/canvas.js` for generative UI surfaces, collapse to a single `import '@adia-ai/web-modules/generative'`. Both old paths still work.
|
|
42
42
|
- See [§ v0.6.7 section below](#migrating-to-adia-aiv067-2026-05-19).
|
|
43
43
|
|
|
44
|
+
- **Upcoming (variant/color/tone convention cut)** — **BREAKING:** `accent` is retired as an enum value on `badge-ui` (`variant`), `tag-ui` (`variant`), and `button-ui` (`color`) — `primary` is the brand-emphasis value on badge/tag (identical rendering; pure rename). Badge-ui's `variant` becomes family-only: `variant="muted"` / `variant="neutral"` are removed — both map to `tone="muted"` (the fill-style axis they always belonged on). Badge's family-less `tone="muted"` also re-tokens to the `--md-sys-color-neutral-low` fill (a visibly stronger tint — no markup change). The same one-axis rule then reaches **alert-ui** (`variant="muted"` / `"neutral"` removed — they were exact aliases of the default, so just drop the attribute; zero visual change) and **mark-ui** (`variant="muted"` removed with no enum successor — a neutral highlight is now a `--mark-bg`/`--mark-fg` token override); neither gains a `tone` axis. `accent` on OTHER components (icon-ui, text-ui, chart-ui, …) is unchanged. Pre-1.0 PATCH cadence. See [§ Upcoming — variant/color/tone convention cut](#upcoming-variant-color-tone-convention-cut).
|
|
45
|
+
|
|
44
46
|
- **Upcoming (`<theme-provider>` cut)** — **BREAKING:** the token-theme attribute is renamed `data-theme` → `theme` repo-wide. Replace `<html data-theme="ocean">` → `<html theme="ocean">` and any `[data-theme="…"]` selectors → `[theme="…"]`. Mechanical: a word-boundary find-replace of `data-theme` → `theme` (preserve compound attrs like `data-theme-slug` / `data-themed`). Pre-1.0 PATCH cadence. Also new: the **`<theme-provider>`** element adopts the AdiaUI foundation into the document from anywhere (no `<head>` stylesheet link), with opt-in `theme="…"` (named preset) + `scale="verse|prose"` (typographic register) attributes that fetch their layer on demand.
|
|
45
47
|
- **`theme` ≠ `scheme` — don't conflate the two axes.** This rename is purely the *theme* axis (a named preset: ocean / forest / slate / …). A light/dark *mode* is the orthogonal *scheme* axis — it belongs on `color-scheme` / `data-scheme`, **never** on `[theme]`. If you'd encoded a mode as `data-theme="dark"`, that was a category error: after the rename it is a no-op on *both* axes. Move it to the scheme axis — `<html style="color-scheme: dark">`, `data-scheme`, or `<toggle-scheme-ui>`. (Surfaced by color-app, 2026-06-07.)
|
|
46
48
|
|
|
@@ -54,6 +56,140 @@ Live demos and per-component docs at [ui-kit.exe.xyz/site/](https://ui-kit.exe.x
|
|
|
54
56
|
|
|
55
57
|
---
|
|
56
58
|
|
|
59
|
+
## Upcoming — variant/color/tone convention cut
|
|
60
|
+
|
|
61
|
+
**One theme: every color-ish attribute owns exactly one axis.** `variant`/`tone` on badge & tag
|
|
62
|
+
and `variant`/`color` on button now each carry either a *semantic family* or a *fill style*,
|
|
63
|
+
never both. `accent` (the name, not the capability) is retired on the three components —
|
|
64
|
+
`primary` carries brand emphasis. The `accent` retirement's scope is **badge-ui / tag-ui /
|
|
65
|
+
button-ui only**: `accent` remains valid on icon-ui, text-ui, chart-ui, heatmap-ui, rating-ui,
|
|
66
|
+
spinner-ui, and toggle-scheme-ui — scope every sweep to the three tags, never find-replace
|
|
67
|
+
`accent` blanket. The same one-axis discipline additionally retires style-like values from two
|
|
68
|
+
more `variant` enums — **alert-ui** loses `muted` *and* `neutral`, **mark-ui** loses `muted`
|
|
69
|
+
only (`neutral` was never one of its values) — and neither component gains a `tone` axis; they
|
|
70
|
+
have exactly one fill style each.
|
|
71
|
+
|
|
72
|
+
### Mechanical sweeps (safe to automate)
|
|
73
|
+
|
|
74
|
+
- **`<badge-ui variant="accent">` → `<badge-ui variant="primary">`** — identical rendering
|
|
75
|
+
(both resolved to the same solid primary pair; the rename also gains working
|
|
76
|
+
`tone="muted"`/`tone="outline"` pairings that `primary` previously lacked).
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
git grep -nE '<badge-ui[^>]*variant="accent"'
|
|
80
|
+
git grep -lE '<badge-ui[^>]*variant="accent"' | xargs perl -i -pe 's/(<badge-ui[^>]*variant=")accent(")/${1}primary${2}/g'
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
- **`<tag-ui variant="accent">` → `<tag-ui variant="primary">`** — identical rendering
|
|
84
|
+
(`primary` is new to tag-ui's enum in this cut, replacing `accent` token-for-token).
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
git grep -nE '<tag-ui[^>]*variant="accent"'
|
|
88
|
+
git grep -lE '<tag-ui[^>]*variant="accent"' | xargs perl -i -pe 's/(<tag-ui[^>]*variant=")accent(")/${1}primary${2}/g'
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
- **`<badge-ui variant="muted">` / `<badge-ui variant="neutral">` → `<badge-ui tone="muted">`**
|
|
92
|
+
— drop the `variant` attribute, add `tone="muted"`. The two old values were identical to each
|
|
93
|
+
other; both land on the same new form. (Keep any real family value: `variant="info"
|
|
94
|
+
tone="muted"` stays exactly as it is — only bare `muted`/`neutral` on `variant` move.)
|
|
95
|
+
If the element already carries a `tone` attribute, drop `variant` instead — the one-liner
|
|
96
|
+
below would otherwise produce two `tone` attributes on the same element.
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
git grep -nE '<badge-ui[^>]*variant="(muted|neutral)"'
|
|
100
|
+
git grep -lE '<badge-ui[^>]*variant="(muted|neutral)"' | xargs perl -i -pe 's/(<badge-ui[^>]*)variant="(?:muted|neutral)"/${1}tone="muted"/g'
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
- **`<alert-ui variant="muted">` / `<alert-ui variant="neutral">` → drop the attribute** — both
|
|
104
|
+
values re-declared the exact tokens of the family-less default, so a bare `<alert-ui>` renders
|
|
105
|
+
identically. Zero visual change. (Keep any real family value untouched: only bare
|
|
106
|
+
`muted`/`neutral` move.) alert-ui gains **no** `tone` axis — the quiet banner IS the default.
|
|
107
|
+
|
|
108
|
+
`-U` because an alert tag's attributes often span several lines (a line-based `git grep`
|
|
109
|
+
misses those); `(\s[^>]*)?\svariant=` so the pattern can't match `data-variant` or a
|
|
110
|
+
longer tag like `<alert-ui-extended>`; the `-g` excludes keep the rewrite off this guide's
|
|
111
|
+
own examples and off generated artifacts (regenerate those with your build, don't sed them).
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
EX=(-g '!*MIGRATION*' -g '!*CHANGELOG.md' -g '!**/dist/**' -g '!*.a2ui.json' -g '!*.examples.md')
|
|
115
|
+
rg -nU '<alert-ui(\s[^>]*)?\svariant="(muted|neutral)"' "${EX[@]}"
|
|
116
|
+
rg -lU '<alert-ui(\s[^>]*)?\svariant="(muted|neutral)"' "${EX[@]}" \
|
|
117
|
+
| xargs perl -0777 -i -pe 's/(<alert-ui(?:\s[^>]*)?)\s+variant="(?:muted|neutral)"/${1}/gs'
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
- **Programmatic setters (JS render paths — the sweep HTML greps miss):**
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
git grep -rnE "setAttribute\('variant', *'(accent|muted|neutral)'\)"
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
For badge/tag hosts: `'accent'` → `'primary'`; `setAttribute('variant', 'muted'|'neutral')`
|
|
127
|
+
on a badge → `setAttribute('tone', 'muted')`. Verify each hit's element is actually a
|
|
128
|
+
badge/tag before rewriting — other components legitimately use these values.
|
|
129
|
+
|
|
130
|
+
- **A2UI JSON documents** (chunks, saved compositions): on `"component": "Badge"` /
|
|
131
|
+
`"component": "Tag"` nodes only — `"variant": "accent"` → `"variant": "primary"`;
|
|
132
|
+
Badge `"variant": "muted"|"neutral"` → delete the `variant` member, add `"tone": "muted"`.
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
git grep -nE '"variant": *"(accent|muted|neutral)"' # then filter to Badge/Tag nodes — Text/Icon nodes also match and must NOT be touched
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Judgment items — *never auto-sweep*
|
|
139
|
+
|
|
140
|
+
- **`<button-ui color="accent">` — value removed, no direct successor.** It never had CSS
|
|
141
|
+
(rendered as the default fill), so every call site encodes an *intent* the renderer ignored.
|
|
142
|
+
Re-read each site: a brand CTA wants `variant="primary"` (drop `color`); a semantic action
|
|
143
|
+
wants a real family (`color="danger"` / `"success"` / …); decorative uses just drop the attr.
|
|
144
|
+
Expect a visible change either way — the button finally renders something on purpose.
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
git grep -nE '<button-ui[^>]*color="accent"'
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
- **`badge-ui` muted rendering change (no markup change; review only).** Family-less
|
|
151
|
+
`tone="muted"` (including everything the mechanical sweeps just moved onto it) now renders
|
|
152
|
+
the mid-tone `--md-sys-color-neutral-low` fill instead of the previous near-white container
|
|
153
|
+
tint — deliberately more distinct from the page surface (AA-verified at 8.2:1/8.5:1). If a
|
|
154
|
+
dense row of muted badges reads too heavy after upgrading, that surface likely wants
|
|
155
|
+
`<tag-ui tone="muted">` (which keeps the quiet chrome) or no chip at all.
|
|
156
|
+
|
|
157
|
+
- **`status="beta"`** now renders `primary` instead of `accent` — same pixels, no action;
|
|
158
|
+
listed only so diff-watchers aren't surprised by the attribute reflection changing.
|
|
159
|
+
|
|
160
|
+
- **`<mark-ui variant="muted">` — value REMOVED, and unlike alert's it was a real render.**
|
|
161
|
+
There is no enum successor: mark's four variants are a *family* axis and its family-less
|
|
162
|
+
identity is the yellow marker itself, so a neutral highlight is now a token override, not a
|
|
163
|
+
variant. Rewrite each site to the public tokens (`--mark-bg` / `--mark-fg` already exist and
|
|
164
|
+
are unchanged), or pick a real family if the highlight was actually semantic:
|
|
165
|
+
|
|
166
|
+
```html
|
|
167
|
+
<!-- before --> <mark-ui variant="muted">term</mark-ui>
|
|
168
|
+
<!-- after --> <mark-ui style="--mark-bg: var(--a-bg-muted); --mark-fg: var(--md-sys-color-neutral-on-surface)">term</mark-ui>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
rg -nU '<mark-ui(\s[^>]*)?\svariant="muted"' -g '!*MIGRATION*' -g '!*CHANGELOG.md'
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Left unmigrated, `variant="muted"` matches no rule and the mark renders the default yellow —
|
|
176
|
+
fails loud (visibly), not silent.
|
|
177
|
+
|
|
178
|
+
### Verify
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
git grep -nE '<(badge|tag)-ui[^>]*variant="(accent|muted|neutral)"' # must be 0
|
|
182
|
+
git grep -nE '<button-ui[^>]*color="accent"' # must be 0
|
|
183
|
+
# must be 0 — -U for multi-line tags; the excludes keep this guide's own before/after
|
|
184
|
+
# examples and generated artifacts out of the count
|
|
185
|
+
rg -nU '<(alert|mark)-ui(\s[^>]*)?\svariant="(muted|neutral)"' \
|
|
186
|
+
-g '!*MIGRATION*' -g '!*CHANGELOG.md' -g '!**/dist/**' -g '!*.a2ui.json' -g '!*.examples.md'
|
|
187
|
+
git grep -rnE "setAttribute\('variant', *'(accent|muted|neutral)'\)" # 0 on badge/tag/alert/mark hosts
|
|
188
|
+
npm run check # in-repo consumers; downstream: your app's build + a visual pass on badge-dense views
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
57
193
|
## Migrating to `@adia-ai/*@v0.8.23` (2026-07-30)
|
|
58
194
|
|
|
59
195
|
- **Removed: the `--a-font` alias** (pre-ADR-0035 name, DEPRECATED since the 0.8.x line). Any surviving `font-family: var(--a-font)` goes invalid-at-computed-value-time → `inherit` → UA **serif** — the visible symptom is Times-faced labels on an otherwise-correct page.
|
|
@@ -102,6 +102,14 @@
|
|
|
102
102
|
"description": "Open body top padding.",
|
|
103
103
|
"default": 0
|
|
104
104
|
},
|
|
105
|
+
"--accordion-item-body-px": {
|
|
106
|
+
"description": "Open body left/right padding — independent of the header's --accordion-item-px.",
|
|
107
|
+
"default": "var(--accordion-item-px)"
|
|
108
|
+
},
|
|
109
|
+
"--accordion-item-body-py": {
|
|
110
|
+
"description": "Open body bottom padding — independent of the header's --accordion-item-py.",
|
|
111
|
+
"default": "var(--accordion-item-py)"
|
|
112
|
+
},
|
|
105
113
|
"--accordion-item-border": {
|
|
106
114
|
"description": "Item surface border (full shorthand — mirrors --card-border).",
|
|
107
115
|
"default": "none"
|
|
@@ -82,6 +82,12 @@ tokens:
|
|
|
82
82
|
--accordion-item-body-pt:
|
|
83
83
|
description: Open body top padding.
|
|
84
84
|
default: 0
|
|
85
|
+
--accordion-item-body-px:
|
|
86
|
+
description: Open body left/right padding — independent of the header's --accordion-item-px.
|
|
87
|
+
default: var(--accordion-item-px)
|
|
88
|
+
--accordion-item-body-py:
|
|
89
|
+
description: Open body bottom padding — independent of the header's --accordion-item-py.
|
|
90
|
+
default: var(--accordion-item-py)
|
|
85
91
|
|
|
86
92
|
keywords:
|
|
87
93
|
- accordion-item
|
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
--accordion-item-border: none;
|
|
37
37
|
--accordion-item-divider: none;
|
|
38
38
|
--accordion-item-body-pt: 0;
|
|
39
|
+
--accordion-item-body-px: var(--accordion-item-px);
|
|
40
|
+
--accordion-item-body-py: var(--accordion-item-py);
|
|
39
41
|
|
|
40
42
|
/* ── Colors ── */
|
|
41
43
|
--accordion-item-header-fg: var(--md-sys-color-neutral-on-surface-variant);
|
|
@@ -196,7 +198,7 @@
|
|
|
196
198
|
|
|
197
199
|
:scope[open] [slot="body"] {
|
|
198
200
|
display: block;
|
|
199
|
-
padding: var(--accordion-item-body-pt) var(--accordion-item-px) var(--accordion-item-py);
|
|
201
|
+
padding: var(--accordion-item-body-pt) var(--accordion-item-body-px) var(--accordion-item-body-py);
|
|
200
202
|
border-top: var(--accordion-item-divider); /* default `none` in flat → invisible, zero layout (Finding 3) */
|
|
201
203
|
}
|
|
202
204
|
}
|
|
@@ -38,12 +38,12 @@ plus a divider between them when open) — the composition to reach for
|
|
|
38
38
|
when nesting the accordion inside <card-ui>. */
|
|
39
39
|
variant: 'flat' | 'contained';
|
|
40
40
|
|
|
41
|
+
addEventListener(type: 'toggle', listener: (ev: AccordionToggleEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
41
42
|
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
42
43
|
type: K,
|
|
43
44
|
listener: (this: UIAccordion, ev: HTMLElementEventMap[K]) => unknown,
|
|
44
45
|
options?: boolean | AddEventListenerOptions,
|
|
45
46
|
): void;
|
|
46
|
-
addEventListener(type: 'toggle', listener: (ev: AccordionToggleEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
export interface AccordionItemToggleEventDetail {
|
|
@@ -63,10 +63,10 @@ accordion-ui's [variant] — set this directly on ONE item only to
|
|
|
63
63
|
opt it out of (`flat`) or into (`contained`) the host's posture. */
|
|
64
64
|
variant: 'flat' | 'contained';
|
|
65
65
|
|
|
66
|
+
addEventListener(type: 'toggle', listener: (ev: AccordionItemToggleEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
66
67
|
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
67
68
|
type: K,
|
|
68
69
|
listener: (this: UIAccordionItem, ev: HTMLElementEventMap[K]) => unknown,
|
|
69
70
|
options?: boolean | AddEventListenerOptions,
|
|
70
71
|
): void;
|
|
71
|
-
addEventListener(type: 'toggle', listener: (ev: AccordionItemToggleEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
72
72
|
}
|
|
@@ -37,7 +37,9 @@ export class UIActionList extends UIElement {
|
|
|
37
37
|
#bound = false;
|
|
38
38
|
|
|
39
39
|
connected() {
|
|
40
|
-
|
|
40
|
+
// Adopt-or-stamp (gh#753): a consumer-authored role survives connect —
|
|
41
|
+
// only stamp when absent.
|
|
42
|
+
if (!this.hasAttribute('role')) this.setAttribute('role', 'list');
|
|
41
43
|
if (!this.#bound) {
|
|
42
44
|
this.#bound = true;
|
|
43
45
|
this.addEventListener('click', this.#onClick);
|
|
@@ -100,7 +102,9 @@ export class UIActionItem extends UIElement {
|
|
|
100
102
|
static template = () => null;
|
|
101
103
|
|
|
102
104
|
connected() {
|
|
103
|
-
|
|
105
|
+
// Adopt-or-stamp (gh#753): a consumer-authored role survives connect —
|
|
106
|
+
// only stamp "listitem" when the host has no author-set role at all.
|
|
107
|
+
if (!this.hasAttribute('role')) this.setAttribute('role', 'listitem');
|
|
104
108
|
if (!this.hasAttribute('tabindex')) this.setAttribute('tabindex', '0');
|
|
105
109
|
this.#stamp();
|
|
106
110
|
this.#syncAria();
|
|
@@ -25,12 +25,12 @@ export type ActionListActionEvent = CustomEvent<ActionListActionEventDetail>;
|
|
|
25
25
|
|
|
26
26
|
export class UIActionList extends UIElement {
|
|
27
27
|
|
|
28
|
+
addEventListener(type: 'action', listener: (ev: ActionListActionEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
28
29
|
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
29
30
|
type: K,
|
|
30
31
|
listener: (this: UIActionList, ev: HTMLElementEventMap[K]) => unknown,
|
|
31
32
|
options?: boolean | AddEventListenerOptions,
|
|
32
33
|
): void;
|
|
33
|
-
addEventListener(type: 'action', listener: (ev: ActionListActionEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export class UIActionItem extends UIElement {
|
|
@@ -31,10 +31,10 @@ export class UIAgentArtifact extends UIElement {
|
|
|
31
31
|
/** neutral | accent | warning | danger */
|
|
32
32
|
tone: string;
|
|
33
33
|
|
|
34
|
+
addEventListener(type: 'artifact-toggle', listener: (ev: AgentArtifactArtifactToggleEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
34
35
|
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
35
36
|
type: K,
|
|
36
37
|
listener: (this: UIAgentArtifact, ev: HTMLElementEventMap[K]) => unknown,
|
|
37
38
|
options?: boolean | AddEventListenerOptions,
|
|
38
39
|
): void;
|
|
39
|
-
addEventListener(type: 'artifact-toggle', listener: (ev: AgentArtifactArtifactToggleEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
40
40
|
}
|
|
@@ -35,11 +35,11 @@ export class UIAgentFeedbackBar extends UIElement {
|
|
|
35
35
|
/** Save button text; empty hides it. */
|
|
36
36
|
saveLabel: string;
|
|
37
37
|
|
|
38
|
+
addEventListener(type: 'feedback-rate', listener: (ev: AgentFeedbackBarFeedbackRateEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
39
|
+
addEventListener(type: 'feedback-save', listener: (ev: AgentFeedbackBarFeedbackSaveEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
38
40
|
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
39
41
|
type: K,
|
|
40
42
|
listener: (this: UIAgentFeedbackBar, ev: HTMLElementEventMap[K]) => unknown,
|
|
41
43
|
options?: boolean | AddEventListenerOptions,
|
|
42
44
|
): void;
|
|
43
|
-
addEventListener(type: 'feedback-rate', listener: (ev: AgentFeedbackBarFeedbackRateEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
44
|
-
addEventListener(type: 'feedback-save', listener: (ev: AgentFeedbackBarFeedbackSaveEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
45
45
|
}
|
|
@@ -38,10 +38,10 @@ export class UIAgentQuestions extends UIElement {
|
|
|
38
38
|
/** Submit button label (multi mode). */
|
|
39
39
|
submitLabel: string;
|
|
40
40
|
|
|
41
|
+
addEventListener(type: 'questions-answer', listener: (ev: AgentQuestionsQuestionsAnswerEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
41
42
|
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
42
43
|
type: K,
|
|
43
44
|
listener: (this: UIAgentQuestions, ev: HTMLElementEventMap[K]) => unknown,
|
|
44
45
|
options?: boolean | AddEventListenerOptions,
|
|
45
46
|
): void;
|
|
46
|
-
addEventListener(type: 'questions-answer', listener: (ev: AgentQuestionsQuestionsAnswerEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
47
47
|
}
|
|
@@ -43,12 +43,12 @@ export class UIAgentReasoning extends UIElement {
|
|
|
43
43
|
/** Drives the status icon — idle, active (spinner), done (check), error (warning). */
|
|
44
44
|
status: 'idle' | 'active' | 'done' | 'error';
|
|
45
45
|
|
|
46
|
+
addEventListener(type: 'reasoning-finish', listener: (ev: AgentReasoningReasoningFinishEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
47
|
+
addEventListener(type: 'reasoning-step-toggle', listener: (ev: AgentReasoningReasoningStepToggleEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
48
|
+
addEventListener(type: 'reasoning-toggle', listener: (ev: AgentReasoningReasoningToggleEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
46
49
|
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
47
50
|
type: K,
|
|
48
51
|
listener: (this: UIAgentReasoning, ev: HTMLElementEventMap[K]) => unknown,
|
|
49
52
|
options?: boolean | AddEventListenerOptions,
|
|
50
53
|
): void;
|
|
51
|
-
addEventListener(type: 'reasoning-finish', listener: (ev: AgentReasoningReasoningFinishEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
52
|
-
addEventListener(type: 'reasoning-step-toggle', listener: (ev: AgentReasoningReasoningStepToggleEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
53
|
-
addEventListener(type: 'reasoning-toggle', listener: (ev: AgentReasoningReasoningToggleEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
54
54
|
}
|
|
@@ -37,10 +37,10 @@ export class UIAgentSuggestions extends UIElement {
|
|
|
37
37
|
/** outline | ghost | subtle */
|
|
38
38
|
variant: string;
|
|
39
39
|
|
|
40
|
+
addEventListener(type: 'suggestion-select', listener: (ev: AgentSuggestionsSuggestionSelectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
40
41
|
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
41
42
|
type: K,
|
|
42
43
|
listener: (this: UIAgentSuggestions, ev: HTMLElementEventMap[K]) => unknown,
|
|
43
44
|
options?: boolean | AddEventListenerOptions,
|
|
44
45
|
): void;
|
|
45
|
-
addEventListener(type: 'suggestion-select', listener: (ev: AgentSuggestionsSuggestionSelectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
46
46
|
}
|
|
@@ -25,10 +25,10 @@ export class UIAgentTrace extends UIElement {
|
|
|
25
25
|
/** Fallback summary label. */
|
|
26
26
|
label: string;
|
|
27
27
|
|
|
28
|
+
addEventListener(type: 'trace-toggle', listener: (ev: AgentTraceTraceToggleEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
28
29
|
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
29
30
|
type: K,
|
|
30
31
|
listener: (this: UIAgentTrace, ev: HTMLElementEventMap[K]) => unknown,
|
|
31
32
|
options?: boolean | AddEventListenerOptions,
|
|
32
33
|
): void;
|
|
33
|
-
addEventListener(type: 'trace-toggle', listener: (ev: AgentTraceTraceToggleEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
34
34
|
}
|
|
@@ -88,16 +88,14 @@
|
|
|
88
88
|
"default": ""
|
|
89
89
|
},
|
|
90
90
|
"variant": {
|
|
91
|
-
"description": "Semantic color
|
|
91
|
+
"description": "Semantic severity family — the only color axis alert exposes. A quiet/passive banner is the family-less `default` (no attribute): the retired `muted`/`neutral` values were exact aliases of it.",
|
|
92
92
|
"type": "string",
|
|
93
93
|
"enum": [
|
|
94
94
|
"default",
|
|
95
95
|
"info",
|
|
96
96
|
"success",
|
|
97
97
|
"warning",
|
|
98
|
-
"danger"
|
|
99
|
-
"muted",
|
|
100
|
-
"neutral"
|
|
98
|
+
"danger"
|
|
101
99
|
],
|
|
102
100
|
"default": "default"
|
|
103
101
|
}
|
|
@@ -137,7 +135,7 @@
|
|
|
137
135
|
},
|
|
138
136
|
{
|
|
139
137
|
"description": "Card with error alert and a retry button for error state display.",
|
|
140
|
-
"a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"Card\",\n \"children\": [\n \"sec\",\n \"ftr\"\n ]\n },\n {\n \"id\": \"sec\",\n \"component\": \"Section\",\n \"children\": [\n \"alert\"\n ]\n },\n {\n \"id\": \"alert\",\n \"component\": \"Alert\",\n \"variant\": \"
|
|
138
|
+
"a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"Card\",\n \"children\": [\n \"sec\",\n \"ftr\"\n ]\n },\n {\n \"id\": \"sec\",\n \"component\": \"Section\",\n \"children\": [\n \"alert\"\n ]\n },\n {\n \"id\": \"alert\",\n \"component\": \"Alert\",\n \"variant\": \"danger\",\n \"title\": \"Something went wrong\",\n \"description\": \"We encountered an error while loading the data. Please try again.\"\n },\n {\n \"id\": \"ftr\",\n \"component\": \"Footer\",\n \"children\": [\n \"retry\"\n ]\n },\n {\n \"id\": \"retry\",\n \"component\": \"Button\",\n \"text\": \"Retry\",\n \"icon\": \"refresh\",\n \"variant\": \"primary\"\n }\n]",
|
|
141
139
|
"name": "error-state"
|
|
142
140
|
},
|
|
143
141
|
{
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*
|
|
18
18
|
* Inline alert banner with optional icon and close button.
|
|
19
19
|
*
|
|
20
|
-
* Variants: default, info, success, warning, danger
|
|
20
|
+
* Variants: default, info, success, warning, danger
|
|
21
21
|
* Slots: leading (icon), content (text), close (dismiss button)
|
|
22
22
|
*
|
|
23
23
|
* Events:
|
|
@@ -100,14 +100,14 @@ export class UIAlert extends UIElement {
|
|
|
100
100
|
*
|
|
101
101
|
* HALLUCINATION ALIASES (warn-once, encourage canonical form):
|
|
102
102
|
* - variant="error" → variant="danger" (not in the canonical
|
|
103
|
-
* enum [default, info, success, warning, danger
|
|
103
|
+
* enum [default, info, success, warning, danger])
|
|
104
104
|
* - [closeable] → [closable] (alternate spelling, less standard
|
|
105
105
|
* than dismissible/closable; warn to discourage)
|
|
106
106
|
*/
|
|
107
107
|
#normalizeAliases() {
|
|
108
108
|
// variant=error → danger (hallucination; warn)
|
|
109
109
|
if (this.getAttribute('variant') === 'error') {
|
|
110
|
-
_warnOnce('variant-error', 'variant="error" is not in the canonical enum [default, info, success, warning, danger
|
|
110
|
+
_warnOnce('variant-error', 'variant="error" is not in the canonical enum [default, info, success, warning, danger]. Mapping to "danger". Fix the source (LLM prompt / corpus pattern) to emit "danger" directly.');
|
|
111
111
|
this.setAttribute('variant', 'danger');
|
|
112
112
|
}
|
|
113
113
|
|
|
@@ -76,18 +76,11 @@
|
|
|
76
76
|
--alert-icon-fg: var(--md-sys-color-danger-on-danger-container);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
/*
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
:scope[variant="muted"],
|
|
85
|
-
:scope[variant="neutral"] {
|
|
86
|
-
--alert-bg: var(--a-bg-muted);
|
|
87
|
-
--alert-fg: var(--md-sys-color-neutral-on-surface);
|
|
88
|
-
--alert-border: var(--md-sys-color-neutral-outline-variant);
|
|
89
|
-
--alert-icon-fg: var(--a-fg-muted);
|
|
90
|
-
}
|
|
79
|
+
/* NB `variant` is alert's ONLY color axis and it is family-only
|
|
80
|
+
(gh#881, the two-axis convention). The former `muted`/`neutral`
|
|
81
|
+
values re-declared the exact base tokens above — a second name for
|
|
82
|
+
"no variant attribute" — and are retired: a passive callout (beta
|
|
83
|
+
banner, tertiary note) is just a bare <alert-ui>. */
|
|
91
84
|
|
|
92
85
|
/* ── Slots ── */
|
|
93
86
|
:scope [slot="leading"] {
|