@helixui/library 3.2.0-next.76 → 3.2.0-next.78

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 (41) hide show
  1. package/custom-elements.json +364 -364
  2. package/dist/components/hx-button/hx-button.d.ts.map +1 -1
  3. package/dist/components/hx-button/hx-button.styles.d.ts.map +1 -1
  4. package/dist/components/hx-button/index.js +1 -1
  5. package/dist/components/hx-side-nav/hx-nav-item.d.ts.map +1 -1
  6. package/dist/components/hx-side-nav/hx-nav-item.styles.d.ts.map +1 -1
  7. package/dist/components/hx-side-nav/hx-side-nav.d.ts.map +1 -1
  8. package/dist/components/hx-side-nav/hx-side-nav.styles.d.ts.map +1 -1
  9. package/dist/components/hx-side-nav/index.js +1 -1
  10. package/dist/components/hx-text-input/hx-text-input.d.ts +5 -5
  11. package/dist/components/hx-text-input/hx-text-input.d.ts.map +1 -1
  12. package/dist/components/hx-text-input/index.js +1 -1
  13. package/dist/components/hx-toast/hx-toast.styles.d.ts.map +1 -1
  14. package/dist/components/hx-toast/index.js +1 -1
  15. package/dist/css/helix-all.css +85 -63
  16. package/dist/css/helix-core.css +54 -34
  17. package/dist/css/helix-feedback.css +18 -13
  18. package/dist/css/helix-forms.css +4 -4
  19. package/dist/css/helix-navigation.css +9 -12
  20. package/dist/css/helix-tokens.css +43 -0
  21. package/dist/css/hx-button.css +54 -34
  22. package/dist/css/hx-side-nav.css +9 -12
  23. package/dist/css/hx-text-input.css +4 -4
  24. package/dist/css/hx-toast.css +18 -13
  25. package/dist/css/index.css +1 -1
  26. package/dist/css/manifest.json +25 -16
  27. package/dist/index.js +4 -4
  28. package/dist/shared/{hx-button-modUSOpY.js → hx-button-YeUzCe6e.js} +77 -58
  29. package/dist/shared/hx-button-YeUzCe6e.js.map +1 -0
  30. package/dist/shared/{hx-nav-item-D8xHLVOs.js → hx-nav-item-BqML5BAh.js} +45 -42
  31. package/dist/shared/hx-nav-item-BqML5BAh.js.map +1 -0
  32. package/dist/shared/{hx-text-input-B-caO5fI.js → hx-text-input-ClrrmoE1.js} +20 -21
  33. package/dist/shared/hx-text-input-ClrrmoE1.js.map +1 -0
  34. package/dist/shared/{toast-factory-DvDRAh0l.js → toast-factory-CIiZDZGZ.js} +59 -54
  35. package/dist/shared/toast-factory-CIiZDZGZ.js.map +1 -0
  36. package/figma-inventory.json +16 -16
  37. package/package.json +2 -2
  38. package/dist/shared/hx-button-modUSOpY.js.map +0 -1
  39. package/dist/shared/hx-nav-item-D8xHLVOs.js.map +0 -1
  40. package/dist/shared/hx-text-input-B-caO5fI.js.map +0 -1
  41. package/dist/shared/toast-factory-DvDRAh0l.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  {
2
- "generated": "2026-04-25T17:34:50.956Z",
2
+ "generated": "2026-04-25T19:54:31.679Z",
3
3
  "components": [
4
4
  {
5
5
  "name": "hx-accordion",
@@ -255,21 +255,32 @@
255
255
  "--hx-button-border-color",
256
256
  "--hx-button-border-radius",
257
257
  "--hx-button-color",
258
+ "--hx-button-focus-ring-color",
258
259
  "--hx-button-font-family",
259
260
  "--hx-button-font-weight",
260
261
  "--hx-button-hover-bg",
261
262
  "--hx-button-inverted-color",
263
+ "--hx-color-action-danger-bg",
264
+ "--hx-color-action-danger-bg-hover",
265
+ "--hx-color-action-ghost-fg",
266
+ "--hx-color-action-primary-bg",
267
+ "--hx-color-action-primary-bg-hover",
268
+ "--hx-color-action-primary-bg-inverted-hover",
269
+ "--hx-color-action-secondary-border",
270
+ "--hx-color-action-secondary-fg",
271
+ "--hx-color-border-on-dark-default",
272
+ "--hx-color-border-on-dark-strong",
273
+ "--hx-color-border-on-dark-subtle",
262
274
  "--hx-color-border-strong",
263
- "--hx-color-error-500",
264
- "--hx-color-error-600",
265
275
  "--hx-color-neutral-0",
266
- "--hx-color-primary-400",
267
276
  "--hx-color-primary-500",
268
- "--hx-color-primary-600",
269
277
  "--hx-color-surface-raised",
270
278
  "--hx-color-surface-sunken",
279
+ "--hx-color-text-inverse",
271
280
  "--hx-color-text-on-error",
281
+ "--hx-color-text-on-error-strong",
272
282
  "--hx-color-text-on-primary",
283
+ "--hx-color-text-on-primary-strong",
273
284
  "--hx-color-text-primary",
274
285
  "--hx-duration-spinner",
275
286
  "--hx-filter-brightness-active",
@@ -285,11 +296,6 @@
285
296
  "--hx-line-height-tight",
286
297
  "--hx-opacity-disabled",
287
298
  "--hx-opacity-muted",
288
- "--hx-overlay-white-15",
289
- "--hx-overlay-white-20",
290
- "--hx-overlay-white-25",
291
- "--hx-overlay-white-50",
292
- "--hx-overlay-white-70",
293
299
  "--hx-size-10",
294
300
  "--hx-size-12",
295
301
  "--hx-space-1",
@@ -1756,7 +1762,6 @@
1756
1762
  "--hx-border-radius-sm",
1757
1763
  "--hx-border-width-thin",
1758
1764
  "--hx-color-border-strong",
1759
- "--hx-color-primary-400",
1760
1765
  "--hx-color-surface-inverse",
1761
1766
  "--hx-color-text-inverse",
1762
1767
  "--hx-focus-ring-color",
@@ -1766,9 +1771,11 @@
1766
1771
  "--hx-side-nav-border-color",
1767
1772
  "--hx-side-nav-collapsed-width",
1768
1773
  "--hx-side-nav-color",
1774
+ "--hx-side-nav-focus-ring-color",
1769
1775
  "--hx-side-nav-footer-padding",
1770
1776
  "--hx-side-nav-header-padding",
1771
1777
  "--hx-side-nav-toggle-color",
1778
+ "--hx-side-nav-toggle-hover-color",
1772
1779
  "--hx-side-nav-width",
1773
1780
  "--hx-space-14",
1774
1781
  "--hx-space-2",
@@ -2453,14 +2460,16 @@
2453
2460
  "tokens": [
2454
2461
  "--hx-border-radius-md",
2455
2462
  "--hx-border-radius-sm",
2456
- "--hx-color-error-600",
2457
- "--hx-color-neutral-0",
2458
- "--hx-color-primary-600",
2459
- "--hx-color-success-700",
2463
+ "--hx-color-surface-danger-strong",
2464
+ "--hx-color-surface-info-strong",
2460
2465
  "--hx-color-surface-inverse",
2466
+ "--hx-color-surface-success-strong",
2467
+ "--hx-color-surface-warning-strong",
2461
2468
  "--hx-color-text-inverse",
2469
+ "--hx-color-text-on-error-strong",
2470
+ "--hx-color-text-on-primary-strong",
2471
+ "--hx-color-text-on-success-strong",
2462
2472
  "--hx-color-text-on-warning",
2463
- "--hx-color-warning-500",
2464
2473
  "--hx-focus-ring-offset",
2465
2474
  "--hx-focus-ring-width",
2466
2475
  "--hx-font-family-sans",
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { H as v } from "./shared/hx-avatar-C9hOmlAb.js";
6
6
  import { H as E } from "./shared/hx-badge-CQXgOXJM.js";
7
7
  import { H as k } from "./shared/hx-banner-DT7Zn9Bo.js";
8
8
  import { H as B, a as P } from "./shared/hx-breadcrumb-item-COeYcB2x.js";
9
- import { H as M } from "./shared/hx-button-modUSOpY.js";
9
+ import { H as M } from "./shared/hx-button-YeUzCe6e.js";
10
10
  import { H as N } from "./shared/hx-button-group-BI-QBqmO.js";
11
11
  import { H as A } from "./shared/hx-card-CU1QnjNb.js";
12
12
  import { H as G, a as U } from "./shared/hx-carousel-item-BaE4hpLl.js";
@@ -53,7 +53,7 @@ import { H as So } from "./shared/hx-prose-BThYcASV.js";
53
53
  import { H as To, a as bo } from "./shared/hx-radio-N8xgDd_5.js";
54
54
  import { H as go } from "./shared/hx-rating-i2FL1WUN.js";
55
55
  import { H as vo } from "./shared/hx-select-vgaBo1Ai.js";
56
- import { H as Eo, a as Do } from "./shared/hx-nav-item-D8xHLVOs.js";
56
+ import { H as Eo, a as Do } from "./shared/hx-nav-item-BqML5BAh.js";
57
57
  import { H as wo } from "./shared/hx-skeleton-Cnieh5Uc.js";
58
58
  import { H as Po } from "./shared/hx-slider-ydBamYhd.js";
59
59
  import { H as Mo } from "./shared/hx-spinner-DL5AYr16.js";
@@ -70,11 +70,11 @@ import { H as tt, a as rt, b as at, c as it, d as st, e as xt, f as Ht } from ".
70
70
  import { H as nt, a as pt, b as mt } from "./shared/hx-tab-panel-CbkO9VKu.js";
71
71
  import { H as ct } from "./shared/hx-tag-CNSmdyaK.js";
72
72
  import { H as ht } from "./shared/hx-text-Bz_9fJ3J.js";
73
- import { F as St, H as yt } from "./shared/hx-text-input-B-caO5fI.js";
73
+ import { F as St, H as yt } from "./shared/hx-text-input-ClrrmoE1.js";
74
74
  import { H as bt } from "./shared/hx-textarea-D9O4U8cb.js";
75
75
  import { H as gt } from "./shared/hx-theme-BiyQ7UUK.js";
76
76
  import { H as vt } from "./shared/hx-time-picker-m0z4nFB-.js";
77
- import { H as Et, a as Dt, t as kt } from "./shared/toast-factory-DvDRAh0l.js";
77
+ import { H as Et, a as Dt, t as kt } from "./shared/toast-factory-CIiZDZGZ.js";
78
78
  import { H as Bt } from "./shared/hx-toggle-button-Dd8clXB4.js";
79
79
  import { H as Ft } from "./shared/hx-tooltip-nYOv9OLu.js";
80
80
  import { H as Lt } from "./shared/hx-top-nav-CchPYaiV.js";
@@ -1,12 +1,11 @@
1
- import { css as f, html as u, nothing as s } from "lit";
1
+ import { css as f, html as d, nothing as s } from "lit";
2
2
  import { property as a, customElement as v } from "lit/decorators.js";
3
- import { classMap as d } from "lit/directives/class-map.js";
3
+ import { classMap as b } from "lit/directives/class-map.js";
4
4
  import { ifDefined as c } from "lit/directives/if-defined.js";
5
- import { f as p } from "./forced-colors-CTEDFRGa.js";
6
- import { d as x } from "./dev-warn-YlwPHjtX.js";
7
- import { m } from "./aria-delegation-Doq6RRUy.js";
5
+ import { d as p } from "./dev-warn-YlwPHjtX.js";
6
+ import { m as x } from "./aria-delegation-Doq6RRUy.js";
8
7
  import { H as g } from "./helix-element-BNEYeiys.js";
9
- const y = f`
8
+ const m = f`
10
9
  :host {
11
10
  display: inline-block;
12
11
  }
@@ -54,10 +53,7 @@ const y = f`
54
53
 
55
54
  .button:focus-visible {
56
55
  outline: var(--hx-focus-ring-width, 2px) solid
57
- var(
58
- --hx-button-focus-ring-color,
59
- var(--hx-focus-ring-color, var(--hx-color-primary-500, #429797))
60
- );
56
+ var(--hx-button-focus-ring-color, var(--hx-focus-ring-color, #6ab1b1));
61
57
  outline-offset: var(--hx-focus-ring-offset, 2px);
62
58
  }
63
59
 
@@ -95,8 +91,11 @@ const y = f`
95
91
  /* ─── Style Variants ─── */
96
92
 
97
93
  .button--primary {
98
- --hx-button-bg: var(--hx-color-primary-500, #429797);
99
- --hx-button-color: var(--hx-color-text-on-primary, #ffffff);
94
+ --hx-button-bg: var(--hx-color-action-primary-bg, #429797);
95
+ /* Inline #0d1825 matches text.on-primary's resolved primitive (neutral-900);
96
+ cold-start without the semantic still paints AA-tuned dark-on-primary
97
+ rather than white-on-primary (3.43:1 fail). */
98
+ --hx-button-color: var(--hx-color-text-on-primary, #0d1825);
100
99
  --hx-button-border-color: transparent;
101
100
  }
102
101
 
@@ -104,8 +103,8 @@ const y = f`
104
103
  --hx-button-bg: transparent;
105
104
  /* primary-500 (#429797) text on white surface = 3.43:1 — fails AA.
106
105
  primary-600 (#0F7078) on white = 6.06:1 — AA pass. */
107
- --hx-button-color: var(--hx-color-primary-600, #0f7078);
108
- --hx-button-border-color: var(--hx-color-primary-600, #0f7078);
106
+ --hx-button-color: var(--hx-color-action-secondary-fg, #0f7078);
107
+ --hx-button-border-color: var(--hx-color-action-secondary-border, #0f7078);
109
108
  }
110
109
 
111
110
  .button--secondary:hover {
@@ -123,25 +122,29 @@ const y = f`
123
122
  }
124
123
 
125
124
  .button--danger {
126
- --hx-button-bg: var(--hx-color-error-500, #e5493e);
127
- --hx-button-color: var(--hx-color-text-on-error, #ffffff);
125
+ --hx-button-bg: var(--hx-color-action-danger-bg, #e5493e);
126
+ /* Inline #0d1825 matches text.on-error's resolved primitive (neutral-900);
127
+ cold-start without the semantic still paints AA-tuned dark-on-error
128
+ rather than white-on-error (3.92:1 fail). */
129
+ --hx-button-color: var(--hx-color-text-on-error, #0d1825);
128
130
  --hx-button-border-color: transparent;
129
131
  }
130
132
 
131
133
  /* on-error tokens are tuned for error-500 (neutral-900 on #E5493E ≈ 4.59:1).
132
- error-600 (#C92A2A) drops that to 2.25:1 — AA fail. Hold fg at neutral-0
133
- directly so darker hover fills stay legible. Mirrors hx-toast precedent
134
- (commit 300e21ab0). */
134
+ error-600 (#C92A2A) drops that to 2.25:1 — AA fail. text.on-error-strong
135
+ resolves to neutral-0 across modes (no dark flip) so the darker hover fill
136
+ stays legible. Mirrors hx-toast precedent (commit 300e21ab0); routed
137
+ through the semantic tier in 3.2.1 token-cascade remediation. */
135
138
  .button--danger:hover {
136
- --hx-button-bg: var(--hx-button-hover-bg, var(--hx-color-error-600, #c92a2a));
137
- --hx-button-color: var(--hx-color-neutral-0, #ffffff);
139
+ --hx-button-bg: var(--hx-button-hover-bg, var(--hx-color-action-danger-bg-hover, #c92a2a));
140
+ --hx-button-color: var(--hx-color-text-on-error-strong, #ffffff);
138
141
  }
139
142
 
140
143
  .button--ghost {
141
144
  --hx-button-bg: transparent;
142
145
  /* primary-500 (#429797) text on white surface = 3.43:1 — fails AA.
143
146
  primary-600 (#0F7078) on white = 6.06:1 — AA pass. */
144
- --hx-button-color: var(--hx-color-primary-600, #0f7078);
147
+ --hx-button-color: var(--hx-color-action-ghost-fg, #0f7078);
145
148
  --hx-button-border-color: transparent;
146
149
  }
147
150
 
@@ -160,12 +163,13 @@ const y = f`
160
163
  }
161
164
 
162
165
  /* on-primary token resolves to neutral-900 (#0D1825) — tuned for primary-500.
163
- primary-600 (#0F7078) drops the pair to 3.07:1 — AA fail. Pin fg at
164
- neutral-0 for the darker hover fill. Mirrors hx-toast precedent
165
- (commit 300e21ab0). */
166
+ primary-600 (#0F7078) drops the pair to 3.07:1 — AA fail. text.on-primary-strong
167
+ resolves to neutral-0 across modes (no dark flip) for the darker hover fill.
168
+ Mirrors hx-toast precedent (commit 300e21ab0); routed through the semantic
169
+ tier in 3.2.1 token-cascade remediation. */
166
170
  .button--primary:hover {
167
- --hx-button-bg: var(--hx-button-hover-bg, var(--hx-color-primary-600, #0f7078));
168
- --hx-button-color: var(--hx-color-neutral-0, #ffffff);
171
+ --hx-button-bg: var(--hx-button-hover-bg, var(--hx-color-action-primary-bg-hover, #0f7078));
172
+ --hx-button-color: var(--hx-color-text-on-primary-strong, #ffffff);
169
173
  }
170
174
 
171
175
  /* ─── Disabled ─── */
@@ -211,7 +215,7 @@ const y = f`
211
215
 
212
216
  /* Override text color and filter-based hover/active for all variants */
213
217
  :host([inverted]) .button {
214
- color: var(--hx-button-inverted-color, var(--hx-color-neutral-0, #ffffff));
218
+ color: var(--hx-button-inverted-color, var(--hx-color-text-inverse, #ffffff));
215
219
  filter: none;
216
220
  }
217
221
 
@@ -224,37 +228,42 @@ const y = f`
224
228
  }
225
229
 
226
230
  :host([inverted]) .button:focus-visible {
231
+ /* WCAG 1.4.11: focus indicator needs ≥3:1 against adjacent colors.
232
+ border-on-dark-default (overlay-white-30) ≈ 2.7:1 on neutral-900 — fails.
233
+ border-on-dark-strong (overlay-white-70) ≈ 5:1 — passes. */
227
234
  outline-color: var(
228
235
  --hx-button-inverted-focus-ring-color,
229
- var(--hx-overlay-white-50, rgba(255, 255, 255, 0.5))
236
+ var(--hx-color-border-on-dark-strong, rgba(255, 255, 255, 0.7))
230
237
  );
231
238
  }
232
239
 
233
240
  /* Primary inverted — slight transparent white overlay on hover */
234
241
  :host([inverted]) .button--primary:hover {
235
- --hx-button-bg: var(--hx-color-primary-400, #6ab1b1);
242
+ --hx-button-bg: var(--hx-color-action-primary-bg-inverted-hover, #6ab1b1);
236
243
  }
237
244
 
238
- /* Secondary inverted — white border and text */
245
+ /* Secondary inverted — white border and translucent hover fill */
239
246
  :host([inverted]) .button--secondary {
240
- --hx-button-border-color: var(--hx-overlay-white-70, rgba(255, 255, 255, 0.7));
247
+ --hx-button-border-color: var(--hx-color-border-on-dark-strong, rgba(255, 255, 255, 0.7));
241
248
  }
242
249
 
243
250
  :host([inverted]) .button--secondary:hover {
244
- --hx-button-bg: var(--hx-overlay-white-15, rgba(255, 255, 255, 0.15));
251
+ --hx-button-bg: var(--hx-color-border-on-dark-default, rgba(255, 255, 255, 0.15));
245
252
  }
246
253
 
247
- /* Tertiary inverted */
254
+ /* Tertiary inverted — resting at subtle (10%) lifts to default (30%) on hover
255
+ so the runtime hover delta is visually distinct, not collapsed onto a
256
+ single token. */
248
257
  :host([inverted]) .button--tertiary {
249
- --hx-button-bg: var(--hx-overlay-white-15, rgba(255, 255, 255, 0.15));
258
+ --hx-button-bg: var(--hx-color-border-on-dark-subtle, rgba(255, 255, 255, 0.1));
250
259
  --hx-button-border-color: transparent;
251
260
  }
252
261
 
253
262
  :host([inverted]) .button--tertiary:hover {
254
- --hx-button-bg: var(--hx-overlay-white-25, rgba(255, 255, 255, 0.25));
263
+ --hx-button-bg: var(--hx-color-border-on-dark-default, rgba(255, 255, 255, 0.3));
255
264
  }
256
265
 
257
- /* Ghost inverted — transparent base, white hover bg */
266
+ /* Ghost inverted — transparent base, translucent hover bg */
258
267
  :host([inverted]) .button--ghost {
259
268
  --hx-button-bg: transparent;
260
269
  --hx-button-border-color: transparent;
@@ -263,17 +272,17 @@ const y = f`
263
272
  :host([inverted]) .button--ghost:hover {
264
273
  --hx-button-bg: var(
265
274
  --hx-button-inverted-ghost-hover-bg,
266
- var(--hx-overlay-white-20, rgba(255, 255, 255, 0.2))
275
+ var(--hx-color-border-on-dark-default, rgba(255, 255, 255, 0.2))
267
276
  );
268
277
  }
269
278
 
270
279
  /* Outline inverted — white border */
271
280
  :host([inverted]) .button--outline {
272
- --hx-button-border-color: var(--hx-overlay-white-70, rgba(255, 255, 255, 0.7));
281
+ --hx-button-border-color: var(--hx-color-border-on-dark-strong, rgba(255, 255, 255, 0.7));
273
282
  }
274
283
 
275
284
  :host([inverted]) .button--outline:hover {
276
- --hx-button-bg: var(--hx-overlay-white-15, rgba(255, 255, 255, 0.15));
285
+ --hx-button-bg: var(--hx-color-border-on-dark-default, rgba(255, 255, 255, 0.15));
277
286
  }
278
287
 
279
288
  /* ─── Prefix / Suffix / Label ─── */
@@ -301,6 +310,16 @@ const y = f`
301
310
  border: 2px solid ButtonText;
302
311
  }
303
312
 
313
+ .button:hover {
314
+ /* Hover affordance must survive in HC. Highlight/HighlightText is the
315
+ OS-level "selected" pair, mirroring the forcedColorsInteractive mixin's
316
+ hover contract — kept inline since this component owns its bespoke HC
317
+ block (XOR rule). */
318
+ background-color: Highlight;
319
+ color: HighlightText;
320
+ border-color: Highlight;
321
+ }
322
+
304
323
  .button:focus-visible {
305
324
  outline: 3px solid Highlight;
306
325
  outline-offset: 2px;
@@ -323,12 +342,12 @@ const y = f`
323
342
  }
324
343
  }
325
344
  `;
326
- var _ = Object.defineProperty, w = Object.getOwnPropertyDescriptor, n = (t, r, h, i) => {
327
- for (var o = i > 1 ? void 0 : i ? w(r, h) : r, l = t.length - 1, b; l >= 0; l--)
328
- (b = t[l]) && (o = (i ? b(r, h, o) : b(o)) || o);
329
- return i && o && _(r, h, o), o;
345
+ var y = Object.defineProperty, _ = Object.getOwnPropertyDescriptor, n = (t, r, l, i) => {
346
+ for (var o = i > 1 ? void 0 : i ? _(r, l) : r, h = t.length - 1, u; h >= 0; h--)
347
+ (u = t[h]) && (o = (i ? u(r, l, o) : u(o)) || o);
348
+ return i && o && y(r, l, o), o;
330
349
  };
331
- let e = class extends m(g) {
350
+ let e = class extends x(g) {
332
351
  constructor() {
333
352
  super(...arguments), this.variant = "primary", this.size = "md", this.disabled = !1, this.loading = !1, this.type = "button", this.href = void 0, this.target = void 0, this.name = void 0, this.value = void 0, this.full = !1, this.inverted = !1, this.accessibleLabel = "", this._emptySlotWarnEmitted = !1;
334
353
  }
@@ -351,15 +370,15 @@ let e = class extends m(g) {
351
370
  const r = (i = this.shadowRoot) == null ? void 0 : i.querySelector("slot:not([name])");
352
371
  !((r == null ? void 0 : r.assignedNodes({ flatten: !0 })) ?? []).some(
353
372
  (o) => {
354
- var l;
355
- return o.nodeType !== Node.TEXT_NODE || (((l = o.textContent) == null ? void 0 : l.trim().length) ?? 0) > 0;
373
+ var h;
374
+ return o.nodeType !== Node.TEXT_NODE || (((h = o.textContent) == null ? void 0 : h.trim().length) ?? 0) > 0;
356
375
  }
357
376
  ) && !this._effectiveLabel && (this._emptySlotWarnEmitted = !0);
358
377
  }
359
378
  updated(t) {
360
379
  if (super.updated(t), t.has("variant")) {
361
380
  const r = [...e._VALID_VARIANTS];
362
- r.includes(this.variant) || (x(
381
+ r.includes(this.variant) || (p(
363
382
  "hx-button",
364
383
  `Invalid variant "${this.variant}". Expected one of: ${r.join(", ")}. Clamping to "primary".`
365
384
  ), this.variant = "primary");
@@ -368,11 +387,11 @@ let e = class extends m(g) {
368
387
  // ─── Slot Handlers ───
369
388
  /** @internal */
370
389
  _handleDefaultSlotChange(t) {
371
- const h = t.target.assignedNodes({ flatten: !0 }).some((i) => {
390
+ const l = t.target.assignedNodes({ flatten: !0 }).some((i) => {
372
391
  var o;
373
392
  return i.nodeType !== Node.TEXT_NODE || (((o = i.textContent) == null ? void 0 : o.trim().length) ?? 0) > 0;
374
393
  });
375
- !h && !this._effectiveLabel && this._emptySlotWarnEmitted, h && (this._emptySlotWarnEmitted = !1);
394
+ !l && !this._effectiveLabel && this._emptySlotWarnEmitted, l && (this._emptySlotWarnEmitted = !1);
376
395
  }
377
396
  // ─── Event Handling ───
378
397
  /**
@@ -398,7 +417,7 @@ let e = class extends m(g) {
398
417
  * @internal
399
418
  */
400
419
  _renderSpinner() {
401
- return u`
420
+ return d`
402
421
  <svg
403
422
  class="button__spinner"
404
423
  part="spinner"
@@ -430,7 +449,7 @@ let e = class extends m(g) {
430
449
  * @internal
431
450
  */
432
451
  _renderInner() {
433
- return u`
452
+ return d`
434
453
  ${this.loading ? this._renderSpinner() : s}
435
454
  <span part="prefix" class="button__prefix">
436
455
  <slot name="prefix"></slot>
@@ -451,10 +470,10 @@ let e = class extends m(g) {
451
470
  [`button--${this.size}`]: !0,
452
471
  "button--loading": this.loading
453
472
  };
454
- return this.href !== void 0 ? u`
473
+ return this.href !== void 0 ? d`
455
474
  <a
456
475
  part="button"
457
- class=${d(t)}
476
+ class=${b(t)}
458
477
  href=${this.disabled || this.loading ? s : c(this.href)}
459
478
  target=${c(this.target)}
460
479
  rel=${this.target === "_blank" ? "noopener noreferrer" : s}
@@ -466,10 +485,10 @@ let e = class extends m(g) {
466
485
  >
467
486
  ${this._renderInner()}
468
487
  </a>
469
- ` : u`
488
+ ` : d`
470
489
  <button
471
490
  part="button"
472
- class=${d(t)}
491
+ class=${b(t)}
473
492
  ?disabled=${this.disabled}
474
493
  type=${this.type}
475
494
  aria-label=${this._effectiveLabel || s}
@@ -481,7 +500,7 @@ let e = class extends m(g) {
481
500
  `;
482
501
  }
483
502
  };
484
- e.styles = [y, p];
503
+ e.styles = [m];
485
504
  e.formAssociated = !0;
486
505
  e._VALID_VARIANTS = [
487
506
  "primary",
@@ -533,4 +552,4 @@ e = n([
533
552
  export {
534
553
  e as H
535
554
  };
536
- //# sourceMappingURL=hx-button-modUSOpY.js.map
555
+ //# sourceMappingURL=hx-button-YeUzCe6e.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-button-YeUzCe6e.js","sources":["../../src/components/hx-button/hx-button.styles.ts","../../src/components/hx-button/hx-button.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixButtonStyles = css`\n :host {\n display: inline-block;\n }\n\n :host([disabled]) {\n pointer-events: none;\n opacity: var(--hx-opacity-disabled, 0.5);\n }\n\n :host([full]) {\n display: block;\n width: 100%;\n }\n\n :host([full]) .button {\n width: 100%;\n justify-content: center;\n }\n\n /* ─── Base Button ─── */\n\n .button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--hx-space-2, 0.5rem);\n border: var(--hx-border-width-thin, 1px) solid var(--hx-button-border-color, transparent);\n border-radius: var(--hx-button-border-radius, var(--hx-border-radius-md, 0.375rem));\n background-color: var(--hx-button-bg, var(--hx-color-primary-500, #429797));\n color: var(--hx-button-color, var(--hx-color-neutral-0, #ffffff));\n font-family: var(--hx-button-font-family, var(--hx-font-family-sans, sans-serif));\n font-weight: var(--hx-button-font-weight, var(--hx-font-weight-semibold, 600));\n line-height: var(--hx-line-height-tight, 1.25);\n cursor: pointer;\n transition:\n background-color var(--hx-transition-fast, 150ms ease),\n color var(--hx-transition-fast, 150ms ease),\n border-color var(--hx-transition-fast, 150ms ease),\n box-shadow var(--hx-transition-fast, 150ms ease);\n text-decoration: none;\n white-space: nowrap;\n user-select: none;\n -webkit-user-select: none;\n }\n\n .button:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-button-focus-ring-color, var(--hx-focus-ring-color, #6ab1b1));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n .button:hover {\n filter: brightness(var(--hx-filter-brightness-hover, 0.9));\n }\n\n .button:active {\n filter: brightness(var(--hx-filter-brightness-active, 0.8));\n }\n\n /* ─── Size Variants ─── */\n\n /* WCAG 2.5.5 (healthcare mandate): minimum 44px touch target for sm variant.\n min-height uses --hx-touch-target-min to guarantee the interactive area\n meets the threshold even though the visual size token is smaller. */\n .button--sm {\n padding: var(--hx-space-1, 0.25rem) var(--hx-space-3, 0.75rem);\n font-size: var(--hx-font-size-sm, 0.875rem);\n min-height: var(--hx-touch-target-min, 2.75rem);\n }\n\n .button--md {\n padding: var(--hx-space-2, 0.5rem) var(--hx-space-4, 1rem);\n font-size: var(--hx-font-size-md, 1rem);\n min-height: var(--hx-size-10, 2.5rem);\n }\n\n .button--lg {\n padding: var(--hx-space-3, 0.75rem) var(--hx-space-6, 1.5rem);\n font-size: var(--hx-font-size-lg, 1.125rem);\n min-height: var(--hx-size-12, 3rem);\n }\n\n /* ─── Style Variants ─── */\n\n .button--primary {\n --hx-button-bg: var(--hx-color-action-primary-bg, #429797);\n /* Inline #0d1825 matches text.on-primary's resolved primitive (neutral-900);\n cold-start without the semantic still paints AA-tuned dark-on-primary\n rather than white-on-primary (3.43:1 fail). */\n --hx-button-color: var(--hx-color-text-on-primary, #0d1825);\n --hx-button-border-color: transparent;\n }\n\n .button--secondary {\n --hx-button-bg: transparent;\n /* primary-500 (#429797) text on white surface = 3.43:1 — fails AA.\n primary-600 (#0F7078) on white = 6.06:1 — AA pass. */\n --hx-button-color: var(--hx-color-action-secondary-fg, #0f7078);\n --hx-button-border-color: var(--hx-color-action-secondary-border, #0f7078);\n }\n\n .button--secondary:hover {\n --hx-button-bg: var(--hx-button-hover-bg, var(--hx-color-surface-raised, #f5f8f3));\n }\n\n .button--tertiary {\n --hx-button-bg: var(--hx-color-surface-sunken, #ebeee9);\n --hx-button-color: var(--hx-color-text-primary, #0d1825);\n --hx-button-border-color: transparent;\n }\n\n .button--tertiary:hover {\n --hx-button-bg: var(--hx-button-hover-bg, var(--hx-color-surface-raised, #f5f8f3));\n }\n\n .button--danger {\n --hx-button-bg: var(--hx-color-action-danger-bg, #e5493e);\n /* Inline #0d1825 matches text.on-error's resolved primitive (neutral-900);\n cold-start without the semantic still paints AA-tuned dark-on-error\n rather than white-on-error (3.92:1 fail). */\n --hx-button-color: var(--hx-color-text-on-error, #0d1825);\n --hx-button-border-color: transparent;\n }\n\n /* on-error tokens are tuned for error-500 (neutral-900 on #E5493E ≈ 4.59:1).\n error-600 (#C92A2A) drops that to 2.25:1 — AA fail. text.on-error-strong\n resolves to neutral-0 across modes (no dark flip) so the darker hover fill\n stays legible. Mirrors hx-toast precedent (commit 300e21ab0); routed\n through the semantic tier in 3.2.1 token-cascade remediation. */\n .button--danger:hover {\n --hx-button-bg: var(--hx-button-hover-bg, var(--hx-color-action-danger-bg-hover, #c92a2a));\n --hx-button-color: var(--hx-color-text-on-error-strong, #ffffff);\n }\n\n .button--ghost {\n --hx-button-bg: transparent;\n /* primary-500 (#429797) text on white surface = 3.43:1 — fails AA.\n primary-600 (#0F7078) on white = 6.06:1 — AA pass. */\n --hx-button-color: var(--hx-color-action-ghost-fg, #0f7078);\n --hx-button-border-color: transparent;\n }\n\n .button--ghost:hover {\n --hx-button-bg: var(--hx-button-hover-bg, var(--hx-color-surface-raised, #f5f8f3));\n }\n\n .button--outline {\n --hx-button-bg: transparent;\n --hx-button-color: var(--hx-color-text-primary, #0d1825);\n --hx-button-border-color: var(--hx-color-border-strong, #8e9c98);\n }\n\n .button--outline:hover {\n --hx-button-bg: var(--hx-button-hover-bg, var(--hx-color-surface-raised, #f5f8f3));\n }\n\n /* on-primary token resolves to neutral-900 (#0D1825) — tuned for primary-500.\n primary-600 (#0F7078) drops the pair to 3.07:1 — AA fail. text.on-primary-strong\n resolves to neutral-0 across modes (no dark flip) for the darker hover fill.\n Mirrors hx-toast precedent (commit 300e21ab0); routed through the semantic\n tier in 3.2.1 token-cascade remediation. */\n .button--primary:hover {\n --hx-button-bg: var(--hx-button-hover-bg, var(--hx-color-action-primary-bg-hover, #0f7078));\n --hx-button-color: var(--hx-color-text-on-primary-strong, #ffffff);\n }\n\n /* ─── Disabled ─── */\n\n /* Note: opacity is applied on :host([disabled]) above — do NOT add opacity here.\n Stacking opacity on both :host and .button[disabled] would multiply to 0.25. */\n .button[disabled] {\n cursor: not-allowed;\n }\n\n /* ─── Loading State ─── */\n\n .button--loading {\n position: relative;\n cursor: wait;\n }\n\n .button__spinner {\n width: 1em;\n height: 1em;\n flex-shrink: 0;\n animation: hx-spin var(--hx-duration-spinner, 750ms) linear infinite;\n }\n\n @keyframes hx-spin {\n to {\n transform: rotate(360deg);\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .button {\n transition: none;\n }\n\n .button__spinner {\n animation: none;\n opacity: var(--hx-opacity-muted, 0.6);\n }\n }\n\n /* ─── Inverted Mode ─── */\n\n /* Override text color and filter-based hover/active for all variants */\n :host([inverted]) .button {\n color: var(--hx-button-inverted-color, var(--hx-color-text-inverse, #ffffff));\n filter: none;\n }\n\n :host([inverted]) .button:hover {\n filter: none;\n }\n\n :host([inverted]) .button:active {\n filter: none;\n }\n\n :host([inverted]) .button:focus-visible {\n /* WCAG 1.4.11: focus indicator needs ≥3:1 against adjacent colors.\n border-on-dark-default (overlay-white-30) ≈ 2.7:1 on neutral-900 — fails.\n border-on-dark-strong (overlay-white-70) ≈ 5:1 — passes. */\n outline-color: var(\n --hx-button-inverted-focus-ring-color,\n var(--hx-color-border-on-dark-strong, rgba(255, 255, 255, 0.7))\n );\n }\n\n /* Primary inverted — slight transparent white overlay on hover */\n :host([inverted]) .button--primary:hover {\n --hx-button-bg: var(--hx-color-action-primary-bg-inverted-hover, #6ab1b1);\n }\n\n /* Secondary inverted — white border and translucent hover fill */\n :host([inverted]) .button--secondary {\n --hx-button-border-color: var(--hx-color-border-on-dark-strong, rgba(255, 255, 255, 0.7));\n }\n\n :host([inverted]) .button--secondary:hover {\n --hx-button-bg: var(--hx-color-border-on-dark-default, rgba(255, 255, 255, 0.15));\n }\n\n /* Tertiary inverted — resting at subtle (10%) lifts to default (30%) on hover\n so the runtime hover delta is visually distinct, not collapsed onto a\n single token. */\n :host([inverted]) .button--tertiary {\n --hx-button-bg: var(--hx-color-border-on-dark-subtle, rgba(255, 255, 255, 0.1));\n --hx-button-border-color: transparent;\n }\n\n :host([inverted]) .button--tertiary:hover {\n --hx-button-bg: var(--hx-color-border-on-dark-default, rgba(255, 255, 255, 0.3));\n }\n\n /* Ghost inverted — transparent base, translucent hover bg */\n :host([inverted]) .button--ghost {\n --hx-button-bg: transparent;\n --hx-button-border-color: transparent;\n }\n\n :host([inverted]) .button--ghost:hover {\n --hx-button-bg: var(\n --hx-button-inverted-ghost-hover-bg,\n var(--hx-color-border-on-dark-default, rgba(255, 255, 255, 0.2))\n );\n }\n\n /* Outline inverted — white border */\n :host([inverted]) .button--outline {\n --hx-button-border-color: var(--hx-color-border-on-dark-strong, rgba(255, 255, 255, 0.7));\n }\n\n :host([inverted]) .button--outline:hover {\n --hx-button-bg: var(--hx-color-border-on-dark-default, rgba(255, 255, 255, 0.15));\n }\n\n /* ─── Prefix / Suffix / Label ─── */\n\n .button__prefix,\n .button__suffix {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n }\n\n .button__label {\n flex: 1 1 auto;\n }\n\n /* ─── High Contrast Mode (forced-colors) ─── */\n\n @media (forced-colors: active) {\n .button {\n /* Ensure button outline is visible in Windows High Contrast mode.\n ButtonText/ButtonFace are system colors recognized by the browser. */\n forced-color-adjust: none;\n background-color: ButtonFace;\n color: ButtonText;\n border: 2px solid ButtonText;\n }\n\n .button:hover {\n /* Hover affordance must survive in HC. Highlight/HighlightText is the\n OS-level \"selected\" pair, mirroring the forcedColorsInteractive mixin's\n hover contract — kept inline since this component owns its bespoke HC\n block (XOR rule). */\n background-color: Highlight;\n color: HighlightText;\n border-color: Highlight;\n }\n\n .button:focus-visible {\n outline: 3px solid Highlight;\n outline-offset: 2px;\n }\n\n .button[disabled] {\n background-color: ButtonFace;\n color: GrayText;\n border-color: GrayText;\n opacity: 1;\n }\n\n :host([disabled]) {\n opacity: 1;\n }\n\n .button--loading .button__spinner {\n /* Ensure spinner is visible in HCM */\n forced-color-adjust: auto;\n }\n }\n`;\n","import { html, nothing, type TemplateResult, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { HelixElement } from '../../base/index.js';\nimport { mixinDelegatesAria } from '../../mixins/index.js';\nimport { helixButtonStyles } from './hx-button.styles.js';\nimport { devWarn } from '../../utils/dev-warn.js';\n\n/** Detail for the hx-click event dispatched by hx-button. */\nexport interface HxButtonClickDetail {\n originalEvent: MouseEvent;\n}\n\n/**\n * A production-grade button component for user interaction. Supports multiple\n * visual variants, sizes, loading state, prefix/suffix slots, anchor rendering,\n * and full ElementInternals form association.\n *\n * @summary Primary interactive element for triggering actions and form submission.\n *\n * @tag hx-button\n *\n * @slot - Default slot for button label text or content.\n * @slot prefix - Icon or content rendered before the label.\n * @slot suffix - Icon or content rendered after the label.\n *\n * @fires {CustomEvent<{originalEvent: MouseEvent}>} hx-click - Dispatched when\n * the button is clicked and is neither disabled nor loading.\n *\n * @csspart button - The native button or anchor element.\n * @csspart label - The label text wrapper span.\n * @csspart prefix - The prefix slot container span.\n * @csspart suffix - The suffix slot container span.\n * @csspart spinner - The loading spinner SVG element.\n *\n * @cssprop [--hx-button-bg=var(--hx-color-primary-500)] - Button background color.\n * @cssprop [--hx-button-hover-bg] - Hover background color override. When set, overrides the variant default hover background from outside the shadow DOM.\n * @cssprop [--hx-button-color=var(--hx-color-neutral-0)] - Button text color.\n * @cssprop [--hx-button-border-color=transparent] - Button border color.\n * @cssprop [--hx-button-border-radius=var(--hx-border-radius-md)] - Button border radius.\n * @cssprop [--hx-button-font-family=var(--hx-font-family-sans)] - Button font family.\n * @cssprop [--hx-button-font-weight=var(--hx-font-weight-semibold)] - Button font weight.\n * @cssprop [--hx-button-focus-ring-color=var(--hx-focus-ring-color)] - Focus ring color.\n *\n * @cssprop [--hx-button-inverted-color=#ffffff] - Text color when inverted.\n * @cssprop [--hx-button-inverted-ghost-hover-bg=rgba(255,255,255,0.15)] - Ghost hover bg when inverted.\n * @cssprop [--hx-button-inverted-focus-ring-color=rgba(255,255,255,0.5)] - Focus ring color when inverted.\n * @cssprop [--hx-opacity-disabled] - Opacity.\n * @cssprop [--hx-space-2] - Spacing token.\n * @cssprop [--hx-border-width-thin] - Width.\n * @cssprop [--hx-border-radius-md] - CSS custom property.\n * @cssprop [--hx-color-primary-500] - Color.\n * @cssprop [--hx-color-neutral-0] - Color.\n * @cssprop [--hx-font-family-sans] - Font family.\n * @cssprop [--hx-font-weight-semibold] - Font weight.\n * @cssprop [--hx-line-height-tight] - Line height.\n * @cssprop [--hx-transition-fast] - Transition timing.\n * @cssprop [--hx-focus-ring-width] - Width.\n * @cssprop [--hx-focus-ring-color] - Color.\n * @cssprop [--hx-focus-ring-offset] - CSS custom property.\n * @cssprop [--hx-filter-brightness-hover] - CSS filter.\n * @cssprop [--hx-filter-brightness-active] - CSS filter.\n * @cssprop [--hx-space-1] - Spacing token.\n * @cssprop [--hx-space-3] - Spacing token.\n * @cssprop [--hx-font-size-sm] - Font size.\n * @cssprop [--hx-touch-target-min] - Minimum touch target size.\n * @cssprop [--hx-space-4] - Spacing token.\n * @cssprop [--hx-font-size-md] - Font size.\n * @cssprop [--hx-size-10] - Size token.\n * @cssprop [--hx-space-6] - Spacing token.\n * @cssprop [--hx-font-size-lg] - Font size.\n * @cssprop [--hx-size-12] - Size token.\n * @cssprop [--hx-color-primary-50] - Color.\n * @cssprop [--hx-color-neutral-100] - Color.\n * @cssprop [--hx-color-neutral-900] - Color.\n * @cssprop [--hx-color-neutral-200] - Color.\n * @cssprop [--hx-color-error-500] - Color.\n * @cssprop [--hx-color-error-600] - Color.\n * @cssprop [--hx-color-neutral-300] - Color.\n * @cssprop [--hx-color-neutral-50] - Color.\n * @cssprop [--hx-color-primary-600] - Color.\n * @cssprop [--hx-duration-spinner] - Animation duration.\n * @cssprop [--hx-opacity-muted] - Opacity.\n * @cssprop [--hx-overlay-white-50] - Overlay color.\n * @cssprop [--hx-color-primary-400] - Color.\n * @cssprop [--hx-overlay-white-70] - Overlay color.\n * @cssprop [--hx-overlay-white-15] - Overlay color.\n * @cssprop [--hx-overlay-white-25] - Overlay color.\n * @cssprop [--hx-overlay-white-20] - Overlay color.\n */\n@customElement('hx-button')\nexport class HelixButton extends mixinDelegatesAria(HelixElement) {\n // 3.2.1: forced-colors deference is owned by the bespoke @media block in\n // hx-button.styles.ts (covers loading/disabled/focus, not just the base).\n // Do NOT also compose forcedColorsInteractive here — the mixin's docstring\n // forbids dual composition (XOR rule) and the dual approach was flagged in\n // the token-cascade campaign findings.\n static override styles = [helixButtonStyles];\n\n // ─── Form Association ───\n\n /** @internal */\n static override formAssociated = true;\n\n // ─── Public Properties ───\n\n /**\n * Visual style variant of the button.\n * @attr variant\n */\n @property({ type: String, reflect: true })\n variant: 'primary' | 'secondary' | 'tertiary' | 'danger' | 'ghost' | 'outline' = 'primary';\n\n /**\n * Size of the button.\n * @attr hx-size\n */\n @property({ type: String, reflect: true, attribute: 'hx-size' })\n size: 'sm' | 'md' | 'lg' = 'md';\n\n /**\n * Whether the button is disabled. Prevents all interaction and form actions.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Whether the button is in a loading state. Shows spinner, prevents interaction,\n * and sets aria-busy. Does not set the disabled attribute.\n * @attr loading\n */\n @property({ type: Boolean, reflect: true })\n loading = false;\n\n /**\n * The type attribute for the underlying button element. Ignored when href is set.\n * @attr type\n */\n @property({ type: String })\n type: 'button' | 'submit' | 'reset' = 'button';\n\n /**\n * When set, renders an anchor element instead of a button.\n * @attr href\n */\n @property({ type: String })\n href: string | undefined = undefined;\n\n /**\n * Anchor target attribute. Only used when href is set.\n * @attr target\n */\n @property({ type: String })\n target: string | undefined = undefined;\n\n /**\n * Form field name submitted via ElementInternals.setFormValue on submit.\n * @attr name\n */\n @property({ type: String })\n name: string | undefined = undefined;\n\n /**\n * Form field value submitted via ElementInternals.setFormValue on submit.\n * @attr value\n */\n @property({ type: String })\n value: string | undefined = undefined;\n\n /**\n * When true, the button stretches to fill its container width.\n * Sets the host to `display: block` and the inner element to `width: 100%`.\n * @attr full\n */\n @property({ type: Boolean, reflect: true })\n full = false;\n\n /**\n * When true, flips button colors for placement on dark or gradient backgrounds.\n * Forces text to white and adjusts hover/focus ring colors across all variants.\n * @attr inverted\n */\n @property({ type: Boolean, reflect: true })\n inverted = false;\n\n /**\n * Accessible label for icon-only or text-less buttons.\n * Required when the button has no visible text content.\n *\n * Accepts both `accessible-label` and the standard `aria-label` HTML attribute.\n * `accessible-label` takes precedence when both are set.\n *\n * @attr accessible-label\n */\n @property({ type: String, attribute: 'accessible-label' })\n accessibleLabel: string = '';\n\n /**\n * Returns the effective label for the button, checking accessible-label first,\n * then the aria-label attribute, falling back to empty string.\n * @internal\n */\n private get _effectiveLabel(): string {\n return this.accessibleLabel?.trim() || this.ariaLabel?.trim() || '';\n }\n\n // ─── Form API ───\n\n protected override _onFormDisabled(disabled: boolean): void {\n this.disabled = disabled;\n }\n\n // ─── Lifecycle ───\n\n /** @internal */\n private static readonly _VALID_VARIANTS = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'danger',\n 'ghost',\n 'outline',\n ] as const;\n\n // Prevents double-warn on browsers that fire slotchange for empty initial slots.\n private _emptySlotWarnEmitted = false;\n\n override firstUpdated(changedProperties: PropertyValues<this>): void {\n super.firstUpdated(changedProperties);\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n const hasContent = (slot?.assignedNodes({ flatten: true }) ?? []).some(\n (n) => n.nodeType !== Node.TEXT_NODE || (n.textContent?.trim().length ?? 0) > 0,\n );\n if (!hasContent && !this._effectiveLabel) {\n this._emptySlotWarnEmitted = true;\n devWarn(\n 'hx-button',\n 'hx-button has no slot content and no accessible-label — button will have no accessible name.',\n );\n }\n }\n\n override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n if (changedProperties.has('variant')) {\n const validVariants: string[] = [...HelixButton._VALID_VARIANTS];\n if (!validVariants.includes(this.variant)) {\n devWarn(\n 'hx-button',\n `Invalid variant \"${this.variant}\". Expected one of: ${validVariants.join(', ')}. Clamping to \"primary\".`,\n );\n this.variant = 'primary';\n }\n }\n }\n\n // ─── Slot Handlers ───\n\n /** @internal */\n private _handleDefaultSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n const hasContent = slot\n .assignedNodes({ flatten: true })\n .some((n) => n.nodeType !== Node.TEXT_NODE || (n.textContent?.trim().length ?? 0) > 0);\n if (!hasContent && !this._effectiveLabel && !this._emptySlotWarnEmitted) {\n devWarn(\n 'hx-button',\n 'hx-button has no slot content and no accessible-label — button will have no accessible name.',\n );\n }\n // Only reset once content arrives so the guard stays armed for browsers\n // that fire a second slotchange for the same empty initial slot.\n if (hasContent) {\n this._emptySlotWarnEmitted = false;\n }\n }\n\n // ─── Event Handling ───\n\n /**\n * @private\n * @internal\n */\n private _handleClick(e: MouseEvent): void {\n if (this.disabled || this.loading) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n\n /**\n * Dispatched when the button is clicked.\n * @event hx-click\n */\n this.dispatchEvent(\n new CustomEvent<{ originalEvent: MouseEvent }>('hx-click', {\n bubbles: true,\n composed: true,\n detail: { originalEvent: e },\n }),\n );\n\n // Handle form submission/reset if form-associated and not in anchor mode\n if (this.href === undefined && this.type === 'submit' && this._internals.form) {\n if (this.name !== undefined && this.value !== undefined) {\n this._internals.setFormValue(this.value);\n }\n this._internals.form.requestSubmit();\n } else if (this.href === undefined && this.type === 'reset' && this._internals.form) {\n this._internals.form.reset();\n }\n }\n\n // ─── Render Helpers ───\n\n /**\n * @private\n * @internal\n */\n private _renderSpinner(): TemplateResult {\n return html`\n <svg\n class=\"button__spinner\"\n part=\"spinner\"\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n >\n <circle\n class=\"button__spinner-track\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n opacity=\"0.3\"\n />\n <path\n class=\"button__spinner-arc\"\n d=\"M12 2a10 10 0 0 1 10 10\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n </svg>\n `;\n }\n\n /**\n * @private\n * @internal\n */\n private _renderInner(): TemplateResult {\n return html`\n ${this.loading ? this._renderSpinner() : nothing}\n <span part=\"prefix\" class=\"button__prefix\">\n <slot name=\"prefix\"></slot>\n </span>\n <span part=\"label\" class=\"button__label\">\n <slot @slotchange=${this._handleDefaultSlotChange}></slot>\n </span>\n <span part=\"suffix\" class=\"button__suffix\">\n <slot name=\"suffix\"></slot>\n </span>\n `;\n }\n\n // ─── Render ───\n\n override render() {\n const classes = {\n button: true,\n [`button--${this.variant}`]: true,\n [`button--${this.size}`]: true,\n 'button--loading': this.loading,\n };\n\n if (this.href !== undefined) {\n return html`\n <a\n part=\"button\"\n class=${classMap(classes)}\n href=${this.disabled || this.loading ? nothing : ifDefined(this.href)}\n target=${ifDefined(this.target)}\n rel=${this.target === '_blank' ? 'noopener noreferrer' : nothing}\n aria-label=${this._effectiveLabel || nothing}\n aria-disabled=${this.disabled ? 'true' : nothing}\n aria-busy=${this.loading ? 'true' : nothing}\n tabindex=${this.disabled ? '-1' : nothing}\n @click=${this._handleClick}\n >\n ${this._renderInner()}\n </a>\n `;\n }\n\n return html`\n <button\n part=\"button\"\n class=${classMap(classes)}\n ?disabled=${this.disabled}\n type=${this.type}\n aria-label=${this._effectiveLabel || nothing}\n aria-busy=${this.loading ? 'true' : nothing}\n @click=${this._handleClick}\n >\n ${this._renderInner()}\n </button>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-button': HelixButton;\n }\n interface HTMLElementEventMap {\n 'hx-click': CustomEvent<{ originalEvent: MouseEvent }>;\n }\n}\n"],"names":["helixButtonStyles","css","HelixButton","mixinDelegatesAria","HelixElement","_a","_b","disabled","changedProperties","slot","n","validVariants","devWarn","e","hasContent","html","nothing","classes","classMap","ifDefined","__decorateClass","property","customElement"],"mappings":";;;;;;;AAEO,MAAMA,IAAoBC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;;;;AC2F1B,IAAMC,IAAN,cAA0BC,EAAmBC,CAAY,EAAE;AAAA,EAA3D,cAAA;AAAA,UAAA,GAAA,SAAA,GAoBL,KAAA,UAAiF,WAOjF,KAAA,OAA2B,MAO3B,KAAA,WAAW,IAQX,KAAA,UAAU,IAOV,KAAA,OAAsC,UAOtC,KAAA,OAA2B,QAO3B,KAAA,SAA6B,QAO7B,KAAA,OAA2B,QAO3B,KAAA,QAA4B,QAQ5B,KAAA,OAAO,IAQP,KAAA,WAAW,IAYX,KAAA,kBAA0B,IA8B1B,KAAQ,wBAAwB;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAvBhC,IAAY,kBAA0B;;AACpC,aAAOC,IAAA,KAAK,oBAAL,gBAAAA,EAAsB,aAAUC,IAAA,KAAK,cAAL,gBAAAA,EAAgB,WAAU;AAAA,EACnE;AAAA;AAAA,EAImB,gBAAgBC,GAAyB;AAC1D,SAAK,WAAWA;AAAA,EAClB;AAAA,EAiBS,aAAaC,GAA+C;;AACnE,UAAM,aAAaA,CAAiB;AACpC,UAAMC,KAAOJ,IAAA,KAAK,eAAL,gBAAAA,EAAiB,cAA+B;AAI7D,IAAI,GAHgBI,KAAA,gBAAAA,EAAM,cAAc,EAAE,SAAS,GAAA,OAAW,CAAA,GAAI;AAAA,MAChE,CAACC,MAAA;;AAAM,eAAAA,EAAE,aAAa,KAAK,gBAAcL,IAAAK,EAAE,gBAAF,gBAAAL,EAAe,OAAO,WAAU,KAAK;AAAA;AAAA,IAAA,KAE7D,CAAC,KAAK,oBACvB,KAAK,wBAAwB;AAAA,EAMjC;AAAA,EAES,QAAQG,GAA+C;AAE9D,QADA,MAAM,QAAQA,CAAiB,GAC3BA,EAAkB,IAAI,SAAS,GAAG;AACpC,YAAMG,IAA0B,CAAC,GAAGT,EAAY,eAAe;AAC/D,MAAKS,EAAc,SAAS,KAAK,OAAO,MACtCC;AAAA,QACE;AAAA,QACA,oBAAoB,KAAK,OAAO,uBAAuBD,EAAc,KAAK,IAAI,CAAC;AAAA,MAAA,GAEjF,KAAK,UAAU;AAAA,IAEnB;AAAA,EACF;AAAA;AAAA;AAAA,EAKQ,yBAAyBE,GAAgB;AAE/C,UAAMC,IADOD,EAAE,OAEZ,cAAc,EAAE,SAAS,IAAM,EAC/B,KAAK,CAACH;;AAAM,aAAAA,EAAE,aAAa,KAAK,gBAAcL,IAAAK,EAAE,gBAAF,gBAAAL,EAAe,OAAO,WAAU,KAAK;AAAA,KAAC;AACvF,IAAI,CAACS,KAAc,CAAC,KAAK,mBAAoB,KAAK,uBAQ9CA,MACF,KAAK,wBAAwB;AAAA,EAEjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,aAAaD,GAAqB;AACxC,QAAI,KAAK,YAAY,KAAK,SAAS;AACjC,MAAAA,EAAE,eAAA,GACFA,EAAE,gBAAA;AACF;AAAA,IACF;AAMA,SAAK;AAAA,MACH,IAAI,YAA2C,YAAY;AAAA,QACzD,SAAS;AAAA,QACT,UAAU;AAAA,QACV,QAAQ,EAAE,eAAeA,EAAA;AAAA,MAAE,CAC5B;AAAA,IAAA,GAIC,KAAK,SAAS,UAAa,KAAK,SAAS,YAAY,KAAK,WAAW,QACnE,KAAK,SAAS,UAAa,KAAK,UAAU,UAC5C,KAAK,WAAW,aAAa,KAAK,KAAK,GAEzC,KAAK,WAAW,KAAK,cAAA,KACZ,KAAK,SAAS,UAAa,KAAK,SAAS,WAAW,KAAK,WAAW,QAC7E,KAAK,WAAW,KAAK,MAAA;AAAA,EAEzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,iBAAiC;AACvC,WAAOE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0BT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,eAA+B;AACrC,WAAOA;AAAA,QACH,KAAK,UAAU,KAAK,eAAA,IAAmBC,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA,4BAK1B,KAAK,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMvD;AAAA;AAAA,EAIS,SAAS;AAChB,UAAMC,IAAU;AAAA,MACd,QAAQ;AAAA,MACR,CAAC,WAAW,KAAK,OAAO,EAAE,GAAG;AAAA,MAC7B,CAAC,WAAW,KAAK,IAAI,EAAE,GAAG;AAAA,MAC1B,mBAAmB,KAAK;AAAA,IAAA;AAG1B,WAAI,KAAK,SAAS,SACTF;AAAA;AAAA;AAAA,kBAGKG,EAASD,CAAO,CAAC;AAAA,iBAClB,KAAK,YAAY,KAAK,UAAUD,IAAUG,EAAU,KAAK,IAAI,CAAC;AAAA,mBAC5DA,EAAU,KAAK,MAAM,CAAC;AAAA,gBACzB,KAAK,WAAW,WAAW,wBAAwBH,CAAO;AAAA,uBACnD,KAAK,mBAAmBA,CAAO;AAAA,0BAC5B,KAAK,WAAW,SAASA,CAAO;AAAA,sBACpC,KAAK,UAAU,SAASA,CAAO;AAAA,qBAChC,KAAK,WAAW,OAAOA,CAAO;AAAA,mBAChC,KAAK,YAAY;AAAA;AAAA,YAExB,KAAK,cAAc;AAAA;AAAA,UAKpBD;AAAA;AAAA;AAAA,gBAGKG,EAASD,CAAO,CAAC;AAAA,oBACb,KAAK,QAAQ;AAAA,eAClB,KAAK,IAAI;AAAA,qBACH,KAAK,mBAAmBD,CAAO;AAAA,oBAChC,KAAK,UAAU,SAASA,CAAO;AAAA,iBAClC,KAAK,YAAY;AAAA;AAAA,UAExB,KAAK,cAAc;AAAA;AAAA;AAAA,EAG3B;AACF;AAhUad,EAMK,SAAS,CAACF,CAAiB;AANhCE,EAWK,iBAAiB;AAXtBA,EA6Ha,kBAAkB;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAhHAkB,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM;AAAA,GAnB9BnB,EAoBX,WAAA,WAAA,CAAA;AAOAkB,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM,WAAW,WAAW;AAAA,GA1BpDnB,EA2BX,WAAA,QAAA,CAAA;AAOAkB,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,SAAS,IAAM;AAAA,GAjC/BnB,EAkCX,WAAA,YAAA,CAAA;AAQAkB,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,SAAS,IAAM;AAAA,GAzC/BnB,EA0CX,WAAA,WAAA,CAAA;AAOAkB,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GAhDfnB,EAiDX,WAAA,QAAA,CAAA;AAOAkB,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GAvDfnB,EAwDX,WAAA,QAAA,CAAA;AAOAkB,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GA9DfnB,EA+DX,WAAA,UAAA,CAAA;AAOAkB,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GArEfnB,EAsEX,WAAA,QAAA,CAAA;AAOAkB,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GA5EfnB,EA6EX,WAAA,SAAA,CAAA;AAQAkB,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,SAAS,IAAM;AAAA,GApF/BnB,EAqFX,WAAA,QAAA,CAAA;AAQAkB,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,SAAS,IAAM;AAAA,GA5F/BnB,EA6FX,WAAA,YAAA,CAAA;AAYAkB,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,WAAW,oBAAoB;AAAA,GAxG9CnB,EAyGX,WAAA,mBAAA,CAAA;AAzGWA,IAANkB,EAAA;AAAA,EADNE,EAAc,WAAW;AAAA,GACbpB,CAAA;"}