@helixui/library 3.7.0 → 3.8.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.
- package/custom-elements.json +1926 -303
- package/dist/components/hx-action-bar/hx-action-bar.d.ts +18 -0
- package/dist/components/hx-action-bar/hx-action-bar.d.ts.map +1 -1
- package/dist/components/hx-action-bar/hx-action-bar.styles.d.ts.map +1 -1
- package/dist/components/hx-action-bar/index.js +1 -1
- package/dist/components/hx-banner/hx-banner.d.ts +19 -0
- package/dist/components/hx-banner/hx-banner.d.ts.map +1 -1
- package/dist/components/hx-breadcrumb/hx-breadcrumb-item.d.ts.map +1 -1
- package/dist/components/hx-breadcrumb/hx-breadcrumb-item.styles.d.ts.map +1 -1
- package/dist/components/hx-breadcrumb/hx-breadcrumb.d.ts +18 -0
- package/dist/components/hx-breadcrumb/hx-breadcrumb.d.ts.map +1 -1
- package/dist/components/hx-breadcrumb/index.js +1 -1
- package/dist/components/hx-button/hx-button.styles.d.ts.map +1 -1
- package/dist/components/hx-button/index.js +1 -1
- package/dist/components/hx-button-group/hx-button-group.d.ts +47 -0
- package/dist/components/hx-button-group/hx-button-group.d.ts.map +1 -1
- package/dist/components/hx-button-group/index.js +1 -1
- package/dist/components/hx-checkbox/hx-checkbox.styles.d.ts.map +1 -1
- package/dist/components/hx-checkbox/index.js +1 -1
- package/dist/components/hx-checkbox-group/hx-checkbox-group.d.ts +36 -0
- package/dist/components/hx-checkbox-group/hx-checkbox-group.d.ts.map +1 -1
- package/dist/components/hx-checkbox-group/hx-checkbox-group.styles.d.ts.map +1 -1
- package/dist/components/hx-checkbox-group/index.js +1 -1
- package/dist/components/hx-clinical-status/hx-clinical-status.d.ts +19 -0
- package/dist/components/hx-clinical-status/hx-clinical-status.d.ts.map +1 -1
- package/dist/components/hx-color-picker/hx-color-picker.d.ts +18 -0
- package/dist/components/hx-color-picker/hx-color-picker.d.ts.map +1 -1
- package/dist/components/hx-color-picker/hx-color-picker.styles.d.ts.map +1 -1
- package/dist/components/hx-color-picker/index.js +1 -1
- package/dist/components/hx-combobox/hx-combobox.d.ts +18 -0
- package/dist/components/hx-combobox/hx-combobox.d.ts.map +1 -1
- package/dist/components/hx-copy-button/hx-copy-button.d.ts +18 -0
- package/dist/components/hx-copy-button/hx-copy-button.d.ts.map +1 -1
- package/dist/components/hx-copy-button/hx-copy-button.styles.d.ts.map +1 -1
- package/dist/components/hx-copy-button/index.js +1 -1
- package/dist/components/hx-date-picker/hx-date-picker.d.ts +18 -0
- package/dist/components/hx-date-picker/hx-date-picker.d.ts.map +1 -1
- package/dist/components/hx-date-picker/hx-date-picker.styles.d.ts.map +1 -1
- package/dist/components/hx-date-picker/index.js +1 -1
- package/dist/components/hx-drawer/hx-drawer.d.ts +18 -0
- package/dist/components/hx-drawer/hx-drawer.d.ts.map +1 -1
- package/dist/components/hx-dropdown/hx-dropdown.d.ts +18 -0
- package/dist/components/hx-dropdown/hx-dropdown.d.ts.map +1 -1
- package/dist/components/hx-dropdown/hx-dropdown.styles.d.ts.map +1 -1
- package/dist/components/hx-dropdown/index.js +1 -1
- package/dist/components/hx-field/hx-field.d.ts +17 -0
- package/dist/components/hx-field/hx-field.d.ts.map +1 -1
- package/dist/components/hx-field-label/hx-field-label.d.ts +17 -0
- package/dist/components/hx-field-label/hx-field-label.d.ts.map +1 -1
- package/dist/components/hx-file-upload/hx-file-upload.d.ts +18 -0
- package/dist/components/hx-file-upload/hx-file-upload.d.ts.map +1 -1
- package/dist/components/hx-form/hx-form.d.ts +19 -0
- package/dist/components/hx-form/hx-form.d.ts.map +1 -1
- package/dist/components/hx-help-text/hx-help-text.d.ts +17 -0
- package/dist/components/hx-help-text/hx-help-text.d.ts.map +1 -1
- package/dist/components/hx-icon-button/hx-icon-button.d.ts +18 -0
- package/dist/components/hx-icon-button/hx-icon-button.d.ts.map +1 -1
- package/dist/components/hx-menu/hx-menu.d.ts +18 -0
- package/dist/components/hx-menu/hx-menu.d.ts.map +1 -1
- package/dist/components/hx-nav/hx-nav.d.ts +18 -0
- package/dist/components/hx-nav/hx-nav.d.ts.map +1 -1
- package/dist/components/hx-nav/hx-nav.styles.d.ts.map +1 -1
- package/dist/components/hx-nav/index.js +1 -1
- package/dist/components/hx-number-input/hx-number-input.d.ts +18 -0
- package/dist/components/hx-number-input/hx-number-input.d.ts.map +1 -1
- package/dist/components/hx-number-input/index.js +1 -1
- package/dist/components/hx-overflow-menu/hx-overflow-menu.d.ts +18 -0
- package/dist/components/hx-overflow-menu/hx-overflow-menu.d.ts.map +1 -1
- package/dist/components/hx-popover/hx-popover.d.ts +18 -0
- package/dist/components/hx-popover/hx-popover.d.ts.map +1 -1
- package/dist/components/hx-popover/hx-popover.styles.d.ts.map +1 -1
- package/dist/components/hx-popover/index.js +1 -1
- package/dist/components/hx-popup/hx-popup.d.ts +18 -0
- package/dist/components/hx-popup/hx-popup.d.ts.map +1 -1
- package/dist/components/hx-popup/hx-popup.styles.d.ts.map +1 -1
- package/dist/components/hx-popup/index.js +1 -1
- package/dist/components/hx-radio-group/hx-radio-group.d.ts +18 -0
- package/dist/components/hx-radio-group/hx-radio-group.d.ts.map +1 -1
- package/dist/components/hx-radio-group/hx-radio-group.styles.d.ts.map +1 -1
- package/dist/components/hx-radio-group/hx-radio.styles.d.ts.map +1 -1
- package/dist/components/hx-radio-group/index.js +1 -1
- package/dist/components/hx-rating/hx-rating.d.ts +19 -0
- package/dist/components/hx-rating/hx-rating.d.ts.map +1 -1
- package/dist/components/hx-select/hx-select.d.ts +18 -0
- package/dist/components/hx-select/hx-select.d.ts.map +1 -1
- package/dist/components/hx-side-nav/hx-side-nav.d.ts +18 -0
- package/dist/components/hx-side-nav/hx-side-nav.d.ts.map +1 -1
- package/dist/components/hx-side-nav/hx-side-nav.styles.d.ts.map +1 -1
- package/dist/components/hx-side-nav/index.js +1 -1
- package/dist/components/hx-slider/hx-slider.d.ts +19 -0
- package/dist/components/hx-slider/hx-slider.d.ts.map +1 -1
- package/dist/components/hx-split-button/hx-split-button.d.ts +18 -0
- package/dist/components/hx-split-button/hx-split-button.d.ts.map +1 -1
- package/dist/components/hx-split-button/hx-split-button.styles.d.ts.map +1 -1
- package/dist/components/hx-split-button/index.js +1 -1
- package/dist/components/hx-switch/hx-switch.d.ts +18 -0
- package/dist/components/hx-switch/hx-switch.d.ts.map +1 -1
- package/dist/components/hx-switch/hx-switch.styles.d.ts.map +1 -1
- package/dist/components/hx-switch/index.js +1 -1
- package/dist/components/hx-tabs/hx-tab.styles.d.ts.map +1 -1
- package/dist/components/hx-tabs/hx-tabs.d.ts +18 -0
- package/dist/components/hx-tabs/hx-tabs.d.ts.map +1 -1
- package/dist/components/hx-tabs/index.js +1 -1
- package/dist/components/hx-text-input/hx-text-input.styles.d.ts.map +1 -1
- package/dist/components/hx-text-input/index.js +1 -1
- package/dist/components/hx-textarea/hx-textarea.d.ts +18 -0
- package/dist/components/hx-textarea/hx-textarea.d.ts.map +1 -1
- package/dist/components/hx-time-picker/hx-time-picker.d.ts +18 -0
- package/dist/components/hx-time-picker/hx-time-picker.d.ts.map +1 -1
- package/dist/components/hx-time-picker/hx-time-picker.styles.d.ts.map +1 -1
- package/dist/components/hx-time-picker/index.js +1 -1
- package/dist/components/hx-toast/hx-toast.d.ts +19 -0
- package/dist/components/hx-toast/hx-toast.d.ts.map +1 -1
- package/dist/components/hx-toggle-button/hx-toggle-button.d.ts +18 -0
- package/dist/components/hx-toggle-button/hx-toggle-button.d.ts.map +1 -1
- package/dist/components/hx-toggle-button/hx-toggle-button.styles.d.ts.map +1 -1
- package/dist/components/hx-toggle-button/index.js +1 -1
- package/dist/components/hx-tooltip/hx-tooltip.d.ts +18 -0
- package/dist/components/hx-tooltip/hx-tooltip.d.ts.map +1 -1
- package/dist/components/hx-tooltip/hx-tooltip.styles.d.ts.map +1 -1
- package/dist/components/hx-tooltip/index.js +1 -1
- package/dist/components/hx-top-nav/hx-top-nav.d.ts +18 -0
- package/dist/components/hx-top-nav/hx-top-nav.d.ts.map +1 -1
- package/dist/components/hx-top-nav/hx-top-nav.styles.d.ts.map +1 -1
- package/dist/components/hx-top-nav/index.js +1 -1
- package/dist/css/helix-all.css +239 -30
- package/dist/css/helix-core.css +4 -1
- package/dist/css/helix-forms.css +100 -15
- package/dist/css/helix-navigation.css +43 -2
- package/dist/css/helix-overlay.css +53 -0
- package/dist/css/helix-tokens.css +13 -12
- package/dist/css/helix-utility.css +39 -12
- package/dist/css/hx-action-bar.css +12 -0
- package/dist/css/hx-button.css +4 -1
- package/dist/css/hx-checkbox-group.css +11 -0
- package/dist/css/hx-checkbox.css +10 -0
- package/dist/css/hx-color-picker.css +14 -1
- package/dist/css/hx-copy-button.css +5 -2
- package/dist/css/hx-date-picker.css +6 -1
- package/dist/css/hx-dropdown.css +13 -0
- package/dist/css/hx-nav.css +24 -2
- package/dist/css/hx-number-input.css +8 -8
- package/dist/css/hx-popover.css +13 -0
- package/dist/css/hx-popup.css +14 -0
- package/dist/css/hx-radio-group.css +10 -0
- package/dist/css/hx-side-nav.css +7 -0
- package/dist/css/hx-split-button.css +22 -10
- package/dist/css/hx-switch.css +19 -1
- package/dist/css/hx-text-input.css +4 -1
- package/dist/css/hx-time-picker.css +7 -2
- package/dist/css/hx-toggle-button.css +11 -1
- package/dist/css/hx-tooltip.css +13 -0
- package/dist/css/hx-top-nav.css +12 -0
- package/dist/css/index.css +1 -1
- package/dist/css/manifest.json +40 -12
- package/dist/index.js +24 -24
- package/dist/shared/{hx-action-bar-CitgcpGv.js → hx-action-bar-BlEG4aZv.js} +41 -29
- package/dist/shared/hx-action-bar-BlEG4aZv.js.map +1 -0
- package/dist/shared/hx-banner-fpRnciIO.js.map +1 -1
- package/dist/shared/{hx-breadcrumb-item-3tKppF9h.js → hx-breadcrumb-item-D8xYqe3s.js} +56 -43
- package/dist/shared/hx-breadcrumb-item-D8xYqe3s.js.map +1 -0
- package/dist/shared/{hx-button-rRNmD4fd.js → hx-button-DOZTZnz-.js} +18 -15
- package/dist/shared/hx-button-DOZTZnz-.js.map +1 -0
- package/dist/shared/hx-button-group-D3QUmSzl.js +248 -0
- package/dist/shared/hx-button-group-D3QUmSzl.js.map +1 -0
- package/dist/shared/{hx-checkbox-hPlIw6Lb.js → hx-checkbox-DcgyGS9V.js} +30 -20
- package/dist/shared/hx-checkbox-DcgyGS9V.js.map +1 -0
- package/dist/shared/{hx-checkbox-group-D5piJLY8.js → hx-checkbox-group-C0q6HDqn.js} +101 -58
- package/dist/shared/hx-checkbox-group-C0q6HDqn.js.map +1 -0
- package/dist/shared/hx-clinical-status-D3XQIOqX.js.map +1 -1
- package/dist/shared/{hx-color-picker-DBwJzT5f.js → hx-color-picker-CYjx8i8R.js} +97 -84
- package/dist/shared/hx-color-picker-CYjx8i8R.js.map +1 -0
- package/dist/shared/hx-combobox-NgJaLbs2.js.map +1 -1
- package/dist/shared/{hx-copy-button-sUVuikyH.js → hx-copy-button-DJirFCUL.js} +18 -15
- package/dist/shared/hx-copy-button-DJirFCUL.js.map +1 -0
- package/dist/shared/{hx-date-picker-DSKDkCy1.js → hx-date-picker-0PtEav0K.js} +66 -61
- package/dist/shared/hx-date-picker-0PtEav0K.js.map +1 -0
- package/dist/shared/hx-drawer-CM_upadk.js.map +1 -1
- package/dist/shared/{hx-dropdown-D626S2ZG.js → hx-dropdown-xHwTJecv.js} +44 -31
- package/dist/shared/hx-dropdown-xHwTJecv.js.map +1 -0
- package/dist/shared/hx-field-label-BVRyyKeh.js.map +1 -1
- package/dist/shared/hx-field-zw0U1KVi.js.map +1 -1
- package/dist/shared/hx-file-upload-D3rKROK5.js.map +1 -1
- package/dist/shared/hx-form-CkChEATa.js.map +1 -1
- package/dist/shared/hx-help-text-Xb2Yr8x2.js.map +1 -1
- package/dist/shared/hx-icon-button-B2BdVdyK.js.map +1 -1
- package/dist/shared/hx-menu-divider-A6Guuzi_.js.map +1 -1
- package/dist/shared/{hx-nav-ldFM3Fle.js → hx-nav-ChMTfn7o.js} +66 -44
- package/dist/shared/hx-nav-ChMTfn7o.js.map +1 -0
- package/dist/shared/{hx-nav-item-CODtUlew.js → hx-nav-item-ClN17f1y.js} +62 -55
- package/dist/shared/hx-nav-item-ClN17f1y.js.map +1 -0
- package/dist/shared/{hx-number-input-yUzFOSC1.js → hx-number-input-MggsT7F0.js} +13 -13
- package/dist/shared/hx-number-input-MggsT7F0.js.map +1 -0
- package/dist/shared/hx-overflow-menu-DFjJAziP.js.map +1 -1
- package/dist/shared/{hx-popover-BAlAFOH9.js → hx-popover-BjB0nkcq.js} +51 -38
- package/dist/shared/hx-popover-BjB0nkcq.js.map +1 -0
- package/dist/shared/{hx-popup-COUXXZ9X.js → hx-popup-BiV_2evC.js} +59 -45
- package/dist/shared/hx-popup-BiV_2evC.js.map +1 -0
- package/dist/shared/{hx-radio-CY4kQfZw.js → hx-radio-BY4zpwdh.js} +45 -27
- package/dist/shared/hx-radio-BY4zpwdh.js.map +1 -0
- package/dist/shared/hx-rating-C3QP53k9.js.map +1 -1
- package/dist/shared/hx-select-DahFehiZ.js.map +1 -1
- package/dist/shared/hx-slider-Blmv_rwS.js.map +1 -1
- package/dist/shared/{hx-split-button-Ddle8iVx.js → hx-split-button-CdNz1XAu.js} +62 -50
- package/dist/shared/hx-split-button-CdNz1XAu.js.map +1 -0
- package/dist/shared/{hx-switch-TvKGvZJz.js → hx-switch-BCXuNxEH.js} +42 -24
- package/dist/shared/hx-switch-BCXuNxEH.js.map +1 -0
- package/dist/shared/{hx-tab-panel-DzsX8BHV.js → hx-tab-panel-BfisavKo.js} +47 -32
- package/dist/shared/hx-tab-panel-BfisavKo.js.map +1 -0
- package/dist/shared/{hx-text-input-D6FlOZM-.js → hx-text-input-V5sQOpDh.js} +5 -2
- package/dist/shared/hx-text-input-V5sQOpDh.js.map +1 -0
- package/dist/shared/hx-textarea-CNG590KY.js.map +1 -1
- package/dist/shared/{hx-time-picker-Bo7FWzmf.js → hx-time-picker-DfJkBwcX.js} +41 -36
- package/dist/shared/hx-time-picker-DfJkBwcX.js.map +1 -0
- package/dist/shared/{hx-toggle-button-DSJeFlb0.js → hx-toggle-button-xNVYeA3X.js} +37 -27
- package/dist/shared/hx-toggle-button-xNVYeA3X.js.map +1 -0
- package/dist/shared/{hx-tooltip-DVqtKPCD.js → hx-tooltip-CamO-9nd.js} +24 -11
- package/dist/shared/hx-tooltip-CamO-9nd.js.map +1 -0
- package/dist/shared/{hx-top-nav-DP6OFS8C.js → hx-top-nav-CsTxOtVI.js} +26 -14
- package/dist/shared/hx-top-nav-CsTxOtVI.js.map +1 -0
- package/dist/shared/toast-factory-Dht3pVsw.js.map +1 -1
- package/figma-inventory.json +1121 -345
- package/package.json +2 -2
- package/dist/shared/hx-action-bar-CitgcpGv.js.map +0 -1
- package/dist/shared/hx-breadcrumb-item-3tKppF9h.js.map +0 -1
- package/dist/shared/hx-button-group-4NUBpkyC.js +0 -181
- package/dist/shared/hx-button-group-4NUBpkyC.js.map +0 -1
- package/dist/shared/hx-button-rRNmD4fd.js.map +0 -1
- package/dist/shared/hx-checkbox-group-D5piJLY8.js.map +0 -1
- package/dist/shared/hx-checkbox-hPlIw6Lb.js.map +0 -1
- package/dist/shared/hx-color-picker-DBwJzT5f.js.map +0 -1
- package/dist/shared/hx-copy-button-sUVuikyH.js.map +0 -1
- package/dist/shared/hx-date-picker-DSKDkCy1.js.map +0 -1
- package/dist/shared/hx-dropdown-D626S2ZG.js.map +0 -1
- package/dist/shared/hx-nav-item-CODtUlew.js.map +0 -1
- package/dist/shared/hx-nav-ldFM3Fle.js.map +0 -1
- package/dist/shared/hx-number-input-yUzFOSC1.js.map +0 -1
- package/dist/shared/hx-popover-BAlAFOH9.js.map +0 -1
- package/dist/shared/hx-popup-COUXXZ9X.js.map +0 -1
- package/dist/shared/hx-radio-CY4kQfZw.js.map +0 -1
- package/dist/shared/hx-split-button-Ddle8iVx.js.map +0 -1
- package/dist/shared/hx-switch-TvKGvZJz.js.map +0 -1
- package/dist/shared/hx-tab-panel-DzsX8BHV.js.map +0 -1
- package/dist/shared/hx-text-input-D6FlOZM-.js.map +0 -1
- package/dist/shared/hx-time-picker-Bo7FWzmf.js.map +0 -1
- package/dist/shared/hx-toggle-button-DSJeFlb0.js.map +0 -1
- package/dist/shared/hx-tooltip-DVqtKPCD.js.map +0 -1
- package/dist/shared/hx-top-nav-DP6OFS8C.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hx-select-DahFehiZ.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 /* Round-3 finding 1: host is the canonical combobox surface, so it owns\n keyboard focus. Suppress the UA default outline; the custom focus ring\n is painted on the inner trigger via :host(:focus-visible)\n .field__trigger so visual feedback follows the host's focus state. */\n outline: none;\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 /* Round-3 finding 1 / CodeRabbit F1: trigger is a <button type=\"button\">\n (labelable) so native <label for> click activation works for mouse\n users. Reset native button chrome before applying field styles. */\n appearance: none;\n -webkit-appearance: none;\n margin: 0;\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: inherit;\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 /* Round-3 finding 1: host is the canonical focusable surface. Both the\n :host(:focus-visible) descendant selector AND the legacy\n .field__trigger:focus-visible (kept for forced-colors regression test\n parity) paint the focus ring on the visual trigger. */\n :host(:focus-visible) .field__trigger,\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 :host(:focus-visible) .field--error .field__trigger,\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 :host(:focus-visible) .field__trigger,\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';\n// Round-2 finding 7: do NOT compose `forcedColorsField`. The shared mixin's\n// selectors target real `<input>`/`<select>`/`[part=\"input\"]`/`[part=\"control\"]`\n// surfaces, which `hx-select` does not expose — the announced control is the\n// `[part=\"trigger\"]` div. The bespoke `.field__trigger:focus-visible` rule\n// inside `hx-select.styles.ts` is the only forced-colors path that paints,\n// so composing the mixin would only add dead selectors. Per the\n// `forced-colors.ts` contract (\"compose mixin OR author bespoke block, not\n// both\"), we author the bespoke block.\nimport { devWarn } from '../../utils/dev-warn.js';\nimport {\n installAriaIdrefMirror,\n resolveIdrefTokens,\n supportsIdrefElementReferences,\n type AriaIdrefMirrorHandle,\n} from '../../utils/aria-idref.js';\n\n/**\n * Reads visible text from a shadow wrapper that contains a `<slot>`. Prefers\n * the slot's flattened assigned-nodes text when light DOM is projected,\n * otherwise falls back to the wrapper's own `textContent` (so property-driven\n * fallback content rendered inside the slot is still readable). Aligned with\n * the Group 2 round-23 P2 helper used by `hx-radio-group` / `hx-checkbox-group`.\n */\nfunction readSlottedOrShadowText(wrapper: Element): string {\n const slot = wrapper.querySelector('slot');\n if (slot) {\n const assigned = (slot as HTMLSlotElement).assignedNodes({ flatten: true });\n if (assigned.length > 0) {\n return assigned\n .map((node) => node.textContent ?? '')\n .join('')\n .trim();\n }\n }\n return (wrapper.textContent ?? '').trim();\n}\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 * @fires {Event} invalid - Platform constraint-validation event fired when checkValidity() / reportValidity() determine the value is invalid (form-associated component contract via ElementInternals.setValidity).\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;\n\n // ─── Form Association ───\n\n /** Marks this element as form-associated for ElementInternals support. @internal */\n static override formAssociated = true;\n\n /**\n * Test seam (round-3 finding 4): when set to `true` or `false`, overrides\n * the platform `supportsIdrefElementReferences` probe before\n * `connectedCallback` seeds `_supportsIdrefRefs`. Mid-life flag flips on a\n * connected instance allowed stale modern internals (set during connect)\n * to leak into the fallback branch — tests must select the path BEFORE\n * the host connects so the synthetic environment matches a legacy engine.\n *\n * Production code MUST NOT touch this field. It is a `static` so the test\n * stub cleanup is global and obvious.\n * @internal\n */\n static __testSupportsIdrefRefsOverride: boolean | null = null;\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 /**\n * Whether the named label slot contributes a useful name. Round-3 finding 3:\n * the previous boolean tracked `assignedNodes().length > 0`, which is `true`\n * for whitespace-only slot content. That suppressed the unlabeled devWarn\n * even when the slot only carried a stray newline. This flag now requires\n * either a labellable element OR non-empty trimmed text content.\n * @internal\n */\n @state() private _hasLabelSlot = false;\n /**\n * Source of the accessible name. Round-3 finding 8 replaces the magic\n * sentinel `'*slotted*'` with a discriminated union so a future caller\n * setting `label=\"*slotted*\"` literally cannot be confused with a slotted\n * label.\n * @internal\n */\n @state() private _labelSource: 'string' | 'slot' | 'none' = 'none';\n /**\n * Flattened, trimmed text content of any text nodes in the label slot —\n * used to drive `internals.ariaLabel` when the consumer projects only a\n * text node (no element to add to `labelEls`). Round-3 finding 3.\n * @internal\n */\n @state() private _labelSlotText = '';\n /** Whether the help-text slot contains projected content. @internal */\n @state() private _hasHelpSlot = false;\n /**\n * Direct reference to the first labellable element projected into the\n * `<slot name=\"label\">`. Round-3 finding 5 replaces the previous round-trip\n * through `document.getElementById(this._slottedLabelId)` so we no longer\n * mutate consumer light-DOM (assigning ids) and the lookup is robust under\n * nested shadow roots (round-3 finding 6).\n * @internal\n */\n private _slottedLabelEl: Element | null = null;\n /** Zero-based index of the keyboard-focused option in the listbox; -1 means none. @internal */\n @state() private _focusedOptionIndex = -1;\n /**\n * Whether the platform supports IDL element references on `ElementInternals`.\n * Drives the cross-shadow naming strategy: modern path uses\n * `internals.ariaLabelledByElements` / `internals.ariaDescribedByElements`\n * to bridge consumer light-DOM IDREFs into the host accessible node;\n * fallback path mirrors the consumer's `aria-labelledby` / `aria-describedby`\n * tokens onto host attributes and text-mirrors shadow help/error wrappers\n * via `internals.ariaDescription`.\n *\n * ARCHITECTURE — Round-3 host-canonical (overturns scope doc Path A).\n * Codex round-2 finding 1 demonstrated that the dual-channel approach\n * (host roleless on modern, host-announced on fallback) creates a \"named\n * surface ≠ focused surface\" gap on legacy engines. Round-3 commits to a\n * single canonical surface — the HOST — on BOTH paths:\n *\n * - `internals.role = 'combobox'` on both paths (and `role=\"combobox\"`\n * attribute mirror so CSS / `getAttribute` / AT inspecting the DOM see it).\n * - `tabindex=\"0\"` on the host (so it is the focusable surface).\n * - Click + keydown listeners on the host (so user input lands on the\n * announced surface).\n * - `focus()` routes to the host (so programmatic focus matches AT focus).\n * - `setValidity()` anchor = host (so UA validation UI routes to the\n * focusable announced surface).\n * - Inner trigger drops `role`, `tabindex`, and the combobox ARIA mirror\n * on both paths so AT does not see a doubled accessible.\n *\n * This matches Group 2's host-canonical pattern (PR #1625, validated for\n * `hx-radio-group` and `hx-checkbox-group`). The scope doc's APG concern\n * about doubled comboboxes is moot once the inner role is dropped.\n * @internal\n */\n @state() private _supportsIdrefRefs = true;\n /**\n * Deferred copy of `error` driven through reactive state so the persistent\n * live region can re-announce on transitions without direct DOM mutation.\n * Aligned with Group 2 round-1 finding #10.\n * @internal\n */\n @state() private _announcedError = '';\n /**\n * Cached invalidity flag derived from `internals.validity.valid` after the\n * latest `setValidity()` call. Both the modern (`internals.ariaInvalid`)\n * and fallback (host attribute) writes read from the same source so they\n * cannot disagree. Round-2 finding 2.\n * @internal\n */\n @state() private _invalid = false;\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 // ─── Host-canonical ARIA bookkeeping ───\n\n /**\n * Handle for the shared IDREF observer. See `installAriaIdrefMirror()`.\n * @internal\n */\n private _ariaMirror: AriaIdrefMirrorHandle | null = null;\n /**\n * Watches assigned `<slot name=\"help-text\">` nodes for in-place text\n * mutations so the no-IDL-ref fallback `internals.ariaDescription` stays in\n * sync. Aligned with Group 2 round-23 P2 (Finding C).\n * @internal\n */\n private _helpSlotTextObserver: MutationObserver | null = null;\n /**\n * Watches assigned `<slot name=\"error\">` nodes for in-place text mutations.\n * Aligned with Group 2 round-23 P2 (Finding C).\n * @internal\n */\n private _errorSlotTextObserver: MutationObserver | null = null;\n /**\n * Round-10 finding 1 (supersedes round-8 counter): dedicated host observer\n * scoped to `aria-describedby` with `attributeOldValue: true`. The fallback\n * path strips the host `aria-describedby` attribute on every sync (per\n * round-6 single-channel design). Once stripped, a subsequent consumer\n * `removeAttribute('aria-describedby')` causes NO DOM mutation (the\n * attribute is already absent), so the post-sync `getAttribute()` baseline\n * diff at the top of `_syncHostAriaSemantics` cannot detect the retraction\n * — `_consumerDescribedBy` would remain pinned to the originally-cached\n * string forever and the consumer's external help text would keep\n * concatenating into `internals.ariaDescription` indefinitely.\n *\n * Round-10 architectural lockdown (`.reports/aria-group-3-architecture-\n * decision-r10.md` section 7.1): the observer is governed by the\n * **disconnect-during-strip** discipline. Before the fallback strip\n * `removeAttribute('aria-describedby')` runs, this observer is\n * `disconnect()`ed; immediately after the strip, it is re-`observe()`d.\n * Self-mutations are therefore never delivered to the callback, so the\n * observer only sees consumer-driven mutations. This eliminates the\n * round-8 pending-strip counter / discriminator entirely and the lockstep\n * invariant it depended on.\n *\n * The observer detects `oldValue !== null && newValue === null`\n * (consumer authentically retracted the attribute) and clears\n * `_consumerDescribedBy`. Framework-batched attach-then-detach (Vue /\n * React / Lit reconciliation flipping `aria-describedby` from a string to\n * null in one render commit) produces two records in the same callback\n * batch — the second record's `oldValue` is observable. The bare\n * `removeAttribute` no-op on an already-absent attribute is unobservable\n * by design (a null → null DOM operation is not a mutation); see\n * `hx-select.test.ts` round-10 contract block for the three documented\n * retraction sequences.\n *\n * @internal\n */\n private _hostDescribedByObserver: MutationObserver | null = null;\n /**\n * Last value of `aria-labelledby` we wrote to the host. Used to distinguish\n * external (consumer) attribute mutations from our own internal augmentation\n * writes. Aligned with Group 2 round-10 P2.\n * @internal\n */\n private _lastWrittenLabelledBy: string | null = null;\n /** @internal — see `_lastWrittenLabelledBy`. */\n private _lastWrittenDescribedBy: string | null = null;\n /**\n * Last value of `aria-label` we wrote to the host. Used to distinguish\n * external (consumer) attribute mutations from our own internal mirror\n * writes on the fallback path. Round-5 finding 1: without this snapshot\n * the round-3 fallback `aria-label` mirror was self-sealing — sync N\n * wrote `aria-label=\"Country\"`, sync N+1 read that same string back via\n * `getAttribute('aria-label')` and treated it as a consumer override,\n * caching it into `internals.ariaLabel` and short-circuiting\n * `_writeHostAttributeMirror`. Subsequent label/accessibleLabel/slot\n * mutations never propagated.\n * @internal\n */\n private _lastWrittenAriaLabel: string | null = null;\n /**\n * Most recently observed *consumer-supplied* `aria-labelledby` baseline.\n * Refreshed only when the host attribute changes via an external write —\n * internal writes leave the baseline untouched. Cached so consumer tokens\n * can replay if their target element later attaches to the DOM.\n * @internal\n */\n private _consumerLabelledBy: string | null = null;\n /** @internal — see `_consumerLabelledBy`. */\n private _consumerDescribedBy: string | null = null;\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n // Group 2 round-17 P1 parity: detect IDL element-references API support\n // so the cross-shadow naming strategy can branch between modern (host\n // `internals.aria*Elements`) and fallback (host-attribute mirror).\n //\n // Round-3 finding 4: honour the static test override so synthetic\n // environments choose the path BEFORE connect runs. Without this seam,\n // tests had to flip the `_supportsIdrefRefs` flag mid-life — by then,\n // the modern branch had already written `internals.ariaLabelledByElements`\n // and the fallback branch never cleared it, so stale modern artifacts\n // leaked into \"fallback\" assertions.\n const ctor = this.constructor as typeof HelixSelect;\n this._supportsIdrefRefs =\n ctor.__testSupportsIdrefRefsOverride !== null\n ? ctor.__testSupportsIdrefRefsOverride\n : supportsIdrefElementReferences(this._internals);\n // Round-3 finding 1: host is the canonical combobox surface, so user\n // input listeners attach to the host (not the inner trigger).\n this.addEventListener('click', this._handleHostClick);\n this.addEventListener('keydown', this._handleKeydown);\n // Round-10 finding 1 (supersedes round-8 counter): install the dedicated\n // `aria-describedby` retraction observer BEFORE the seeded\n // `_syncHostAriaSemantics()` call below, then govern its lifetime with\n // the disconnect-during-strip discipline (see `_syncHostAriaSemantics`\n // fallback branch). The observer therefore sees only consumer-driven\n // mutations; self-strips never reach the callback. No counter, no\n // discriminator, no lockstep invariant.\n this._hostDescribedByObserver = new MutationObserver((records) => {\n let consumerCleared = false;\n for (const record of records) {\n if (record.attributeName !== 'aria-describedby') continue;\n const oldValue = record.oldValue;\n const newValue = this.getAttribute('aria-describedby');\n if (oldValue !== null && newValue === null) {\n // Consumer authentically retracted `aria-describedby`. Clear the\n // cached baseline so the next sync's description recompute drops\n // the external help text.\n this._consumerDescribedBy = null;\n consumerCleared = true;\n }\n }\n if (consumerCleared) {\n // Resync so `internals.ariaDescription` rebuilds without the\n // stale external description text on the fallback path.\n this._syncHostAriaSemantics();\n }\n });\n this._hostDescribedByObserver.observe(this, {\n attributes: true,\n attributeFilter: ['aria-describedby'],\n attributeOldValue: true,\n });\n // Seed root-independent semantics from connect so the host announces the\n // combobox role before first paint. Per round-10 finding 1, the\n // dedicated `aria-describedby` observer is wired above so the\n // disconnect-during-strip helper inside the sync method has a live\n // observer to disconnect/reconnect against.\n this._syncHostAriaSemantics();\n this._ariaMirror = installAriaIdrefMirror(this, () => {\n this._syncHostAriaSemantics();\n });\n }\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 this.removeEventListener('click', this._handleHostClick);\n this.removeEventListener('keydown', this._handleKeydown);\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 this._ariaMirror?.disconnect();\n this._ariaMirror = null;\n this._helpSlotTextObserver?.disconnect();\n this._helpSlotTextObserver = null;\n this._errorSlotTextObserver?.disconnect();\n this._errorSlotTextObserver = null;\n this._hostDescribedByObserver?.disconnect();\n this._hostDescribedByObserver = null;\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('label')) {\n // Round-3 finding 8: keep the discriminated label-source state in sync\n // when the `label` property toggles between empty and non-empty.\n this._refreshLabelSource();\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 // Host-elevated ARIA semantics — see _syncHostAriaSemantics.\n this._syncHostAriaSemantics();\n // Group 2 round-1 finding #10: drive re-announcement from reactive state\n // so the persistent live region stays in the shadow tree across error\n // transitions. The persistent `<div role=\"alert\">` always lives in DOM;\n // changing `_announcedError` re-paints its slot fallback content and AT\n // re-announces.\n if (changedProperties.has('error')) {\n const previousError = changedProperties.get('error') as string;\n if (previousError && this.error) {\n // Error→error: clear then re-set after rAF so AT re-announces.\n this._announcedError = '';\n requestAnimationFrame(() => {\n this._announcedError = this.error;\n });\n } else {\n this._announcedError = this.error;\n }\n }\n }\n\n override firstUpdated(changedProperties: PropertyValues<this>): void {\n super.firstUpdated(changedProperties);\n // Round-3 finding 3: `slotchange` fires as a microtask after the\n // initial synchronous render, so reading the lazily-tracked\n // `_hasLabelSlot` here would observe its stale `false` seed. Read the\n // slot's assigned nodes directly so the unlabeled devWarn observes the\n // real first-paint state.\n const labelSlot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"label\"]');\n const hasLabellableSlotContent = labelSlot\n ? this._readLabelSlotState(labelSlot).hasUsefulName\n : false;\n // WCAG 4.1.2: warn when no accessible name is available. The host (the\n // canonical announced surface) needs either a `label` prop, an\n // `accessible-label` attribute, or a host-level `aria-label` /\n // `aria-labelledby` so AT can identify the form field.\n if (\n !this.label &&\n !this.accessibleLabel &&\n !hasLabellableSlotContent &&\n !this.getAttribute('aria-label') &&\n !this.getAttribute('aria-labelledby')\n ) {\n devWarn(\n 'hx-select',\n 'No accessible label provided. Set the `label` attribute, `accessible-label`, `aria-label`, `aria-labelledby`, or project a `<slot name=\"label\">` child. An unlabeled select violates WCAG 2.1 AA (4.1.2 Name, Role, Value).',\n );\n }\n }\n\n /**\n * Reads the label slot's assigned nodes and computes the discriminated\n * naming state. Round-3 finding 3 + 5 + 8: an empty whitespace-only slot\n * does NOT count as a useful name; the first labellable element is\n * captured by reference (no consumer-DOM mutation); the result drives a\n * discriminated `_labelSource` rather than a magic `'*slotted*'` string.\n * @internal\n */\n private _readLabelSlotState(slot: HTMLSlotElement): {\n hasUsefulName: boolean;\n element: Element | null;\n text: string;\n } {\n const nodes = slot.assignedNodes({ flatten: true });\n let element: Element | null = null;\n let text = '';\n for (const node of nodes) {\n if (node.nodeType === Node.ELEMENT_NODE && !element) {\n element = node as Element;\n } else if (node.nodeType === Node.TEXT_NODE) {\n text += node.textContent ?? '';\n }\n }\n const trimmedText = text.trim();\n return {\n hasUsefulName: element !== null || trimmedText.length > 0,\n element,\n text: trimmedText,\n };\n }\n\n // ─── Host-canonical ARIA sync ───\n\n /**\n * Mirrors combobox semantics onto the host via ElementInternals so that the\n * host IS the canonical announced surface on BOTH the modern (IDL element\n * references) and fallback paths. Round-3 finding 1: codex round-2\n * demonstrated that putting `role=\"combobox\"` on the host on fallback while\n * leaving focus/click/keyboard/validity anchored to the inner trigger\n * created a \"named surface ≠ focused surface\" gap. Round-3 collapses both\n * paths to the host-canonical model used by Group 2's `hx-radio-group` /\n * `hx-checkbox-group` (PR #1625):\n *\n * - `internals.role = 'combobox'` so AT advertises the combobox role.\n * - Host attribute `role=\"combobox\"` mirror so legacy engines, axe-core,\n * and `getAttribute('role')` agree with the internals default.\n * - Host attribute `tabindex=\"0\"` so the host is the focusable surface.\n * - Host attributes `aria-expanded`, `aria-haspopup`, `aria-controls`,\n * `aria-activedescendant`, `aria-required`, `aria-invalid`,\n * `aria-disabled` so AT walking the host's attribute graph sees the\n * combobox state without depending on internals defaults.\n * - Modern path additionally sets `internals.ariaLabelledByElements` /\n * `internals.ariaDescribedByElements` so consumer light-DOM IDREFs cross\n * the shadow boundary into the host's accessible node.\n * - Fallback path mirrors consumer label/desc tokens onto the host\n * attribute and text-mirrors shadow help/error wrappers via\n * `internals.ariaDescription`.\n * @internal\n */\n private _syncHostAriaSemantics(): void {\n const internals = this._internals;\n // Round-3 finding 1: host IS the canonical combobox on both paths.\n internals.role = 'combobox';\n internals.ariaRequired = this.required ? 'true' : 'false';\n // Round-2 finding 2 (preserved): read `internals.validity.valid` once and\n // cache the derived flag in reactive state so both the IDREF-elements\n // path and the host-attribute mirror read from the same source.\n const isInvalid = !internals.validity.valid;\n this._invalid = isInvalid;\n internals.ariaInvalid = isInvalid ? 'true' : 'false';\n internals.ariaDisabled = this.disabled ? 'true' : 'false';\n internals.ariaExpanded = this.open ? 'true' : 'false';\n internals.ariaHasPopup = 'listbox';\n\n // Round-5 finding 1: the live `aria-label` attribute may be either a\n // consumer-authored override OR our own previous mirror write. Compare\n // against `_lastWrittenAriaLabel` to distinguish — if the live value\n // matches what we wrote last sync, treat as component-owned (empty\n // string here) so the recompute pass below picks fresh values from\n // `label` / `accessibleLabel` / slotted text. Only a divergent live\n // value is treated as a consumer override.\n //\n // Round-8 finding 4 (low — deliberate behaviour): the `.trim() || ''`\n // collapse means a whitespace-only consumer override (e.g.\n // `aria-label=\" \"`) is treated as no consumer override at all — the\n // internal candidate (`label` / `accessibleLabel` / slotted text) wins\n // and overwrites the host attribute on the fallback path. This is a\n // deliberate choice: whitespace-only `aria-label` is a code smell\n // pattern that is hard to author intentionally, the AT-suppression\n // behaviour it sometimes produces is not part of the documented\n // hx-select contract, and silently erasing the consumer's intended\n // name would be the worse failure mode. A positive regression test in\n // `hx-select.test.ts` (`whitespace-only consumer aria-label is treated\n // as no override on fallback`) locks this intent.\n const liveAriaLabel = this.getAttribute('aria-label');\n const hostAriaLabel =\n liveAriaLabel !== null && liveAriaLabel !== this._lastWrittenAriaLabel\n ? liveAriaLabel.trim() || ''\n : '';\n\n // Resolve the candidate label/desc element references once — the IDL-ref\n // path consumes them as `Element[]`, the fallback path mirrors consumer\n // tokens onto the host attribute.\n const internalLabel = this.shadowRoot?.getElementById(this._labelId) ?? null;\n // Round-3 finding 5 + 6: hold the slotted label element by direct\n // reference (captured in `_handleLabelSlotChange`) so we no longer\n // mutate consumer light DOM with a tracked id and the lookup survives\n // nested shadow roots without a fragile `getElementById` chain.\n const slottedLabelEl = this._slottedLabelEl;\n const helpEl = this.shadowRoot?.getElementById(this._helpTextId) ?? null;\n const errorEl = this.shadowRoot?.getElementById(this._errorId) ?? null;\n\n // Group 2 round-10 P2: refresh the consumer baseline only when the host\n // attribute moved due to an *external* write. Compare the live attribute\n // against our last-written snapshot — if it differs, the consumer wrote.\n const liveLabelledBy = this.getAttribute('aria-labelledby');\n if (liveLabelledBy !== this._lastWrittenLabelledBy) {\n this._consumerLabelledBy = liveLabelledBy;\n }\n const liveDescribedBy = this.getAttribute('aria-describedby');\n if (liveDescribedBy !== this._lastWrittenDescribedBy) {\n this._consumerDescribedBy = liveDescribedBy;\n }\n const externalLabelTokens = this._consumerLabelledBy;\n const externalDescTokens = this._consumerDescribedBy;\n\n const labelEls = resolveIdrefTokens(this, externalLabelTokens);\n // Group 2 round-35: `aria-labelledby` is only \"effective\" when at least\n // one IDREF resolves. A typo or transiently-missing target must NOT erase\n // the visible label.\n const hasEffectiveLabelledBy = labelEls.length > 0;\n\n // Round-3 finding 8: discriminated union replaces the magic\n // `'*slotted*'` sentinel. The state name drives both the modern\n // `internals.ariaLabel` decision and the labelEls fallback append.\n if (hostAriaLabel) {\n internals.ariaLabel = hostAriaLabel;\n } else if (hasEffectiveLabelledBy) {\n // labelledby chain wins — ariaLabel must be null so it does not\n // shadow the chain.\n internals.ariaLabel = null;\n } else if (this._labelSource === 'slot') {\n if (slottedLabelEl) {\n // labelEls path will append the slotted element below; no string.\n internals.ariaLabel = null;\n } else {\n // Text-only slot — no element to add to labelEls. Mirror the\n // flattened text into ariaLabel so AT still announces the name.\n internals.ariaLabel = this._labelSlotText || this.accessibleLabel || null;\n }\n } else if (this._labelSource === 'string') {\n internals.ariaLabel = this.label || this.accessibleLabel || null;\n } else {\n internals.ariaLabel = this.accessibleLabel || null;\n }\n if (!hasEffectiveLabelledBy && !hostAriaLabel) {\n if (this._labelSource === 'slot' && slottedLabelEl) {\n labelEls.push(slottedLabelEl);\n } else if (this._labelSource === 'string' && internalLabel) {\n labelEls.push(internalLabel);\n }\n }\n\n const descEls = resolveIdrefTokens(this, externalDescTokens);\n const hasError = !!(this.error || this._hasErrorSlot);\n if (helpEl && !hasError && (this.helpText || this._hasHelpSlot)) {\n descEls.push(helpEl);\n }\n if (errorEl && hasError) {\n descEls.push(errorEl);\n }\n\n if (this._supportsIdrefRefs) {\n type InternalsWithRefs = ElementInternals & {\n ariaLabelledByElements: Element[] | null;\n ariaDescribedByElements: Element[] | null;\n };\n const refsInternals = internals as InternalsWithRefs;\n refsInternals.ariaLabelledByElements = labelEls.length > 0 ? labelEls : null;\n refsInternals.ariaDescribedByElements = descEls.length > 0 ? descEls : null;\n // Clear stale fallback ariaDescription string in case a prior sync ran\n // on the fallback path (e.g. tests using the static override seam).\n internals.ariaDescription = null;\n } else {\n // ─── No-IDL-ref fallback (Group 2 round-19 P1 parity) ───\n // The IDL element-references API is unavailable, so internal shadow\n // help/error/label wrappers cannot be projected onto the host\n // accessibility node via `internals.aria*Elements`. The host owns the\n // ARIA strings (via `internals.ariaLabel`, `internals.ariaDescription`)\n // and we mirror only consumer-supplied tokens onto the host attribute\n // (shadow ids cannot resolve across the boundary).\n //\n // Round-3 finding 4 (symmetric clear): explicitly null the modern\n // IDL element-reference fields so a stub-flipped fallback test cannot\n // inherit modern artifacts seeded on a previous sync. Production code\n // never hits this branch with non-null modern refs (the platform\n // probe is once-per-connect), but the symmetric clear hardens the\n // contract for tests using the static override seam.\n type InternalsWithRefs = ElementInternals & {\n ariaLabelledByElements: Element[] | null;\n ariaDescribedByElements: Element[] | null;\n };\n const refsInternals = internals as InternalsWithRefs;\n refsInternals.ariaLabelledByElements = null;\n refsInternals.ariaDescribedByElements = null;\n\n // Round-3 finding 7: build the host `aria-labelledby` mirror from\n // *resolved* token ids only — `labelEls.map(e => e.id).filter(Boolean)`.\n // The previous round-2 implementation ran the consumer token strings\n // through `filter(Boolean)`, which only stripped empty strings, not\n // unresolved ids. A consumer `aria-labelledby=\"resolved missing\"` was\n // written back wholesale, so MutationObservers and legacy AT saw a\n // persistent broken IDREF on the host.\n const resolvedLabelIds = labelEls.map((el) => el.id).filter((id): id is string => !!id);\n const hostLabel = hasEffectiveLabelledBy ? resolvedLabelIds.join(' ') : '';\n const liveLabel = this.getAttribute('aria-labelledby');\n if (hostLabel) {\n if (liveLabel !== hostLabel) {\n this.setAttribute('aria-labelledby', hostLabel);\n }\n this._lastWrittenLabelledBy = hostLabel;\n } else if (liveLabel !== null) {\n this.removeAttribute('aria-labelledby');\n this._lastWrittenLabelledBy = null;\n }\n\n // Round-6 (option b — single-channel fallback): per W3C AccName 1.2,\n // an `aria-describedby` token list — even one authored by the\n // consumer — takes precedence over `internals.ariaDescription`. When\n // a consumer authors any working `aria-describedby` token on the\n // host, AT may announce only the consumer text and never reach the\n // internal help/error strings the component renders in its shadow\n // root. Cross-shadow id splice (option a) does not portably resolve\n // on Firefox / VoiceOver, so we collapse to a single channel:\n // concatenate the consumer-described text with internal help/error\n // text into `internals.ariaDescription` (below) and strip the host\n // `aria-describedby` attribute on this path regardless of authorship.\n // The consumer's intended description text is preserved (it surfaces\n // through `internals.ariaDescription`); only the attribute mirror is\n // removed. `_consumerDescribedBy` retains the consumer's original\n // token list (see baseline diff at lines 691-694) so the modern path\n // continues to work if the platform later upgrades and re-syncs.\n // Modern path is unchanged (it uses `ariaDescribedByElements` which\n // the platform concatenates correctly across shadow roots).\n if (this.hasAttribute('aria-describedby')) {\n // Round-10 finding 1 (supersedes round-8 counter): disconnect the\n // dedicated host observer, perform our self-mutation strip, then\n // reconnect. The observer therefore never delivers our own write\n // to the callback, eliminating the round-8 counter / discriminator\n // and the lockstep invariant it depended on. See architecture\n // decision `.reports/aria-group-3-architecture-decision-r10.md`\n // section 7.1. Genuine consumer-driven mutations (set / set-empty\n // / framework attach-then-detach) continue to be observed because\n // we reconnect immediately, with no microtask boundary inside the\n // strip block where a consumer mutation could slip through.\n this._hostDescribedByObserver?.disconnect();\n this.removeAttribute('aria-describedby');\n this._hostDescribedByObserver?.observe(this, {\n attributes: true,\n attributeFilter: ['aria-describedby'],\n attributeOldValue: true,\n });\n }\n this._lastWrittenDescribedBy = null;\n\n // Resolve consumer-described elements through the same idref helper\n // the modern path uses, then concatenate their text with internal\n // shadow help/error text. Tokens that fail to resolve are dropped by\n // `resolveIdrefTokens` (matching native AT behaviour) so unresolved\n // ids do not leak into the description as literal strings. Broken\n // consumer tokens stay cached in `_consumerDescribedBy` and replay\n // when their target later attaches.\n const consumerDescEls = resolveIdrefTokens(this, externalDescTokens);\n const consumerDescText = consumerDescEls\n .map((el) => (el.textContent ?? '').trim())\n .filter(Boolean)\n .join(' ');\n const helpText =\n helpEl && !hasError && (this.helpText || this._hasHelpSlot)\n ? readSlottedOrShadowText(helpEl)\n : '';\n const errorText = errorEl && hasError ? readSlottedOrShadowText(errorEl) : '';\n const combinedDescription = [consumerDescText, helpText, errorText]\n .filter(Boolean)\n .join(' ')\n .trim();\n internals.ariaDescription = combinedDescription || null;\n\n // Round-3 finding 1 (host-canonical): on the fallback path the host\n // attributes carry the combobox state in addition to the internals\n // defaults. CSS / `getAttribute` / axe-core read these attributes\n // (internals defaults are not exposed to either).\n this._writeHostAttributeMirror({ hostAriaLabel, isInvalid });\n }\n\n // Round-3 finding 1: host attributes for the canonical combobox state.\n // Always written on BOTH paths (the modern path's internals defaults\n // are invisible to CSS/getAttribute, so attribute writes are required\n // to match `expect(el.getAttribute('role')).toBe('combobox')` and to\n // make `:host(:focus-visible)` paint a focus ring).\n this._writeHostRoleAndTabindex();\n this._writeHostComboboxStateAttributes();\n }\n\n /**\n * Writes host `role=\"combobox\"` and `tabindex` so the host is the\n * focusable, AT-visible canonical surface. Round-3 finding 1.\n * @internal\n */\n private _writeHostRoleAndTabindex(): void {\n if (this.getAttribute('role') !== 'combobox') {\n this.setAttribute('role', 'combobox');\n }\n const tab = this.disabled ? '-1' : '0';\n if (this.getAttribute('tabindex') !== tab) {\n this.setAttribute('tabindex', tab);\n }\n }\n\n /**\n * Writes the host's combobox state attributes (`aria-expanded`,\n * `aria-haspopup`, `aria-controls`, `aria-activedescendant`,\n * `aria-required`, `aria-invalid`, `aria-disabled`) on every sync. CSS\n * selectors and AT inspecting the live attribute graph read these — the\n * `internals.aria*` defaults are invisible. Round-3 finding 1.\n * @internal\n */\n private _writeHostComboboxStateAttributes(): void {\n if (this.getAttribute('aria-haspopup') !== 'listbox') {\n this.setAttribute('aria-haspopup', 'listbox');\n }\n if (this.getAttribute('aria-controls') !== this._listboxId) {\n this.setAttribute('aria-controls', this._listboxId);\n }\n const expanded = this.open ? 'true' : 'false';\n if (this.getAttribute('aria-expanded') !== expanded) {\n this.setAttribute('aria-expanded', expanded);\n }\n const activeDescendant =\n this.open && this._focusedOptionIndex >= 0 ? this._optionId(this._focusedOptionIndex) : null;\n if (activeDescendant) {\n if (this.getAttribute('aria-activedescendant') !== activeDescendant) {\n this.setAttribute('aria-activedescendant', activeDescendant);\n }\n } else if (this.hasAttribute('aria-activedescendant')) {\n this.removeAttribute('aria-activedescendant');\n }\n if (this.required) {\n if (this.getAttribute('aria-required') !== 'true') {\n this.setAttribute('aria-required', 'true');\n }\n } else if (this.hasAttribute('aria-required')) {\n this.removeAttribute('aria-required');\n }\n if (this._invalid) {\n if (this.getAttribute('aria-invalid') !== 'true') {\n this.setAttribute('aria-invalid', 'true');\n }\n } else if (this.hasAttribute('aria-invalid')) {\n this.removeAttribute('aria-invalid');\n }\n if (this.disabled) {\n if (this.getAttribute('aria-disabled') !== 'true') {\n this.setAttribute('aria-disabled', 'true');\n }\n } else if (this.hasAttribute('aria-disabled')) {\n this.removeAttribute('aria-disabled');\n }\n }\n\n /**\n * Writes a host `aria-label` attribute mirror on the fallback path so AT\n * inspecting the live attribute graph sees the resolved name even when\n * the platform lacks IDL element references. Skip when an effective\n * `aria-labelledby` is already set on the host (labelledby > label by\n * ARIA priority). Round-3 finding 1 + round-2 finding 5.\n * @internal\n */\n private _writeHostAttributeMirror(args: { hostAriaLabel: string; isInvalid: boolean }): void {\n const { hostAriaLabel } = args;\n if (hostAriaLabel) {\n // Round-8 finding 2 (medium): consumer-authored aria-label path. The\n // caller already disambiguated against `_lastWrittenAriaLabel` (see\n // `_syncHostAriaSemantics` line ~668) — `hostAriaLabel` is non-empty\n // ONLY when the live attribute diverges from our last write. We must\n // null the snapshot here so the next sync's disambiguation correctly\n // classifies the consumer string as external on every subsequent pass.\n //\n // Without this null, the snapshot stays pinned to the value WE wrote\n // last. If the consumer subsequently rewrites `aria-label` to a string\n // that happens to equal our prior write, the disambiguation would see\n // `liveAttr === _lastWrittenAriaLabel` and treat the consumer string\n // as component-owned — a later internal `label = ''` would then\n // silently delete the consumer's attribute. The sentinel `null` means\n // \"we did NOT author the live attr,\" which makes a coincident-string\n // round-trip still differ from `null` and correctly classify the\n // consumer string as external.\n this._lastWrittenAriaLabel = null;\n return;\n }\n const candidate =\n this.accessibleLabel ||\n this.label ||\n (this._labelSource === 'slot' ? this._labelSlotText : '') ||\n '';\n const hasLabelledBy = this.hasAttribute('aria-labelledby');\n const liveAttr = this.getAttribute('aria-label');\n if (!hasLabelledBy && candidate) {\n if (liveAttr !== candidate) {\n this.setAttribute('aria-label', candidate);\n }\n // Round-5 finding 1: record what we just wrote so the next sync can\n // distinguish \"I wrote this\" from \"consumer overrode this.\"\n this._lastWrittenAriaLabel = candidate;\n } else {\n // No candidate (label/accessibleLabel cleared) OR labelledby chain\n // wins. Round-5 finding 1: if the live attribute matches our last\n // mirror write, it is component-owned and must be removed now that\n // the source has emptied. Otherwise (whitespace-only attribute or no\n // attribute at all) keep historical behaviour.\n if (liveAttr !== null && liveAttr === this._lastWrittenAriaLabel) {\n this.removeAttribute('aria-label');\n this._lastWrittenAriaLabel = null;\n } else if (this.hasAttribute('aria-label') && !liveAttr?.trim()) {\n this.removeAttribute('aria-label');\n this._lastWrittenAriaLabel = null;\n } else if (!this.hasAttribute('aria-label')) {\n // No mirror present — clear the snapshot so a future consumer\n // write starts from a clean slate.\n this._lastWrittenAriaLabel = null;\n }\n }\n }\n\n /**\n * (Re-)installs the mutation observer over the current set of assigned\n * help-text-slot nodes. Aligned with Group 2 round-23 P2 (Finding C).\n * @internal\n */\n private _installHelpSlotTextObserver(slot: HTMLSlotElement | null): void {\n this._helpSlotTextObserver?.disconnect();\n if (!slot) {\n this._helpSlotTextObserver = null;\n return;\n }\n const observer = new MutationObserver(() => {\n this._syncHostAriaSemantics();\n });\n slot.assignedNodes().forEach((node) => {\n observer.observe(node, {\n characterData: true,\n childList: true,\n subtree: true,\n });\n });\n this._helpSlotTextObserver = observer;\n }\n\n /**\n * (Re-)installs the mutation observer over the current set of assigned\n * error-slot nodes. Aligned with Group 2 round-23 P2 (Finding C).\n * @internal\n */\n private _installErrorSlotTextObserver(slot: HTMLSlotElement | null): void {\n this._errorSlotTextObserver?.disconnect();\n if (!slot) {\n this._errorSlotTextObserver = null;\n return;\n }\n const observer = new MutationObserver(() => {\n this._syncHostAriaSemantics();\n });\n slot.assignedNodes().forEach((node) => {\n observer.observe(node, {\n characterData: true,\n childList: true,\n subtree: true,\n });\n });\n this._errorSlotTextObserver = observer;\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 // Round-3 finding 1: the host is now the canonical announced + focused\n // combobox surface, so anchor `setValidity()` to the host. The UA\n // routes validation UI / error recovery to a focusable element with\n // a stable accessible name — that is the host (which carries\n // `role=\"combobox\"`, `tabindex=\"0\"`, and the resolved name via\n // `internals.ariaLabel*`). Anchoring to the inner trigger would route\n // recovery into the shadow tree, where the consumer cannot intercept.\n this._internals.setValidity({ valueMissing: true }, this.error || this.labelRequired, this);\n } else {\n this._internals.setValidity({});\n }\n // Group 2 round-1 finding #6: re-sync host ARIA after every setValidity()\n // so `aria-invalid` reflects the freshly computed validity state.\n this._syncHostAriaSemantics();\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 /**\n * Tracks the slotted label so projected `<span slot=\"label\">` content joins\n * the accessible-name chain without forcing the consumer to also pass the\n * `label` property.\n *\n * Round-3 findings 3, 5, 6, 8:\n * - 3: count whitespace-only text as \"no useful name\" (devWarn must fire).\n * - 5: hold the slotted element by direct reference instead of mutating\n * consumer light DOM with a tracked id.\n * - 6: lookup is by reference, so it survives nested shadow roots without\n * the fragile `getElementById` chain.\n * - 8: discriminated `_labelSource` replaces the magic `'*slotted*'`\n * sentinel string.\n * @internal\n */\n private _handleLabelSlotChange(e: Event): void {\n if (!(e.target instanceof HTMLSlotElement)) return;\n const state = this._readLabelSlotState(e.target);\n this._hasLabelSlot = state.hasUsefulName;\n this._slottedLabelEl = state.element;\n this._labelSlotText = state.text;\n this._refreshLabelSource();\n this._syncHostAriaSemantics();\n }\n\n /**\n * Recomputes the discriminated label source. Round-3 finding 8.\n *\n * CodeRabbit F2: slotted label wins over the `label` prop. When a\n * consumer provides BOTH a `<span slot=\"label\">` AND a `label=\"...\"`\n * attribute the visible label is the slotted node (rendered by the\n * named slot's default content branch never firing). Preferring the\n * string form here would route the accessible name through the prop\n * and diverge from the rendered text. The slot is the canonical\n * source whenever it has useful content.\n * @internal\n */\n private _refreshLabelSource(): void {\n if (this._hasLabelSlot) {\n this._labelSource = 'slot';\n } else if (this.label) {\n this._labelSource = 'string';\n } else {\n this._labelSource = 'none';\n }\n }\n\n /** @internal */\n private _handleErrorSlotChange(e: Event): void {\n if (!(e.target instanceof HTMLSlotElement)) return;\n this._hasErrorSlot = e.target.assignedNodes({ flatten: true }).length > 0;\n // Group 2 round-23 P2 (Finding C): re-tune the in-place text observer\n // over the new assigned-node set so in-place `textContent` rewrites of\n // slotted error nodes resync `internals.ariaDescription` on the no-IDL-ref\n // fallback path. `slotchange` only fires when the *node set* changes.\n this._installErrorSlotTextObserver(e.target);\n this._syncHostAriaSemantics();\n }\n\n /** @internal */\n private _handleHelpSlotChange(e: Event): void {\n if (!(e.target instanceof HTMLSlotElement)) return;\n this._hasHelpSlot = e.target.assignedNodes({ flatten: true }).length > 0;\n this._installHelpSlotTextObserver(e.target);\n this._syncHostAriaSemantics();\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 // ─── Host Click ───\n\n /**\n * Click handler attached to the host. Round-3 finding 1: the host is the\n * canonical combobox surface, so user input listeners live here. Clicks\n * inside the open listbox are handled by the per-option `@click` binding\n * in `_renderOptions()` and stop here only if `composedPath` indicates\n * the original click target was the open listbox panel (so option clicks\n * don't double-toggle the dropdown).\n * @internal\n */\n private _handleHostClick = (e: MouseEvent): void => {\n const path = e.composedPath();\n // Ignore clicks that originated inside the open listbox panel — the\n // option's own `@click` handler already routed selection.\n const listbox = this.shadowRoot?.querySelector('.field__listbox') ?? null;\n if (listbox && path.includes(listbox)) {\n return;\n }\n // CodeRabbit F3: only toggle when the click traverses the trigger\n // element. Clicks on the slotted label, help text, error text, or\n // any other host-light-DOM content must NOT toggle the dropdown —\n // they are consumer-owned regions that happen to live inside the\n // host. The `<label for=${selectId}>` we render in shadow DOM\n // forwards label clicks to the <button> trigger natively, so those\n // clicks reach this handler with the trigger in their composedPath\n // and toggle correctly.\n const trigger = this._trigger ?? this.shadowRoot?.querySelector('.field__trigger') ?? null;\n if (!trigger || !path.includes(trigger)) {\n return;\n }\n this._toggleDropdown();\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 // Round-3 finding 1: refocus the host (the canonical combobox).\n this.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 /**\n * Moves focus to the host. Round-3 finding 1: the host is the canonical\n * combobox surface (`role=\"combobox\"`, `tabindex=\"0\"`), so programmatic\n * focus must land there to match where AT routes focus and where\n * `setValidity()` anchors recovery.\n */\n override focus(options?: FocusOptions): void {\n HTMLElement.prototype.focus.call(this, 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 const hasHelp = !!this.helpText || this._hasHelpSlot;\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 // Round-3 finding 1: the host is the canonical announced + focused\n // combobox surface on BOTH paths. The inner trigger is a presentational\n // visual surface only — it carries no role, no tabindex, and no ARIA\n // attributes. User input listeners (click, keydown) live on the host so\n // the focused surface IS the announced surface; programmatic `focus()`\n // and `setValidity()` anchor to the host for the same reason. The hidden\n // native `<select>` is `aria-hidden=\"true\"`, never the announced surface.\n\n return html`\n <div part=\"field\" class=${classMap(fieldClasses)}>\n <!-- Label -->\n <slot name=\"label\" @slotchange=${this._handleLabelSlotChange}>\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 <!--\n Visual trigger surface. Round-3 finding 1: role, tabindex,\n and combobox ARIA all live on the host so AT sees a single\n announced + focused surface. The trigger is rendered as a\n <button type=\"button\"> (a labelable element) so the visible\n label[for=selectId] performs native click activation —\n mouse users clicking the label focus the trigger which\n bubbles to the host click handler and toggles the dropdown.\n type=\"button\" prevents implicit form submission. The trigger\n has no role and no ARIA, so AT walks its subtree text as\n the combobox's value content (host carries the real\n association via internals.ariaLabel*).\n -->\n <button\n type=\"button\"\n part=\"trigger\"\n id=${this._selectId}\n class=${classMap(triggerClasses)}\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 </button>\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 @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 <!--\n Persistent error live region. role=\"alert\" is set from first paint\n so the WAI-ARIA contract for live updates is honoured: content\n changes in place rather than the container being toggled. Aligned\n with Group 2 round-1 finding #10.\n -->\n <div\n part=\"error\"\n class=\"field__error\"\n id=${this._errorId}\n role=\"alert\"\n ?hidden=${!hasError}\n >\n <slot name=\"error\" @slotchange=${this._handleErrorSlotChange}\n >${this._announcedError}</slot\n >\n </div>\n\n <!--\n Persistent help-text container. Rendered whenever the property OR\n the slot has content; hidden when an error is present so guidance\n does not compete with validation feedback. Always in the shadow\n tree so the host's aria-describedby chain is stable.\n -->\n <div\n part=\"help-text\"\n class=\"field__help-text\"\n id=${this._helpTextId}\n ?hidden=${!hasHelp || hasError}\n >\n <slot name=\"help-text\" @slotchange=${this._handleHelpSlotChange}>${this.helpText}</slot>\n </div>\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","readSlottedOrShadowText","wrapper","slot","assigned","node","_nextSelectId","createIdCounter","HelixSelect","FormMixin","HelixElement","path","listbox","_a","trigger","_b","enabledIndices","o","i","nextDown","lastEnabled","prevUp","lastEnabledUp","selIdx","opt","char","startIndex","matching","target","ctor","supportsIdrefElementReferences","records","consumerCleared","record","oldValue","newValue","installAriaIdrefMirror","_c","_d","changedProperties","validSizes","devWarn","labelSlot","hasLabellableSlotContent","nodes","element","text","trimmedText","internals","isInvalid","liveAriaLabel","hostAriaLabel","internalLabel","slottedLabelEl","helpEl","errorEl","liveLabelledBy","liveDescribedBy","externalLabelTokens","externalDescTokens","labelEls","resolveIdrefTokens","hasEffectiveLabelledBy","descEls","hasError","refsInternals","resolvedLabelIds","el","id","hostLabel","liveLabel","_e","consumerDescText","helpText","errorText","combinedDescription","tab","expanded","activeDescendant","args","candidate","hasLabelledBy","liveAttr","observer","state","_mode","disabled","parsed","cloneIntoSelect","optEl","clone","child","selectedIndex","option","options","index","html","repeat","isSelected","isFocused","classMap","nothing","hasHelp","fieldClasses","triggerClasses","selectClasses","ifDefined","__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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"}
|
|
1
|
+
{"version":3,"file":"hx-select-DahFehiZ.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 /* Round-3 finding 1: host is the canonical combobox surface, so it owns\n keyboard focus. Suppress the UA default outline; the custom focus ring\n is painted on the inner trigger via :host(:focus-visible)\n .field__trigger so visual feedback follows the host's focus state. */\n outline: none;\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 /* Round-3 finding 1 / CodeRabbit F1: trigger is a <button type=\"button\">\n (labelable) so native <label for> click activation works for mouse\n users. Reset native button chrome before applying field styles. */\n appearance: none;\n -webkit-appearance: none;\n margin: 0;\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: inherit;\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 /* Round-3 finding 1: host is the canonical focusable surface. Both the\n :host(:focus-visible) descendant selector AND the legacy\n .field__trigger:focus-visible (kept for forced-colors regression test\n parity) paint the focus ring on the visual trigger. */\n :host(:focus-visible) .field__trigger,\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 :host(:focus-visible) .field--error .field__trigger,\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 :host(:focus-visible) .field__trigger,\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';\n// Round-2 finding 7: do NOT compose `forcedColorsField`. The shared mixin's\n// selectors target real `<input>`/`<select>`/`[part=\"input\"]`/`[part=\"control\"]`\n// surfaces, which `hx-select` does not expose — the announced control is the\n// `[part=\"trigger\"]` div. The bespoke `.field__trigger:focus-visible` rule\n// inside `hx-select.styles.ts` is the only forced-colors path that paints,\n// so composing the mixin would only add dead selectors. Per the\n// `forced-colors.ts` contract (\"compose mixin OR author bespoke block, not\n// both\"), we author the bespoke block.\nimport { devWarn } from '../../utils/dev-warn.js';\nimport {\n installAriaIdrefMirror,\n resolveIdrefTokens,\n supportsIdrefElementReferences,\n type AriaIdrefMirrorHandle,\n} from '../../utils/aria-idref.js';\n\n/**\n * Reads visible text from a shadow wrapper that contains a `<slot>`. Prefers\n * the slot's flattened assigned-nodes text when light DOM is projected,\n * otherwise falls back to the wrapper's own `textContent` (so property-driven\n * fallback content rendered inside the slot is still readable). Aligned with\n * the Group 2 round-23 P2 helper used by `hx-radio-group` / `hx-checkbox-group`.\n */\nfunction readSlottedOrShadowText(wrapper: Element): string {\n const slot = wrapper.querySelector('slot');\n if (slot) {\n const assigned = (slot as HTMLSlotElement).assignedNodes({ flatten: true });\n if (assigned.length > 0) {\n return assigned\n .map((node) => node.textContent ?? '')\n .join('')\n .trim();\n }\n }\n return (wrapper.textContent ?? '').trim();\n}\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 * @fires {Event} invalid - Platform constraint-validation event fired when checkValidity() / reportValidity() determine the value is invalid (form-associated component contract via ElementInternals.setValidity).\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 * @aaa-certified 2026-05-08\n * @aaa-criteria 1.4.6, 1.4.9, 2.1.3, 2.3.3, 2.4.12, 2.4.13, 2.5.5, 3.2.5, 3.3.6, forced-colors, apg-keyboard\n * @aaa-audit src/components/hx-select/AAA-AUDIT.md\n * @keyboard-contract navigate=Arrow,Home,End; activate=Enter; dismiss=Escape; disabled-suppresses=true\n * @aria-pattern combobox\n * @aria-pattern-source https://www.w3.org/WAI/ARIA/apg/patterns/combobox/\n * @forced-colors-supported true\n * @stability stable\n * @since 3.7.0\n * @form-associated true\n * @theme-aware true\n * @brand-aware true\n * @drupal-sdc-eligible true\n * @react-wrapper-status complete\n * @figma-component-name hx-select\n * @priority-tier P0\n * @phi-handles false\n * @clinical-context none\n */\n@customElement('hx-select')\nexport class HelixSelect extends FormMixin(HelixElement) {\n static override styles = helixSelectStyles;\n\n // ─── Form Association ───\n\n /** Marks this element as form-associated for ElementInternals support. @internal */\n static override formAssociated = true;\n\n /**\n * Test seam (round-3 finding 4): when set to `true` or `false`, overrides\n * the platform `supportsIdrefElementReferences` probe before\n * `connectedCallback` seeds `_supportsIdrefRefs`. Mid-life flag flips on a\n * connected instance allowed stale modern internals (set during connect)\n * to leak into the fallback branch — tests must select the path BEFORE\n * the host connects so the synthetic environment matches a legacy engine.\n *\n * Production code MUST NOT touch this field. It is a `static` so the test\n * stub cleanup is global and obvious.\n * @internal\n */\n static __testSupportsIdrefRefsOverride: boolean | null = null;\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 /**\n * Whether the named label slot contributes a useful name. Round-3 finding 3:\n * the previous boolean tracked `assignedNodes().length > 0`, which is `true`\n * for whitespace-only slot content. That suppressed the unlabeled devWarn\n * even when the slot only carried a stray newline. This flag now requires\n * either a labellable element OR non-empty trimmed text content.\n * @internal\n */\n @state() private _hasLabelSlot = false;\n /**\n * Source of the accessible name. Round-3 finding 8 replaces the magic\n * sentinel `'*slotted*'` with a discriminated union so a future caller\n * setting `label=\"*slotted*\"` literally cannot be confused with a slotted\n * label.\n * @internal\n */\n @state() private _labelSource: 'string' | 'slot' | 'none' = 'none';\n /**\n * Flattened, trimmed text content of any text nodes in the label slot —\n * used to drive `internals.ariaLabel` when the consumer projects only a\n * text node (no element to add to `labelEls`). Round-3 finding 3.\n * @internal\n */\n @state() private _labelSlotText = '';\n /** Whether the help-text slot contains projected content. @internal */\n @state() private _hasHelpSlot = false;\n /**\n * Direct reference to the first labellable element projected into the\n * `<slot name=\"label\">`. Round-3 finding 5 replaces the previous round-trip\n * through `document.getElementById(this._slottedLabelId)` so we no longer\n * mutate consumer light-DOM (assigning ids) and the lookup is robust under\n * nested shadow roots (round-3 finding 6).\n * @internal\n */\n private _slottedLabelEl: Element | null = null;\n /** Zero-based index of the keyboard-focused option in the listbox; -1 means none. @internal */\n @state() private _focusedOptionIndex = -1;\n /**\n * Whether the platform supports IDL element references on `ElementInternals`.\n * Drives the cross-shadow naming strategy: modern path uses\n * `internals.ariaLabelledByElements` / `internals.ariaDescribedByElements`\n * to bridge consumer light-DOM IDREFs into the host accessible node;\n * fallback path mirrors the consumer's `aria-labelledby` / `aria-describedby`\n * tokens onto host attributes and text-mirrors shadow help/error wrappers\n * via `internals.ariaDescription`.\n *\n * ARCHITECTURE — Round-3 host-canonical (overturns scope doc Path A).\n * Codex round-2 finding 1 demonstrated that the dual-channel approach\n * (host roleless on modern, host-announced on fallback) creates a \"named\n * surface ≠ focused surface\" gap on legacy engines. Round-3 commits to a\n * single canonical surface — the HOST — on BOTH paths:\n *\n * - `internals.role = 'combobox'` on both paths (and `role=\"combobox\"`\n * attribute mirror so CSS / `getAttribute` / AT inspecting the DOM see it).\n * - `tabindex=\"0\"` on the host (so it is the focusable surface).\n * - Click + keydown listeners on the host (so user input lands on the\n * announced surface).\n * - `focus()` routes to the host (so programmatic focus matches AT focus).\n * - `setValidity()` anchor = host (so UA validation UI routes to the\n * focusable announced surface).\n * - Inner trigger drops `role`, `tabindex`, and the combobox ARIA mirror\n * on both paths so AT does not see a doubled accessible.\n *\n * This matches Group 2's host-canonical pattern (PR #1625, validated for\n * `hx-radio-group` and `hx-checkbox-group`). The scope doc's APG concern\n * about doubled comboboxes is moot once the inner role is dropped.\n * @internal\n */\n @state() private _supportsIdrefRefs = true;\n /**\n * Deferred copy of `error` driven through reactive state so the persistent\n * live region can re-announce on transitions without direct DOM mutation.\n * Aligned with Group 2 round-1 finding #10.\n * @internal\n */\n @state() private _announcedError = '';\n /**\n * Cached invalidity flag derived from `internals.validity.valid` after the\n * latest `setValidity()` call. Both the modern (`internals.ariaInvalid`)\n * and fallback (host attribute) writes read from the same source so they\n * cannot disagree. Round-2 finding 2.\n * @internal\n */\n @state() private _invalid = false;\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 // ─── Host-canonical ARIA bookkeeping ───\n\n /**\n * Handle for the shared IDREF observer. See `installAriaIdrefMirror()`.\n * @internal\n */\n private _ariaMirror: AriaIdrefMirrorHandle | null = null;\n /**\n * Watches assigned `<slot name=\"help-text\">` nodes for in-place text\n * mutations so the no-IDL-ref fallback `internals.ariaDescription` stays in\n * sync. Aligned with Group 2 round-23 P2 (Finding C).\n * @internal\n */\n private _helpSlotTextObserver: MutationObserver | null = null;\n /**\n * Watches assigned `<slot name=\"error\">` nodes for in-place text mutations.\n * Aligned with Group 2 round-23 P2 (Finding C).\n * @internal\n */\n private _errorSlotTextObserver: MutationObserver | null = null;\n /**\n * Round-10 finding 1 (supersedes round-8 counter): dedicated host observer\n * scoped to `aria-describedby` with `attributeOldValue: true`. The fallback\n * path strips the host `aria-describedby` attribute on every sync (per\n * round-6 single-channel design). Once stripped, a subsequent consumer\n * `removeAttribute('aria-describedby')` causes NO DOM mutation (the\n * attribute is already absent), so the post-sync `getAttribute()` baseline\n * diff at the top of `_syncHostAriaSemantics` cannot detect the retraction\n * — `_consumerDescribedBy` would remain pinned to the originally-cached\n * string forever and the consumer's external help text would keep\n * concatenating into `internals.ariaDescription` indefinitely.\n *\n * Round-10 architectural lockdown (`.reports/aria-group-3-architecture-\n * decision-r10.md` section 7.1): the observer is governed by the\n * **disconnect-during-strip** discipline. Before the fallback strip\n * `removeAttribute('aria-describedby')` runs, this observer is\n * `disconnect()`ed; immediately after the strip, it is re-`observe()`d.\n * Self-mutations are therefore never delivered to the callback, so the\n * observer only sees consumer-driven mutations. This eliminates the\n * round-8 pending-strip counter / discriminator entirely and the lockstep\n * invariant it depended on.\n *\n * The observer detects `oldValue !== null && newValue === null`\n * (consumer authentically retracted the attribute) and clears\n * `_consumerDescribedBy`. Framework-batched attach-then-detach (Vue /\n * React / Lit reconciliation flipping `aria-describedby` from a string to\n * null in one render commit) produces two records in the same callback\n * batch — the second record's `oldValue` is observable. The bare\n * `removeAttribute` no-op on an already-absent attribute is unobservable\n * by design (a null → null DOM operation is not a mutation); see\n * `hx-select.test.ts` round-10 contract block for the three documented\n * retraction sequences.\n *\n * @internal\n */\n private _hostDescribedByObserver: MutationObserver | null = null;\n /**\n * Last value of `aria-labelledby` we wrote to the host. Used to distinguish\n * external (consumer) attribute mutations from our own internal augmentation\n * writes. Aligned with Group 2 round-10 P2.\n * @internal\n */\n private _lastWrittenLabelledBy: string | null = null;\n /** @internal — see `_lastWrittenLabelledBy`. */\n private _lastWrittenDescribedBy: string | null = null;\n /**\n * Last value of `aria-label` we wrote to the host. Used to distinguish\n * external (consumer) attribute mutations from our own internal mirror\n * writes on the fallback path. Round-5 finding 1: without this snapshot\n * the round-3 fallback `aria-label` mirror was self-sealing — sync N\n * wrote `aria-label=\"Country\"`, sync N+1 read that same string back via\n * `getAttribute('aria-label')` and treated it as a consumer override,\n * caching it into `internals.ariaLabel` and short-circuiting\n * `_writeHostAttributeMirror`. Subsequent label/accessibleLabel/slot\n * mutations never propagated.\n * @internal\n */\n private _lastWrittenAriaLabel: string | null = null;\n /**\n * Most recently observed *consumer-supplied* `aria-labelledby` baseline.\n * Refreshed only when the host attribute changes via an external write —\n * internal writes leave the baseline untouched. Cached so consumer tokens\n * can replay if their target element later attaches to the DOM.\n * @internal\n */\n private _consumerLabelledBy: string | null = null;\n /** @internal — see `_consumerLabelledBy`. */\n private _consumerDescribedBy: string | null = null;\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n // Group 2 round-17 P1 parity: detect IDL element-references API support\n // so the cross-shadow naming strategy can branch between modern (host\n // `internals.aria*Elements`) and fallback (host-attribute mirror).\n //\n // Round-3 finding 4: honour the static test override so synthetic\n // environments choose the path BEFORE connect runs. Without this seam,\n // tests had to flip the `_supportsIdrefRefs` flag mid-life — by then,\n // the modern branch had already written `internals.ariaLabelledByElements`\n // and the fallback branch never cleared it, so stale modern artifacts\n // leaked into \"fallback\" assertions.\n const ctor = this.constructor as typeof HelixSelect;\n this._supportsIdrefRefs =\n ctor.__testSupportsIdrefRefsOverride !== null\n ? ctor.__testSupportsIdrefRefsOverride\n : supportsIdrefElementReferences(this._internals);\n // Round-3 finding 1: host is the canonical combobox surface, so user\n // input listeners attach to the host (not the inner trigger).\n this.addEventListener('click', this._handleHostClick);\n this.addEventListener('keydown', this._handleKeydown);\n // Round-10 finding 1 (supersedes round-8 counter): install the dedicated\n // `aria-describedby` retraction observer BEFORE the seeded\n // `_syncHostAriaSemantics()` call below, then govern its lifetime with\n // the disconnect-during-strip discipline (see `_syncHostAriaSemantics`\n // fallback branch). The observer therefore sees only consumer-driven\n // mutations; self-strips never reach the callback. No counter, no\n // discriminator, no lockstep invariant.\n this._hostDescribedByObserver = new MutationObserver((records) => {\n let consumerCleared = false;\n for (const record of records) {\n if (record.attributeName !== 'aria-describedby') continue;\n const oldValue = record.oldValue;\n const newValue = this.getAttribute('aria-describedby');\n if (oldValue !== null && newValue === null) {\n // Consumer authentically retracted `aria-describedby`. Clear the\n // cached baseline so the next sync's description recompute drops\n // the external help text.\n this._consumerDescribedBy = null;\n consumerCleared = true;\n }\n }\n if (consumerCleared) {\n // Resync so `internals.ariaDescription` rebuilds without the\n // stale external description text on the fallback path.\n this._syncHostAriaSemantics();\n }\n });\n this._hostDescribedByObserver.observe(this, {\n attributes: true,\n attributeFilter: ['aria-describedby'],\n attributeOldValue: true,\n });\n // Seed root-independent semantics from connect so the host announces the\n // combobox role before first paint. Per round-10 finding 1, the\n // dedicated `aria-describedby` observer is wired above so the\n // disconnect-during-strip helper inside the sync method has a live\n // observer to disconnect/reconnect against.\n this._syncHostAriaSemantics();\n this._ariaMirror = installAriaIdrefMirror(this, () => {\n this._syncHostAriaSemantics();\n });\n }\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 this.removeEventListener('click', this._handleHostClick);\n this.removeEventListener('keydown', this._handleKeydown);\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 this._ariaMirror?.disconnect();\n this._ariaMirror = null;\n this._helpSlotTextObserver?.disconnect();\n this._helpSlotTextObserver = null;\n this._errorSlotTextObserver?.disconnect();\n this._errorSlotTextObserver = null;\n this._hostDescribedByObserver?.disconnect();\n this._hostDescribedByObserver = null;\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('label')) {\n // Round-3 finding 8: keep the discriminated label-source state in sync\n // when the `label` property toggles between empty and non-empty.\n this._refreshLabelSource();\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 // Host-elevated ARIA semantics — see _syncHostAriaSemantics.\n this._syncHostAriaSemantics();\n // Group 2 round-1 finding #10: drive re-announcement from reactive state\n // so the persistent live region stays in the shadow tree across error\n // transitions. The persistent `<div role=\"alert\">` always lives in DOM;\n // changing `_announcedError` re-paints its slot fallback content and AT\n // re-announces.\n if (changedProperties.has('error')) {\n const previousError = changedProperties.get('error') as string;\n if (previousError && this.error) {\n // Error→error: clear then re-set after rAF so AT re-announces.\n this._announcedError = '';\n requestAnimationFrame(() => {\n this._announcedError = this.error;\n });\n } else {\n this._announcedError = this.error;\n }\n }\n }\n\n override firstUpdated(changedProperties: PropertyValues<this>): void {\n super.firstUpdated(changedProperties);\n // Round-3 finding 3: `slotchange` fires as a microtask after the\n // initial synchronous render, so reading the lazily-tracked\n // `_hasLabelSlot` here would observe its stale `false` seed. Read the\n // slot's assigned nodes directly so the unlabeled devWarn observes the\n // real first-paint state.\n const labelSlot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"label\"]');\n const hasLabellableSlotContent = labelSlot\n ? this._readLabelSlotState(labelSlot).hasUsefulName\n : false;\n // WCAG 4.1.2: warn when no accessible name is available. The host (the\n // canonical announced surface) needs either a `label` prop, an\n // `accessible-label` attribute, or a host-level `aria-label` /\n // `aria-labelledby` so AT can identify the form field.\n if (\n !this.label &&\n !this.accessibleLabel &&\n !hasLabellableSlotContent &&\n !this.getAttribute('aria-label') &&\n !this.getAttribute('aria-labelledby')\n ) {\n devWarn(\n 'hx-select',\n 'No accessible label provided. Set the `label` attribute, `accessible-label`, `aria-label`, `aria-labelledby`, or project a `<slot name=\"label\">` child. An unlabeled select violates WCAG 2.1 AA (4.1.2 Name, Role, Value).',\n );\n }\n }\n\n /**\n * Reads the label slot's assigned nodes and computes the discriminated\n * naming state. Round-3 finding 3 + 5 + 8: an empty whitespace-only slot\n * does NOT count as a useful name; the first labellable element is\n * captured by reference (no consumer-DOM mutation); the result drives a\n * discriminated `_labelSource` rather than a magic `'*slotted*'` string.\n * @internal\n */\n private _readLabelSlotState(slot: HTMLSlotElement): {\n hasUsefulName: boolean;\n element: Element | null;\n text: string;\n } {\n const nodes = slot.assignedNodes({ flatten: true });\n let element: Element | null = null;\n let text = '';\n for (const node of nodes) {\n if (node.nodeType === Node.ELEMENT_NODE && !element) {\n element = node as Element;\n } else if (node.nodeType === Node.TEXT_NODE) {\n text += node.textContent ?? '';\n }\n }\n const trimmedText = text.trim();\n return {\n hasUsefulName: element !== null || trimmedText.length > 0,\n element,\n text: trimmedText,\n };\n }\n\n // ─── Host-canonical ARIA sync ───\n\n /**\n * Mirrors combobox semantics onto the host via ElementInternals so that the\n * host IS the canonical announced surface on BOTH the modern (IDL element\n * references) and fallback paths. Round-3 finding 1: codex round-2\n * demonstrated that putting `role=\"combobox\"` on the host on fallback while\n * leaving focus/click/keyboard/validity anchored to the inner trigger\n * created a \"named surface ≠ focused surface\" gap. Round-3 collapses both\n * paths to the host-canonical model used by Group 2's `hx-radio-group` /\n * `hx-checkbox-group` (PR #1625):\n *\n * - `internals.role = 'combobox'` so AT advertises the combobox role.\n * - Host attribute `role=\"combobox\"` mirror so legacy engines, axe-core,\n * and `getAttribute('role')` agree with the internals default.\n * - Host attribute `tabindex=\"0\"` so the host is the focusable surface.\n * - Host attributes `aria-expanded`, `aria-haspopup`, `aria-controls`,\n * `aria-activedescendant`, `aria-required`, `aria-invalid`,\n * `aria-disabled` so AT walking the host's attribute graph sees the\n * combobox state without depending on internals defaults.\n * - Modern path additionally sets `internals.ariaLabelledByElements` /\n * `internals.ariaDescribedByElements` so consumer light-DOM IDREFs cross\n * the shadow boundary into the host's accessible node.\n * - Fallback path mirrors consumer label/desc tokens onto the host\n * attribute and text-mirrors shadow help/error wrappers via\n * `internals.ariaDescription`.\n * @internal\n */\n private _syncHostAriaSemantics(): void {\n const internals = this._internals;\n // Round-3 finding 1: host IS the canonical combobox on both paths.\n internals.role = 'combobox';\n internals.ariaRequired = this.required ? 'true' : 'false';\n // Round-2 finding 2 (preserved): read `internals.validity.valid` once and\n // cache the derived flag in reactive state so both the IDREF-elements\n // path and the host-attribute mirror read from the same source.\n const isInvalid = !internals.validity.valid;\n this._invalid = isInvalid;\n internals.ariaInvalid = isInvalid ? 'true' : 'false';\n internals.ariaDisabled = this.disabled ? 'true' : 'false';\n internals.ariaExpanded = this.open ? 'true' : 'false';\n internals.ariaHasPopup = 'listbox';\n\n // Round-5 finding 1: the live `aria-label` attribute may be either a\n // consumer-authored override OR our own previous mirror write. Compare\n // against `_lastWrittenAriaLabel` to distinguish — if the live value\n // matches what we wrote last sync, treat as component-owned (empty\n // string here) so the recompute pass below picks fresh values from\n // `label` / `accessibleLabel` / slotted text. Only a divergent live\n // value is treated as a consumer override.\n //\n // Round-8 finding 4 (low — deliberate behaviour): the `.trim() || ''`\n // collapse means a whitespace-only consumer override (e.g.\n // `aria-label=\" \"`) is treated as no consumer override at all — the\n // internal candidate (`label` / `accessibleLabel` / slotted text) wins\n // and overwrites the host attribute on the fallback path. This is a\n // deliberate choice: whitespace-only `aria-label` is a code smell\n // pattern that is hard to author intentionally, the AT-suppression\n // behaviour it sometimes produces is not part of the documented\n // hx-select contract, and silently erasing the consumer's intended\n // name would be the worse failure mode. A positive regression test in\n // `hx-select.test.ts` (`whitespace-only consumer aria-label is treated\n // as no override on fallback`) locks this intent.\n const liveAriaLabel = this.getAttribute('aria-label');\n const hostAriaLabel =\n liveAriaLabel !== null && liveAriaLabel !== this._lastWrittenAriaLabel\n ? liveAriaLabel.trim() || ''\n : '';\n\n // Resolve the candidate label/desc element references once — the IDL-ref\n // path consumes them as `Element[]`, the fallback path mirrors consumer\n // tokens onto the host attribute.\n const internalLabel = this.shadowRoot?.getElementById(this._labelId) ?? null;\n // Round-3 finding 5 + 6: hold the slotted label element by direct\n // reference (captured in `_handleLabelSlotChange`) so we no longer\n // mutate consumer light DOM with a tracked id and the lookup survives\n // nested shadow roots without a fragile `getElementById` chain.\n const slottedLabelEl = this._slottedLabelEl;\n const helpEl = this.shadowRoot?.getElementById(this._helpTextId) ?? null;\n const errorEl = this.shadowRoot?.getElementById(this._errorId) ?? null;\n\n // Group 2 round-10 P2: refresh the consumer baseline only when the host\n // attribute moved due to an *external* write. Compare the live attribute\n // against our last-written snapshot — if it differs, the consumer wrote.\n const liveLabelledBy = this.getAttribute('aria-labelledby');\n if (liveLabelledBy !== this._lastWrittenLabelledBy) {\n this._consumerLabelledBy = liveLabelledBy;\n }\n const liveDescribedBy = this.getAttribute('aria-describedby');\n if (liveDescribedBy !== this._lastWrittenDescribedBy) {\n this._consumerDescribedBy = liveDescribedBy;\n }\n const externalLabelTokens = this._consumerLabelledBy;\n const externalDescTokens = this._consumerDescribedBy;\n\n const labelEls = resolveIdrefTokens(this, externalLabelTokens);\n // Group 2 round-35: `aria-labelledby` is only \"effective\" when at least\n // one IDREF resolves. A typo or transiently-missing target must NOT erase\n // the visible label.\n const hasEffectiveLabelledBy = labelEls.length > 0;\n\n // Round-3 finding 8: discriminated union replaces the magic\n // `'*slotted*'` sentinel. The state name drives both the modern\n // `internals.ariaLabel` decision and the labelEls fallback append.\n if (hostAriaLabel) {\n internals.ariaLabel = hostAriaLabel;\n } else if (hasEffectiveLabelledBy) {\n // labelledby chain wins — ariaLabel must be null so it does not\n // shadow the chain.\n internals.ariaLabel = null;\n } else if (this._labelSource === 'slot') {\n if (slottedLabelEl) {\n // labelEls path will append the slotted element below; no string.\n internals.ariaLabel = null;\n } else {\n // Text-only slot — no element to add to labelEls. Mirror the\n // flattened text into ariaLabel so AT still announces the name.\n internals.ariaLabel = this._labelSlotText || this.accessibleLabel || null;\n }\n } else if (this._labelSource === 'string') {\n internals.ariaLabel = this.label || this.accessibleLabel || null;\n } else {\n internals.ariaLabel = this.accessibleLabel || null;\n }\n if (!hasEffectiveLabelledBy && !hostAriaLabel) {\n if (this._labelSource === 'slot' && slottedLabelEl) {\n labelEls.push(slottedLabelEl);\n } else if (this._labelSource === 'string' && internalLabel) {\n labelEls.push(internalLabel);\n }\n }\n\n const descEls = resolveIdrefTokens(this, externalDescTokens);\n const hasError = !!(this.error || this._hasErrorSlot);\n if (helpEl && !hasError && (this.helpText || this._hasHelpSlot)) {\n descEls.push(helpEl);\n }\n if (errorEl && hasError) {\n descEls.push(errorEl);\n }\n\n if (this._supportsIdrefRefs) {\n type InternalsWithRefs = ElementInternals & {\n ariaLabelledByElements: Element[] | null;\n ariaDescribedByElements: Element[] | null;\n };\n const refsInternals = internals as InternalsWithRefs;\n refsInternals.ariaLabelledByElements = labelEls.length > 0 ? labelEls : null;\n refsInternals.ariaDescribedByElements = descEls.length > 0 ? descEls : null;\n // Clear stale fallback ariaDescription string in case a prior sync ran\n // on the fallback path (e.g. tests using the static override seam).\n internals.ariaDescription = null;\n } else {\n // ─── No-IDL-ref fallback (Group 2 round-19 P1 parity) ───\n // The IDL element-references API is unavailable, so internal shadow\n // help/error/label wrappers cannot be projected onto the host\n // accessibility node via `internals.aria*Elements`. The host owns the\n // ARIA strings (via `internals.ariaLabel`, `internals.ariaDescription`)\n // and we mirror only consumer-supplied tokens onto the host attribute\n // (shadow ids cannot resolve across the boundary).\n //\n // Round-3 finding 4 (symmetric clear): explicitly null the modern\n // IDL element-reference fields so a stub-flipped fallback test cannot\n // inherit modern artifacts seeded on a previous sync. Production code\n // never hits this branch with non-null modern refs (the platform\n // probe is once-per-connect), but the symmetric clear hardens the\n // contract for tests using the static override seam.\n type InternalsWithRefs = ElementInternals & {\n ariaLabelledByElements: Element[] | null;\n ariaDescribedByElements: Element[] | null;\n };\n const refsInternals = internals as InternalsWithRefs;\n refsInternals.ariaLabelledByElements = null;\n refsInternals.ariaDescribedByElements = null;\n\n // Round-3 finding 7: build the host `aria-labelledby` mirror from\n // *resolved* token ids only — `labelEls.map(e => e.id).filter(Boolean)`.\n // The previous round-2 implementation ran the consumer token strings\n // through `filter(Boolean)`, which only stripped empty strings, not\n // unresolved ids. A consumer `aria-labelledby=\"resolved missing\"` was\n // written back wholesale, so MutationObservers and legacy AT saw a\n // persistent broken IDREF on the host.\n const resolvedLabelIds = labelEls.map((el) => el.id).filter((id): id is string => !!id);\n const hostLabel = hasEffectiveLabelledBy ? resolvedLabelIds.join(' ') : '';\n const liveLabel = this.getAttribute('aria-labelledby');\n if (hostLabel) {\n if (liveLabel !== hostLabel) {\n this.setAttribute('aria-labelledby', hostLabel);\n }\n this._lastWrittenLabelledBy = hostLabel;\n } else if (liveLabel !== null) {\n this.removeAttribute('aria-labelledby');\n this._lastWrittenLabelledBy = null;\n }\n\n // Round-6 (option b — single-channel fallback): per W3C AccName 1.2,\n // an `aria-describedby` token list — even one authored by the\n // consumer — takes precedence over `internals.ariaDescription`. When\n // a consumer authors any working `aria-describedby` token on the\n // host, AT may announce only the consumer text and never reach the\n // internal help/error strings the component renders in its shadow\n // root. Cross-shadow id splice (option a) does not portably resolve\n // on Firefox / VoiceOver, so we collapse to a single channel:\n // concatenate the consumer-described text with internal help/error\n // text into `internals.ariaDescription` (below) and strip the host\n // `aria-describedby` attribute on this path regardless of authorship.\n // The consumer's intended description text is preserved (it surfaces\n // through `internals.ariaDescription`); only the attribute mirror is\n // removed. `_consumerDescribedBy` retains the consumer's original\n // token list (see baseline diff at lines 691-694) so the modern path\n // continues to work if the platform later upgrades and re-syncs.\n // Modern path is unchanged (it uses `ariaDescribedByElements` which\n // the platform concatenates correctly across shadow roots).\n if (this.hasAttribute('aria-describedby')) {\n // Round-10 finding 1 (supersedes round-8 counter): disconnect the\n // dedicated host observer, perform our self-mutation strip, then\n // reconnect. The observer therefore never delivers our own write\n // to the callback, eliminating the round-8 counter / discriminator\n // and the lockstep invariant it depended on. See architecture\n // decision `.reports/aria-group-3-architecture-decision-r10.md`\n // section 7.1. Genuine consumer-driven mutations (set / set-empty\n // / framework attach-then-detach) continue to be observed because\n // we reconnect immediately, with no microtask boundary inside the\n // strip block where a consumer mutation could slip through.\n this._hostDescribedByObserver?.disconnect();\n this.removeAttribute('aria-describedby');\n this._hostDescribedByObserver?.observe(this, {\n attributes: true,\n attributeFilter: ['aria-describedby'],\n attributeOldValue: true,\n });\n }\n this._lastWrittenDescribedBy = null;\n\n // Resolve consumer-described elements through the same idref helper\n // the modern path uses, then concatenate their text with internal\n // shadow help/error text. Tokens that fail to resolve are dropped by\n // `resolveIdrefTokens` (matching native AT behaviour) so unresolved\n // ids do not leak into the description as literal strings. Broken\n // consumer tokens stay cached in `_consumerDescribedBy` and replay\n // when their target later attaches.\n const consumerDescEls = resolveIdrefTokens(this, externalDescTokens);\n const consumerDescText = consumerDescEls\n .map((el) => (el.textContent ?? '').trim())\n .filter(Boolean)\n .join(' ');\n const helpText =\n helpEl && !hasError && (this.helpText || this._hasHelpSlot)\n ? readSlottedOrShadowText(helpEl)\n : '';\n const errorText = errorEl && hasError ? readSlottedOrShadowText(errorEl) : '';\n const combinedDescription = [consumerDescText, helpText, errorText]\n .filter(Boolean)\n .join(' ')\n .trim();\n internals.ariaDescription = combinedDescription || null;\n\n // Round-3 finding 1 (host-canonical): on the fallback path the host\n // attributes carry the combobox state in addition to the internals\n // defaults. CSS / `getAttribute` / axe-core read these attributes\n // (internals defaults are not exposed to either).\n this._writeHostAttributeMirror({ hostAriaLabel, isInvalid });\n }\n\n // Round-3 finding 1: host attributes for the canonical combobox state.\n // Always written on BOTH paths (the modern path's internals defaults\n // are invisible to CSS/getAttribute, so attribute writes are required\n // to match `expect(el.getAttribute('role')).toBe('combobox')` and to\n // make `:host(:focus-visible)` paint a focus ring).\n this._writeHostRoleAndTabindex();\n this._writeHostComboboxStateAttributes();\n }\n\n /**\n * Writes host `role=\"combobox\"` and `tabindex` so the host is the\n * focusable, AT-visible canonical surface. Round-3 finding 1.\n * @internal\n */\n private _writeHostRoleAndTabindex(): void {\n if (this.getAttribute('role') !== 'combobox') {\n this.setAttribute('role', 'combobox');\n }\n const tab = this.disabled ? '-1' : '0';\n if (this.getAttribute('tabindex') !== tab) {\n this.setAttribute('tabindex', tab);\n }\n }\n\n /**\n * Writes the host's combobox state attributes (`aria-expanded`,\n * `aria-haspopup`, `aria-controls`, `aria-activedescendant`,\n * `aria-required`, `aria-invalid`, `aria-disabled`) on every sync. CSS\n * selectors and AT inspecting the live attribute graph read these — the\n * `internals.aria*` defaults are invisible. Round-3 finding 1.\n * @internal\n */\n private _writeHostComboboxStateAttributes(): void {\n if (this.getAttribute('aria-haspopup') !== 'listbox') {\n this.setAttribute('aria-haspopup', 'listbox');\n }\n if (this.getAttribute('aria-controls') !== this._listboxId) {\n this.setAttribute('aria-controls', this._listboxId);\n }\n const expanded = this.open ? 'true' : 'false';\n if (this.getAttribute('aria-expanded') !== expanded) {\n this.setAttribute('aria-expanded', expanded);\n }\n const activeDescendant =\n this.open && this._focusedOptionIndex >= 0 ? this._optionId(this._focusedOptionIndex) : null;\n if (activeDescendant) {\n if (this.getAttribute('aria-activedescendant') !== activeDescendant) {\n this.setAttribute('aria-activedescendant', activeDescendant);\n }\n } else if (this.hasAttribute('aria-activedescendant')) {\n this.removeAttribute('aria-activedescendant');\n }\n if (this.required) {\n if (this.getAttribute('aria-required') !== 'true') {\n this.setAttribute('aria-required', 'true');\n }\n } else if (this.hasAttribute('aria-required')) {\n this.removeAttribute('aria-required');\n }\n if (this._invalid) {\n if (this.getAttribute('aria-invalid') !== 'true') {\n this.setAttribute('aria-invalid', 'true');\n }\n } else if (this.hasAttribute('aria-invalid')) {\n this.removeAttribute('aria-invalid');\n }\n if (this.disabled) {\n if (this.getAttribute('aria-disabled') !== 'true') {\n this.setAttribute('aria-disabled', 'true');\n }\n } else if (this.hasAttribute('aria-disabled')) {\n this.removeAttribute('aria-disabled');\n }\n }\n\n /**\n * Writes a host `aria-label` attribute mirror on the fallback path so AT\n * inspecting the live attribute graph sees the resolved name even when\n * the platform lacks IDL element references. Skip when an effective\n * `aria-labelledby` is already set on the host (labelledby > label by\n * ARIA priority). Round-3 finding 1 + round-2 finding 5.\n * @internal\n */\n private _writeHostAttributeMirror(args: { hostAriaLabel: string; isInvalid: boolean }): void {\n const { hostAriaLabel } = args;\n if (hostAriaLabel) {\n // Round-8 finding 2 (medium): consumer-authored aria-label path. The\n // caller already disambiguated against `_lastWrittenAriaLabel` (see\n // `_syncHostAriaSemantics` line ~668) — `hostAriaLabel` is non-empty\n // ONLY when the live attribute diverges from our last write. We must\n // null the snapshot here so the next sync's disambiguation correctly\n // classifies the consumer string as external on every subsequent pass.\n //\n // Without this null, the snapshot stays pinned to the value WE wrote\n // last. If the consumer subsequently rewrites `aria-label` to a string\n // that happens to equal our prior write, the disambiguation would see\n // `liveAttr === _lastWrittenAriaLabel` and treat the consumer string\n // as component-owned — a later internal `label = ''` would then\n // silently delete the consumer's attribute. The sentinel `null` means\n // \"we did NOT author the live attr,\" which makes a coincident-string\n // round-trip still differ from `null` and correctly classify the\n // consumer string as external.\n this._lastWrittenAriaLabel = null;\n return;\n }\n const candidate =\n this.accessibleLabel ||\n this.label ||\n (this._labelSource === 'slot' ? this._labelSlotText : '') ||\n '';\n const hasLabelledBy = this.hasAttribute('aria-labelledby');\n const liveAttr = this.getAttribute('aria-label');\n if (!hasLabelledBy && candidate) {\n if (liveAttr !== candidate) {\n this.setAttribute('aria-label', candidate);\n }\n // Round-5 finding 1: record what we just wrote so the next sync can\n // distinguish \"I wrote this\" from \"consumer overrode this.\"\n this._lastWrittenAriaLabel = candidate;\n } else {\n // No candidate (label/accessibleLabel cleared) OR labelledby chain\n // wins. Round-5 finding 1: if the live attribute matches our last\n // mirror write, it is component-owned and must be removed now that\n // the source has emptied. Otherwise (whitespace-only attribute or no\n // attribute at all) keep historical behaviour.\n if (liveAttr !== null && liveAttr === this._lastWrittenAriaLabel) {\n this.removeAttribute('aria-label');\n this._lastWrittenAriaLabel = null;\n } else if (this.hasAttribute('aria-label') && !liveAttr?.trim()) {\n this.removeAttribute('aria-label');\n this._lastWrittenAriaLabel = null;\n } else if (!this.hasAttribute('aria-label')) {\n // No mirror present — clear the snapshot so a future consumer\n // write starts from a clean slate.\n this._lastWrittenAriaLabel = null;\n }\n }\n }\n\n /**\n * (Re-)installs the mutation observer over the current set of assigned\n * help-text-slot nodes. Aligned with Group 2 round-23 P2 (Finding C).\n * @internal\n */\n private _installHelpSlotTextObserver(slot: HTMLSlotElement | null): void {\n this._helpSlotTextObserver?.disconnect();\n if (!slot) {\n this._helpSlotTextObserver = null;\n return;\n }\n const observer = new MutationObserver(() => {\n this._syncHostAriaSemantics();\n });\n slot.assignedNodes().forEach((node) => {\n observer.observe(node, {\n characterData: true,\n childList: true,\n subtree: true,\n });\n });\n this._helpSlotTextObserver = observer;\n }\n\n /**\n * (Re-)installs the mutation observer over the current set of assigned\n * error-slot nodes. Aligned with Group 2 round-23 P2 (Finding C).\n * @internal\n */\n private _installErrorSlotTextObserver(slot: HTMLSlotElement | null): void {\n this._errorSlotTextObserver?.disconnect();\n if (!slot) {\n this._errorSlotTextObserver = null;\n return;\n }\n const observer = new MutationObserver(() => {\n this._syncHostAriaSemantics();\n });\n slot.assignedNodes().forEach((node) => {\n observer.observe(node, {\n characterData: true,\n childList: true,\n subtree: true,\n });\n });\n this._errorSlotTextObserver = observer;\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 // Round-3 finding 1: the host is now the canonical announced + focused\n // combobox surface, so anchor `setValidity()` to the host. The UA\n // routes validation UI / error recovery to a focusable element with\n // a stable accessible name — that is the host (which carries\n // `role=\"combobox\"`, `tabindex=\"0\"`, and the resolved name via\n // `internals.ariaLabel*`). Anchoring to the inner trigger would route\n // recovery into the shadow tree, where the consumer cannot intercept.\n this._internals.setValidity({ valueMissing: true }, this.error || this.labelRequired, this);\n } else {\n this._internals.setValidity({});\n }\n // Group 2 round-1 finding #6: re-sync host ARIA after every setValidity()\n // so `aria-invalid` reflects the freshly computed validity state.\n this._syncHostAriaSemantics();\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 /**\n * Tracks the slotted label so projected `<span slot=\"label\">` content joins\n * the accessible-name chain without forcing the consumer to also pass the\n * `label` property.\n *\n * Round-3 findings 3, 5, 6, 8:\n * - 3: count whitespace-only text as \"no useful name\" (devWarn must fire).\n * - 5: hold the slotted element by direct reference instead of mutating\n * consumer light DOM with a tracked id.\n * - 6: lookup is by reference, so it survives nested shadow roots without\n * the fragile `getElementById` chain.\n * - 8: discriminated `_labelSource` replaces the magic `'*slotted*'`\n * sentinel string.\n * @internal\n */\n private _handleLabelSlotChange(e: Event): void {\n if (!(e.target instanceof HTMLSlotElement)) return;\n const state = this._readLabelSlotState(e.target);\n this._hasLabelSlot = state.hasUsefulName;\n this._slottedLabelEl = state.element;\n this._labelSlotText = state.text;\n this._refreshLabelSource();\n this._syncHostAriaSemantics();\n }\n\n /**\n * Recomputes the discriminated label source. Round-3 finding 8.\n *\n * CodeRabbit F2: slotted label wins over the `label` prop. When a\n * consumer provides BOTH a `<span slot=\"label\">` AND a `label=\"...\"`\n * attribute the visible label is the slotted node (rendered by the\n * named slot's default content branch never firing). Preferring the\n * string form here would route the accessible name through the prop\n * and diverge from the rendered text. The slot is the canonical\n * source whenever it has useful content.\n * @internal\n */\n private _refreshLabelSource(): void {\n if (this._hasLabelSlot) {\n this._labelSource = 'slot';\n } else if (this.label) {\n this._labelSource = 'string';\n } else {\n this._labelSource = 'none';\n }\n }\n\n /** @internal */\n private _handleErrorSlotChange(e: Event): void {\n if (!(e.target instanceof HTMLSlotElement)) return;\n this._hasErrorSlot = e.target.assignedNodes({ flatten: true }).length > 0;\n // Group 2 round-23 P2 (Finding C): re-tune the in-place text observer\n // over the new assigned-node set so in-place `textContent` rewrites of\n // slotted error nodes resync `internals.ariaDescription` on the no-IDL-ref\n // fallback path. `slotchange` only fires when the *node set* changes.\n this._installErrorSlotTextObserver(e.target);\n this._syncHostAriaSemantics();\n }\n\n /** @internal */\n private _handleHelpSlotChange(e: Event): void {\n if (!(e.target instanceof HTMLSlotElement)) return;\n this._hasHelpSlot = e.target.assignedNodes({ flatten: true }).length > 0;\n this._installHelpSlotTextObserver(e.target);\n this._syncHostAriaSemantics();\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 // ─── Host Click ───\n\n /**\n * Click handler attached to the host. Round-3 finding 1: the host is the\n * canonical combobox surface, so user input listeners live here. Clicks\n * inside the open listbox are handled by the per-option `@click` binding\n * in `_renderOptions()` and stop here only if `composedPath` indicates\n * the original click target was the open listbox panel (so option clicks\n * don't double-toggle the dropdown).\n * @internal\n */\n private _handleHostClick = (e: MouseEvent): void => {\n const path = e.composedPath();\n // Ignore clicks that originated inside the open listbox panel — the\n // option's own `@click` handler already routed selection.\n const listbox = this.shadowRoot?.querySelector('.field__listbox') ?? null;\n if (listbox && path.includes(listbox)) {\n return;\n }\n // CodeRabbit F3: only toggle when the click traverses the trigger\n // element. Clicks on the slotted label, help text, error text, or\n // any other host-light-DOM content must NOT toggle the dropdown —\n // they are consumer-owned regions that happen to live inside the\n // host. The `<label for=${selectId}>` we render in shadow DOM\n // forwards label clicks to the <button> trigger natively, so those\n // clicks reach this handler with the trigger in their composedPath\n // and toggle correctly.\n const trigger = this._trigger ?? this.shadowRoot?.querySelector('.field__trigger') ?? null;\n if (!trigger || !path.includes(trigger)) {\n return;\n }\n this._toggleDropdown();\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 // Round-3 finding 1: refocus the host (the canonical combobox).\n this.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 /**\n * Moves focus to the host. Round-3 finding 1: the host is the canonical\n * combobox surface (`role=\"combobox\"`, `tabindex=\"0\"`), so programmatic\n * focus must land there to match where AT routes focus and where\n * `setValidity()` anchors recovery.\n */\n override focus(options?: FocusOptions): void {\n HTMLElement.prototype.focus.call(this, 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 const hasHelp = !!this.helpText || this._hasHelpSlot;\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 // Round-3 finding 1: the host is the canonical announced + focused\n // combobox surface on BOTH paths. The inner trigger is a presentational\n // visual surface only — it carries no role, no tabindex, and no ARIA\n // attributes. User input listeners (click, keydown) live on the host so\n // the focused surface IS the announced surface; programmatic `focus()`\n // and `setValidity()` anchor to the host for the same reason. The hidden\n // native `<select>` is `aria-hidden=\"true\"`, never the announced surface.\n\n return html`\n <div part=\"field\" class=${classMap(fieldClasses)}>\n <!-- Label -->\n <slot name=\"label\" @slotchange=${this._handleLabelSlotChange}>\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 <!--\n Visual trigger surface. Round-3 finding 1: role, tabindex,\n and combobox ARIA all live on the host so AT sees a single\n announced + focused surface. The trigger is rendered as a\n <button type=\"button\"> (a labelable element) so the visible\n label[for=selectId] performs native click activation —\n mouse users clicking the label focus the trigger which\n bubbles to the host click handler and toggles the dropdown.\n type=\"button\" prevents implicit form submission. The trigger\n has no role and no ARIA, so AT walks its subtree text as\n the combobox's value content (host carries the real\n association via internals.ariaLabel*).\n -->\n <button\n type=\"button\"\n part=\"trigger\"\n id=${this._selectId}\n class=${classMap(triggerClasses)}\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 </button>\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 @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 <!--\n Persistent error live region. role=\"alert\" is set from first paint\n so the WAI-ARIA contract for live updates is honoured: content\n changes in place rather than the container being toggled. Aligned\n with Group 2 round-1 finding #10.\n -->\n <div\n part=\"error\"\n class=\"field__error\"\n id=${this._errorId}\n role=\"alert\"\n ?hidden=${!hasError}\n >\n <slot name=\"error\" @slotchange=${this._handleErrorSlotChange}\n >${this._announcedError}</slot\n >\n </div>\n\n <!--\n Persistent help-text container. Rendered whenever the property OR\n the slot has content; hidden when an error is present so guidance\n does not compete with validation feedback. Always in the shadow\n tree so the host's aria-describedby chain is stable.\n -->\n <div\n part=\"help-text\"\n class=\"field__help-text\"\n id=${this._helpTextId}\n ?hidden=${!hasHelp || hasError}\n >\n <slot name=\"help-text\" @slotchange=${this._handleHelpSlotChange}>${this.helpText}</slot>\n </div>\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","readSlottedOrShadowText","wrapper","slot","assigned","node","_nextSelectId","createIdCounter","HelixSelect","FormMixin","HelixElement","path","listbox","_a","trigger","_b","enabledIndices","o","i","nextDown","lastEnabled","prevUp","lastEnabledUp","selIdx","opt","char","startIndex","matching","target","ctor","supportsIdrefElementReferences","records","consumerCleared","record","oldValue","newValue","installAriaIdrefMirror","_c","_d","changedProperties","validSizes","devWarn","labelSlot","hasLabellableSlotContent","nodes","element","text","trimmedText","internals","isInvalid","liveAriaLabel","hostAriaLabel","internalLabel","slottedLabelEl","helpEl","errorEl","liveLabelledBy","liveDescribedBy","externalLabelTokens","externalDescTokens","labelEls","resolveIdrefTokens","hasEffectiveLabelledBy","descEls","hasError","refsInternals","resolvedLabelIds","el","id","hostLabel","liveLabel","_e","consumerDescText","helpText","errorText","combinedDescription","tab","expanded","activeDescendant","args","candidate","hasLabelledBy","liveAttr","observer","state","_mode","disabled","parsed","cloneIntoSelect","optEl","clone","child","selectedIndex","option","options","index","html","repeat","isSelected","isFocused","classMap","nothing","hasHelp","fieldClasses","triggerClasses","selectClasses","ifDefined","__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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"}
|