@adia-ai/web-components 0.0.19 → 0.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/components/accordion/accordion.css +101 -102
  2. package/components/agent-feedback-bar/agent-feedback-bar.js +8 -8
  3. package/components/agent-questions/agent-questions.css +2 -1
  4. package/components/agent-questions/agent-questions.js +6 -6
  5. package/components/agent-reasoning/agent-reasoning.js +20 -5
  6. package/components/agent-trace/agent-trace.a2ui.json +5 -5
  7. package/components/agent-trace/agent-trace.js +7 -5
  8. package/components/agent-trace/agent-trace.yaml +2 -2
  9. package/components/alert/alert.a2ui.json +1 -2
  10. package/components/alert/alert.css +4 -4
  11. package/components/alert/alert.yaml +1 -2
  12. package/components/avatar/avatar.a2ui.json +3 -3
  13. package/components/avatar/avatar.js +15 -4
  14. package/components/avatar/avatar.yaml +6 -6
  15. package/components/button/button.a2ui.json +14 -2
  16. package/components/button/button.css +19 -2
  17. package/components/button/button.js +1 -0
  18. package/components/button/button.yaml +20 -2
  19. package/components/calendar-picker/calendar-picker.css +2 -1
  20. package/components/calendar-picker/calendar-picker.js +12 -1
  21. package/components/chart/chart.css +11 -11
  22. package/components/chart/chart.js +26 -18
  23. package/components/chart-legend/chart-legend.a2ui.json +2 -2
  24. package/components/chart-legend/chart-legend.js +9 -6
  25. package/components/chart-legend/chart-legend.yaml +2 -2
  26. package/components/chat/chat-input.js +13 -5
  27. package/components/chat/chat.a2ui.json +2 -2
  28. package/components/chat/chat.js +14 -3
  29. package/components/chat/chat.yaml +2 -2
  30. package/components/code/code.css +16 -6
  31. package/components/command/command.js +9 -1
  32. package/components/field/field.a2ui.json +0 -5
  33. package/components/field/field.css +2 -2
  34. package/components/field/field.js +53 -5
  35. package/components/field/field.yaml +5 -8
  36. package/components/heatmap/heatmap.css +32 -23
  37. package/components/input/input.js +30 -1
  38. package/components/kbd/kbd.a2ui.json +5 -1
  39. package/components/kbd/kbd.yaml +5 -1
  40. package/components/menu/menu.css +20 -8
  41. package/components/menu/menu.js +9 -1
  42. package/components/modal/modal.css +101 -108
  43. package/components/noodles/noodles.js +25 -8
  44. package/components/pipeline-status/pipeline-status.css +4 -4
  45. package/components/pipeline-status/pipeline-status.js +11 -7
  46. package/components/popover/popover.js +4 -0
  47. package/components/progress-row/progress-row.a2ui.json +3 -2
  48. package/components/progress-row/progress-row.yaml +2 -1
  49. package/components/range/range.js +7 -0
  50. package/components/richtext/richtext.css +2 -2
  51. package/components/richtext/richtext.js +4 -1
  52. package/components/segment/segment.css +1 -1
  53. package/components/segmented/segmented.js +7 -1
  54. package/components/select/select.css +7 -4
  55. package/components/slider/slider.js +15 -8
  56. package/components/stepper/stepper.css +181 -144
  57. package/components/stepper/stepper.js +5 -2
  58. package/components/swiper/swiper.a2ui.json +3 -3
  59. package/components/swiper/swiper.css +11 -77
  60. package/components/swiper/swiper.js +6 -5
  61. package/components/swiper/swiper.yaml +3 -3
  62. package/components/switch/switch.a2ui.json +8 -1
  63. package/components/switch/switch.yaml +8 -1
  64. package/components/table/table.js +9 -1
  65. package/components/table-toolbar/table-toolbar.a2ui.json +20 -20
  66. package/components/table-toolbar/table-toolbar.css +15 -7
  67. package/components/table-toolbar/table-toolbar.js +24 -11
  68. package/components/table-toolbar/table-toolbar.yaml +16 -12
  69. package/components/tabs/tabs.css +3 -2
  70. package/components/tabs/tabs.js +7 -1
  71. package/components/tag/tag.a2ui.json +2 -2
  72. package/components/tag/tag.yaml +2 -2
  73. package/components/timeline/timeline.css +244 -204
  74. package/components/timeline/timeline.js +11 -11
  75. package/components/toast/toast.a2ui.json +2 -3
  76. package/components/toast/toast.yaml +5 -3
  77. package/components/toolbar/toolbar.css +6 -1
  78. package/components/toolbar/toolbar.js +10 -2
  79. package/components/tooltip/tooltip.css +8 -2
  80. package/components/tooltip/tooltip.js +12 -14
  81. package/components/tree/tree.css +21 -0
  82. package/core/icons.js +14 -0
  83. package/core/polyfills.js +17 -7
  84. package/package.json +1 -1
  85. package/patterns/a2ui-root/a2ui-root.js +21 -14
  86. package/patterns/app-shell/css/app-shell.main.css +30 -1
  87. package/patterns/app-shell/css/app-shell.tokens.css +1 -0
  88. package/patterns/gen-ui/gen-ui.js +1 -1
  89. package/styles/colors/semantics.css +59 -2
  90. package/styles/tokens.css +16 -12
@@ -1,8 +1,14 @@
1
1
  @scope (tooltip-ui) {
2
+ :where(:scope) {
3
+ /* Host (anchor) tokens. The popover itself escapes to top-layer and
4
+ cannot inherit these — top-layer rules below reference --a-*
5
+ directly per documented exception. */
6
+ --tooltip-host-display: inline-flex;
7
+ }
2
8
  :scope {
3
9
  /* ── Base ── */
4
10
  box-sizing: border-box;
5
- display: inline-flex;
11
+ display: var(--tooltip-host-display);
6
12
  position: relative;
7
13
  }
8
14
  }
@@ -94,7 +100,7 @@
94
100
  grid-template-columns: auto 1fr auto;
95
101
  align-items: center;
96
102
  gap: var(--a-space-1) var(--a-space-2);
97
- padding: 1px 0;
103
+ padding: var(--a-space-px) 0;
98
104
  }
99
105
 
100
106
  /* Multi-series mode: the row the pointer is actually over gets a
@@ -26,9 +26,9 @@ class AdiaTooltip extends AdiaElement {
26
26
  text: { type: String, default: '', reflect: true },
27
27
  placement: { type: String, default: 'top', reflect: true },
28
28
  delay: { type: Number, default: 400, reflect: true },
29
- follows: { type: String, default: 'trigger', reflect: true }, // trigger | pointer
29
+ follows: { type: String, default: 'trigger', reflect: true },
30
30
  for: { type: String, default: '', reflect: true },
31
- indicator: { type: String, default: 'none', reflect: true }, // none | dot | line | dashed
31
+ indicator: { type: String, default: 'none', reflect: true },
32
32
  };
33
33
 
34
34
  static template = () => null;
@@ -39,8 +39,6 @@ class AdiaTooltip extends AdiaElement {
39
39
 
40
40
  /* Pointer-follow mode state */
41
41
  #target = null;
42
- #hoverHandler = null;
43
- #leaveHandler = null;
44
42
  #lastDetail = null;
45
43
 
46
44
  connected() {
@@ -138,21 +136,21 @@ class AdiaTooltip extends AdiaElement {
138
136
  return;
139
137
  }
140
138
 
141
- this.#hoverHandler = (e) => this.#onChartHover(e);
142
- this.#leaveHandler = () => this.#hide();
143
- this.#target.addEventListener('chart-hover', this.#hoverHandler);
144
- this.#target.addEventListener('chart-leave', this.#leaveHandler);
139
+ this.#target.addEventListener('chart-hover', this.#onChartHover);
140
+ this.#target.addEventListener('chart-leave', this.#onChartLeave);
145
141
  }
146
142
 
147
143
  #detachPointerFollow() {
148
- if (this.#target && this.#hoverHandler) this.#target.removeEventListener('chart-hover', this.#hoverHandler);
149
- if (this.#target && this.#leaveHandler) this.#target.removeEventListener('chart-leave', this.#leaveHandler);
144
+ if (this.#target) {
145
+ this.#target.removeEventListener('chart-hover', this.#onChartHover);
146
+ this.#target.removeEventListener('chart-leave', this.#onChartLeave);
147
+ }
150
148
  this.#target = null;
151
- this.#hoverHandler = null;
152
- this.#leaveHandler = null;
153
149
  }
154
150
 
155
- #onChartHover(event) {
151
+ #onChartLeave = () => this.#hide();
152
+
153
+ #onChartHover = (event) => {
156
154
  const detail = event.detail;
157
155
  if (!detail) return;
158
156
  this.#lastDetail = detail;
@@ -165,7 +163,7 @@ class AdiaTooltip extends AdiaElement {
165
163
 
166
164
  this.#paintPointerContent(detail);
167
165
  this.#positionAtPointer(detail.pointerX, detail.pointerY);
168
- }
166
+ };
169
167
 
170
168
  #createPopover() {
171
169
  const el = document.createElement('div');
@@ -38,6 +38,27 @@
38
38
  }
39
39
 
40
40
  @scope (tree-item-ui) {
41
+ :where(:scope) {
42
+ /* Item-local aliases of parent tree-ui tokens (sanctioned co-located
43
+ composition pattern; matches stepper-item-ui / timeline-item-ui). */
44
+ --tree-item-row-height: var(--tree-row-height, var(--a-size));
45
+ --tree-item-row-radius: var(--tree-row-radius, var(--a-radius-sm));
46
+ --tree-item-row-px: var(--tree-row-px, var(--a-space-1));
47
+ --tree-item-row-gap: var(--tree-row-gap, var(--a-space-1));
48
+ --tree-item-actions-gap: var(--tree-actions-gap, var(--a-space-0-5));
49
+ --tree-item-indent: var(--tree-indent, var(--a-space-4));
50
+ --tree-item-chevron-size: var(--tree-chevron-size, var(--a-space-2));
51
+ --tree-item-icon-size: var(--tree-icon-size, var(--a-space-3));
52
+
53
+ --tree-item-fg: var(--tree-fg, var(--a-fg));
54
+ --tree-item-fg-muted: var(--tree-fg-muted, var(--a-fg-muted));
55
+ --tree-item-bg-hover: var(--tree-bg-hover, var(--a-bg-muted));
56
+ --tree-item-bg-selected: var(--tree-bg-selected, var(--a-bg-hover));
57
+
58
+ --tree-item-duration: var(--tree-duration, var(--a-duration-fast));
59
+ --tree-item-easing: var(--tree-easing, var(--a-easing));
60
+ --tree-item-focus-ring: var(--tree-focus-ring, var(--a-focus-ring));
61
+ }
41
62
  :scope {
42
63
  box-sizing: border-box;
43
64
  display: flex;
package/core/icons.js CHANGED
@@ -92,6 +92,18 @@ try {
92
92
  Flipped to `true` in the manifest `.then()` handler below. */
93
93
  let registryReady = hasViteGlob;
94
94
 
95
+ /* `whenIconRegistryReady` lets a component defer icon-name decisions until
96
+ the registry is sync-checkable. <icon-ui> doesn't need it — it owns the
97
+ <name → svg> render and the manifest .then() handler below re-runs
98
+ loadIcon for every element. But components that branch on whether a
99
+ string IS an icon (e.g. <input-ui>'s prefix/suffix, which accept both
100
+ icon names and text labels like "@" or "$") need to re-evaluate after
101
+ manifest load, otherwise a kebab-case name baked into the DOM as text
102
+ never recovers. */
103
+ let resolveRegistryReady;
104
+ export const whenIconRegistryReady = new Promise((r) => { resolveRegistryReady = r; });
105
+ if (hasViteGlob) resolveRegistryReady();
106
+
95
107
  // Non-Vite environments (plain static serving): fetch the build-time
96
108
  // manifest in the background and rebuild `weightModules` with lazy
97
109
  // fetch-based loaders. No top-level await — the module finishes loading
@@ -123,6 +135,7 @@ if (!hasViteGlob) {
123
135
  if (name) loadIcon(name, weight);
124
136
  }
125
137
  }
138
+ resolveRegistryReady();
126
139
  }).catch(() => { /* keep EMPTY_WEIGHTS — registryReady stays false */ });
127
140
  }
128
141
 
@@ -199,6 +212,7 @@ const ICON_ALIASES = {
199
212
  'phone': 'phone', 'location': 'map-pin', 'calendar': 'calendar-blank',
200
213
  'time': 'clock', 'user': 'user', 'users': 'users-three',
201
214
  'menu': 'list', 'more': 'dots-three', 'dots-vertical': 'dots-three-vertical', 'filter': 'funnel',
215
+ 'inbox': 'tray', 'text-bold': 'text-b',
202
216
  'sort': 'sort-ascending', 'refresh': 'arrow-clockwise', 'download': 'download-simple',
203
217
  'upload': 'upload-simple', 'share': 'share-network', 'copy': 'copy-simple',
204
218
  'link': 'link-simple', 'external-link': 'arrow-square-out',
package/core/polyfills.js CHANGED
@@ -1,13 +1,23 @@
1
1
  /**
2
- * AdiaUI Polyfillsconditional loading for missing platform features.
3
- * Import BEFORE component registration.
2
+ * AdiaUI optional polyfills for consumers extending below the
3
+ * library's stated baseline (Chromium 125+, Safari 17.4+, Firefox 129+).
4
4
  *
5
- * Currently polyfills:
6
- * - Popover API (Firefox < 125)
7
- * - CSS Anchor Positioning (only Chrome 125+ native)
5
+ * Side-effect import — runs feature checks at module-evaluation time
6
+ * and dynamically loads polyfills only when the runtime lacks support:
8
7
  *
9
- * Both are loaded conditionally — browsers with native support skip the download.
10
- * Packages are optional — if not installed, the import silently fails.
8
+ * import '@adia-ai/web-components/core/polyfills.js';
9
+ * // before importing components
10
+ *
11
+ * Conditionally loads:
12
+ * - Popover API (consumers extending to Firefox < 125 / Safari < 17)
13
+ * - CSS Anchor Positioning (consumers extending to Safari < 26 / Firefox < 147)
14
+ *
15
+ * Browsers with native support skip the download. Packages are
16
+ * optional — if not installed, the import silently fails.
17
+ *
18
+ * Consumers on the stated baseline should NOT import this module —
19
+ * the runtime checks below resolve to no-op on every supported browser
20
+ * but you avoid the conditional `import()` setup entirely by skipping it.
11
21
  */
12
22
 
13
23
  if (typeof window !== 'undefined') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adia-ai/web-components",
3
- "version": "0.0.19",
3
+ "version": "0.0.21",
4
4
  "description": "AdiaUI web components — vanilla custom elements. A2UI runtime (renderer, registry, streams, wiring) lives in @adia-ai/a2ui-utils.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -49,24 +49,29 @@ class AdiaA2UIRoot extends AdiaElement {
49
49
  #renderer = null;
50
50
  #abortCtrl = null;
51
51
  #doc = null;
52
+ #bound = false;
53
+
54
+ #onClick = (e) => {
55
+ const actionEl = e.target.closest('[data-action]');
56
+ if (!actionEl) return;
57
+ this.dispatchEvent(new CustomEvent('a2ui-action', {
58
+ bubbles: true,
59
+ detail: {
60
+ name: actionEl.getAttribute('data-action'),
61
+ sourceComponentId: actionEl.getAttribute('data-a2ui-id') || '',
62
+ timestamp: Date.now(),
63
+ context: {},
64
+ },
65
+ }));
66
+ };
52
67
 
53
68
  connected() {
54
69
  this.#renderer = new A2UIRenderer(this, registry, { batch: this.batch });
55
70
 
56
- this.addEventListener('click', (e) => {
57
- const actionEl = e.target.closest('[data-action]');
58
- if (actionEl) {
59
- this.dispatchEvent(new CustomEvent('a2ui-action', {
60
- bubbles: true,
61
- detail: {
62
- name: actionEl.getAttribute('data-action'),
63
- sourceComponentId: actionEl.getAttribute('data-a2ui-id') || '',
64
- timestamp: Date.now(),
65
- context: {},
66
- },
67
- }));
68
- }
69
- });
71
+ if (!this.#bound) {
72
+ this.#bound = true;
73
+ this.addEventListener('click', this.#onClick);
74
+ }
70
75
  }
71
76
 
72
77
  render() {
@@ -174,6 +179,8 @@ class AdiaA2UIRoot extends AdiaElement {
174
179
 
175
180
  disconnected() {
176
181
  this.#abortCtrl?.abort();
182
+ this.removeEventListener('click', this.#onClick);
183
+ this.#bound = false;
177
184
  this.active = false;
178
185
  }
179
186
 
@@ -12,6 +12,30 @@ app-shell-ui > main {
12
12
  border-inline: var(--page-main-border);
13
13
  }
14
14
 
15
+ /* ── Edge breathing room when adjacent chrome is absent ──
16
+ When the shell has no visible chrome on a given edge of the content
17
+ surface, `> main > section` owns a small inset against that edge so
18
+ the rounded/elevated surface doesn't sit flush with the viewport.
19
+ The `:has(... :not([hidden]))` probe catches both "element missing
20
+ from the DOM" AND "element present but [hidden]" — toggling
21
+ [hidden] flips the inset live with no JS coordination. Scope is the
22
+ section, not main, so the topbar/statusbar inside main keep
23
+ spanning full-width even when the section gets its inset.
24
+
25
+ Block-edge probes accept either an app-level chrome bar
26
+ (`app-shell-ui > header / > footer`) OR a main-level chrome bar
27
+ (`> main > header / > main > footer`). Either qualifies as
28
+ "chrome present" and suppresses the corresponding margin. */
29
+ app-shell-ui:not(:has(> aside[data-sidebar="trailing"]:not([hidden]))) > main > section {
30
+ margin-inline-end: var(--a-space-2);
31
+ }
32
+ app-shell-ui:not(:has(> header:not([hidden]), > main > header:not([hidden]))) > main > section {
33
+ margin-block-start: var(--a-space-2);
34
+ }
35
+ app-shell-ui:not(:has(> footer:not([hidden]), > main > footer:not([hidden]))) > main > section {
36
+ margin-block-end: var(--a-space-2);
37
+ }
38
+
15
39
  /* ── Main > header (topbar) ──
16
40
  Contains: sidebar toggle, breadcrumb, spacer, action buttons.
17
41
 
@@ -71,7 +95,11 @@ app-shell-ui > main > header > [slot="action-leading"] {
71
95
  }
72
96
 
73
97
  /* ── Main > section (scroll container) ──
74
- Wraps [data-content-root]. Scrolls vertically, hides scrollbar. */
98
+ Wraps [data-content-root]. Scrolls vertically, hides scrollbar.
99
+ Soft elevation via --page-content-shadow (defaults to --a-shadow-md)
100
+ so the content surface lifts off the chrome canvas regardless of
101
+ mode — pairs with --page-content-radius under "rounded" and stays
102
+ visible under "borderless". */
75
103
  app-shell-ui > main > section {
76
104
  flex: 1;
77
105
  min-height: 0;
@@ -79,6 +107,7 @@ app-shell-ui > main > section {
79
107
  overscroll-behavior: contain;
80
108
  scrollbar-width: none;
81
109
  background: var(--page-content-bg);
110
+ box-shadow: var(--page-content-shadow);
82
111
  }
83
112
 
84
113
  app-shell-ui > main > section::-webkit-scrollbar { display: none; }
@@ -42,6 +42,7 @@
42
42
  --page-content-inset: var(--a-space-10); /* padding for header/body/footer */
43
43
  --page-content-max-width: 1540px; /* max-width for content children */
44
44
  --page-content-border: 1px solid var(--a-border-subtle); /* content dividers (persists in "borderless") */
45
+ --page-content-shadow: var(--a-shadow-sm); /* soft lift on the content surface */
45
46
 
46
47
  /* Section labels (e.g. "WORKSPACE") — maps to "kicker" typography role */
47
48
  --page-section-gap: var(--a-space-6);
@@ -14,7 +14,7 @@ import '../../components/inspector/inspector.js';
14
14
  * Consumer-owns-LLM: forwards all events from children, never calls fetch.
15
15
  *
16
16
  * Events (forwarded):
17
- * chat-submit — from thread
17
+ * submit — from thread (chat input)
18
18
  * suggestion-select — from thread
19
19
  * canvas-interaction — from canvas
20
20
  */
@@ -437,6 +437,63 @@
437
437
  --a-danger-border-disabled: var(--a-danger-border-subtle);
438
438
  --a-danger-border-invalid: var(--a-danger-border);
439
439
 
440
+ /* ══════════════════════════════════════════════════════════════
441
+ BUCKETS — sequential 5-step ramp per family for data-vis consumers
442
+
443
+ Distinct from `--a-data-0..9` (categorical, distinct hues) — buckets
444
+ are 5 steps within ONE family, ordered low → high. Used by sequential
445
+ data-vis surfaces (heatmap-ui, choropleth, treemap intensity).
446
+
447
+ Anchor: bucket-2 = `--a-{family}-strong` (semantic step-50). Steps 0
448
+ and 1 sit lighter (toward muted); steps 3 and 4 sit deeper (toward
449
+ active). Light/dark scheme polarity is honored via `light-dark()` —
450
+ in light mode the lighter primitives are step-tints, in dark mode
451
+ the lighter primitives are step-shades, so "low intensity" reads
452
+ as low contrast against canvas-0 in either scheme.
453
+
454
+ Coordinate with chart palette: `--a-data-N` are categorical (10 hue
455
+ slots fanning out from accent-hue); buckets stay within one family
456
+ so a heatmap colored `[color="success"]` doesn't drift through the
457
+ accent → success → warning hue arc. Five steps were chosen so the
458
+ ramp fits within WCAG AA against `--a-canvas-0` at the high end and
459
+ remains visually distinguishable at the low end.
460
+ ══════════════════════════════════════════════════════════════ */
461
+
462
+ /* Accent buckets */
463
+ --a-accent-bucket-0: light-dark(var(--a-accent-15-tint), var(--a-accent-85-shade));
464
+ --a-accent-bucket-1: light-dark(var(--a-accent-30-tint), var(--a-accent-70-shade));
465
+ --a-accent-bucket-2: var(--a-accent-strong);
466
+ --a-accent-bucket-3: light-dark(var(--a-accent-65-tint), var(--a-accent-35-shade));
467
+ --a-accent-bucket-4: light-dark(var(--a-accent-80-tint), var(--a-accent-20-shade));
468
+
469
+ /* Success buckets */
470
+ --a-success-bucket-0: light-dark(var(--a-success-15-tint), var(--a-success-85-shade));
471
+ --a-success-bucket-1: light-dark(var(--a-success-30-tint), var(--a-success-70-shade));
472
+ --a-success-bucket-2: var(--a-success-strong);
473
+ --a-success-bucket-3: light-dark(var(--a-success-65-tint), var(--a-success-35-shade));
474
+ --a-success-bucket-4: light-dark(var(--a-success-80-tint), var(--a-success-20-shade));
475
+
476
+ /* Warning buckets */
477
+ --a-warning-bucket-0: light-dark(var(--a-warning-15-tint), var(--a-warning-85-shade));
478
+ --a-warning-bucket-1: light-dark(var(--a-warning-30-tint), var(--a-warning-70-shade));
479
+ --a-warning-bucket-2: var(--a-warning-strong);
480
+ --a-warning-bucket-3: light-dark(var(--a-warning-65-tint), var(--a-warning-35-shade));
481
+ --a-warning-bucket-4: light-dark(var(--a-warning-80-tint), var(--a-warning-20-shade));
482
+
483
+ /* Danger buckets */
484
+ --a-danger-bucket-0: light-dark(var(--a-danger-15-tint), var(--a-danger-85-shade));
485
+ --a-danger-bucket-1: light-dark(var(--a-danger-30-tint), var(--a-danger-70-shade));
486
+ --a-danger-bucket-2: var(--a-danger-strong);
487
+ --a-danger-bucket-3: light-dark(var(--a-danger-65-tint), var(--a-danger-35-shade));
488
+ --a-danger-bucket-4: light-dark(var(--a-danger-80-tint), var(--a-danger-20-shade));
489
+
490
+ /* Info buckets */
491
+ --a-info-bucket-0: light-dark(var(--a-info-15-tint), var(--a-info-85-shade));
492
+ --a-info-bucket-1: light-dark(var(--a-info-30-tint), var(--a-info-70-shade));
493
+ --a-info-bucket-2: var(--a-info-strong);
494
+ --a-info-bucket-3: light-dark(var(--a-info-65-tint), var(--a-info-35-shade));
495
+ --a-info-bucket-4: light-dark(var(--a-info-80-tint), var(--a-info-20-shade));
496
+
440
497
  /* ══════════════════════════════════════════════════════════════
441
498
  FOCUS — canonical ring recipe shared by all form controls
442
499
  ══════════════════════════════════════════════════════════════
@@ -530,8 +587,8 @@
530
587
  equally against red / blue / dark / light underlays.
531
588
  ══════════════════════════════════════════════════════════════ */
532
589
 
533
- --a-chrome-light: oklch(1 0 0);
534
- --a-chrome-dark: oklch(0 0 0);
590
+ --a-chrome-light: var(--a-neutral-0-tint);
591
+ --a-chrome-dark: var(--a-neutral-0-shade);
535
592
  --a-chrome-border: var(--a-chrome-light);
536
593
  --a-chrome-ring-subtle: oklch(0 0 0 / 0.3);
537
594
  --a-chrome-shadow-soft: oklch(0 0 0 / 0.2);
package/styles/tokens.css CHANGED
@@ -141,21 +141,25 @@
141
141
  allows themes to control shadow strength (0 = flat, 2 = heavy). */
142
142
  --a-shadow-intensity: 1;
143
143
  --a-shadow-color: oklch(0.2 0.01 var(--a-neutral-hue));
144
+ /* Per-layer alpha is applied via the oklch(from …) relative-color
145
+ syntax — the legacy `var(--c) / alpha` form is invalid outside a
146
+ color function and silently resolved to `box-shadow: none` (latent
147
+ bug fixed 2026-04-27). */
144
148
  --a-shadow-sm:
145
- 0 1px 1px var(--a-shadow-color) / calc(0.04 * var(--a-shadow-intensity)),
146
- 0 1px 2px var(--a-shadow-color) / calc(0.03 * var(--a-shadow-intensity)),
147
- 0 2px 4px var(--a-shadow-color) / calc(0.02 * var(--a-shadow-intensity)),
148
- 0 0 1px var(--a-shadow-color) / calc(0.03 * var(--a-shadow-intensity));
149
+ 0 0.5px 0.5px oklch(from var(--a-shadow-color) l c h / calc(0.04 * var(--a-shadow-intensity))),
150
+ 0 0.5px 1.5px oklch(from var(--a-shadow-color) l c h / calc(0.03 * var(--a-shadow-intensity))),
151
+ 0 2px 4px oklch(from var(--a-shadow-color) l c h / calc(0.02 * var(--a-shadow-intensity))),
152
+ 0 0 1px oklch(from var(--a-shadow-color) l c h / calc(0.03 * var(--a-shadow-intensity)));
149
153
  --a-shadow-md:
150
- 0 1px 2px var(--a-shadow-color) / calc(0.04 * var(--a-shadow-intensity)),
151
- 0 2px 4px var(--a-shadow-color) / calc(0.03 * var(--a-shadow-intensity)),
152
- 0 4px 12px var(--a-shadow-color) / calc(0.03 * var(--a-shadow-intensity)),
153
- 0 0 1px var(--a-shadow-color) / calc(0.04 * var(--a-shadow-intensity));
154
+ 0 0.5px 1px oklch(from var(--a-shadow-color) l c h / calc(0.04 * var(--a-shadow-intensity))),
155
+ 0 1.5px 3px oklch(from var(--a-shadow-color) l c h / calc(0.03 * var(--a-shadow-intensity))),
156
+ 0 4px 12px oklch(from var(--a-shadow-color) l c h / calc(0.03 * var(--a-shadow-intensity))),
157
+ 0 0 1px oklch(from var(--a-shadow-color) l c h / calc(0.04 * var(--a-shadow-intensity)));
154
158
  --a-shadow-lg:
155
- 0 2px 4px var(--a-shadow-color) / calc(0.03 * var(--a-shadow-intensity)),
156
- 0 4px 8px var(--a-shadow-color) / calc(0.03 * var(--a-shadow-intensity)),
157
- 0 8px 24px var(--a-shadow-color) / calc(0.04 * var(--a-shadow-intensity)),
158
- 0 0 1px var(--a-shadow-color) / calc(0.04 * var(--a-shadow-intensity));
159
+ 0 2px 4px oklch(from var(--a-shadow-color) l c h / calc(0.03 * var(--a-shadow-intensity))),
160
+ 0 4px 8px oklch(from var(--a-shadow-color) l c h / calc(0.03 * var(--a-shadow-intensity))),
161
+ 0 8px 24px oklch(from var(--a-shadow-color) l c h / calc(0.04 * var(--a-shadow-intensity))),
162
+ 0 0 1px oklch(from var(--a-shadow-color) l c h / calc(0.04 * var(--a-shadow-intensity)));
159
163
 
160
164
  --a-icon-size: 1.00rem;
161
165
  --a-caret-size: 0.875rem;