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

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 (32) hide show
  1. package/custom-elements.json +27 -12
  2. package/dist/components/hx-button/index.js +1 -1
  3. package/dist/components/hx-side-nav/hx-nav-item.d.ts +7 -5
  4. package/dist/components/hx-side-nav/hx-nav-item.d.ts.map +1 -1
  5. package/dist/components/hx-side-nav/hx-nav-item.styles.d.ts.map +1 -1
  6. package/dist/components/hx-side-nav/hx-side-nav.d.ts +5 -4
  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-toast/hx-toast.d.ts +2 -2
  11. package/dist/components/hx-toast/index.js +1 -1
  12. package/dist/css/helix-all.css +16 -8
  13. package/dist/css/helix-core.css +5 -5
  14. package/dist/css/helix-feedback.css +3 -3
  15. package/dist/css/helix-navigation.css +8 -0
  16. package/dist/css/hx-button.css +5 -5
  17. package/dist/css/hx-side-nav.css +8 -0
  18. package/dist/css/hx-toast.css +3 -3
  19. package/dist/css/index.css +1 -1
  20. package/dist/css/manifest.json +3 -1
  21. package/dist/index.js +3 -3
  22. package/dist/shared/{hx-button-YeUzCe6e.js → hx-button-kWxjKqo-.js} +6 -6
  23. package/dist/shared/hx-button-kWxjKqo-.js.map +1 -0
  24. package/dist/shared/{hx-nav-item-BqML5BAh.js → hx-nav-item-CMyMv5Gv.js} +98 -60
  25. package/dist/shared/hx-nav-item-CMyMv5Gv.js.map +1 -0
  26. package/dist/shared/{toast-factory-CIiZDZGZ.js → toast-factory-Mz1GaaFF.js} +4 -4
  27. package/dist/shared/toast-factory-Mz1GaaFF.js.map +1 -0
  28. package/figma-inventory.json +71 -35
  29. package/package.json +2 -2
  30. package/dist/shared/hx-button-YeUzCe6e.js.map +0 -1
  31. package/dist/shared/hx-nav-item-BqML5BAh.js.map +0 -1
  32. package/dist/shared/toast-factory-CIiZDZGZ.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { css as A, html as c, nothing as h } from "lit";
2
- import { property as p, customElement as $, state as T } from "lit/decorators.js";
3
- import { H as E } from "./helix-element-BNEYeiys.js";
4
- import { c as I } from "./id-counter-DuX8vsui.js";
2
+ import { property as p, customElement as T, state as $ } from "lit/decorators.js";
3
+ import { H as I } from "./helix-element-BNEYeiys.js";
4
+ import { c as E } from "./id-counter-DuX8vsui.js";
5
5
  const S = A`
6
6
  :host {
7
7
  display: block;
@@ -168,22 +168,30 @@ const S = A`
168
168
  }
169
169
 
170
170
  .side-nav__toggle {
171
+ forced-color-adjust: none;
172
+ background-color: ButtonFace;
171
173
  color: ButtonText;
172
174
  border: 1px solid ButtonText;
173
175
  }
174
176
 
177
+ .side-nav__toggle:hover {
178
+ background-color: Highlight;
179
+ color: HighlightText;
180
+ border-color: Highlight;
181
+ }
182
+
175
183
  .side-nav__toggle:focus-visible {
176
184
  outline: 3px solid Highlight;
177
185
  outline-offset: 2px;
178
186
  }
179
187
  }
180
188
  `;
181
- var D = Object.defineProperty, N = Object.getOwnPropertyDescriptor, u = (e, r, o, i) => {
182
- for (var t = i > 1 ? void 0 : i ? N(r, o) : r, l = e.length - 1, n; l >= 0; l--)
183
- (n = e[l]) && (t = (i ? n(r, o, t) : n(t)) || t);
184
- return i && t && D(r, o, t), t;
189
+ var H = Object.defineProperty, B = Object.getOwnPropertyDescriptor, u = (e, o, a, i) => {
190
+ for (var t = i > 1 ? void 0 : i ? B(o, a) : o, l = e.length - 1, n; l >= 0; l--)
191
+ (n = e[l]) && (t = (i ? n(o, a, t) : n(t)) || t);
192
+ return i && t && H(o, a, t), t;
185
193
  };
186
- let x = class extends E {
194
+ let x = class extends I {
187
195
  constructor() {
188
196
  super(...arguments), this.collapsed = !1, this.label = "Main Navigation";
189
197
  }
@@ -199,11 +207,11 @@ let x = class extends E {
199
207
  */
200
208
  /** @internal */
201
209
  _propagateCollapsedToChildren() {
202
- var o;
203
- const e = (o = this.shadowRoot) == null ? void 0 : o.querySelector("slot:not([name])");
210
+ var a;
211
+ const e = (a = this.shadowRoot) == null ? void 0 : a.querySelector("slot:not([name])");
204
212
  if (!e) return;
205
- const r = e.assignedElements({ flatten: !0 }).filter((i) => i.tagName.toLowerCase() === "hx-nav-item");
206
- for (const i of r)
213
+ const o = e.assignedElements({ flatten: !0 }).filter((i) => i.tagName.toLowerCase() === "hx-nav-item");
214
+ for (const i of o)
207
215
  i instanceof HTMLElement && (this.collapsed ? i.setAttribute("data-collapsed", "") : i.removeAttribute("data-collapsed"));
208
216
  }
209
217
  /**
@@ -225,16 +233,16 @@ let x = class extends E {
225
233
  _handleKeydown(e) {
226
234
  var b, w, y, k, C;
227
235
  if (!["ArrowDown", "ArrowUp", "ArrowRight", "ArrowLeft", "Home", "End"].includes(e.key)) return;
228
- const o = (b = this.shadowRoot) == null ? void 0 : b.querySelector("slot:not([name])");
229
- if (!o) return;
230
- const i = o.assignedElements({ flatten: !0 }).filter(
231
- (a) => a.tagName.toLowerCase() === "hx-nav-item" && !a.hasAttribute("disabled")
236
+ const a = (b = this.shadowRoot) == null ? void 0 : b.querySelector("slot:not([name])");
237
+ if (!a) return;
238
+ const i = a.assignedElements({ flatten: !0 }).filter(
239
+ (r) => r.tagName.toLowerCase() === "hx-nav-item" && !r.hasAttribute("disabled")
232
240
  );
233
241
  if (i.length === 0) return;
234
242
  const t = [];
235
- for (const a of i)
236
- if (t.push(a), a.hasAttribute("expanded")) {
237
- const s = (w = a.shadowRoot) == null ? void 0 : w.querySelector('slot[name="children"]');
243
+ for (const r of i)
244
+ if (t.push(r), r.hasAttribute("expanded")) {
245
+ const s = (w = r.shadowRoot) == null ? void 0 : w.querySelector('slot[name="children"]');
238
246
  if (s) {
239
247
  const g = s.assignedElements({ flatten: !0 }).filter(
240
248
  (m) => m.tagName.toLowerCase() === "hx-nav-item" && !m.hasAttribute("disabled")
@@ -245,22 +253,22 @@ let x = class extends E {
245
253
  if (t.length === 0) return;
246
254
  const l = document.activeElement;
247
255
  let n = -1;
248
- for (let a = 0; a < t.length; a++) {
249
- const s = t[a];
256
+ for (let r = 0; r < t.length; r++) {
257
+ const s = t[r];
250
258
  if (s && (s === l || s.contains(l) || ((y = s.shadowRoot) == null ? void 0 : y.contains(l)) === !0)) {
251
- n = a;
259
+ n = r;
252
260
  break;
253
261
  }
254
262
  }
255
263
  if (e.key === "ArrowRight" || e.key === "ArrowLeft") {
256
264
  e.preventDefault();
257
- const a = n >= 0 ? t[n] : null;
258
- if (!a) return;
265
+ const r = n >= 0 ? t[n] : null;
266
+ if (!r) return;
259
267
  if (e.key === "ArrowRight") {
260
- if (a.hasAttribute("expanded") === !1 && a.querySelector('[slot="children"]'))
261
- a.setAttribute("expanded", ""), a.expanded = !0;
262
- else if (a.hasAttribute("expanded")) {
263
- const s = (k = a.shadowRoot) == null ? void 0 : k.querySelector('slot[name="children"]');
268
+ if (r.hasAttribute("expanded") === !1 && r.querySelector('[slot="children"]'))
269
+ r.setAttribute("expanded", ""), r.expanded = !0;
270
+ else if (r.hasAttribute("expanded")) {
271
+ const s = (k = r.shadowRoot) == null ? void 0 : k.querySelector('slot[name="children"]');
264
272
  if (s) {
265
273
  const g = s.assignedElements({ flatten: !0 }).find(
266
274
  (m) => m.tagName.toLowerCase() === "hx-nav-item" && !m.hasAttribute("disabled")
@@ -271,10 +279,10 @@ let x = class extends E {
271
279
  }
272
280
  }
273
281
  }
274
- } else if (a.hasAttribute("expanded"))
275
- a.removeAttribute("expanded"), a.expanded = !1;
282
+ } else if (r.hasAttribute("expanded"))
283
+ r.removeAttribute("expanded"), r.expanded = !1;
276
284
  else {
277
- const s = a.closest("hx-nav-item:not(:scope)") ?? ((C = a.parentElement) == null ? void 0 : C.closest("hx-nav-item")) ?? null;
285
+ const s = r.closest("hx-nav-item:not(:scope)") ?? ((C = r.parentElement) == null ? void 0 : C.closest("hx-nav-item")) ?? null;
278
286
  s && !s.hasAttribute("disabled") && s.focus();
279
287
  }
280
288
  return;
@@ -346,9 +354,9 @@ u([
346
354
  p({ type: String })
347
355
  ], x.prototype, "label", 2);
348
356
  x = u([
349
- $("hx-side-nav")
357
+ T("hx-side-nav")
350
358
  ], x);
351
- const H = A`
359
+ const N = A`
352
360
  :host {
353
361
  display: block;
354
362
  /* The host background must be a concrete color so that axe-core can
@@ -606,8 +614,38 @@ const H = A`
606
614
  /* ─── Forced Colors (Windows High Contrast) ─── */
607
615
 
608
616
  @media (forced-colors: active) {
617
+ /*
618
+ * Bespoke block — sole owner of forced-colors deference for hx-nav-item
619
+ * (the forcedColorsInteractive mixin is intentionally NOT composed; XOR
620
+ * rule in styles/forced-colors.ts). Mirrors the mixin's interactive
621
+ * contract (ButtonFace / ButtonText / Highlight on hover, GrayText on
622
+ * disabled) for the .nav-item__link interactive surface, then layers the
623
+ * component-specific active-state border and tooltip border on top.
624
+ */
625
+ .nav-item__link {
626
+ forced-color-adjust: none;
627
+ background-color: ButtonFace;
628
+ color: ButtonText;
629
+ border: 1px solid ButtonText;
630
+ }
631
+
632
+ .nav-item__link:hover {
633
+ background-color: Highlight;
634
+ color: HighlightText;
635
+ border-color: Highlight;
636
+ }
637
+
638
+ :host([disabled]) {
639
+ opacity: 1;
640
+ }
641
+
642
+ :host([disabled]) .nav-item__link {
643
+ color: GrayText;
644
+ border-color: GrayText;
645
+ }
646
+
609
647
  :host([active]) .nav-item__link {
610
- border: 1px solid Highlight;
648
+ border-color: Highlight;
611
649
  }
612
650
 
613
651
  .nav-item__link:focus-visible {
@@ -620,22 +658,22 @@ const H = A`
620
658
  }
621
659
  }
622
660
  `;
623
- var B = Object.defineProperty, L = Object.getOwnPropertyDescriptor, v = (e, r, o, i) => {
624
- for (var t = i > 1 ? void 0 : i ? L(r, o) : r, l = e.length - 1, n; l >= 0; l--)
625
- (n = e[l]) && (t = (i ? n(r, o, t) : n(t)) || t);
626
- return i && t && B(r, o, t), t;
661
+ var D = Object.defineProperty, j = Object.getOwnPropertyDescriptor, v = (e, o, a, i) => {
662
+ for (var t = i > 1 ? void 0 : i ? j(o, a) : o, l = e.length - 1, n; l >= 0; l--)
663
+ (n = e[l]) && (t = (i ? n(o, a, t) : n(t)) || t);
664
+ return i && t && D(o, a, t), t;
627
665
  };
628
- const j = I("hx-nav-item");
629
- let d = class extends E {
666
+ const L = E("hx-nav-item");
667
+ let d = class extends I {
630
668
  constructor() {
631
- super(...arguments), this._tooltipId = `${j()}-tooltip`, this.href = "", this.active = !1, this.expanded = !1, this.disabled = !1, this._hasChildren = !1, this._isCollapsed = !1;
669
+ super(...arguments), this._tooltipId = `${L()}-tooltip`, this.href = "", this.active = !1, this.expanded = !1, this.disabled = !1, this._hasChildren = !1, this._isCollapsed = !1;
632
670
  }
633
671
  // ─── Attribute Observer ───
634
672
  static get observedAttributes() {
635
673
  return [...super.observedAttributes, "data-collapsed"];
636
674
  }
637
- attributeChangedCallback(e, r, o) {
638
- super.attributeChangedCallback(e, r, o), e === "data-collapsed" && (this._isCollapsed = o !== null);
675
+ attributeChangedCallback(e, o, a) {
676
+ super.attributeChangedCallback(e, o, a), e === "data-collapsed" && (this._isCollapsed = a !== null);
639
677
  }
640
678
  // ─── Public API ───
641
679
  /**
@@ -645,22 +683,22 @@ let d = class extends E {
645
683
  * direct shadowRoot queries.
646
684
  */
647
685
  focus(e) {
648
- var o;
649
- const r = (o = this.shadowRoot) == null ? void 0 : o.querySelector('[part="link"]');
650
- r ? r.focus(e) : super.focus(e);
686
+ var a;
687
+ const o = (a = this.shadowRoot) == null ? void 0 : a.querySelector('[part="link"]');
688
+ o ? o.focus(e) : super.focus(e);
651
689
  }
652
690
  // ─── Slot Change Handler ───
653
691
  /** @internal */
654
692
  _onChildrenSlotChange(e) {
655
- const r = e.target;
656
- this._hasChildren = r.assignedNodes({ flatten: !0 }).length > 0;
693
+ const o = e.target;
694
+ this._hasChildren = o.assignedNodes({ flatten: !0 }).length > 0;
657
695
  }
658
696
  // ─── Private Helpers ───
659
697
  /** @internal */
660
698
  _getDirectText() {
661
699
  return Array.from(this.childNodes).filter((e) => e.nodeType === Node.TEXT_NODE).map((e) => {
662
- var r;
663
- return ((r = e.textContent) == null ? void 0 : r.trim()) ?? "";
700
+ var o;
701
+ return ((o = e.textContent) == null ? void 0 : o.trim()) ?? "";
664
702
  }).filter(Boolean).join(" ");
665
703
  }
666
704
  /** @internal */
@@ -679,7 +717,7 @@ let d = class extends E {
679
717
  }
680
718
  // ─── Render ───
681
719
  render() {
682
- const e = this._getDirectText(), r = c`
720
+ const e = this._getDirectText(), o = c`
683
721
  <span part="icon" class="nav-item__icon">
684
722
  <slot name="icon"></slot>
685
723
  </span>
@@ -691,7 +729,7 @@ let d = class extends E {
691
729
  </span>
692
730
  ${this._hasChildren ? this._renderExpandArrow() : h}
693
731
  ${this._isCollapsed ? c`<span id=${this._tooltipId} class="nav-item__tooltip" role="tooltip">${e}</span>` : h}
694
- `, o = this.href && !this._hasChildren ? c`<a
732
+ `, a = this.href && !this._hasChildren ? c`<a
695
733
  part="link"
696
734
  class="nav-item__link"
697
735
  href=${this.href}
@@ -700,7 +738,7 @@ let d = class extends E {
700
738
  aria-describedby=${this._isCollapsed ? this._tooltipId : h}
701
739
  tabindex=${this.disabled ? "-1" : "0"}
702
740
  >
703
- ${r}
741
+ ${o}
704
742
  </a>` : c`<button
705
743
  part="link"
706
744
  class="nav-item__link"
@@ -710,11 +748,11 @@ let d = class extends E {
710
748
  tabindex=${this.disabled ? "-1" : "0"}
711
749
  @click=${this._handleToggle}
712
750
  >
713
- ${r}
751
+ ${o}
714
752
  </button>`;
715
753
  return c`
716
754
  <div class="nav-item">
717
- ${o}
755
+ ${a}
718
756
  <div part="children" class="nav-item__children" role="group">
719
757
  <div class="nav-item__children-inner">
720
758
  <slot name="children" @slotchange=${this._onChildrenSlotChange}></slot>
@@ -724,7 +762,7 @@ let d = class extends E {
724
762
  `;
725
763
  }
726
764
  };
727
- d.styles = [H];
765
+ d.styles = [N];
728
766
  v([
729
767
  p({ type: String })
730
768
  ], d.prototype, "href", 2);
@@ -738,16 +776,16 @@ v([
738
776
  p({ type: Boolean, reflect: !0 })
739
777
  ], d.prototype, "disabled", 2);
740
778
  v([
741
- T()
779
+ $()
742
780
  ], d.prototype, "_hasChildren", 2);
743
781
  v([
744
- T()
782
+ $()
745
783
  ], d.prototype, "_isCollapsed", 2);
746
784
  d = v([
747
- $("hx-nav-item")
785
+ T("hx-nav-item")
748
786
  ], d);
749
787
  export {
750
788
  d as H,
751
789
  x as a
752
790
  };
753
- //# sourceMappingURL=hx-nav-item-BqML5BAh.js.map
791
+ //# sourceMappingURL=hx-nav-item-CMyMv5Gv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-nav-item-CMyMv5Gv.js","sources":["../../src/components/hx-side-nav/hx-side-nav.styles.ts","../../src/components/hx-side-nav/hx-side-nav.ts","../../src/components/hx-side-nav/hx-nav-item.styles.ts","../../src/components/hx-side-nav/hx-nav-item.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixSideNavStyles = css`\n :host {\n display: block;\n height: 100%;\n /* Mirror the nav background and text on the host so slotted light-DOM\n content (header, footer slots) inherits the correct dark surface color.\n Without this, axe-core cannot resolve the background for slotted nodes\n and evaluates their text against the page white background, producing\n false-positive color-contrast violations (WCAG 2.1 AA). */\n background-color: var(--hx-side-nav-bg, var(--hx-color-surface-inverse, #0d1825));\n color: var(--hx-side-nav-color, var(--hx-color-text-inverse, #ffffff));\n }\n\n * {\n box-sizing: border-box;\n }\n\n /* ─── Nav Container ─── */\n\n .side-nav {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: var(--hx-side-nav-width, 16rem);\n background-color: var(--hx-side-nav-bg, var(--hx-color-surface-inverse, #0d1825));\n color: var(--hx-side-nav-color, var(--hx-color-text-inverse, #ffffff));\n transition: width var(--hx-transition-normal, 300ms) ease;\n overflow: hidden;\n border-inline-end: var(--hx-border-width-thin, 1px) solid\n var(--hx-side-nav-border-color, var(--hx-color-border-strong, #8e9c98));\n }\n\n /* ─── Collapsed State ─── */\n\n :host([collapsed]) .side-nav {\n width: var(--hx-side-nav-collapsed-width, 3.5rem);\n }\n\n /* ─── Header ─── */\n\n .side-nav__header {\n display: flex;\n align-items: center;\n padding: var(--hx-side-nav-header-padding, var(--hx-space-4, 1rem));\n flex-shrink: 0;\n min-height: var(--hx-space-14, 3.5rem);\n border-bottom: var(--hx-border-width-thin, 1px) solid\n var(--hx-side-nav-border-color, var(--hx-color-border-strong, #8e9c98));\n overflow: hidden;\n }\n\n :host([collapsed]) .side-nav__header {\n justify-content: center;\n padding: var(--hx-space-3, 0.75rem);\n }\n\n /* ─── Body ─── */\n\n .side-nav__body {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n padding: var(--hx-space-2, 0.5rem) 0;\n }\n\n /* ─── Footer ─── */\n\n .side-nav__footer {\n display: flex;\n align-items: center;\n padding: var(--hx-side-nav-footer-padding, var(--hx-space-4, 1rem));\n flex-shrink: 0;\n min-height: var(--hx-space-14, 3.5rem);\n border-top: var(--hx-border-width-thin, 1px) solid\n var(--hx-side-nav-border-color, var(--hx-color-border-strong, #8e9c98));\n overflow: hidden;\n }\n\n :host([collapsed]) .side-nav__footer {\n justify-content: center;\n padding: var(--hx-space-3, 0.75rem);\n }\n\n /* ─── Toggle Button ─── */\n\n .side-nav__toggle {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--hx-space-8, 2rem);\n height: var(--hx-space-8, 2rem);\n margin-inline-start: auto;\n flex-shrink: 0;\n padding: 0;\n border: none;\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n background: transparent;\n color: var(--hx-side-nav-toggle-color, var(--hx-color-text-inverse, #ffffff));\n cursor: pointer;\n transition:\n background-color var(--hx-transition-fast, 150ms) ease,\n color var(--hx-transition-fast, 150ms) ease;\n }\n\n .side-nav__toggle:hover {\n background-color: var(\n --hx-color-border-on-dark-subtle,\n rgba(255, 255, 255, 0.1)\n ); /* fallback for browsers without color-mix() */\n color: var(--hx-side-nav-toggle-hover-color, var(--hx-color-text-inverse, #ffffff));\n }\n\n @supports (color: color-mix(in srgb, red 50%, blue)) {\n .side-nav__toggle:hover {\n background-color: color-mix(in srgb, currentColor 15%, transparent);\n }\n }\n\n .side-nav__toggle:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-side-nav-focus-ring-color, var(--hx-focus-ring-color, #6ab1b1));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n .side-nav__toggle svg {\n width: var(--hx-space-5, 1.25rem);\n height: var(--hx-space-5, 1.25rem);\n fill: currentColor;\n flex-shrink: 0;\n transition: transform var(--hx-transition-normal, 300ms) ease;\n }\n\n :host([collapsed]) .side-nav__toggle svg {\n transform: rotate(180deg);\n }\n\n @media (prefers-reduced-motion: reduce) {\n .side-nav {\n transition: none;\n }\n\n .side-nav__toggle {\n transition: none;\n }\n\n .side-nav__toggle svg {\n transition: none;\n }\n }\n\n /* ─── Forced Colors (Windows High Contrast) ─── */\n\n @media (forced-colors: active) {\n .side-nav {\n border-inline-end-color: CanvasText;\n }\n\n .side-nav__header {\n border-bottom-color: CanvasText;\n }\n\n .side-nav__footer {\n border-top-color: CanvasText;\n }\n\n .side-nav__toggle {\n forced-color-adjust: none;\n background-color: ButtonFace;\n color: ButtonText;\n border: 1px solid ButtonText;\n }\n\n .side-nav__toggle:hover {\n background-color: Highlight;\n color: HighlightText;\n border-color: Highlight;\n }\n\n .side-nav__toggle:focus-visible {\n outline: 3px solid Highlight;\n outline-offset: 2px;\n }\n }\n`;\n","import { html, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property } from 'lit/decorators.js';\nimport { HelixElement } from '../../base/index.js';\nimport { helixSideNavStyles } from './hx-side-nav.styles.js';\n\n/**\n * A collapsible left-side navigation panel with nested menu item support.\n * Designed for clinical portals, admin dashboards, and department navigation.\n *\n * @summary Collapsible side navigation panel for enterprise healthcare applications.\n *\n * @tag hx-side-nav\n *\n * @slot - Default slot for hx-nav-item children.\n * @slot header - Logo or branding content.\n * @slot footer - User profile or settings content.\n *\n * @fires {CustomEvent<{ collapsed: boolean }>} hx-collapse - Dispatched when the nav collapses to icon-only mode.\n * @fires {CustomEvent<{ collapsed: boolean }>} hx-expand - Dispatched when the nav expands to full width.\n *\n * @csspart nav - The outer nav element.\n * @csspart header - The header section.\n * @csspart body - The scrollable body section.\n * @csspart footer - The footer section.\n * @csspart toggle - The collapse/expand toggle button.\n *\n * @cssprop [--hx-side-nav-width=16rem] - Full expanded width.\n * @cssprop [--hx-side-nav-collapsed-width=3.5rem] - Collapsed icon-only width.\n * @cssprop [--hx-side-nav-bg=var(--hx-color-surface-inverse)] - Background color.\n * @cssprop [--hx-side-nav-color=var(--hx-color-text-inverse)] - Text color.\n * @cssprop [--hx-side-nav-border-color=var(--hx-color-border-strong)] - Border color.\n * @cssprop [--hx-side-nav-header-padding=var(--hx-space-4)] - Header padding.\n * @cssprop [--hx-side-nav-footer-padding=var(--hx-space-4)] - Footer padding.\n * @cssprop [--hx-side-nav-toggle-color=var(--hx-color-text-inverse)] - Toggle button icon color (resting).\n * @cssprop [--hx-side-nav-toggle-hover-color=var(--hx-color-text-inverse)] - Toggle button icon color on hover.\n * @cssprop [--hx-color-neutral-900] - Color.\n * @cssprop [--hx-color-neutral-100] - Color.\n * @cssprop [--hx-transition-normal] - Transition timing.\n * @cssprop [--hx-border-width-thin] - Width.\n * @cssprop [--hx-color-neutral-700] - Color.\n * @cssprop [--hx-space-4] - Spacing token.\n * @cssprop [--hx-space-14] - Spacing token.\n * @cssprop [--hx-space-3] - Spacing token.\n * @cssprop [--hx-space-2] - Spacing token.\n * @cssprop [--hx-space-8] - Spacing token.\n * @cssprop [--hx-border-radius-sm] - CSS custom property.\n * @cssprop [--hx-color-neutral-400] - Color.\n * @cssprop [--hx-transition-fast] - Transition timing.\n * @cssprop [--hx-overlay-white-10] - Overlay color.\n * @cssprop [--hx-focus-ring-width] - Width.\n * @cssprop [--hx-focus-ring-color] - Color.\n * @cssprop [--hx-color-primary-400] - Color.\n * @cssprop [--hx-focus-ring-offset] - CSS custom property.\n * @cssprop [--hx-space-5] - Spacing token.\n */\n@customElement('hx-side-nav')\nexport class HelixSideNav extends HelixElement {\n // 3.2.1: forced-colors deference is owned by the bespoke @media block in\n // hx-side-nav.styles.ts (toggle button, header/footer borders). Do NOT also\n // compose forcedColorsInteractive — XOR rule per the mixin docstring.\n static override styles = [helixSideNavStyles];\n\n // ─── Properties ───\n\n /**\n * When true, the nav collapses to show icons only.\n * @attr collapsed\n */\n @property({ type: Boolean, reflect: true })\n collapsed = false;\n\n /**\n * The accessible label for the nav landmark.\n * @attr label\n */\n @property({ type: String })\n label = 'Main Navigation';\n\n // ─── Lifecycle ───\n\n override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n if (changedProperties.has('collapsed')) {\n this._propagateCollapsedToChildren();\n }\n }\n\n // ─── Collapsed State Propagation ───\n\n /**\n * Propagates the collapsed state to all slotted hx-nav-item children by\n * setting or removing the `data-collapsed` attribute. This allows child\n * items to respond to collapsed mode via their CSS selectors.\n */\n /** @internal */\n private _propagateCollapsedToChildren(): void {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n if (!slot) return;\n\n const navItems = slot\n .assignedElements({ flatten: true })\n .filter((el) => el.tagName.toLowerCase() === 'hx-nav-item');\n\n for (const item of navItems) {\n if (!(item instanceof HTMLElement)) continue;\n if (this.collapsed) {\n item.setAttribute('data-collapsed', '');\n } else {\n item.removeAttribute('data-collapsed');\n }\n }\n }\n\n /**\n * Handles the default slot's slotchange event so that if items are added\n * after initial render, they immediately receive the correct collapsed state.\n */\n /** @internal */\n private _onDefaultSlotChange(): void {\n this._propagateCollapsedToChildren();\n }\n\n // ─── Keyboard Navigation ───\n\n /**\n * Implements roving tabindex-style ArrowUp/ArrowDown keyboard navigation\n * among direct hx-nav-item children in the body slot. Disabled items are\n * skipped. Focus is applied to the interactive element inside the shadow DOM\n * of each item (anchor or button with part=\"link\").\n */\n /** @internal */\n private _handleKeydown(e: KeyboardEvent): void {\n const validKeys = ['ArrowDown', 'ArrowUp', 'ArrowRight', 'ArrowLeft', 'Home', 'End'];\n if (!validKeys.includes(e.key)) return;\n\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n if (!slot) return;\n\n const topLevelItems = slot\n .assignedElements({ flatten: true })\n .filter(\n (el): el is HTMLElement =>\n el.tagName.toLowerCase() === 'hx-nav-item' && !el.hasAttribute('disabled'),\n );\n\n if (topLevelItems.length === 0) return;\n\n // Build a flattened list of navigable items: direct children plus visible\n // child items from expanded parent items (per ARIA APG tree pattern).\n const navItems: HTMLElement[] = [];\n for (const item of topLevelItems) {\n navItems.push(item);\n // If this item is expanded, include its non-disabled children\n if (item.hasAttribute('expanded')) {\n const childrenSlot =\n item.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"children\"]');\n if (childrenSlot) {\n const childItems = childrenSlot\n .assignedElements({ flatten: true })\n .filter(\n (el): el is HTMLElement =>\n el.tagName.toLowerCase() === 'hx-nav-item' && !el.hasAttribute('disabled'),\n );\n navItems.push(...childItems);\n }\n }\n }\n\n if (navItems.length === 0) return;\n\n // Find which item currently contains focus\n const activeEl = document.activeElement;\n let currentIndex = -1;\n for (let i = 0; i < navItems.length; i++) {\n const item = navItems[i];\n if (!item) continue;\n if (\n item === activeEl ||\n item.contains(activeEl) ||\n item.shadowRoot?.contains(activeEl) === true\n ) {\n currentIndex = i;\n break;\n }\n }\n\n // Handle ArrowRight/ArrowLeft for expand/collapse (ARIA APG tree pattern)\n if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') {\n e.preventDefault();\n const currentItem = currentIndex >= 0 ? navItems[currentIndex] : null;\n if (!currentItem) return;\n\n if (e.key === 'ArrowRight') {\n // If the item has children and is collapsed, expand it\n if (\n currentItem.hasAttribute('expanded') === false &&\n currentItem.querySelector('[slot=\"children\"]')\n ) {\n currentItem.setAttribute('expanded', '');\n (currentItem as HTMLElement & { expanded?: boolean }).expanded = true;\n } else if (currentItem.hasAttribute('expanded')) {\n // Already expanded: move focus to first child item\n const childrenSlot =\n currentItem.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"children\"]');\n if (childrenSlot) {\n const firstChild = childrenSlot\n .assignedElements({ flatten: true })\n .find(\n (el): el is HTMLElement =>\n el.tagName.toLowerCase() === 'hx-nav-item' && !el.hasAttribute('disabled'),\n );\n if (firstChild) {\n firstChild.focus();\n return;\n }\n }\n }\n } else {\n // ArrowLeft: if expanded, collapse; if collapsed or non-expandable, find parent\n if (currentItem.hasAttribute('expanded')) {\n currentItem.removeAttribute('expanded');\n (currentItem as HTMLElement & { expanded?: boolean }).expanded = false;\n } else {\n // Move focus to parent item if this item is a child in another item's slot\n const parentNavItem =\n currentItem.closest<HTMLElement>('hx-nav-item:not(:scope)') ??\n currentItem.parentElement?.closest<HTMLElement>('hx-nav-item') ??\n null;\n if (parentNavItem && !parentNavItem.hasAttribute('disabled')) {\n parentNavItem.focus();\n }\n }\n }\n return;\n }\n\n e.preventDefault();\n\n let nextIndex: number;\n if (e.key === 'ArrowDown') {\n nextIndex = currentIndex < navItems.length - 1 ? currentIndex + 1 : 0;\n } else if (e.key === 'ArrowUp') {\n nextIndex = currentIndex > 0 ? currentIndex - 1 : navItems.length - 1;\n } else if (e.key === 'Home') {\n nextIndex = 0;\n } else {\n nextIndex = navItems.length - 1;\n }\n\n const targetItem = navItems[nextIndex];\n if (!targetItem) return;\n // WCAG 2.1.1: call the public focus() method on the nav item rather than\n // piercing its Shadow DOM directly. hx-nav-item.focus() delegates to the\n // internal [part=\"link\"] element, preserving shadow encapsulation.\n targetItem.focus();\n }\n\n // ─── Event Handling ───\n\n /** @internal */\n private _handleToggle(): void {\n this.collapsed = !this.collapsed;\n\n if (this.collapsed) {\n /**\n * Dispatched when the nav collapses to icon-only mode.\n * @event hx-collapse\n */\n this.dispatchEvent(\n new CustomEvent<{ collapsed: boolean }>('hx-collapse', {\n bubbles: true,\n composed: true,\n detail: { collapsed: true },\n }),\n );\n } else {\n /**\n * Dispatched when the nav expands to full width.\n * @event hx-expand\n */\n this.dispatchEvent(\n new CustomEvent<{ collapsed: boolean }>('hx-expand', {\n bubbles: true,\n composed: true,\n detail: { collapsed: false },\n }),\n );\n }\n }\n\n // ─── Render ───\n\n /** @internal */\n private _renderToggleIcon() {\n return html`<svg viewBox=\"0 0 20 20\" aria-hidden=\"true\">\n <path\n d=\"M12.79 5.23a.75.75 0 01-.02 1.06L8.832 10l3.938 3.71a.75.75 0 11-1.04 1.08l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 011.06.02z\"\n />\n </svg>`;\n }\n\n override render() {\n return html`\n <nav part=\"nav\" class=\"side-nav\" aria-label=${this.label}>\n <div part=\"header\" class=\"side-nav__header\">\n <slot name=\"header\"></slot>\n <button\n part=\"toggle\"\n class=\"side-nav__toggle\"\n aria-label=${this.collapsed ? 'Expand navigation' : 'Collapse navigation'}\n aria-expanded=${!this.collapsed}\n @click=${this._handleToggle}\n >\n ${this._renderToggleIcon()}\n </button>\n </div>\n\n <div part=\"body\" class=\"side-nav__body\" id=\"side-nav-body\" @keydown=${this._handleKeydown}>\n <slot @slotchange=${this._onDefaultSlotChange}></slot>\n </div>\n\n <div part=\"footer\" class=\"side-nav__footer\">\n <slot name=\"footer\"></slot>\n </div>\n </nav>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-side-nav': HelixSideNav;\n }\n}\n\nexport type { HelixSideNav as HxSideNav };\n","import { css } from 'lit';\n\nexport const helixNavItemStyles = css`\n :host {\n display: block;\n /* The host background must be a concrete color so that axe-core can\n resolve text contrast ratios for shadow-DOM content correctly.\n surface-inverse + text-inverse flip per mode: dark-on-light in dark\n mode, light-on-dark in light mode. */\n background-color: var(--hx-nav-item-host-bg, var(--hx-color-surface-inverse, #0d1825));\n color: var(--hx-nav-item-color, var(--hx-color-text-inverse, #ffffff));\n }\n\n * {\n box-sizing: border-box;\n }\n\n /* ─── Nav Item ─── */\n\n .nav-item {\n display: flex;\n flex-direction: column;\n }\n\n /* ─── Link / Button ─── */\n\n .nav-item__link {\n display: flex;\n align-items: center;\n gap: var(--hx-space-3, 0.75rem);\n padding: var(--hx-nav-item-padding, var(--hx-space-2, 0.5rem) var(--hx-space-4, 1rem));\n min-height: var(--hx-space-10, 2.5rem);\n text-decoration: none;\n color: var(--hx-nav-item-color, var(--hx-color-text-inverse, #ffffff));\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n margin: 0 var(--hx-space-2, 0.5rem);\n transition:\n background-color var(--hx-transition-fast, 150ms) ease,\n color var(--hx-transition-fast, 150ms) ease;\n white-space: nowrap;\n overflow: hidden;\n cursor: pointer;\n font-family: var(--hx-nav-item-font-family, var(--hx-font-family-sans, sans-serif));\n font-size: var(--hx-font-size-sm, 0.875rem);\n font-weight: var(--hx-font-weight-medium, 500);\n line-height: var(--hx-line-height-normal, 1.5);\n position: relative;\n border: none;\n background: transparent;\n width: calc(100% - var(--hx-space-4, 1rem));\n text-align: start;\n }\n\n /* Link variant */\n a.nav-item__link {\n display: flex;\n }\n\n .nav-item__link:hover {\n background-color: var(\n --hx-nav-item-hover-bg,\n var(--hx-overlay-white-8, rgba(255, 255, 255, 0.08))\n ); /* fallback for browsers without color-mix() */\n color: var(--hx-nav-item-hover-color, var(--hx-color-text-inverse, #ffffff));\n }\n\n @supports (color: color-mix(in srgb, red 50%, blue)) {\n .nav-item__link:hover {\n background-color: var(\n --hx-nav-item-hover-bg,\n color-mix(in srgb, currentColor 10%, transparent)\n );\n }\n }\n\n .nav-item__link:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-nav-item-focus-ring-color, var(--hx-focus-ring-color, #6ab1b1));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n /* ─── Active State ─── */\n\n :host([active]) .nav-item__link {\n /* Active state sits on the darker primary-600 (#0F7078) fill. White text\n (#ffffff) on primary-600 = 5.39:1 WCAG AA pass. text-on-primary now\n resolves to neutral-900 (intended for the lighter primary-500 surface)\n which would fail here. text.on-primary-strong holds at neutral-0 across\n modes (no dark flip) so the active fg stays AA. 3.2.1: routed through\n the action.* / on-{role}-strong semantic tier per token-cascade\n remediation (no more bare primary-600 / neutral-0 consumption). */\n background-color: var(\n --hx-nav-item-active-bg,\n var(--hx-color-action-primary-bg-hover, #0f7078)\n );\n color: var(--hx-nav-item-active-color, var(--hx-color-text-on-primary-strong, #ffffff));\n }\n\n :host([active]) .nav-item__link:hover {\n /* text.on-primary-strong (#ffffff) on primary-700 (#0F6363) = WCAG AA ✓ */\n background-color: var(\n --hx-nav-item-active-hover-bg,\n var(--hx-color-action-primary-bg-active, #0f6363)\n );\n }\n\n /* ─── Disabled State ─── */\n\n :host([disabled]) .nav-item__link {\n opacity: var(--hx-opacity-disabled, 0.5);\n pointer-events: none;\n cursor: not-allowed;\n }\n\n /* ─── Icon ─── */\n\n .nav-item__icon {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: var(--hx-space-5, 1.25rem);\n height: var(--hx-space-5, 1.25rem);\n }\n\n /* ─── Label ─── */\n\n .nav-item__label {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n transition: opacity var(--hx-transition-fast, 150ms) ease;\n }\n\n /* ─── Badge ─── */\n\n .nav-item__badge {\n margin-inline-start: auto;\n flex-shrink: 0;\n }\n\n /* ─── Expand Arrow ─── */\n\n .nav-item__arrow {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n margin-inline-start: auto;\n width: var(--hx-space-5, 1.25rem);\n height: var(--hx-space-5, 1.25rem);\n transition: transform var(--hx-transition-normal, 300ms) ease;\n }\n\n .nav-item__arrow svg {\n width: var(--hx-space-4, 1rem);\n height: var(--hx-space-4, 1rem);\n fill: currentColor;\n }\n\n :host([expanded]) .nav-item__arrow {\n transform: rotate(90deg);\n }\n\n /* ─── Children (sub-nav) ─── */\n\n .nav-item__children {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows var(--hx-transition-normal, 300ms ease);\n overflow: hidden;\n }\n\n :host([expanded]) .nav-item__children {\n grid-template-rows: 1fr;\n }\n\n .nav-item__children-inner {\n overflow: hidden;\n min-height: 0;\n display: flex;\n flex-direction: column;\n padding-inline-start: var(--hx-space-6, 1.5rem);\n }\n\n /* ─── Tooltip (collapsed mode) ─── */\n\n .nav-item__tooltip {\n position: absolute;\n left: calc(100% + var(--hx-space-2, 0.5rem));\n top: 50%;\n transform: translateY(-50%);\n /* tooltip is an inverted surface — flips per mode via surface-inverse /\n text-inverse. 3.2.1: wrapped with component-tier slots so consumers can\n theme tooltip surface/text without overriding the global semantics. */\n background-color: var(--hx-nav-item-tooltip-bg, var(--hx-color-surface-inverse, #0d1825));\n color: var(--hx-nav-item-tooltip-color, var(--hx-color-text-inverse, #ffffff));\n padding: var(--hx-space-1, 0.25rem) var(--hx-space-2, 0.5rem);\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n font-size: var(--hx-font-size-xs, 0.75rem);\n white-space: nowrap;\n pointer-events: none;\n opacity: 0;\n transition: opacity var(--hx-transition-fast, 150ms) ease;\n z-index: var(--hx-z-index-tooltip, 1600);\n box-shadow: var(--hx-shadow-md, 0 2px 8px rgb(0 0 0 / 0.2));\n }\n\n :host([data-collapsed]) .nav-item__link:hover .nav-item__tooltip,\n :host([data-collapsed]) .nav-item__link:focus-visible .nav-item__tooltip {\n opacity: 1;\n }\n\n /* ─── Collapsed host state (propagated from parent) ─── */\n\n :host([data-collapsed]) .nav-item__label {\n width: 0;\n overflow: hidden;\n opacity: 0;\n }\n\n :host([data-collapsed]) .nav-item__badge {\n display: none;\n }\n\n :host([data-collapsed]) .nav-item__arrow {\n display: none;\n }\n\n :host([data-collapsed]) .nav-item__children {\n display: none !important;\n }\n\n :host([data-collapsed]) .nav-item__link {\n justify-content: center;\n margin: 0 var(--hx-space-1, 0.25rem);\n width: calc(100% - var(--hx-space-2, 0.5rem));\n padding: var(--hx-space-2, 0.5rem);\n position: relative;\n overflow: visible;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .nav-item__link,\n .nav-item__label,\n .nav-item__arrow,\n .nav-item__children,\n .nav-item__tooltip {\n transition: none;\n }\n\n :host([expanded]) .nav-item__children {\n grid-template-rows: 1fr;\n }\n }\n\n /* ─── Forced Colors (Windows High Contrast) ─── */\n\n @media (forced-colors: active) {\n /*\n * Bespoke block — sole owner of forced-colors deference for hx-nav-item\n * (the forcedColorsInteractive mixin is intentionally NOT composed; XOR\n * rule in styles/forced-colors.ts). Mirrors the mixin's interactive\n * contract (ButtonFace / ButtonText / Highlight on hover, GrayText on\n * disabled) for the .nav-item__link interactive surface, then layers the\n * component-specific active-state border and tooltip border on top.\n */\n .nav-item__link {\n forced-color-adjust: none;\n background-color: ButtonFace;\n color: ButtonText;\n border: 1px solid ButtonText;\n }\n\n .nav-item__link:hover {\n background-color: Highlight;\n color: HighlightText;\n border-color: Highlight;\n }\n\n :host([disabled]) {\n opacity: 1;\n }\n\n :host([disabled]) .nav-item__link {\n color: GrayText;\n border-color: GrayText;\n }\n\n :host([active]) .nav-item__link {\n border-color: Highlight;\n }\n\n .nav-item__link:focus-visible {\n outline: 3px solid Highlight;\n outline-offset: 2px;\n }\n\n .nav-item__tooltip {\n border: 1px solid CanvasText;\n }\n }\n`;\n","import { html, nothing } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { HelixElement, createIdCounter } from '../../base/index.js';\nimport { helixNavItemStyles } from './hx-nav-item.styles.js';\n\nconst _nextNavItemId = createIdCounter('hx-nav-item');\n\n/**\n * A navigation item for use inside hx-side-nav.\n * Supports icons, badges, sub-navigation, and active/disabled states.\n *\n * @summary Navigation item for hx-side-nav with support for icons, badges, and nested children.\n *\n * @tag hx-nav-item\n *\n * @slot - Default slot for item label text.\n * @slot icon - Icon to display before the label.\n * @slot badge - Badge content (e.g., notification count).\n * @slot children - Nested hx-nav-item children for sub-navigation.\n *\n * @csspart link - The anchor or button element.\n * @csspart icon - The icon container.\n * @csspart label - The label container.\n * @csspart badge - The badge container.\n * @csspart children - The children container.\n *\n * @cssprop [--hx-nav-item-color=var(--hx-color-text-inverse)] - Item text color.\n * @cssprop [--hx-nav-item-hover-bg] - Item hover background.\n * @cssprop [--hx-nav-item-hover-color=var(--hx-color-text-inverse)] - Item hover text color.\n * @cssprop [--hx-nav-item-active-bg=var(--hx-color-action-primary-bg-hover)] - Active item background.\n * @cssprop [--hx-nav-item-active-color=var(--hx-color-text-on-primary-strong)] - Active item text color.\n * @cssprop [--hx-nav-item-padding] - Item padding.\n * @cssprop [--hx-nav-item-host-bg=var(--hx-color-surface-inverse)] - Component host background color.\n * @cssprop [--hx-nav-item-tooltip-bg=var(--hx-color-surface-inverse)] - Tooltip background color (collapsed-rail tooltip).\n * @cssprop [--hx-nav-item-tooltip-color=var(--hx-color-text-inverse)] - Tooltip text color (collapsed-rail tooltip).\n */\n@customElement('hx-nav-item')\nexport class HelixNavItem extends HelixElement {\n // 3.2.1: forced-colors deference is owned by the bespoke @media block in\n // hx-nav-item.styles.ts (active border, focus outline, tooltip border).\n // Do NOT also compose forcedColorsInteractive — XOR rule per the mixin\n // docstring.\n static override styles = [helixNavItemStyles];\n\n /** @internal — per-instance tooltip ID */\n private _tooltipId = `${_nextNavItemId()}-tooltip`;\n\n // ─── Properties ───\n\n /**\n * The URL this nav item links to.\n * @attr href\n */\n @property({ type: String })\n href = '';\n\n /**\n * Whether this item is the current/active page.\n * @attr active\n */\n @property({ type: Boolean, reflect: true })\n active = false;\n\n /**\n * Whether the sub-navigation is expanded.\n * @attr expanded\n */\n @property({ type: Boolean, reflect: true })\n expanded = false;\n\n /**\n * Whether this nav item is disabled.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n // ─── State ───\n\n /** Whether the children slot has assigned nodes. Updated via slotchange. */\n /** @internal */\n @state() private _hasChildren = false;\n\n /** Whether this item is in collapsed mode. Set externally by hx-side-nav via data-collapsed attribute. */\n /** @internal */\n @state() private _isCollapsed = false;\n\n // ─── Attribute Observer ───\n\n static override get observedAttributes(): string[] {\n return [...super.observedAttributes, 'data-collapsed'];\n }\n\n override attributeChangedCallback(name: string, old: string | null, value: string | null): void {\n super.attributeChangedCallback(name, old, value);\n if (name === 'data-collapsed') {\n this._isCollapsed = value !== null;\n }\n }\n\n // ─── Public API ───\n\n /**\n * Delegates focus to the internal link or button element (part=\"link\").\n * Allows parent components to focus nav items without piercing the Shadow DOM.\n * WCAG 2.1.1: keyboard navigation must not cross shadow boundaries via\n * direct shadowRoot queries.\n */\n override focus(options?: FocusOptions): void {\n const inner = this.shadowRoot?.querySelector<HTMLElement>('[part=\"link\"]');\n if (inner) {\n inner.focus(options);\n } else {\n super.focus(options);\n }\n }\n\n // ─── Slot Change Handler ───\n\n /** @internal */\n private _onChildrenSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasChildren = slot.assignedNodes({ flatten: true }).length > 0;\n }\n\n // ─── Private Helpers ───\n\n /** @internal */\n private _getDirectText(): string {\n return Array.from(this.childNodes)\n .filter((n) => n.nodeType === Node.TEXT_NODE)\n .map((n) => n.textContent?.trim() ?? '')\n .filter(Boolean)\n .join(' ');\n }\n\n /** @internal */\n private _handleToggle(e: Event): void {\n if (this.disabled) return;\n e.preventDefault();\n this.expanded = !this.expanded;\n }\n\n /** @internal */\n private _renderExpandArrow() {\n return html`<span class=\"nav-item__arrow\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 20 20\">\n <path\n d=\"M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z\"\n />\n </svg>\n </span>`;\n }\n\n // ─── Render ───\n\n override render() {\n const label = this._getDirectText();\n\n const innerContent = html`\n <span part=\"icon\" class=\"nav-item__icon\">\n <slot name=\"icon\"></slot>\n </span>\n <span part=\"label\" class=\"nav-item__label\">\n <slot></slot>\n </span>\n <span part=\"badge\" class=\"nav-item__badge\">\n <slot name=\"badge\"></slot>\n </span>\n ${this._hasChildren ? this._renderExpandArrow() : nothing}\n ${this._isCollapsed\n ? html`<span id=${this._tooltipId} class=\"nav-item__tooltip\" role=\"tooltip\">${label}</span>`\n : nothing}\n `;\n\n // Render as anchor when href provided and no expandable children\n const linkEl =\n this.href && !this._hasChildren\n ? html`<a\n part=\"link\"\n class=\"nav-item__link\"\n href=${this.href}\n aria-current=${this.active ? 'page' : nothing}\n aria-disabled=${this.disabled ? 'true' : nothing}\n aria-describedby=${this._isCollapsed ? this._tooltipId : nothing}\n tabindex=${this.disabled ? '-1' : '0'}\n >\n ${innerContent}\n </a>`\n : html`<button\n part=\"link\"\n class=\"nav-item__link\"\n aria-disabled=${this.disabled ? 'true' : nothing}\n aria-expanded=${this._hasChildren ? String(this.expanded) : nothing}\n aria-describedby=${this._isCollapsed ? this._tooltipId : nothing}\n tabindex=${this.disabled ? '-1' : '0'}\n @click=${this._handleToggle}\n >\n ${innerContent}\n </button>`;\n\n return html`\n <div class=\"nav-item\">\n ${linkEl}\n <div part=\"children\" class=\"nav-item__children\" role=\"group\">\n <div class=\"nav-item__children-inner\">\n <slot name=\"children\" @slotchange=${this._onChildrenSlotChange}></slot>\n </div>\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-nav-item': HelixNavItem;\n }\n}\n\nexport type { HelixNavItem as HxNavItem };\n"],"names":["helixSideNavStyles","css","HelixSideNav","HelixElement","changedProperties","slot","_a","navItems","el","item","topLevelItems","childrenSlot","_b","childItems","activeEl","currentIndex","i","_c","currentItem","_d","firstChild","parentNavItem","_e","nextIndex","targetItem","html","__decorateClass","property","customElement","helixNavItemStyles","_nextNavItemId","createIdCounter","HelixNavItem","name","old","value","options","inner","n","label","innerContent","nothing","linkEl","state"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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"}
@@ -56,10 +56,10 @@ const w = m`
56
56
  * are tuned for the lighter -500 surfaces and would fail here (e.g.
57
57
  * neutral-900 on primary-600 = 3.07:1), so the on-{role}-strong tokens
58
58
  * (neutral-0, no dark-mode flip) keep fg legible on the darker fills.
59
- * - text.on-primary-strong on info.bg-strong (primary-600, #0F7078) = 5.39:1
59
+ * - text.on-primary-strong on info.bg-strong (primary-600, #0F7078) = 5.82:1
60
60
  * - text.on-success-strong on success.bg-strong (success-700, #146831) = 6.88:1
61
- * (success-600 #0E8A4A on white = 4.41:1 — drifts under AA at 14px)
62
- * - text.on-error-strong on danger.bg-strong (error-600, #C92A2A) = 5.92:1
61
+ * (success-600 #0E8A4A on white = 4.42:1 — drifts under AA at 14px)
62
+ * - text.on-error-strong on danger.bg-strong (error-600, #C92A2A) = 5.46:1
63
63
  * - text.on-warning on warning.bg-strong (warning-500, #C2711C) = 4.83:1
64
64
  * (warning stays on the lighter -500 surface so on-warning works)
65
65
  *
@@ -563,4 +563,4 @@ export {
563
563
  d as a,
564
564
  H as t
565
565
  };
566
- //# sourceMappingURL=toast-factory-CIiZDZGZ.js.map
566
+ //# sourceMappingURL=toast-factory-Mz1GaaFF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toast-factory-Mz1GaaFF.js","sources":["../../src/components/hx-toast/hx-toast.styles.ts","../../src/components/hx-toast/hx-toast.ts","../../src/components/hx-toast/hx-toast-stack.ts","../../src/components/hx-toast/toast-factory.ts"],"sourcesContent":["import { css } from 'lit';\n\n/**\n * hx-toast styles.\n *\n * Component-tier tokens with two-level var() fallback:\n * var(--hx-toast-{prop}, var(--hx-color-{semantic}, #hex))\n * Inner hex fallbacks track the \"precision cool\" palette (3.2.0):\n * neutral-0 = #FFFFFF, neutral-900 = #0D1825 (surface-inverse anchor),\n * primary-600 = #0F7078, success-600 = #0E8A4A, warning-500 = #C2711C,\n * error-600 = #C92A2A.\n */\nexport const helixToastStyles = css`\n /* ─── hx-toast host ─── */\n\n :host {\n display: block;\n pointer-events: none;\n }\n\n :host([open]) {\n pointer-events: auto;\n }\n\n /* ─── Toast base ─── */\n\n .toast {\n display: flex;\n align-items: flex-start;\n gap: var(--hx-space-3, 0.75rem);\n padding: var(--hx-space-3, 0.75rem) var(--hx-space-4, 1rem);\n border-radius: var(--hx-toast-border-radius, var(--hx-border-radius-md, 0.375rem));\n background-color: var(--hx-toast-bg, var(--hx-color-surface-inverse, #0d1825));\n color: var(--hx-toast-color, var(--hx-color-text-inverse, #ffffff));\n font-family: var(--hx-toast-font-family, var(--hx-font-family-sans, sans-serif));\n font-size: var(--hx-font-size-sm, 0.875rem);\n line-height: var(--hx-line-height-normal, 1.5);\n box-shadow: var(\n --hx-toast-shadow,\n var(--hx-shadow-md, 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1))\n );\n opacity: 0;\n transform: translateY(var(--hx-toast-enter-translate, var(--hx-space-2, 0.5rem)));\n transition:\n opacity var(--hx-transition-normal, 250ms ease),\n transform var(--hx-transition-normal, 250ms ease);\n width: var(--hx-toast-width, 20rem);\n max-width: 100%;\n pointer-events: auto;\n }\n\n :host([open]) .toast {\n opacity: 1;\n transform: translateY(0);\n }\n\n /* ─── Variant overrides ─── */\n\n /*\n * Toast variants paint on darker brand fills (primary-600/success-700/\n * error-600) because the lighter -500 fills can't pass AA against white\n * text in the precision-cool palette. The neutral-900 on-{role} tokens\n * are tuned for the lighter -500 surfaces and would fail here (e.g.\n * neutral-900 on primary-600 = 3.07:1), so the on-{role}-strong tokens\n * (neutral-0, no dark-mode flip) keep fg legible on the darker fills.\n * - text.on-primary-strong on info.bg-strong (primary-600, #0F7078) = 5.82:1\n * - text.on-success-strong on success.bg-strong (success-700, #146831) = 6.88:1\n * (success-600 #0E8A4A on white = 4.42:1 — drifts under AA at 14px)\n * - text.on-error-strong on danger.bg-strong (error-600, #C92A2A) = 5.46:1\n * - text.on-warning on warning.bg-strong (warning-500, #C2711C) = 4.83:1\n * (warning stays on the lighter -500 surface so on-warning works)\n *\n * 3.2.1 token-cascade: bg variants now route through surface.{role}-strong\n * semantics; fg variants route through text.on-{role}-strong (or on-warning\n * for the warning variant). Component-tier tokens are NOT bypassed — the\n * --hx-toast-bg / --hx-toast-color slots remain the single override point.\n */\n .toast--success {\n --hx-toast-bg: var(--hx-color-surface-success-strong, #146831);\n --hx-toast-color: var(--hx-color-text-on-success-strong, #ffffff);\n }\n\n .toast--warning {\n --hx-toast-bg: var(--hx-color-surface-warning-strong, #c2711c);\n --hx-toast-color: var(--hx-color-text-on-warning, #0d1825);\n }\n\n .toast--danger {\n --hx-toast-bg: var(--hx-color-surface-danger-strong, #c92a2a);\n --hx-toast-color: var(--hx-color-text-on-error-strong, #ffffff);\n }\n\n .toast--info {\n --hx-toast-bg: var(--hx-color-surface-info-strong, #0f7078);\n --hx-toast-color: var(--hx-color-text-on-primary-strong, #ffffff);\n }\n\n /* ─── Severity Label (WCAG 1.4.1) ─── */\n /* Visually hidden — non-color cue for severity variants (success/warning/danger/info). */\n /* Ensures variant is not conveyed by color alone for color-blind users. */\n\n .toast__severity-label {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n\n /* ─── Icon ─── */\n\n .toast__icon {\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n line-height: 1;\n }\n\n .toast__icon:empty {\n display: none;\n }\n\n /* ─── Message ─── */\n\n .toast__message {\n flex: 1 1 auto;\n min-width: 0;\n }\n\n /* ─── Action slot ─── */\n\n .toast__action {\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n }\n\n .toast__action:empty {\n display: none;\n }\n\n /* ─── Close button ─── */\n\n .toast__close {\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n /* WCAG 2.5.5: minimum 44×44px touch target */\n min-width: var(--hx-touch-target-min, 2.75rem);\n min-height: var(--hx-touch-target-min, 2.75rem);\n padding: var(--hx-space-1, 0.25rem);\n background: transparent;\n border: none;\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n color: inherit;\n cursor: pointer;\n opacity: var(--hx-opacity-75, 0.75);\n transition: opacity var(--hx-transition-fast, 150ms ease);\n }\n\n .toast__close:hover {\n opacity: 1;\n }\n\n .toast__close:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid currentColor;\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n /* ─── Reduced motion ─── */\n\n @media (prefers-reduced-motion: reduce) {\n .toast {\n transition: none;\n }\n\n .toast__close {\n transition: none;\n }\n }\n\n /* ─── Forced Colors (Windows High Contrast) ─── */\n /* Belt-and-suspenders: rich per-class HC overrides PLUS the forcedColorsSurface mixin. */\n\n @media (forced-colors: active) {\n .toast {\n border: 1px solid CanvasText;\n }\n\n .toast__close {\n color: ButtonText;\n border: 1px solid ButtonText;\n }\n }\n`;\n\nexport const helixToastStackStyles = css`\n :host {\n display: block;\n position: fixed;\n z-index: var(--hx-z-index-toast, 1700);\n pointer-events: none;\n }\n\n .toast-stack {\n display: flex;\n flex-direction: column;\n gap: var(--hx-space-3, 0.75rem);\n padding: var(--hx-space-4, 1rem);\n pointer-events: none;\n }\n\n /* ─── Placements ─── */\n\n :host([placement='top-start']) {\n top: 0;\n inset-inline-start: 0;\n inset-inline-end: auto;\n bottom: auto;\n }\n\n :host([placement='top-center']) {\n top: 0;\n inset-inline-start: 50%;\n transform: translateX(-50%);\n inset-inline-end: auto;\n bottom: auto;\n }\n\n :host([placement='top-end']) {\n top: 0;\n inset-inline-end: 0;\n inset-inline-start: auto;\n bottom: auto;\n }\n\n :host([placement='bottom-start']) {\n bottom: 0;\n inset-inline-start: 0;\n inset-inline-end: auto;\n top: auto;\n }\n\n :host([placement='bottom-center']) {\n bottom: 0;\n inset-inline-start: 50%;\n transform: translateX(-50%);\n inset-inline-end: auto;\n top: auto;\n }\n\n :host([placement='bottom-end']) {\n bottom: 0;\n inset-inline-end: 0;\n inset-inline-start: auto;\n top: auto;\n }\n\n /* ─── Bottom placements: reverse order so newest is on top ─── */\n\n :host([placement^='bottom']) .toast-stack {\n flex-direction: column-reverse;\n }\n\n /* ─── Slide direction by placement ─── */\n\n :host([placement^='top']) ::slotted(hx-toast) {\n --hx-toast-enter-translate: calc(var(--hx-space-2, 0.5rem) * -1);\n }\n`;\n","import { html, nothing, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property, query, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { HelixElement } from '../../base/index.js';\nimport { helixToastStyles } from './hx-toast.styles.js';\nimport { forcedColorsSurface } from '../../styles/forced-colors.js';\n\nexport type ToastVariant = 'default' | 'success' | 'warning' | 'danger' | 'info';\n\n/**\n * A transient notification message that auto-dismisses after a configurable duration.\n * Supports multiple visual variants, a closable button, icon/action slots, and full\n * ARIA live region semantics for screen readers.\n *\n * @summary Transient notification toast component.\n *\n * @tag hx-toast\n *\n * @slot - Default slot for the notification message.\n * @slot icon - Optional icon rendered before the message.\n * @slot action - Optional action button rendered after the message.\n *\n * @fires {CustomEvent} hx-show - Dispatched when the toast becomes visible.\n * @fires {CustomEvent} hx-hide - Dispatched when the toast begins hiding.\n * @fires {CustomEvent} hx-after-hide - Dispatched after the hide animation completes.\n *\n * @csspart base - The inner toast container div.\n * @csspart icon - The icon slot wrapper.\n * @csspart message - The message slot wrapper.\n * @csspart close-button - The dismiss button (only when closable).\n * @csspart action - The action slot wrapper.\n *\n * @cssprop [--hx-toast-bg=var(--hx-color-surface-inverse)] - Toast background color.\n * @cssprop [--hx-toast-color=var(--hx-color-text-inverse)] - Toast text color.\n * @cssprop [--hx-toast-border-radius=var(--hx-border-radius-md)] - Toast border radius.\n * @cssprop [--hx-toast-shadow] - Toast box shadow.\n * @cssprop [--hx-toast-width=20rem] - Toast width.\n * @cssprop [--hx-space-3] - Spacing token.\n * @cssprop [--hx-space-4] - Spacing token.\n * @cssprop [--hx-border-radius-md] - CSS custom property.\n * @cssprop [--hx-color-neutral-900] - Color.\n * @cssprop [--hx-color-neutral-0] - Color.\n * @cssprop [--hx-toast-font-family=var(--hx-font-family-sans)] - CSS custom property.\n * @cssprop [--hx-font-family-sans] - Font family.\n * @cssprop [--hx-font-size-sm] - Font size.\n * @cssprop [--hx-line-height-normal] - Line height.\n * @cssprop [--hx-shadow-md] - Box shadow.\n * @cssprop [--hx-toast-enter-translate=var(--hx-space-2)] - CSS custom property.\n * @cssprop [--hx-space-2] - Spacing token.\n * @cssprop [--hx-transition-normal] - Transition timing.\n * @cssprop [--hx-color-success-600] - Color.\n * @cssprop [--hx-color-warning-500] - Color.\n * @cssprop [--hx-color-error-600] - Color.\n * @cssprop [--hx-color-primary-600] - Color.\n * @cssprop [--hx-touch-target-min] - Minimum touch target size.\n * @cssprop [--hx-space-1] - Spacing token.\n * @cssprop [--hx-border-radius-sm] - CSS custom property.\n * @cssprop [--hx-opacity-75] - Opacity.\n * @cssprop [--hx-transition-fast] - Transition timing.\n * @cssprop [--hx-focus-ring-width] - Width.\n * @cssprop [--hx-focus-ring-offset] - CSS custom property.\n */\n@customElement('hx-toast')\nexport class HelixToast extends HelixElement {\n static override styles = [helixToastStyles, forcedColorsSurface];\n\n // ─── Public Properties ───\n\n /**\n * Visual style variant.\n * @attr variant\n */\n @property({ type: String, reflect: true })\n variant: 'default' | 'success' | 'warning' | 'danger' | 'info' = 'default';\n\n /**\n * Auto-dismiss duration in milliseconds. Set to 0 for persistent toasts.\n * @attr duration\n */\n @property({ type: Number })\n duration = 5000;\n\n /**\n * Whether to show a close button.\n * @attr closable\n */\n @property({ type: Boolean, reflect: true })\n closable = false;\n\n /**\n * Whether the toast is currently visible.\n * @attr open\n */\n @property({ type: Boolean, reflect: true })\n open = false;\n\n /**\n * Accessible label for the close button. Override for localization.\n * @attr label-close\n */\n @property({ attribute: 'label-close' })\n labelClose = 'Dismiss notification';\n\n // ─── Private State ───\n\n /** @internal */\n @query('.toast') private _toastEl!: HTMLElement | null;\n\n /** @internal Tracks whether the action slot has slotted content. */\n @state() private _hasActionContent = false;\n\n /** @internal */\n private _timer: ReturnType<typeof setTimeout> | null = null;\n\n // ─── Reduced Motion ───\n\n /** @internal Returns true when the user has opted into reduced motion. */\n private get _reducedMotion(): boolean {\n // Guard for SSR — window.matchMedia is unavailable server-side\n if (typeof window === 'undefined') return false;\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n }\n\n /** @internal */\n private _timerStartedAt: number | null = null;\n\n /** @internal */\n private _timerRemaining: number | null = null;\n\n // ─── Lifecycle ───\n\n override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n if (changedProperties.has('open')) {\n if (this.open) {\n this.removeAttribute('aria-hidden');\n this._emitShow();\n if (this.duration > 0 && !this._reducedMotion && !this._hasActionContent) {\n this._startTimer();\n }\n } else {\n this.setAttribute('aria-hidden', 'true');\n this._clearTimer();\n this._emitHide();\n }\n }\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this._clearTimer();\n }\n\n // ─── Public API ───\n\n /** Show the toast. */\n show(): void {\n if (!this.open) {\n this.open = true;\n }\n }\n\n /** Hide the toast. */\n hide(): void {\n if (this.open) {\n this.open = false;\n }\n }\n\n // ─── Private Helpers ───\n\n /** @internal */\n private _startTimer(remaining?: number): void {\n this._clearTimerHandle();\n const delay = remaining ?? this.duration;\n this._timerStartedAt = Date.now();\n this._timerRemaining = delay;\n this._timer = setTimeout(() => {\n this.open = false;\n }, delay);\n }\n\n /** @internal */\n private _pauseTimer(): void {\n if (this._timer === null || this._timerStartedAt === null || this._timerRemaining === null) {\n return;\n }\n const elapsed = Date.now() - this._timerStartedAt;\n this._timerRemaining = Math.max(0, this._timerRemaining - elapsed);\n this._clearTimerHandle();\n }\n\n /** @internal */\n private _clearTimerHandle(): void {\n if (this._timer !== null) {\n clearTimeout(this._timer);\n this._timer = null;\n }\n }\n\n /** @internal */\n private _clearTimer(): void {\n this._clearTimerHandle();\n this._timerStartedAt = null;\n this._timerRemaining = null;\n }\n\n /** @internal */\n private _emitShow(): void {\n this.dispatchEvent(new CustomEvent<void>('hx-show', { bubbles: true, composed: true }));\n }\n\n /** @internal */\n private _emitHide(): void {\n this.dispatchEvent(new CustomEvent<void>('hx-hide', { bubbles: true, composed: true }));\n\n let fired = false;\n const fireAfterHide = () => {\n if (fired) return;\n fired = true;\n this.dispatchEvent(new CustomEvent<void>('hx-after-hide', { bubbles: true, composed: true }));\n };\n\n // Fire on transitionend if available; fallback ensures it fires in test environments\n // and when transitions are disabled (prefers-reduced-motion, no CSS transitions).\n const base = this._toastEl;\n if (base) {\n base.addEventListener('transitionend', fireAfterHide, { once: true });\n }\n // Fallback fires after the CSS transition duration (250ms) plus a small buffer.\n setTimeout(fireAfterHide, 300);\n }\n\n // ─── Event Handlers ───\n\n /** @internal */\n private _handleMouseEnter(): void {\n this._pauseTimer();\n }\n\n /** @internal */\n private _handleMouseLeave(): void {\n if (this.open && this.duration > 0) {\n this._startTimer(this._timerRemaining ?? undefined);\n }\n }\n\n /** @internal */\n private _handleFocusIn(): void {\n this._pauseTimer();\n }\n\n /** @internal */\n private _handleFocusOut(): void {\n if (this.open && this.duration > 0) {\n this._startTimer(this._timerRemaining ?? undefined);\n }\n }\n\n /** @internal */\n private _handleClose(): void {\n this.hide();\n }\n\n /** @internal */\n private _handleActionSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasActionContent = slot.assignedNodes({ flatten: true }).length > 0;\n if (this._hasActionContent && this.open) {\n this._pauseTimer();\n }\n }\n\n // ─── ARIA Helpers ───\n\n /** @internal */\n private get _role(): 'alert' | 'status' {\n return this.variant === 'danger' ? 'alert' : 'status';\n }\n\n /** @internal */\n private get _ariaLive(): 'assertive' | 'polite' {\n return this.variant === 'danger' ? 'assertive' : 'polite';\n }\n\n // ─── WCAG 1.4.1: Default Icons ───\n // Each semantic variant renders a default icon when no icon is slotted,\n // ensuring the severity is not conveyed by color alone.\n\n /** @internal */\n private _renderSuccessIcon() {\n return html`<svg viewBox=\"0 0 20 20\" aria-hidden=\"true\" width=\"16\" height=\"16\">\n <path\n fill=\"currentColor\"\n d=\"M10 2a8 8 0 100 16 8 8 0 000-16zm3.03 6.28a.75.75 0 00-1.06-1.06L9 10.19 7.78 8.97a.75.75 0 00-1.06 1.06l1.75 1.75a.75.75 0 001.06 0l3.5-3.5z\"\n />\n </svg>`;\n }\n\n /** @internal */\n private _renderWarningIcon() {\n return html`<svg viewBox=\"0 0 20 20\" aria-hidden=\"true\" width=\"16\" height=\"16\">\n <path\n fill=\"currentColor\"\n d=\"M8.49 2.92a1.75 1.75 0 013.02 0l6.25 10.83A1.75 1.75 0 0116.25 16H3.75a1.75 1.75 0 01-1.51-2.25L8.49 2.92zM10 7a.75.75 0 01.75.75v3a.75.75 0 01-1.5 0v-3A.75.75 0 0110 7zm0 7.5a.75.75 0 100-1.5.75.75 0 000 1.5z\"\n />\n </svg>`;\n }\n\n /** @internal */\n private _renderDangerIcon() {\n return html`<svg viewBox=\"0 0 20 20\" aria-hidden=\"true\" width=\"16\" height=\"16\">\n <path\n fill=\"currentColor\"\n d=\"M10 2a8 8 0 100 16 8 8 0 000-16zm-1.72 5.22a.75.75 0 011.06 0L10 7.94l.66-.72a.75.75 0 111.06 1.06L11.06 9l.66.72a.75.75 0 11-1.06 1.06L10 10.06l-.66.72a.75.75 0 01-1.06-1.06L8.94 9l-.66-.72a.75.75 0 010-1.06z\"\n />\n </svg>`;\n }\n\n /** @internal */\n private _renderInfoIcon() {\n return html`<svg viewBox=\"0 0 20 20\" aria-hidden=\"true\" width=\"16\" height=\"16\">\n <path\n fill=\"currentColor\"\n d=\"M10 2a8 8 0 100 16 8 8 0 000-16zm.75 4.75a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM9.25 9a.75.75 0 011.5 0v4a.75.75 0 01-1.5 0V9z\"\n />\n </svg>`;\n }\n\n /** @internal Returns the default icon for the current variant, or nothing for 'default'. */\n private get _defaultIcon() {\n switch (this.variant) {\n case 'success':\n return this._renderSuccessIcon();\n case 'warning':\n return this._renderWarningIcon();\n case 'danger':\n return this._renderDangerIcon();\n case 'info':\n return this._renderInfoIcon();\n default:\n return nothing;\n }\n }\n\n // ─── WCAG 1.4.1: Severity label map ───\n\n /** @internal */\n private static readonly _SEVERITY_LABELS: Partial<Record<ToastVariant, string>> = {\n success: 'Success',\n warning: 'Warning',\n danger: 'Error',\n info: 'Info',\n };\n\n /** @internal */\n private get _severityLabel(): string {\n return HelixToast._SEVERITY_LABELS[this.variant] ?? '';\n }\n\n // ─── Render ───\n\n override render() {\n const severityLabel = this._severityLabel;\n\n return html`\n <div\n part=\"base\"\n class=${classMap({\n toast: true,\n [`toast--${this.variant}`]: true,\n })}\n role=${this._role}\n aria-live=${this._ariaLive}\n aria-atomic=\"true\"\n @mouseenter=${this._handleMouseEnter}\n @mouseleave=${this._handleMouseLeave}\n @focusin=${this._handleFocusIn}\n @focusout=${this._handleFocusOut}\n >\n ${this.open\n ? html`\n ${severityLabel\n ? html`<span class=\"toast__severity-label\">${severityLabel}: </span>`\n : nothing}\n <span part=\"icon\" class=\"toast__icon\">\n <slot name=\"icon\">${this._defaultIcon}</slot>\n </span>\n <span part=\"message\" class=\"toast__message\">\n <slot></slot>\n </span>\n <span part=\"action\" class=\"toast__action\">\n <slot name=\"action\" @slotchange=${this._handleActionSlotChange}></slot>\n </span>\n ${this.closable\n ? html`\n <button\n part=\"close-button\"\n class=\"toast__close\"\n aria-label=${this.labelClose}\n @click=${this._handleClose}\n >\n <svg\n aria-hidden=\"true\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M18 6L6 18M6 6l12 12\" />\n </svg>\n </button>\n `\n : nothing}\n `\n : nothing}\n </div>\n `;\n }\n}\n\n// ─── Declarative Global Types ───\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-toast': HelixToast;\n }\n}\n","import { html } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { HelixElement } from '../../base/index.js';\nimport { helixToastStackStyles } from './hx-toast.styles.js';\n\nexport type ToastStackPlacement =\n | 'top-start'\n | 'top-center'\n | 'top-end'\n | 'bottom-start'\n | 'bottom-center'\n | 'bottom-end';\n\n/**\n * A fixed-position container that stacks `hx-toast` elements at the specified\n * corner of the viewport. Enforces a maximum visible toast count via `stack-limit`.\n *\n * @summary Toast stack container managing position and count limits.\n *\n * @tag hx-toast-stack\n *\n * @slot - Accepts `hx-toast` elements.\n *\n * @csspart base - The inner stack container div.\n *\n * @cssprop [--hx-z-index-toast=9000] - Z-index for the fixed stack.\n */\n@customElement('hx-toast-stack')\nexport class HelixToastStack extends HelixElement {\n static override styles = [helixToastStackStyles];\n\n /**\n * Corner of the viewport where toasts appear.\n * @attr placement\n */\n @property({ type: String, reflect: true })\n placement:\n | 'top-start'\n | 'top-center'\n | 'top-end'\n | 'bottom-start'\n | 'bottom-center'\n | 'bottom-end' = 'bottom-end';\n\n /**\n * Maximum number of simultaneously visible toasts. 0 = unlimited.\n * @attr stack-limit\n */\n @property({ type: Number, attribute: 'stack-limit' })\n stackLimit = 3;\n\n override render() {\n return html`\n <div\n part=\"base\"\n class=${classMap({\n 'toast-stack': true,\n [`toast-stack--${this.placement}`]: true,\n })}\n >\n <slot></slot>\n </div>\n `;\n }\n}\n\n// ─── Declarative Global Types ───\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-toast-stack': HelixToastStack;\n }\n}\n","import { HelixToast } from './hx-toast.js';\nimport { HelixToastStack } from './hx-toast-stack.js';\nimport type { ToastVariant } from './hx-toast.js';\nimport type { ToastStackPlacement } from './hx-toast-stack.js';\n\nexport interface ToastOptions {\n /** The notification message text. */\n message: string;\n /** Visual variant. Defaults to 'default'. */\n variant?: ToastVariant;\n /** Auto-dismiss duration in ms. 0 = persistent. Defaults to 3000. */\n duration?: number;\n /** Placement of the shared stack. Defaults to 'bottom-end'. */\n placement?: ToastStackPlacement;\n}\n\n/**\n * Imperatively create and display a toast notification.\n *\n * Creates a shared `hx-toast-stack` on `document.body` if one does not exist,\n * then appends a new `hx-toast` with the given options. Respects the stack's\n * `stackLimit` by hiding the oldest visible toast when the limit is exceeded.\n *\n * @example\n * import { toast } from '@helixui/library/components/hx-toast/index.js';\n * toast({ message: 'Patient record saved.', variant: 'success' });\n */\nexport function toast(options: ToastOptions): HelixToast {\n if (typeof document === 'undefined') throw new Error('toast() requires a browser environment');\n const placement = options.placement ?? 'bottom-end';\n\n // Find or create a dedicated stack for this placement\n const stackSelector = `hx-toast-stack[placement=\"${placement}\"]`;\n let stack = document.querySelector<HelixToastStack>(stackSelector);\n if (!stack) {\n stack = document.createElement('hx-toast-stack');\n stack.placement = placement;\n // Intentional design decision: the toast stack is appended to document.body rather\n // than inserted near the caller. This is required so that the fixed-position overlay\n // is not clipped by an ancestor with `overflow: hidden`, `transform`, or `filter`\n // (all of which create a new stacking context and break fixed positioning).\n // Drupal compatibility note: Drupal's BigPipe / AJAX behaviors can re-attach the\n // document body without removing these stacks. The selector check above\n // (`document.querySelector`) ensures only one stack per placement is ever created,\n // preventing duplicates on re-attach cycles.\n document.body.appendChild(stack);\n }\n\n // Enforce stack limit: hide oldest open toast if at capacity\n if (stack.stackLimit > 0) {\n const openToasts = [...stack.querySelectorAll<HelixToast>('hx-toast')].filter((t) => t.open);\n if (openToasts.length >= stack.stackLimit) {\n openToasts[0]?.hide();\n }\n }\n\n // Create toast element\n const toastEl = document.createElement('hx-toast');\n toastEl.variant = options.variant ?? 'default';\n toastEl.duration = options.duration ?? 3000;\n toastEl.closable = true;\n toastEl.textContent = options.message;\n\n // Remove from DOM after hiding\n toastEl.addEventListener('hx-after-hide', () => {\n toastEl.remove();\n });\n\n stack.appendChild(toastEl);\n toastEl.show();\n\n return toastEl;\n}\n"],"names":["helixToastStyles","css","helixToastStackStyles","HelixToast","HelixElement","changedProperties","remaining","delay","elapsed","fired","fireAfterHide","base","e","slot","html","nothing","severityLabel","classMap","forcedColorsSurface","__decorateClass","property","query","state","customElement","HelixToastStack","toast","options","placement","stackSelector","stack","openToasts","t","_a","toastEl"],"mappings":";;;;;AAYO,MAAMA,IAAmBC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GA6LnBC,IAAwBD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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"}