@adia-ai/web-components 0.5.13 → 0.5.14

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 CHANGED
@@ -11,6 +11,60 @@ runtime ships in the sibling `@adia-ai/a2ui-runtime` package
11
11
 
12
12
  _No pending changes._
13
13
 
14
+ ## [0.5.14] - 2026-05-15
15
+
16
+ ### v0.5.14 — Form-control hover-fg token unification (precursor to §315; commit `178418525`)
17
+
18
+ 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.
19
+
20
+ #### Fixed — `components/{input,textarea,segment,select}/`
21
+
22
+ - `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)`.
23
+ - `textarea-ui`: matching routing on hover/affix-hover.
24
+ - `segment-ui`: same routing change.
25
+ - `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).
26
+
27
+ 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`.
28
+
29
+ ### v0.5.14 §315 — Drift-class sweep follow-up (`ui-audit-coherence` + `maintain-tokens` + `analyze-css` triple-pass)
30
+
31
+ 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.
32
+
33
+ #### Fixed — `components/select/`, `components/nav-group/`, `components/chat-thread/chat-input.css`
34
+
35
+ - `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).
36
+ - `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}`.
37
+ - `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.
38
+
39
+ #### Fixed — `components/{check,radio,switch,tabs,action-list,calendar-picker,menu}/`
40
+
41
+ - 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).
42
+ - `menu.css:96,99` also fixed `--menu-item-fg-disabled` + `--menu-item-icon-disabled` (the third variant `--a-fg-disabled` shape; same canonical target).
43
+
44
+ #### Added — `components/otp-input/`, `components/search/`
45
+
46
+ - `<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.
47
+ - `.d.ts` files gain `OtpInputChangeEvent` + `SearchChangeEvent` type exports (reuse existing detail interfaces; matches v0.5.10 `check-form-bearing-dts` audit convention).
48
+ - yaml `events:` blocks declare the new `change` event with `detail.value`.
49
+
50
+ #### Fixed — `styles/typography.css`
51
+
52
+ - `: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.
53
+
54
+ #### Fixed — `components/{textarea,upload,option-card,pagination}/` transition consistency
55
+
56
+ - `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).
57
+ - `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.
58
+
59
+ #### Fixed — `components/input/`, `components/select/` `-active` token routing
60
+
61
+ - `--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).
62
+ - `--select-option-fg-active: var(--a-fg)` → `var(--a-ui-text-active)` (same routing; cross-primitive form-control consistency).
63
+
64
+ #### Fixed — `components/heatmap/`
65
+
66
+ - `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.
67
+
14
68
  ## [0.5.13] - 2026-05-15
15
69
 
16
70
  ### 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-fg-muted);
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-fg-muted);
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-fg-muted);
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-fg);
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-fg-disabled);
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-fg-muted);
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-fg);
41
- --input-affix-fg-hover: var(--a-fg-subtle);
42
- --input-fg-focus: var(--a-fg-strong);
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);
@@ -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-fg-disabled);
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-fg-disabled);
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(--a-fg-muted);
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(--a-fg-subtle);
234
+ color: var(--nav-group-fg-muted);
235
235
  cursor: pointer;
236
236
  white-space: nowrap;
237
237
  transition:
@@ -241,7 +241,7 @@ nav-group-ui [slot="popover"] [role="option"] {
241
241
 
242
242
  nav-group-ui [slot="popover"] [role="option"]:hover {
243
243
  background: var(--a-bg-hover);
244
- color: var(--a-fg-strong);
244
+ color: var(--nav-group-fg-hover);
245
245
  }
246
246
 
247
247
  /* Selected / current option — mirrors the main rail's nav-item selection:
@@ -250,7 +250,7 @@ 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
252
  background: var(--a-bg-hover);
253
- color: var(--a-fg-strong);
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(--a-fg-muted);
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(--a-fg-muted);
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
@@ -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-fg-muted);
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
  }
@@ -39,6 +39,12 @@ events:
39
39
  description: Fired when the clear button is clicked
40
40
  input:
41
41
  description: "Fired on each input change during typing."
42
+ change:
43
+ description: Fired alongside `input` on every keystroke + on clear. Provides form-bearing-primitive event-shape parity (every UIFormElement emits `change` with `detail.value`).
44
+ detail:
45
+ value:
46
+ type: string
47
+ description: Current search-input value.
42
48
  search:
43
49
  description: Debounced CustomEvent with detail.query containing the search string
44
50
  slots:
@@ -20,10 +20,10 @@ segment-ui[selected] {
20
20
  --segment-font-weight: var(--a-ui-weight);
21
21
 
22
22
  /* ── Colors ── */
23
- --segment-fg: var(--a-fg-subtle);
24
- --segment-fg-hover: var(--a-fg);
23
+ --segment-fg: var(--a-ui-text-subtle);
24
+ --segment-fg-hover: var(--a-ui-text-hover);
25
25
  --segment-fg-selected: var(--a-chrome-dark);
26
- --segment-fg-disabled: var(--a-fg-muted);
26
+ --segment-fg-disabled: var(--a-ui-text-disabled);
27
27
 
28
28
  /* ── Transition ── */
29
29
  --segment-duration: var(--a-duration-fast);
@@ -24,6 +24,7 @@
24
24
  --select-placeholder-fg: var(--a-ui-text-placeholder);
25
25
  --select-caret-fg: var(--a-ui-text-muted);
26
26
  --select-fg: var(--a-ui-text);
27
+ --select-fg-hover: var(--a-ui-text-hover);
27
28
  --select-fg-selected: var(--a-ui-text-selected);
28
29
  --select-fg-subtle: var(--a-ui-text-subtle);
29
30
  --select-fg-muted: var(--a-ui-text-muted);
@@ -38,7 +39,7 @@
38
39
  --select-option-px: var(--a-ui-px);
39
40
  --select-option-py: var(--a-ui-py);
40
41
  --select-option-bg-hover: var(--a-bg-hover);
41
- --select-option-fg-active: var(--a-fg);
42
+ --select-option-fg-active: var(--a-ui-text-active);
42
43
  --select-option-fg-disabled: var(--a-ui-text-disabled);
43
44
  --select-group-label-size: var(--a-ui-size);
44
45
  }
@@ -223,7 +224,7 @@ select-ui [role="option"] {
223
224
  padding: var(--a-space-1) var(--a-ui-px);
224
225
  border-radius: var(--a-radius-sm);
225
226
  white-space: nowrap;
226
- color: var(--a-fg-subtle);
227
+ color: var(--select-fg-subtle);
227
228
  cursor: pointer;
228
229
  transition:
229
230
  background var(--a-duration-fast) var(--a-easing),
@@ -231,8 +232,8 @@ select-ui [role="option"] {
231
232
  }
232
233
  select-ui [role="option"]:hover,
233
234
  select-ui [role="option"][data-focused] {
234
- background: var(--a-bg-hover);
235
- color: var(--a-fg-hover);
235
+ background: var(--select-option-bg-hover);
236
+ color: var(--select-fg-hover);
236
237
  }
237
238
  select-ui [role="option"][aria-selected="true"] {
238
239
  color: var(--select-fg-selected);
@@ -240,7 +241,7 @@ select-ui [role="option"][aria-selected="true"] {
240
241
  font-weight: var(--a-ui-weight);
241
242
  }
242
243
  select-ui [role="option"][aria-disabled="true"] {
243
- color: var(--a-fg-muted);
244
+ color: var(--select-option-fg-disabled);
244
245
  cursor: not-allowed;
245
246
  }
246
247
  select-ui [role="option"][aria-disabled="true"]:hover {
@@ -250,7 +251,7 @@ select-ui [role="option"][aria-disabled="true"]:hover {
250
251
  /* Option with icon */
251
252
  select-ui [role="option"] icon-ui {
252
253
  --a-icon-size: var(--a-ui-size);
253
- color: var(--a-fg-muted);
254
+ color: var(--select-fg-muted);
254
255
  margin-inline-end: var(--a-space-1);
255
256
  vertical-align: -0.125em;
256
257
  }
@@ -282,21 +283,21 @@ select-ui [data-menu-header] img {
282
283
  select-ui [data-menu-header] strong {
283
284
  display: block;
284
285
  font-weight: var(--a-weight-medium);
285
- color: var(--a-fg);
286
+ color: var(--select-fg);
286
287
  font-size: var(--a-ui-size);
287
288
  }
288
289
 
289
290
  select-ui [data-menu-header] span {
290
291
  display: block;
291
292
  font-size: var(--a-ui-sm);
292
- color: var(--a-fg-muted);
293
+ color: var(--select-fg-muted);
293
294
  }
294
295
 
295
296
  select-ui [role="group"] [slot="group-label"] {
296
297
  padding: var(--a-space-1) var(--a-ui-px);
297
298
  font-size: var(--a-ui-tiny);
298
299
  font-weight: var(--a-ui-weight);
299
- color: var(--a-fg-muted);
300
+ color: var(--select-fg-muted);
300
301
  text-transform: uppercase;
301
302
  letter-spacing: 0.05em;
302
303
  white-space: nowrap;
@@ -59,7 +59,7 @@ switch-ui[checked] [slot="thumb"] {
59
59
  --switch-thumb-travel: calc(var(--switch-track-width) - var(--switch-thumb-size) - 2 * var(--switch-thumb-inset) - 2px);
60
60
 
61
61
  /* ── State: disabled ── */
62
- --switch-fg-disabled: var(--a-fg-muted);
62
+ --switch-fg-disabled: var(--a-ui-text-disabled);
63
63
  }
64
64
 
65
65
  :scope {
@@ -13,7 +13,7 @@
13
13
  --tabs-fg: var(--a-fg-subtle);
14
14
  --tabs-fg-hover: var(--a-fg);
15
15
  --tabs-fg-active: var(--a-fg-selected);
16
- --tabs-fg-disabled: var(--a-fg-muted);
16
+ --tabs-fg-disabled: var(--a-ui-text-disabled);
17
17
 
18
18
  /* ── Spacing ── */
19
19
  --tabs-vertical-gap: var(--a-space-4);
@@ -32,7 +32,7 @@ textarea-ui:not([disabled]) [slot="text"]:hover {
32
32
 
33
33
  /* ── State ── */
34
34
  --textarea-bg-hover: var(--a-ui-bg-hover);
35
- --textarea-fg-hover: var(--a-fg);
35
+ --textarea-fg-hover: var(--a-ui-text-hover);
36
36
  --textarea-label-fg-focus: var(--a-fg-subtle);
37
37
  --textarea-bg-disabled: var(--a-ui-bg-disabled);
38
38
  --textarea-fg-disabled: var(--a-ui-text-disabled);
@@ -73,7 +73,11 @@ textarea-ui:not([disabled]) [slot="text"]:hover {
73
73
  overflow-y: auto;
74
74
  cursor: text;
75
75
  outline: none;
76
- transition: border-color var(--textarea-duration) var(--textarea-easing);
76
+ transition:
77
+ background var(--textarea-duration) var(--textarea-easing),
78
+ border-color var(--textarea-duration) var(--textarea-easing),
79
+ color var(--textarea-duration) var(--textarea-easing),
80
+ box-shadow var(--textarea-duration) var(--textarea-easing);
77
81
  }
78
82
  /* hover rule moved outside @scope — see Safari 17.x bug note at top. */
79
83
  :scope:not([disabled]) [slot="text"]:focus {
@@ -64,7 +64,8 @@
64
64
  transition:
65
65
  border-color var(--upload-duration) var(--upload-easing),
66
66
  background var(--upload-duration) var(--upload-easing),
67
- color var(--upload-duration) var(--upload-easing);
67
+ color var(--upload-duration) var(--upload-easing),
68
+ box-shadow var(--upload-duration) var(--upload-easing);
68
69
  }
69
70
  [data-dropzone]:hover {
70
71
  border-color: var(--upload-border-hover);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adia-ai/web-components",
3
- "version": "0.5.13",
3
+ "version": "0.5.14",
4
4
  "description": "AdiaUI web components — vanilla custom elements. A2UI runtime (renderer, registry, streams, wiring) lives in @adia-ai/a2ui-runtime.",
5
5
  "type": "module",
6
6
  "types": "./index.d.ts",
@@ -584,7 +584,7 @@
584
584
 
585
585
  /* ── Mark — system highlight tokens ── */
586
586
  :where(mark) {
587
- background: var(--a-highlight-default);
587
+ background: var(--a-highlight);
588
588
  color: var(--a-highlight-text);
589
589
  padding-inline: 0.125em;
590
590
  border-radius: 0.125em;