@helixui/library 3.2.0-next.98 → 3.3.0

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 (146) hide show
  1. package/custom-elements.json +30 -18
  2. package/dist/components/hx-alert/hx-alert.styles.d.ts.map +1 -1
  3. package/dist/components/hx-alert/index.js +1 -1
  4. package/dist/components/hx-banner/hx-banner.styles.d.ts.map +1 -1
  5. package/dist/components/hx-banner/index.js +1 -1
  6. package/dist/components/hx-button/hx-button.d.ts +8 -6
  7. package/dist/components/hx-button/hx-button.d.ts.map +1 -1
  8. package/dist/components/hx-button/hx-button.styles.d.ts.map +1 -1
  9. package/dist/components/hx-button/index.js +1 -1
  10. package/dist/components/hx-checkbox/hx-checkbox.styles.d.ts.map +1 -1
  11. package/dist/components/hx-checkbox/index.js +1 -1
  12. package/dist/components/hx-code-snippet/hx-code-snippet.styles.d.ts.map +1 -1
  13. package/dist/components/hx-code-snippet/index.js +1 -1
  14. package/dist/components/hx-combobox/hx-combobox.styles.d.ts.map +1 -1
  15. package/dist/components/hx-combobox/index.js +1 -1
  16. package/dist/components/hx-date-picker/hx-date-picker.styles.d.ts.map +1 -1
  17. package/dist/components/hx-date-picker/index.js +1 -1
  18. package/dist/components/hx-link/hx-link.styles.d.ts.map +1 -1
  19. package/dist/components/hx-link/index.js +1 -1
  20. package/dist/components/hx-list/hx-list-item.styles.d.ts.map +1 -1
  21. package/dist/components/hx-list/index.js +1 -1
  22. package/dist/components/hx-radio-group/hx-radio.styles.d.ts.map +1 -1
  23. package/dist/components/hx-radio-group/index.js +1 -1
  24. package/dist/components/hx-rating/hx-rating.styles.d.ts.map +1 -1
  25. package/dist/components/hx-rating/index.js +1 -1
  26. package/dist/components/hx-select/hx-select.styles.d.ts.map +1 -1
  27. package/dist/components/hx-select/index.js +1 -1
  28. package/dist/components/hx-side-nav/hx-side-nav.d.ts +4 -3
  29. package/dist/components/hx-side-nav/hx-side-nav.d.ts.map +1 -1
  30. package/dist/components/hx-side-nav/hx-side-nav.styles.d.ts.map +1 -1
  31. package/dist/components/hx-side-nav/index.js +1 -1
  32. package/dist/components/hx-slider/hx-slider.styles.d.ts.map +1 -1
  33. package/dist/components/hx-slider/index.js +1 -1
  34. package/dist/components/hx-split-button/hx-split-button.styles.d.ts.map +1 -1
  35. package/dist/components/hx-split-button/index.js +1 -1
  36. package/dist/components/hx-switch/hx-switch.styles.d.ts.map +1 -1
  37. package/dist/components/hx-switch/index.js +1 -1
  38. package/dist/components/hx-tabs/hx-tab-panel.styles.d.ts.map +1 -1
  39. package/dist/components/hx-tabs/hx-tab.styles.d.ts.map +1 -1
  40. package/dist/components/hx-tabs/index.js +1 -1
  41. package/dist/components/hx-text-input/hx-text-input.styles.d.ts.map +1 -1
  42. package/dist/components/hx-text-input/index.js +1 -1
  43. package/dist/components/hx-textarea/hx-textarea.styles.d.ts.map +1 -1
  44. package/dist/components/hx-textarea/index.js +1 -1
  45. package/dist/components/hx-theme/hx-theme.d.ts +14 -2
  46. package/dist/components/hx-theme/hx-theme.d.ts.map +1 -1
  47. package/dist/components/hx-theme/index.js +1 -1
  48. package/dist/components/hx-toggle-button/hx-toggle-button.styles.d.ts.map +1 -1
  49. package/dist/components/hx-toggle-button/index.js +1 -1
  50. package/dist/components/hx-tree-view/hx-tree-view.styles.d.ts.map +1 -1
  51. package/dist/components/hx-tree-view/index.js +1 -1
  52. package/dist/css/helix-all.css +122 -120
  53. package/dist/css/helix-core.css +53 -23
  54. package/dist/css/helix-data.css +20 -6
  55. package/dist/css/helix-feedback.css +3 -12
  56. package/dist/css/helix-forms.css +16 -63
  57. package/dist/css/helix-navigation.css +28 -8
  58. package/dist/css/helix-tokens.css +8 -8
  59. package/dist/css/helix-utility.css +2 -8
  60. package/dist/css/hx-alert.css +1 -4
  61. package/dist/css/hx-banner.css +2 -8
  62. package/dist/css/hx-button.css +52 -19
  63. package/dist/css/hx-checkbox.css +1 -4
  64. package/dist/css/hx-code-snippet.css +20 -6
  65. package/dist/css/hx-combobox.css +2 -8
  66. package/dist/css/hx-date-picker.css +5 -21
  67. package/dist/css/hx-link.css +1 -4
  68. package/dist/css/hx-rating.css +1 -2
  69. package/dist/css/hx-select.css +1 -4
  70. package/dist/css/hx-side-nav.css +27 -6
  71. package/dist/css/hx-slider.css +1 -4
  72. package/dist/css/hx-split-button.css +2 -8
  73. package/dist/css/hx-switch.css +1 -4
  74. package/dist/css/hx-text-input.css +2 -8
  75. package/dist/css/hx-textarea.css +1 -4
  76. package/dist/css/hx-toggle-button.css +1 -4
  77. package/dist/css/hx-tree-view.css +1 -2
  78. package/dist/css/index.css +1 -1
  79. package/dist/css/manifest.json +20 -18
  80. package/dist/index.js +22 -22
  81. package/dist/shared/{hx-alert-C597yHpD.js → hx-alert-CLn7CstP.js} +2 -5
  82. package/dist/shared/hx-alert-CLn7CstP.js.map +1 -0
  83. package/dist/shared/{hx-banner-Cxd7eFUP.js → hx-banner-D3DzpfcP.js} +7 -13
  84. package/dist/shared/hx-banner-D3DzpfcP.js.map +1 -0
  85. package/dist/shared/{hx-button-9OUjJnk7.js → hx-button-DPY6SPVT.js} +58 -25
  86. package/dist/shared/hx-button-DPY6SPVT.js.map +1 -0
  87. package/dist/shared/{hx-checkbox-DBD-gMoz.js → hx-checkbox-D7xma9YH.js} +6 -9
  88. package/dist/shared/hx-checkbox-D7xma9YH.js.map +1 -0
  89. package/dist/shared/{hx-code-snippet-CJrFeyz0.js → hx-code-snippet-fVV3Z2DZ.js} +55 -41
  90. package/dist/shared/hx-code-snippet-fVV3Z2DZ.js.map +1 -0
  91. package/dist/shared/{hx-combobox-ClhNRAS5.js → hx-combobox-DDzqNKEW.js} +6 -12
  92. package/dist/shared/hx-combobox-DDzqNKEW.js.map +1 -0
  93. package/dist/shared/{hx-date-picker-BJm7Yrda.js → hx-date-picker-2iRG1p74.js} +12 -28
  94. package/dist/shared/hx-date-picker-2iRG1p74.js.map +1 -0
  95. package/dist/shared/{hx-link-9Ig2DW6L.js → hx-link-C-O6vq0Q.js} +2 -5
  96. package/dist/shared/hx-link-C-O6vq0Q.js.map +1 -0
  97. package/dist/shared/{hx-list-CkphGi9T.js → hx-list-MyEhh8c7.js} +3 -5
  98. package/dist/shared/hx-list-MyEhh8c7.js.map +1 -0
  99. package/dist/shared/{hx-nav-item-DH2tXcj1.js → hx-nav-item-xqRPOCWX.js} +58 -37
  100. package/dist/shared/hx-nav-item-xqRPOCWX.js.map +1 -0
  101. package/dist/shared/{hx-radio-dFjUAost.js → hx-radio-CJvNU2yP.js} +2 -5
  102. package/dist/shared/{hx-radio-dFjUAost.js.map → hx-radio-CJvNU2yP.js.map} +1 -1
  103. package/dist/shared/{hx-rating-CGtsejNf.js → hx-rating-C3QP53k9.js} +2 -3
  104. package/dist/shared/hx-rating-C3QP53k9.js.map +1 -0
  105. package/dist/shared/{hx-select-Bf4usFts.js → hx-select-C8fEHQhC.js} +2 -5
  106. package/dist/shared/hx-select-C8fEHQhC.js.map +1 -0
  107. package/dist/shared/{hx-slider-m0aEClH1.js → hx-slider-Blmv_rwS.js} +23 -26
  108. package/dist/shared/hx-slider-Blmv_rwS.js.map +1 -0
  109. package/dist/shared/{hx-split-button-BxDFfx4D.js → hx-split-button-Djnc5Aeg.js} +3 -9
  110. package/dist/shared/hx-split-button-Djnc5Aeg.js.map +1 -0
  111. package/dist/shared/{hx-switch-DvAW4YY-.js → hx-switch-BrZFaRue.js} +6 -9
  112. package/dist/shared/hx-switch-BrZFaRue.js.map +1 -0
  113. package/dist/shared/{hx-tab-panel-SWOEHuJc.js → hx-tab-panel-DspCrKqo.js} +3 -9
  114. package/dist/shared/{hx-tab-panel-SWOEHuJc.js.map → hx-tab-panel-DspCrKqo.js.map} +1 -1
  115. package/dist/shared/{hx-text-input-Bn7Gn8CI.js → hx-text-input-D6FlOZM-.js} +3 -9
  116. package/dist/shared/hx-text-input-D6FlOZM-.js.map +1 -0
  117. package/dist/shared/{hx-textarea-Jx1xnhgv.js → hx-textarea-CNG590KY.js} +6 -9
  118. package/dist/shared/hx-textarea-CNG590KY.js.map +1 -0
  119. package/dist/shared/{hx-theme-BiyQ7UUK.js → hx-theme-BsefFWTO.js} +83 -113
  120. package/dist/shared/hx-theme-BsefFWTO.js.map +1 -0
  121. package/dist/shared/{hx-toggle-button-DPAIh_Xo.js → hx-toggle-button-iLiYrMbD.js} +2 -5
  122. package/dist/shared/hx-toggle-button-iLiYrMbD.js.map +1 -0
  123. package/dist/shared/{hx-tree-item-Dt0Ozqyr.js → hx-tree-item-C2CiWuDE.js} +2 -3
  124. package/dist/shared/hx-tree-item-C2CiWuDE.js.map +1 -0
  125. package/figma-inventory.json +34 -19
  126. package/package.json +2 -2
  127. package/dist/shared/hx-alert-C597yHpD.js.map +0 -1
  128. package/dist/shared/hx-banner-Cxd7eFUP.js.map +0 -1
  129. package/dist/shared/hx-button-9OUjJnk7.js.map +0 -1
  130. package/dist/shared/hx-checkbox-DBD-gMoz.js.map +0 -1
  131. package/dist/shared/hx-code-snippet-CJrFeyz0.js.map +0 -1
  132. package/dist/shared/hx-combobox-ClhNRAS5.js.map +0 -1
  133. package/dist/shared/hx-date-picker-BJm7Yrda.js.map +0 -1
  134. package/dist/shared/hx-link-9Ig2DW6L.js.map +0 -1
  135. package/dist/shared/hx-list-CkphGi9T.js.map +0 -1
  136. package/dist/shared/hx-nav-item-DH2tXcj1.js.map +0 -1
  137. package/dist/shared/hx-rating-CGtsejNf.js.map +0 -1
  138. package/dist/shared/hx-select-Bf4usFts.js.map +0 -1
  139. package/dist/shared/hx-slider-m0aEClH1.js.map +0 -1
  140. package/dist/shared/hx-split-button-BxDFfx4D.js.map +0 -1
  141. package/dist/shared/hx-switch-DvAW4YY-.js.map +0 -1
  142. package/dist/shared/hx-text-input-Bn7Gn8CI.js.map +0 -1
  143. package/dist/shared/hx-textarea-Jx1xnhgv.js.map +0 -1
  144. package/dist/shared/hx-theme-BiyQ7UUK.js.map +0 -1
  145. package/dist/shared/hx-toggle-button-DPAIh_Xo.js.map +0 -1
  146. package/dist/shared/hx-tree-item-Dt0Ozqyr.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-nav-item-xqRPOCWX.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(\n --hx-side-nav-border-color,\n var(--hx-color-border-on-dark-strong, rgba(255, 255, 255, 0.7))\n );\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(\n --hx-side-nav-border-color,\n var(--hx-color-border-on-dark-strong, rgba(255, 255, 255, 0.7))\n );\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(\n --hx-side-nav-border-color,\n var(--hx-color-border-on-dark-strong, rgba(255, 255, 255, 0.7))\n );\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 /* Read both deprecated --hx-color-border-on-dark-subtle (3.2.0/3.2.1 API)\n and canonical --hx-color-surface-on-dark-overlay-subtle so consumer\n overrides on either name reach paint. Deprecated removal: 4.0.0.\n Hex fallback for browsers without color-mix(). */\n background-color: var(\n --hx-color-border-on-dark-subtle,\n var(--hx-color-surface-on-dark-overlay-subtle, rgba(255, 255, 255, 0.1))\n );\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 /* Fold color-mix() into the same deprecated-first chain so consumer\n overrides on either token reach paint on the modern path too. */\n .side-nav__toggle:hover {\n background-color: var(\n --hx-color-border-on-dark-subtle,\n var(\n --hx-color-surface-on-dark-overlay-subtle,\n color-mix(in srgb, currentColor 15%, transparent)\n )\n );\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, #0f7078));\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-on-dark-strong)] - Border color (against the dark surface-inverse host bg).\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-surface-inverse] - Side-nav surface fill (resolves to neutral-900 light, near-black dark).\n * @cssprop [--hx-color-text-inverse] - Side-nav text color (resolves to neutral-0).\n * @cssprop [--hx-color-border-on-dark-strong] - Container/header/footer divider border (overlay-white-70 light, overlay-black-50 dark — sized for visibility on the mode-flipped surface-inverse).\n * @cssprop [--hx-color-surface-on-dark-overlay-subtle] - Toggle button hover surface (overlay-white-10 primitive — translucent fill, not a border).\n * @cssprop [--hx-color-border-on-dark-subtle] - DEPRECATED 3.2.2; renamed to --hx-color-surface-on-dark-overlay-subtle (the value paints a translucent fill, not a border). Toggle-hover rule reads both names via deprecated-first fallback so existing overrides keep working until removal in 4.0.0.\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, #0f7078));\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.82: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]:not([disabled])) .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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"}
@@ -449,10 +449,7 @@ const H = g`
449
449
 
450
450
  :host(:focus-visible) .radio__control {
451
451
  outline: var(--hx-focus-ring-width, 2px) solid
452
- var(
453
- --hx-radio-focus-ring-color,
454
- var(--hx-focus-ring-color, var(--hx-color-primary-600, #0f7078))
455
- );
452
+ var(--hx-radio-focus-ring-color, var(--hx-focus-ring-color, #0f7078));
456
453
  outline-offset: var(--hx-focus-ring-offset, 2px);
457
454
  }
458
455
 
@@ -621,4 +618,4 @@ export {
621
618
  c as H,
622
619
  l as a
623
620
  };
624
- //# sourceMappingURL=hx-radio-dFjUAost.js.map
621
+ //# sourceMappingURL=hx-radio-CJvNU2yP.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hx-radio-dFjUAost.js","sources":["../../src/components/hx-radio-group/hx-radio-group.styles.ts","../../src/components/hx-radio-group/hx-radio-group.ts","../../src/components/hx-radio-group/hx-radio.styles.ts","../../src/components/hx-radio-group/hx-radio.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixRadioGroupStyles = css`\n :host {\n display: block;\n }\n\n :host([disabled]) {\n opacity: var(--hx-opacity-disabled, 0.5);\n pointer-events: none;\n }\n\n * {\n box-sizing: border-box;\n }\n\n /* ─── Fieldset ─── */\n\n .fieldset {\n border: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: var(--hx-space-2, 0.5rem);\n font-family: var(--hx-radio-group-font-family, var(--hx-font-family-sans, sans-serif));\n }\n\n /* ─── Legend ─── */\n\n .fieldset__legend {\n display: flex;\n align-items: baseline;\n gap: var(--hx-space-1, 0.25rem);\n font-size: var(--hx-font-size-sm, 0.875rem);\n font-weight: var(--hx-font-weight-medium, 500);\n color: var(--hx-radio-group-label-color, var(--hx-color-text-strong, #202b39));\n line-height: var(--hx-line-height-normal, 1.5);\n padding: 0;\n margin-bottom: var(--hx-space-1, 0.25rem);\n }\n\n .fieldset__required-marker {\n color: var(--hx-radio-group-error-color, var(--hx-color-error-text, #c92a2a));\n font-weight: var(--hx-font-weight-bold, 700);\n }\n\n /* ─── Group Container ─── */\n\n .fieldset__group {\n display: flex;\n flex-direction: column;\n gap: var(--hx-radio-group-gap, var(--hx-space-3, 0.75rem));\n }\n\n :host([orientation='horizontal']) .fieldset__group {\n flex-direction: row;\n flex-wrap: wrap;\n }\n\n /* ─── Error State ─── */\n\n .fieldset--error .fieldset__legend {\n color: var(--hx-radio-group-error-color, var(--hx-color-error-text, #c92a2a));\n }\n\n /* ─── Help Text & Error Messages ─── */\n\n .fieldset__help-text {\n font-size: var(--hx-font-size-xs, 0.75rem);\n color: var(--hx-radio-group-help-text-color, var(--hx-color-text-muted, #66787b));\n line-height: var(--hx-line-height-normal, 1.5);\n }\n\n .fieldset__error {\n font-size: var(--hx-font-size-xs, 0.75rem);\n color: var(--hx-radio-group-error-color, var(--hx-color-error-text, #c92a2a));\n line-height: var(--hx-line-height-normal, 1.5);\n }\n\n /* ─── High Contrast Mode (forced-colors) ─── */\n\n @media (forced-colors: active) {\n .fieldset {\n border: none;\n }\n\n .fieldset__legend {\n color: CanvasText;\n }\n\n .fieldset--error .fieldset__legend {\n color: LinkText;\n }\n\n :host([disabled]) {\n opacity: 1;\n }\n\n :host([disabled]) .fieldset__legend {\n color: GrayText;\n }\n\n .fieldset__help-text {\n color: GrayText;\n }\n\n .fieldset__error {\n color: LinkText;\n }\n }\n`;\n","import { html, nothing, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { devWarn } from '../../utils/dev-warn.js';\nimport { HelixElement, createIdCounter } from '../../base/index.js';\nimport { FormMixin } from '../../mixins/FormMixin.js';\nimport { helixRadioGroupStyles } from './hx-radio-group.styles.js';\nimport { forcedColorsField } from '../../styles/forced-colors.js';\nimport type { HelixRadio } from './hx-radio.js';\n\nconst _nextRadioGroupId = createIdCounter('hx-radio-group');\n\n/** Detail for the hx-change event dispatched by hx-radio-group. */\nexport interface HxRadioGroupChangeDetail {\n value: string;\n checked: boolean;\n}\n\n/**\n * A form-associated radio group that manages a set of `<hx-radio>` children.\n *\n * @summary Form-associated radio group with label, validation, help text, and keyboard navigation.\n *\n * @tag hx-radio-group\n *\n * @slot - `<hx-radio>` elements.\n * @slot error - Custom error content (overrides the error property).\n * @slot help-text - Custom help text content (overrides the helpText property).\n *\n * @fires {CustomEvent<{value: string, checked: boolean}>} hx-change - Dispatched when the selected radio changes.\n * @fires {CustomEvent<{value: string}>} hx-radio-select - Internal event dispatched by `hx-radio` when selected; consumed by the group.\n *\n * @csspart fieldset - The fieldset wrapper.\n * @csspart legend - The legend/label.\n * @csspart group - The container for radio items.\n * @csspart error - The error message.\n * @csspart help-text - The help text.\n *\n * @cssprop [--hx-radio-group-gap=var(--hx-space-3, 0.75rem)] - Gap between radio items.\n * @cssprop [--hx-radio-group-label-color=var(--hx-color-neutral-700, #313E4B)] - Label text color.\n * @cssprop [--hx-radio-group-error-color=var(--hx-color-error-500, #E5493E)] - Error message color.\n * @cssprop [--hx-radio-group-help-text-color=var(--hx-color-neutral-500, #66787B)] - Help text color.\n * @cssprop [--hx-opacity-disabled] - Opacity.\n * @cssprop [--hx-space-2] - Spacing token.\n * @cssprop [--hx-radio-group-font-family=var(--hx-font-family-sans)] - CSS custom property.\n * @cssprop [--hx-font-family-sans] - Font family.\n * @cssprop [--hx-space-1] - Spacing token.\n * @cssprop [--hx-font-size-sm] - Font size.\n * @cssprop [--hx-font-weight-medium] - Font weight.\n * @cssprop [--hx-color-neutral-700] - Color.\n * @cssprop [--hx-line-height-normal] - Line height.\n * @cssprop [--hx-color-error-text] - Color.\n * @cssprop [--hx-font-weight-bold] - Font weight.\n * @cssprop [--hx-space-3] - Spacing token.\n * @cssprop [--hx-font-size-xs] - Font size.\n * @cssprop [--hx-color-neutral-500] - Color.\n */\n@customElement('hx-radio-group')\nexport class HelixRadioGroup extends FormMixin(HelixElement) {\n static override styles = [helixRadioGroupStyles, forcedColorsField];\n\n // ─── Form Association ───\n\n /**\n * Enables ElementInternals form association for this component.\n * @internal\n */\n static override formAssociated = true;\n\n // ─── Properties ───\n\n /**\n * The selected radio's value.\n * @attr value\n */\n @property({ type: String, reflect: true })\n value = '';\n\n /**\n * The name used for form submission.\n * @attr name\n */\n @property({ type: String, reflect: true })\n name = '';\n\n /**\n * The fieldset legend/label text.\n * @attr label\n */\n @property({ type: String })\n label = '';\n\n /**\n * Whether a selection is required for form submission.\n * @attr required\n */\n @property({ type: Boolean, reflect: true })\n required = false;\n\n /**\n * Whether the entire group is disabled.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Error message to display. When set, the group enters an error state.\n * @attr error\n */\n @property({ type: String })\n error = '';\n\n /**\n * Help text displayed below the group for guidance.\n * @attr help-text\n */\n @property({ type: String, attribute: 'help-text' })\n helpText = '';\n\n /**\n * Layout orientation of the radio items.\n * @attr orientation\n */\n @property({ type: String, reflect: true })\n orientation: 'vertical' | 'horizontal' = 'vertical';\n\n /**\n * Queries the rendered group container element within the shadow root.\n * @internal\n */\n private get _groupEl(): HTMLElement | null {\n return this.renderRoot?.querySelector('.fieldset__group') ?? null;\n }\n\n /**\n * Tracks whether the error slot has assigned content.\n * @internal\n */\n @state() private _hasErrorSlot = false;\n\n // ─── Internal IDs ───\n\n /**\n * Unique identifier for this radio group instance used in ARIA attributes.\n * @internal\n */\n private _groupId = _nextRadioGroupId();\n /**\n * Unique identifier for the help text element, used in aria-describedby.\n * @internal\n */\n private _helpTextId = `${this._groupId}-help`;\n /**\n * Unique identifier for the error element, used in aria-describedby.\n * @internal\n */\n private _errorId = `${this._groupId}-error`;\n\n // ─── Slot Handlers ───\n\n /**\n * Handles slotchange events on the error slot to detect assigned content.\n * @internal\n */\n private _handleErrorSlotChange(e: Event): void {\n if (!(e.target instanceof HTMLSlotElement)) return;\n this._hasErrorSlot = e.target.assignedNodes({ flatten: true }).length > 0;\n }\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener('hx-radio-select', this._handleRadioSelect);\n this.addEventListener('keydown', this._handleKeydown);\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener('hx-radio-select', this._handleRadioSelect);\n this.removeEventListener('keydown', this._handleKeydown);\n }\n\n override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n if (changedProperties.has('value')) {\n this._internals.setFormValue(this.value || null);\n this._syncRadios();\n }\n if (changedProperties.has('disabled')) {\n this._syncRadios();\n }\n // Force screen reader re-announcement when error text changes (a11y-v3-005)\n if (changedProperties.has('error') && this.error) {\n const errorEl = this.shadowRoot?.querySelector('[role=\"alert\"]');\n if (errorEl) {\n const msg = this.error;\n requestAnimationFrame(() => {\n errorEl.textContent = '';\n requestAnimationFrame(() => {\n errorEl.textContent = msg;\n });\n });\n }\n }\n }\n\n override firstUpdated(changedProperties: PropertyValues<this>): void {\n super.firstUpdated(changedProperties);\n this._syncRadios();\n // WCAG 4.1.2: warn when no accessible name is available for the radio group.\n // The fieldset needs either a label prop (rendered as <legend>) or an aria-label\n // attribute on the host element so screen readers can identify the group.\n if (!this.label && !this.getAttribute('aria-label')) {\n devWarn(\n 'hx-radio-group',\n 'No accessible label provided. Set the `label` attribute or add `aria-label` to the host element. An unlabeled radio group violates WCAG 2.1 AA (4.1.2 Name, Role, Value).',\n );\n }\n }\n\n // ─── Radio Management ───\n\n /**\n * Cached list of child hx-radio elements; invalidated on slot change.\n * @internal\n */\n private _cachedRadios: HelixRadio[] | null = null;\n /**\n * Stores each radio's individual disabled state before group-level disabling overrides it.\n * @internal\n */\n private _individualDisabledStates = new WeakMap<HelixRadio, boolean>();\n\n /**\n * Returns all child hx-radio elements, using the cache when available.\n * @internal\n */\n private _getRadios(): HelixRadio[] {\n if (!this._cachedRadios) {\n this._cachedRadios = Array.from(this.querySelectorAll('hx-radio')) as HelixRadio[];\n }\n return this._cachedRadios;\n }\n\n /**\n * Returns only the child hx-radio elements that are not disabled.\n * @internal\n */\n private _getEnabledRadios(): HelixRadio[] {\n return this._getRadios().filter((radio) => !radio.disabled && !this.disabled);\n }\n\n /**\n * Synchronizes checked state, disabled state, and roving tabindex across all child radios.\n * @internal\n */\n private _syncRadios(): void {\n const radios = this._getRadios();\n const enabledRadios = this._getEnabledRadios();\n\n radios.forEach((radio) => {\n const isChecked = radio.value === this.value && this.value !== '';\n radio.checked = isChecked;\n\n if (this.disabled) {\n // Store individual disabled state before overriding with group disabled\n if (!this._individualDisabledStates.has(radio)) {\n this._individualDisabledStates.set(radio, radio.disabled);\n }\n radio.disabled = true;\n } else {\n // Restore individual disabled state when group is re-enabled\n const originalDisabled = this._individualDisabledStates.get(radio);\n if (originalDisabled !== undefined) {\n radio.disabled = originalDisabled;\n this._individualDisabledStates.delete(radio);\n }\n }\n });\n\n // Roving tabindex management\n const checkedRadio = enabledRadios.find((r) => r.checked);\n radios.forEach((radio) => {\n radio.tabIndex = -1;\n });\n\n if (checkedRadio) {\n checkedRadio.tabIndex = 0;\n } else if (enabledRadios.length > 0) {\n const firstRadio = enabledRadios[0];\n if (firstRadio) {\n firstRadio.tabIndex = 0;\n }\n }\n }\n\n // ─── Event Handling ───\n\n /**\n * Handles the internal hx-radio-select event to update the group's selected value.\n * @internal\n */\n private _handleRadioSelect = (e: Event): void => {\n if (!(e instanceof CustomEvent)) return;\n e.stopPropagation();\n\n const newValue = (e.detail as { value: string }).value;\n if (newValue === this.value) {\n return;\n }\n\n this.value = newValue;\n this._handleInteractionInput();\n // Reactive update in updated() will call setFormValue, _syncRadios, _updateValidity\n\n /**\n * Dispatched when the selected radio changes.\n * @event hx-change\n */\n this.dispatchEvent(\n new CustomEvent<{ value: string; checked: boolean }>('hx-change', {\n bubbles: true,\n composed: true,\n detail: { value: this.value, checked: true },\n }),\n );\n };\n\n /**\n * Handles keyboard navigation (arrow keys, Home, End, Space) within the radio group.\n * @internal\n */\n private _handleKeydown = (e: KeyboardEvent): void => {\n const enabledRadios = this._getEnabledRadios();\n if (enabledRadios.length === 0) {\n return;\n }\n\n const isHandledKey = [\n 'ArrowUp',\n 'ArrowDown',\n 'ArrowLeft',\n 'ArrowRight',\n ' ',\n 'Home',\n 'End',\n ].includes(e.key);\n if (!isHandledKey) {\n return;\n }\n\n e.preventDefault();\n\n // Space: select the currently focused radio without moving focus\n if (e.key === ' ') {\n const targetRadio = (e.target as Element)?.closest?.('hx-radio') as HelixRadio | null;\n if (targetRadio && !targetRadio.disabled) {\n targetRadio.dispatchEvent(\n new CustomEvent<{ value: string }>('hx-radio-select', {\n bubbles: true,\n composed: true,\n detail: { value: targetRadio.value },\n }),\n );\n }\n return;\n }\n\n const targetRadio = (e.target as Element)?.closest?.('hx-radio') as HelixRadio | null;\n const currentIndex = targetRadio\n ? enabledRadios.indexOf(targetRadio)\n : enabledRadios.findIndex((radio) => radio.checked);\n\n let nextIndex: number;\n if (e.key === 'Home') {\n nextIndex = 0;\n } else if (e.key === 'End') {\n nextIndex = enabledRadios.length - 1;\n } else if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {\n nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % enabledRadios.length;\n } else {\n nextIndex = currentIndex <= 0 ? enabledRadios.length - 1 : currentIndex - 1;\n }\n\n const nextRadio = enabledRadios[nextIndex];\n if (nextRadio) {\n nextRadio.focus();\n nextRadio.dispatchEvent(\n new CustomEvent<{ value: string }>('hx-radio-select', {\n bubbles: true,\n composed: true,\n detail: { value: nextRadio.value },\n }),\n );\n }\n };\n\n /**\n * Handles slotchange events on the default slot to refresh the radio cache.\n * @internal\n */\n private _handleSlotChange(): void {\n this._cachedRadios = null;\n this._syncRadios();\n }\n\n // ─── Form Integration ───\n\n /**\n * Updates the ElementInternals validity state based on the required constraint and current value.\n * @internal\n */\n override _updateValidity(): void {\n if (this.required && !this.value) {\n this._internals.setValidity(\n { valueMissing: true },\n this.error || 'Please select an option.',\n this._groupEl ?? undefined,\n );\n } else {\n this._internals.setValidity({});\n }\n }\n\n /** @internal */\n protected override _onFormReset(): void {\n this.value = '';\n this._internals.setFormValue(null);\n this._syncRadios();\n this._resetInteractionState();\n }\n\n /** @internal */\n protected override _onFormStateRestore(\n state: File | string | FormData | null,\n _mode: 'restore' | 'autocomplete',\n ): void {\n if (typeof state === 'string') {\n this.value = state;\n }\n }\n\n /** @internal */\n protected override _onFormDisabled(disabled: boolean): void {\n this.disabled = disabled;\n }\n\n // ─── Render ───\n\n override render() {\n const hasError = !!this.error;\n const legendId = `${this._groupId}-legend`;\n\n const fieldsetClasses = {\n fieldset: true,\n 'fieldset--error': hasError,\n 'fieldset--disabled': this.disabled,\n 'fieldset--required': this.required,\n };\n\n // WCAG 1.3.1: _errorId is now on the persistent wrapper div around the error slot,\n // so it remains valid whether error content comes from the slot or the property.\n const hasHelp = !!this.helpText;\n const hasErrorContent = hasError || this._hasErrorSlot;\n const describedByIds = [\n hasErrorContent ? this._errorId : null,\n hasHelp ? this._helpTextId : null,\n ]\n .filter(Boolean)\n .join(' ');\n const describedBy = describedByIds || nothing;\n\n return html`\n <fieldset\n part=\"fieldset\"\n class=${classMap(fieldsetClasses)}\n role=\"radiogroup\"\n aria-labelledby=${this.label ? legendId : nothing}\n aria-describedby=${describedBy}\n aria-required=${this.required ? 'true' : nothing}\n >\n ${this.label\n ? html`\n <legend part=\"legend\" class=\"fieldset__legend\" id=${legendId}>\n ${this.label}\n ${this.required\n ? html`<span class=\"fieldset__required-marker\" aria-hidden=\"true\">*</span>`\n : nothing}\n </legend>\n `\n : nothing}\n\n <div part=\"group\" class=\"fieldset__group\" role=\"none\">\n <slot @slotchange=${this._handleSlotChange}></slot>\n </div>\n\n <!-- WCAG 1.3.1: wrap slot in a persistent container so _errorId stays stable\n regardless of whether error content comes from the slot or the property. -->\n <div id=${this._errorId}>\n <slot name=\"error\" @slotchange=${this._handleErrorSlotChange}>\n ${hasError\n ? html`<div part=\"error\" class=\"fieldset__error\" role=\"alert\">${this.error}</div>`\n : nothing}\n </slot>\n </div>\n\n ${this.helpText && !hasError\n ? html`\n <div part=\"help-text\" class=\"fieldset__help-text\" id=${this._helpTextId}>\n <slot name=\"help-text\">${this.helpText}</slot>\n </div>\n `\n : nothing}\n </fieldset>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-radio-group': HelixRadioGroup;\n }\n}\n\n/** Canonical type alias for the hx-radio-group component. */\nexport type HxRadioGroup = HelixRadioGroup;\n","import { css } from 'lit';\n\nexport const helixRadioStyles = css`\n :host {\n display: block;\n }\n\n :host([disabled]) {\n opacity: var(--hx-opacity-disabled, 0.5);\n pointer-events: none;\n }\n\n * {\n box-sizing: border-box;\n }\n\n .radio {\n display: inline-flex;\n align-items: center;\n gap: var(--hx-space-2, 0.5rem);\n /* WCAG 2.5.5 (healthcare mandate): minimum 44px touch target height */\n min-height: var(--hx-touch-target-min, 2.75rem);\n cursor: pointer;\n position: relative;\n font-family: var(--hx-radio-font-family, var(--hx-font-family-sans, sans-serif));\n }\n\n .radio--disabled {\n cursor: not-allowed;\n }\n\n /* ─── Hidden Native Input ─── */\n\n .radio__input {\n position: absolute;\n width: var(--hx-space-px);\n height: var(--hx-space-px);\n padding: 0;\n margin: calc(var(--hx-space-px) * -1);\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n\n /* ─── Visual Radio Circle ─── */\n\n .radio__control {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--hx-radio-size, var(--hx-size-5, 1.25rem));\n height: var(--hx-radio-size, var(--hx-size-5, 1.25rem));\n border: var(--hx-border-width-medium, 2px) solid\n var(--hx-radio-border-color, var(--hx-color-border-strong, #66787b));\n border-radius: var(--hx-border-radius-full, 9999px);\n background-color: var(--hx-radio-bg, var(--hx-color-surface-default, #ffffff));\n transition:\n border-color var(--hx-transition-fast, 150ms ease),\n background-color var(--hx-transition-fast, 150ms ease),\n box-shadow var(--hx-transition-fast, 150ms ease);\n flex-shrink: 0;\n }\n\n /* ─── Inner Dot ─── */\n\n .radio__dot {\n width: calc(var(--hx-radio-size, var(--hx-size-5, 1.25rem)) * 0.4);\n height: calc(var(--hx-radio-size, var(--hx-size-5, 1.25rem)) * 0.4);\n border-radius: var(--hx-border-radius-full, 9999px);\n background-color: var(--hx-radio-dot-color, var(--hx-color-text-on-primary, #ffffff));\n transform: scale(0);\n transition: transform var(--hx-transition-fast, 150ms ease);\n }\n\n /* ─── Checked State ─── */\n\n .radio--checked .radio__control {\n border-color: var(--hx-radio-checked-border-color, var(--hx-color-primary-500, #429797));\n background-color: var(--hx-radio-checked-bg, var(--hx-color-primary-500, #429797));\n }\n\n .radio--checked .radio__dot {\n transform: scale(1);\n }\n\n /* ─── Focus State ─── */\n\n :host(:focus-visible) .radio__control {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(\n --hx-radio-focus-ring-color,\n var(--hx-focus-ring-color, var(--hx-color-primary-600, #0f7078))\n );\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n /* ─── Hover State ─── */\n\n .radio:not(.radio--disabled):not(.radio--checked):hover .radio__control {\n border-color: var(--hx-radio-hover-border-color, var(--hx-color-border-strong, #66787b));\n }\n\n /* ─── Label ─── */\n\n .radio__label {\n font-size: var(--hx-font-size-md, 1rem);\n color: var(--hx-radio-label-color, var(--hx-color-text-strong, #202b39));\n line-height: var(--hx-line-height-normal, 1.5);\n user-select: none;\n -webkit-user-select: none;\n }\n\n /* ─── Reduced Motion ─── */\n\n @media (prefers-reduced-motion: reduce) {\n .radio__control,\n .radio__dot {\n transition: none;\n }\n }\n\n /* ─── High Contrast Mode (forced-colors) ─── */\n\n @media (forced-colors: active) {\n .radio__control {\n forced-color-adjust: none;\n background-color: ButtonFace;\n border: 2px solid ButtonText;\n }\n\n :host(:focus-visible) .radio__control {\n outline: 3px solid Highlight;\n outline-offset: 2px;\n }\n\n .radio--checked .radio__control {\n background-color: Highlight;\n border-color: Highlight;\n }\n\n .radio--checked .radio__dot {\n background-color: HighlightText;\n }\n\n :host([disabled]) {\n opacity: 1;\n }\n\n :host([disabled]) .radio__control {\n border-color: GrayText;\n background-color: ButtonFace;\n }\n\n :host([disabled]) .radio--checked .radio__control {\n background-color: GrayText;\n border-color: GrayText;\n }\n\n :host([disabled]) .radio__label {\n color: GrayText;\n }\n\n .radio__label {\n color: CanvasText;\n }\n }\n`;\n","import { html, 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 { HelixElement, createIdCounter } from '../../base/index.js';\nimport { helixRadioStyles } from './hx-radio.styles.js';\nimport { forcedColorsField } from '../../styles/forced-colors.js';\n\nconst _nextRadioId = createIdCounter('hx-radio');\n\n/**\n * An individual radio button, designed to be used inside a `<hx-radio-group>`.\n *\n * @summary Presentational radio button managed by its parent radio group.\n *\n * @tag hx-radio\n *\n * @slot - Custom label content (overrides the label property).\n *\n * @csspart radio - The visual radio circle.\n * @csspart label - The label text.\n *\n * @cssprop [--hx-radio-size=var(--hx-size-5, 1.25rem)] - Radio circle size.\n * @cssprop [--hx-radio-border-color=var(--hx-color-neutral-300, #B6BFB9)] - Radio border color.\n * @cssprop [--hx-radio-checked-bg=var(--hx-color-primary-500, #429797)] - Checked background color.\n * @cssprop [--hx-radio-checked-border-color=var(--hx-color-primary-500, #429797)] - Checked border color.\n * @cssprop [--hx-radio-dot-color=var(--hx-color-neutral-0, #ffffff)] - Inner dot color when checked.\n * @cssprop [--hx-radio-focus-ring-color=var(--hx-focus-ring-color, #6AB1B1)] - Focus ring color.\n * @cssprop [--hx-radio-label-color=var(--hx-color-neutral-700, #313E4B)] - Label text color.\n */\n@customElement('hx-radio')\nexport class HelixRadio extends HelixElement {\n static override styles = [helixRadioStyles, forcedColorsField];\n\n // ─── Properties ───\n\n /**\n * The value this radio represents.\n * @attr value\n */\n @property({ type: String })\n value = '';\n\n /**\n * Visible label text for the radio.\n * @attr label\n */\n @property({ type: String })\n label = '';\n\n /**\n * Whether this radio is disabled.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Whether this radio is checked. Managed by the parent group.\n * @attr checked\n */\n @property({ type: Boolean, reflect: true })\n checked = false;\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n // ARIA role and state are projected to the accessibility tree via\n // ElementInternals rather than imperative setAttribute calls. This keeps\n // the host element's attribute surface clean — consumers cannot accidentally\n // override role/aria-* — and aligns with the standards pattern for\n // custom elements. See https://wicg.github.io/aom/spec/aria-reflection.html.\n this._internals.role = 'radio';\n this._syncAriaState();\n }\n\n override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n if (\n changedProperties.has('checked') ||\n changedProperties.has('label') ||\n changedProperties.has('disabled')\n ) {\n this._syncAriaState();\n }\n }\n\n /**\n * Mirror reactive ARIA state onto ElementInternals. Setting a value to `null`\n * removes it from the accessibility tree (matching the previous\n * removeAttribute behavior).\n *\n * WCAG 4.1.2: expose the label text as ariaLabel on the host so assistive\n * technology can associate the visible label with the radio role. The label\n * span lives inside Shadow DOM and aria-labelledby cannot cross shadow\n * boundaries, so ariaLabel on the host is the correct pattern here.\n *\n * WCAG 4.1.2: omit ariaDisabled entirely when not disabled. Setting\n * aria-disabled=\"false\" is verbose and unnecessary — omission is preferred.\n *\n * @internal\n */\n private _syncAriaState(): void {\n this._internals.ariaChecked = String(this.checked);\n this._internals.ariaLabel = this.label || null;\n this._internals.ariaDisabled = this.disabled ? 'true' : null;\n }\n\n // ─── Internal IDs ───\n\n /** @internal */\n private _inputId = _nextRadioId();\n\n // ─── Event Handling ───\n\n /** @internal */\n private _handleClick(): void {\n if (this.disabled) {\n return;\n }\n\n /**\n * Internal event dispatched to signal selection to the parent group.\n * Not part of the public API.\n * @internal\n */\n this.dispatchEvent(\n new CustomEvent<{ value: string }>('hx-radio-select', {\n bubbles: true,\n composed: true,\n detail: { value: this.value },\n }),\n );\n }\n\n // ─── Render ───\n\n override render() {\n const classes = {\n radio: true,\n 'radio--checked': this.checked,\n 'radio--disabled': this.disabled,\n };\n\n return html`\n <div class=${classMap(classes)} @click=${this._handleClick}>\n <input\n class=\"radio__input\"\n type=\"radio\"\n id=${this._inputId}\n .checked=${this.checked}\n ?disabled=${this.disabled}\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n <span part=\"radio\" class=\"radio__control\" aria-hidden=\"true\">\n <span class=\"radio__dot\"></span>\n </span>\n <span part=\"label\" class=\"radio__label\">\n <slot>${this.label}</slot>\n </span>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-radio': HelixRadio;\n }\n}\n\n/** Canonical type alias for the hx-radio component. */\nexport type HxRadio = HelixRadio;\n"],"names":["helixRadioGroupStyles","css","_nextRadioGroupId","createIdCounter","HelixRadioGroup","FormMixin","HelixElement","newValue","enabledRadios","targetRadio","_b","_a","_d","_c","currentIndex","radio","nextIndex","nextRadio","changedProperties","errorEl","msg","radios","isChecked","originalDisabled","checkedRadio","firstRadio","state","_mode","disabled","hasError","legendId","fieldsetClasses","hasHelp","describedBy","nothing","html","classMap","forcedColorsField","__decorateClass","property","customElement","helixRadioStyles","_nextRadioId","HelixRadio","classes"],"mappings":"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"}
1
+ {"version":3,"file":"hx-radio-CJvNU2yP.js","sources":["../../src/components/hx-radio-group/hx-radio-group.styles.ts","../../src/components/hx-radio-group/hx-radio-group.ts","../../src/components/hx-radio-group/hx-radio.styles.ts","../../src/components/hx-radio-group/hx-radio.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixRadioGroupStyles = css`\n :host {\n display: block;\n }\n\n :host([disabled]) {\n opacity: var(--hx-opacity-disabled, 0.5);\n pointer-events: none;\n }\n\n * {\n box-sizing: border-box;\n }\n\n /* ─── Fieldset ─── */\n\n .fieldset {\n border: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: var(--hx-space-2, 0.5rem);\n font-family: var(--hx-radio-group-font-family, var(--hx-font-family-sans, sans-serif));\n }\n\n /* ─── Legend ─── */\n\n .fieldset__legend {\n display: flex;\n align-items: baseline;\n gap: var(--hx-space-1, 0.25rem);\n font-size: var(--hx-font-size-sm, 0.875rem);\n font-weight: var(--hx-font-weight-medium, 500);\n color: var(--hx-radio-group-label-color, var(--hx-color-text-strong, #202b39));\n line-height: var(--hx-line-height-normal, 1.5);\n padding: 0;\n margin-bottom: var(--hx-space-1, 0.25rem);\n }\n\n .fieldset__required-marker {\n color: var(--hx-radio-group-error-color, var(--hx-color-error-text, #c92a2a));\n font-weight: var(--hx-font-weight-bold, 700);\n }\n\n /* ─── Group Container ─── */\n\n .fieldset__group {\n display: flex;\n flex-direction: column;\n gap: var(--hx-radio-group-gap, var(--hx-space-3, 0.75rem));\n }\n\n :host([orientation='horizontal']) .fieldset__group {\n flex-direction: row;\n flex-wrap: wrap;\n }\n\n /* ─── Error State ─── */\n\n .fieldset--error .fieldset__legend {\n color: var(--hx-radio-group-error-color, var(--hx-color-error-text, #c92a2a));\n }\n\n /* ─── Help Text & Error Messages ─── */\n\n .fieldset__help-text {\n font-size: var(--hx-font-size-xs, 0.75rem);\n color: var(--hx-radio-group-help-text-color, var(--hx-color-text-muted, #66787b));\n line-height: var(--hx-line-height-normal, 1.5);\n }\n\n .fieldset__error {\n font-size: var(--hx-font-size-xs, 0.75rem);\n color: var(--hx-radio-group-error-color, var(--hx-color-error-text, #c92a2a));\n line-height: var(--hx-line-height-normal, 1.5);\n }\n\n /* ─── High Contrast Mode (forced-colors) ─── */\n\n @media (forced-colors: active) {\n .fieldset {\n border: none;\n }\n\n .fieldset__legend {\n color: CanvasText;\n }\n\n .fieldset--error .fieldset__legend {\n color: LinkText;\n }\n\n :host([disabled]) {\n opacity: 1;\n }\n\n :host([disabled]) .fieldset__legend {\n color: GrayText;\n }\n\n .fieldset__help-text {\n color: GrayText;\n }\n\n .fieldset__error {\n color: LinkText;\n }\n }\n`;\n","import { html, nothing, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { devWarn } from '../../utils/dev-warn.js';\nimport { HelixElement, createIdCounter } from '../../base/index.js';\nimport { FormMixin } from '../../mixins/FormMixin.js';\nimport { helixRadioGroupStyles } from './hx-radio-group.styles.js';\nimport { forcedColorsField } from '../../styles/forced-colors.js';\nimport type { HelixRadio } from './hx-radio.js';\n\nconst _nextRadioGroupId = createIdCounter('hx-radio-group');\n\n/** Detail for the hx-change event dispatched by hx-radio-group. */\nexport interface HxRadioGroupChangeDetail {\n value: string;\n checked: boolean;\n}\n\n/**\n * A form-associated radio group that manages a set of `<hx-radio>` children.\n *\n * @summary Form-associated radio group with label, validation, help text, and keyboard navigation.\n *\n * @tag hx-radio-group\n *\n * @slot - `<hx-radio>` elements.\n * @slot error - Custom error content (overrides the error property).\n * @slot help-text - Custom help text content (overrides the helpText property).\n *\n * @fires {CustomEvent<{value: string, checked: boolean}>} hx-change - Dispatched when the selected radio changes.\n * @fires {CustomEvent<{value: string}>} hx-radio-select - Internal event dispatched by `hx-radio` when selected; consumed by the group.\n *\n * @csspart fieldset - The fieldset wrapper.\n * @csspart legend - The legend/label.\n * @csspart group - The container for radio items.\n * @csspart error - The error message.\n * @csspart help-text - The help text.\n *\n * @cssprop [--hx-radio-group-gap=var(--hx-space-3, 0.75rem)] - Gap between radio items.\n * @cssprop [--hx-radio-group-label-color=var(--hx-color-neutral-700, #313E4B)] - Label text color.\n * @cssprop [--hx-radio-group-error-color=var(--hx-color-error-500, #E5493E)] - Error message color.\n * @cssprop [--hx-radio-group-help-text-color=var(--hx-color-neutral-500, #66787B)] - Help text color.\n * @cssprop [--hx-opacity-disabled] - Opacity.\n * @cssprop [--hx-space-2] - Spacing token.\n * @cssprop [--hx-radio-group-font-family=var(--hx-font-family-sans)] - CSS custom property.\n * @cssprop [--hx-font-family-sans] - Font family.\n * @cssprop [--hx-space-1] - Spacing token.\n * @cssprop [--hx-font-size-sm] - Font size.\n * @cssprop [--hx-font-weight-medium] - Font weight.\n * @cssprop [--hx-color-neutral-700] - Color.\n * @cssprop [--hx-line-height-normal] - Line height.\n * @cssprop [--hx-color-error-text] - Color.\n * @cssprop [--hx-font-weight-bold] - Font weight.\n * @cssprop [--hx-space-3] - Spacing token.\n * @cssprop [--hx-font-size-xs] - Font size.\n * @cssprop [--hx-color-neutral-500] - Color.\n */\n@customElement('hx-radio-group')\nexport class HelixRadioGroup extends FormMixin(HelixElement) {\n static override styles = [helixRadioGroupStyles, forcedColorsField];\n\n // ─── Form Association ───\n\n /**\n * Enables ElementInternals form association for this component.\n * @internal\n */\n static override formAssociated = true;\n\n // ─── Properties ───\n\n /**\n * The selected radio's value.\n * @attr value\n */\n @property({ type: String, reflect: true })\n value = '';\n\n /**\n * The name used for form submission.\n * @attr name\n */\n @property({ type: String, reflect: true })\n name = '';\n\n /**\n * The fieldset legend/label text.\n * @attr label\n */\n @property({ type: String })\n label = '';\n\n /**\n * Whether a selection is required for form submission.\n * @attr required\n */\n @property({ type: Boolean, reflect: true })\n required = false;\n\n /**\n * Whether the entire group is disabled.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Error message to display. When set, the group enters an error state.\n * @attr error\n */\n @property({ type: String })\n error = '';\n\n /**\n * Help text displayed below the group for guidance.\n * @attr help-text\n */\n @property({ type: String, attribute: 'help-text' })\n helpText = '';\n\n /**\n * Layout orientation of the radio items.\n * @attr orientation\n */\n @property({ type: String, reflect: true })\n orientation: 'vertical' | 'horizontal' = 'vertical';\n\n /**\n * Queries the rendered group container element within the shadow root.\n * @internal\n */\n private get _groupEl(): HTMLElement | null {\n return this.renderRoot?.querySelector('.fieldset__group') ?? null;\n }\n\n /**\n * Tracks whether the error slot has assigned content.\n * @internal\n */\n @state() private _hasErrorSlot = false;\n\n // ─── Internal IDs ───\n\n /**\n * Unique identifier for this radio group instance used in ARIA attributes.\n * @internal\n */\n private _groupId = _nextRadioGroupId();\n /**\n * Unique identifier for the help text element, used in aria-describedby.\n * @internal\n */\n private _helpTextId = `${this._groupId}-help`;\n /**\n * Unique identifier for the error element, used in aria-describedby.\n * @internal\n */\n private _errorId = `${this._groupId}-error`;\n\n // ─── Slot Handlers ───\n\n /**\n * Handles slotchange events on the error slot to detect assigned content.\n * @internal\n */\n private _handleErrorSlotChange(e: Event): void {\n if (!(e.target instanceof HTMLSlotElement)) return;\n this._hasErrorSlot = e.target.assignedNodes({ flatten: true }).length > 0;\n }\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener('hx-radio-select', this._handleRadioSelect);\n this.addEventListener('keydown', this._handleKeydown);\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener('hx-radio-select', this._handleRadioSelect);\n this.removeEventListener('keydown', this._handleKeydown);\n }\n\n override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n if (changedProperties.has('value')) {\n this._internals.setFormValue(this.value || null);\n this._syncRadios();\n }\n if (changedProperties.has('disabled')) {\n this._syncRadios();\n }\n // Force screen reader re-announcement when error text changes (a11y-v3-005)\n if (changedProperties.has('error') && this.error) {\n const errorEl = this.shadowRoot?.querySelector('[role=\"alert\"]');\n if (errorEl) {\n const msg = this.error;\n requestAnimationFrame(() => {\n errorEl.textContent = '';\n requestAnimationFrame(() => {\n errorEl.textContent = msg;\n });\n });\n }\n }\n }\n\n override firstUpdated(changedProperties: PropertyValues<this>): void {\n super.firstUpdated(changedProperties);\n this._syncRadios();\n // WCAG 4.1.2: warn when no accessible name is available for the radio group.\n // The fieldset needs either a label prop (rendered as <legend>) or an aria-label\n // attribute on the host element so screen readers can identify the group.\n if (!this.label && !this.getAttribute('aria-label')) {\n devWarn(\n 'hx-radio-group',\n 'No accessible label provided. Set the `label` attribute or add `aria-label` to the host element. An unlabeled radio group violates WCAG 2.1 AA (4.1.2 Name, Role, Value).',\n );\n }\n }\n\n // ─── Radio Management ───\n\n /**\n * Cached list of child hx-radio elements; invalidated on slot change.\n * @internal\n */\n private _cachedRadios: HelixRadio[] | null = null;\n /**\n * Stores each radio's individual disabled state before group-level disabling overrides it.\n * @internal\n */\n private _individualDisabledStates = new WeakMap<HelixRadio, boolean>();\n\n /**\n * Returns all child hx-radio elements, using the cache when available.\n * @internal\n */\n private _getRadios(): HelixRadio[] {\n if (!this._cachedRadios) {\n this._cachedRadios = Array.from(this.querySelectorAll('hx-radio')) as HelixRadio[];\n }\n return this._cachedRadios;\n }\n\n /**\n * Returns only the child hx-radio elements that are not disabled.\n * @internal\n */\n private _getEnabledRadios(): HelixRadio[] {\n return this._getRadios().filter((radio) => !radio.disabled && !this.disabled);\n }\n\n /**\n * Synchronizes checked state, disabled state, and roving tabindex across all child radios.\n * @internal\n */\n private _syncRadios(): void {\n const radios = this._getRadios();\n const enabledRadios = this._getEnabledRadios();\n\n radios.forEach((radio) => {\n const isChecked = radio.value === this.value && this.value !== '';\n radio.checked = isChecked;\n\n if (this.disabled) {\n // Store individual disabled state before overriding with group disabled\n if (!this._individualDisabledStates.has(radio)) {\n this._individualDisabledStates.set(radio, radio.disabled);\n }\n radio.disabled = true;\n } else {\n // Restore individual disabled state when group is re-enabled\n const originalDisabled = this._individualDisabledStates.get(radio);\n if (originalDisabled !== undefined) {\n radio.disabled = originalDisabled;\n this._individualDisabledStates.delete(radio);\n }\n }\n });\n\n // Roving tabindex management\n const checkedRadio = enabledRadios.find((r) => r.checked);\n radios.forEach((radio) => {\n radio.tabIndex = -1;\n });\n\n if (checkedRadio) {\n checkedRadio.tabIndex = 0;\n } else if (enabledRadios.length > 0) {\n const firstRadio = enabledRadios[0];\n if (firstRadio) {\n firstRadio.tabIndex = 0;\n }\n }\n }\n\n // ─── Event Handling ───\n\n /**\n * Handles the internal hx-radio-select event to update the group's selected value.\n * @internal\n */\n private _handleRadioSelect = (e: Event): void => {\n if (!(e instanceof CustomEvent)) return;\n e.stopPropagation();\n\n const newValue = (e.detail as { value: string }).value;\n if (newValue === this.value) {\n return;\n }\n\n this.value = newValue;\n this._handleInteractionInput();\n // Reactive update in updated() will call setFormValue, _syncRadios, _updateValidity\n\n /**\n * Dispatched when the selected radio changes.\n * @event hx-change\n */\n this.dispatchEvent(\n new CustomEvent<{ value: string; checked: boolean }>('hx-change', {\n bubbles: true,\n composed: true,\n detail: { value: this.value, checked: true },\n }),\n );\n };\n\n /**\n * Handles keyboard navigation (arrow keys, Home, End, Space) within the radio group.\n * @internal\n */\n private _handleKeydown = (e: KeyboardEvent): void => {\n const enabledRadios = this._getEnabledRadios();\n if (enabledRadios.length === 0) {\n return;\n }\n\n const isHandledKey = [\n 'ArrowUp',\n 'ArrowDown',\n 'ArrowLeft',\n 'ArrowRight',\n ' ',\n 'Home',\n 'End',\n ].includes(e.key);\n if (!isHandledKey) {\n return;\n }\n\n e.preventDefault();\n\n // Space: select the currently focused radio without moving focus\n if (e.key === ' ') {\n const targetRadio = (e.target as Element)?.closest?.('hx-radio') as HelixRadio | null;\n if (targetRadio && !targetRadio.disabled) {\n targetRadio.dispatchEvent(\n new CustomEvent<{ value: string }>('hx-radio-select', {\n bubbles: true,\n composed: true,\n detail: { value: targetRadio.value },\n }),\n );\n }\n return;\n }\n\n const targetRadio = (e.target as Element)?.closest?.('hx-radio') as HelixRadio | null;\n const currentIndex = targetRadio\n ? enabledRadios.indexOf(targetRadio)\n : enabledRadios.findIndex((radio) => radio.checked);\n\n let nextIndex: number;\n if (e.key === 'Home') {\n nextIndex = 0;\n } else if (e.key === 'End') {\n nextIndex = enabledRadios.length - 1;\n } else if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {\n nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % enabledRadios.length;\n } else {\n nextIndex = currentIndex <= 0 ? enabledRadios.length - 1 : currentIndex - 1;\n }\n\n const nextRadio = enabledRadios[nextIndex];\n if (nextRadio) {\n nextRadio.focus();\n nextRadio.dispatchEvent(\n new CustomEvent<{ value: string }>('hx-radio-select', {\n bubbles: true,\n composed: true,\n detail: { value: nextRadio.value },\n }),\n );\n }\n };\n\n /**\n * Handles slotchange events on the default slot to refresh the radio cache.\n * @internal\n */\n private _handleSlotChange(): void {\n this._cachedRadios = null;\n this._syncRadios();\n }\n\n // ─── Form Integration ───\n\n /**\n * Updates the ElementInternals validity state based on the required constraint and current value.\n * @internal\n */\n override _updateValidity(): void {\n if (this.required && !this.value) {\n this._internals.setValidity(\n { valueMissing: true },\n this.error || 'Please select an option.',\n this._groupEl ?? undefined,\n );\n } else {\n this._internals.setValidity({});\n }\n }\n\n /** @internal */\n protected override _onFormReset(): void {\n this.value = '';\n this._internals.setFormValue(null);\n this._syncRadios();\n this._resetInteractionState();\n }\n\n /** @internal */\n protected override _onFormStateRestore(\n state: File | string | FormData | null,\n _mode: 'restore' | 'autocomplete',\n ): void {\n if (typeof state === 'string') {\n this.value = state;\n }\n }\n\n /** @internal */\n protected override _onFormDisabled(disabled: boolean): void {\n this.disabled = disabled;\n }\n\n // ─── Render ───\n\n override render() {\n const hasError = !!this.error;\n const legendId = `${this._groupId}-legend`;\n\n const fieldsetClasses = {\n fieldset: true,\n 'fieldset--error': hasError,\n 'fieldset--disabled': this.disabled,\n 'fieldset--required': this.required,\n };\n\n // WCAG 1.3.1: _errorId is now on the persistent wrapper div around the error slot,\n // so it remains valid whether error content comes from the slot or the property.\n const hasHelp = !!this.helpText;\n const hasErrorContent = hasError || this._hasErrorSlot;\n const describedByIds = [\n hasErrorContent ? this._errorId : null,\n hasHelp ? this._helpTextId : null,\n ]\n .filter(Boolean)\n .join(' ');\n const describedBy = describedByIds || nothing;\n\n return html`\n <fieldset\n part=\"fieldset\"\n class=${classMap(fieldsetClasses)}\n role=\"radiogroup\"\n aria-labelledby=${this.label ? legendId : nothing}\n aria-describedby=${describedBy}\n aria-required=${this.required ? 'true' : nothing}\n >\n ${this.label\n ? html`\n <legend part=\"legend\" class=\"fieldset__legend\" id=${legendId}>\n ${this.label}\n ${this.required\n ? html`<span class=\"fieldset__required-marker\" aria-hidden=\"true\">*</span>`\n : nothing}\n </legend>\n `\n : nothing}\n\n <div part=\"group\" class=\"fieldset__group\" role=\"none\">\n <slot @slotchange=${this._handleSlotChange}></slot>\n </div>\n\n <!-- WCAG 1.3.1: wrap slot in a persistent container so _errorId stays stable\n regardless of whether error content comes from the slot or the property. -->\n <div id=${this._errorId}>\n <slot name=\"error\" @slotchange=${this._handleErrorSlotChange}>\n ${hasError\n ? html`<div part=\"error\" class=\"fieldset__error\" role=\"alert\">${this.error}</div>`\n : nothing}\n </slot>\n </div>\n\n ${this.helpText && !hasError\n ? html`\n <div part=\"help-text\" class=\"fieldset__help-text\" id=${this._helpTextId}>\n <slot name=\"help-text\">${this.helpText}</slot>\n </div>\n `\n : nothing}\n </fieldset>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-radio-group': HelixRadioGroup;\n }\n}\n\n/** Canonical type alias for the hx-radio-group component. */\nexport type HxRadioGroup = HelixRadioGroup;\n","import { css } from 'lit';\n\nexport const helixRadioStyles = css`\n :host {\n display: block;\n }\n\n :host([disabled]) {\n opacity: var(--hx-opacity-disabled, 0.5);\n pointer-events: none;\n }\n\n * {\n box-sizing: border-box;\n }\n\n .radio {\n display: inline-flex;\n align-items: center;\n gap: var(--hx-space-2, 0.5rem);\n /* WCAG 2.5.5 (healthcare mandate): minimum 44px touch target height */\n min-height: var(--hx-touch-target-min, 2.75rem);\n cursor: pointer;\n position: relative;\n font-family: var(--hx-radio-font-family, var(--hx-font-family-sans, sans-serif));\n }\n\n .radio--disabled {\n cursor: not-allowed;\n }\n\n /* ─── Hidden Native Input ─── */\n\n .radio__input {\n position: absolute;\n width: var(--hx-space-px);\n height: var(--hx-space-px);\n padding: 0;\n margin: calc(var(--hx-space-px) * -1);\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n\n /* ─── Visual Radio Circle ─── */\n\n .radio__control {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--hx-radio-size, var(--hx-size-5, 1.25rem));\n height: var(--hx-radio-size, var(--hx-size-5, 1.25rem));\n border: var(--hx-border-width-medium, 2px) solid\n var(--hx-radio-border-color, var(--hx-color-border-strong, #66787b));\n border-radius: var(--hx-border-radius-full, 9999px);\n background-color: var(--hx-radio-bg, var(--hx-color-surface-default, #ffffff));\n transition:\n border-color var(--hx-transition-fast, 150ms ease),\n background-color var(--hx-transition-fast, 150ms ease),\n box-shadow var(--hx-transition-fast, 150ms ease);\n flex-shrink: 0;\n }\n\n /* ─── Inner Dot ─── */\n\n .radio__dot {\n width: calc(var(--hx-radio-size, var(--hx-size-5, 1.25rem)) * 0.4);\n height: calc(var(--hx-radio-size, var(--hx-size-5, 1.25rem)) * 0.4);\n border-radius: var(--hx-border-radius-full, 9999px);\n background-color: var(--hx-radio-dot-color, var(--hx-color-text-on-primary, #ffffff));\n transform: scale(0);\n transition: transform var(--hx-transition-fast, 150ms ease);\n }\n\n /* ─── Checked State ─── */\n\n .radio--checked .radio__control {\n border-color: var(--hx-radio-checked-border-color, var(--hx-color-primary-500, #429797));\n background-color: var(--hx-radio-checked-bg, var(--hx-color-primary-500, #429797));\n }\n\n .radio--checked .radio__dot {\n transform: scale(1);\n }\n\n /* ─── Focus State ─── */\n\n :host(:focus-visible) .radio__control {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-radio-focus-ring-color, var(--hx-focus-ring-color, #0f7078));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n /* ─── Hover State ─── */\n\n .radio:not(.radio--disabled):not(.radio--checked):hover .radio__control {\n border-color: var(--hx-radio-hover-border-color, var(--hx-color-border-strong, #66787b));\n }\n\n /* ─── Label ─── */\n\n .radio__label {\n font-size: var(--hx-font-size-md, 1rem);\n color: var(--hx-radio-label-color, var(--hx-color-text-strong, #202b39));\n line-height: var(--hx-line-height-normal, 1.5);\n user-select: none;\n -webkit-user-select: none;\n }\n\n /* ─── Reduced Motion ─── */\n\n @media (prefers-reduced-motion: reduce) {\n .radio__control,\n .radio__dot {\n transition: none;\n }\n }\n\n /* ─── High Contrast Mode (forced-colors) ─── */\n\n @media (forced-colors: active) {\n .radio__control {\n forced-color-adjust: none;\n background-color: ButtonFace;\n border: 2px solid ButtonText;\n }\n\n :host(:focus-visible) .radio__control {\n outline: 3px solid Highlight;\n outline-offset: 2px;\n }\n\n .radio--checked .radio__control {\n background-color: Highlight;\n border-color: Highlight;\n }\n\n .radio--checked .radio__dot {\n background-color: HighlightText;\n }\n\n :host([disabled]) {\n opacity: 1;\n }\n\n :host([disabled]) .radio__control {\n border-color: GrayText;\n background-color: ButtonFace;\n }\n\n :host([disabled]) .radio--checked .radio__control {\n background-color: GrayText;\n border-color: GrayText;\n }\n\n :host([disabled]) .radio__label {\n color: GrayText;\n }\n\n .radio__label {\n color: CanvasText;\n }\n }\n`;\n","import { html, 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 { HelixElement, createIdCounter } from '../../base/index.js';\nimport { helixRadioStyles } from './hx-radio.styles.js';\nimport { forcedColorsField } from '../../styles/forced-colors.js';\n\nconst _nextRadioId = createIdCounter('hx-radio');\n\n/**\n * An individual radio button, designed to be used inside a `<hx-radio-group>`.\n *\n * @summary Presentational radio button managed by its parent radio group.\n *\n * @tag hx-radio\n *\n * @slot - Custom label content (overrides the label property).\n *\n * @csspart radio - The visual radio circle.\n * @csspart label - The label text.\n *\n * @cssprop [--hx-radio-size=var(--hx-size-5, 1.25rem)] - Radio circle size.\n * @cssprop [--hx-radio-border-color=var(--hx-color-neutral-300, #B6BFB9)] - Radio border color.\n * @cssprop [--hx-radio-checked-bg=var(--hx-color-primary-500, #429797)] - Checked background color.\n * @cssprop [--hx-radio-checked-border-color=var(--hx-color-primary-500, #429797)] - Checked border color.\n * @cssprop [--hx-radio-dot-color=var(--hx-color-neutral-0, #ffffff)] - Inner dot color when checked.\n * @cssprop [--hx-radio-focus-ring-color=var(--hx-focus-ring-color, #6AB1B1)] - Focus ring color.\n * @cssprop [--hx-radio-label-color=var(--hx-color-neutral-700, #313E4B)] - Label text color.\n */\n@customElement('hx-radio')\nexport class HelixRadio extends HelixElement {\n static override styles = [helixRadioStyles, forcedColorsField];\n\n // ─── Properties ───\n\n /**\n * The value this radio represents.\n * @attr value\n */\n @property({ type: String })\n value = '';\n\n /**\n * Visible label text for the radio.\n * @attr label\n */\n @property({ type: String })\n label = '';\n\n /**\n * Whether this radio is disabled.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Whether this radio is checked. Managed by the parent group.\n * @attr checked\n */\n @property({ type: Boolean, reflect: true })\n checked = false;\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n // ARIA role and state are projected to the accessibility tree via\n // ElementInternals rather than imperative setAttribute calls. This keeps\n // the host element's attribute surface clean — consumers cannot accidentally\n // override role/aria-* — and aligns with the standards pattern for\n // custom elements. See https://wicg.github.io/aom/spec/aria-reflection.html.\n this._internals.role = 'radio';\n this._syncAriaState();\n }\n\n override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n if (\n changedProperties.has('checked') ||\n changedProperties.has('label') ||\n changedProperties.has('disabled')\n ) {\n this._syncAriaState();\n }\n }\n\n /**\n * Mirror reactive ARIA state onto ElementInternals. Setting a value to `null`\n * removes it from the accessibility tree (matching the previous\n * removeAttribute behavior).\n *\n * WCAG 4.1.2: expose the label text as ariaLabel on the host so assistive\n * technology can associate the visible label with the radio role. The label\n * span lives inside Shadow DOM and aria-labelledby cannot cross shadow\n * boundaries, so ariaLabel on the host is the correct pattern here.\n *\n * WCAG 4.1.2: omit ariaDisabled entirely when not disabled. Setting\n * aria-disabled=\"false\" is verbose and unnecessary — omission is preferred.\n *\n * @internal\n */\n private _syncAriaState(): void {\n this._internals.ariaChecked = String(this.checked);\n this._internals.ariaLabel = this.label || null;\n this._internals.ariaDisabled = this.disabled ? 'true' : null;\n }\n\n // ─── Internal IDs ───\n\n /** @internal */\n private _inputId = _nextRadioId();\n\n // ─── Event Handling ───\n\n /** @internal */\n private _handleClick(): void {\n if (this.disabled) {\n return;\n }\n\n /**\n * Internal event dispatched to signal selection to the parent group.\n * Not part of the public API.\n * @internal\n */\n this.dispatchEvent(\n new CustomEvent<{ value: string }>('hx-radio-select', {\n bubbles: true,\n composed: true,\n detail: { value: this.value },\n }),\n );\n }\n\n // ─── Render ───\n\n override render() {\n const classes = {\n radio: true,\n 'radio--checked': this.checked,\n 'radio--disabled': this.disabled,\n };\n\n return html`\n <div class=${classMap(classes)} @click=${this._handleClick}>\n <input\n class=\"radio__input\"\n type=\"radio\"\n id=${this._inputId}\n .checked=${this.checked}\n ?disabled=${this.disabled}\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n <span part=\"radio\" class=\"radio__control\" aria-hidden=\"true\">\n <span class=\"radio__dot\"></span>\n </span>\n <span part=\"label\" class=\"radio__label\">\n <slot>${this.label}</slot>\n </span>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-radio': HelixRadio;\n }\n}\n\n/** Canonical type alias for the hx-radio component. */\nexport type HxRadio = HelixRadio;\n"],"names":["helixRadioGroupStyles","css","_nextRadioGroupId","createIdCounter","HelixRadioGroup","FormMixin","HelixElement","newValue","enabledRadios","targetRadio","_b","_a","_d","_c","currentIndex","radio","nextIndex","nextRadio","changedProperties","errorEl","msg","radios","isChecked","originalDisabled","checkedRadio","firstRadio","state","_mode","disabled","hasError","legendId","fieldsetClasses","hasHelp","describedBy","nothing","html","classMap","forcedColorsField","__decorateClass","property","customElement","helixRadioStyles","_nextRadioId","HelixRadio","classes"],"mappings":"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"}
@@ -46,8 +46,7 @@ const w = f`
46
46
  }
47
47
 
48
48
  .symbol:focus-visible {
49
- outline: var(--hx-focus-ring-width, 2px) solid
50
- var(--hx-focus-ring-color, var(--hx-color-primary-600, #0f7078));
49
+ outline: var(--hx-focus-ring-width, 2px) solid var(--hx-focus-ring-color, #0f7078);
51
50
  outline-offset: var(--hx-focus-ring-offset, 2px);
52
51
  border-radius: var(--hx-border-radius-sm, 0.25rem);
53
52
  }
@@ -479,4 +478,4 @@ l = n([
479
478
  export {
480
479
  l as H
481
480
  };
482
- //# sourceMappingURL=hx-rating-CGtsejNf.js.map
481
+ //# sourceMappingURL=hx-rating-C3QP53k9.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-rating-C3QP53k9.js","sources":["../../src/components/hx-rating/hx-rating.styles.ts","../../src/components/hx-rating/hx-rating.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixRatingStyles = 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 /* ─── Base Container ─── */\n\n .base {\n display: inline-flex;\n align-items: center;\n gap: var(--hx-rating-gap, var(--hx-space-1, 0.25rem));\n font-size: var(--hx-rating-size, var(--hx-font-size-xl, 1.25rem));\n }\n\n .base--readonly {\n cursor: default;\n }\n\n .base--disabled {\n cursor: not-allowed;\n }\n\n /* ─── Symbol (each star) ─── */\n\n .symbol {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n position: relative;\n cursor: pointer;\n color: var(--hx-rating-empty-color, var(--hx-color-border-strong, #66787b));\n line-height: 1;\n min-width: var(--hx-touch-target-min, 2.75rem);\n min-height: var(--hx-touch-target-min, 2.75rem);\n transition: transform var(--hx-transition-fast, 150ms ease);\n }\n\n .symbol:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid var(--hx-focus-ring-color, #0f7078);\n outline-offset: var(--hx-focus-ring-offset, 2px);\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n }\n\n .symbol--full,\n .symbol--half {\n color: var(--hx-rating-color, var(--hx-color-warning-400, #da904f));\n }\n\n .symbol--disabled {\n cursor: not-allowed;\n }\n\n .base:not(.base--readonly) .symbol:hover {\n transform: scale(1.15);\n color: var(--hx-rating-hover-color, var(--hx-color-warning-300, #eeb383));\n }\n\n /* ─── Half-Star Layout ─── */\n\n .star-half {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n position: relative;\n width: 1em;\n height: 1em;\n }\n\n .star-half__filled {\n position: absolute;\n left: 0;\n top: 0;\n /* Clip to left 50% for the filled half */\n clip-path: inset(0 50% 0 0);\n }\n\n .star-half__empty {\n position: absolute;\n left: 0;\n top: 0;\n color: var(--hx-rating-empty-color, var(--hx-color-border-strong, #66787b));\n /* Clip to right 50% for the empty half */\n clip-path: inset(0 0 0 50%);\n }\n\n /* ─── Reduced Motion ─── */\n\n @media (prefers-reduced-motion: reduce) {\n .symbol {\n transition: none;\n }\n }\n\n /* ─── High Contrast Mode (forced-colors) ─── */\n\n @media (forced-colors: active) {\n .symbol {\n forced-color-adjust: none;\n color: ButtonText;\n }\n\n .symbol:focus-visible {\n outline: 3px solid Highlight;\n outline-offset: 2px;\n }\n\n .symbol--full,\n .symbol--half {\n color: Highlight;\n }\n\n .star-half__empty {\n color: ButtonText;\n }\n\n .base:not(.base--readonly) .symbol:hover {\n color: Highlight;\n }\n\n .symbol--disabled {\n color: GrayText;\n }\n\n :host([disabled]) {\n opacity: 1;\n }\n\n :host([disabled]) .symbol {\n color: GrayText;\n }\n }\n`;\n","// @vrt-approved: A11Y-RATING-001 No VRT snapshots exist for hx-rating yet; this fix\n// corrects a WCAG 2.5.3 accessibility violation and does not change visual appearance.\n// VRT infrastructure for hx-rating to be added as a follow-up.\n// @accessibility-engineer-approved: A11Y-RATING-001\n// Star <span> elements are children of role=\"radiogroup\" or role=\"slider\" containers.\n// Keyboard navigation is handled at the container level (WAI-ARIA composite widget pattern)\n// per https://www.w3.org/WAI/ARIA/apg/patterns/radio/ and\n// https://www.w3.org/WAI/ARIA/apg/patterns/slider/. Individual star spans are not\n// keyboard focus targets — the container div receives @keydown. In the precision=0.5\n// slider branch, star spans carry role=\"presentation\" aria-hidden=\"true\" and are purely\n// decorative click/hover targets with no independent keyboard accessibility obligation.\n\nimport { html, nothing, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { HelixElement } from '../../base/index.js';\nimport { FormMixin } from '../../mixins/FormMixin.js';\nimport { helixRatingStyles } from './hx-rating.styles.js';\nimport { forcedColorsInteractive } from '../../styles/forced-colors.js';\n\n// ─── Event Detail Interfaces ───\n\n/** Detail payload for the hx-change event. */\nexport interface HxRatingChangeDetail {\n /** The new rating value after the change. */\n value: number;\n}\n\n/** Detail payload for the hx-hover event. */\nexport interface HxRatingHoverDetail {\n /** The rating value being previewed on hover. */\n value: number;\n}\n\n/**\n * A star rating input component for user feedback and display.\n * Supports whole and half-star ratings, keyboard navigation, hover preview,\n * and native form participation via ElementInternals.\n *\n * ### Accessibility\n *\n * - **Interactive mode (precision=1)**: Uses `role=\"radiogroup\"` with individual `role=\"radio\"` stars.\n * Each star has `aria-label` (\"1 star\", \"2 stars\", etc.) and `aria-checked`.\n * - **Interactive mode (precision=0.5)**: Uses `role=\"slider\"` with `aria-valuemin`, `aria-valuemax`,\n * `aria-valuenow`, and `aria-valuetext` (e.g. \"2.5 out of 5 stars\"). Star elements are\n * `aria-hidden=\"true\"` decorative visuals. This avoids a WCAG 2.5.3 label-content-name mismatch\n * that would occur if a `role=\"radio\"` labeled \"3 stars\" were checked for a value of 2.5.\n * - **Readonly mode**: Uses `role=\"img\"` with a descriptive `aria-label` (\"Rating: 3 out of 5\").\n * - **Keyboard**: Arrow keys (Left/Right/Up/Down) adjust value by `precision` step.\n * Home sets to 0, End sets to `max`. Focus follows the active tab stop.\n * - **Disabled**: Sets `aria-disabled=\"true\"` on the group and prevents interaction.\n *\n * @summary Star rating input for user feedback and display.\n *\n * @tag hx-rating\n *\n * @slot icon - Custom rating icon. Receives `data-state` attribute (\"full\" | \"half\" | \"empty\").\n *\n * @fires {CustomEvent<HxRatingChangeDetail>} hx-change - Dispatched when the rating value changes.\n * @fires {CustomEvent<HxRatingHoverDetail>} hx-hover - Dispatched while hovering over a star for preview.\n *\n * @csspart base - The outer container element.\n * @csspart symbol - Each individual star/icon element.\n *\n * @cssprop [--hx-rating-color=var(--hx-color-warning-400,#fbbf24)] - Filled star color.\n * @cssprop [--hx-rating-empty-color=var(--hx-color-neutral-300,#d1d5db)] - Empty star color.\n * @cssprop [--hx-rating-hover-color=var(--hx-color-warning-300,#fcd34d)] - Star color on hover.\n * @cssprop [--hx-rating-size=var(--hx-font-size-xl,1.25rem)] - Star icon size.\n * @cssprop [--hx-rating-gap=var(--hx-space-1,0.25rem)] - Gap between stars.\n *\n * @example\n * ```html\n * <!-- Interactive rating -->\n * <hx-rating value=\"3\" max=\"5\" label=\"Product rating\"></hx-rating>\n *\n * <!-- Read-only display -->\n * <hx-rating value=\"4.5\" max=\"5\" precision=\"0.5\" readonly></hx-rating>\n * ```\n * @cssprop [--hx-opacity-disabled] - Opacity.\n * @cssprop [--hx-space-1] - Spacing token.\n * @cssprop [--hx-font-size-xl] - Font size.\n * @cssprop [--hx-touch-target-min] - Minimum touch target size.\n * @cssprop [--hx-transition-fast] - Transition timing.\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-border-radius-sm] - CSS custom property.\n * @cssprop [--hx-color-warning-400] - Color.\n * @cssprop [--hx-color-warning-300] - Color.\n * @cssprop [--hx-color-neutral-300] - Color.\n */\n@customElement('hx-rating')\nexport class HelixRating extends FormMixin(HelixElement) {\n static override styles = [helixRatingStyles, forcedColorsInteractive];\n\n // ─── Form Association ───\n\n /** @internal */\n static override formAssociated = true;\n\n // ─── Properties ───\n\n /**\n * The current rating value (0 to max).\n * @attr value\n */\n @property({ type: Number, reflect: true })\n value = 0;\n\n /**\n * The maximum number of stars.\n * @attr max\n */\n @property({ type: Number, reflect: true })\n max = 5;\n\n /**\n * The minimum selectable increment. Use 0.5 for half-star ratings.\n * @attr precision\n */\n @property({ type: Number, reflect: true })\n precision: 0.5 | 1 = 1;\n\n /**\n * When true, the rating is display-only and cannot be changed.\n * @attr readonly\n */\n @property({ type: Boolean, reflect: true })\n readonly = false;\n\n /**\n * When true, the rating is disabled and cannot be interacted with.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * The name submitted with the form.\n * @attr name\n */\n @property({ type: String, reflect: true })\n name = '';\n\n /**\n * Accessible label for the rating group.\n * @attr label\n */\n @property({ type: String })\n label = '';\n\n /**\n * When true, a non-zero rating is required for form submission.\n * @attr required\n */\n @property({ type: Boolean, reflect: true })\n required = false;\n\n /**\n * Generates the accessible label for individual star elements.\n * Handles singular/plural automatically.\n * @param count - star count (1-based)\n */\n @property({ attribute: false })\n labelStar: (count: number) => string = (count) => (count === 1 ? '1 star' : `${count} stars`);\n\n /**\n * Generates the aria-valuetext for the composite rating widget.\n * @param value - current rating value\n * @param max - maximum rating value\n */\n @property({ attribute: false })\n labelValueText: (value: number, max: number) => string = (value, max) =>\n `${value} out of ${max} stars`;\n\n /** @internal */\n @state() private _hoverValue: number | null = null;\n\n /** @internal */\n private _defaultValue = 0;\n\n // ─── Lifecycle ───\n\n override firstUpdated(): void {\n this._defaultValue = this.value;\n }\n\n override updated(changedProps: PropertyValues<this>): void {\n super.updated(changedProps);\n if (changedProps.has('value') || changedProps.has('name')) {\n this._internals.setFormValue(String(this.value));\n }\n }\n\n protected override _onFormReset(): void {\n this.value = this._defaultValue;\n this._internals.setFormValue(String(this._defaultValue));\n this._resetInteractionState();\n }\n\n protected override _onFormStateRestore(\n state: string | File | FormData | null,\n _mode: 'restore' | 'autocomplete',\n ): void {\n if (typeof state === 'string') {\n const parsed = parseFloat(state);\n if (!isNaN(parsed)) {\n this.value = parsed;\n this._internals.setFormValue(state);\n }\n }\n }\n\n protected override _onFormDisabled(disabled: boolean): void {\n this.disabled = disabled;\n }\n\n /** @internal */\n override _updateValidity(): void {\n if (this.required && this.value === 0) {\n this._internals.setValidity(\n { valueMissing: true },\n 'Please select a rating.',\n this.shadowRoot?.querySelector<HTMLElement>('[part=\"base\"]') ?? undefined,\n );\n } else {\n this._internals.setValidity({});\n }\n }\n\n // ─── Helpers ───\n\n /** @internal */\n private get _displayValue(): number {\n return this._hoverValue ?? this.value;\n }\n\n /** @internal */\n private _clampAndSnap(v: number): number {\n const clamped = Math.min(Math.max(0, v), this.max);\n const steps = Math.round(clamped / this.precision);\n const snapped = steps * this.precision;\n return parseFloat(snapped.toFixed(this.precision === 0.5 ? 1 : 0));\n }\n\n /** @internal */\n private _ariaValueText(): string {\n return this.labelValueText(this.value, this.max);\n }\n\n /** @internal */\n private _getStarState(i: number): 'full' | 'half' | 'empty' {\n const dv = this._displayValue;\n if (dv >= i) return 'full';\n if (this.precision === 0.5 && dv >= i - 0.5) return 'half';\n return 'empty';\n }\n\n /** Star i is \"checked\" when its integer value or its half-value matches the current value. */\n /** @internal */\n private _isChecked(i: number): boolean {\n if (Math.abs(this.value - i) < 0.01) return true;\n if (this.precision === 0.5 && Math.abs(this.value - (i - 0.5)) < 0.01) return true;\n return false;\n }\n\n /** Resolve the clicked/hovered value from mouse position within a star element. */\n /** @internal */\n private _resolveValue(e: MouseEvent, i: number): number {\n if (this.precision === 0.5) {\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();\n const isLeftHalf = (e.clientX - rect.left) / rect.width < 0.5;\n return isLeftHalf ? i - 0.5 : i;\n }\n return i;\n }\n\n // ─── Event Handlers ───\n\n /** @internal */\n private _setValue(v: number): void {\n if (this.readonly || this.disabled) return;\n const next = this._clampAndSnap(v);\n this.value = next;\n this._internals.setFormValue(String(next));\n this._handleInteractionInput();\n this.dispatchEvent(\n new CustomEvent<HxRatingChangeDetail>('hx-change', {\n bubbles: true,\n composed: true,\n detail: { value: next },\n }),\n );\n }\n\n /** @internal */\n private _handleKeydown(e: KeyboardEvent): void {\n if (this.readonly || this.disabled) return;\n let next: number | null = null;\n\n switch (e.key) {\n case 'ArrowRight':\n case 'ArrowUp':\n e.preventDefault();\n next = this._clampAndSnap(this.value + this.precision);\n break;\n case 'ArrowLeft':\n case 'ArrowDown':\n e.preventDefault();\n next = this._clampAndSnap(this.value - this.precision);\n break;\n case 'Home':\n e.preventDefault();\n next = 0;\n break;\n case 'End':\n e.preventDefault();\n next = this.max;\n break;\n default:\n return;\n }\n\n if (next !== null) {\n this._setValue(next);\n if (this.precision !== 0.5) {\n void this.updateComplete.then(() => {\n this.shadowRoot?.querySelector<HTMLElement>('[part=\"symbol\"][tabindex=\"0\"]')?.focus();\n });\n }\n }\n }\n\n /** @internal */\n private _handleSymbolClick(e: MouseEvent, i: number): void {\n if (this.readonly || this.disabled) return;\n this._setValue(this._resolveValue(e, i));\n }\n\n /** @internal */\n private _handleSymbolMouseEnter(e: MouseEvent, i: number): void {\n if (this.readonly || this.disabled) return;\n const val = this._resolveValue(e, i);\n this._hoverValue = val;\n this.dispatchEvent(\n new CustomEvent<HxRatingHoverDetail>('hx-hover', {\n bubbles: true,\n composed: true,\n detail: { value: val },\n }),\n );\n }\n\n /** @internal */\n private _handleSymbolMouseMove(e: MouseEvent, i: number): void {\n if (this.readonly || this.disabled || this.precision !== 0.5) return;\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();\n const isLeftHalf = (e.clientX - rect.left) / rect.width < 0.5;\n const val = isLeftHalf ? i - 0.5 : i;\n if (val !== this._hoverValue) {\n this._hoverValue = val;\n this.dispatchEvent(\n new CustomEvent<HxRatingHoverDetail>('hx-hover', {\n bubbles: true,\n composed: true,\n detail: { value: val },\n }),\n );\n }\n }\n\n /** @internal */\n private _handleMouseLeave(): void {\n this._hoverValue = null;\n }\n\n // ─── SVG Star Icons ───\n\n /** @internal */\n private _renderFullStar() {\n return html`\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n width=\"1em\"\n height=\"1em\"\n >\n <path\n d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\"\n />\n </svg>\n `;\n }\n\n /** @internal */\n private _renderHalfStar() {\n return html`\n <span class=\"star-half\" aria-hidden=\"true\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n width=\"1em\"\n height=\"1em\"\n class=\"star-half__filled\"\n >\n <path\n d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\"\n />\n </svg>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n width=\"1em\"\n height=\"1em\"\n class=\"star-half__empty\"\n >\n <path\n d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\"\n />\n </svg>\n </span>\n `;\n }\n\n /** @internal */\n private _renderEmptyStar() {\n return html`\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n width=\"1em\"\n height=\"1em\"\n >\n <path\n d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\"\n />\n </svg>\n `;\n }\n\n /** @internal */\n private _renderStarIcon(state: 'full' | 'half' | 'empty') {\n if (state === 'full') return this._renderFullStar();\n if (state === 'half') return this._renderHalfStar();\n return this._renderEmptyStar();\n }\n\n // ─── Render ───\n\n override render() {\n // Interactive mode: fall back to 'Rating' so the group/slider always has a name.\n const ariaLabel = this.label || 'Rating';\n\n // WCAG 4.1.2: in readonly mode, build the aria-label from the consumer-provided\n // `label` property rather than a hardcoded 'Rating:' prefix. When a consumer\n // supplies a label (e.g. \"Product quality\"), prepending a hardcoded \"Rating:\"\n // would create redundancy. The `labelValueText` property (default: \"X out of Y\n // stars\") lets consumers customise the value portion independently.\n const valueText = this.labelValueText(this.value, this.max);\n const readonlyAriaLabel = this.label ? `${this.label}: ${valueText}` : valueText;\n\n if (this.readonly) {\n return html`\n <div part=\"base\" class=\"base base--readonly\" role=\"img\" aria-label=${readonlyAriaLabel}>\n ${Array.from({ length: this.max }, (_, idx) => {\n const i = idx + 1;\n const state = this._getStarState(i);\n return html`\n <span part=\"symbol\" class=\"symbol symbol--${state}\" data-index=\"${i}\">\n <slot name=\"icon\" data-state=\"${state}\">${this._renderStarIcon(state)}</slot>\n </span>\n `;\n })}\n </div>\n `;\n }\n\n // Use slider pattern for half-star precision to correctly represent half values\n // in the accessibility tree (WCAG 2.5.3, axe: label-content-name-mismatch)\n if (this.precision === 0.5) {\n return html`\n <div\n part=\"base\"\n class=\"base${this.disabled ? ' base--disabled' : ''}\"\n role=\"slider\"\n aria-label=\"${ariaLabel}\"\n aria-valuemin=\"0\"\n aria-valuemax=\"${this.max}\"\n aria-valuenow=\"${this.value}\"\n aria-valuetext=\"${this._ariaValueText()}\"\n aria-disabled=\"${this.disabled ? 'true' : nothing}\"\n tabindex=\"${this.disabled ? '-1' : '0'}\"\n @keydown=\"${this._handleKeydown}\"\n @mouseleave=\"${this._handleMouseLeave}\"\n >\n ${Array.from({ length: this.max }, (_, idx) => {\n const i = idx + 1;\n const state = this._getStarState(i);\n return html`\n <span\n part=\"symbol\"\n class=\"symbol symbol--${state}${this.disabled ? ' symbol--disabled' : ''}\"\n role=\"presentation\"\n aria-hidden=\"true\"\n data-index=\"${i}\"\n @click=\"${(e: MouseEvent) => this._handleSymbolClick(e, i)}\"\n @mouseenter=\"${(e: MouseEvent) => this._handleSymbolMouseEnter(e, i)}\"\n @mousemove=\"${(e: MouseEvent) => this._handleSymbolMouseMove(e, i)}\"\n >\n <slot name=\"icon\" data-state=\"${state}\">${this._renderStarIcon(state)}</slot>\n </span>\n `;\n })}\n </div>\n `;\n }\n\n return html`\n <div\n part=\"base\"\n class=\"base${this.disabled ? ' base--disabled' : ''}\"\n role=\"radiogroup\"\n aria-label=\"${ariaLabel}\"\n aria-disabled=\"${this.disabled ? 'true' : nothing}\"\n tabindex=\"-1\"\n @keydown=\"${this._handleKeydown}\"\n @mouseleave=\"${this._handleMouseLeave}\"\n >\n ${Array.from({ length: this.max }, (_, idx) => {\n const i = idx + 1;\n const state = this._getStarState(i);\n const checked = this._isChecked(i);\n const starLabel = this.labelStar(i);\n const isActiveTabStop = this.value > 0 ? Math.ceil(this.value) === i : i === 1;\n\n return html`\n <span\n part=\"symbol\"\n class=\"symbol symbol--${state}${this.disabled ? ' symbol--disabled' : ''}\"\n role=\"radio\"\n aria-label=\"${starLabel}\"\n aria-checked=\"${checked ? 'true' : 'false'}\"\n tabindex=\"${!this.disabled && isActiveTabStop ? '0' : '-1'}\"\n data-index=\"${i}\"\n @click=\"${(e: MouseEvent) => this._handleSymbolClick(e, i)}\"\n @mouseenter=\"${(e: MouseEvent) => this._handleSymbolMouseEnter(e, i)}\"\n @mousemove=\"${(e: MouseEvent) => this._handleSymbolMouseMove(e, i)}\"\n >\n <slot name=\"icon\" data-state=\"${state}\">${this._renderStarIcon(state)}</slot>\n </span>\n `;\n })}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-rating': HelixRating;\n }\n}\n"],"names":["helixRatingStyles","css","HelixRating","FormMixin","HelixElement","count","value","max","changedProps","state","_mode","parsed","disabled","_a","v","clamped","snapped","i","dv","rect","next","_b","val","html","ariaLabel","valueText","readonlyAriaLabel","_","idx","nothing","e","checked","starLabel","isActiveTabStop","forcedColorsInteractive","__decorateClass","property","customElement"],"mappings":"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"}
@@ -29,10 +29,7 @@ const C = g`
29
29
  --_border-radius: var(--hx-select-border-radius, var(--hx-border-radius-md, 0.375rem));
30
30
 
31
31
  /* Focus ring */
32
- --_focus-ring-color: var(
33
- --hx-select-focus-ring-color,
34
- var(--hx-focus-ring-color, var(--hx-color-primary-600, #0f7078))
35
- );
32
+ --_focus-ring-color: var(--hx-select-focus-ring-color, var(--hx-focus-ring-color, #0f7078));
36
33
 
37
34
  /* Error */
38
35
  --_error-color: var(--hx-select-error-color, var(--hx-color-error-500, #e5493e));
@@ -807,4 +804,4 @@ i = l([
807
804
  export {
808
805
  i as H
809
806
  };
810
- //# sourceMappingURL=hx-select-Bf4usFts.js.map
807
+ //# sourceMappingURL=hx-select-C8fEHQhC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-select-C8fEHQhC.js","sources":["../../src/components/hx-select/hx-select.styles.ts","../../src/components/hx-select/hx-select.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixSelectStyles = css`\n /* ─── 3-tier token cascade: component → semantic → hardcoded fallback ─── */\n :host {\n display: block;\n\n /* Background & foreground */\n --_bg: var(--hx-select-bg, var(--hx-color-surface-default, #ffffff));\n --_color: var(--hx-select-color, var(--hx-color-text-strong, #202b39));\n --_placeholder-color: var(\n --hx-select-placeholder-color,\n var(--hx-color-text-placeholder, #66787b)\n );\n\n /* Label */\n --_label-color: var(--hx-select-label-color, var(--hx-color-text-strong, #202b39));\n\n /* Border */\n --_border-color: var(--hx-select-border-color, var(--hx-color-border-strong, #66787b));\n --_border-radius: var(--hx-select-border-radius, var(--hx-border-radius-md, 0.375rem));\n\n /* Focus ring */\n --_focus-ring-color: var(--hx-select-focus-ring-color, var(--hx-focus-ring-color, #0f7078));\n\n /* Error */\n --_error-color: var(--hx-select-error-color, var(--hx-color-error-500, #e5493e));\n\n /* Chevron */\n --_chevron-color: var(--hx-select-chevron-color, var(--hx-color-text-muted, #4a5362));\n --_chevron-size: var(--hx-select-chevron-size, 0.5rem);\n\n /* Listbox */\n --_listbox-bg: var(--hx-select-listbox-bg, var(--hx-color-surface-default, #ffffff));\n --_option-hover-bg: var(--hx-select-option-hover-bg, var(--hx-color-primary-50, #ebf8f8));\n --_option-selected-bg: var(\n --hx-select-option-selected-bg,\n var(--hx-color-primary-100, #dbf0f0)\n );\n\n /* Typography */\n --_font-family: var(--hx-select-font-family, var(--hx-font-family-sans, sans-serif));\n }\n\n :host([disabled]) {\n opacity: var(--hx-opacity-disabled, 0.5);\n pointer-events: none;\n }\n\n * {\n box-sizing: border-box;\n }\n\n .field {\n display: flex;\n flex-direction: column;\n gap: var(--hx-space-1, 0.25rem);\n font-family: var(--_font-family);\n position: relative;\n }\n\n .field__label {\n display: flex;\n align-items: baseline;\n gap: var(--hx-space-1, 0.25rem);\n font-size: var(--hx-font-size-sm, 0.875rem);\n font-weight: var(--hx-font-weight-medium, 500);\n color: var(--_label-color);\n line-height: var(--hx-line-height-normal, 1.5);\n }\n\n .field__required-marker {\n color: var(--hx-select-error-color, var(--hx-color-error-text, #c92a2a));\n font-weight: var(--hx-font-weight-bold, 700);\n }\n\n .field__select-wrapper {\n position: relative;\n display: block;\n }\n\n .field__trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--hx-space-2, 0.5rem);\n width: 100%;\n min-height: var(--hx-input-height-md, var(--hx-size-10, 2.5rem));\n border: var(--hx-border-width-thin, 1px) solid var(--_border-color);\n border-radius: var(--_border-radius);\n background-color: var(--_bg);\n color: var(--_color);\n font-family: inherit;\n font-size: var(--hx-font-size-md, 1rem);\n line-height: var(--hx-line-height-normal, 1.5);\n padding: var(--hx-space-2, 0.5rem) var(--hx-space-3, 0.75rem);\n cursor: pointer;\n text-align: start;\n transition:\n border-color var(--hx-transition-fast, 150ms ease),\n box-shadow var(--hx-transition-fast, 150ms ease);\n outline: none;\n }\n\n .field__trigger:focus-visible {\n border-color: var(--_focus-ring-color);\n box-shadow: 0 0 0 var(--hx-focus-ring-width, 2px)\n color-mix(\n in srgb,\n var(--_focus-ring-color) calc(var(--hx-focus-ring-opacity, 0.25) * 100%),\n transparent\n );\n }\n\n .field__trigger[aria-disabled='true'] {\n cursor: not-allowed;\n }\n\n .field__trigger--sm {\n min-height: var(--hx-input-height-sm, var(--hx-size-8, 2rem));\n font-size: var(--hx-font-size-sm, 0.875rem);\n padding: var(--hx-space-1, 0.25rem) var(--hx-space-3, 0.75rem);\n }\n\n .field__trigger--lg {\n min-height: var(--hx-input-height-lg, var(--hx-size-12, 3rem));\n font-size: var(--hx-font-size-lg, 1.125rem);\n padding: var(--hx-space-3, 0.75rem) var(--hx-space-4, 1rem);\n }\n\n .field__trigger-value {\n flex: 1;\n min-width: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .field__trigger--placeholder .field__trigger-value {\n color: var(--_placeholder-color);\n }\n\n .field__chevron {\n flex-shrink: 0;\n width: calc(var(--_chevron-size) * 1.5);\n height: var(--_chevron-size);\n position: relative;\n color: var(--_chevron-color);\n pointer-events: none;\n transition: transform var(--hx-transition-fast, 150ms ease);\n }\n\n .field__chevron::after {\n content: '';\n position: absolute;\n top: 0;\n left: var(--hx-space-px, 2px);\n width: var(--_chevron-size);\n height: var(--_chevron-size);\n border-inline-end: var(--hx-border-width-thin, 1.5px) solid currentColor;\n border-bottom: var(--hx-border-width-thin, 1.5px) solid currentColor;\n transform: rotate(45deg);\n }\n\n .field--open .field__chevron {\n transform: rotate(180deg);\n }\n\n .field--error .field__trigger {\n border-color: var(--_error-color);\n }\n\n .field--error .field__trigger:focus-visible {\n border-color: var(--_error-color);\n box-shadow: 0 0 0 var(--hx-focus-ring-width, 2px)\n color-mix(\n in srgb,\n var(--_error-color) calc(var(--hx-focus-ring-opacity, 0.25) * 100%),\n transparent\n );\n }\n\n .field__listbox {\n position: absolute;\n top: calc(100% + var(--hx-space-1, 0.25rem));\n left: 0;\n right: 0;\n z-index: var(--hx-z-index-dropdown, 1000);\n background-color: var(--_listbox-bg);\n border: var(--hx-border-width-thin, 1px) solid var(--_border-color);\n border-radius: var(--_border-radius);\n box-shadow: var(\n --hx-select-listbox-shadow,\n 0 4px 16px var(--hx-overlay-neutral-12, rgba(13, 17, 23, 0.12))\n );\n max-height: var(--hx-select-listbox-max-height, 16rem);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n .field__listbox[hidden] {\n display: none;\n }\n\n .field__options {\n overflow-y: auto;\n flex: 1;\n padding: var(--hx-space-1, 0.25rem) 0;\n }\n\n .field__option {\n display: flex;\n align-items: center;\n gap: var(--hx-space-2, 0.5rem);\n padding: var(--hx-space-2, 0.5rem) var(--hx-space-3, 0.75rem);\n font-size: var(--hx-font-size-md, 1rem);\n color: var(--_color);\n cursor: pointer;\n user-select: none;\n -webkit-user-select: none;\n transition: background-color var(--hx-transition-fast, 150ms ease);\n }\n\n .field__option:hover {\n background-color: var(--_option-hover-bg);\n }\n\n .field__option--selected {\n background-color: var(--_option-selected-bg);\n font-weight: var(--hx-font-weight-medium, 500);\n }\n\n .field__option--focused {\n background-color: var(--_option-hover-bg);\n outline: var(--hx-focus-ring-width, 2px) solid var(--_focus-ring-color);\n outline-offset: var(--hx-select-option-focus-ring-offset, -2px);\n }\n\n .field__option--focused.field__option--selected {\n background-color: var(--_option-selected-bg);\n }\n\n .field__option--disabled {\n opacity: var(--hx-opacity-disabled, 0.5);\n cursor: not-allowed;\n pointer-events: none;\n }\n\n .field__option-label {\n flex: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .field__no-options {\n padding: var(--hx-space-3, 0.75rem);\n text-align: center;\n color: var(--_placeholder-color);\n font-size: var(--hx-font-size-sm, 0.875rem);\n }\n\n .field__select {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n opacity: 0;\n pointer-events: none;\n clip: rect(0, 0, 0, 0);\n }\n\n .field__help-text,\n .field__error {\n font-size: var(--hx-font-size-xs, 0.75rem);\n line-height: var(--hx-line-height-normal, 1.5);\n }\n\n .field__help-text {\n color: var(--hx-color-text-muted, #4a5362);\n }\n\n .field__error {\n color: var(--hx-select-error-color, var(--hx-color-error-text, #c92a2a));\n }\n\n @media (prefers-reduced-motion: reduce) {\n .field__trigger,\n .field__chevron,\n .field__option {\n transition: none;\n }\n }\n\n /* ─── High Contrast Mode (forced-colors) ─── */\n\n @media (forced-colors: active) {\n .field__trigger {\n forced-color-adjust: none;\n background-color: Field;\n color: FieldText;\n border: 2px solid ButtonText;\n }\n\n .field__trigger:focus-visible {\n outline: 3px solid Highlight;\n outline-offset: 2px;\n box-shadow: none;\n }\n\n .field__trigger[aria-disabled='true'] {\n color: GrayText;\n border-color: GrayText;\n }\n\n .field__trigger--placeholder .field__trigger-value {\n color: GrayText;\n }\n\n .field__chevron::after {\n border-color: FieldText;\n }\n\n .field__listbox {\n forced-color-adjust: none;\n background-color: Canvas;\n border: 2px solid CanvasText;\n box-shadow: none;\n }\n\n .field__option {\n color: CanvasText;\n }\n\n .field__option:hover {\n background-color: Highlight;\n color: HighlightText;\n }\n\n .field__option--selected {\n background-color: Highlight;\n color: HighlightText;\n }\n\n .field__option--focused {\n outline-color: Highlight;\n background-color: Highlight;\n color: HighlightText;\n }\n\n .field__option--disabled {\n color: GrayText;\n opacity: 1;\n }\n\n .field--error .field__trigger {\n border-color: LinkText;\n }\n\n :host([disabled]) {\n opacity: 1;\n }\n\n .field__label {\n color: CanvasText;\n }\n\n .field__help-text {\n color: GrayText;\n }\n\n .field__error {\n color: LinkText;\n }\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 { ifDefined } from 'lit/directives/if-defined.js';\nimport { repeat } from 'lit/directives/repeat.js';\nimport { HelixElement, createIdCounter } from '../../base/index.js';\nimport { FormMixin } from '../../mixins/FormMixin.js';\nimport { helixSelectStyles } from './hx-select.styles.js';\nimport { forcedColorsField } from '../../styles/forced-colors.js';\nimport { devWarn } from '../../utils/dev-warn.js';\n\n// PERF: hx-select exceeds 5KB budget (6.31kb gzipped) -- custom listbox, keyboard navigation, grouped options\n\n// Module-level counter for stable, SSR-safe IDs (avoids Math.random() hydration mismatch)\nconst _nextSelectId = createIdCounter('hx-select');\n\n// ─── Internal option model ───\n\ninterface SelectOption {\n value: string;\n label: string;\n disabled: boolean;\n}\n\n/** Detail for the hx-change event dispatched by hx-select. */\nexport interface HxSelectChangeDetail {\n value: string;\n}\n\n/**\n * A form-associated select component with custom styling, label, error, and\n * help text. Options are provided via slotted `<option>` (and `<optgroup>`)\n * elements in the light DOM. The component wraps a hidden native `<select>`\n * for form participation and provides a combobox trigger for consistent\n * cross-browser styling.\n *\n * @remarks Multi-select is intentionally not supported. This component\n * implements a single-value select (combobox) pattern only. For multi-value\n * selection use a separate multi-select component.\n *\n * @remarks The listbox panel uses `position: absolute` and may be clipped by\n * ancestor elements with `overflow: hidden` or `overflow: auto`. This is a\n * known limitation when embedding the component inside cards, tables, or\n * dialogs. Use the CSS custom property `--hx-select-listbox-shadow` or\n * restructure the containing layout to avoid clipping.\n *\n * @summary Form-associated custom select with label, error, and help text.\n *\n * @tag hx-select\n *\n * @slot - Default slot for `<option>` and `<optgroup>` elements.\n * @slot label - Custom label content (overrides the label property).\n * @slot error - Custom error content (overrides the error property).\n * @slot help-text - Custom help text content (overrides the helpText property).\n *\n * @fires {CustomEvent<{value: string}>} hx-change - Dispatched when the selected option changes.\n *\n * @csspart field - The outer field container.\n * @csspart label - The label element.\n * @csspart select-wrapper - The wrapper containing the trigger and listbox.\n * @csspart select - The hidden native select element (kept for form participation).\n * @csspart trigger - The button that opens/closes the dropdown.\n * @csspart listbox - The dropdown panel containing options.\n * @csspart option - Individual option items in the listbox.\n * @csspart help-text - The help text container.\n * @csspart error - The error message container.\n *\n * @cssprop [--hx-select-bg=var(--hx-color-neutral-0)] - Select background color.\n * @cssprop [--hx-select-color=var(--hx-color-neutral-800)] - Select text color.\n * @cssprop [--hx-select-border-color=var(--hx-color-neutral-300)] - Select border color.\n * @cssprop [--hx-select-border-radius=var(--hx-border-radius-md)] - Select border radius.\n * @cssprop [--hx-select-font-family=var(--hx-font-family-sans)] - Select font family.\n * @cssprop [--hx-select-focus-ring-color=var(--hx-focus-ring-color)] - Focus ring color.\n * @cssprop [--hx-select-error-color=var(--hx-color-error-500)] - Error state color.\n * @cssprop [--hx-select-label-color=var(--hx-color-neutral-700)] - Label text color.\n * @cssprop [--hx-select-chevron-color=var(--hx-color-neutral-500)] - Chevron indicator color.\n * @cssprop [--hx-select-chevron-size=0.5rem] - Chevron indicator size (width/height base unit).\n * @cssprop [--hx-select-listbox-bg=var(--hx-color-neutral-0)] - Listbox panel background color.\n * @cssprop [--hx-select-option-hover-bg=var(--hx-color-primary-50)] - Option hover background color.\n * @cssprop [--hx-select-option-selected-bg=var(--hx-color-primary-100)] - Selected option background color.\n * @cssprop [--hx-select-placeholder-color=var(--hx-color-neutral-400)] - Placeholder text color.\n * @cssprop [--hx-color-neutral-0] - Color.\n * @cssprop [--hx-color-neutral-800] - Color.\n * @cssprop [--hx-color-neutral-400] - Color.\n * @cssprop [--hx-color-neutral-700] - Color.\n * @cssprop [--hx-color-neutral-300] - Color.\n * @cssprop [--hx-border-radius-md] - CSS custom property.\n * @cssprop [--hx-focus-ring-color] - Color.\n * @cssprop [--hx-color-primary-400] - Color.\n * @cssprop [--hx-color-error-500] - Color.\n * @cssprop [--hx-color-neutral-500] - Color.\n * @cssprop [--hx-color-primary-50] - Color.\n * @cssprop [--hx-color-primary-100] - Color.\n * @cssprop [--hx-font-family-sans] - Font family.\n * @cssprop [--hx-opacity-disabled] - Opacity.\n * @cssprop [--hx-space-1] - Spacing token.\n * @cssprop [--hx-font-size-sm] - Font size.\n * @cssprop [--hx-font-weight-medium] - Font weight.\n * @cssprop [--hx-line-height-normal] - Line height.\n * @cssprop [--hx-color-error-text] - Color.\n * @cssprop [--hx-font-weight-bold] - Font weight.\n * @cssprop [--hx-space-2] - Spacing token.\n * @cssprop [--hx-input-height-md] - Height.\n * @cssprop [--hx-size-10] - Size token.\n * @cssprop [--hx-border-width-thin] - Width.\n * @cssprop [--hx-font-size-md] - Font size.\n * @cssprop [--hx-space-3] - Spacing token.\n * @cssprop [--hx-transition-fast] - Transition timing.\n * @cssprop [--hx-focus-ring-width] - Width.\n * @cssprop [--hx-focus-ring-opacity] - CSS custom property.\n * @cssprop [--hx-input-height-sm] - Height.\n * @cssprop [--hx-size-8] - Size token.\n * @cssprop [--hx-input-height-lg] - Height.\n * @cssprop [--hx-size-12] - Size token.\n * @cssprop [--hx-font-size-lg] - Font size.\n * @cssprop [--hx-space-4] - Spacing token.\n * @cssprop [--hx-space-px] - Spacing token.\n * @cssprop [--hx-z-index-dropdown] - Z-index layer.\n * @cssprop [--hx-select-listbox-shadow] - CSS custom property.\n * @cssprop [--hx-overlay-neutral-12] - Overlay color.\n * @cssprop [--hx-select-listbox-max-height=16rem] - Height.\n * @cssprop [--hx-color-primary-500] - Color.\n * @cssprop [--hx-select-option-focus-ring-offset=-2px] - Focus ring styling.\n * @cssprop [--hx-font-size-xs] - Font size.\n */\n@customElement('hx-select')\nexport class HelixSelect extends FormMixin(HelixElement) {\n static override styles = [helixSelectStyles, forcedColorsField];\n\n // ─── Form Association ───\n\n /** Marks this element as form-associated for ElementInternals support. @internal */\n static override formAssociated = true;\n\n // ─── Stable IDs ───\n\n /** @internal */\n private _selectId = _nextSelectId();\n /** @internal */\n private _listboxId = `${this._selectId}-listbox`;\n /** @internal */\n private _labelId = `${this._selectId}-label`;\n /** @internal */\n private _helpTextId = `${this._selectId}-help`;\n /** @internal */\n private _errorId = `${this._selectId}-error`;\n\n // ─── Public Properties ───\n\n /**\n * The visible label text for the select.\n * @attr label\n */\n @property({ type: String })\n label = '';\n\n /**\n * Placeholder text shown in the trigger when no option is selected.\n * @attr placeholder\n */\n @property({ type: String })\n placeholder = '';\n\n /**\n * The current value of the select.\n * @attr value\n */\n @property({ type: String, reflect: true })\n value = '';\n\n /**\n * Whether the select is required for form submission.\n * @attr required\n */\n @property({ type: Boolean, reflect: true })\n required = false;\n\n /**\n * Whether the select is disabled.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * The name used for form submission.\n * @attr name\n */\n @property({ type: String, reflect: true })\n name = '';\n\n /**\n * Error message to display. When set, the field enters an error state.\n * @attr error\n */\n @property({ type: String })\n error = '';\n\n /**\n * Help text displayed below the select for guidance.\n * @attr help-text\n */\n @property({ type: String, attribute: 'help-text' })\n helpText = '';\n\n /**\n * Size variant of the select trigger.\n * @attr hx-size\n */\n @property({ type: String, attribute: 'hx-size', reflect: true })\n size: 'sm' | 'md' | 'lg' = 'md';\n\n /**\n * Accessible name for screen readers, if different from the visible label.\n * Uses `accessible-label` attribute instead of `aria-label` to avoid\n * ARIAMixin shadowing on the host element.\n *\n * Note: `mixinDelegatesAria` is not applied to this component because form\n * inputs with associated labels delegate accessible naming via `<label>`\n * association and `aria-labelledby`, not host-level ARIA delegation. The\n * `accessible-label` attribute is a fallback for label-free usage. The value is forwarded to the\n * internal trigger button's `aria-label`.\n * @attr accessible-label\n */\n @property({ type: String, attribute: 'accessible-label' })\n accessibleLabel: string | null = null;\n\n /**\n * Controls whether the dropdown listbox is open.\n * @attr open\n */\n @property({ type: Boolean, reflect: true })\n open = false;\n\n /**\n * Validation message when no option is selected. Override for i18n.\n * @attr label-required\n */\n @property({ attribute: 'label-required' }) labelRequired = 'Please select an option.';\n\n /**\n * Label shown when no options are available. Override for i18n.\n * @attr label-no-options\n */\n @property({ attribute: 'label-no-options' }) labelNoOptions = 'No options found';\n\n // ─── Internal State ───\n\n /** Parsed option models derived from slotted `<option>` and `<optgroup>` elements. @internal */\n @state() private _options: SelectOption[] = [];\n /** Whether the named error slot contains projected content. @internal */\n @state() private _hasErrorSlot = false;\n /** Zero-based index of the keyboard-focused option in the listbox; -1 means none. @internal */\n @state() private _focusedOptionIndex = -1;\n\n // ─── Queries ───\n\n /** Reference to the hidden native select element used for form participation. @internal */\n @query('.field__select')\n private _select: HTMLSelectElement | undefined;\n\n /** Reference to the visible combobox trigger element that receives keyboard focus. @internal */\n @query('.field__trigger')\n private _trigger: HTMLElement | undefined;\n\n // ─── Computed helpers ───\n\n /** @internal */\n private get _displayValue(): string {\n if (!this.value) return '';\n const opt = this._options.find((o) => o.value === this.value);\n return opt ? opt.label : this.value;\n }\n\n // ─── Lifecycle ───\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n // Safety net: remove listener if component is removed while dropdown is open\n document.removeEventListener('click', this._handleOutsideClick);\n // Reset open state to prevent persisted open state on reconnect\n if (this.open) {\n this.open = false;\n this._focusedOptionIndex = -1;\n }\n }\n\n override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n if (changedProperties.has('open')) {\n if (this.open) {\n document.addEventListener('click', this._handleOutsideClick);\n } else {\n document.removeEventListener('click', this._handleOutsideClick);\n }\n }\n if (changedProperties.has('value')) {\n this._syncNativeSelect();\n this._updateFormValue();\n }\n if (changedProperties.has('size')) {\n const validSizes: string[] = ['sm', 'md', 'lg'];\n if (!validSizes.includes(this.size)) {\n devWarn(\n 'hx-select',\n `Invalid size \"${this.size}\". Expected one of: ${validSizes.join(', ')}.`,\n );\n }\n }\n // Force screen reader re-announcement when error text changes (a11y-v3-005)\n if (changedProperties.has('error') && this.error) {\n const errorEl = this.shadowRoot?.querySelector('[role=\"alert\"]');\n if (errorEl) {\n const msg = this.error;\n requestAnimationFrame(() => {\n errorEl.textContent = '';\n requestAnimationFrame(() => {\n errorEl.textContent = msg;\n });\n });\n }\n }\n }\n\n // ─── Form Integration ───\n\n /** @internal */\n private _updateFormValue(): void {\n this._internals.setFormValue(this.value || null);\n }\n\n /** @internal */\n override _updateValidity(): void {\n if (this.required && !this.value) {\n this._internals.setValidity(\n { valueMissing: true },\n this.error || this.labelRequired,\n this._trigger ?? this._select,\n );\n } else {\n this._internals.setValidity({});\n }\n }\n\n // ─── Form Lifecycle Hooks ───\n\n protected override _onFormReset(): void {\n this.value = '';\n this._internals.setFormValue(null);\n this._resetInteractionState();\n }\n\n protected override _onFormStateRestore(\n state: File | string | FormData | null,\n _mode: 'restore' | 'autocomplete',\n ): void {\n if (typeof state === 'string') {\n this.value = state;\n }\n }\n\n protected override _onFormDisabled(disabled: boolean): void {\n this.disabled = disabled;\n }\n\n // ─── Native Select Sync ───\n\n /** @internal */\n private _syncNativeSelect(): void {\n if (!this._select) return;\n if (this.value) {\n this._select.value = this.value;\n }\n }\n\n // ─── Option Syncing from Slot ───\n\n /** @internal */\n private _parseOption(el: HTMLOptionElement): SelectOption {\n return { value: el.value, label: el.textContent?.trim() ?? el.value, disabled: el.disabled };\n }\n\n /**\n * Single-pass slot handler: reads options into _options for the custom\n * listbox AND clones them into the native <select> for form participation.\n * Handles both top-level <option> and <optgroup> children.\n */\n /** @internal */\n private _handleSlotChange(): void {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n if (!slot) return;\n\n const parsed: SelectOption[] = [];\n\n // Remove previously cloned options from native select\n if (this._select) {\n this._select.querySelectorAll('option[data-cloned]').forEach((opt) => opt.remove());\n }\n\n const cloneIntoSelect = (optEl: HTMLOptionElement): void => {\n if (!this._select) return;\n const clone = optEl.cloneNode(true) as HTMLOptionElement;\n clone.setAttribute('data-cloned', '');\n this._select.appendChild(clone);\n };\n\n for (const el of slot.assignedElements({ flatten: true })) {\n if (el instanceof HTMLOptionElement) {\n parsed.push(this._parseOption(el));\n cloneIntoSelect(el);\n } else if (el instanceof HTMLOptGroupElement) {\n for (const child of Array.from(el.children)) {\n if (child instanceof HTMLOptionElement) {\n parsed.push(this._parseOption(child));\n cloneIntoSelect(child);\n }\n }\n }\n }\n\n this._options = parsed;\n\n if (parsed.length === 0) {\n devWarn(\n 'hx-select',\n 'hx-select has no options — add <option> or <optgroup> elements as children.',\n );\n }\n\n if (this._select) {\n if (this.value) {\n this._select.value = this.value;\n } else if (!this.placeholder && parsed.length > 0) {\n this.value = this._select.value;\n this._updateFormValue();\n }\n }\n }\n\n // ─── Slot Change Handlers ───\n\n /** @internal */\n private _handleErrorSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasErrorSlot = slot.assignedNodes({ flatten: true }).length > 0;\n }\n\n // ─── Dropdown Control ───\n\n /** @internal */\n private _toggleDropdown(): void {\n if (!this.disabled) {\n this.open = !this.open;\n if (this.open) {\n // Pre-focus the currently selected option (or first enabled) when opening\n const selectedIndex = this._options.findIndex((o) => o.value === this.value);\n this._focusedOptionIndex = selectedIndex >= 0 ? selectedIndex : 0;\n } else {\n this._focusedOptionIndex = -1;\n }\n }\n }\n\n // ─── Keyboard Navigation ───\n\n /** @internal */\n private _handleKeydown(e: KeyboardEvent): void {\n if (this.disabled) return;\n\n const enabledIndices = this._options\n .map((o, i) => ({ o, i }))\n .filter(({ o }) => !o.disabled)\n .map(({ i }) => i);\n\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault();\n if (!this.open) {\n this.open = true;\n this._focusedOptionIndex = enabledIndices.length > 0 ? (enabledIndices[0] ?? 0) : 0;\n break;\n }\n const nextDown = enabledIndices.find((i) => i > this._focusedOptionIndex);\n this._focusedOptionIndex =\n nextDown !== undefined ? nextDown : (enabledIndices[0] ?? this._focusedOptionIndex);\n break;\n }\n case 'ArrowUp': {\n e.preventDefault();\n if (!this.open) {\n this.open = true;\n const lastEnabled = enabledIndices[enabledIndices.length - 1];\n this._focusedOptionIndex = lastEnabled !== undefined ? lastEnabled : 0;\n break;\n }\n const prevUp = [...enabledIndices].reverse().find((i) => i < this._focusedOptionIndex);\n const lastEnabledUp = enabledIndices[enabledIndices.length - 1];\n this._focusedOptionIndex =\n prevUp !== undefined ? prevUp : (lastEnabledUp ?? this._focusedOptionIndex);\n break;\n }\n case 'Home': {\n e.preventDefault();\n if (!this.open) {\n this.open = true;\n }\n this._focusedOptionIndex = enabledIndices.length > 0 ? (enabledIndices[0] ?? 0) : 0;\n break;\n }\n case 'End': {\n e.preventDefault();\n if (!this.open) {\n this.open = true;\n }\n const lastEnabled = enabledIndices[enabledIndices.length - 1];\n this._focusedOptionIndex = lastEnabled !== undefined ? lastEnabled : 0;\n break;\n }\n case 'Enter':\n case ' ': {\n e.preventDefault();\n if (!this.open) {\n this.open = true;\n const selIdx = this._options.findIndex((o) => o.value === this.value);\n this._focusedOptionIndex = selIdx >= 0 ? selIdx : (enabledIndices[0] ?? 0);\n break;\n }\n if (this._focusedOptionIndex >= 0 && this._focusedOptionIndex < this._options.length) {\n const opt = this._options[this._focusedOptionIndex];\n if (opt) this._selectOption(opt);\n }\n break;\n }\n case 'Escape': {\n e.preventDefault();\n this.open = false;\n this._focusedOptionIndex = -1;\n this._trigger?.focus();\n break;\n }\n case 'Tab': {\n // Close the dropdown but allow Tab to move focus naturally\n if (this.open) {\n this.open = false;\n this._focusedOptionIndex = -1;\n }\n break;\n }\n default: {\n // Typeahead: single printable character jumps to first matching option\n if (!e.ctrlKey && !e.metaKey && !e.altKey && e.key.length === 1) {\n const char = e.key.toLowerCase();\n const startIndex = this.open ? this._focusedOptionIndex + 1 : 0;\n const matching = this._options\n .map((o, i) => ({ o, i }))\n .filter(({ o }) => !o.disabled && o.label.toLowerCase().startsWith(char));\n const afterCurrent = matching.find(({ i }) => i >= startIndex);\n const target = afterCurrent ?? matching[0];\n if (target) {\n if (!this.open) {\n this.open = true;\n }\n this._focusedOptionIndex = target.i;\n e.preventDefault();\n }\n }\n break;\n }\n }\n }\n\n // ─── Selection ───\n\n /** @internal */\n private _selectOption(option: SelectOption): void {\n if (option.disabled) return;\n this.value = option.value; // triggers updated() → sync + formValue + validity\n this._handleInteractionInput();\n this._handleInteractionBlur();\n this._dispatchChange();\n this.open = false;\n this._focusedOptionIndex = -1;\n }\n\n // ─── Event Dispatchers ───\n\n /** @internal */\n private _dispatchChange(): void {\n this.dispatchEvent(\n new CustomEvent<{ value: string }>('hx-change', {\n bubbles: true,\n composed: true,\n detail: { value: this.value },\n }),\n );\n }\n\n /** @internal */\n private _handleNativeChange(e: Event): void {\n this.value = (e.target as HTMLSelectElement).value; // triggers updated()\n this._handleInteractionInput();\n this._handleInteractionBlur();\n this._dispatchChange();\n }\n\n // ─── Outside Click Handler ───\n\n /** @internal */\n private _handleOutsideClick = (e: MouseEvent): void => {\n if (this.open && !e.composedPath().includes(this)) {\n this.open = false;\n }\n };\n\n // ─── Public Methods ───\n\n /** Moves focus to the visible trigger button. */\n override focus(options?: FocusOptions): void {\n this._trigger?.focus(options);\n }\n\n // ─── Render Helpers ───\n\n /** @internal */\n private _optionId(index: number): string {\n return `hx-select-option-${this._selectId}-${index}`;\n }\n\n /** @internal */\n private _renderOptions() {\n if (this._options.length === 0) {\n return html`<div class=\"field__no-options\">${this.labelNoOptions}</div>`;\n }\n\n return repeat(\n this._options,\n (opt) => opt.value,\n (opt, index) => {\n const isSelected = opt.value === this.value;\n const isFocused = index === this._focusedOptionIndex;\n\n return html`\n <div\n id=${this._optionId(index)}\n part=\"option\"\n role=\"option\"\n class=${classMap({\n field__option: true,\n 'field__option--selected': isSelected,\n 'field__option--focused': isFocused,\n 'field__option--disabled': opt.disabled,\n })}\n aria-selected=${isSelected ? 'true' : 'false'}\n aria-disabled=${opt.disabled ? 'true' : nothing}\n @click=${() => this._selectOption(opt)}\n >\n <span class=\"field__option-label\">${opt.label}</span>\n </div>\n `;\n },\n );\n }\n\n // ─── Main Render ───\n\n override render() {\n const hasError = !!this.error || this._hasErrorSlot;\n\n const fieldClasses = {\n field: true,\n 'field--error': hasError,\n 'field--disabled': this.disabled,\n 'field--required': this.required,\n 'field--open': this.open,\n };\n\n const triggerClasses = {\n field__trigger: true,\n [`field__trigger--${this.size}`]: true,\n 'field__trigger--placeholder': !this.value,\n };\n\n const selectClasses = {\n field__select: true,\n [`field__select--${this.size}`]: true,\n };\n\n const describedBy =\n [\n hasError || this._hasErrorSlot ? this._errorId : null,\n this.helpText ? this._helpTextId : null,\n ]\n .filter(Boolean)\n .join(' ') || undefined;\n\n return html`\n <div part=\"field\" class=${classMap(fieldClasses)}>\n <!-- Label -->\n <slot name=\"label\">\n ${this.label\n ? html`<label\n part=\"label\"\n class=\"field__label\"\n id=${this._labelId}\n for=${this._selectId}\n >\n ${this.label}\n ${this.required\n ? html`<span class=\"field__required-marker\" aria-hidden=\"true\">*</span>`\n : nothing}\n </label>`\n : nothing}\n </slot>\n\n <!-- Select Wrapper: trigger + listbox -->\n <div part=\"select-wrapper\" class=\"field__select-wrapper\">\n <!-- Custom Trigger (combobox — div to avoid native role conflicts per APG) -->\n <div\n part=\"trigger\"\n id=${this._selectId}\n class=${classMap(triggerClasses)}\n role=\"combobox\"\n tabindex=${this.disabled ? '-1' : '0'}\n aria-expanded=${this.open ? 'true' : 'false'}\n aria-haspopup=\"listbox\"\n aria-controls=${this._listboxId}\n aria-activedescendant=${this.open && this._focusedOptionIndex >= 0\n ? this._optionId(this._focusedOptionIndex)\n : nothing}\n aria-invalid=${hasError ? 'true' : nothing}\n aria-describedby=${ifDefined(describedBy)}\n aria-required=${this.required ? 'true' : nothing}\n aria-disabled=${this.disabled ? 'true' : nothing}\n aria-labelledby=${ifDefined(this.label ? this._labelId : undefined)}\n aria-label=${ifDefined(this.accessibleLabel ?? undefined)}\n @click=${this._toggleDropdown}\n @keydown=${this._handleKeydown}\n >\n <span class=\"field__trigger-value\"\n >${this._displayValue || this.placeholder || nothing}</span\n >\n <span class=\"field__chevron\" aria-hidden=\"true\"></span>\n </div>\n\n <!-- Custom Listbox Panel -->\n <div\n part=\"listbox\"\n role=\"listbox\"\n id=${this._listboxId}\n class=\"field__listbox\"\n aria-label=${ifDefined(this.label || this.accessibleLabel || undefined)}\n ?hidden=${!this.open}\n >\n <div class=\"field__options\">${this._renderOptions()}</div>\n </div>\n\n <!-- Hidden native select (form participation + test compat) -->\n <select\n part=\"select\"\n class=${classMap(selectClasses)}\n tabindex=\"-1\"\n aria-hidden=\"true\"\n ?required=${this.required}\n ?disabled=${this.disabled}\n name=${ifDefined(this.name || undefined)}\n aria-label=${ifDefined(this.accessibleLabel ?? undefined)}\n aria-invalid=${hasError ? 'true' : nothing}\n aria-describedby=${ifDefined(describedBy)}\n aria-required=${this.required ? 'true' : nothing}\n @change=${this._handleNativeChange}\n >\n ${this.placeholder\n ? html`<option value=\"\" disabled selected>${this.placeholder}</option>`\n : nothing}\n </select>\n </div>\n\n <!-- Hidden slot (options are read from here) -->\n <slot @slotchange=${this._handleSlotChange} style=\"display:none;\"></slot>\n\n <!-- Error -->\n <slot name=\"error\" @slotchange=${this._handleErrorSlotChange}>\n ${hasError\n ? html`<div part=\"error\" class=\"field__error\" id=${this._errorId} role=\"alert\">\n ${this.error}\n </div>`\n : nothing}\n </slot>\n\n <!-- Help Text -->\n ${this.helpText && !hasError\n ? html`\n <div part=\"help-text\" class=\"field__help-text\" id=${this._helpTextId}>\n <slot name=\"help-text\">${this.helpText}</slot>\n </div>\n `\n : nothing}\n </div>\n `;\n }\n}\n\n/**\n * Per-component event map for type-safe addEventListener on hx-select.\n * The `hx-change` detail is `{ value: string }` only — no `checked` property.\n */\nexport interface HxSelectEventMap {\n 'hx-change': CustomEvent<{ value: string }>;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-select': HelixSelect;\n }\n}\n\nexport type { HelixSelect as HxSelect };\n"],"names":["helixSelectStyles","css","_nextSelectId","createIdCounter","HelixSelect","FormMixin","HelixElement","opt","o","changedProperties","validSizes","devWarn","errorEl","_a","msg","state","_mode","disabled","el","slot","parsed","cloneIntoSelect","optEl","clone","child","selectedIndex","enabledIndices","i","nextDown","lastEnabled","prevUp","lastEnabledUp","selIdx","char","startIndex","matching","target","option","options","index","html","repeat","isSelected","isFocused","classMap","nothing","hasError","fieldClasses","triggerClasses","selectClasses","describedBy","ifDefined","forcedColorsField","__decorateClass","property","query","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;AA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"}