@helixui/library 3.8.0 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/README.md +41 -0
  2. package/aaa-verdicts.json +2036 -0
  3. package/custom-elements.json +532 -569
  4. package/dist/components/hx-accordion/hx-accordion-item.d.ts.map +1 -1
  5. package/dist/components/hx-accordion/hx-accordion-item.styles.d.ts.map +1 -1
  6. package/dist/components/hx-accordion/index.js +1 -1
  7. package/dist/components/hx-alert/hx-alert.d.ts +0 -8
  8. package/dist/components/hx-alert/hx-alert.d.ts.map +1 -1
  9. package/dist/components/hx-alert/hx-alert.styles.d.ts.map +1 -1
  10. package/dist/components/hx-alert/index.js +1 -1
  11. package/dist/components/hx-avatar/hx-avatar.d.ts +4 -1
  12. package/dist/components/hx-avatar/hx-avatar.d.ts.map +1 -1
  13. package/dist/components/hx-avatar/hx-avatar.styles.d.ts.map +1 -1
  14. package/dist/components/hx-avatar/index.js +1 -1
  15. package/dist/components/hx-badge/hx-badge.d.ts.map +1 -1
  16. package/dist/components/hx-badge/hx-badge.styles.d.ts.map +1 -1
  17. package/dist/components/hx-badge/index.js +1 -1
  18. package/dist/components/hx-banner/hx-banner.d.ts +0 -8
  19. package/dist/components/hx-banner/hx-banner.d.ts.map +1 -1
  20. package/dist/components/hx-banner/hx-banner.styles.d.ts.map +1 -1
  21. package/dist/components/hx-banner/index.js +1 -1
  22. package/dist/components/hx-carousel/hx-carousel.d.ts.map +1 -1
  23. package/dist/components/hx-carousel/hx-carousel.styles.d.ts.map +1 -1
  24. package/dist/components/hx-carousel/index.js +1 -1
  25. package/dist/components/hx-checkbox/hx-checkbox.d.ts.map +1 -1
  26. package/dist/components/hx-checkbox/hx-checkbox.styles.d.ts.map +1 -1
  27. package/dist/components/hx-checkbox/index.js +1 -1
  28. package/dist/components/hx-clinical-status/hx-clinical-status.d.ts +7 -9
  29. package/dist/components/hx-clinical-status/hx-clinical-status.d.ts.map +1 -1
  30. package/dist/components/hx-clinical-status/hx-clinical-status.styles.d.ts.map +1 -1
  31. package/dist/components/hx-clinical-status/index.js +1 -1
  32. package/dist/components/hx-combobox/hx-combobox.d.ts.map +1 -1
  33. package/dist/components/hx-combobox/hx-combobox.styles.d.ts.map +1 -1
  34. package/dist/components/hx-combobox/index.js +1 -1
  35. package/dist/components/hx-date-picker/hx-date-picker.d.ts.map +1 -1
  36. package/dist/components/hx-date-picker/hx-date-picker.styles.d.ts.map +1 -1
  37. package/dist/components/hx-date-picker/index.js +1 -1
  38. package/dist/components/hx-drawer/hx-drawer.d.ts.map +1 -1
  39. package/dist/components/hx-drawer/hx-drawer.styles.d.ts.map +1 -1
  40. package/dist/components/hx-drawer/index.js +1 -1
  41. package/dist/components/hx-dropdown/hx-dropdown.styles.d.ts.map +1 -1
  42. package/dist/components/hx-dropdown/index.js +1 -1
  43. package/dist/components/hx-file-upload/hx-file-upload.d.ts +28 -0
  44. package/dist/components/hx-file-upload/hx-file-upload.d.ts.map +1 -1
  45. package/dist/components/hx-file-upload/hx-file-upload.styles.d.ts.map +1 -1
  46. package/dist/components/hx-file-upload/index.js +1 -1
  47. package/dist/components/hx-help-text/hx-help-text.d.ts.map +1 -1
  48. package/dist/components/hx-help-text/hx-help-text.styles.d.ts.map +1 -1
  49. package/dist/components/hx-help-text/index.js +1 -1
  50. package/dist/components/hx-icon/hx-icon.d.ts +108 -12
  51. package/dist/components/hx-icon/hx-icon.d.ts.map +1 -1
  52. package/dist/components/hx-icon/hx-icon.styles.d.ts.map +1 -1
  53. package/dist/components/hx-icon/index.js +1 -1
  54. package/dist/components/hx-link/hx-link.d.ts.map +1 -1
  55. package/dist/components/hx-link/hx-link.styles.d.ts.map +1 -1
  56. package/dist/components/hx-link/index.js +1 -1
  57. package/dist/components/hx-menu/hx-menu-item.d.ts.map +1 -1
  58. package/dist/components/hx-menu/hx-menu-item.styles.d.ts.map +1 -1
  59. package/dist/components/hx-menu/index.js +1 -1
  60. package/dist/components/hx-nav/hx-nav.d.ts.map +1 -1
  61. package/dist/components/hx-nav/hx-nav.styles.d.ts.map +1 -1
  62. package/dist/components/hx-nav/index.js +1 -1
  63. package/dist/components/hx-number-input/hx-number-input.d.ts.map +1 -1
  64. package/dist/components/hx-number-input/hx-number-input.styles.d.ts.map +1 -1
  65. package/dist/components/hx-number-input/index.js +1 -1
  66. package/dist/components/hx-overflow-menu/hx-overflow-menu.d.ts +5 -1
  67. package/dist/components/hx-overflow-menu/hx-overflow-menu.d.ts.map +1 -1
  68. package/dist/components/hx-overflow-menu/hx-overflow-menu.styles.d.ts.map +1 -1
  69. package/dist/components/hx-overflow-menu/index.js +1 -1
  70. package/dist/components/hx-phi-field/hx-phi-field.d.ts.map +1 -1
  71. package/dist/components/hx-phi-field/hx-phi-field.styles.d.ts.map +1 -1
  72. package/dist/components/hx-phi-field/index.js +1 -1
  73. package/dist/components/hx-radio-group/hx-radio-group.d.ts.map +1 -1
  74. package/dist/components/hx-radio-group/index.js +1 -1
  75. package/dist/components/hx-rating/hx-rating.d.ts.map +1 -1
  76. package/dist/components/hx-rating/hx-rating.styles.d.ts.map +1 -1
  77. package/dist/components/hx-rating/index.js +1 -1
  78. package/dist/components/hx-side-nav/hx-nav-item.d.ts.map +1 -1
  79. package/dist/components/hx-side-nav/hx-nav-item.styles.d.ts.map +1 -1
  80. package/dist/components/hx-side-nav/hx-side-nav.d.ts.map +1 -1
  81. package/dist/components/hx-side-nav/hx-side-nav.styles.d.ts.map +1 -1
  82. package/dist/components/hx-side-nav/index.js +1 -1
  83. package/dist/components/hx-slider/hx-slider.d.ts +28 -0
  84. package/dist/components/hx-slider/hx-slider.d.ts.map +1 -1
  85. package/dist/components/hx-slider/index.js +1 -1
  86. package/dist/components/hx-split-button/hx-split-button.d.ts.map +1 -1
  87. package/dist/components/hx-split-button/hx-split-button.styles.d.ts.map +1 -1
  88. package/dist/components/hx-split-button/index.js +1 -1
  89. package/dist/components/hx-stat/hx-stat.d.ts.map +1 -1
  90. package/dist/components/hx-stat/hx-stat.styles.d.ts.map +1 -1
  91. package/dist/components/hx-stat/index.js +1 -1
  92. package/dist/components/hx-steps/hx-step.d.ts.map +1 -1
  93. package/dist/components/hx-steps/hx-step.styles.d.ts.map +1 -1
  94. package/dist/components/hx-steps/index.js +1 -1
  95. package/dist/components/hx-tag/hx-tag.d.ts.map +1 -1
  96. package/dist/components/hx-tag/hx-tag.styles.d.ts.map +1 -1
  97. package/dist/components/hx-tag/index.js +1 -1
  98. package/dist/components/hx-time-picker/hx-time-picker.d.ts.map +1 -1
  99. package/dist/components/hx-time-picker/hx-time-picker.styles.d.ts.map +1 -1
  100. package/dist/components/hx-time-picker/index.js +1 -1
  101. package/dist/components/hx-toast/hx-toast.d.ts +0 -8
  102. package/dist/components/hx-toast/hx-toast.d.ts.map +1 -1
  103. package/dist/components/hx-toast/hx-toast.styles.d.ts.map +1 -1
  104. package/dist/components/hx-toast/index.js +1 -1
  105. package/dist/components/hx-top-nav/hx-top-nav.d.ts.map +1 -1
  106. package/dist/components/hx-top-nav/hx-top-nav.styles.d.ts.map +1 -1
  107. package/dist/components/hx-top-nav/index.js +1 -1
  108. package/dist/components/hx-tree-view/hx-tree-item.d.ts.map +1 -1
  109. package/dist/components/hx-tree-view/hx-tree-item.styles.d.ts.map +1 -1
  110. package/dist/components/hx-tree-view/index.js +1 -1
  111. package/dist/css/helix-all.css +116 -54
  112. package/dist/css/helix-core.css +19 -4
  113. package/dist/css/helix-feedback.css +15 -18
  114. package/dist/css/helix-forms.css +39 -12
  115. package/dist/css/helix-media.css +6 -3
  116. package/dist/css/helix-navigation.css +16 -7
  117. package/dist/css/helix-overlay.css +10 -0
  118. package/dist/css/helix-tokens.css +3 -2
  119. package/dist/css/helix-utility.css +5 -0
  120. package/dist/css/hx-alert.css +4 -8
  121. package/dist/css/hx-avatar.css +1 -2
  122. package/dist/css/hx-badge.css +5 -0
  123. package/dist/css/hx-banner.css +4 -8
  124. package/dist/css/hx-carousel.css +6 -3
  125. package/dist/css/hx-checkbox.css +4 -9
  126. package/dist/css/hx-clinical-status.css +4 -7
  127. package/dist/css/hx-combobox.css +8 -0
  128. package/dist/css/hx-date-picker.css +5 -0
  129. package/dist/css/hx-drawer.css +5 -0
  130. package/dist/css/hx-dropdown.css +5 -0
  131. package/dist/css/hx-file-upload.css +4 -0
  132. package/dist/css/hx-help-text.css +5 -0
  133. package/dist/css/hx-icon.css +7 -0
  134. package/dist/css/hx-link.css +1 -2
  135. package/dist/css/hx-nav.css +7 -0
  136. package/dist/css/hx-number-input.css +2 -3
  137. package/dist/css/hx-overflow-menu.css +5 -0
  138. package/dist/css/hx-phi-field.css +2 -3
  139. package/dist/css/hx-rating.css +6 -0
  140. package/dist/css/hx-side-nav.css +3 -5
  141. package/dist/css/hx-split-button.css +5 -0
  142. package/dist/css/hx-stat.css +1 -2
  143. package/dist/css/hx-tag.css +5 -0
  144. package/dist/css/hx-time-picker.css +5 -0
  145. package/dist/css/hx-toast.css +6 -0
  146. package/dist/css/hx-top-nav.css +1 -2
  147. package/dist/css/index.css +1 -1
  148. package/dist/css/manifest.json +4 -1
  149. package/dist/index.js +33 -33
  150. package/dist/shared/{hx-accordion-ZVzgDzTG.js → hx-accordion-DR--Ev4t.js} +48 -54
  151. package/dist/shared/hx-accordion-DR--Ev4t.js.map +1 -0
  152. package/dist/shared/{hx-alert-Bto8-TIi.js → hx-alert-C0axS32J.js} +40 -79
  153. package/dist/shared/hx-alert-C0axS32J.js.map +1 -0
  154. package/dist/shared/{hx-avatar-C9hOmlAb.js → hx-avatar-ChAYWnK8.js} +22 -24
  155. package/dist/shared/hx-avatar-ChAYWnK8.js.map +1 -0
  156. package/dist/shared/{hx-badge-DFL35nzi.js → hx-badge-vX-1cuLA.js} +16 -11
  157. package/dist/shared/hx-badge-vX-1cuLA.js.map +1 -0
  158. package/dist/shared/{hx-banner-fpRnciIO.js → hx-banner-PbHwFNSb.js} +51 -90
  159. package/dist/shared/hx-banner-PbHwFNSb.js.map +1 -0
  160. package/dist/shared/{hx-carousel-item-z1Lc24op.js → hx-carousel-item-BVIKgQ4i.js} +72 -102
  161. package/dist/shared/hx-carousel-item-BVIKgQ4i.js.map +1 -0
  162. package/dist/shared/{hx-checkbox-DcgyGS9V.js → hx-checkbox-DDSXXhps.js} +31 -38
  163. package/dist/shared/hx-checkbox-DDSXXhps.js.map +1 -0
  164. package/dist/shared/{hx-clinical-status-D3XQIOqX.js → hx-clinical-status-ZSVEc3Qg.js} +68 -87
  165. package/dist/shared/hx-clinical-status-ZSVEc3Qg.js.map +1 -0
  166. package/dist/shared/{hx-combobox-NgJaLbs2.js → hx-combobox-Be-mqOv4.js} +35 -45
  167. package/dist/shared/hx-combobox-Be-mqOv4.js.map +1 -0
  168. package/dist/shared/{hx-date-picker-0PtEav0K.js → hx-date-picker-CziP3Hm1.js} +15 -22
  169. package/dist/shared/hx-date-picker-CziP3Hm1.js.map +1 -0
  170. package/dist/shared/{hx-drawer-CM_upadk.js → hx-drawer-BlU2oX8-.js} +32 -36
  171. package/dist/shared/hx-drawer-BlU2oX8-.js.map +1 -0
  172. package/dist/shared/{hx-dropdown-xHwTJecv.js → hx-dropdown-DREqpIpm.js} +16 -11
  173. package/dist/shared/hx-dropdown-DREqpIpm.js.map +1 -0
  174. package/dist/shared/{hx-file-upload-D3rKROK5.js → hx-file-upload-CU5QGZSP.js} +137 -80
  175. package/dist/shared/hx-file-upload-CU5QGZSP.js.map +1 -0
  176. package/dist/shared/hx-help-text-CNaZ82LT.js +137 -0
  177. package/dist/shared/hx-help-text-CNaZ82LT.js.map +1 -0
  178. package/dist/shared/hx-icon-bxz9eB9a.js +386 -0
  179. package/dist/shared/hx-icon-bxz9eB9a.js.map +1 -0
  180. package/dist/shared/{hx-link-CMnZRUtQ.js → hx-link-BURSdYLp.js} +19 -26
  181. package/dist/shared/hx-link-BURSdYLp.js.map +1 -0
  182. package/dist/shared/{hx-menu-divider-A6Guuzi_.js → hx-menu-divider-g0grbWV9.js} +19 -31
  183. package/dist/shared/hx-menu-divider-g0grbWV9.js.map +1 -0
  184. package/dist/shared/{hx-nav-ChMTfn7o.js → hx-nav-GTsAZGOx.js} +46 -59
  185. package/dist/shared/hx-nav-GTsAZGOx.js.map +1 -0
  186. package/dist/shared/{hx-nav-item-ClN17f1y.js → hx-nav-item-CxE7Mp3M.js} +62 -64
  187. package/dist/shared/hx-nav-item-CxE7Mp3M.js.map +1 -0
  188. package/dist/shared/{hx-number-input-MggsT7F0.js → hx-number-input-Bvyc9kOi.js} +48 -53
  189. package/dist/shared/hx-number-input-Bvyc9kOi.js.map +1 -0
  190. package/dist/shared/{hx-overflow-menu-DFjJAziP.js → hx-overflow-menu-LrTteeR1.js} +32 -39
  191. package/dist/shared/hx-overflow-menu-LrTteeR1.js.map +1 -0
  192. package/dist/shared/{hx-phi-field-C19oxlrr.js → hx-phi-field-sZt_rYIL.js} +46 -66
  193. package/dist/shared/hx-phi-field-sZt_rYIL.js.map +1 -0
  194. package/dist/shared/{hx-radio-BY4zpwdh.js → hx-radio-BD_c9NJy.js} +51 -56
  195. package/dist/shared/{hx-radio-BY4zpwdh.js.map → hx-radio-BD_c9NJy.js.map} +1 -1
  196. package/dist/shared/{hx-rating-C3QP53k9.js → hx-rating-BGK4AxvI.js} +45 -71
  197. package/dist/shared/hx-rating-BGK4AxvI.js.map +1 -0
  198. package/dist/shared/{hx-slider-Blmv_rwS.js → hx-slider-CkOk5BCY.js} +83 -23
  199. package/dist/shared/{hx-slider-Blmv_rwS.js.map → hx-slider-CkOk5BCY.js.map} +1 -1
  200. package/dist/shared/{hx-split-button-CdNz1XAu.js → hx-split-button-Bg9FHrFK.js} +12 -16
  201. package/dist/shared/hx-split-button-Bg9FHrFK.js.map +1 -0
  202. package/dist/shared/{hx-stat-Gtw_SpK8.js → hx-stat-wKxbyep6.js} +22 -55
  203. package/dist/shared/hx-stat-wKxbyep6.js.map +1 -0
  204. package/dist/shared/{hx-step-CUzliIK_.js → hx-step-CyGQAuiB.js} +5 -25
  205. package/dist/shared/hx-step-CyGQAuiB.js.map +1 -0
  206. package/dist/shared/{hx-tag-C5aCUpVi.js → hx-tag-BqO6HY6V.js} +26 -21
  207. package/dist/shared/hx-tag-BqO6HY6V.js.map +1 -0
  208. package/dist/shared/{hx-time-picker-DfJkBwcX.js → hx-time-picker-if5Cl0Ei.js} +32 -38
  209. package/dist/shared/hx-time-picker-if5Cl0Ei.js.map +1 -0
  210. package/dist/shared/{hx-top-nav-CsTxOtVI.js → hx-top-nav-vP6oDWMV.js} +24 -38
  211. package/dist/shared/hx-top-nav-vP6oDWMV.js.map +1 -0
  212. package/dist/shared/{hx-tree-item-CXyspGxI.js → hx-tree-item-D8hwKd5m.js} +54 -57
  213. package/dist/shared/hx-tree-item-D8hwKd5m.js.map +1 -0
  214. package/dist/shared/{toast-factory-Dht3pVsw.js → toast-factory-DgnbFxVs.js} +127 -153
  215. package/dist/shared/toast-factory-DgnbFxVs.js.map +1 -0
  216. package/figma-inventory.json +283 -304
  217. package/package.json +8 -4
  218. package/dist/shared/hx-accordion-ZVzgDzTG.js.map +0 -1
  219. package/dist/shared/hx-alert-Bto8-TIi.js.map +0 -1
  220. package/dist/shared/hx-avatar-C9hOmlAb.js.map +0 -1
  221. package/dist/shared/hx-badge-DFL35nzi.js.map +0 -1
  222. package/dist/shared/hx-banner-fpRnciIO.js.map +0 -1
  223. package/dist/shared/hx-carousel-item-z1Lc24op.js.map +0 -1
  224. package/dist/shared/hx-checkbox-DcgyGS9V.js.map +0 -1
  225. package/dist/shared/hx-clinical-status-D3XQIOqX.js.map +0 -1
  226. package/dist/shared/hx-combobox-NgJaLbs2.js.map +0 -1
  227. package/dist/shared/hx-date-picker-0PtEav0K.js.map +0 -1
  228. package/dist/shared/hx-drawer-CM_upadk.js.map +0 -1
  229. package/dist/shared/hx-dropdown-xHwTJecv.js.map +0 -1
  230. package/dist/shared/hx-file-upload-D3rKROK5.js.map +0 -1
  231. package/dist/shared/hx-help-text-Xb2Yr8x2.js +0 -156
  232. package/dist/shared/hx-help-text-Xb2Yr8x2.js.map +0 -1
  233. package/dist/shared/hx-icon-fuVm4-bk.js +0 -283
  234. package/dist/shared/hx-icon-fuVm4-bk.js.map +0 -1
  235. package/dist/shared/hx-link-CMnZRUtQ.js.map +0 -1
  236. package/dist/shared/hx-menu-divider-A6Guuzi_.js.map +0 -1
  237. package/dist/shared/hx-nav-ChMTfn7o.js.map +0 -1
  238. package/dist/shared/hx-nav-item-ClN17f1y.js.map +0 -1
  239. package/dist/shared/hx-number-input-MggsT7F0.js.map +0 -1
  240. package/dist/shared/hx-overflow-menu-DFjJAziP.js.map +0 -1
  241. package/dist/shared/hx-phi-field-C19oxlrr.js.map +0 -1
  242. package/dist/shared/hx-rating-C3QP53k9.js.map +0 -1
  243. package/dist/shared/hx-split-button-CdNz1XAu.js.map +0 -1
  244. package/dist/shared/hx-stat-Gtw_SpK8.js.map +0 -1
  245. package/dist/shared/hx-step-CUzliIK_.js.map +0 -1
  246. package/dist/shared/hx-tag-C5aCUpVi.js.map +0 -1
  247. package/dist/shared/hx-time-picker-DfJkBwcX.js.map +0 -1
  248. package/dist/shared/hx-top-nav-CsTxOtVI.js.map +0 -1
  249. package/dist/shared/hx-tree-item-CXyspGxI.js.map +0 -1
  250. package/dist/shared/toast-factory-Dht3pVsw.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-help-text-CNaZ82LT.js","sources":["../../src/components/hx-help-text/hx-help-text.styles.ts","../../src/components/hx-help-text/hx-help-text.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixHelpTextStyles = css`\n :host {\n display: block;\n }\n\n .help-text {\n display: inline-flex;\n align-items: baseline;\n gap: var(--hx-help-text-icon-gap, 0.375rem);\n font-family: var(--hx-help-text-font-family, var(--hx-font-family-sans, sans-serif));\n font-size: var(--hx-help-text-font-size, var(--hx-font-size-sm, 0.875rem));\n font-weight: var(--hx-help-text-font-weight, var(--hx-font-weight-normal, 400));\n line-height: var(--hx-help-text-line-height, var(--hx-line-height-normal, 1.5));\n color: var(--hx-help-text-color, var(--hx-color-text-muted, #4a5362));\n margin: 0;\n }\n\n .help-text__icon {\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n }\n\n /* Render the migrated hx-icon glyph at 1em (matches the previous inline SVG sizing). */\n .help-text__glyph {\n --hx-icon-size: 1em;\n }\n\n .help-text__text {\n min-width: 0;\n }\n\n /* ─── Variant: default ─── */\n\n .help-text--default {\n --hx-help-text-color: var(--hx-color-text-muted, #4a5362);\n }\n\n /* ─── Variant: error ─── */\n\n .help-text--error {\n --hx-help-text-color: var(--hx-color-error-600, #c92a2a);\n }\n\n /* ─── Variant: warning ─── */\n\n .help-text--warning {\n --hx-help-text-color: var(--hx-color-warning-700, #804605);\n }\n\n /* ─── Variant: success ─── */\n\n .help-text--success {\n --hx-help-text-color: var(--hx-color-success-700, #146831);\n }\n\n /* ─── High Contrast Mode (forced-colors) ─── */\n\n @media (forced-colors: active) {\n .help-text {\n color: GrayText;\n }\n\n .help-text--error {\n color: LinkText;\n }\n\n .help-text--warning {\n color: CanvasText;\n }\n\n .help-text--success {\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 { classMap } from 'lit/directives/class-map.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport '../hx-icon/hx-icon.js';\nimport { HelixElement } from '../../base/index.js';\nimport { helixHelpTextStyles } from './hx-help-text.styles.js';\nimport { forcedColorsSurface } from '../../styles/forced-colors.js';\n\n/** Icon for error variant. */\nconst errorIcon = html`<hx-icon\n class=\"help-text__glyph\"\n library=\"helix\"\n name=\"error\"\n aria-hidden=\"true\"\n></hx-icon>`;\n\n/** Icon for warning variant. */\nconst warningIcon = html`<hx-icon\n class=\"help-text__glyph\"\n library=\"helix\"\n name=\"warning\"\n aria-hidden=\"true\"\n></hx-icon>`;\n\n/** Icon for success variant. */\nconst successIcon = html`<hx-icon\n class=\"help-text__glyph\"\n library=\"helix\"\n name=\"success\"\n aria-hidden=\"true\"\n></hx-icon>`;\n\n/** Map of variant to icon template. Default has no icon. */\nconst variantIcons = {\n default: nothing,\n error: errorIcon,\n warning: warningIcon,\n success: successIcon,\n} as const;\n\n/**\n * Standardized help/hint text displayed below form fields.\n * Used by hx-field as a consistent sub-component for guidance and validation messages.\n *\n * Non-default variants render an inline icon alongside the text to satisfy\n * WCAG 1.4.1 (color is not the sole visual indicator). The `error` variant\n * uses `role=\"alert\"` for immediate screen-reader announcement; `warning`\n * and `success` use `aria-live=\"polite\"` for non-intrusive announcements.\n *\n * @summary Help text displayed below form controls for guidance or validation feedback.\n *\n * @tag hx-help-text\n *\n * @slot - The help text content.\n *\n * @csspart base - The root element of the help text.\n * @csspart icon - The icon wrapper (only rendered for non-default variants).\n * @csspart text - The text wrapper around the default slot.\n *\n * @cssprop [--hx-help-text-color=var(--hx-color-neutral-500)] - Text color.\n * @cssprop [--hx-help-text-font-family=var(--hx-font-family-sans)] - Font family.\n * @cssprop [--hx-help-text-font-size=var(--hx-font-size-sm)] - Font size.\n * @cssprop [--hx-help-text-font-weight=var(--hx-font-weight-normal)] - Font weight.\n * @cssprop [--hx-help-text-line-height=var(--hx-line-height-normal)] - Line height.\n * @cssprop [--hx-help-text-icon-gap=0.375rem] - Gap between icon and text.\n * @cssprop [--hx-font-family-sans] - Font family.\n * @cssprop [--hx-font-size-sm] - Font size.\n * @cssprop [--hx-font-weight-normal] - Font weight.\n * @cssprop [--hx-line-height-normal] - Line height.\n * @cssprop [--hx-color-neutral-500] - Color.\n * @cssprop [--hx-color-error-600] - Color.\n * @cssprop [--hx-color-warning-700] - Color.\n * @cssprop [--hx-color-success-700] - Color.\n *\n * @aaa-certified 2026-05-09\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-help-text/AAA-AUDIT.md\n * @aria-pattern label\n * @forced-colors-supported true\n * @stability stable\n * @since 3.7.0\n * @form-associated false\n * @theme-aware true\n * @brand-aware true\n * @drupal-sdc-eligible true\n * @react-wrapper-status complete\n * @figma-component-name hx-help-text\n * @priority-tier P0\n * @phi-handles false\n * @clinical-context none\n */\n@customElement('hx-help-text')\nexport class HelixHelpText extends HelixElement {\n static override styles = [helixHelpTextStyles, forcedColorsSurface];\n\n /**\n * Visual variant that determines the text color and icon.\n * Use `error` for validation errors, `warning` for cautions, `success` for confirmation.\n * @attr variant\n */\n @property({ type: String, reflect: true })\n variant: 'default' | 'error' | 'warning' | 'success' = 'default';\n\n override render() {\n const classes = {\n 'help-text': true,\n [`help-text--${this.variant}`]: true,\n };\n\n const icon = variantIcons[this.variant];\n const role = this.variant === 'error' ? 'alert' : undefined;\n const ariaLive =\n this.variant === 'warning' || this.variant === 'success' ? 'polite' : undefined;\n\n return html`<span\n part=\"base\"\n class=${classMap(classes)}\n role=${ifDefined(role)}\n aria-live=${ifDefined(ariaLive)}\n >${icon !== nothing\n ? html`<span part=\"icon\" class=\"help-text__icon\">${icon}</span>`\n : nothing}<span part=\"text\" class=\"help-text__text\"><slot></slot></span>\n </span>`;\n }\n}\n\n/** Canonical type alias for the hx-help-text component. */\nexport type HxHelpText = HelixHelpText;\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-help-text': HelixHelpText;\n }\n}\n"],"names":["helixHelpTextStyles","css","errorIcon","html","warningIcon","successIcon","variantIcons","nothing","HelixHelpText","HelixElement","classes","icon","role","ariaLive","classMap","ifDefined","forcedColorsSurface","__decorateClass","property","customElement"],"mappings":";;;;;;AAEO,MAAMA,IAAsBC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;;;;ACSnC,MAAMC,IAAYC;AAAA;AAAA;AAAA;AAAA;AAAA,cAQZC,IAAcD;AAAA;AAAA;AAAA;AAAA;AAAA,cAQdE,IAAcF;AAAA;AAAA;AAAA;AAAA;AAAA,cAQdG,IAAe;AAAA,EACnB,SAASC;AAAA,EACT,OAAOL;AAAA,EACP,SAASE;AAAA,EACT,SAASC;AACX;AAsDO,IAAMG,IAAN,cAA4BC,EAAa;AAAA,EAAzC,cAAA;AAAA,UAAA,GAAA,SAAA,GASL,KAAA,UAAuD;AAAA,EAAA;AAAA,EAE9C,SAAS;AAChB,UAAMC,IAAU;AAAA,MACd,aAAa;AAAA,MACb,CAAC,cAAc,KAAK,OAAO,EAAE,GAAG;AAAA,IAAA,GAG5BC,IAAOL,EAAa,KAAK,OAAO,GAChCM,IAAO,KAAK,YAAY,UAAU,UAAU,QAC5CC,IACJ,KAAK,YAAY,aAAa,KAAK,YAAY,YAAY,WAAW;AAExE,WAAOV;AAAA;AAAA,cAEGW,EAASJ,CAAO,CAAC;AAAA,aAClBK,EAAUH,CAAI,CAAC;AAAA,kBACVG,EAAUF,CAAQ,CAAC;AAAA,SAC5BF,MAASJ,IACRJ,8CAAiDQ,CAAI,YACrDJ,CAAO;AAAA;AAAA,EAEf;AACF;AAhCaC,EACK,SAAS,CAACR,GAAqBgB,CAAmB;AAQlEC,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM;AAAA,GAR9BV,EASX,WAAA,WAAA,CAAA;AATWA,IAANS,EAAA;AAAA,EADNE,EAAc,cAAc;AAAA,GAChBX,CAAA;"}
@@ -0,0 +1,386 @@
1
+ import { css as b, nothing as a, html as v } from "lit";
2
+ import { property as c, state as y, customElement as _ } from "lit/decorators.js";
3
+ import { unsafeHTML as w } from "lit/directives/unsafe-html.js";
4
+ import { getIconLibrary as S } from "@helixui/icons";
5
+ import { a as x } from "./forced-colors-CTEDFRGa.js";
6
+ import { H as L } from "./helix-element-BNEYeiys.js";
7
+ const k = b`
8
+ :host {
9
+ display: inline-flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+ /* vertical-align: middle ensures the icon aligns to the visual centre of
13
+ adjacent inline text rather than the text baseline. */
14
+ vertical-align: middle;
15
+ /* overflow: hidden prevents malformed or oversized icons from painting
16
+ outside the component boundary. The inner SVG may still set
17
+ overflow: visible for its own viewBox content. */
18
+ overflow: hidden;
19
+ width: var(--hx-icon-size, var(--hx-size-6, 1.5rem));
20
+ height: var(--hx-icon-size, var(--hx-size-6, 1.5rem));
21
+ color: var(--hx-icon-color, currentColor);
22
+ flex-shrink: 0;
23
+ }
24
+
25
+ /* ─── Size Variants ───
26
+ Fallback pixel values mirror the design token values at time of writing.
27
+ If token values are updated the fallbacks should be updated to match. */
28
+
29
+ :host([hx-size='xs']) {
30
+ --hx-icon-size: var(--hx-size-4, 1rem);
31
+ }
32
+
33
+ :host([hx-size='sm']) {
34
+ --hx-icon-size: var(--hx-size-5, 1.25rem);
35
+ }
36
+
37
+ :host([hx-size='md']) {
38
+ --hx-icon-size: var(--hx-size-6, 1.5rem);
39
+ }
40
+
41
+ :host([hx-size='lg']) {
42
+ --hx-icon-size: var(--hx-size-8, 2rem);
43
+ }
44
+
45
+ :host([hx-size='xl']) {
46
+ --hx-icon-size: var(--hx-size-10, 2.5rem);
47
+ }
48
+
49
+ /* ─── SVG (sprite mode) ───
50
+ In sprite mode [part="svg"] is an actual <svg> element. The selector
51
+ targets it specifically. In inline mode the part is applied to a <span>
52
+ wrapper — see .icon__inline below. */
53
+
54
+ svg[part='svg'] {
55
+ width: 100%;
56
+ height: 100%;
57
+ fill: currentColor;
58
+ /* stroke-width is consumed by stroke-paint and mixed-paint icon libraries
59
+ (Lucide, Heroicons-outline, Phosphor). Built-in helix + fa-free
60
+ libraries declare paintMode: 'fill' and ignore this property; setting
61
+ it here makes the token universally available without per-library
62
+ branching in the resolver. */
63
+ stroke-width: var(--hx-icon-stroke-width, 2);
64
+ display: block;
65
+ overflow: visible;
66
+ }
67
+
68
+ /* ─── Inline SVG wrapper ───
69
+ In inline mode [part="svg"] is a <span> that wraps the fetched SVG.
70
+ The inner <svg> is sized to fill the wrapper. */
71
+
72
+ .icon__inline {
73
+ display: inline-flex;
74
+ align-items: center;
75
+ justify-content: center;
76
+ width: 100%;
77
+ height: 100%;
78
+ }
79
+
80
+ .icon__inline svg {
81
+ width: 100%;
82
+ height: 100%;
83
+ fill: currentColor;
84
+ stroke-width: var(--hx-icon-stroke-width, 2);
85
+ display: block;
86
+ }
87
+
88
+ /* ─── Forced Colors (Windows High Contrast) ─── */
89
+
90
+ @media (forced-colors: active) {
91
+ :host {
92
+ color: CanvasText;
93
+ }
94
+ }
95
+ `;
96
+ var z = Object.defineProperty, $ = Object.getOwnPropertyDescriptor, o = (e, t, r, n) => {
97
+ for (var i = n > 1 ? void 0 : n ? $(t, r) : t, l = e.length - 1, d; l >= 0; l--)
98
+ (d = e[l]) && (i = (n ? d(t, r, i) : d(i)) || i);
99
+ return n && i && z(t, r, i), i;
100
+ };
101
+ let s = class extends L {
102
+ constructor() {
103
+ super(...arguments), this.name = "", this.library = "", this.src = void 0, this.spriteUrl = void 0, this.size = "md", this.label = "", this.allowedOrigins = "", this._inlineSvg = "", this._fetchedSrc = void 0, this._fetchedLibraryKey = void 0, this._fetchSeq = 0, this._onLibraryRegistered = (e) => {
104
+ const t = e.detail;
105
+ (!t || t.name === this.library) && (this._fetchedLibraryKey = void 0, this.requestUpdate(), this._maybeFetchLibraryIcon());
106
+ }, this._onBasePathChanged = () => {
107
+ this._fetchedLibraryKey = void 0, this.requestUpdate(), this._maybeFetchLibraryIcon();
108
+ };
109
+ }
110
+ connectedCallback() {
111
+ super.connectedCallback(), typeof globalThis.addEventListener == "function" && (globalThis.addEventListener("helixicon-library-registered", this._onLibraryRegistered), globalThis.addEventListener("helixicon-base-path-changed", this._onBasePathChanged));
112
+ }
113
+ disconnectedCallback() {
114
+ typeof globalThis.removeEventListener == "function" && (globalThis.removeEventListener("helixicon-library-registered", this._onLibraryRegistered), globalThis.removeEventListener("helixicon-base-path-changed", this._onBasePathChanged)), super.disconnectedCallback();
115
+ }
116
+ updated(e) {
117
+ super.updated(e), e.has("src") && this.src !== this._fetchedSrc && (this._fetchedLibraryKey = void 0, this._fetchInlineSvg(this.src));
118
+ const t = typeof this.src == "string" && this.src.trim().length > 0, r = typeof this.spriteUrl == "string" && this.spriteUrl.length > 0;
119
+ !t && !r && (e.has("library") || e.has("name") || e.has("src") || e.has("spriteUrl")) && this._maybeFetchLibraryIcon();
120
+ }
121
+ // ─── Inline SVG Fetch ───
122
+ /** @internal */
123
+ async _fetchInlineSvg(e) {
124
+ const t = ++this._fetchSeq;
125
+ if (!e) {
126
+ this._inlineSvg = "", this._fetchedSrc = void 0;
127
+ return;
128
+ }
129
+ if (!this._isAllowedOrigin(e)) {
130
+ console.warn(
131
+ `[hx-icon] Blocked cross-origin SVG fetch: "${e}". Only same-origin URLs are allowed by default. Set the allowed-origins attribute to permit specific external origins.`
132
+ ), this._inlineSvg = "", this._fetchedSrc = void 0;
133
+ return;
134
+ }
135
+ try {
136
+ let r = p.get(e);
137
+ r || (r = fetch(e).then(async (l) => l.ok ? l.text() : (p.delete(e), "")), p.set(e, r));
138
+ const n = await r;
139
+ if (t !== this._fetchSeq) return;
140
+ if (!n) {
141
+ this._inlineSvg = "", this._fetchedSrc = void 0;
142
+ return;
143
+ }
144
+ const i = this._sanitizeSvg(n);
145
+ this._inlineSvg = i, this._fetchedSrc = e;
146
+ } catch {
147
+ if (t !== this._fetchSeq) return;
148
+ p.delete(e), this._inlineSvg = "", this._fetchedSrc = void 0;
149
+ }
150
+ }
151
+ /**
152
+ * Checks whether a URL is same-origin or matches the configured allowedOrigins.
153
+ * Relative URLs and data: URIs are always allowed. Cross-origin URLs are blocked
154
+ * unless their origin appears in the allowedOrigins list.
155
+ * @internal
156
+ */
157
+ _isAllowedOrigin(e) {
158
+ if (e.startsWith("/") || e.startsWith("./") || e.startsWith("../"))
159
+ return !0;
160
+ try {
161
+ const t = new URL(e, window.location.href);
162
+ return t.origin === window.location.origin ? !0 : this.allowedOrigins ? this.allowedOrigins.split(",").map((n) => n.trim().toLowerCase()).filter(Boolean).includes(t.origin.toLowerCase()) : !1;
163
+ } catch {
164
+ return !1;
165
+ }
166
+ }
167
+ /**
168
+ * Parses the raw SVG text, strips dangerous content (script elements,
169
+ * foreignObject, on* event-handler attributes, javascript:/data: URIs,
170
+ * and style attributes that could carry CSS injection payloads), and
171
+ * returns the outer SVG markup safe for rendering via `unsafeHTML`.
172
+ *
173
+ * Additionally injects `focusable="false"` on the root SVG element to
174
+ * prevent IE11/old-Edge from making the SVG keyboard-focusable, and strips
175
+ * any ARIA attributes from the inner SVG to prevent conflicts with the
176
+ * wrapper's own ARIA semantics.
177
+ */
178
+ /** @internal */
179
+ _sanitizeSvg(e) {
180
+ const r = new DOMParser().parseFromString(e, "image/svg+xml");
181
+ if (r.querySelector("parsererror"))
182
+ return "";
183
+ const i = r.querySelector("svg");
184
+ if (!i)
185
+ return "";
186
+ const l = [
187
+ "script",
188
+ "foreignObject",
189
+ "style",
190
+ "animate",
191
+ "animateTransform",
192
+ "animateMotion",
193
+ "set"
194
+ ];
195
+ i.querySelectorAll(l.join(", ")).forEach((h) => {
196
+ h.remove();
197
+ });
198
+ const d = /* @__PURE__ */ new Set(["href", "xlink:href", "src", "action", "formaction"]), g = /* @__PURE__ */ new Set(["role", "aria-label", "aria-labelledby", "aria-hidden"]);
199
+ return [i, ...Array.from(i.querySelectorAll("*"))].forEach((h) => {
200
+ Array.from(h.attributes).forEach((f) => {
201
+ const u = f.name.toLowerCase();
202
+ if (u.startsWith("on")) {
203
+ h.removeAttribute(f.name);
204
+ return;
205
+ }
206
+ if (u === "style") {
207
+ h.removeAttribute(f.name);
208
+ return;
209
+ }
210
+ if (d.has(u)) {
211
+ const m = f.value.replace(/\s/g, "").toLowerCase();
212
+ (m.startsWith("javascript:") || m.startsWith("data:")) && h.removeAttribute(f.name);
213
+ }
214
+ });
215
+ }), g.forEach((h) => i.removeAttribute(h)), i.setAttribute("focusable", "false"), i.outerHTML;
216
+ }
217
+ // ─── Registry Resolution ───
218
+ /**
219
+ * Looks up the configured library in the `@helixui/icons` registry and
220
+ * returns either:
221
+ * - `{ kind: 'sprite', href }` for sprite-sheet libraries (the default
222
+ * `helix` and `fa-free` bundles)
223
+ * - `{ kind: 'inline', url, library }` for fetch-mode libraries that may
224
+ * also carry a post-sanitize mutator
225
+ * - `null` if the library is unknown (warning logged once per unique
226
+ * library+name pair)
227
+ * @internal
228
+ */
229
+ _resolveLibraryHref() {
230
+ const e = S(this.library);
231
+ if (!e) {
232
+ if (this.library === "") return null;
233
+ const r = `${this.library} ${this.name}`;
234
+ return s._unknownLibraryWarned.has(r) || (s._unknownLibraryWarned.add(r), console.warn(
235
+ `[hx-icon] Unknown icon library "${this.library}" requested for name "${this.name}". Register it via registerIconLibrary() from @helixui/icons before use.`
236
+ )), null;
237
+ }
238
+ let t;
239
+ try {
240
+ t = e.resolver(this.name);
241
+ } catch (r) {
242
+ return console.warn(
243
+ `[hx-icon] Resolver for library "${this.library}" threw on name "${this.name}":`,
244
+ r
245
+ ), null;
246
+ }
247
+ return typeof t != "string" || t.length === 0 ? null : e.spriteSheet ? { kind: "sprite", href: t } : { kind: "inline", url: t, library: e };
248
+ }
249
+ /**
250
+ * Drives the inline-fetch path for registry libraries that declare
251
+ * `spriteSheet: false`. Reuses `_fetchInlineSvg` for security sanitization
252
+ * and the `_svgCache` module-level dedupe, then layers the optional
253
+ * library mutator on top of the sanitized markup.
254
+ * @internal
255
+ */
256
+ async _maybeFetchLibraryIcon() {
257
+ const e = this._resolveLibraryHref();
258
+ if (!e || e.kind !== "inline") {
259
+ this._fetchedLibraryKey = void 0, this.src || (this._inlineSvg = "");
260
+ return;
261
+ }
262
+ const t = `${this.library} ${this.name} ${e.url}`;
263
+ this._fetchedLibraryKey !== t && (this._fetchedLibraryKey = t, await this._fetchInlineSvg(e.url), this._fetchedLibraryKey === t && e.library.mutator && this._inlineSvg && (this._inlineSvg = this._applyLibraryMutator(this._inlineSvg, e.library)));
264
+ }
265
+ /**
266
+ * Re-parses sanitized SVG markup, runs the library's mutator on the root
267
+ * SVGElement, and re-serializes. Sanitization always runs FIRST in
268
+ * `_fetchInlineSvg`; the mutator never sees raw consumer SVG.
269
+ * @internal
270
+ */
271
+ _applyLibraryMutator(e, t) {
272
+ if (!t.mutator) return e;
273
+ try {
274
+ const i = new DOMParser().parseFromString(e, "image/svg+xml").querySelector("svg");
275
+ return i ? (t.mutator(i), i.outerHTML) : e;
276
+ } catch (r) {
277
+ return console.warn(
278
+ `[hx-icon] Mutator for library "${t.name}" threw on name "${this.name}"; rendering un-mutated SVG.`,
279
+ r
280
+ ), e;
281
+ }
282
+ }
283
+ // ─── Render Helpers ───
284
+ /**
285
+ * Returns the href used in the `<use>` element for explicit sprite mode.
286
+ * If `name` already begins with `#` it is treated as an inline reference.
287
+ */
288
+ /** @internal */
289
+ _spriteHref() {
290
+ const e = this.name;
291
+ if (e.startsWith("#"))
292
+ return e;
293
+ const t = this.spriteUrl ?? "";
294
+ return t ? `${t}#${e}` : `#${e}`;
295
+ }
296
+ /** @internal */
297
+ _renderSprite(e) {
298
+ const t = !this.label.trim(), r = e ?? this._spriteHref();
299
+ return v`
300
+ <svg
301
+ part="svg"
302
+ class="icon__svg"
303
+ viewBox="0 0 24 24"
304
+ xmlns="http://www.w3.org/2000/svg"
305
+ role=${t ? a : "img"}
306
+ aria-label=${t ? a : this.label}
307
+ aria-hidden=${t ? "true" : a}
308
+ focusable="false"
309
+ >
310
+ ${t ? a : v`<title>${this.label}</title>`}
311
+ <use href=${r}></use>
312
+ </svg>
313
+ `;
314
+ }
315
+ /** @internal */
316
+ _renderInline() {
317
+ if (!this._inlineSvg)
318
+ return a;
319
+ const e = !this.label.trim();
320
+ return v`
321
+ <span
322
+ part="svg"
323
+ class="icon__inline"
324
+ role=${e ? a : "img"}
325
+ aria-label=${e ? a : this.label}
326
+ aria-hidden=${e ? "true" : a}
327
+ >
328
+ ${w(this._inlineSvg)}
329
+ </span>
330
+ `;
331
+ }
332
+ // ─── Render ───
333
+ render() {
334
+ if (typeof this.src == "string" && this.src.trim().length > 0)
335
+ return this._renderInline();
336
+ if (typeof this.spriteUrl == "string" && this.name)
337
+ return this._renderSprite();
338
+ if (this.name.startsWith("#"))
339
+ return this._renderSprite();
340
+ if (this.name && this.library) {
341
+ const e = this._resolveLibraryHref();
342
+ return e ? e.kind === "sprite" ? this._renderSprite(e.href) : this._renderInline() : a;
343
+ }
344
+ return this.name ? this._renderSprite() : a;
345
+ }
346
+ };
347
+ s.styles = [k, x];
348
+ s._unknownLibraryWarned = /* @__PURE__ */ new Set();
349
+ o([
350
+ c({ type: String })
351
+ ], s.prototype, "name", 2);
352
+ o([
353
+ c({ type: String, reflect: !0 })
354
+ ], s.prototype, "library", 2);
355
+ o([
356
+ c({ type: String })
357
+ ], s.prototype, "src", 2);
358
+ o([
359
+ c({ type: String, attribute: "sprite-url" })
360
+ ], s.prototype, "spriteUrl", 2);
361
+ o([
362
+ c({ type: String, reflect: !0, attribute: "hx-size" })
363
+ ], s.prototype, "size", 2);
364
+ o([
365
+ c({ type: String })
366
+ ], s.prototype, "label", 2);
367
+ o([
368
+ c({ type: String, attribute: "allowed-origins" })
369
+ ], s.prototype, "allowedOrigins", 2);
370
+ o([
371
+ y()
372
+ ], s.prototype, "_inlineSvg", 2);
373
+ o([
374
+ y()
375
+ ], s.prototype, "_fetchedSrc", 2);
376
+ o([
377
+ y()
378
+ ], s.prototype, "_fetchedLibraryKey", 2);
379
+ s = o([
380
+ _("hx-icon")
381
+ ], s);
382
+ const p = /* @__PURE__ */ new Map();
383
+ export {
384
+ s as H
385
+ };
386
+ //# sourceMappingURL=hx-icon-bxz9eB9a.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-icon-bxz9eB9a.js","sources":["../../src/components/hx-icon/hx-icon.styles.ts","../../src/components/hx-icon/hx-icon.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixIconStyles = css`\n :host {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n /* vertical-align: middle ensures the icon aligns to the visual centre of\n adjacent inline text rather than the text baseline. */\n vertical-align: middle;\n /* overflow: hidden prevents malformed or oversized icons from painting\n outside the component boundary. The inner SVG may still set\n overflow: visible for its own viewBox content. */\n overflow: hidden;\n width: var(--hx-icon-size, var(--hx-size-6, 1.5rem));\n height: var(--hx-icon-size, var(--hx-size-6, 1.5rem));\n color: var(--hx-icon-color, currentColor);\n flex-shrink: 0;\n }\n\n /* ─── Size Variants ───\n Fallback pixel values mirror the design token values at time of writing.\n If token values are updated the fallbacks should be updated to match. */\n\n :host([hx-size='xs']) {\n --hx-icon-size: var(--hx-size-4, 1rem);\n }\n\n :host([hx-size='sm']) {\n --hx-icon-size: var(--hx-size-5, 1.25rem);\n }\n\n :host([hx-size='md']) {\n --hx-icon-size: var(--hx-size-6, 1.5rem);\n }\n\n :host([hx-size='lg']) {\n --hx-icon-size: var(--hx-size-8, 2rem);\n }\n\n :host([hx-size='xl']) {\n --hx-icon-size: var(--hx-size-10, 2.5rem);\n }\n\n /* ─── SVG (sprite mode) ───\n In sprite mode [part=\"svg\"] is an actual <svg> element. The selector\n targets it specifically. In inline mode the part is applied to a <span>\n wrapper — see .icon__inline below. */\n\n svg[part='svg'] {\n width: 100%;\n height: 100%;\n fill: currentColor;\n /* stroke-width is consumed by stroke-paint and mixed-paint icon libraries\n (Lucide, Heroicons-outline, Phosphor). Built-in helix + fa-free\n libraries declare paintMode: 'fill' and ignore this property; setting\n it here makes the token universally available without per-library\n branching in the resolver. */\n stroke-width: var(--hx-icon-stroke-width, 2);\n display: block;\n overflow: visible;\n }\n\n /* ─── Inline SVG wrapper ───\n In inline mode [part=\"svg\"] is a <span> that wraps the fetched SVG.\n The inner <svg> is sized to fill the wrapper. */\n\n .icon__inline {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n }\n\n .icon__inline svg {\n width: 100%;\n height: 100%;\n fill: currentColor;\n stroke-width: var(--hx-icon-stroke-width, 2);\n display: block;\n }\n\n /* ─── Forced Colors (Windows High Contrast) ─── */\n\n @media (forced-colors: active) {\n :host {\n color: CanvasText;\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 { unsafeHTML } from 'lit/directives/unsafe-html.js';\nimport { getIconLibrary } from '@helixui/icons';\nimport type { IconLibrary } from '@helixui/icons';\nimport { HelixElement } from '../../base/index.js';\nimport { helixIconStyles } from './hx-icon.styles.js';\nimport { forcedColorsSurface } from '../../styles/forced-colors.js';\n\n/**\n * An icon component that resolves through the `@helixui/icons` registry,\n * with sprite-sheet and inline-fetch escape hatches for legacy / consumer\n * patterns. Decorative icons are automatically hidden from assistive\n * technology. When a label is provided the icon is announced as an image\n * with that label.\n *\n * **Render modes (in resolution order):**\n * 1. **Inline fetch (`src`)** — Set `src` to a URL of a standalone SVG file.\n * The component fetches, sanitizes, and embeds the SVG markup. Requires\n * JavaScript; not server-side renderable. Library attribute is ignored.\n * 2. **Explicit sprite (`sprite-url` + `name`)** — Pin the sprite URL on the\n * element instead of resolving through the registry. Library attribute is\n * ignored.\n * 3. **Registry (`library` + `name`)** (recommended) — Resolves through the\n * `@helixui/icons` registry. Set `library=\"fa-free\"` for the FA Free Solid\n * set bundled with `@helixui/icons`, or `library=\"helix\"` for the curated\n * 32-glyph helix set. Consumer libraries register via `registerIconLibrary()`\n * and become resolvable here without modifying the component.\n * 4. **Bare name (`name` only, no `library`)** — `library` defaults to the\n * empty string. The component emits `<use href=\"#name\">` against a\n * document-local sprite — pre-3.9.0 back-compat behavior. Consumers\n * opt INTO registry resolution by setting `library` explicitly.\n * 5. **No name / no src** — Renders nothing.\n *\n * @summary SVG icon with registry resolution + sprite and inline-fetch escape hatches.\n *\n * @tag hx-icon\n *\n * @csspart svg - The SVG element rendered in sprite mode, or the inline SVG container\n * in inline mode. In sprite mode this is an `<svg>` element; in inline mode it is a\n * `<span>` element wrapping the fetched SVG. Both expose the same `part` name for\n * consistent external styling via `::part(svg)`.\n *\n * @cssprop [--hx-icon-size=var(--hx-size-6,1.5rem)] - Width and height of the icon.\n * @cssprop [--hx-icon-color=currentColor] - Icon color.\n * @cssprop [--hx-icon-stroke-width=2] - Default stroke width consumed by stroke-paint and mixed-paint icon libraries (Lucide, Heroicons-outline, Phosphor). Built-in helix + fa-free libraries are fill-only and ignore this token.\n * @cssprop [--hx-size-4] - Size token.\n * @cssprop [--hx-size-5] - Size token.\n * @cssprop [--hx-size-6] - Size token.\n * @cssprop [--hx-size-8] - Size token.\n * @cssprop [--hx-size-10] - Size token.\n *\n * @aaa-certified 2026-05-10\n * @aaa-criteria 1.4.9,3.2.5,forced-colors,apg-keyboard,non-text-contrast-icon\n * @aaa-audit src/components/hx-icon/AAA-AUDIT.md\n * @aria-pattern none\n * @forced-colors-supported true\n * @stability stable\n * @since 3.9.0\n * @priority-tier P0\n */\n@customElement('hx-icon')\nexport class HelixIcon extends HelixElement {\n static override styles = [helixIconStyles, forcedColorsSurface];\n\n /**\n * Icon name used as the fragment identifier when referencing a sprite sheet.\n * For sprite mode provide the bare symbol id (e.g. `check`). The component\n * will build the full href as `${spriteUrl}#${name}`. If `name` already\n * starts with `#` it is used as-is (inline sprite reference without a base\n * URL).\n * @attr name\n */\n @property({ type: String })\n name = '';\n\n /**\n * Identifier of the icon library to resolve `name` through. Default is\n * the empty string — meaning bare `<hx-icon name=\"foo\">` continues to\n * render a document-local sprite fragment (`<use href=\"#foo\">`) per the\n * pre-3.9.0 contract. Set explicitly to `'fa-free'` for the FA Free Solid\n * set bundled with `@helixui/icons`, `'helix'` for the curated 32-glyph\n * helix set, or to any consumer-registered library identifier.\n *\n * Library resolution applies only when `src` is empty AND `sprite-url` is\n * not set; the inline-fetch and explicit-sprite paths are kept as escape\n * hatches and ignore the library attribute.\n * @attr library\n */\n @property({ type: String, reflect: true })\n library: string = '';\n\n /**\n * URL of a standalone SVG file to fetch and render inline. Takes precedence\n * over sprite mode when both `src` and `spriteUrl`/`name` are set.\n *\n * **Note:** Inline mode requires browser JavaScript (`fetch` + `DOMParser`).\n * It is not server-side renderable. For Drupal/Twig use sprite mode instead.\n * @attr src\n */\n @property({ type: String })\n src: string | undefined = undefined;\n\n /**\n * Base URL of the SVG sprite sheet. Used together with `name` to construct\n * the `<use>` href: `${spriteUrl}#${name}`.\n * @attr sprite-url\n */\n @property({ type: String, attribute: 'sprite-url' })\n spriteUrl: string | undefined = undefined;\n\n /**\n * Size variant of the icon.\n *\n * Set via the `hx-size` HTML attribute (e.g. `hx-size=\"lg\"`) or via the\n * `size` JavaScript property (e.g. `el.size = 'lg'`). Both are equivalent —\n * the `attribute: 'hx-size'` mapping is used to avoid colliding with the\n * native HTMLInputElement `size` attribute in Drupal attribute-passthrough\n * scenarios.\n * The CEM exposes both the JS property name (`size`) and the HTML attribute\n * name (`hx-size`).\n *\n * @attr hx-size\n */\n @property({ type: String, reflect: true, attribute: 'hx-size' })\n size: 'xs' | 'sm' | 'md' | 'lg' | 'xl' = 'md';\n\n /**\n * Accessible label for the icon. When non-empty, `role=\"img\"` and\n * `aria-label` are applied so assistive technology announces the icon.\n * When empty the icon is treated as decorative and `aria-hidden=\"true\"` is\n * applied.\n * @attr label\n */\n @property({ type: String })\n label = '';\n\n /**\n * Comma-separated list of allowed origins for cross-origin SVG fetches.\n * By default, only same-origin URLs are permitted. Set this to allow\n * specific CDN or asset server origins (e.g., \"https://cdn.example.com,https://assets.example.com\").\n * @attr allowed-origins\n */\n @property({ type: String, attribute: 'allowed-origins' })\n allowedOrigins = '';\n\n /**\n * Stores the sanitized inner markup of an externally fetched SVG.\n * @internal\n */\n @state()\n private _inlineSvg = '';\n\n /**\n * Tracks the `src` URL that was last successfully fetched so that we only\n * refetch when it genuinely changes.\n * @internal\n */\n @state()\n private _fetchedSrc: string | undefined = undefined;\n\n /**\n * Tracks the (library, name) pair that was last resolved to an inline-fetch\n * URL via the registry. Distinct from `_fetchedSrc` so the explicit `src`\n * attribute and registry-driven inline mode never thrash each other.\n * @internal\n */\n @state()\n private _fetchedLibraryKey: string | undefined = undefined;\n\n /**\n * De-duplication set for unknown-library warnings. Keyed by\n * `${library} ${name}` so each unique pair logs at most once.\n * @internal\n */\n private static readonly _unknownLibraryWarned = new Set<string>();\n\n /**\n * Monotonically-increasing sequence number. Incremented before each fetch so\n * that stale out-of-order responses can be discarded.\n */\n /** @internal */\n private _fetchSeq = 0;\n\n // ─── Lifecycle ───\n\n /**\n * Listener for late-registered icon libraries. When a consumer calls\n * `registerIconLibrary()` AFTER hx-icon instances have already rendered,\n * the registry fires `helixicon-library-registered` on globalThis. Each\n * mounted hx-icon listens and re-resolves so the new library takes\n * effect without manual re-render.\n * @internal\n */\n private _onLibraryRegistered = (e: Event): void => {\n const detail = (e as CustomEvent<{ name: string }>).detail;\n // Only re-resolve if the registered library matches our current one.\n // Avoids unnecessary re-renders for every other registration on the page.\n if (!detail || detail.name === this.library) {\n // Reset the cached fetch identity so library-driven inline mode re-fetches.\n this._fetchedLibraryKey = undefined;\n this.requestUpdate();\n // For fetch-mode (`spriteSheet: false`) libraries, requestUpdate alone\n // re-renders but doesn't trigger _maybeFetchLibraryIcon (which is gated\n // on changed-property detection in updated()). Fire it explicitly so\n // late-registered inline-fetch libraries actually fetch.\n void this._maybeFetchLibraryIcon();\n }\n };\n\n /**\n * Listener for setBasePath() changes. When the registry's base path is\n * mutated after mount, sprite-mode icons need to re-resolve against the\n * new path; inline-fetch icons need to re-fetch with the new URL.\n * @internal\n */\n private _onBasePathChanged = (): void => {\n this._fetchedLibraryKey = undefined;\n this.requestUpdate();\n void this._maybeFetchLibraryIcon();\n };\n\n override connectedCallback(): void {\n super.connectedCallback();\n if (typeof globalThis.addEventListener === 'function') {\n globalThis.addEventListener('helixicon-library-registered', this._onLibraryRegistered);\n globalThis.addEventListener('helixicon-base-path-changed', this._onBasePathChanged);\n }\n }\n\n override disconnectedCallback(): void {\n if (typeof globalThis.removeEventListener === 'function') {\n globalThis.removeEventListener('helixicon-library-registered', this._onLibraryRegistered);\n globalThis.removeEventListener('helixicon-base-path-changed', this._onBasePathChanged);\n }\n super.disconnectedCallback();\n }\n\n override updated(changed: PropertyValues<this>): void {\n super.updated(changed);\n\n // Inline-fetch via explicit `src` takes precedence and resets any\n // previous library-driven inline state so the two paths never overlay.\n if (changed.has('src') && this.src !== this._fetchedSrc) {\n this._fetchedLibraryKey = undefined;\n void this._fetchInlineSvg(this.src);\n // Don't return — fall through so a CLEARED src can still trigger\n // library fetch if the component falls back to registry resolution.\n }\n\n // When neither `src` nor `spriteUrl` is set, the library + name pair\n // drives resolution. Registry libraries that declare `spriteSheet: false`\n // require an inline fetch (with optional post-sanitize mutator). Detect\n // changes to library / name AND to src / sprite-url (so clearing an\n // explicit source falls back to library fetch instead of leaving the\n // icon stuck on empty inline content).\n const hasExplicitSrc = typeof this.src === 'string' && this.src.trim().length > 0;\n const hasExplicitSprite = typeof this.spriteUrl === 'string' && this.spriteUrl.length > 0;\n if (\n !hasExplicitSrc &&\n !hasExplicitSprite &&\n (changed.has('library') ||\n changed.has('name') ||\n changed.has('src') ||\n changed.has('spriteUrl'))\n ) {\n void this._maybeFetchLibraryIcon();\n }\n }\n\n // ─── Inline SVG Fetch ───\n\n /** @internal */\n private async _fetchInlineSvg(url: string | undefined): Promise<void> {\n const seq = ++this._fetchSeq;\n\n if (!url) {\n this._inlineSvg = '';\n this._fetchedSrc = undefined;\n return;\n }\n\n // Validate URL origin — only allow same-origin or data: URIs by default.\n // Cross-origin SVGs are blocked unless explicitly allowed via allowedOrigins.\n if (!this._isAllowedOrigin(url)) {\n console.warn(\n `[hx-icon] Blocked cross-origin SVG fetch: \"${url}\". ` +\n 'Only same-origin URLs are allowed by default. ' +\n 'Set the allowed-origins attribute to permit specific external origins.',\n );\n this._inlineSvg = '';\n this._fetchedSrc = undefined;\n return;\n }\n\n // Use module-level cache to avoid duplicate network requests for the same URL.\n // Multiple hx-icon instances sharing the same src will share one in-flight fetch.\n try {\n let pending = _svgCache.get(url);\n if (!pending) {\n pending = fetch(url).then(async (response) => {\n if (!response.ok) {\n _svgCache.delete(url);\n return '';\n }\n return response.text();\n });\n _svgCache.set(url, pending);\n }\n\n const text = await pending;\n if (seq !== this._fetchSeq) return;\n\n if (!text) {\n this._inlineSvg = '';\n this._fetchedSrc = undefined;\n return;\n }\n\n const sanitized = this._sanitizeSvg(text);\n this._inlineSvg = sanitized;\n this._fetchedSrc = url;\n } catch {\n if (seq !== this._fetchSeq) return;\n _svgCache.delete(url);\n this._inlineSvg = '';\n this._fetchedSrc = undefined;\n }\n }\n\n /**\n * Checks whether a URL is same-origin or matches the configured allowedOrigins.\n * Relative URLs and data: URIs are always allowed. Cross-origin URLs are blocked\n * unless their origin appears in the allowedOrigins list.\n * @internal\n */\n private _isAllowedOrigin(url: string): boolean {\n // Relative URLs are always same-origin\n if (url.startsWith('/') || url.startsWith('./') || url.startsWith('../')) {\n return true;\n }\n\n try {\n const parsed = new URL(url, window.location.href);\n\n // Same-origin is always allowed\n if (parsed.origin === window.location.origin) {\n return true;\n }\n\n // Check configured allowlist\n if (this.allowedOrigins) {\n const allowed = this.allowedOrigins\n .split(',')\n .map((o) => o.trim().toLowerCase())\n .filter(Boolean);\n return allowed.includes(parsed.origin.toLowerCase());\n }\n\n return false;\n } catch {\n // Unparseable URL — block it\n return false;\n }\n }\n\n /**\n * Parses the raw SVG text, strips dangerous content (script elements,\n * foreignObject, on* event-handler attributes, javascript:/data: URIs,\n * and style attributes that could carry CSS injection payloads), and\n * returns the outer SVG markup safe for rendering via `unsafeHTML`.\n *\n * Additionally injects `focusable=\"false\"` on the root SVG element to\n * prevent IE11/old-Edge from making the SVG keyboard-focusable, and strips\n * any ARIA attributes from the inner SVG to prevent conflicts with the\n * wrapper's own ARIA semantics.\n */\n /** @internal */\n private _sanitizeSvg(raw: string): string {\n const parser = new DOMParser();\n const doc = parser.parseFromString(raw, 'image/svg+xml');\n\n const parseError = doc.querySelector('parsererror');\n if (parseError) {\n return '';\n }\n\n const svgEl = doc.querySelector('svg');\n if (!svgEl) {\n return '';\n }\n\n // Remove dangerous embedded elements:\n // - script: arbitrary code execution\n // - foreignObject: can embed arbitrary HTML including scripts\n // - style: CSS injection (url() payloads, expression(), external references)\n // - animate, animateTransform, animateMotion, set: SMIL animation elements\n // can trigger event handlers and modify attributes to bypass sanitization\n const dangerousElements = [\n 'script',\n 'foreignObject',\n 'style',\n 'animate',\n 'animateTransform',\n 'animateMotion',\n 'set',\n ];\n svgEl.querySelectorAll(dangerousElements.join(', ')).forEach((s) => {\n s.remove();\n });\n\n // URL-bearing attributes that can carry javascript:/data: payloads.\n const urlAttrs = new Set(['href', 'xlink:href', 'src', 'action', 'formaction']);\n\n // ARIA attributes that may conflict with the wrapper element's own semantics.\n // The wrapper <span part=\"svg\"> owns role/aria-label/aria-hidden — the inner\n // SVG must not duplicate or contradict these.\n const ariaAttrs = new Set(['role', 'aria-label', 'aria-labelledby', 'aria-hidden']);\n\n // Sanitize every element including the root svg.\n const allElements: Element[] = [svgEl, ...Array.from(svgEl.querySelectorAll('*'))];\n allElements.forEach((el) => {\n const attrs = Array.from(el.attributes);\n attrs.forEach((attr) => {\n const attrName = attr.name.toLowerCase();\n // Strip event-handler attributes.\n if (attrName.startsWith('on')) {\n el.removeAttribute(attr.name);\n return;\n }\n // Strip style attributes — CSS can carry injection payloads via\n // url(javascript:...), expression(), or external filter/clip-path references.\n if (attrName === 'style') {\n el.removeAttribute(attr.name);\n return;\n }\n // Strip javascript: and data: URIs from URL-bearing attributes.\n if (urlAttrs.has(attrName)) {\n const val = attr.value.replace(/\\s/g, '').toLowerCase();\n if (val.startsWith('javascript:') || val.startsWith('data:')) {\n el.removeAttribute(attr.name);\n }\n }\n });\n });\n\n // Strip ARIA attributes from the root SVG only — they conflict with the\n // wrapper element's ARIA. Child elements' ARIA is left intact.\n ariaAttrs.forEach((a) => svgEl.removeAttribute(a));\n\n // Inject focusable=\"false\" so IE11/old-Edge do not tab into the SVG.\n svgEl.setAttribute('focusable', 'false');\n\n return svgEl.outerHTML;\n }\n\n // ─── Registry Resolution ───\n\n /**\n * Looks up the configured library in the `@helixui/icons` registry and\n * returns either:\n * - `{ kind: 'sprite', href }` for sprite-sheet libraries (the default\n * `helix` and `fa-free` bundles)\n * - `{ kind: 'inline', url, library }` for fetch-mode libraries that may\n * also carry a post-sanitize mutator\n * - `null` if the library is unknown (warning logged once per unique\n * library+name pair)\n * @internal\n */\n private _resolveLibraryHref():\n | { kind: 'sprite'; href: string }\n | { kind: 'inline'; url: string; library: IconLibrary }\n | null {\n const lib = getIconLibrary(this.library);\n if (!lib) {\n // Empty `library` is the documented opt-out for legacy bare-name\n // + document-local sprite usage. Don't warn — the caller is opting\n // OUT of registry resolution, not asking for a missing library.\n if (this.library === '') return null;\n const key = `${this.library} ${this.name}`;\n if (!HelixIcon._unknownLibraryWarned.has(key)) {\n HelixIcon._unknownLibraryWarned.add(key);\n console.warn(\n `[hx-icon] Unknown icon library \"${this.library}\" requested for name \"${this.name}\". ` +\n 'Register it via registerIconLibrary() from @helixui/icons before use.',\n );\n }\n return null;\n }\n let resolved: string;\n try {\n resolved = lib.resolver(this.name);\n } catch (err) {\n console.warn(\n `[hx-icon] Resolver for library \"${this.library}\" threw on name \"${this.name}\":`,\n err,\n );\n return null;\n }\n if (typeof resolved !== 'string' || resolved.length === 0) {\n return null;\n }\n if (lib.spriteSheet) {\n return { kind: 'sprite', href: resolved };\n }\n return { kind: 'inline', url: resolved, library: lib };\n }\n\n /**\n * Drives the inline-fetch path for registry libraries that declare\n * `spriteSheet: false`. Reuses `_fetchInlineSvg` for security sanitization\n * and the `_svgCache` module-level dedupe, then layers the optional\n * library mutator on top of the sanitized markup.\n * @internal\n */\n private async _maybeFetchLibraryIcon(): Promise<void> {\n const resolved = this._resolveLibraryHref();\n // Sprite libraries render synchronously via `<use>` — no fetch needed.\n if (!resolved || resolved.kind !== 'inline') {\n this._fetchedLibraryKey = undefined;\n // Clear any prior library-driven inline content so a sprite library\n // does not paint over with stale fetch markup.\n if (!this.src) {\n this._inlineSvg = '';\n }\n return;\n }\n const key = `${this.library} ${this.name} ${resolved.url}`;\n if (this._fetchedLibraryKey === key) return;\n this._fetchedLibraryKey = key;\n await this._fetchInlineSvg(resolved.url);\n // After sanitization, apply the optional library mutator on the parsed\n // SVG, then re-serialize. Mutation is best-effort: a thrown mutator\n // falls back to the un-mutated sanitized markup.\n // Race guard: between the await above and now, `library` or `name`\n // may have changed and a newer fetch may have superseded this one.\n // Re-check the fetch identity before mutating — otherwise library A's\n // mutator could apply to library B's already-rendered SVG.\n if (this._fetchedLibraryKey === key && resolved.library.mutator && this._inlineSvg) {\n this._inlineSvg = this._applyLibraryMutator(this._inlineSvg, resolved.library);\n }\n }\n\n /**\n * Re-parses sanitized SVG markup, runs the library's mutator on the root\n * SVGElement, and re-serializes. Sanitization always runs FIRST in\n * `_fetchInlineSvg`; the mutator never sees raw consumer SVG.\n * @internal\n */\n private _applyLibraryMutator(sanitized: string, library: IconLibrary): string {\n if (!library.mutator) return sanitized;\n try {\n const parser = new DOMParser();\n const doc = parser.parseFromString(sanitized, 'image/svg+xml');\n const svg = doc.querySelector('svg');\n if (!svg) return sanitized;\n library.mutator(svg as unknown as SVGElement);\n return svg.outerHTML;\n } catch (err) {\n console.warn(\n `[hx-icon] Mutator for library \"${library.name}\" threw on name \"${this.name}\"; rendering un-mutated SVG.`,\n err,\n );\n return sanitized;\n }\n }\n\n // ─── Render Helpers ───\n\n /**\n * Returns the href used in the `<use>` element for explicit sprite mode.\n * If `name` already begins with `#` it is treated as an inline reference.\n */\n /** @internal */\n private _spriteHref(): string {\n const n = this.name;\n if (n.startsWith('#')) {\n return n;\n }\n const base = this.spriteUrl ?? '';\n return base ? `${base}#${n}` : `#${n}`;\n }\n\n /** @internal */\n private _renderSprite(href?: string) {\n const isDecorative = !this.label.trim();\n const useHref = href ?? this._spriteHref();\n\n return html`\n <svg\n part=\"svg\"\n class=\"icon__svg\"\n viewBox=\"0 0 24 24\"\n xmlns=\"http://www.w3.org/2000/svg\"\n role=${isDecorative ? nothing : 'img'}\n aria-label=${isDecorative ? nothing : this.label}\n aria-hidden=${isDecorative ? 'true' : nothing}\n focusable=\"false\"\n >\n ${isDecorative ? nothing : html`<title>${this.label}</title>`}\n <use href=${useHref}></use>\n </svg>\n `;\n }\n\n /** @internal */\n private _renderInline() {\n if (!this._inlineSvg) {\n return nothing;\n }\n\n const isDecorative = !this.label.trim();\n\n // The fetched SVG is rendered inside a wrapper span that carries the\n // csspart and ARIA semantics. The inner SVG from unsafeHTML fills the\n // container via the `.icon__inline svg` CSS rule. ARIA attributes and\n // focusable=\"false\" are injected into the inner SVG by _sanitizeSvg.\n return html`\n <span\n part=\"svg\"\n class=\"icon__inline\"\n role=${isDecorative ? nothing : 'img'}\n aria-label=${isDecorative ? nothing : this.label}\n aria-hidden=${isDecorative ? 'true' : nothing}\n >\n ${unsafeHTML(this._inlineSvg)}\n </span>\n `;\n }\n\n // ─── Render ───\n\n override render() {\n // 1. Inline fetch mode takes precedence when src is a non-empty string.\n // Library attribute is ignored on this path.\n if (typeof this.src === 'string' && this.src.trim().length > 0) {\n return this._renderInline();\n }\n\n // 2. Explicit sprite-url + name. Library attribute is ignored — the\n // consumer is pinning the sheet location explicitly. An empty\n // string `sprite-url=\"\"` is treated as an explicit opt-out of\n // registry resolution and renders `<use href=\"#${name}\">` —\n // consumers who want strict document-local sprite behavior\n // can pin it that way without setting `library=\"\"`.\n if (typeof this.spriteUrl === 'string' && this.name) {\n return this._renderSprite();\n }\n\n // 2b. In-document fragment reference (`name=\"#custom-icon\"`). Library\n // attribute is ignored — the consumer is pointing at a sprite symbol\n // embedded directly in the host page.\n if (this.name.startsWith('#')) {\n return this._renderSprite();\n }\n\n // 3. Registry resolution — only when `library` is explicitly set to a\n // registered library name. The default empty `library` preserves\n // the pre-3.9.0 contract (bare `name` → document-local sprite\n // fragment) so existing markup keeps working unchanged. Consumers\n // opt INTO registry resolution by setting `library=\"fa-free\"`,\n // `library=\"helix\"`, or any registered custom library.\n if (this.name && this.library) {\n const resolved = this._resolveLibraryHref();\n if (!resolved) return nothing;\n if (resolved.kind === 'sprite') {\n return this._renderSprite(resolved.href);\n }\n // Inline fetch mode driven by the registry — markup populated by\n // `_maybeFetchLibraryIcon` after sanitization + mutator pass.\n return this._renderInline();\n }\n\n // 4. Bare `name` (no library) → document-local sprite fragment\n // (`<use href=\"#<name>\">`). Pre-3.9.0 contract preserved.\n if (this.name) {\n return this._renderSprite();\n }\n\n return nothing;\n }\n}\n\n/**\n * Module-level SVG fetch cache. Shared across all `hx-icon` instances so that\n * multiple icons sharing the same `src` URL issue only one network request.\n * The cache stores in-flight `Promise<string>` values — resolved entries remain\n * cached for the lifetime of the page to prevent redundant re-fetches.\n */\nconst _svgCache = new Map<string, Promise<string>>();\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-icon': HelixIcon;\n }\n}\n"],"names":["helixIconStyles","css","HelixIcon","HelixElement","detail","changed","hasExplicitSrc","hasExplicitSprite","url","seq","pending","_svgCache","response","text","sanitized","parsed","o","raw","doc","svgEl","dangerousElements","s","urlAttrs","ariaAttrs","el","attr","attrName","val","a","lib","getIconLibrary","key","resolved","err","library","svg","n","base","href","isDecorative","useHref","html","nothing","unsafeHTML","forcedColorsSurface","__decorateClass","property","state","customElement"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
- import { css as x, nothing as v, svg as f, html as h } from "lit";
2
- import { property as n, customElement as u } from "lit/decorators.js";
1
+ import { css as x, nothing as f, html as h } from "lit";
2
+ import { property as n, customElement as v } from "lit/decorators.js";
3
3
  import { classMap as p } from "lit/directives/class-map.js";
4
4
  import { ifDefined as r } from "lit/directives/if-defined.js";
5
- import { c as b } from "./forced-colors-CTEDFRGa.js";
6
- import { m as k } from "./aria-delegation-Doq6RRUy.js";
7
- import { H as y } from "./helix-element-BNEYeiys.js";
8
- const m = x`
5
+ import { c as u } from "./forced-colors-CTEDFRGa.js";
6
+ import { m as b } from "./aria-delegation-Doq6RRUy.js";
7
+ import { H as k } from "./helix-element-BNEYeiys.js";
8
+ const y = x`
9
9
  :host {
10
10
  display: inline;
11
11
  }
@@ -83,8 +83,7 @@ const m = x`
83
83
 
84
84
  .link__external-icon {
85
85
  display: inline-flex;
86
- width: 0.75em;
87
- height: 0.75em;
86
+ --hx-icon-size: 0.75em;
88
87
  flex-shrink: 0;
89
88
  }
90
89
 
@@ -126,12 +125,12 @@ const m = x`
126
125
  border: 0;
127
126
  }
128
127
  `;
129
- var g = Object.defineProperty, w = Object.getOwnPropertyDescriptor, o = (e, a, s, l) => {
130
- for (var t = l > 1 ? void 0 : l ? w(a, s) : a, d = e.length - 1, c; d >= 0; d--)
128
+ var m = Object.defineProperty, g = Object.getOwnPropertyDescriptor, o = (e, a, s, l) => {
129
+ for (var t = l > 1 ? void 0 : l ? g(a, s) : a, d = e.length - 1, c; d >= 0; d--)
131
130
  (c = e[d]) && (t = (l ? c(a, s, t) : c(t)) || t);
132
- return l && t && g(a, s, t), t;
131
+ return l && t && m(a, s, t), t;
133
132
  };
134
- let i = class extends k(y) {
133
+ let i = class extends b(k) {
135
134
  constructor() {
136
135
  super(...arguments), this.href = void 0, this.target = void 0, this.variant = "default", this.disabled = !1, this.download = void 0, this.rel = void 0, this.externalLabel = "(opens in new tab)";
137
136
  }
@@ -162,20 +161,14 @@ let i = class extends k(y) {
162
161
  }
163
162
  /** @internal */
164
163
  _renderExternalIcon() {
165
- return this.target !== "_blank" ? v : h`
166
- <svg
164
+ return this.target !== "_blank" ? f : h`
165
+ <hx-icon
167
166
  class="link__external-icon"
168
167
  part="external-icon"
168
+ library="helix"
169
+ name="external-link"
169
170
  aria-hidden="true"
170
- viewBox="0 0 24 24"
171
- fill="none"
172
- stroke="currentColor"
173
- stroke-width="2.5"
174
- stroke-linecap="round"
175
- stroke-linejoin="round"
176
- >
177
- ${f`<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /><polyline points="15 3 21 3 21 9" /><line x1="10" y1="14" x2="21" y2="3" />`}
178
- </svg>
171
+ ></hx-icon>
179
172
  <span class="sr-only">${this.externalLabel}</span>
180
173
  `;
181
174
  }
@@ -222,7 +215,7 @@ let i = class extends k(y) {
222
215
  `;
223
216
  }
224
217
  };
225
- i.styles = [m, b];
218
+ i.styles = [y, u];
226
219
  o([
227
220
  n({ type: String })
228
221
  ], i.prototype, "href", 2);
@@ -245,9 +238,9 @@ o([
245
238
  n({ type: String, attribute: "external-label" })
246
239
  ], i.prototype, "externalLabel", 2);
247
240
  i = o([
248
- u("hx-link")
241
+ v("hx-link")
249
242
  ], i);
250
243
  export {
251
244
  i as H
252
245
  };
253
- //# sourceMappingURL=hx-link-CMnZRUtQ.js.map
246
+ //# sourceMappingURL=hx-link-BURSdYLp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-link-BURSdYLp.js","sources":["../../src/components/hx-link/hx-link.styles.ts","../../src/components/hx-link/hx-link.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixLinkStyles = css`\n :host {\n display: inline;\n }\n\n :host([disabled]) {\n cursor: not-allowed;\n }\n\n /* --- Base Link --- */\n\n .link {\n display: inline-flex;\n align-items: center;\n gap: var(--hx-space-1, 0.25rem);\n color: var(--hx-link-color, var(--hx-color-primary-600, #0f7078));\n font-family: var(--hx-link-font-family, var(--hx-font-family-sans, inherit));\n font-size: inherit;\n line-height: inherit;\n text-decoration: var(--hx-link-text-decoration, underline);\n text-underline-offset: var(--hx-link-underline-offset, 2px);\n cursor: pointer;\n outline: 0;\n transition:\n color var(--hx-transition-fast, 150ms ease),\n text-decoration-color var(--hx-transition-fast, 150ms ease);\n }\n\n .link:hover {\n color: var(--hx-link-color-hover, var(--hx-color-primary-700, #0f6363));\n text-decoration: var(--hx-link-text-decoration-hover, underline);\n }\n\n .link:active {\n color: var(--hx-link-color-active, var(--hx-color-primary-800, #07494a));\n }\n\n .link:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-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 /* --- Variant: subtle --- */\n\n .link--subtle {\n color: var(--hx-link-color-subtle, var(--hx-color-text-secondary, #313e4b));\n text-decoration: none;\n }\n\n .link--subtle:hover {\n color: var(--hx-link-color-hover, var(--hx-color-primary-700, #0f6363));\n text-decoration: underline;\n }\n\n /* --- Variant: danger --- */\n\n .link--danger {\n color: var(--hx-link-color-danger, var(--hx-color-error-text, #c92a2a));\n }\n\n .link--danger:hover {\n color: var(--hx-link-color-danger-hover, var(--hx-color-error-700, #a21312));\n }\n\n /* --- Disabled --- */\n\n .link--disabled {\n color: var(--hx-link-color-disabled, var(--hx-color-text-disabled, #8e9c98));\n text-decoration: none;\n cursor: not-allowed;\n }\n\n /* --- External link icon --- */\n\n .link__external-icon {\n display: inline-flex;\n --hx-icon-size: 0.75em;\n flex-shrink: 0;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .link {\n transition: none;\n }\n }\n\n /* ─── High Contrast Mode (forced-colors) ─── */\n\n @media (forced-colors: active) {\n .link {\n forced-color-adjust: none;\n color: LinkText;\n }\n\n .link:focus-visible {\n outline: 3px solid Highlight;\n outline-offset: 2px;\n }\n\n .link--disabled {\n color: GrayText;\n }\n }\n\n /* --- Visually hidden (sr-only) --- */\n\n .sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n }\n`;\n","import { html, nothing } 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 '../hx-icon/hx-icon.js';\nimport { HelixElement } from '../../base/index.js';\nimport { mixinDelegatesAria } from '../../mixins/index.js';\nimport { helixLinkStyles } from './hx-link.styles.js';\nimport { forcedColorsLink } from '../../styles/forced-colors.js';\n\n/**\n * Variant options for the link component.\n */\nexport type LinkVariant = 'default' | 'subtle' | 'danger';\n\n/**\n * A semantic hyperlink component with accessibility-first design.\n * Renders a native `<a>` element for enabled state and a `<span>` for\n * disabled state with full keyboard and screen reader support.\n *\n * @summary Accessible hyperlink with external-link detection, disabled state,\n * and download support.\n *\n * @tag hx-link\n *\n * @slot - Default slot for link label text or content.\n *\n * @fires {CustomEvent<{originalEvent: MouseEvent}>} hx-click - Dispatched when\n * the link is clicked and is not disabled.\n *\n * @csspart link - The inner anchor or span element.\n * @csspart external-icon - The external link icon SVG (when target=\"_blank\").\n *\n * @cssprop [--hx-link-color=var(--hx-color-primary-500)] - Default link color.\n * @cssprop [--hx-link-color-hover=var(--hx-color-primary-700)] - Hover color.\n * @cssprop [--hx-link-color-active=var(--hx-color-primary-800)] - Active color.\n * @cssprop [--hx-link-color-disabled=var(--hx-color-neutral-400)] - Disabled color.\n * @cssprop [--hx-link-color-subtle=var(--hx-color-neutral-600)] - Subtle variant color.\n * @cssprop [--hx-link-color-danger=var(--hx-color-error-text)] - Danger variant color.\n * @cssprop [--hx-link-color-danger-hover=var(--hx-color-error-700)] - Danger variant hover color.\n * @cssprop [--hx-link-font-family=var(--hx-font-family-sans)] - Link font family.\n * @cssprop [--hx-link-text-decoration=underline] - Link text decoration.\n * @cssprop [--hx-link-text-decoration-hover=underline] - Hover text decoration.\n * @cssprop [--hx-link-underline-offset=2px] - Text underline offset.\n * @cssprop [--hx-link-focus-ring-color=var(--hx-focus-ring-color)] - Focus ring color.\n *\n * @note The `:visited` pseudo-class does not work inside Shadow DOM due to\n * browser privacy restrictions. This is a known platform limitation.\n * @cssprop [--hx-space-1] - Spacing token.\n * @cssprop [--hx-color-primary-500] - Color.\n * @cssprop [--hx-font-family-sans] - Font family.\n * @cssprop [--hx-transition-fast] - Transition timing.\n * @cssprop [--hx-color-primary-700] - Color.\n * @cssprop [--hx-color-primary-800] - Color.\n * @cssprop [--hx-focus-ring-width] - Width.\n * @cssprop [--hx-focus-ring-color] - Color.\n * @cssprop [--hx-color-primary-400] - Color.\n * @cssprop [--hx-focus-ring-offset] - CSS custom property.\n * @cssprop [--hx-border-radius-sm] - CSS custom property.\n * @cssprop [--hx-color-neutral-600] - Color.\n * @cssprop [--hx-color-error-text] - Color.\n * @cssprop [--hx-color-error-700] - Color.\n * @cssprop [--hx-color-neutral-400] - Color.\n */\n@customElement('hx-link')\nexport class HelixLink extends mixinDelegatesAria(HelixElement) {\n static override styles = [helixLinkStyles, forcedColorsLink];\n\n /**\n * The URL the link points to.\n * @attr href\n */\n @property({ type: String })\n href: string | undefined = undefined;\n\n /**\n * Where to display the linked URL (_self, _blank, etc.).\n * When set to \"_blank\", automatically adds rel=\"noopener noreferrer\"\n * and shows an external-link indicator.\n * @attr target\n */\n @property({ type: String })\n target: string | undefined = undefined;\n\n /**\n * Visual style variant of the link.\n * @attr variant\n */\n @property({ type: String, reflect: true })\n variant: 'default' | 'subtle' | 'danger' = 'default';\n\n /**\n * Whether the link is disabled. Renders a span instead of an anchor.\n * The disabled span is keyboard-focusable (tabindex=\"0\") and announces\n * as a disabled link to screen readers.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Prompts the user to download the linked URL. When set to a string,\n * the value is used as the suggested filename.\n * @attr download\n */\n @property({ type: String })\n download: string | undefined = undefined;\n\n /**\n * Relationship between the current document and the linked URL.\n * Automatically set to \"noopener noreferrer\" when target=\"_blank\".\n * @attr rel\n */\n @property({ type: String })\n rel: string | undefined = undefined;\n\n /**\n * Localised announcement text appended (visually hidden) to the link's\n * accessible name when `target=\"_blank\"`. Defaults to English. Override\n * for i18n contexts so AT users hear the new-tab notice in their locale.\n * @attr external-label\n */\n @property({ type: String, attribute: 'external-label' })\n externalLabel: string = '(opens in new tab)';\n\n // --- Event Handling ---\n\n /** @internal Blocks Enter and Space activation on disabled span. */\n private _handleDisabledKeydown(e: KeyboardEvent): void {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n }\n }\n\n /** @internal */\n private _handleClick(e: MouseEvent): void {\n if (this.disabled) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\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\n // --- Render Helpers ---\n\n /** @internal */\n private _computeRel(): string | undefined {\n if (this.rel) return this.rel;\n if (this.target === '_blank') return 'noopener noreferrer';\n return undefined;\n }\n\n /** @internal */\n private _renderExternalIcon() {\n if (this.target !== '_blank') return nothing;\n\n return html`\n <hx-icon\n class=\"link__external-icon\"\n part=\"external-icon\"\n library=\"helix\"\n name=\"external-link\"\n aria-hidden=\"true\"\n ></hx-icon>\n <span class=\"sr-only\">${this.externalLabel}</span>\n `;\n }\n\n // --- Render ---\n\n override render() {\n const classes = {\n link: true,\n [`link--${this.variant}`]: this.variant !== 'default',\n 'link--disabled': this.disabled,\n };\n\n // mixinDelegatesAria forwarding: consumer-set aria-* land in\n // data-aria-* on the host. Project them onto the inner role-bearing\n // element (the <a> or the <span role=\"link\">) so AT walks them\n // correctly without double-announcement on the host.\n const projectedLabel = this.getAttribute('data-aria-label');\n const projectedLabelledBy = this.getAttribute('data-aria-labelledby');\n const projectedDescribedBy = this.getAttribute('data-aria-describedby');\n const projectedCurrent = this.getAttribute('data-aria-current');\n\n if (this.disabled) {\n return html`\n <span\n part=\"link\"\n class=${classMap(classes)}\n role=\"link\"\n aria-disabled=\"true\"\n aria-label=${ifDefined(projectedLabel ?? undefined)}\n aria-labelledby=${ifDefined(projectedLabelledBy ?? undefined)}\n aria-describedby=${ifDefined(projectedDescribedBy ?? undefined)}\n aria-current=${ifDefined(projectedCurrent ?? undefined)}\n tabindex=\"0\"\n @click=${this._handleClick}\n @keydown=${this._handleDisabledKeydown}\n >\n <slot></slot>\n </span>\n `;\n }\n\n return html`\n <a\n part=\"link\"\n class=${classMap(classes)}\n href=${ifDefined(this.href)}\n target=${ifDefined(this.target)}\n rel=${ifDefined(this._computeRel())}\n download=${ifDefined(this.download)}\n aria-label=${ifDefined(projectedLabel ?? undefined)}\n aria-labelledby=${ifDefined(projectedLabelledBy ?? undefined)}\n aria-describedby=${ifDefined(projectedDescribedBy ?? undefined)}\n aria-current=${ifDefined(projectedCurrent ?? undefined)}\n @click=${this._handleClick}\n >\n <slot></slot>\n ${this._renderExternalIcon()}\n </a>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-link': HelixLink;\n }\n}\n"],"names":["helixLinkStyles","css","HelixLink","mixinDelegatesAria","HelixElement","nothing","html","classes","projectedLabel","projectedLabelledBy","projectedDescribedBy","projectedCurrent","classMap","ifDefined","forcedColorsLink","__decorateClass","property","customElement"],"mappings":"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"}