@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.
- package/custom-elements.json +364 -364
- package/dist/components/hx-button/hx-button.d.ts.map +1 -1
- package/dist/components/hx-button/hx-button.styles.d.ts.map +1 -1
- package/dist/components/hx-button/index.js +1 -1
- package/dist/components/hx-side-nav/hx-nav-item.d.ts.map +1 -1
- package/dist/components/hx-side-nav/hx-nav-item.styles.d.ts.map +1 -1
- package/dist/components/hx-side-nav/hx-side-nav.d.ts.map +1 -1
- package/dist/components/hx-side-nav/hx-side-nav.styles.d.ts.map +1 -1
- package/dist/components/hx-side-nav/index.js +1 -1
- package/dist/components/hx-text-input/hx-text-input.d.ts +5 -5
- package/dist/components/hx-text-input/hx-text-input.d.ts.map +1 -1
- package/dist/components/hx-text-input/index.js +1 -1
- package/dist/components/hx-toast/hx-toast.styles.d.ts.map +1 -1
- package/dist/components/hx-toast/index.js +1 -1
- package/dist/css/helix-all.css +85 -63
- package/dist/css/helix-core.css +54 -34
- package/dist/css/helix-feedback.css +18 -13
- package/dist/css/helix-forms.css +4 -4
- package/dist/css/helix-navigation.css +9 -12
- package/dist/css/helix-tokens.css +43 -0
- package/dist/css/hx-button.css +54 -34
- package/dist/css/hx-side-nav.css +9 -12
- package/dist/css/hx-text-input.css +4 -4
- package/dist/css/hx-toast.css +18 -13
- package/dist/css/index.css +1 -1
- package/dist/css/manifest.json +25 -16
- package/dist/index.js +4 -4
- package/dist/shared/{hx-button-modUSOpY.js → hx-button-YeUzCe6e.js} +77 -58
- package/dist/shared/hx-button-YeUzCe6e.js.map +1 -0
- package/dist/shared/{hx-nav-item-D8xHLVOs.js → hx-nav-item-BqML5BAh.js} +45 -42
- package/dist/shared/hx-nav-item-BqML5BAh.js.map +1 -0
- package/dist/shared/{hx-text-input-B-caO5fI.js → hx-text-input-ClrrmoE1.js} +20 -21
- package/dist/shared/hx-text-input-ClrrmoE1.js.map +1 -0
- package/dist/shared/{toast-factory-DvDRAh0l.js → toast-factory-CIiZDZGZ.js} +59 -54
- package/dist/shared/toast-factory-CIiZDZGZ.js.map +1 -0
- package/figma-inventory.json +16 -16
- package/package.json +2 -2
- package/dist/shared/hx-button-modUSOpY.js.map +0 -1
- package/dist/shared/hx-nav-item-D8xHLVOs.js.map +0 -1
- package/dist/shared/hx-text-input-B-caO5fI.js.map +0 -1
- package/dist/shared/toast-factory-DvDRAh0l.js.map +0 -1
package/dist/css/manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generated": "2026-04-
|
|
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-
|
|
2457
|
-
"--hx-color-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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
|
|
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 {
|
|
6
|
-
import {
|
|
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
|
|
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-
|
|
99
|
-
|
|
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-
|
|
108
|
-
--hx-button-border-color: var(--hx-color-
|
|
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-
|
|
127
|
-
|
|
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.
|
|
133
|
-
|
|
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-
|
|
137
|
-
--hx-button-color: var(--hx-color-
|
|
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-
|
|
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.
|
|
164
|
-
neutral-0 for the darker hover fill.
|
|
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-
|
|
168
|
-
--hx-button-color: var(--hx-color-
|
|
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-
|
|
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-
|
|
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-
|
|
242
|
+
--hx-button-bg: var(--hx-color-action-primary-bg-inverted-hover, #6ab1b1);
|
|
236
243
|
}
|
|
237
244
|
|
|
238
|
-
/* Secondary inverted — white border and
|
|
245
|
+
/* Secondary inverted — white border and translucent hover fill */
|
|
239
246
|
:host([inverted]) .button--secondary {
|
|
240
|
-
--hx-button-border-color: var(--hx-
|
|
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-
|
|
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-
|
|
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-
|
|
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,
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
327
|
-
for (var o = i > 1 ? void 0 : i ?
|
|
328
|
-
(
|
|
329
|
-
return i && 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
|
|
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
|
|
355
|
-
return o.nodeType !== Node.TEXT_NODE || (((
|
|
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) || (
|
|
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
|
|
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
|
-
!
|
|
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
|
|
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
|
|
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 ?
|
|
473
|
+
return this.href !== void 0 ? d`
|
|
455
474
|
<a
|
|
456
475
|
part="button"
|
|
457
|
-
class=${
|
|
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
|
-
` :
|
|
488
|
+
` : d`
|
|
470
489
|
<button
|
|
471
490
|
part="button"
|
|
472
|
-
class=${
|
|
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 = [
|
|
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-
|
|
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;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"}
|