@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@helixui/library",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.0",
|
|
4
4
|
"description": "Enterprise Web Component Library built with Lit 3.x",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"customElements": "custom-elements.json",
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"lit": "^3.3.2",
|
|
62
|
-
"@helixui/tokens": "3.
|
|
62
|
+
"@helixui/tokens": "3.8.0"
|
|
63
63
|
},
|
|
64
64
|
"peerDependencies": {
|
|
65
65
|
"@floating-ui/dom": "^1.7.6"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hx-action-bar-CitgcpGv.js","sources":["../../src/components/hx-action-bar/hx-action-bar.styles.ts","../../src/components/hx-action-bar/hx-action-bar.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixActionBarStyles = css`\n :host {\n display: block;\n }\n\n /* ─── Base ─── */\n\n .base {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: var(--hx-action-bar-gap, var(--hx-space-2, 0.5rem));\n padding: var(--hx-action-bar-padding, var(--hx-space-2, 0.5rem) var(--hx-space-3, 0.75rem));\n background: var(--hx-action-bar-bg, transparent);\n border: var(--hx-action-bar-border, none);\n box-sizing: border-box;\n width: 100%;\n }\n\n /* ─── Sticky (top) ─── */\n\n .base--sticky {\n position: sticky;\n top: 0;\n padding-top: max(var(--hx-action-bar-padding-block-start, 0px), env(safe-area-inset-top, 0px));\n z-index: var(--hx-action-bar-z-index, 10);\n /*\n * Consumers: when this bar is sticky, add scroll-padding-top to the scroll container\n * equal to the bar's rendered height so anchor targets are not hidden behind the bar.\n * Example: .scroll-container { scroll-padding-top: 2.5rem; }\n */\n }\n\n /* ─── Bottom sticky ─── */\n\n .base--bottom {\n position: sticky;\n bottom: 0;\n /*\n * Respect iOS home-indicator safe area on devices with notch/home bar.\n * Falls back to 0px on devices that do not support env().\n */\n padding-bottom: max(\n var(--hx-action-bar-padding-block-end, 0px),\n env(safe-area-inset-bottom, 0px)\n );\n z-index: var(--hx-action-bar-z-index, 10);\n }\n\n /* ─── Variant: outlined ─── */\n\n .base--outlined {\n background: var(--hx-action-bar-bg, var(--hx-color-surface-default, #ffffff));\n border: var(\n --hx-action-bar-border,\n var(--hx-border-width-thin, 1px) solid var(--hx-color-border-default, #d6dbd5)\n );\n border-radius: var(--hx-action-bar-border-radius, var(--hx-border-radius-md, 0.375rem));\n }\n\n /* ─── Variant: filled ─── */\n\n .base--filled {\n background: var(--hx-action-bar-bg, var(--hx-color-surface-raised, #f5f8f3));\n border-radius: var(--hx-action-bar-border-radius, var(--hx-border-radius-md, 0.375rem));\n }\n\n /* ─── Size modifiers ─── */\n\n .base--sm {\n padding: var(--hx-action-bar-padding, var(--hx-space-1, 0.25rem) var(--hx-space-2, 0.5rem));\n gap: var(--hx-action-bar-gap, var(--hx-space-1, 0.25rem));\n min-height: var(--hx-size-8, 2rem);\n }\n\n .base--md {\n min-height: var(--hx-size-10, 2.5rem);\n }\n\n .base--lg {\n padding: var(--hx-action-bar-padding, var(--hx-space-3, 0.75rem) var(--hx-space-4, 1rem));\n gap: var(--hx-action-bar-gap, var(--hx-space-3, 0.75rem));\n min-height: var(--hx-size-12, 3rem);\n }\n\n /* ─── Sections ─── */\n\n .section {\n display: flex;\n align-items: center;\n gap: inherit;\n }\n\n /*\n * Equal flex-basis on start and end guarantees the center section is visually centered\n * within the full bar width, regardless of how wide start and end content are.\n */\n .section--start {\n flex: 1 1 0;\n justify-content: flex-start;\n }\n\n .section--center {\n flex: 0 0 auto;\n justify-content: center;\n }\n\n .section--end {\n flex: 1 1 0;\n justify-content: flex-end;\n }\n\n /* ─── Slotted content ─── */\n\n ::slotted(*) {\n flex-shrink: 0;\n }\n\n /* ─── High Contrast Mode (forced-colors) ─── */\n\n @media (forced-colors: active) {\n /* Outlined variant: border is already present and will be honored by the browser */\n .base--outlined {\n border: 1px solid CanvasText;\n }\n\n /* Filled variant: background is suppressed — add border to maintain visual separation */\n .base--filled {\n border: 1px solid CanvasText;\n }\n }\n`;\n","import { html } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { HelixElement } from '../../base/index.js';\nimport { helixActionBarStyles } from './hx-action-bar.styles.js';\nimport { forcedColorsInteractive } from '../../styles/forced-colors.js';\nimport { devWarn } from '../../utils/dev-warn.js';\n\n// Re-export size type for external consumers.\nexport type ActionBarSize = 'sm' | 'md' | 'lg';\n\n/**\n * A horizontal toolbar container for grouping related action buttons and controls.\n * Implements the ARIA toolbar pattern with roving tabindex keyboard navigation.\n *\n * @summary Horizontal action bar for grouping related controls.\n *\n * @tag hx-action-bar\n *\n * @slot start - Left-aligned actions.\n * @slot - Center content (default slot).\n * @slot end - Right-aligned actions.\n * @slot overflow - Actions revealed when the bar is constrained for space.\n *\n * @csspart base - The root toolbar container element.\n * @csspart start - The start (left) slot wrapper.\n * @csspart center - The center (default) slot wrapper.\n * @csspart end - The end (right) slot wrapper.\n * @csspart overflow - The overflow slot wrapper (hidden when no overflow content).\n *\n * @cssprop [--hx-action-bar-bg=transparent] - Bar background color (default variant).\n * @cssprop [--hx-action-bar-border=none] - Bar border (default variant).\n * @cssprop [--hx-action-bar-padding=var(--hx-space-2,0.5rem) var(--hx-space-3,0.75rem)] - Inner padding.\n * @cssprop [--hx-action-bar-gap=var(--hx-space-2,0.5rem)] - Gap between slotted items.\n * @cssprop [--hx-action-bar-z-index=10] - Z-index when sticky or bottom position.\n *\n * @attr {string} accessible-label - Identifies the toolbar to assistive technology.\n * When multiple toolbars appear on the same page, each must have a unique, descriptive label.\n * Falls back to the native `aria-label` attribute if not set.\n *\n * @example\n * ```html\n * <hx-action-bar aria-label=\"Patient actions\">\n * <hx-button slot=\"start\">Save</hx-button>\n * <hx-button slot=\"end\" variant=\"ghost\">Cancel</hx-button>\n * </hx-action-bar>\n * ```\n * @cssprop [--hx-action-bar-padding-block-start=0px] - Padding.\n * @cssprop [--hx-action-bar-padding-block-end=0px] - Padding.\n * @cssprop [--hx-border-radius-md] - CSS custom property.\n * @cssprop [--hx-border-width-thin] - Width.\n * @cssprop [--hx-color-neutral-0] - Color.\n * @cssprop [--hx-color-neutral-200] - Color.\n * @cssprop [--hx-color-neutral-50] - Color.\n * @cssprop [--hx-size-8] - Size token.\n * @cssprop [--hx-size-10] - Size token.\n * @cssprop [--hx-size-12] - Size token.\n * @cssprop [--hx-space-1] - Spacing token.\n * @cssprop [--hx-space-2] - Spacing token.\n * @cssprop [--hx-space-3] - Spacing token.\n * @cssprop [--hx-space-4] - Spacing token.\n */\n@customElement('hx-action-bar')\nexport class HelixActionBar extends HelixElement {\n static override styles = [helixActionBarStyles, forcedColorsInteractive];\n\n /**\n * Size of the action bar — propagated as a data attribute to slotted children.\n * @attr hx-size\n */\n @property({ type: String, reflect: true, attribute: 'hx-size' })\n size: 'sm' | 'md' | 'lg' = 'md';\n\n /**\n * Visual variant controlling the bar background.\n * @attr variant\n */\n @property({ type: String, reflect: true })\n variant: 'default' | 'outlined' | 'filled' = 'default';\n\n /**\n * Position and sticky behavior of the action bar.\n * - `top` — normal flow (default)\n * - `sticky` — sticks to the top of the scroll container; add `scroll-padding-top` to the\n * scroll container equal to the bar height to prevent anchor targets from scrolling behind it\n * - `bottom` — sticks to the bottom of the scroll container with iOS safe-area-inset support\n * @attr position\n */\n @property({ type: String, reflect: true })\n position: 'top' | 'bottom' | 'sticky' = 'top';\n\n // The deprecated `sticky` boolean property has been removed in 3.0.0.\n // Use `position=\"sticky\"` instead.\n\n /**\n * Accessible label for the toolbar.\n * Required when multiple toolbars appear on the same page.\n *\n * Accepts both `accessible-label` and the standard `aria-label` HTML attribute.\n * The `accessible-label` attribute takes precedence when both are set.\n *\n * Previously this was exposed as the `ariaLabel` JS property, which shadowed\n * the native `HTMLElement.ariaLabel`. That shadowing is removed; use\n * `accessibleLabel` or the HTML attributes instead.\n *\n * @attr accessible-label\n */\n @property({ attribute: 'accessible-label' })\n accessibleLabel: string = '';\n\n /**\n * Observed mirror of the host's `aria-label` attribute so Lit re-renders\n * when consumers set `aria-label` (the standard HTML pattern).\n * @internal\n */\n @property({ attribute: 'aria-label' })\n private _ariaLabelAttr: string = '';\n\n /**\n * Returns the effective label for the toolbar, checking accessible-label first,\n * then the aria-label attribute, falling back to 'Actions'.\n * @internal\n */\n private get _effectiveLabel(): string {\n return this.accessibleLabel || this._ariaLabelAttr || 'Actions';\n }\n\n /** Cached list of focusable items — invalidated on slot change. */\n /** @internal */\n private _focusableCache: HTMLElement[] | null = null;\n\n /** Whether the overflow slot has assigned content. * @internal\n */\n @state()\n private _hasOverflow = false;\n\n // ─── Lifecycle ───\n\n /** Arrow function field — stable reference for add/removeEventListener. */\n /** @internal */\n private _handleKeydown = (e: KeyboardEvent): void => {\n if (e.key === 'ArrowRight') {\n e.preventDefault();\n this._moveFocus('next');\n } else if (e.key === 'ArrowLeft') {\n e.preventDefault();\n this._moveFocus('prev');\n } else if (e.key === 'Home') {\n e.preventDefault();\n // Move directly to first item — do NOT call _moveFocus which would visit other items first.\n const items = this._getFocusableItems();\n if (items.length) {\n items.forEach((el, i) => el.setAttribute('tabindex', i === 0 ? '0' : '-1'));\n items[0]?.focus();\n }\n } else if (e.key === 'End') {\n e.preventDefault();\n const items = this._getFocusableItems();\n const last = items.length - 1;\n if (items.length) {\n items.forEach((el, i) => el.setAttribute('tabindex', i === last ? '0' : '-1'));\n items[last]?.focus();\n }\n }\n };\n\n override connectedCallback(): void {\n super.connectedCallback();\n // Backward compat: accept legacy `size` attribute. When present and `hx-size`\n // is not set, map the value and emit a deprecation warning.\n const legacySize = this.getAttribute('size');\n if (legacySize !== null && !this.hasAttribute('hx-size')) {\n devWarn('hx-action-bar', 'The \"size\" attribute is deprecated. Use \"hx-size\" instead.');\n this.size = legacySize as ActionBarSize;\n }\n // Prevent dual aria-label announcement: the host carries the consumer's\n // aria-label attribute while the inner div[role=\"toolbar\"] receives the\n // same value. Setting role=\"none\" on the host hides it from the\n // accessibility tree so only the toolbar is announced.\n if (!this.hasAttribute('role')) {\n this.setAttribute('role', 'none');\n } else if (this.getAttribute('role') !== 'none') {\n devWarn(\n 'hx-action-bar',\n `Setting role=\"${this.getAttribute('role')}\" on the host creates a duplicate toolbar announcement. ` +\n 'The shadow DOM already contains role=\"toolbar\". Set role=\"none\" on the host to suppress it.',\n );\n }\n this.addEventListener('keydown', this._handleKeydown);\n }\n\n override firstUpdated(): void {\n // Slot assignments are complete by firstUpdated; initialize roving tabindex\n // immediately rather than waiting for the async slotchange event.\n this._initRovingTabindex();\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener('keydown', this._handleKeydown);\n }\n\n // ─── Focusable item discovery ───\n\n /** @internal */\n private _isFocusable(el: HTMLElement): boolean {\n // Check disabled via DOM attribute (native elements) or property (custom elements)\n if (el.hasAttribute('disabled')) return false;\n const elWithDisabled = el as HTMLElement & { disabled?: boolean };\n if (elWithDisabled.disabled === true) return false;\n\n // Use the IDL tabIndex property — covers both DOM attribute and ElementInternals settings.\n // Custom elements (e.g. hx-button) that set tabIndex via ElementInternals are discoverable.\n if (el.tabIndex >= 0) return true;\n\n const tag = el.tagName.toLowerCase();\n return tag === 'button' || tag === 'input' || tag === 'select' || tag === 'textarea';\n }\n\n /** @internal */\n private _getFocusableItems(): HTMLElement[] {\n if (this._focusableCache) return this._focusableCache;\n\n const slots = this.shadowRoot?.querySelectorAll('slot') ?? [];\n const items: HTMLElement[] = [];\n\n for (const slot of Array.from(slots)) {\n const assigned = (slot as HTMLSlotElement).assignedElements({ flatten: true });\n for (const el of assigned) {\n if (!(el instanceof HTMLElement)) continue;\n if (this._isFocusable(el)) {\n // Element is itself focusable — include it and do NOT also recurse into its children\n // to prevent double-counting compound components (e.g. <a><button>).\n items.push(el);\n } else {\n // Element is a non-focusable wrapper (e.g. <div>, <span>) — find focusable children.\n const descendants = el.querySelectorAll<HTMLElement>('*');\n for (const d of Array.from(descendants)) {\n if (this._isFocusable(d)) {\n items.push(d);\n }\n }\n }\n }\n }\n\n this._focusableCache = items;\n return items;\n }\n\n // ─── Roving tabindex helpers ───\n\n /** @internal */\n private _initRovingTabindex(): void {\n this._focusableCache = null; // invalidate cache on slot change\n const items = this._getFocusableItems();\n if (!items.length) return;\n // Find the currently active item. If none exists (e.g. first render or active item was\n // removed), fall back to index 0. Then set ALL items explicitly so newly added items and\n // items whose tabindex changed externally are always in a correct state.\n const activeIndex = items.findIndex((el) => el.getAttribute('tabindex') === '0');\n const targetIndex = activeIndex === -1 ? 0 : activeIndex;\n items.forEach((el, i) => el.setAttribute('tabindex', i === targetIndex ? '0' : '-1'));\n }\n\n /** @internal */\n private _moveFocus(direction: 'next' | 'prev'): void {\n const items = this._getFocusableItems();\n if (!items.length) return;\n\n const focused = document.activeElement as HTMLElement | null;\n const currentIndex = items.indexOf(focused as HTMLElement);\n\n let nextIndex: number;\n if (direction === 'next') {\n nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;\n } else {\n nextIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;\n }\n\n items.forEach((el, i) => {\n el.setAttribute('tabindex', i === nextIndex ? '0' : '-1');\n });\n\n items[nextIndex]?.focus();\n }\n\n // ─── Event Handlers ───\n\n /** @internal */\n private _handleSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n if (slot.name === 'overflow') {\n this._hasOverflow = slot.assignedElements({ flatten: true }).length > 0;\n }\n this._initRovingTabindex();\n }\n\n // ─── Render ───\n\n override render() {\n const isSticky = this.position === 'sticky';\n const isBottom = this.position === 'bottom';\n const positionClass = isSticky ? ' base--sticky' : isBottom ? ' base--bottom' : '';\n\n return html`\n <div\n part=\"base\"\n role=\"toolbar\"\n aria-label=${this._effectiveLabel}\n aria-orientation=\"horizontal\"\n class=\"base base--${this.size} base--${this.variant}${positionClass}\"\n >\n <div part=\"start\" class=\"section section--start\">\n <slot name=\"start\" @slotchange=${this._handleSlotChange}></slot>\n </div>\n <div part=\"center\" class=\"section section--center\">\n <slot @slotchange=${this._handleSlotChange}></slot>\n </div>\n <div part=\"end\" class=\"section section--end\">\n <slot name=\"end\" @slotchange=${this._handleSlotChange}></slot>\n </div>\n <div part=\"overflow\" class=\"section section--overflow\" ?hidden=${!this._hasOverflow}>\n <slot name=\"overflow\" @slotchange=${this._handleSlotChange}></slot>\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-action-bar': HelixActionBar;\n }\n}\n"],"names":["helixActionBarStyles","css","HelixActionBar","HelixElement","items","el","_a","last","i","_b","legacySize","devWarn","tag","slots","slot","assigned","descendants","activeIndex","targetIndex","direction","focused","currentIndex","nextIndex","isSticky","isBottom","positionClass","html","forcedColorsInteractive","__decorateClass","property","state","customElement"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hx-breadcrumb-item-3tKppF9h.js","sources":["../../src/components/hx-breadcrumb/hx-breadcrumb.styles.ts","../../src/components/hx-breadcrumb/hx-breadcrumb.ts","../../src/components/hx-breadcrumb/hx-breadcrumb-item.styles.ts","../../src/components/hx-breadcrumb/hx-breadcrumb-item.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixBreadcrumbStyles = css`\n :host {\n display: block;\n font-family: var(\n --hx-breadcrumb-font-family,\n var(--hx-font-family-sans, system-ui, sans-serif)\n );\n font-size: var(--hx-breadcrumb-font-size, var(--hx-font-size-sm, 0.875rem));\n }\n\n [part='nav'] {\n /* nav landmark — no additional styling needed */\n }\n\n [part='list'] {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n list-style: none;\n margin: 0;\n padding: 0;\n gap: 0;\n }\n\n /* Hide middle items when collapsed via maxItems */\n ::slotted([data-bc-hidden]) {\n display: none;\n }\n\n /*\n * Ellipsis ordering when collapsed (FS-014 fix):\n * The ellipsis <hx-breadcrumb-item> lives in shadow DOM to avoid mutating\n * the consumer's light DOM. CSS order places it between the first item\n * (order: 0, default) and the last item (order: 2) in the flex <ol>.\n */\n .hx-bc-ellipsis {\n order: 1;\n }\n\n ::slotted([data-bc-last]) {\n order: 2;\n }\n\n /* Visually hide the separator slot — used only to read text content.\n * display:none is intentional: the slot contains no interactive or focusable\n * content. If a future change adds focusable elements to this slot, switch to\n * visibility:hidden + position:absolute to preserve focus reachability. */\n .separator-slot {\n display: none;\n }\n\n /* ─── High Contrast Mode (forced-colors) ─── */\n\n /*\n * hx-breadcrumb is a nav container. Link colors and separators are handled\n * by hx-breadcrumb-item's own forced-colors block. No additional overrides needed\n * at the container level.\n */\n @media (forced-colors: active) {\n :host {\n forced-color-adjust: auto;\n }\n }\n`;\n","import { html, nothing, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { HelixElement, createIdCounter } from '../../base/index.js';\nimport { forcedColorsInteractive } from '../../styles/forced-colors.js';\nimport { helixBreadcrumbStyles } from './hx-breadcrumb.styles.js';\n\n/** Typed schema.org ListItem entry for JSON-LD BreadcrumbList structured data. */\ninterface JsonLdListItem {\n '@type': string;\n position: number;\n name: string;\n item?: string;\n}\n\nconst _nextBreadcrumbId = createIdCounter('hx-breadcrumb');\n\n/**\n * Hierarchical page path navigation showing current location in site structure.\n *\n * @summary Navigation breadcrumb showing the page hierarchy. Works with Drupal's breadcrumb system.\n *\n * @tag hx-breadcrumb\n *\n * @slot - Default slot for hx-breadcrumb-item children.\n * @slot separator - Optional separator element. Its text content overrides the `separator` property.\n *\n * @csspart nav - The nav landmark element.\n * @csspart list - The ordered list containing items.\n *\n * @cssprop [--hx-breadcrumb-separator-content='/'] - Separator character between items.\n * NOTE: If overriding this custom property directly in CSS (rather than via the `separator`\n * attribute), the value MUST be quoted: `--hx-breadcrumb-separator-content: \">\"`. An unquoted\n * value is invalid for the CSS `content` property and will silently render nothing.\n * @cssprop [--hx-breadcrumb-separator-color=var(--hx-color-neutral-400)] - Separator color.\n * @cssprop [--hx-breadcrumb-separator-gap=var(--hx-space-1)] - Horizontal gap around separators.\n * @cssprop [--hx-breadcrumb-font-size=var(--hx-font-size-sm)] - Font size.\n * @cssprop [--hx-breadcrumb-link-color=var(--hx-color-primary-600)] - Link color.\n * @cssprop [--hx-breadcrumb-link-hover-color=var(--hx-color-primary-700)] - Link hover color.\n * @cssprop [--hx-breadcrumb-text-color=var(--hx-color-neutral-700)] - Current page text color.\n * @cssprop [--hx-breadcrumb-item-max-width] - Max-width for item text truncation (e.g. `12rem`).\n * @cssprop [--hx-breadcrumb-font-family] - CSS custom property.\n * @cssprop [--hx-font-family-sans] - Font family.\n * @cssprop [--hx-font-size-sm] - Font size.\n */\n@customElement('hx-breadcrumb')\nexport class HelixBreadcrumb extends HelixElement {\n static override styles = [helixBreadcrumbStyles, forcedColorsInteractive];\n\n /**\n * The separator character displayed between breadcrumb items.\n * @attr separator\n */\n @property({ type: String })\n separator = '/';\n\n /**\n * The accessible label for the nav landmark.\n * @attr label\n */\n @property({ type: String })\n label = 'Breadcrumb';\n\n /**\n * Maximum number of items to show before collapsing middle items with an ellipsis.\n * Set to 0 (default) to show all items. The ellipsis is a keyboard-accessible\n * button; activating it expands the full breadcrumb by setting maxItems to 0.\n * @attr max-items\n */\n @property({ type: Number, attribute: 'max-items' })\n maxItems = 0;\n\n /**\n * Accessible label for the expand ellipsis button. Override for i18n.\n * @attr label-ellipsis\n */\n @property({ attribute: 'label-ellipsis' }) labelEllipsis = 'Show all breadcrumb items';\n\n /**\n * When true, injects a JSON-LD BreadcrumbList structured data script into the document head.\n *\n * NOTE: Drupal manages `<head>` content via its own render pipeline. Injecting a\n * `<script>` directly via `document.head.appendChild()` in a Drupal context:\n * 1. Bypasses Drupal's deduplication and `hook_html_head_alter()` hook.\n * 2. Is not cacheable by Drupal's page cache.\n * 3. Will be wiped on BigPipe partial page replacements.\n *\n * For Drupal integrations, leave `json-ld` false and use the structured data\n * Twig template instead (see `hx-breadcrumb.twig` in the component directory).\n *\n * @attr json-ld\n */\n @property({ type: Boolean, attribute: 'json-ld' })\n jsonLd = false;\n\n /**\n * Whether the ellipsis expand button is currently shown (shadow DOM rendered).\n * Driven by maxItems collapse logic; replaces the old light-DOM ellipsis injection.\n * @internal\n */\n @state() private _showEllipsis = false;\n /** @internal */\n private _jsonLdScript: HTMLScriptElement | null = null;\n\n /**\n * Tracks which items had their `current` attribute set by this component\n * (as opposed to set by a consumer/Drupal template). This lets us re-evaluate\n * positional current-page detection on each slotchange without incorrectly\n * treating a previously component-set `current` attribute as a consumer-set\n * explicit override.\n * @internal\n */\n private readonly _managedCurrentItems = new WeakSet<Element>();\n\n /**\n * Stable per-instance ID used to tag the injected script element so that\n * multiple hx-breadcrumb instances on the same page don't produce conflicting\n * or duplicate structured-data blocks. Each instance owns exactly one script\n * tag identified by this ID; any stale tag from a previous render cycle is\n * removed before a new one is inserted.\n *\n * Uses a static counter (not Math.random()) so IDs are deterministic across\n * server and client renders, enabling SSR hydration matching.\n * @internal\n */\n private readonly _jsonLdId = `${_nextBreadcrumbId()}-ld`;\n\n // ─── Item Helpers ───\n\n /**\n * Returns only real breadcrumb items, excluding the managed ellipsis element.\n * @internal\n */\n private _getBreadcrumbItems(slot: HTMLSlotElement): Element[] {\n return slot\n .assignedElements({ flatten: true })\n .filter(\n (el) =>\n el.tagName.toLowerCase() === 'hx-breadcrumb-item' &&\n !el.classList.contains('hx-bc-ellipsis'),\n );\n }\n\n /**\n * Applies aria/state attributes to the item list.\n *\n * Current-page detection: if any item has an explicit `current` attribute\n * (e.g. set by a Drupal Twig template), that item is treated as the current\n * page. Otherwise the last item is the current page (default behaviour).\n *\n * This separation allows Drupal to control current-page marking without\n * relying on item order.\n * @internal\n */\n private _applyItemAttributes(items: Element[]): void {\n // Detect consumer-set 'current' attributes. An item has an explicit consumer\n // current if it has the 'current' attribute AND the component did not set it\n // (tracked via _managedCurrentItems). This prevents component-managed state\n // from being misread as a consumer override on subsequent slotchange events.\n const hasExplicitCurrent = items.some(\n (el) => el.hasAttribute('current') && !this._managedCurrentItems.has(el),\n );\n\n items.forEach((item, i) => {\n const el = item as HTMLElement;\n const isLast = i === items.length - 1;\n\n // Separator hiding: always positional — last item has no trailing separator.\n if (isLast) {\n el.setAttribute('data-bc-last', '');\n } else {\n el.removeAttribute('data-bc-last');\n }\n\n // Current-page marker: explicit consumer attribute wins over positional last.\n // The item component renders aria-current=\"page\" on its inner element\n // based on this attribute (see hx-breadcrumb-item.ts).\n if (!hasExplicitCurrent) {\n if (isLast) {\n el.setAttribute('current', '');\n this._managedCurrentItems.add(el);\n } else {\n el.removeAttribute('current');\n this._managedCurrentItems.delete(el);\n }\n }\n // When hasExplicitCurrent is true, leave 'current' attributes as-is so\n // consumer or Drupal template markup is not overridden.\n });\n }\n\n // ─── Slot Handling ───\n\n /** @internal */\n private _handleSlotChange(e: Event): void {\n if (!(e.target instanceof HTMLSlotElement)) return;\n const items = this._getBreadcrumbItems(e.target);\n\n // Handle collapse behaviour\n if (this.maxItems > 0 && items.length > this.maxItems) {\n this._applyCollapse(items);\n } else {\n this._removeCollapse(items);\n }\n\n this._applyItemAttributes(items);\n\n if (this.jsonLd) {\n this._updateJsonLd(items);\n }\n }\n\n /** @internal */\n private _handleSeparatorSlotChange(e: Event): void {\n if (!(e.target instanceof HTMLSlotElement)) return;\n const assigned = e.target.assignedElements({ flatten: true });\n if (assigned.length > 0) {\n const text = (assigned[0] as HTMLElement).textContent?.trim() ?? '';\n this.style.setProperty('--hx-breadcrumb-separator-content', JSON.stringify(text));\n }\n }\n\n // ─── Collapse ───\n\n /** @internal */\n private _applyCollapse(items: Element[]): void {\n // Show only first and last; hide all middle items via data attribute.\n // The ellipsis is rendered in shadow DOM (see render()) to avoid mutating\n // the consumer's light DOM structure (FS-014).\n items.forEach((item, i) => {\n const el = item as HTMLElement;\n if (i === 0 || i === items.length - 1) {\n el.removeAttribute('data-bc-hidden');\n } else {\n el.setAttribute('data-bc-hidden', '');\n }\n });\n\n this._showEllipsis = true;\n }\n\n /** @internal */\n private _removeCollapse(items: Element[]): void {\n items.forEach((item) => {\n (item as HTMLElement).removeAttribute('data-bc-hidden');\n });\n\n this._showEllipsis = false;\n }\n\n /**\n * Expands a collapsed breadcrumb by resetting maxItems to 0.\n * Called by the ellipsis expand button (click or Enter/Space).\n * @internal\n */\n private _expandBreadcrumb(): void {\n this.maxItems = 0;\n // updated() will detect the maxItems change and call _removeCollapse.\n }\n\n // ─── JSON-LD ───\n\n /**\n * JSON-LD ListItem entry with typed fields to avoid Record<string, unknown>.\n * @internal\n */\n private _buildListItem(item: Element, position: number): JsonLdListItem {\n const href = (item as HTMLElement).getAttribute('href');\n const name = (item as HTMLElement).textContent?.trim() ?? '';\n const entry: JsonLdListItem = {\n '@type': 'ListItem',\n position,\n name,\n };\n if (href) entry.item = href;\n return entry;\n }\n\n /** @internal */\n private _updateJsonLd(items: Element[]): void {\n const schema = {\n '@context': 'https://schema.org',\n '@type': 'BreadcrumbList',\n itemListElement: items.map((item, i) => this._buildListItem(item, i + 1)),\n };\n\n // Guard for SSR — document is unavailable server-side\n if (typeof document === 'undefined') return;\n\n if (!this._jsonLdScript) {\n // Dedup guard: remove any stale script with this instance's ID before\n // creating a fresh one. This handles the edge case where the element was\n // reconnected to the DOM after being disconnected without the script\n // reference being re-established.\n document.getElementById(this._jsonLdId)?.remove();\n\n this._jsonLdScript = document.createElement('script');\n this._jsonLdScript.type = 'application/ld+json';\n this._jsonLdScript.id = this._jsonLdId;\n this._jsonLdScript.setAttribute('data-hx-breadcrumb', '');\n document.head.appendChild(this._jsonLdScript);\n }\n\n this._jsonLdScript.textContent = JSON.stringify(schema);\n }\n\n /** @internal */\n private _removeJsonLd(): void {\n this._jsonLdScript?.remove();\n this._jsonLdScript = null;\n }\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n // Do NOT set role=\"list\" on the host. The shadow-DOM <nav><ol> provides\n // both navigation landmark and list semantics. Setting role=\"list\" on\n // the host conflicts with the <nav> child — axe-core flags\n // aria-required-children because a list cannot own a navigation landmark.\n // Slotted items no longer need role=\"listitem\" either; the native <ol>\n // handles list semantics in the composed accessibility tree.\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this._removeJsonLd();\n }\n\n override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n\n if (changedProperties.has('separator')) {\n // JSON.stringify wraps the string in quotes so the value is valid\n // for use in the CSS `content` property (e.g. '/' becomes '\"/\"').\n this.style.setProperty('--hx-breadcrumb-separator-content', JSON.stringify(this.separator));\n }\n\n if (changedProperties.has('maxItems')) {\n // Re-evaluate collapse state when maxItems changes programmatically\n // (e.g. when the expand button resets maxItems to 0).\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n if (slot) {\n const items = this._getBreadcrumbItems(slot);\n if (this.maxItems > 0 && items.length > this.maxItems) {\n this._applyCollapse(items);\n } else {\n this._removeCollapse(items);\n }\n this._applyItemAttributes(items);\n }\n }\n\n if (changedProperties.has('jsonLd')) {\n if (this.jsonLd) {\n // json-ld toggled on after initial render — inject script immediately.\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n if (slot) {\n this._updateJsonLd(this._getBreadcrumbItems(slot));\n }\n } else {\n // json-ld toggled off — remove existing script.\n this._removeJsonLd();\n }\n }\n }\n\n // ─── Render ───\n\n override render() {\n return html`\n <nav part=\"nav\" aria-label=${this.label}>\n <ol part=\"list\">\n <slot @slotchange=${this._handleSlotChange}></slot>\n ${this._showEllipsis\n ? html`\n <hx-breadcrumb-item class=\"hx-bc-ellipsis\">\n <button\n type=\"button\"\n aria-label=${this.labelEllipsis}\n @click=${this._expandBreadcrumb}\n >\n …\n </button>\n </hx-breadcrumb-item>\n `\n : nothing}\n </ol>\n </nav>\n <slot\n name=\"separator\"\n class=\"separator-slot\"\n @slotchange=${this._handleSeparatorSlotChange}\n ></slot>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-breadcrumb': HelixBreadcrumb;\n }\n}\n","import { css } from 'lit';\n\nexport const helixBreadcrumbItemStyles = css`\n :host {\n display: inline-flex;\n align-items: center;\n }\n\n /*\n * display: contents removes [part='item'] from the box model entirely.\n * This is intentional — the wrapper exists only for slot selection purposes.\n * Consumers using ::part(item) CANNOT apply box-model properties (padding,\n * margin, background, border) to this part. Use ::part(link) or ::part(text)\n * for visual styling of breadcrumb item content.\n */\n [part='item'] {\n display: contents;\n }\n\n [part='link'] {\n color: var(--hx-breadcrumb-link-color, var(--hx-color-primary-600, #0f7078));\n text-decoration: none;\n cursor: pointer;\n font-family: inherit;\n font-size: inherit;\n max-width: var(--hx-breadcrumb-item-max-width);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n [part='link']:hover {\n color: var(--hx-breadcrumb-link-hover-color, var(--hx-color-primary-700, #0f6363));\n text-decoration: underline;\n }\n\n [part='link']:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-breadcrumb-link-focus-ring-color, var(--hx-focus-ring-color, #0f7078));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n }\n\n [part='text'] {\n color: var(--hx-breadcrumb-text-color, var(--hx-color-text-strong, #202b39));\n font-family: inherit;\n font-size: inherit;\n max-width: var(--hx-breadcrumb-item-max-width);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .separator {\n margin-inline: var(--hx-breadcrumb-separator-gap, var(--hx-space-1, 0.25rem));\n color: var(--hx-breadcrumb-separator-color, var(--hx-color-text-muted, #4a5362));\n user-select: none;\n }\n\n .separator::before {\n content: var(--hx-breadcrumb-separator-content, '/');\n }\n\n /* Normalize buttons slotted into breadcrumb items (e.g. the expand-ellipsis button). */\n ::slotted(button) {\n background: none;\n border: none;\n cursor: pointer;\n font: inherit;\n color: inherit;\n padding: 0;\n }\n\n /* ─── Forced Colors (Windows High Contrast) ─── */\n\n @media (forced-colors: active) {\n [part='link'] {\n color: LinkText;\n }\n\n [part='link']:hover {\n color: LinkText;\n }\n\n .separator {\n color: CanvasText;\n }\n }\n`;\n","import { html, nothing } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property } from 'lit/decorators.js';\nimport { HelixElement } from '../../base/index.js';\nimport { forcedColorsInteractive } from '../../styles/forced-colors.js';\nimport { helixBreadcrumbItemStyles } from './hx-breadcrumb-item.styles.js';\n\n/**\n * A single breadcrumb navigation item.\n *\n * @summary A navigation item within an hx-breadcrumb component. Renders as a link when `href` is\n * provided, or as static text for the current page item. The current page item is determined by\n * the `current` attribute (set explicitly or automatically by the parent `hx-breadcrumb`).\n *\n * @tag hx-breadcrumb-item\n *\n * @slot - The link or page text content. Accepts text, HTML, or icon elements.\n *\n * @csspart item - Wrapper around the link or text content.\n * @csspart link - The anchor element when href is provided (non-current items only).\n * @csspart text - The span element for the current page or items without href.\n * @csspart separator - The separator element rendered after non-last items.\n *\n * @cssprop [--hx-breadcrumb-link-color=var(--hx-color-primary-600)] - Link text color.\n * @cssprop [--hx-breadcrumb-link-hover-color=var(--hx-color-primary-700)] - Link hover text color.\n * @cssprop [--hx-breadcrumb-text-color=var(--hx-color-neutral-700)] - Current page text color.\n * @cssprop [--hx-breadcrumb-separator-content='/'] - Separator character displayed after non-last items.\n * @cssprop [--hx-breadcrumb-separator-color=var(--hx-color-neutral-400)] - Separator color.\n * @cssprop [--hx-breadcrumb-separator-gap=var(--hx-space-1)] - Horizontal margin around separator.\n * @cssprop [--hx-breadcrumb-item-max-width] - Optional max-width for text truncation.\n * @cssprop [--hx-breadcrumb-link-focus-ring-color=var(--hx-focus-ring-color, var(--hx-color-primary-500))] - Focus ring color for breadcrumb links.\n */\n@customElement('hx-breadcrumb-item')\nexport class HelixBreadcrumbItem extends HelixElement {\n static override styles = [helixBreadcrumbItemStyles, forcedColorsInteractive];\n\n override connectedCallback(): void {\n super.connectedCallback();\n // No explicit role needed. The shadow-DOM <ol> inside hx-breadcrumb\n // provides native list semantics; slotted items are projected into it.\n // Previously role=\"listitem\" was set here, but it conflicted with the\n // host's role=\"list\" + shadow-DOM <nav> structure (aria-required-children).\n // The native <ol> handles the accessibility tree correctly without\n // explicit ARIA roles on host or items.\n }\n\n /**\n * The URL for this breadcrumb link. Omit for the current page item.\n * When `current` is true, this attribute is ignored and the item always\n * renders as static text per WAI-ARIA APG breadcrumb guidance.\n * @attr href\n */\n @property({ type: String, reflect: true })\n href: string | undefined = undefined;\n\n /**\n * Whether this is the last item in the breadcrumb trail. Set by the parent\n * hx-breadcrumb component via the `data-bc-last` boolean attribute. When\n * present the trailing separator is hidden.\n *\n * @attr data-bc-last\n * @internal\n */\n @property({ type: Boolean, attribute: 'data-bc-last', reflect: true })\n dataBcLast = false;\n\n /**\n * Marks this item as the current page. When set, the item always renders as\n * static text (never a navigable link) and `aria-current=\"page\"` is placed on\n * the inner text element per WAI-ARIA APG breadcrumb guidance, yielding the\n * canonical AT announcement (\"current page, Patient Records\").\n *\n * Can be set explicitly by consumers (e.g. Drupal Twig templates) to override\n * the default positional last-item detection in `hx-breadcrumb`. When any item\n * in the breadcrumb has an explicit `current` attribute, the parent will not\n * override it.\n *\n * @attr current\n */\n @property({ type: Boolean, reflect: true })\n current = false;\n\n override render() {\n // Per WAI-ARIA APG, the current page item MUST NOT be a navigable link.\n // aria-current=\"page\" is placed on the inner element (not the listitem host)\n // for canonical AT announcement (\"current page, Patient Records\" vs\n // \"current page, list item\").\n return html`\n <span part=\"item\">\n ${this.current\n ? html`<span part=\"text\" aria-current=\"page\"><slot></slot></span>`\n : this.href\n ? html`<a part=\"link\" href=${this.href}><slot></slot></a>`\n : html`<span part=\"text\"><slot></slot></span>`}\n </span>\n ${!this.dataBcLast\n ? html`<span class=\"separator\" part=\"separator\" aria-hidden=\"true\"></span>`\n : nothing}\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-breadcrumb-item': HelixBreadcrumbItem;\n }\n}\n"],"names":["helixBreadcrumbStyles","css","_nextBreadcrumbId","createIdCounter","HelixBreadcrumb","HelixElement","slot","el","items","hasExplicitCurrent","item","i","isLast","e","assigned","text","_a","position","href","name","entry","schema","changedProperties","_b","html","nothing","forcedColorsInteractive","__decorateClass","property","state","customElement","helixBreadcrumbItemStyles","HelixBreadcrumbItem"],"mappings":"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"}
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import { css as u, html as h } from "lit";
|
|
2
|
-
import { property as d, customElement as c } from "lit/decorators.js";
|
|
3
|
-
import { classMap as b } from "lit/directives/class-map.js";
|
|
4
|
-
import { f as p } from "./forced-colors-CTEDFRGa.js";
|
|
5
|
-
import { H as m } from "./helix-element-BNEYeiys.js";
|
|
6
|
-
const g = u`
|
|
7
|
-
:host {
|
|
8
|
-
display: inline-flex;
|
|
9
|
-
contain: layout style;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
.group {
|
|
13
|
-
display: inline-flex;
|
|
14
|
-
align-items: stretch;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/* ─── Orientation Variants ─── */
|
|
18
|
-
|
|
19
|
-
.group--horizontal {
|
|
20
|
-
flex-direction: row;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
.group--vertical {
|
|
24
|
-
flex-direction: column;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/* ─── No Double Borders: Horizontal ─── */
|
|
28
|
-
|
|
29
|
-
.group--horizontal ::slotted(*:not(:first-child)) {
|
|
30
|
-
margin-inline-start: var(
|
|
31
|
-
--hx-button-group-divider-offset,
|
|
32
|
-
calc(-1 * var(--hx-border-width-thin, 1px))
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/* ─── No Double Borders: Vertical ─── */
|
|
37
|
-
|
|
38
|
-
.group--vertical ::slotted(*:not(:first-child)) {
|
|
39
|
-
margin-top: var(--hx-button-group-divider-offset, calc(-1 * var(--hx-border-width-thin, 1px)));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/* ─── Border Radius: Horizontal — Single child keeps all corners ─── */
|
|
43
|
-
|
|
44
|
-
.group--horizontal ::slotted(:only-child) {
|
|
45
|
-
--hx-button-border-radius: var(
|
|
46
|
-
--hx-button-group-border-radius,
|
|
47
|
-
var(--hx-border-radius-md, 0.375rem)
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/* ─── Border Radius: Horizontal — First child keeps left corners ─── */
|
|
52
|
-
|
|
53
|
-
.group--horizontal ::slotted(:first-child:not(:only-child)) {
|
|
54
|
-
--hx-button-border-radius: var(
|
|
55
|
-
--hx-button-group-border-radius,
|
|
56
|
-
var(--hx-border-radius-md, 0.375rem)
|
|
57
|
-
)
|
|
58
|
-
0 0 var(--hx-button-group-border-radius, var(--hx-border-radius-md, 0.375rem));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/* ─── Border Radius: Horizontal — Last child keeps right corners ─── */
|
|
62
|
-
|
|
63
|
-
.group--horizontal ::slotted(:last-child:not(:only-child)) {
|
|
64
|
-
--hx-button-border-radius: 0
|
|
65
|
-
var(--hx-button-group-border-radius, var(--hx-border-radius-md, 0.375rem))
|
|
66
|
-
var(--hx-button-group-border-radius, var(--hx-border-radius-md, 0.375rem)) 0;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/* ─── Border Radius: Horizontal — Middle children have no radius ─── */
|
|
70
|
-
|
|
71
|
-
.group--horizontal ::slotted(:not(:first-child):not(:last-child)) {
|
|
72
|
-
--hx-button-border-radius: 0;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/* ─── Border Radius: Vertical — Single child keeps all corners ─── */
|
|
76
|
-
|
|
77
|
-
.group--vertical ::slotted(:only-child) {
|
|
78
|
-
--hx-button-border-radius: var(
|
|
79
|
-
--hx-button-group-border-radius,
|
|
80
|
-
var(--hx-border-radius-md, 0.375rem)
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/* ─── Border Radius: Vertical — First child keeps top corners ─── */
|
|
85
|
-
|
|
86
|
-
.group--vertical ::slotted(:first-child:not(:only-child)) {
|
|
87
|
-
--hx-button-border-radius: var(
|
|
88
|
-
--hx-button-group-border-radius,
|
|
89
|
-
var(--hx-border-radius-md, 0.375rem)
|
|
90
|
-
)
|
|
91
|
-
var(--hx-button-group-border-radius, var(--hx-border-radius-md, 0.375rem)) 0 0;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/* ─── Border Radius: Vertical — Last child keeps bottom corners ─── */
|
|
95
|
-
|
|
96
|
-
.group--vertical ::slotted(:last-child:not(:only-child)) {
|
|
97
|
-
--hx-button-border-radius: 0 0
|
|
98
|
-
var(--hx-button-group-border-radius, var(--hx-border-radius-md, 0.375rem))
|
|
99
|
-
var(--hx-button-group-border-radius, var(--hx-border-radius-md, 0.375rem));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/* ─── Border Radius: Vertical — Middle children have no radius ─── */
|
|
103
|
-
|
|
104
|
-
.group--vertical ::slotted(:not(:first-child):not(:last-child)) {
|
|
105
|
-
--hx-button-border-radius: 0;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/* ─── Z-index: Raise focused child above siblings to show full focus ring ─── */
|
|
109
|
-
|
|
110
|
-
.group ::slotted(:focus-within) {
|
|
111
|
-
z-index: var(--hx-button-group-focus-z-index, 1);
|
|
112
|
-
position: relative;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/* ─── High Contrast Mode (forced-colors) ─── */
|
|
116
|
-
|
|
117
|
-
@media (forced-colors: active) {
|
|
118
|
-
/*
|
|
119
|
-
* In forced-colors mode, negative margins that collapse borders between grouped
|
|
120
|
-
* buttons can obscure focus rings. Raise focused children so the Highlight
|
|
121
|
-
* outline from hx-button's own forced-colors block is fully visible.
|
|
122
|
-
*/
|
|
123
|
-
.group ::slotted(:focus-within) {
|
|
124
|
-
z-index: var(--hx-button-group-focus-z-index-hc, 2);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
`;
|
|
128
|
-
var f = Object.defineProperty, x = Object.getOwnPropertyDescriptor, s = (r, o, l, i) => {
|
|
129
|
-
for (var t = i > 1 ? void 0 : i ? x(o, l) : o, a = r.length - 1, n; a >= 0; a--)
|
|
130
|
-
(n = r[a]) && (t = (i ? n(o, l, t) : n(t)) || t);
|
|
131
|
-
return i && t && f(o, l, t), t;
|
|
132
|
-
};
|
|
133
|
-
let e = class extends m {
|
|
134
|
-
constructor() {
|
|
135
|
-
super(...arguments), this._orientation = "horizontal", this.size = "md", this.label = "", this._consumerAriaLabel = null, this._consumerRole = null, this._emptyLabelWarnEmitted = !1;
|
|
136
|
-
}
|
|
137
|
-
get orientation() {
|
|
138
|
-
return this._orientation;
|
|
139
|
-
}
|
|
140
|
-
set orientation(r) {
|
|
141
|
-
r !== "horizontal" && r !== "vertical" && (r = "horizontal"), this._orientation = r;
|
|
142
|
-
}
|
|
143
|
-
updated(r) {
|
|
144
|
-
super.updated(r), r.has("size") && this.style.setProperty("--hx-button-group-size", this.size), r.has("label") && (this.label ? this.setAttribute("aria-label", this.label) : this._consumerAriaLabel !== null ? this.setAttribute("aria-label", this._consumerAriaLabel) : this.removeAttribute("aria-label"));
|
|
145
|
-
}
|
|
146
|
-
connectedCallback() {
|
|
147
|
-
super.connectedCallback(), this._consumerAriaLabel === null && this.hasAttribute("aria-label") && (this._consumerAriaLabel = this.getAttribute("aria-label")), this._consumerRole === null && this.hasAttribute("role") && (this._consumerRole = this.getAttribute("role")), this._consumerRole ? this._internals.role = this._consumerRole : (this._internals.role = "group", this.setAttribute("role", "group")), this.style.setProperty("--hx-button-group-size", this.size), this.label ? this.setAttribute("aria-label", this.label) : this._consumerAriaLabel !== null || this._emptyLabelWarnEmitted || (this._emptyLabelWarnEmitted = !0);
|
|
148
|
-
}
|
|
149
|
-
// ─── Render ───
|
|
150
|
-
render() {
|
|
151
|
-
return h`
|
|
152
|
-
<div
|
|
153
|
-
part="group"
|
|
154
|
-
class=${b({
|
|
155
|
-
group: !0,
|
|
156
|
-
"group--horizontal": this.orientation === "horizontal",
|
|
157
|
-
"group--vertical": this.orientation === "vertical"
|
|
158
|
-
})}
|
|
159
|
-
>
|
|
160
|
-
<slot></slot>
|
|
161
|
-
</div>
|
|
162
|
-
`;
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
e.styles = [g, p];
|
|
166
|
-
s([
|
|
167
|
-
d({ type: String, reflect: !0 })
|
|
168
|
-
], e.prototype, "orientation", 1);
|
|
169
|
-
s([
|
|
170
|
-
d({ type: String, reflect: !0, attribute: "hx-size" })
|
|
171
|
-
], e.prototype, "size", 2);
|
|
172
|
-
s([
|
|
173
|
-
d({ type: String })
|
|
174
|
-
], e.prototype, "label", 2);
|
|
175
|
-
e = s([
|
|
176
|
-
c("hx-button-group")
|
|
177
|
-
], e);
|
|
178
|
-
export {
|
|
179
|
-
e as H
|
|
180
|
-
};
|
|
181
|
-
//# sourceMappingURL=hx-button-group-4NUBpkyC.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hx-button-group-4NUBpkyC.js","sources":["../../src/components/hx-button-group/hx-button-group.styles.ts","../../src/components/hx-button-group/hx-button-group.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixButtonGroupStyles = css`\n :host {\n display: inline-flex;\n contain: layout style;\n }\n\n .group {\n display: inline-flex;\n align-items: stretch;\n }\n\n /* ─── Orientation Variants ─── */\n\n .group--horizontal {\n flex-direction: row;\n }\n\n .group--vertical {\n flex-direction: column;\n }\n\n /* ─── No Double Borders: Horizontal ─── */\n\n .group--horizontal ::slotted(*:not(:first-child)) {\n margin-inline-start: var(\n --hx-button-group-divider-offset,\n calc(-1 * var(--hx-border-width-thin, 1px))\n );\n }\n\n /* ─── No Double Borders: Vertical ─── */\n\n .group--vertical ::slotted(*:not(:first-child)) {\n margin-top: var(--hx-button-group-divider-offset, calc(-1 * var(--hx-border-width-thin, 1px)));\n }\n\n /* ─── Border Radius: Horizontal — Single child keeps all corners ─── */\n\n .group--horizontal ::slotted(:only-child) {\n --hx-button-border-radius: var(\n --hx-button-group-border-radius,\n var(--hx-border-radius-md, 0.375rem)\n );\n }\n\n /* ─── Border Radius: Horizontal — First child keeps left corners ─── */\n\n .group--horizontal ::slotted(:first-child:not(:only-child)) {\n --hx-button-border-radius: var(\n --hx-button-group-border-radius,\n var(--hx-border-radius-md, 0.375rem)\n )\n 0 0 var(--hx-button-group-border-radius, var(--hx-border-radius-md, 0.375rem));\n }\n\n /* ─── Border Radius: Horizontal — Last child keeps right corners ─── */\n\n .group--horizontal ::slotted(:last-child:not(:only-child)) {\n --hx-button-border-radius: 0\n var(--hx-button-group-border-radius, var(--hx-border-radius-md, 0.375rem))\n var(--hx-button-group-border-radius, var(--hx-border-radius-md, 0.375rem)) 0;\n }\n\n /* ─── Border Radius: Horizontal — Middle children have no radius ─── */\n\n .group--horizontal ::slotted(:not(:first-child):not(:last-child)) {\n --hx-button-border-radius: 0;\n }\n\n /* ─── Border Radius: Vertical — Single child keeps all corners ─── */\n\n .group--vertical ::slotted(:only-child) {\n --hx-button-border-radius: var(\n --hx-button-group-border-radius,\n var(--hx-border-radius-md, 0.375rem)\n );\n }\n\n /* ─── Border Radius: Vertical — First child keeps top corners ─── */\n\n .group--vertical ::slotted(:first-child:not(:only-child)) {\n --hx-button-border-radius: var(\n --hx-button-group-border-radius,\n var(--hx-border-radius-md, 0.375rem)\n )\n var(--hx-button-group-border-radius, var(--hx-border-radius-md, 0.375rem)) 0 0;\n }\n\n /* ─── Border Radius: Vertical — Last child keeps bottom corners ─── */\n\n .group--vertical ::slotted(:last-child:not(:only-child)) {\n --hx-button-border-radius: 0 0\n var(--hx-button-group-border-radius, var(--hx-border-radius-md, 0.375rem))\n var(--hx-button-group-border-radius, var(--hx-border-radius-md, 0.375rem));\n }\n\n /* ─── Border Radius: Vertical — Middle children have no radius ─── */\n\n .group--vertical ::slotted(:not(:first-child):not(:last-child)) {\n --hx-button-border-radius: 0;\n }\n\n /* ─── Z-index: Raise focused child above siblings to show full focus ring ─── */\n\n .group ::slotted(:focus-within) {\n z-index: var(--hx-button-group-focus-z-index, 1);\n position: relative;\n }\n\n /* ─── High Contrast Mode (forced-colors) ─── */\n\n @media (forced-colors: active) {\n /*\n * In forced-colors mode, negative margins that collapse borders between grouped\n * buttons can obscure focus rings. Raise focused children so the Highlight\n * outline from hx-button's own forced-colors block is fully visible.\n */\n .group ::slotted(:focus-within) {\n z-index: var(--hx-button-group-focus-z-index-hc, 2);\n }\n }\n`;\n","import { html, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { HelixElement } from '../../base/index.js';\nimport { helixButtonGroupStyles } from './hx-button-group.styles.js';\nimport { forcedColorsInteractive } from '../../styles/forced-colors.js';\nimport { devWarn } from '../../utils/dev-warn.js';\n\n/**\n * A container component that groups related hx-button elements into a cohesive\n * horizontal or vertical action set. Eliminates double borders between adjacent\n * buttons and squares off inner border-radius for a unified visual appearance.\n *\n * **Accessibility:** Always provide an accessible label via `aria-label` or\n * `aria-labelledby` so screen readers can announce the group purpose.\n *\n * @summary Groups hx-button elements into a horizontal or vertical action set with shared borders.\n *\n * @tag hx-button-group\n *\n * @slot - Default slot accepting hx-button children.\n *\n * @csspart group - The container div element wrapping all slotted buttons.\n *\n * @cssprop [--hx-button-group-size=md] - Size token forwarded to child buttons. Accepts 'sm', 'md', or 'lg'.\n * @cssprop [--hx-border-width-thin] - Width.\n * @cssprop [--hx-border-radius-md] - CSS custom property.\n */\n@customElement('hx-button-group')\nexport class HelixButtonGroup extends HelixElement {\n static override styles = [helixButtonGroupStyles, forcedColorsInteractive];\n\n /**\n * Layout orientation of the button group.\n * @attr orientation\n */\n @property({ type: String, reflect: true })\n get orientation(): 'horizontal' | 'vertical' {\n return this._orientation;\n }\n set orientation(value: string) {\n if (value !== 'horizontal' && value !== 'vertical') {\n devWarn('hx-button-group', `Invalid orientation \"${value}\", defaulting to \"horizontal\".`);\n value = 'horizontal';\n }\n this._orientation = value as 'horizontal' | 'vertical';\n }\n /**\n * Backing store for the orientation property, holding the validated orientation value.\n * @internal\n */\n private _orientation: 'horizontal' | 'vertical' = 'horizontal';\n\n /**\n * Size applied to the button group and cascaded to child buttons via\n * the --hx-button-group-size CSS custom property.\n * @attr hx-size\n */\n @property({ type: String, reflect: true, attribute: 'hx-size' })\n size: 'sm' | 'md' | 'lg' = 'md';\n\n /**\n * Accessible label for the button group. Sets aria-label on the host element.\n * **Strongly recommended** for WCAG 2.1 AA compliance — without it, screen\n * readers announce an unnamed \"group\". For Drupal/Twig compatibility, prefer\n * applying `aria-label` directly as an HTML attribute instead.\n * @attr label\n */\n @property({ type: String })\n label: string = '';\n\n // ─── Lifecycle ───\n\n /**\n * Tracks whether the consumer set `aria-label` directly as an HTML attribute\n * BEFORE the `label` property fired. Used to avoid clobbering consumer-set\n * aria-label when `label` is empty.\n * @internal\n */\n private _consumerAriaLabel: string | null = null;\n\n /**\n * Snapshot of the consumer-set `role` attribute taken at connect time.\n * When non-null, the consumer has explicitly set a role (e.g. `toolbar`,\n * `radiogroup`) and the host-canonical mirror MUST defer to that role\n * rather than overwriting `internals.role` with the default `\"group\"`.\n * Mirrors the `_consumerAriaLabel` snapshot pattern above.\n * @internal\n */\n private _consumerRole: string | null = null;\n\n /**\n * Tracks whether the no-label devWarn has already fired for this instance,\n * so disconnect/reconnect cycles do not spam the console.\n * @internal\n */\n private _emptyLabelWarnEmitted = false;\n\n override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n\n if (changedProperties.has('size')) {\n this.style.setProperty('--hx-button-group-size', this.size);\n }\n\n if (changedProperties.has('label')) {\n if (this.label) {\n this.setAttribute('aria-label', this.label);\n } else if (this._consumerAriaLabel !== null) {\n // Restore consumer-set aria-label rather than removing it. The\n // consumer's HTML attribute is the documented Drupal/Twig path\n // (lines 67-68 narrative) and must not be clobbered by an empty\n // `label` property.\n this.setAttribute('aria-label', this._consumerAriaLabel);\n } else {\n this.removeAttribute('aria-label');\n }\n }\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n // Capture any consumer-set aria-label BEFORE the `label` property writes\n // overwrite it. This snapshot wins back the host attribute when `label`\n // is later cleared.\n if (this._consumerAriaLabel === null && this.hasAttribute('aria-label')) {\n this._consumerAriaLabel = this.getAttribute('aria-label');\n }\n // CodeRabbit SHOULD-FIX (PR #1649 follow-up): snapshot the consumer's\n // explicit `role` BEFORE the host-canonical mirror overwrites it. When\n // a consumer sets `role=\"toolbar\"` (or `radiogroup`, etc.), the mirror\n // must defer to that role; otherwise two surfaces disagree (host attr\n // says toolbar, internals says group) and AT picks one inconsistently.\n if (this._consumerRole === null && this.hasAttribute('role')) {\n this._consumerRole = this.getAttribute('role');\n }\n // Host-canonical role: use ElementInternals so the role survives in the\n // a11y tree even if a consumer attribute-strips the host. Mirror to the\n // host attribute as well for older AT/devtools that walk attributes.\n if (this._consumerRole) {\n // Defer to the consumer's role on both surfaces.\n this._internals.role = this._consumerRole;\n } else {\n this._internals.role = 'group';\n this.setAttribute('role', 'group');\n }\n this.style.setProperty('--hx-button-group-size', this.size);\n if (this.label) {\n this.setAttribute('aria-label', this.label);\n } else if (this._consumerAriaLabel !== null) {\n // Consumer-set aria-label is fine — no warning needed.\n } else if (!this._emptyLabelWarnEmitted) {\n this._emptyLabelWarnEmitted = true;\n devWarn(\n 'hx-button-group',\n 'Missing accessible label. Provide a `label` attribute so screen readers can announce the group purpose (WCAG 4.1.2).',\n );\n }\n }\n\n // ─── Render ───\n\n override render() {\n return html`\n <div\n part=\"group\"\n class=${classMap({\n group: true,\n 'group--horizontal': this.orientation === 'horizontal',\n 'group--vertical': this.orientation === 'vertical',\n })}\n >\n <slot></slot>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-button-group': HelixButtonGroup;\n }\n}\n"],"names":["helixButtonGroupStyles","css","HelixButtonGroup","HelixElement","value","changedProperties","html","classMap","forcedColorsInteractive","__decorateClass","property","customElement"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hx-button-rRNmD4fd.js","sources":["../../src/components/hx-button/hx-button.styles.ts","../../src/components/hx-button/hx-button.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixButtonStyles = css`\n :host {\n display: inline-block;\n }\n\n :host([disabled]) {\n pointer-events: none;\n opacity: var(--hx-opacity-disabled, 0.5);\n }\n\n :host([full]) {\n display: block;\n width: 100%;\n }\n\n :host([full]) .button {\n width: 100%;\n justify-content: center;\n }\n\n /* ─── Base Button ─── */\n\n .button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--hx-space-2, 0.5rem);\n border: var(--hx-border-width-thin, 1px) solid var(--hx-button-border-color, transparent);\n border-radius: var(--hx-button-border-radius, var(--hx-border-radius-md, 0.375rem));\n background-color: var(--hx-button-bg, var(--hx-color-primary-500, #429797));\n color: var(--hx-button-color, var(--hx-color-neutral-0, #ffffff));\n font-family: var(--hx-button-font-family, var(--hx-font-family-sans, sans-serif));\n font-weight: var(--hx-button-font-weight, var(--hx-font-weight-semibold, 600));\n line-height: var(--hx-line-height-tight, 1.25);\n cursor: pointer;\n transition:\n background-color var(--hx-transition-fast, 150ms ease),\n color var(--hx-transition-fast, 150ms ease),\n border-color var(--hx-transition-fast, 150ms ease),\n box-shadow var(--hx-transition-fast, 150ms ease);\n text-decoration: none;\n white-space: nowrap;\n user-select: none;\n -webkit-user-select: none;\n }\n\n .button:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-button-focus-ring-color, var(--hx-focus-ring-color, #0f7078));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n .button:hover {\n filter: brightness(var(--hx-filter-brightness-hover, 0.9));\n }\n\n .button:active {\n filter: brightness(var(--hx-filter-brightness-active, 0.8));\n }\n\n /* ─── Size Variants ─── */\n\n /* WCAG 2.5.5 (healthcare mandate): minimum 44px touch target for sm variant.\n min-height uses --hx-touch-target-min to guarantee the interactive area\n meets the threshold even though the visual size token is smaller. */\n .button--sm {\n padding: var(--hx-space-1, 0.25rem) var(--hx-space-3, 0.75rem);\n font-size: var(--hx-font-size-sm, 0.875rem);\n min-height: var(--hx-touch-target-min, 2.75rem);\n }\n\n .button--md {\n padding: var(--hx-space-2, 0.5rem) var(--hx-space-4, 1rem);\n font-size: var(--hx-font-size-md, 1rem);\n min-height: var(--hx-size-10, 2.5rem);\n }\n\n .button--lg {\n padding: var(--hx-space-3, 0.75rem) var(--hx-space-6, 1.5rem);\n font-size: var(--hx-font-size-lg, 1.125rem);\n min-height: var(--hx-size-12, 3rem);\n }\n\n /* ─── Style Variants ─── */\n\n .button--primary {\n --hx-button-bg: var(--hx-color-action-primary-bg, #429797);\n /* Inline #0d1825 matches text.on-primary's resolved primitive (neutral-900);\n cold-start without the semantic still paints AA-tuned dark-on-primary\n rather than white-on-primary (3.43:1 fail). */\n --hx-button-color: var(--hx-color-text-on-primary, #0d1825);\n --hx-button-border-color: transparent;\n }\n\n .button--secondary {\n --hx-button-bg: transparent;\n /* primary-500 (#429797) text on white surface = 3.44:1 — fails AA.\n primary-600 (#0F7078) on white = 5.82:1 — AA pass. */\n --hx-button-color: var(--hx-color-action-secondary-fg, #0f7078);\n --hx-button-border-color: var(--hx-color-action-secondary-border, #0f7078);\n }\n\n .button--secondary:hover {\n --hx-button-bg: var(--hx-button-hover-bg, var(--hx-color-action-secondary-bg-hover, #ebf8f8));\n }\n\n .button--tertiary {\n --hx-button-bg: var(--hx-color-surface-sunken, #ebeee9);\n --hx-button-color: var(--hx-color-text-primary, #0d1825);\n --hx-button-border-color: transparent;\n }\n\n .button--tertiary:hover {\n --hx-button-bg: var(--hx-button-hover-bg, var(--hx-color-surface-raised, #f5f8f3));\n }\n\n .button--danger {\n --hx-button-bg: var(--hx-color-action-danger-bg, #e5493e);\n /* Inline #0d1825 matches text.on-error's resolved primitive (neutral-900);\n cold-start without the semantic still paints AA-tuned dark-on-error\n rather than white-on-error (3.92:1 fail). */\n --hx-button-color: var(--hx-color-text-on-error, #0d1825);\n --hx-button-border-color: transparent;\n }\n\n /* on-error tokens are tuned for error-500 (neutral-900 on #E5493E ≈ 4.59:1).\n error-600 (#C92A2A) drops that to 3.28:1 — AA fail. text.on-error-strong\n resolves to neutral-0 across modes (no dark flip) so the darker hover fill\n stays legible. Mirrors hx-toast precedent (commit 300e21ab0); routed\n through the semantic tier in 3.2.1 token-cascade remediation. */\n .button--danger:hover {\n --hx-button-bg: var(--hx-button-hover-bg, var(--hx-color-action-danger-bg-hover, #c92a2a));\n --hx-button-color: var(--hx-color-text-on-error-strong, #ffffff);\n }\n\n /* Pressed state binds explicitly to action.danger.bg-active (error-700,\n #A21312) + text.on-error-strong (neutral-0) = 7.96:1 AA. Base\n .button:active filter:brightness(0.8) would compound on top of bg-hover\n (#C92A2A) and produce ~3.3:1 sub-AA on the bound colors. Override the\n filter to none. HC override on action.danger.bg-active flips to HC\n error-500 so the on-error-strong (HC = #000) pair is AA in HC too. */\n .button--danger:active {\n --hx-button-bg: var(--hx-button-active-bg, var(--hx-color-action-danger-bg-active, #a21312));\n --hx-button-color: var(--hx-color-text-on-error-strong, #ffffff);\n filter: none;\n }\n\n .button--ghost {\n --hx-button-bg: transparent;\n /* primary-500 (#429797) text on white surface = 3.44:1 — fails AA.\n primary-600 (#0F7078) on white = 5.82:1 — AA pass. */\n --hx-button-color: var(--hx-color-action-ghost-fg, #0f7078);\n --hx-button-border-color: transparent;\n }\n\n .button--ghost:hover {\n --hx-button-bg: var(--hx-button-hover-bg, var(--hx-color-action-ghost-bg-hover, #ebf8f8));\n }\n\n .button--outline {\n --hx-button-bg: transparent;\n --hx-button-color: var(--hx-color-text-primary, #0d1825);\n --hx-button-border-color: var(--hx-color-border-strong, #66787b);\n }\n\n .button--outline:hover {\n --hx-button-bg: var(--hx-button-hover-bg, var(--hx-color-surface-raised, #f5f8f3));\n }\n\n /* on-primary token resolves to neutral-900 (#0D1825) — tuned for primary-500.\n primary-600 (#0F7078) drops the pair to 3.07:1 — AA fail. text.on-primary-strong\n resolves to neutral-0 across modes (no dark flip) for the darker hover fill.\n Mirrors hx-toast precedent (commit 300e21ab0); routed through the semantic\n tier in 3.2.1 token-cascade remediation. */\n .button--primary:hover {\n --hx-button-bg: var(--hx-button-hover-bg, var(--hx-color-action-primary-bg-hover, #0f7078));\n --hx-button-color: var(--hx-color-text-on-primary-strong, #ffffff);\n }\n\n /* Pressed state binds explicitly to action.primary.bg-active (primary-700,\n #0F6363) + text.on-primary-strong (neutral-0) = 7.03:1 AA. The base\n .button:active filter:brightness(0.8) would compound on top of bg-hover\n (#0F7078) and produce ~3.7:1 sub-AA on the bound colors. Override the\n filter to none so the action.*.bg-active token is what actually paints. */\n .button--primary:active {\n --hx-button-bg: var(--hx-button-active-bg, var(--hx-color-action-primary-bg-active, #0f6363));\n --hx-button-color: var(--hx-color-text-on-primary-strong, #ffffff);\n filter: none;\n }\n\n /* ─── Disabled ─── */\n\n /* Note: opacity is applied on :host([disabled]) above — do NOT add opacity here.\n Stacking opacity on both :host and .button[disabled] would multiply to 0.25. */\n .button[disabled] {\n cursor: not-allowed;\n }\n\n /* ─── Loading State ─── */\n\n .button--loading {\n position: relative;\n cursor: wait;\n }\n\n .button__spinner {\n width: 1em;\n height: 1em;\n flex-shrink: 0;\n animation: hx-spin var(--hx-duration-spinner, 750ms) linear infinite;\n }\n\n @keyframes hx-spin {\n to {\n transform: rotate(360deg);\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .button {\n transition: none;\n }\n\n .button__spinner {\n animation: none;\n opacity: var(--hx-opacity-muted, 0.6);\n }\n }\n\n /* ─── Inverted Mode ─── */\n\n /* Inline-fallback contract for the on-dark-* tokens in this section:\n the literal rgba(255, 255, 255, 0.X) arms are a LIGHT-MODE-only last\n resort (cold-start, CSS-not-loaded). At runtime, hx-theme injects\n the dark.* override (overlay-black-* for the strong border and the\n surface.on-dark-overlay-* fills) so dark-mode inverted buttons stay\n visible on the now-light surface.inverse (#EBEEE9). The inline white\n overlays would render invisible (≈1.1:1) on a light surface, but\n they never paint when the theme is mounted. If a future change\n moves these into a context where hx-theme is not guaranteed,\n replace with mode-aware tokens. */\n\n /* Override text color and filter-based hover/active for all variants */\n :host([inverted]) .button {\n color: var(--hx-button-inverted-color, var(--hx-color-text-inverse, #ffffff));\n filter: none;\n }\n\n :host([inverted]) .button:hover {\n filter: none;\n }\n\n :host([inverted]) .button:active {\n filter: none;\n }\n\n :host([inverted]) .button:focus-visible {\n /* WCAG 1.4.11: focus indicator needs ≥3:1 against adjacent colors.\n border-on-dark-strong (overlay-white-70) ≈ 5:1 on neutral-900 — passes.\n The lower-alpha siblings used to live in border.* but were sub-3:1\n against any plausible surface and could not honour a border contract;\n they're now surface.on-dark-overlay-{default,subtle} (translucent\n fills, not borders) and used elsewhere in this file. See\n tokens.json color.surface.on-dark-overlay-* for canonical ratios. */\n outline-color: var(\n --hx-button-inverted-focus-ring-color,\n var(--hx-color-border-on-dark-strong, rgba(255, 255, 255, 0.7))\n );\n }\n\n /* Primary inverted — resting bg routes through action.primary.bg-inverted-rest\n so dark mode can flip the fill to primary-600. surface.inverse becomes light\n (#EBEEE9) in dark mode; primary-500 on #EBEEE9 = 2.94:1 (sub-3:1 UI floor\n fail for the inverted-button boundary). primary-600 on #EBEEE9 = 4.97:1\n (AA pass). Light mode tracks action.primary.bg-inverted-rest's base value\n (primary-500, 5.20:1 on dark surface.inverse).\n\n Override path note (codex round-11): this binds --hx-button-bg directly,\n matching the cascade convention every other .button--{variant} rule uses\n (light primary at line 89, danger at 120, secondary, tertiary, ghost). The\n consumer override path for inverted-primary rest is\n --hx-color-action-primary-bg-inverted-rest, NOT --hx-button-bg — the same\n pattern as light primary (consumers override --hx-color-action-primary-bg).\n Pinned by dark-mode-resolution.test.ts:185-197 across both modes. */\n :host([inverted]) .button--primary {\n --hx-button-bg: var(--hx-color-action-primary-bg-inverted-rest, #429797);\n }\n\n /* Primary inverted — hover/pressed lift to action.primary.bg-inverted-hover\n (primary-400, light teal). The base :host([inverted]) .button rule binds\n color to text.inverse, which flips by mode (neutral-0 in light, neutral-900\n in dark). On a permanent light-teal fill, white text drops to 2.4:1 in\n light mode (AA fail). Pin color to neutral-900 directly (the primitive,\n not text.primary which flips to neutral-100 in dark mode and would regress\n the pair to ~2.10:1) so the foreground is dark in both modes —\n neutral-900 on primary-400 = 7.27:1 AAA in Apex; AAA across all 6 brands.\n Decoupled from text.on-primary in 3.3.x because text.on-primary now\n resolves to neutral-0 (white) for the AAA-large coordinated pair on\n primary-600; using it here would regress this pair to ~2.45:1 (Apex)\n since primary-400 is light teal. neutral-900 is the correct anchor — it\n is the primitive that both light/dark text.primary used to resolve to,\n never flipped by mode/brand.\n Pressed === hover visually in inverted mode is acceptable UX (the\n transient absence of pointer over the button signals release).\n The fallback chain wraps --hx-button-active-bg (highest precedence) and\n --hx-button-hover-bg so consumer overrides on either prop apply under\n :host([inverted]) — the two share a paint here, so either knob is\n honored, with active-bg winning when both are set. */\n :host([inverted]) .button--primary:hover,\n :host([inverted]) .button--primary:active {\n --hx-button-bg: var(\n --hx-button-active-bg,\n var(--hx-button-hover-bg, var(--hx-color-action-primary-bg-inverted-hover, #6ab1b1))\n );\n color: var(\n --hx-button-inverted-primary-interactive-color,\n var(--hx-color-neutral-900, #0d1825)\n );\n }\n\n /* Danger inverted — sister to primary. Hover/pressed lift to\n action.danger.bg-inverted-hover (error-400, #FC7264). Same foreground\n contract: text.inverse fails in light mode (white on light red ≈ 2.6:1);\n pin to text.on-error (neutral-900, no dark-mode flip) for 6.58:1 in both\n modes. Same active-bg → hover-bg → semantic fallback chain as primary. */\n :host([inverted]) .button--danger:hover,\n :host([inverted]) .button--danger:active {\n --hx-button-bg: var(\n --hx-button-active-bg,\n var(--hx-button-hover-bg, var(--hx-color-action-danger-bg-inverted-hover, #fc7264))\n );\n color: var(\n --hx-button-inverted-danger-interactive-color,\n var(--hx-color-text-on-error, #0d1825)\n );\n }\n\n /* Secondary inverted — white border and translucent hover fill */\n :host([inverted]) .button--secondary {\n --hx-button-border-color: var(--hx-color-border-on-dark-strong, rgba(255, 255, 255, 0.7));\n }\n\n /* Inverted overlay fills read both names so consumer overrides reach paint\n regardless of which token they target: the deprecated --hx-color-border-on-dark-*\n names (3.2.0/3.2.1 public API, scheduled for removal in 4.0.0) and the canonical\n --hx-color-surface-on-dark-overlay-* names (round-8 rename). Deprecated name\n wins when set; otherwise resolves through the canonical alias chain. */\n :host([inverted]) .button--secondary:hover {\n --hx-button-bg: var(\n --hx-color-border-on-dark-default,\n var(--hx-color-surface-on-dark-overlay-default, rgba(255, 255, 255, 0.3))\n );\n }\n\n /* Tertiary inverted — resting at the subtle overlay (10%) lifts to the default\n overlay (30%) on hover so the runtime hover delta is visually distinct, not\n collapsed onto a single token. */\n :host([inverted]) .button--tertiary {\n --hx-button-bg: var(\n --hx-color-border-on-dark-subtle,\n var(--hx-color-surface-on-dark-overlay-subtle, rgba(255, 255, 255, 0.1))\n );\n --hx-button-border-color: transparent;\n }\n\n :host([inverted]) .button--tertiary:hover {\n --hx-button-bg: var(\n --hx-color-border-on-dark-default,\n var(--hx-color-surface-on-dark-overlay-default, rgba(255, 255, 255, 0.3))\n );\n }\n\n /* Ghost inverted — transparent base, translucent hover bg */\n :host([inverted]) .button--ghost {\n --hx-button-bg: transparent;\n --hx-button-border-color: transparent;\n }\n\n :host([inverted]) .button--ghost:hover {\n --hx-button-bg: var(\n --hx-button-inverted-ghost-hover-bg,\n var(\n --hx-color-border-on-dark-default,\n var(--hx-color-surface-on-dark-overlay-default, rgba(255, 255, 255, 0.3))\n )\n );\n }\n\n /* Outline inverted — white border */\n :host([inverted]) .button--outline {\n --hx-button-border-color: var(--hx-color-border-on-dark-strong, rgba(255, 255, 255, 0.7));\n }\n\n :host([inverted]) .button--outline:hover {\n --hx-button-bg: var(\n --hx-color-border-on-dark-default,\n var(--hx-color-surface-on-dark-overlay-default, rgba(255, 255, 255, 0.3))\n );\n }\n\n /* ─── Prefix / Suffix / Label ─── */\n\n .button__prefix,\n .button__suffix {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n }\n\n .button__label {\n flex: 1 1 auto;\n }\n\n /* ─── High Contrast Mode (forced-colors) ─── */\n\n @media (forced-colors: active) {\n .button {\n /* Ensure button outline is visible in Windows High Contrast mode.\n ButtonText/ButtonFace are system colors recognized by the browser. */\n forced-color-adjust: none;\n background-color: ButtonFace;\n color: ButtonText;\n border: 2px solid ButtonText;\n }\n\n .button:hover {\n /* Hover affordance must survive in HC. Highlight/HighlightText is the\n OS-level \"selected\" pair, mirroring the forcedColorsInteractive mixin's\n hover contract — kept inline since this component owns its bespoke HC\n block (XOR rule). */\n background-color: Highlight;\n color: HighlightText;\n border-color: Highlight;\n }\n\n .button:focus-visible {\n outline: 3px solid Highlight;\n outline-offset: 2px;\n }\n\n .button[disabled] {\n background-color: ButtonFace;\n color: GrayText;\n border-color: GrayText;\n opacity: 1;\n }\n\n :host([disabled]) {\n opacity: 1;\n }\n\n .button--loading .button__spinner {\n /* Ensure spinner is visible in HCM */\n forced-color-adjust: auto;\n }\n }\n`;\n","import { html, nothing, type TemplateResult, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { HelixElement } from '../../base/index.js';\nimport { mixinDelegatesAria } from '../../mixins/index.js';\nimport { helixButtonStyles } from './hx-button.styles.js';\nimport { devWarn } from '../../utils/dev-warn.js';\n\n/** Detail for the hx-click event dispatched by hx-button. */\nexport interface HxButtonClickDetail {\n originalEvent: MouseEvent;\n}\n\n/**\n * A production-grade button component for user interaction. Supports multiple\n * visual variants, sizes, loading state, prefix/suffix slots, anchor rendering,\n * and full ElementInternals form association.\n *\n * @summary Primary interactive element for triggering actions and form submission.\n *\n * @tag hx-button\n *\n * @slot - Default slot for button label text or content.\n * @slot prefix - Icon or content rendered before the label.\n * @slot suffix - Icon or content rendered after the label.\n *\n * @fires {CustomEvent<{originalEvent: MouseEvent}>} hx-click - Dispatched when\n * the button is clicked and is neither disabled nor loading.\n *\n * @csspart button - The native button or anchor element.\n * @csspart label - The label text wrapper span.\n * @csspart prefix - The prefix slot container span.\n * @csspart suffix - The suffix slot container span.\n * @csspart spinner - The loading spinner SVG element.\n *\n * @cssprop [--hx-button-bg=var(--hx-color-action-primary-bg)] - Button background color. **Variant-shadowed**: every `.button--{variant}` rule rebinds this property at descendant scope, so a host-level override (`style=\"--hx-button-bg: …\"` or `:host`-targeting CSS) does not reach paint for primary/secondary/tertiary/danger/ghost/outline buttons. To recolor a variant fill, override the upstream semantic instead — e.g. `--hx-color-action-primary-bg` (light primary), `--hx-color-action-primary-bg-inverted-rest` (inverted primary), `--hx-color-action-danger-bg` (danger), or the inverted overlay tokens for tertiary/secondary/ghost/outline. The `--hx-button-bg` hook still works for the rare case of an unstyled base button without a `variant` class, and is internally rebound by the variant rules to participate in the active/hover override chain.\n * @cssprop [--hx-button-hover-bg] - Hover background override (primary and danger variants only). Other variants (secondary/outline, ghost) keep their hover fills routed through their semantic action.* tokens and do not consume this hook. Under [inverted] for primary/danger, hover and active share a paint (combined :hover, :active rule), so this override applies to both states unless --hx-button-active-bg also takes precedence.\n * @cssprop [--hx-button-active-bg] - Pressed/active background override (primary and danger variants only, including their inverted modes). Takes precedence over --hx-button-hover-bg in the fallback chain. Standard-mode primary/danger default to action.{primary,danger}.bg-active (with filter:none) for AA-pinned pressed contrast. Inverted-mode primary/danger reuse action.{primary,danger}.bg-inverted-hover (combined :hover, :active rule); setting --hx-button-active-bg under [inverted] therefore overrides the lifted hover fill as well as the pressed fill — the two share a paint in inverted mode. Other variants do not consume this hook.\n * @cssprop [--hx-button-color=var(--hx-color-text-on-primary)] - Button text color (variants route through text.on-{role} / text.on-{role}-strong).\n * @cssprop [--hx-button-border-color=transparent] - Button border color (secondary/outline variants route through action.secondary.border).\n * @cssprop [--hx-button-border-radius=var(--hx-border-radius-md)] - Button border radius.\n * @cssprop [--hx-button-font-family=var(--hx-font-family-sans)] - Button font family.\n * @cssprop [--hx-button-font-weight=var(--hx-font-weight-semibold)] - Button font weight.\n * @cssprop [--hx-button-focus-ring-color=var(--hx-focus-ring-color)] - Focus ring color.\n *\n * @cssprop [--hx-button-inverted-color=var(--hx-color-text-inverse)] - Text color when inverted (resolves to neutral-0).\n * @cssprop [--hx-button-inverted-primary-interactive-color=var(--hx-color-text-on-primary)] - Foreground override for inverted primary hover and pressed (combined :hover, :active rule). Defaults to text.on-primary (neutral-900, no dark-mode flip) so dark text rides the lifted primary-400 fill — text.inverse on light teal collapses to ~2.4:1 in light mode.\n * @cssprop [--hx-button-inverted-danger-interactive-color=var(--hx-color-text-on-error)] - Foreground override for inverted danger hover and pressed (combined :hover, :active rule). Defaults to text.on-error (neutral-900); same rationale as the primary override.\n * @cssprop [--hx-button-inverted-ghost-hover-bg=var(--hx-color-surface-on-dark-overlay-default)] - Ghost hover bg when inverted (overlay-white-30 — translucent fill, not a border; contrast not applicable).\n * @cssprop [--hx-button-inverted-focus-ring-color=var(--hx-color-border-on-dark-strong)] - Focus ring color when inverted (overlay-white-70 ≈ 5:1 vs neutral-900 — clears WCAG 1.4.11 3:1 floor for non-text UI).\n *\n * @cssprop [--hx-color-action-primary-bg] - Primary variant resting fill (3.2.1 semantic action layer).\n * @cssprop [--hx-color-action-primary-bg-hover] - Primary variant hover fill.\n * @cssprop [--hx-color-action-primary-bg-active] - Primary variant active/pressed fill.\n * @cssprop [--hx-color-action-secondary-fg] - Secondary/outline variant fg (resolves to primary-600 light, primary-400 dark). Consumed only by .button--secondary; the actual border/surface paint for outline currently routes through --hx-color-border-strong / --hx-color-surface-raised in styles, with this token reserved for the foreground.\n * @cssprop [--hx-color-action-secondary-border] - Secondary/outline variant border (3.2.1 semantic; outline still routes through --hx-color-border-strong by default).\n * @cssprop [--hx-color-action-secondary-bg-hover] - Secondary/outline variant hover fill (3.2.1 semantic; outline still routes through --hx-color-surface-raised by default).\n * @cssprop [--hx-color-action-ghost-fg] - Ghost variant fg.\n * @cssprop [--hx-color-action-ghost-bg-hover] - Ghost variant hover fill.\n * @cssprop [--hx-color-action-danger-bg] - Danger variant resting fill.\n * @cssprop [--hx-color-action-danger-bg-hover] - Danger variant hover fill.\n * @cssprop [--hx-color-action-danger-bg-active] - Danger variant active fill.\n * @cssprop [--hx-color-action-primary-bg-inverted-hover] - Primary variant hover/pressed fill on dark/inverted surface (resolves to primary-400, 7.27:1 on neutral-900).\n * @cssprop [--hx-color-action-danger-bg-inverted-hover] - Danger variant hover/pressed fill on dark/inverted surface (resolves to error-400, 6.58:1 on neutral-900).\n * @cssprop [--hx-color-text-on-primary] - Foreground for primary fill (resolves to neutral-900 — AA-tuned for primary-500).\n * @cssprop [--hx-color-text-on-primary-strong] - Foreground for primary-hover fill (resolves to neutral-0 across modes).\n * @cssprop [--hx-color-text-on-error] - Foreground for danger fill (resolves to neutral-900).\n * @cssprop [--hx-color-text-on-error-strong] - Foreground for danger-hover fill (resolves to neutral-0 across modes).\n * @cssprop [--hx-color-text-primary] - Foreground for tertiary variant on surface.sunken.\n * @cssprop [--hx-color-surface-sunken] - Tertiary variant resting fill.\n * @cssprop [--hx-color-surface-raised] - Tertiary variant hover fill.\n * @cssprop [--hx-color-surface-on-dark-overlay-subtle] - Inverted-tertiary resting fill (overlay-white-10 — translucent fill, not a border).\n * @cssprop [--hx-color-surface-on-dark-overlay-default] - Inverted-tertiary hover fill + inverted-secondary/ghost/outline hover fill (overlay-white-30 — translucent fill, not a border).\n * @cssprop [--hx-color-border-on-dark-strong] - Inverted-secondary/outline border + inverted focus-visible outline (overlay-white-70 ≈ 5:1 — clears WCAG 1.4.11 3:1 floor).\n * @cssprop [--hx-color-border-on-dark-subtle] - DEPRECATED 3.2.2; renamed to --hx-color-surface-on-dark-overlay-subtle (the value paints a translucent fill, not a border). Consume sites read both names via deprecated-first fallback so existing overrides keep working until removal in 4.0.0.\n * @cssprop [--hx-color-border-on-dark-default] - DEPRECATED 3.2.2; renamed to --hx-color-surface-on-dark-overlay-default (the value paints a translucent fill, not a border). Consume sites read both names via deprecated-first fallback so existing overrides keep working until removal in 4.0.0.\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-button/AAA-AUDIT.md\n * @keyboard-contract activate=Enter,Space; disabled-suppresses=true\n * @aria-pattern button\n * @aria-pattern-source https://www.w3.org/WAI/ARIA/apg/patterns/button/\n * @forced-colors-supported true\n * @stability stable\n * @since 3.6.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-button\n * @priority-tier P0\n * @phi-handles false\n * @clinical-context none\n */\n@customElement('hx-button')\nexport class HelixButton extends mixinDelegatesAria(HelixElement) {\n // 3.2.1: forced-colors deference is owned by the bespoke @media block in\n // hx-button.styles.ts (covers loading/disabled/focus, not just the base).\n // Do NOT also compose forcedColorsInteractive here — the mixin's docstring\n // forbids dual composition (XOR rule) and the dual approach was flagged in\n // the token-cascade campaign findings.\n static override styles = [helixButtonStyles];\n\n // ─── Form Association ───\n\n /** @internal */\n static override formAssociated = true;\n\n // ─── Public Properties ───\n\n /**\n * Visual style variant of the button.\n * @attr variant\n */\n @property({ type: String, reflect: true })\n variant: 'primary' | 'secondary' | 'tertiary' | 'danger' | 'ghost' | 'outline' = 'primary';\n\n /**\n * Size of the button.\n * @attr hx-size\n */\n @property({ type: String, reflect: true, attribute: 'hx-size' })\n size: 'sm' | 'md' | 'lg' = 'md';\n\n /**\n * Whether the button is disabled. Prevents all interaction and form actions.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Whether the button is in a loading state. Shows spinner, prevents interaction,\n * and sets aria-busy. Does not set the disabled attribute.\n * @attr loading\n */\n @property({ type: Boolean, reflect: true })\n loading = false;\n\n /**\n * The type attribute for the underlying button element. Ignored when href is set.\n * @attr type\n */\n @property({ type: String })\n type: 'button' | 'submit' | 'reset' = 'button';\n\n /**\n * When set, renders an anchor element instead of a button.\n * @attr href\n */\n @property({ type: String })\n href: string | undefined = undefined;\n\n /**\n * Anchor target attribute. Only used when href is set.\n * @attr target\n */\n @property({ type: String })\n target: string | undefined = undefined;\n\n /**\n * Form field name submitted via ElementInternals.setFormValue on submit.\n * @attr name\n */\n @property({ type: String })\n name: string | undefined = undefined;\n\n /**\n * Form field value submitted via ElementInternals.setFormValue on submit.\n * @attr value\n */\n @property({ type: String })\n value: string | undefined = undefined;\n\n /**\n * When true, the button stretches to fill its container width.\n * Sets the host to `display: block` and the inner element to `width: 100%`.\n * @attr full\n */\n @property({ type: Boolean, reflect: true })\n full = false;\n\n /**\n * When true, flips button colors for placement on dark or gradient backgrounds.\n * Forces text to white and adjusts hover/focus ring colors across all variants.\n *\n * **Mode scope:** `[inverted]` is validated for placement on a dark *region*\n * within a light-mode-active page (hero banners, gradient sections, dark\n * cards). It is NOT validated for use within a dark-mode-active root\n * context: in dark mode, `surface.inverse` flips to a light surface\n * (neutral-100), and the lifted `-400` hover/active fills lose UI-floor\n * contrast against it (primary 2.10:1, danger 2.32:1 vs WCAG 1.4.11's 3:1\n * floor). Mode-aware fill stops + foreground for the dark-mode-inverted\n * combination are tracked as a 3.2.x follow-up.\n *\n * @attr inverted\n */\n @property({ type: Boolean, reflect: true })\n inverted = false;\n\n /**\n * Accessible label for icon-only or text-less buttons.\n * Required when the button has no visible text content.\n *\n * Accepts both `accessible-label` and the standard `aria-label` HTML attribute.\n * `accessible-label` takes precedence when both are set.\n *\n * @attr accessible-label\n */\n @property({ type: String, attribute: 'accessible-label' })\n accessibleLabel: string = '';\n\n /**\n * Returns the effective label for the button, checking accessible-label first,\n * then the consumer-set aria-label attribute (read via data-aria-label which\n * mixinDelegatesAria writes when consumers set the host's `aria-label`),\n * then the still-pending `aria-label` attribute (in case the mixin has not\n * yet processed it), falling back to empty string.\n *\n * HX-015 migration: never read `this.ariaLabel` (the native HTMLElement IDL\n * property). The mixin overrides `ariaLabel` to read `data-aria-label` in\n * modern browsers, but the IDL fallback path breaks in fallback browsers and\n * confuses consumers who follow the v3 upgrade guide. Read the attribute\n * storage directly so the source-of-truth is unambiguous.\n *\n * @internal\n */\n private get _effectiveLabel(): string {\n return (\n this.accessibleLabel?.trim() ||\n this.getAttribute('data-aria-label')?.trim() ||\n this.getAttribute('aria-label')?.trim() ||\n ''\n );\n }\n\n // ─── Form API ───\n\n protected override _onFormDisabled(disabled: boolean): void {\n this.disabled = disabled;\n }\n\n // ─── Lifecycle ───\n\n /** @internal */\n private static readonly _VALID_VARIANTS = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'danger',\n 'ghost',\n 'outline',\n ] as const;\n\n // Prevents double-warn on browsers that fire slotchange for empty initial slots.\n private _emptySlotWarnEmitted = false;\n\n override firstUpdated(changedProperties: PropertyValues<this>): void {\n super.firstUpdated(changedProperties);\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n const hasContent = (slot?.assignedNodes({ flatten: true }) ?? []).some(\n (n) => n.nodeType !== Node.TEXT_NODE || (n.textContent?.trim().length ?? 0) > 0,\n );\n if (!hasContent && !this._effectiveLabel) {\n this._emptySlotWarnEmitted = true;\n devWarn(\n 'hx-button',\n 'hx-button has no slot content and no accessible-label — button will have no accessible name.',\n );\n }\n }\n\n override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n if (changedProperties.has('variant')) {\n const validVariants: string[] = [...HelixButton._VALID_VARIANTS];\n if (!validVariants.includes(this.variant)) {\n devWarn(\n 'hx-button',\n `Invalid variant \"${this.variant}\". Expected one of: ${validVariants.join(', ')}. Clamping to \"primary\".`,\n );\n this.variant = 'primary';\n }\n }\n }\n\n // ─── Slot Handlers ───\n\n /** @internal */\n private _handleDefaultSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n const hasContent = slot\n .assignedNodes({ flatten: true })\n .some((n) => n.nodeType !== Node.TEXT_NODE || (n.textContent?.trim().length ?? 0) > 0);\n if (!hasContent && !this._effectiveLabel && !this._emptySlotWarnEmitted) {\n devWarn(\n 'hx-button',\n 'hx-button has no slot content and no accessible-label — button will have no accessible name.',\n );\n }\n // Only reset once content arrives so the guard stays armed for browsers\n // that fire a second slotchange for the same empty initial slot.\n if (hasContent) {\n this._emptySlotWarnEmitted = false;\n }\n }\n\n // ─── Event Handling ───\n\n /**\n * @private\n * @internal\n */\n private _handleClick(e: MouseEvent): void {\n if (this.disabled || this.loading) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n\n /**\n * Dispatched when the button is clicked.\n * @event hx-click\n */\n this.dispatchEvent(\n new CustomEvent<{ originalEvent: MouseEvent }>('hx-click', {\n bubbles: true,\n composed: true,\n detail: { originalEvent: e },\n }),\n );\n\n // Handle form submission/reset if form-associated and not in anchor mode\n if (this.href === undefined && this.type === 'submit' && this._internals.form) {\n if (this.name !== undefined && this.value !== undefined) {\n this._internals.setFormValue(this.value);\n }\n this._internals.form.requestSubmit();\n } else if (this.href === undefined && this.type === 'reset' && this._internals.form) {\n this._internals.form.reset();\n }\n }\n\n // ─── Render Helpers ───\n\n /**\n * @private\n * @internal\n */\n private _renderSpinner(): TemplateResult {\n return html`\n <svg\n class=\"button__spinner\"\n part=\"spinner\"\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n >\n <circle\n class=\"button__spinner-track\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n opacity=\"0.3\"\n />\n <path\n class=\"button__spinner-arc\"\n d=\"M12 2a10 10 0 0 1 10 10\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n </svg>\n `;\n }\n\n /**\n * @private\n * @internal\n */\n private _renderInner(): TemplateResult {\n return html`\n ${this.loading ? this._renderSpinner() : nothing}\n <span part=\"prefix\" class=\"button__prefix\">\n <slot name=\"prefix\"></slot>\n </span>\n <span part=\"label\" class=\"button__label\">\n <slot @slotchange=${this._handleDefaultSlotChange}></slot>\n </span>\n <span part=\"suffix\" class=\"button__suffix\">\n <slot name=\"suffix\"></slot>\n </span>\n `;\n }\n\n // ─── Render ───\n\n override render() {\n const classes = {\n button: true,\n [`button--${this.variant}`]: true,\n [`button--${this.size}`]: true,\n 'button--loading': this.loading,\n };\n\n // mixinDelegatesAria forwarding: consumer-set aria-* (aria-pressed for\n // toggle buttons, aria-expanded/aria-haspopup/aria-controls for\n // menu/dropdown triggers, aria-describedby for inline help, aria-current\n // for active-link semantics in href mode) land in data-aria-* on the\n // host. Project them onto the inner role-bearing native element so AT\n // walks them on the <button>/<a>, not the generic host.\n const projectedPressed = this.getAttribute('data-aria-pressed');\n const projectedExpanded = this.getAttribute('data-aria-expanded');\n const projectedHasPopup = this.getAttribute('data-aria-haspopup');\n const projectedControls = this.getAttribute('data-aria-controls');\n const projectedDescribedBy = this.getAttribute('data-aria-describedby');\n const projectedCurrent = this.getAttribute('data-aria-current');\n const projectedLabelledBy = this.getAttribute('data-aria-labelledby');\n\n if (this.href !== undefined) {\n return html`\n <a\n part=\"button\"\n class=${classMap(classes)}\n href=${this.disabled || this.loading ? nothing : ifDefined(this.href)}\n target=${ifDefined(this.target)}\n rel=${this.target === '_blank' ? 'noopener noreferrer' : nothing}\n aria-label=${this._effectiveLabel || nothing}\n aria-labelledby=${ifDefined(projectedLabelledBy ?? undefined)}\n aria-disabled=${this.disabled ? 'true' : nothing}\n aria-busy=${this.loading ? 'true' : nothing}\n aria-pressed=${ifDefined(projectedPressed ?? undefined)}\n aria-expanded=${ifDefined(projectedExpanded ?? undefined)}\n aria-haspopup=${ifDefined(projectedHasPopup ?? undefined)}\n aria-controls=${ifDefined(projectedControls ?? undefined)}\n aria-describedby=${ifDefined(projectedDescribedBy ?? undefined)}\n aria-current=${ifDefined(projectedCurrent ?? undefined)}\n tabindex=${this.disabled || this.loading ? '-1' : nothing}\n @click=${this._handleClick}\n >\n ${this._renderInner()}\n </a>\n `;\n }\n\n return html`\n <button\n part=\"button\"\n class=${classMap(classes)}\n ?disabled=${this.disabled}\n type=${this.type}\n aria-label=${this._effectiveLabel || nothing}\n aria-labelledby=${ifDefined(projectedLabelledBy ?? undefined)}\n aria-busy=${this.loading ? 'true' : nothing}\n aria-pressed=${ifDefined(projectedPressed ?? undefined)}\n aria-expanded=${ifDefined(projectedExpanded ?? undefined)}\n aria-haspopup=${ifDefined(projectedHasPopup ?? undefined)}\n aria-controls=${ifDefined(projectedControls ?? undefined)}\n aria-describedby=${ifDefined(projectedDescribedBy ?? undefined)}\n @click=${this._handleClick}\n >\n ${this._renderInner()}\n </button>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-button': HelixButton;\n }\n interface HTMLElementEventMap {\n 'hx-click': CustomEvent<{ originalEvent: MouseEvent }>;\n }\n}\n"],"names":["helixButtonStyles","css","HelixButton","mixinDelegatesAria","HelixElement","_a","_b","_c","disabled","changedProperties","slot","n","validVariants","devWarn","e","hasContent","html","nothing","classes","projectedPressed","projectedExpanded","projectedHasPopup","projectedControls","projectedDescribedBy","projectedCurrent","projectedLabelledBy","classMap","ifDefined","__decorateClass","property","customElement"],"mappings":";;;;;;;AAEO,MAAMA,IAAoBC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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"}
|