@adia-ai/web-components 0.5.13 → 0.5.15
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 +241 -0
- package/components/action-list/action-list.css +1 -1
- package/components/calendar-picker/calendar-picker.css +1 -1
- package/components/chat-thread/chat-input.css +3 -3
- package/components/check/check.css +1 -1
- package/components/heatmap/class.js +4 -0
- package/components/heatmap/heatmap.css +1 -1
- package/components/input/input.css +3 -3
- package/components/list/list.css +6 -6
- package/components/menu/menu.css +2 -2
- package/components/nav-group/nav-group.css +8 -8
- package/components/option-card/option-card.css +2 -1
- package/components/otp-input/class.js +4 -1
- package/components/otp-input/otp-input.a2ui.json +9 -0
- package/components/otp-input/otp-input.d.ts +3 -0
- package/components/otp-input/otp-input.yaml +6 -0
- package/components/page/page.css +1 -1
- package/components/pagination/pagination.css +3 -1
- package/components/radio/radio.css +1 -1
- package/components/search/class.js +2 -0
- package/components/search/search.a2ui.json +9 -0
- package/components/search/search.d.ts +2 -0
- package/components/search/search.yaml +6 -0
- package/components/segment/segment.css +3 -3
- package/components/select/select.css +12 -11
- package/components/stepper/stepper.css +24 -24
- package/components/swatch/class.js +56 -1
- package/components/swatch/swatch.css +2 -2
- package/components/swatch/swatch.test.js +163 -0
- package/components/switch/switch.css +1 -1
- package/components/tabs/tabs.css +1 -1
- package/components/text/text.a2ui.json +16 -2
- package/components/text/text.d.ts +28 -45
- package/components/text/text.yaml +25 -1
- package/components/textarea/textarea.css +6 -2
- package/components/toolbar/toolbar.css +1 -1
- package/components/upload/upload.css +2 -1
- package/package.json +1 -1
- package/styles/colors/semantics.css +7 -84
- package/styles/typography.css +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,247 @@ runtime ships in the sibling `@adia-ai/a2ui-runtime` package
|
|
|
11
11
|
|
|
12
12
|
_No pending changes._
|
|
13
13
|
|
|
14
|
+
## [0.5.15] - 2026-05-16
|
|
15
|
+
|
|
16
|
+
> **Scope note:** §325 + §326 were originally planned as a separate v0.5.16 cycle (see retired `docs/plans/0.5.16-release-plan.md`); absorbed into v0.5.15 since both landed within the same release window (same-day commits prior to bump). The peer's v0.5.16 plan-doc explicitly anticipated this absorption option ("Hermes's call per `/lockstep-release`"). v0.5.16 plan-doc marked SHIPPED-IN-V0.5.15.
|
|
17
|
+
|
|
18
|
+
### v0.5.15 §326 — `<swatch-ui>` `[slot="chrome"]` lost in template contexts (FB-37; P1)
|
|
19
|
+
|
|
20
|
+
Closes FB-37. Pre-§326, `<swatch-ui>` consumed via any `html\`\`` template engine (or any engine that batches child appends separately from element creation) lost its `[slot="chrome"]` children entirely. Root cause: `#stamp()` is gated by a `#stamped` once-only guard + scans `this.children` for chrome slots at `connectedCallback` time. Template engines create the host via `createElement` + connect it FIRST (children empty), then batch-append children AFTER. `#stamp()`'s chrome scan returns `[]`; the `#stamped` guard prevents re-runs; chrome children landed at the END of host's children (after `data-copy`) instead of the canonical sibling-of-tile position. Blocked Tokens Studio's C1.3 dogfood migration (9 failing Playwright tests root-caused to this) — and >95% of AdiaUI consumers using `html\`\`` templates.
|
|
21
|
+
|
|
22
|
+
#### Fixed — `components/swatch/class.js`
|
|
23
|
+
|
|
24
|
+
- NEW `#absorbChromeSlot()` helper. Idempotent. Walks `this.children`; for any child with `slot="chrome"` that's not one of the internal stamped elements + not in the canonical position, calls `this.insertBefore(child, this.#badgeEl)` to move it to the slot between `#tileEl` + `#badgeEl`.
|
|
25
|
+
- `#syncCore()` calls `#absorbChromeSlot()` at the top of every render pass. Late-arriving chrome children (template-render path) get picked up on the next reactive cycle; pre-positioned chrome children (static-HTML path) are no-op-moves.
|
|
26
|
+
- `#stamp()`'s once-only eager scan stays intact for the static-HTML path; `#absorbChromeSlot()` is the safety net for late-append.
|
|
27
|
+
|
|
28
|
+
#### Added — `components/swatch/swatch.test.js`
|
|
29
|
+
|
|
30
|
+
- 2 NEW vitest cases under "FB-37 / §326":
|
|
31
|
+
- `absorbs chrome child appended AFTER connectedCallback (template-render path)` — mimics the bug's exact timing (create + connect with empty children → late append → property mutation triggers render → fix picks up the chrome child).
|
|
32
|
+
- `still handles chrome child appended BEFORE connect (static-HTML path)` — regression check that §314's original behavior still works.
|
|
33
|
+
- Total suite: 6 cases (4 from §325 + 2 from §326).
|
|
34
|
+
|
|
35
|
+
#### Migration
|
|
36
|
+
|
|
37
|
+
Consumers using `<swatch-ui>` via `html\`\`` template engines (lit-html, AdiaUI's own `html\`\``, Preact, Vue, etc.) get the §314 chrome-slot feature working as documented in v0.5.13's `swatch.yaml slots:` block. No consumer-side changes required — the §326 fix is invisible to consumers writing canonical templates.
|
|
38
|
+
|
|
39
|
+
### v0.5.15 §325 — `<swatch-ui>` default-slot wiped by label-attr (FB-36)
|
|
40
|
+
|
|
41
|
+
Closes FB-36 + FB-36-followup. Pre-§325, `<swatch-ui label="500"><span>tip</span></swatch-ui>` written as indented multi-line HTML dropped the default-slot child entirely; only `label="500"` rendered. Root cause: `#stamp()`'s `Array.from(this.childNodes).filter(...)` captured pure-whitespace text nodes from the indentation; those landed in `#labelEl.firstChild`; `#syncCore()`'s "stale text-node" branch (designed for runtime `el.label = 'X'` mutations on attr-only swatches) then matched the whitespace + did `textContent = this.label`, wiping both the whitespace AND the consumer's `<span>`.
|
|
42
|
+
|
|
43
|
+
Pre-§325 the yaml spec ("Use the default slot for richer content" — `swatch.yaml:40`) and the implementation contradicted each other; post-§325 they agree.
|
|
44
|
+
|
|
45
|
+
#### Fixed — `components/swatch/class.js`
|
|
46
|
+
|
|
47
|
+
- `#stamp()`'s `slotted` filter chain gains `&& !(n.nodeType === 3 && !n.textContent.trim())` — strips pure-whitespace text nodes from the capture. `#labelEl.firstChild` now becomes the consumer's element (or non-whitespace text), so `#syncCore`'s stale-text-node branch fires only for genuinely attr-driven cases.
|
|
48
|
+
|
|
49
|
+
#### Added — `components/swatch/swatch.test.js`
|
|
50
|
+
|
|
51
|
+
- NEW 4-case vitest suite covering FB-36's 3 input axes (default-slot present/absent × label-attr present/absent × runtime mutation). Locks the §325 fix structurally — future regressions of any of the 4 paths fail CI.
|
|
52
|
+
|
|
53
|
+
#### Migration
|
|
54
|
+
|
|
55
|
+
`<swatch-ui label="500"><span>tip</span></swatch-ui>` now behaves per yaml spec: default-slot wins, label-attr drops. Consumers using the workaround `.label=` property binding (per FB-36-followup) to bypass the bug can switch back to the canonical `[label]` attribute form post-upgrade.
|
|
56
|
+
|
|
57
|
+
### v0.5.15 §324 — Yaml `enum_descriptions:` codegen extension (v0.6.0 FB-23 §3 retire carry-forward, PATCH-eligible)
|
|
58
|
+
|
|
59
|
+
Closes the v0.6.0 carry-forward "Codegen yaml `enum:` per-member descriptions (FB-23 §3 retire)" from the RESPONSE-29 carry-forwards table. v0.5.15 ships the additive codegen surface on PATCH cadence; v0.6.0 no longer needs to carry this item.
|
|
60
|
+
|
|
61
|
+
**The surface change:** `scripts/schemas/component.yaml.schema.json` `Prop` definition gains an optional `enum_descriptions:` object field — when present alongside `enum:`, dts-codegen emits an exported type alias (e.g. `UITextVariant`) above the class declaration with per-variant JSDoc carrying each description. IDE hover surfaces the disambiguation at authoring time without requiring hand-authored .d.ts.
|
|
62
|
+
|
|
63
|
+
#### Added
|
|
64
|
+
|
|
65
|
+
- `scripts/schemas/component.yaml.schema.json` — NEW optional `enum_descriptions: { type: object, additionalProperties: { type: string } }` field per prop. Backward-compatible: existing yamls without the field continue to emit inline enum unions.
|
|
66
|
+
- `scripts/build/components.mjs` — forwards `enum_descriptions:` through `compileProp()` into the a2ui.json sidecar.
|
|
67
|
+
- `scripts/build/dts-codegen.mjs` — `emitEnumTypeAlias()` helper detects props with both `enum:` and `enum_descriptions:`; emits a typed union alias `${className}${PascalCase(propName)}` with per-variant JSDoc; class property references the alias instead of inlining the union.
|
|
68
|
+
|
|
69
|
+
#### Changed — `components/text/text.yaml`
|
|
70
|
+
|
|
71
|
+
- `variant:` prop gains 12-entry `enum_descriptions:` block (one description per variant). Prop `description:` block also gains the FB-23 §2 ARIA-role disclaimer that previously lived in the hand-authored .d.ts.
|
|
72
|
+
|
|
73
|
+
#### Changed — `scripts/build/dts-codegen.mjs` `HAND_AUTHORED_DTS`
|
|
74
|
+
|
|
75
|
+
- `text` removed from skip-list. `text.d.ts` now codegen-driven; per-variant JSDoc surfaces via the new `enum_descriptions:` field. Net effect: 1 of 3 remaining `HAND_AUTHORED_DTS` entries closed (`toast` + `feed` remain — both carry runtime API the yaml shape can't express).
|
|
76
|
+
|
|
77
|
+
#### Regenerated — `components/text/text.d.ts`
|
|
78
|
+
|
|
79
|
+
- Replaces hand-authored .d.ts with codegen output. `UITextVariant` type alias preserved as exported type; per-variant JSDoc preserved verbatim. Net result: zero consumer-facing diff in the .d.ts surface; codegen takes over maintenance.
|
|
80
|
+
|
|
81
|
+
### v0.5.15 §322 — NEW audit slot 32: `check-dead-constants.mjs`
|
|
82
|
+
|
|
83
|
+
Graduates the FB-35 bug class (declared-but-unread module-level consts) to a CI gate. Walks every `.js` file under `packages/<pkg>/`; extracts SCREAMING_SNAKE_CASE / PascalCase `const NAME = ...;` declarations at module level; flags zero-reference cases. Skips `export const NAME` (consumed externally) + test/spec/examples/config files.
|
|
84
|
+
|
|
85
|
+
First sweep surfaced + removed 2 real dead constants:
|
|
86
|
+
- `packages/a2ui/validator/validator.js:38` — `WIRING_WEIGHTS` (vestigial from planned weighted-scoring system; safe deletion).
|
|
87
|
+
- `packages/llm/adapters/openai.js:10` — `DEFAULT_MAX_TOKENS = 4096` (replaced by explicit `opts.max_tokens` plumbing).
|
|
88
|
+
|
|
89
|
+
### v0.5.15 §323 — NEW audit slot 33: `check-hover-guard-discipline.mjs`
|
|
90
|
+
|
|
91
|
+
Graduates the `analyze-css` bare-`:hover` discipline class to a CI gate. Detects `:hover` rules on host primitives + interactive slots/roles that lack a `:not([disabled])` guard AND set visual properties (background/color/border/box-shadow/etc.). Skips cursor-only / outline-style-only rule bodies.
|
|
92
|
+
|
|
93
|
+
First sweep surfaced 19 findings across 11 components — all baselined in EXPECTED_DRIFT for cycle-by-cycle close:
|
|
94
|
+
- **Real drift** (rely on `pointer-events: none` for click-suppression but hover styles still apply): action-list (4×), menu (1×), nav-item (2×), nav-group (1×).
|
|
95
|
+
- **Slotted-trigger drift** (mitigated via parent guard rules; cosmetic-OK but worth tightening): select (5×), calendar-picker (1×), range (2×), tag (1×).
|
|
96
|
+
- **Decorative-pop hover** (intentional): avatar (1×). Long-term whitelist candidate.
|
|
97
|
+
- **@scope-Safari-17.x-workaround**: action-list (3 of 4) + nav-item (2) + rating (1) — guard can't live inside @scope; lift alongside the hover rule.
|
|
98
|
+
|
|
99
|
+
#### Removed — dead module-level constants
|
|
100
|
+
|
|
101
|
+
- `packages/a2ui/validator/validator.js` — `WIRING_WEIGHTS` constant (zero refs).
|
|
102
|
+
- `packages/llm/adapters/openai.js` — `DEFAULT_MAX_TOKENS` constant (zero refs).
|
|
103
|
+
|
|
104
|
+
### v0.5.15 §319 — NEW audit slot 29: `check-exports-map-resolves.mjs`
|
|
105
|
+
|
|
106
|
+
Graduates the FB-32 bug class to a CI gate. Closes the recurring drift where a package.json `exports` map entry resolves (via wildcard expansion or static path) to a file that doesn't exist on disk — consumer TS imports fail with `TS2882` even though Vite/Node can fall through `node_modules/<pkg>/<path>` directly.
|
|
107
|
+
|
|
108
|
+
#### Added — `scripts/release/check-exports-map-resolves.mjs`
|
|
109
|
+
|
|
110
|
+
- ~270 LOC. Walks every `packages/<pkg>/package.json` `exports` map. For each STATIC entry, verifies the referenced file path exists. For each WILDCARD entry matching the FB-32 shape (`./X/*` key + `./X/*/*.<ext>` value), enumerates filesystem candidates that would match the wildcard + verifies each resolved path exists.
|
|
111
|
+
- Skips wildcard expansions where an explicit non-wildcard entry overrides the resolution (Node exports-map resolution algorithm precedence).
|
|
112
|
+
- Skips CSS-only / no-JS components (header / footer / section / aside / various web-modules shells) — these intentionally ship no `.js`.
|
|
113
|
+
- Result: 9 packages / 52 static + 39 wildcard exports entries / 59 resolutions verified / 0 unresolved.
|
|
114
|
+
|
|
115
|
+
### v0.5.15 §320 — NEW audit slot 30: `check-composes-passthrough.mjs`
|
|
116
|
+
|
|
117
|
+
Graduates the FB-33 bug class to a CI gate. Closes the recurring drift where a form-bearing primitive that programmatically creates ANOTHER form-bearing primitive in its lifecycle (light-DOM composition) doesn't expose / forward the composed primitive's documented prop + event-detail surface.
|
|
118
|
+
|
|
119
|
+
#### Added — `scripts/release/check-composes-passthrough.mjs`
|
|
120
|
+
|
|
121
|
+
- ~270 LOC. Static `COMPOSITION_PAIRS` list declares the required pass-through surface per pair. For each pair: verifies composing primitive's yaml `composes:` block lists the composed primitive; verifies yaml `props:` block exposes every required prop; verifies yaml `events:` block declares each event with required detail keys.
|
|
122
|
+
- Initial pair: `color-input → color-picker` (8 props + 12 event-detail keys across `change` + `input`).
|
|
123
|
+
- Adding a new pair: any new primitive that programmatically wraps a form-bearing primitive declares its contract in `COMPOSITION_PAIRS`; the audit fires at PR time.
|
|
124
|
+
- Companion to slot 23 (`check-form-bearing-dts.mjs`): that audit checks .d.ts type-coverage; this audit checks yaml-declared shape.
|
|
125
|
+
|
|
126
|
+
### v0.5.15 §321 — NEW audit slot 31: `check-token-name-canonical-shape.mjs`
|
|
127
|
+
|
|
128
|
+
Graduates the stepper / list state-first naming drift class to a CI gate. Closes the recurring drift where a component-local CSS token uses state-first (`--stepper-active-bg`) or color-infix-state (`--link-color-hover`) naming instead of the canonical property-first state-suffix shape (`--<comp>(-<sub>)*-<prop>(-<state>)?`).
|
|
129
|
+
|
|
130
|
+
#### Added — `scripts/release/check-token-name-canonical-shape.mjs`
|
|
131
|
+
|
|
132
|
+
- ~210 LOC. Extracts `--<comp>-*` declarations from each component CSS file. Classifies each by segment shape: canonical-prop / canonical-prop-state / canonical-leaf / canonical-domain / drift-state-first / drift-infix-color-state.
|
|
133
|
+
- Vocabularies (PROP_TOKENS + STATE_TOKENS) capture the catalog's recognized property + state terms (bg/fg/border/ring/shadow/color/outline/size + hover/active/selected/disabled/focus/checked/done/...).
|
|
134
|
+
- Result: 92 components / 1809 component-local tokens audited / 0 drift findings.
|
|
135
|
+
- `link.css` `--link-color-hover` not flagged at v0.5.15 ship: `color` is a recognized PROP_TOKEN + `hover` is a recognized STATE_TOKEN — the shape matches canonical. A future, stricter audit (alternative-property-naming) could flag the `color` vs `fg` divergence; deferred to v0.6.0 BREAKING window since link.css's `--link-color-*` are documented public API.
|
|
136
|
+
|
|
137
|
+
### v0.5.15 §316 — Naming-convention drift fix (stepper + list state-first → property-first)
|
|
138
|
+
|
|
139
|
+
`<stepper-ui>` and `<list-ui>` declared internal tokens with state-first naming (`--stepper-active-bg`, `--list-selected-fg`, etc.) — the catalog convention is property-first (`--<comp>-{prop}-{state}`). 18 token names renamed; usage sites updated in-place. `<link-ui>` skipped — its `--link-color-*` tokens are documented public API per `link.yaml tokens:` block; renaming is BREAKING and defers to v0.6.0.
|
|
140
|
+
|
|
141
|
+
#### Changed — `components/stepper/`, `components/list/`
|
|
142
|
+
|
|
143
|
+
- `stepper.css` — 12 tokens renamed: `--stepper-{active,done}-{bg,border,fg}` + `--stepper-item-{active,done}-{bg,border,fg}` → `--stepper-{bg,border,fg}-{active,done}` + `--stepper-item-{bg,border,fg}-{active,done}`.
|
|
144
|
+
- `list.css` — 3 tokens renamed: `--list-selected-{bg,fg,border}` → `--list-{bg,fg,border}-selected`.
|
|
145
|
+
|
|
146
|
+
### v0.5.15 §316 — Hand-rolled transition-duration literals → token references (swatch + heatmap + page)
|
|
147
|
+
|
|
148
|
+
Three components hand-rolled `100ms ease-out` / `120ms ease-out` / `0.1s ease-out` / `150ms ease` instead of routing through the canonical `var(--a-duration-fast)` + `var(--a-easing*)` token chain. Theme designers couldn't swap transition cadence centrally.
|
|
149
|
+
|
|
150
|
+
#### Changed — `components/swatch/`, `components/heatmap/`, `components/page/`
|
|
151
|
+
|
|
152
|
+
- `swatch.css:294,311` — `100ms ease-out` + `120ms ease-out` → `var(--a-duration-fast) var(--a-easing-out)`.
|
|
153
|
+
- `heatmap.css:80` — `0.1s ease-out` → `var(--a-duration-fast) var(--a-easing-out)`.
|
|
154
|
+
- `page.css:61` — `150ms ease` → `var(--a-duration-fast) var(--a-easing)`.
|
|
155
|
+
|
|
156
|
+
### v0.5.15 §317 — NEW audit slot 27: `check-raw-fg-in-component-css.mjs`
|
|
157
|
+
|
|
158
|
+
Graduates the §313/§315/§316 bug class to a CI gate. Closes the recurring drift class where component CSS rules bypass their own `--<comp>-<family>-<state>` semantic-layer token and reference the raw global `--a-<family>-<state>` token directly. The audit slot found 5 additional bypass instances across the codebase (nav-group + select + toolbar) — all routed through the component-local semantic layer in this commit.
|
|
159
|
+
|
|
160
|
+
#### Added — `scripts/release/check-raw-fg-in-component-css.mjs`
|
|
161
|
+
|
|
162
|
+
- ~190 LOC audit script. Parses each `<comp>.css` for the `:where(:scope) { ... }` declaration block + the set of `--<comp>-*` tokens defined therein. Scans the remainder for `<standard-css-property>: var(--a-<family>-<state>?)` usage where the parallel `--<comp>-<family>-<state>?` is defined — emits a finding per bypass.
|
|
163
|
+
- Whitelisted CSS properties: `color`, `background`, `background-color`, `border` (+ all sub-axis variants), `outline`, `fill`, `stroke`, `caret-color`, `accent-color`, `text-decoration-color`, `column-rule-color`.
|
|
164
|
+
- Distinguishes declaration sites (`--<comp>-X: var(--a-X)`) from usage sites mechanically — declaration sites are NOT flagged (those are correct semantic-layer aliasing).
|
|
165
|
+
- Companion to slot 25 (`check-component-css-vs-styles-tokens.mjs`): that audit catches orphan references (`var(--a-X)` where `--a-X` doesn't exist); this audit catches the inverse (`var(--a-X)` where it exists but `--<comp>-X` should be preferred).
|
|
166
|
+
- EXPECTED_FINDINGS baseline: 0 entries at v0.5.15 ship (sweep landed every concrete bypass).
|
|
167
|
+
|
|
168
|
+
#### Fixed — 5 component CSS bypass sites
|
|
169
|
+
|
|
170
|
+
- `nav-group.css:243,252` — `background: var(--a-bg-hover)` → `var(--nav-group-bg-hover)`.
|
|
171
|
+
- `select.css:199` — listbox container `color: var(--a-fg)` → `var(--select-fg)`.
|
|
172
|
+
- `select.css:319` — hint slot fallback `var(--a-fg-muted)` → `var(--select-fg-muted)`.
|
|
173
|
+
- `toolbar.css:107` — overflow popover `color: var(--a-fg)` → `var(--toolbar-fg)`.
|
|
174
|
+
|
|
175
|
+
### v0.5.15 §317 — NEW audit slot 28: `check-form-bearing-events-symmetric.mjs`
|
|
176
|
+
|
|
177
|
+
Graduates the FB-31/§315 bug class to a CI gate. Closes the recurring drift class where UIFormElement-extending primitives emit only `input` (or only `complete`/`search`/`pick`) without the canonical `change` event with `detail.value`. The audit confirms all 17 form-bearing primitives are now symmetric post-v0.5.14 §315.
|
|
178
|
+
|
|
179
|
+
#### Added — `scripts/release/check-form-bearing-events-symmetric.mjs`
|
|
180
|
+
|
|
181
|
+
- ~145 LOC audit script. Walks every `class.js` for classes that `extends UIFormElement`. For each, verifies a `dispatchEvent(new CustomEvent('change', { detail: { value: ... } }))` occurs (statically or via dynamic-name dispatch + nearby `'change'` string literal + `detail = { value: ... }` construction).
|
|
182
|
+
- Heuristic limitations documented: false negatives possible if event name is computed via complex variable assignment; manual review at addition time required.
|
|
183
|
+
- ALLOW_LIST mechanism for primitives where the commit model legitimately omits `change` (none at v0.5.15 ship).
|
|
184
|
+
- Companion to slot 23 (`check-form-bearing-dts.mjs`): that audit verifies typed `<Comp>ChangeEvent` exists in `.d.ts`; this audit verifies the runtime dispatch.
|
|
185
|
+
|
|
186
|
+
### v0.5.15 §318 — ADR-0028: L3 state-matrix orphan retirement (78 unused tokens deleted)
|
|
187
|
+
|
|
188
|
+
ADR-0028 ratifies the deletion of 78 orphaned L3 state-matrix tokens from `semantics.css`. The `maintain-tokens` skill audit during v0.5.14 §315 surfaced that 73 of ~80 L3 `--a-{family}-{prop}-{state}` tokens have zero consumers across the entire codebase. The per-primitive `--<comp>-*` token pattern (validated across v0.5.13/14/15 sweeps) is the actual consumer-facing API.
|
|
189
|
+
|
|
190
|
+
#### Removed — `styles/colors/semantics.css`
|
|
191
|
+
|
|
192
|
+
- 78 `--a-{accent,primary,brand,info,success,warning,danger,fg,border}-{fg,bg,border}-{active,selected,disabled,invalid}` declarations deleted. ~80 LOC reduction.
|
|
193
|
+
- Token count dropped 1301 → 1217 (-6.5%).
|
|
194
|
+
- Kept (have consumers): `--a-bg-{active,selected,disabled,invalid}`, `--a-fg-{selected,disabled}`, `--a-accent-bg-active`, `--a-primary-bg-active` (transitive via accent-bg-active).
|
|
195
|
+
- Kept (consumer-facing hover state): all `--a-*-hover` variants — hover is the only state where the generic-L3 layer is load-bearing.
|
|
196
|
+
|
|
197
|
+
#### Added — `.brain/adrs/0028-l3-state-matrix-orphan-retirement.md`
|
|
198
|
+
|
|
199
|
+
- ADR documents the audit-driven discovery, the retention rationale for the 7 used tokens, the deletion rationale for the 78 orphans, alternatives considered (keep all / delete only family-specific / add audit), and the per-primitive convention as the canonical consumer-facing override surface.
|
|
200
|
+
|
|
201
|
+
## [0.5.14] - 2026-05-15
|
|
202
|
+
|
|
203
|
+
### v0.5.14 — Form-control hover-fg token unification (precursor to §315; commit `178418525`)
|
|
204
|
+
|
|
205
|
+
Form-control primitives diverged on hover-text token routing. `button-ui` correctly used `--a-ui-text-hover` (resolves to `--a-fg-strong` — canonical hover-lift for form-control text); every other form-control primitive used the generic `--a-fg` (no actual lift from default `--a-canvas-text`) — so "hover" had zero text-color effect on `input`/`textarea`/`segment`, and `select-ui`'s listbox/caret hover paths bypassed the `--select-*` token layer entirely with raw `--a-fg-*` references.
|
|
206
|
+
|
|
207
|
+
#### Fixed — `components/{input,textarea,segment,select}/`
|
|
208
|
+
|
|
209
|
+
- `input-ui`: `--input-fg-hover: var(--a-fg)` → `var(--a-ui-text-hover)`; `--input-affix-fg-hover: var(--a-fg-subtle)` → `var(--a-ui-text-subtle)`.
|
|
210
|
+
- `textarea-ui`: matching routing on hover/affix-hover.
|
|
211
|
+
- `segment-ui`: same routing change.
|
|
212
|
+
- `select-ui`: listbox + caret + option-hover tokens previously raw `--a-fg-*` references now route through their `--select-*` semantic layer (which itself anchors at `--a-ui-text-*` family).
|
|
213
|
+
|
|
214
|
+
Net effect: hover-text colors now consistent across all form-control primitives + theme designers can swap them all by changing one semantic token (`--a-ui-text-hover`) at the `--a-ui-*` family root in `semantics.css`.
|
|
215
|
+
|
|
216
|
+
### v0.5.14 §315 — Drift-class sweep follow-up (`ui-audit-coherence` + `maintain-tokens` + `analyze-css` triple-pass)
|
|
217
|
+
|
|
218
|
+
Three parallel skill-driven audits surfaced 7 mechanical drift-classes; landed as a single sweep in one commit (`025a981db`). Follow-up to the form-control hover-fg unification at `178418525` — same bug shape (component CSS bypassing its own semantic-layer routing through raw global tokens), broader scope.
|
|
219
|
+
|
|
220
|
+
#### Fixed — `components/select/`, `components/nav-group/`, `components/chat-thread/chat-input.css`
|
|
221
|
+
|
|
222
|
+
- `select.css:254,286,293,300` — 4 raw `var(--a-fg-*)` callsites routed through `--select-fg-{muted,fg,muted,muted}` (semantic layer already existed but was bypassed).
|
|
223
|
+
- `nav-group.css:225,234,244,253,286,298` — 6 raw `var(--a-fg-{muted,subtle,strong})` callsites routed through `--nav-group-fg-{muted,muted,hover,selected,muted,muted}`.
|
|
224
|
+
- `chat-input.css:40,50,58` — placeholder/hover/disabled tokens routed through the canonical `--a-ui-text-{placeholder,hover,disabled}` field-family chain instead of the generic `--a-fg-*` family. Continues the v0.5.13 hover-fg unification arc.
|
|
225
|
+
|
|
226
|
+
#### Fixed — `components/{check,radio,switch,tabs,action-list,calendar-picker,menu}/`
|
|
227
|
+
|
|
228
|
+
- 6 form-adjacent primitives' `--{comp}-fg-disabled` token routed through canonical `--a-ui-text-disabled` instead of `--a-fg-muted` (the disabled-text version of the v0.5.13 hover-fg drift class).
|
|
229
|
+
- `menu.css:96,99` also fixed `--menu-item-fg-disabled` + `--menu-item-icon-disabled` (the third variant `--a-fg-disabled` shape; same canonical target).
|
|
230
|
+
|
|
231
|
+
#### Added — `components/otp-input/`, `components/search/`
|
|
232
|
+
|
|
233
|
+
- `<otp-input-ui>` + `<search-ui>` now dispatch `change` alongside `input` on every value-altering event. Both primitives previously emitted only `input` (the sole exceptions across all UIFormElement-bearing primitives). Closes consumer event-binding gap — `@change=${handler}` now works against every form-bearing tag uniformly.
|
|
234
|
+
- `.d.ts` files gain `OtpInputChangeEvent` + `SearchChangeEvent` type exports (reuse existing detail interfaces; matches v0.5.10 `check-form-bearing-dts` audit convention).
|
|
235
|
+
- yaml `events:` blocks declare the new `change` event with `detail.value`.
|
|
236
|
+
|
|
237
|
+
#### Fixed — `styles/typography.css`
|
|
238
|
+
|
|
239
|
+
- `:where(mark)` background reference fixed: `var(--a-highlight-default)` → `var(--a-highlight)` (`--a-highlight-default` token does not exist; pre-fix `<mark>` silently fell back to UA-default highlight). Single-line typo discovered via `maintain-tokens` skill sweep.
|
|
240
|
+
|
|
241
|
+
#### Fixed — `components/{textarea,upload,option-card,pagination}/` transition consistency
|
|
242
|
+
|
|
243
|
+
- `textarea.css:76` transition list expanded from `border-color` only → `background + border-color + color + box-shadow` (matches `input-ui`'s 4-property parity; pre-fix hover/focus slammed in instead of easing).
|
|
244
|
+
- `upload.css:64-67` + `option-card.css:102-104` + `pagination.css:71-73` each gain `box-shadow` (and `border-color` for pagination) so focus-ring eases in alongside the other state changes. Cross-primitive hover/focus polish parity restored.
|
|
245
|
+
|
|
246
|
+
#### Fixed — `components/input/`, `components/select/` `-active` token routing
|
|
247
|
+
|
|
248
|
+
- `--input-fg-focus: var(--a-fg-strong)` → `var(--a-ui-text-active)` (closes orphan: `--a-ui-text-active` semantic-layer alias existed at `semantics.css:552` with zero consumers).
|
|
249
|
+
- `--select-option-fg-active: var(--a-fg)` → `var(--a-ui-text-active)` (same routing; cross-primitive form-control consistency).
|
|
250
|
+
|
|
251
|
+
#### Fixed — `components/heatmap/`
|
|
252
|
+
|
|
253
|
+
- `class.js:disconnected()` now cancels `#raf` via `cancelAnimationFrame()`. Pre-fix the `requestAnimationFrame` handle was stored but never cleaned up on detach — minor memory leak in scroll-heavy demos. ~3 LOC.
|
|
254
|
+
|
|
14
255
|
## [0.5.13] - 2026-05-15
|
|
15
256
|
|
|
16
257
|
### v0.5.13 §310 — `apcaContrast` soft-clamp constant fix (FEEDBACK-35; P2 correctness)
|
|
@@ -39,7 +39,7 @@ action-item-ui:hover [slot="icon"] {
|
|
|
39
39
|
--action-item-fg: var(--a-fg-subtle);
|
|
40
40
|
--action-item-fg-hover: var(--a-fg-strong);
|
|
41
41
|
--action-item-bg-hover: var(--a-bg-muted);
|
|
42
|
-
--action-item-fg-disabled: var(--a-
|
|
42
|
+
--action-item-fg-disabled: var(--a-ui-text-disabled);
|
|
43
43
|
--action-item-icon-fg: var(--a-fg-muted);
|
|
44
44
|
--action-item-icon-fg-hover: var(--a-fg);
|
|
45
45
|
--action-item-danger-fg: var(--a-danger-bg);
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
--calendar-picker-day-bg-selected: var(--a-accent);
|
|
69
69
|
--calendar-picker-day-fg-selected: var(--a-accent-fg);
|
|
70
70
|
--calendar-picker-day-fg-outside: var(--a-fg-muted);
|
|
71
|
-
--calendar-picker-day-fg-disabled: var(--a-
|
|
71
|
+
--calendar-picker-day-fg-disabled: var(--a-ui-text-disabled);
|
|
72
72
|
--calendar-picker-day-today-border: var(--a-accent);
|
|
73
73
|
--calendar-picker-day-today-dot-size: var(--a-space-0-5);
|
|
74
74
|
--calendar-picker-day-today-dot-offset: var(--a-space-1);
|
|
@@ -37,7 +37,7 @@ chat-input-ui textarea-ui:not([disabled]) [slot="text"]:hover {
|
|
|
37
37
|
--chat-input-fg: var(--a-fg);
|
|
38
38
|
--chat-input-border: var(--a-border-subtle);
|
|
39
39
|
--chat-input-caret-color: var(--a-fg-subtle);
|
|
40
|
-
--chat-input-placeholder-fg: var(--a-
|
|
40
|
+
--chat-input-placeholder-fg: var(--a-ui-text-placeholder);
|
|
41
41
|
|
|
42
42
|
/* Hover — matches input-ui's affordance (subtle alpha lift on the
|
|
43
43
|
canvas surface + slightly stronger border + brightened fg). The
|
|
@@ -47,7 +47,7 @@ chat-input-ui textarea-ui:not([disabled]) [slot="text"]:hover {
|
|
|
47
47
|
doesn't compound. */
|
|
48
48
|
--chat-input-bg-hover: var(--a-ui-bg-hover);
|
|
49
49
|
--chat-input-border-hover: var(--a-ui-border-hover);
|
|
50
|
-
--chat-input-fg-hover: var(--a-
|
|
50
|
+
--chat-input-fg-hover: var(--a-ui-text-hover);
|
|
51
51
|
|
|
52
52
|
/* Disabled — host-level chrome (background, border, pointer-events)
|
|
53
53
|
lives on `:scope[disabled]` below. Inner-text disabled color is
|
|
@@ -55,7 +55,7 @@ chat-input-ui textarea-ui:not([disabled]) [slot="text"]:hover {
|
|
|
55
55
|
leak through. */
|
|
56
56
|
--chat-input-bg-disabled: transparent;
|
|
57
57
|
--chat-input-border-disabled: var(--a-border-subtle);
|
|
58
|
-
--chat-input-fg-disabled: var(--a-
|
|
58
|
+
--chat-input-fg-disabled: var(--a-ui-text-disabled);
|
|
59
59
|
|
|
60
60
|
/* Canonical focus ring — chat-input is a *nested-control host*.
|
|
61
61
|
See semantics.css FOCUS block + the nested-control pattern
|
|
@@ -60,7 +60,7 @@ check-ui[indeterminate] {
|
|
|
60
60
|
--check-bg-checked-hover: var(--a-primary-hover);
|
|
61
61
|
|
|
62
62
|
/* ── State: disabled ── */
|
|
63
|
-
--check-fg-disabled: var(--a-
|
|
63
|
+
--check-fg-disabled: var(--a-ui-text-disabled);
|
|
64
64
|
|
|
65
65
|
/* ── Check icon (CSS border trick, no SVG) ── */
|
|
66
66
|
--_icon-w: 0;
|
|
@@ -86,6 +86,10 @@ export class UIHeatmap extends UIElement {
|
|
|
86
86
|
this.removeEventListener('pointerleave', this.#onLeave);
|
|
87
87
|
this.removeEventListener('click', this.#onClick);
|
|
88
88
|
this.#bound = false;
|
|
89
|
+
if (this.#raf != null) {
|
|
90
|
+
cancelAnimationFrame(this.#raf);
|
|
91
|
+
this.#raf = null;
|
|
92
|
+
}
|
|
89
93
|
}
|
|
90
94
|
|
|
91
95
|
#raf = null;
|
|
@@ -37,9 +37,9 @@ input-ui:not([disabled]) [slot="field"]:hover [slot="suffix"] {
|
|
|
37
37
|
|
|
38
38
|
/* ── State: hover/focus ── */
|
|
39
39
|
--input-bg-hover: var(--a-ui-bg-hover);
|
|
40
|
-
--input-fg-hover: var(--a-
|
|
41
|
-
--input-affix-fg-hover: var(--a-
|
|
42
|
-
--input-fg-focus: var(--a-
|
|
40
|
+
--input-fg-hover: var(--a-ui-text-hover);
|
|
41
|
+
--input-affix-fg-hover: var(--a-ui-text-subtle);
|
|
42
|
+
--input-fg-focus: var(--a-ui-text-active);
|
|
43
43
|
|
|
44
44
|
/* ── State: disabled ── */
|
|
45
45
|
--input-bg-disabled: var(--a-ui-bg-disabled);
|
package/components/list/list.css
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
:where(:scope) {
|
|
3
3
|
/* ── Colors ── */
|
|
4
4
|
--list-divider-color: var(--a-border-subtle);
|
|
5
|
-
--list-selected
|
|
6
|
-
--list-selected
|
|
7
|
-
--list-selected
|
|
5
|
+
--list-bg-selected: var(--a-accent-muted);
|
|
6
|
+
--list-fg-selected: var(--a-accent-strong);
|
|
7
|
+
--list-border-selected: var(--a-accent-strong);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
:scope {
|
|
@@ -68,9 +68,9 @@
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
:scope[selectable] > [role="listitem"][aria-selected="true"] {
|
|
71
|
-
background: var(--list-selected
|
|
72
|
-
color: var(--list-selected
|
|
73
|
-
border-inline-start-color: var(--list-selected
|
|
71
|
+
background: var(--list-bg-selected);
|
|
72
|
+
color: var(--list-fg-selected);
|
|
73
|
+
border-inline-start-color: var(--list-border-selected);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
:scope[selectable] > [role="listitem"]:focus-visible {
|
package/components/menu/menu.css
CHANGED
|
@@ -93,10 +93,10 @@ menu-item-ui[variant="danger"]:hover [slot="icon"] {
|
|
|
93
93
|
--menu-item-fg: var(--a-fg-subtle);
|
|
94
94
|
--menu-item-fg-hover: var(--a-fg);
|
|
95
95
|
--menu-item-bg-hover: var(--a-bg-muted);
|
|
96
|
-
--menu-item-fg-disabled: var(--a-
|
|
96
|
+
--menu-item-fg-disabled: var(--a-ui-text-disabled);
|
|
97
97
|
--menu-item-icon-fg: var(--a-fg-muted);
|
|
98
98
|
--menu-item-icon-fg-hover: var(--a-fg);
|
|
99
|
-
--menu-item-icon-disabled: var(--a-
|
|
99
|
+
--menu-item-icon-disabled: var(--a-ui-text-disabled);
|
|
100
100
|
--menu-item-danger-fg: var(--a-danger-bg);
|
|
101
101
|
--menu-item-danger-bg: var(--a-danger-muted);
|
|
102
102
|
|
|
@@ -222,7 +222,7 @@ nav-group-ui [slot="popover"] {
|
|
|
222
222
|
nav-group-ui [slot="popover-label"] {
|
|
223
223
|
padding: var(--a-space-1) var(--a-space-2);
|
|
224
224
|
font-weight: var(--a-weight-medium);
|
|
225
|
-
color: var(--
|
|
225
|
+
color: var(--nav-group-fg-muted);
|
|
226
226
|
font-size: var(--a-ui-tiny);
|
|
227
227
|
text-transform: uppercase;
|
|
228
228
|
letter-spacing: 0.06em;
|
|
@@ -231,7 +231,7 @@ nav-group-ui [slot="popover-label"] {
|
|
|
231
231
|
nav-group-ui [slot="popover"] [role="option"] {
|
|
232
232
|
padding: var(--a-space-1) var(--a-space-2);
|
|
233
233
|
border-radius: var(--a-radius);
|
|
234
|
-
color: var(--
|
|
234
|
+
color: var(--nav-group-fg-muted);
|
|
235
235
|
cursor: pointer;
|
|
236
236
|
white-space: nowrap;
|
|
237
237
|
transition:
|
|
@@ -240,8 +240,8 @@ nav-group-ui [slot="popover"] [role="option"] {
|
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
nav-group-ui [slot="popover"] [role="option"]:hover {
|
|
243
|
-
background: var(--
|
|
244
|
-
color: var(--
|
|
243
|
+
background: var(--nav-group-bg-hover);
|
|
244
|
+
color: var(--nav-group-fg-hover);
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
/* Selected / current option — mirrors the main rail's nav-item selection:
|
|
@@ -249,8 +249,8 @@ nav-group-ui [slot="popover"] [role="option"]:hover {
|
|
|
249
249
|
nav-group-ui [slot="popover"] [role="option"][aria-current="page"],
|
|
250
250
|
nav-group-ui [slot="popover"] [role="option"][aria-selected="true"] {
|
|
251
251
|
position: relative;
|
|
252
|
-
background: var(--
|
|
253
|
-
color: var(--
|
|
252
|
+
background: var(--nav-group-bg-hover);
|
|
253
|
+
color: var(--nav-group-fg-selected);
|
|
254
254
|
font-weight: var(--a-weight-medium);
|
|
255
255
|
}
|
|
256
256
|
|
|
@@ -283,7 +283,7 @@ nav-ui[variant="section"] > nav-group-ui:not([variant]) > [slot="header"] {
|
|
|
283
283
|
padding: var(--a-space-3) var(--a-space-2) var(--a-space-1);
|
|
284
284
|
font-size: var(--a-kicker-sm, var(--a-ui-tiny));
|
|
285
285
|
font-weight: var(--a-weight-medium);
|
|
286
|
-
color: var(--
|
|
286
|
+
color: var(--nav-group-fg-muted);
|
|
287
287
|
text-transform: uppercase;
|
|
288
288
|
letter-spacing: 0.06em;
|
|
289
289
|
cursor: default;
|
|
@@ -295,7 +295,7 @@ nav-group-ui[variant="section"] > [slot="header"]:focus-visible,
|
|
|
295
295
|
nav-ui[variant="section"] > nav-group-ui:not([variant]) > [slot="header"]:hover,
|
|
296
296
|
nav-ui[variant="section"] > nav-group-ui:not([variant]) > [slot="header"]:focus-visible {
|
|
297
297
|
background: transparent;
|
|
298
|
-
color: var(--
|
|
298
|
+
color: var(--nav-group-fg-muted);
|
|
299
299
|
}
|
|
300
300
|
|
|
301
301
|
nav-group-ui[variant="section"] > [slot="header"] [slot="caret"],
|
|
@@ -101,7 +101,8 @@ option-card-ui[checked] > [slot="icon"] {
|
|
|
101
101
|
outline: none;
|
|
102
102
|
transition:
|
|
103
103
|
background var(--option-card-duration) var(--option-card-easing),
|
|
104
|
-
border-color var(--option-card-duration) var(--option-card-easing)
|
|
104
|
+
border-color var(--option-card-duration) var(--option-card-easing),
|
|
105
|
+
box-shadow var(--option-card-duration) var(--option-card-easing);
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
/* When an icon slot is present, insert a third column for it
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* Each digit gets its own input — auto-advances on entry,
|
|
19
19
|
* backs up on delete, distributes pasted values.
|
|
20
20
|
*
|
|
21
|
-
* Events: 'complete' (detail: {value}), 'input'
|
|
21
|
+
* Events: 'complete' (detail: {value}), 'input' (detail: {value}), 'change' (detail: {value})
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import { UIFormElement } from '../../core/form.js';
|
|
@@ -107,6 +107,7 @@ export class UIOtpInput extends UIFormElement {
|
|
|
107
107
|
|
|
108
108
|
this.#syncCombined();
|
|
109
109
|
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
110
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
110
111
|
|
|
111
112
|
if (input.value && index < this.#inputs.length - 1) {
|
|
112
113
|
this.#inputs[index + 1].focus();
|
|
@@ -121,6 +122,7 @@ export class UIOtpInput extends UIFormElement {
|
|
|
121
122
|
this.#inputs[index - 1].value = '';
|
|
122
123
|
this.#syncCombined();
|
|
123
124
|
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
125
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
124
126
|
}
|
|
125
127
|
}
|
|
126
128
|
|
|
@@ -132,6 +134,7 @@ export class UIOtpInput extends UIFormElement {
|
|
|
132
134
|
}
|
|
133
135
|
this.#syncCombined();
|
|
134
136
|
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
137
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
135
138
|
|
|
136
139
|
// Focus last filled or first empty
|
|
137
140
|
const firstEmpty = this.#inputs.findIndex(inp => !inp.value);
|
|
@@ -46,6 +46,15 @@
|
|
|
46
46
|
"category": "input",
|
|
47
47
|
"composes": [],
|
|
48
48
|
"events": {
|
|
49
|
+
"change": {
|
|
50
|
+
"description": "Fired alongside `input` on every digit change. Provides form-bearing-primitive event-shape parity (every UIFormElement emits `change` with `detail.value`).",
|
|
51
|
+
"detail": {
|
|
52
|
+
"value": {
|
|
53
|
+
"description": "Combined digits at the moment of the change.",
|
|
54
|
+
"type": "string"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
49
58
|
"complete": {
|
|
50
59
|
"description": "Fired exactly once when the user fills the last digit slot. detail.value is the combined string.",
|
|
51
60
|
"detail": {
|
|
@@ -11,6 +11,8 @@ export interface OtpInputEventDetail {
|
|
|
11
11
|
value: string;
|
|
12
12
|
}
|
|
13
13
|
export type OtpInputEvent = CustomEvent<OtpInputEventDetail>;
|
|
14
|
+
export type OtpInputInputEvent = CustomEvent<OtpInputEventDetail>;
|
|
15
|
+
export type OtpInputChangeEvent = CustomEvent<OtpInputEventDetail>;
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* Detail payload for the `complete` event — fired exactly once when the
|
|
@@ -32,5 +34,6 @@ export class UIOtpInput extends UIFormElement {
|
|
|
32
34
|
options?: boolean | AddEventListenerOptions,
|
|
33
35
|
): void;
|
|
34
36
|
addEventListener(type: 'input', listener: (ev: OtpInputEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
37
|
+
addEventListener(type: 'change', listener: (ev: OtpInputEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
35
38
|
addEventListener(type: 'complete', listener: (ev: OtpInputCompleteEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
36
39
|
}
|
|
@@ -36,6 +36,12 @@ events:
|
|
|
36
36
|
value:
|
|
37
37
|
type: string
|
|
38
38
|
description: Combined digits at the moment of the change.
|
|
39
|
+
change:
|
|
40
|
+
description: Fired alongside `input` on every digit change. Provides form-bearing-primitive event-shape parity (every UIFormElement emits `change` with `detail.value`).
|
|
41
|
+
detail:
|
|
42
|
+
value:
|
|
43
|
+
type: string
|
|
44
|
+
description: Combined digits at the moment of the change.
|
|
39
45
|
slots:
|
|
40
46
|
field:
|
|
41
47
|
description: Container for the digit input boxes and optional separator
|
package/components/page/page.css
CHANGED
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
top: 0;
|
|
59
59
|
z-index: 1;
|
|
60
60
|
background: var(--page-sticky-bg);
|
|
61
|
-
transition: border-color
|
|
61
|
+
transition: border-color var(--a-duration-fast) var(--a-easing), box-shadow var(--a-duration-fast) var(--a-easing);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
:scope[data-header-stuck] > :where(header, header-ui) {
|
|
@@ -70,7 +70,9 @@
|
|
|
70
70
|
line-height: 1;
|
|
71
71
|
transition:
|
|
72
72
|
background var(--pagination-duration) var(--pagination-easing),
|
|
73
|
-
color var(--pagination-duration) var(--pagination-easing)
|
|
73
|
+
border-color var(--pagination-duration) var(--pagination-easing),
|
|
74
|
+
color var(--pagination-duration) var(--pagination-easing),
|
|
75
|
+
box-shadow var(--pagination-duration) var(--pagination-easing);
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
[slot="nav"] button:not([disabled]):hover {
|
|
@@ -39,7 +39,7 @@ radio-ui[checked] [slot="dot"]::after {
|
|
|
39
39
|
--radio-bg-checked-hover: var(--a-primary-hover);
|
|
40
40
|
|
|
41
41
|
/* ── State: disabled ── */
|
|
42
|
-
--radio-fg-disabled: var(--a-
|
|
42
|
+
--radio-fg-disabled: var(--a-ui-text-disabled);
|
|
43
43
|
|
|
44
44
|
/* ── State: focus ── */
|
|
45
45
|
--radio-focus-ring: var(--a-focus-ring);
|
|
@@ -76,6 +76,7 @@ export class UISearch extends UIFormElement {
|
|
|
76
76
|
this.value = this.#inputEl.value;
|
|
77
77
|
this.syncValue(this.value);
|
|
78
78
|
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
79
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
79
80
|
|
|
80
81
|
clearTimeout(this.#timer);
|
|
81
82
|
this.#timer = setTimeout(() => {
|
|
@@ -106,6 +107,7 @@ export class UISearch extends UIFormElement {
|
|
|
106
107
|
this.syncValue('');
|
|
107
108
|
this.setAttribute('value', '');
|
|
108
109
|
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
110
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
109
111
|
this.dispatchEvent(new CustomEvent('search', {
|
|
110
112
|
bubbles: true,
|
|
111
113
|
detail: { value: '' },
|
|
@@ -56,6 +56,15 @@
|
|
|
56
56
|
"category": "input",
|
|
57
57
|
"composes": [],
|
|
58
58
|
"events": {
|
|
59
|
+
"change": {
|
|
60
|
+
"description": "Fired alongside `input` on every keystroke + on clear. Provides form-bearing-primitive event-shape parity (every UIFormElement emits `change` with `detail.value`).",
|
|
61
|
+
"detail": {
|
|
62
|
+
"value": {
|
|
63
|
+
"description": "Current search-input value.",
|
|
64
|
+
"type": "string"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
59
68
|
"clear": {
|
|
60
69
|
"description": "Fired when the clear button is clicked"
|
|
61
70
|
},
|
|
@@ -10,6 +10,7 @@ export interface SearchInputEventDetail {
|
|
|
10
10
|
value: string;
|
|
11
11
|
}
|
|
12
12
|
export type SearchInputEvent = CustomEvent<SearchInputEventDetail>;
|
|
13
|
+
export type SearchChangeEvent = CustomEvent<SearchInputEventDetail>;
|
|
13
14
|
|
|
14
15
|
export interface SearchEventDetail {
|
|
15
16
|
value: string;
|
|
@@ -31,5 +32,6 @@ export class UISearch extends UIFormElement {
|
|
|
31
32
|
options?: boolean | AddEventListenerOptions,
|
|
32
33
|
): void;
|
|
33
34
|
addEventListener(type: 'input', listener: (ev: SearchInputEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
35
|
+
addEventListener(type: 'change', listener: (ev: SearchInputEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
34
36
|
addEventListener(type: 'search', listener: (ev: SearchEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
35
37
|
}
|