@xplortech/apollo-core 3.2.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (496) hide show
  1. package/README.md +1 -1
  2. package/build/components/accordion.css +1 -1
  3. package/build/components/badge.css +1 -1
  4. package/build/components/banner.css +1 -1
  5. package/build/components/breadcrumbs.css +1 -1
  6. package/build/components/button.css +1 -1
  7. package/build/components/choicelist.css +1 -1
  8. package/build/components/data-card.css +1 -1
  9. package/build/components/divider.css +1 -1
  10. package/build/components/dropdown.css +1 -1
  11. package/build/components/dynamic-table-cell.css +1 -1
  12. package/build/components/header-accordion.css +1 -1
  13. package/build/components/input-color.css +1 -1
  14. package/build/components/input-file.css +1 -1
  15. package/build/components/input-search.css +1 -1
  16. package/build/components/input.css +1 -1
  17. package/build/components/label.css +1 -1
  18. package/build/components/large-card.css +1 -1
  19. package/build/components/list-item.css +1 -1
  20. package/build/components/main-nav.css +1 -1
  21. package/build/components/modal.css +1 -1
  22. package/build/components/pagination.css +1 -1
  23. package/build/components/secondary-nav.css +1 -1
  24. package/build/components/select.css +1 -1
  25. package/build/components/side-nav.css +1 -1
  26. package/build/components/slideout.css +1 -1
  27. package/build/components/spotlight.css +1 -1
  28. package/build/components/table-cell-base.css +1 -1
  29. package/build/components/table-header-cell.css +1 -1
  30. package/build/components/table.css +1 -1
  31. package/build/components/tag.css +1 -1
  32. package/build/components/toast.css +1 -1
  33. package/build/components/toggle.css +1 -1
  34. package/build/components/toolbar.css +1 -1
  35. package/build/components/tooltip.css +1 -1
  36. package/build/components/top-nav.css +1 -1
  37. package/build/core.css +1 -1
  38. package/build/style.css +1 -1
  39. package/build/typography.css +1 -1
  40. package/build/variables/apollo.css +30 -19
  41. package/build/variables/field-edge.css +32 -19
  42. package/dist/apollo-core/apollo-core.css +1 -1
  43. package/dist/apollo-core/apollo-core.esm.js +1 -1
  44. package/dist/apollo-core/p-01c55c74.entry.js +1 -0
  45. package/dist/apollo-core/{p-050a8bf2.entry.js → p-01ed2bb5.entry.js} +1 -1
  46. package/dist/apollo-core/{p-b3b4678b.entry.js → p-08ea8939.entry.js} +1 -1
  47. package/dist/apollo-core/{p-256ece8b.entry.js → p-0a7d57ab.entry.js} +1 -1
  48. package/dist/apollo-core/p-13253d8f.entry.js +1 -0
  49. package/dist/apollo-core/{p-8845106f.entry.js → p-14f5d98a.entry.js} +1 -1
  50. package/dist/apollo-core/{p-5c3be343.entry.js → p-17e38eaf.entry.js} +1 -1
  51. package/dist/apollo-core/{p-a0d3b7f8.entry.js → p-18486aba.entry.js} +1 -1
  52. package/dist/apollo-core/{p-49531864.entry.js → p-19fb0e5f.entry.js} +1 -1
  53. package/dist/apollo-core/p-1c2135e3.entry.js +1 -0
  54. package/dist/apollo-core/{p-7d70e089.entry.js → p-2111cc38.entry.js} +1 -1
  55. package/dist/apollo-core/p-2b813212.entry.js +1 -0
  56. package/dist/apollo-core/p-325a3b43.entry.js +1 -0
  57. package/dist/apollo-core/p-38a6a53d.entry.js +1 -0
  58. package/dist/apollo-core/{p-808f3d9c.entry.js → p-38fa8398.entry.js} +1 -1
  59. package/dist/apollo-core/{p-b64ed46d.entry.js → p-3d4a6de1.entry.js} +1 -1
  60. package/dist/apollo-core/{p-3713cd77.entry.js → p-3e3ab332.entry.js} +1 -1
  61. package/dist/apollo-core/p-3f8dc5b9.entry.js +1 -0
  62. package/dist/apollo-core/{p-ff16f574.entry.js → p-4249ee9b.entry.js} +1 -1
  63. package/dist/apollo-core/{p-715baae0.entry.js → p-439d5cdd.entry.js} +1 -1
  64. package/dist/apollo-core/{p-f384a990.entry.js → p-4b949aeb.entry.js} +1 -1
  65. package/dist/apollo-core/{p-eba654a3.entry.js → p-54053547.entry.js} +1 -1
  66. package/dist/apollo-core/p-620fc472.entry.js +1 -0
  67. package/dist/apollo-core/{p-9feaa8be.entry.js → p-651b49c3.entry.js} +1 -1
  68. package/dist/apollo-core/{p-9d731673.entry.js → p-6aee7d6b.entry.js} +1 -1
  69. package/dist/apollo-core/{p-d469bf5d.entry.js → p-6de10879.entry.js} +1 -1
  70. package/dist/apollo-core/p-73a1c68d.entry.js +1 -0
  71. package/dist/apollo-core/{p-8f8f4759.entry.js → p-79d306a2.entry.js} +1 -1
  72. package/dist/apollo-core/{p-f579e4b0.entry.js → p-7f00b542.entry.js} +1 -1
  73. package/dist/apollo-core/{p-bc821e69.entry.js → p-7fd3da3a.entry.js} +1 -1
  74. package/dist/apollo-core/p-890c641b.entry.js +1 -0
  75. package/dist/apollo-core/p-9832bc63.entry.js +1 -0
  76. package/dist/apollo-core/{p-7eb02cf3.entry.js → p-9955c4f2.entry.js} +1 -1
  77. package/dist/apollo-core/p-9b757459.entry.js +1 -0
  78. package/dist/apollo-core/p-9c41d07f.entry.js +1 -0
  79. package/dist/apollo-core/{p-ee68bed6.entry.js → p-9c7a0229.entry.js} +1 -1
  80. package/dist/apollo-core/{p-24fa8a03.entry.js → p-9d091b05.entry.js} +1 -1
  81. package/dist/apollo-core/p-CJABLO_1.js +3 -0
  82. package/dist/apollo-core/{p-14e86de8.entry.js → p-a149351a.entry.js} +1 -1
  83. package/dist/apollo-core/p-a2c3a38a.entry.js +1 -0
  84. package/dist/apollo-core/p-a5ddb3c4.entry.js +1 -0
  85. package/dist/apollo-core/{p-3417c1ce.entry.js → p-a73b0ca4.entry.js} +1 -1
  86. package/dist/apollo-core/{p-fcb17ff9.entry.js → p-aece2c3d.entry.js} +1 -1
  87. package/dist/apollo-core/{p-aeb2bcb8.entry.js → p-af188297.entry.js} +1 -1
  88. package/dist/apollo-core/{p-ca83003e.entry.js → p-b2dcdb19.entry.js} +1 -1
  89. package/dist/apollo-core/p-b55951a2.entry.js +1 -0
  90. package/dist/apollo-core/p-b7b91f27.entry.js +1 -0
  91. package/dist/apollo-core/{p-11914cd4.entry.js → p-b9c85394.entry.js} +1 -1
  92. package/dist/apollo-core/{p-64f5c129.entry.js → p-ba85f686.entry.js} +1 -1
  93. package/dist/apollo-core/p-c4fd031e.entry.js +1 -0
  94. package/dist/apollo-core/p-cab7d816.entry.js +1 -0
  95. package/dist/apollo-core/p-cca0f1a2.entry.js +1 -0
  96. package/dist/apollo-core/{p-2971e2ec.entry.js → p-cd2d73ee.entry.js} +1 -1
  97. package/dist/apollo-core/p-cfb00d23.entry.js +1 -0
  98. package/dist/apollo-core/p-d060572b.entry.js +1 -0
  99. package/dist/apollo-core/p-d13a07ea.entry.js +1 -0
  100. package/dist/apollo-core/p-d1571245.entry.js +1 -0
  101. package/dist/apollo-core/p-d204ed1a.entry.js +1 -0
  102. package/dist/apollo-core/{p-9905c77c.entry.js → p-d3720611.entry.js} +1 -1
  103. package/dist/apollo-core/p-d9f78325.entry.js +1 -0
  104. package/dist/apollo-core/{p-df257cbf.entry.js → p-dad5d377.entry.js} +1 -1
  105. package/dist/apollo-core/{p-d3ada7d0.entry.js → p-e21af3d1.entry.js} +1 -1
  106. package/dist/apollo-core/{p-c5bc3b2c.entry.js → p-e7145461.entry.js} +1 -1
  107. package/dist/apollo-core/p-e99503a2.entry.js +1 -0
  108. package/dist/apollo-core/{p-e4d50301.entry.js → p-ec99c433.entry.js} +1 -1
  109. package/dist/apollo-core/{p-e09d8beb.entry.js → p-fc73497e.entry.js} +1 -1
  110. package/dist/apollo-core/{p-907bc21e.entry.js → p-fdd1b7da.entry.js} +1 -1
  111. package/dist/cjs/apollo-core.cjs.js +2 -2
  112. package/dist/cjs/{index-3cBvw409.js → index-EdmnG7t8.js} +52 -11
  113. package/dist/cjs/loader.cjs.js +1 -1
  114. package/dist/cjs/xpl-accordion.cjs.entry.js +3 -3
  115. package/dist/cjs/xpl-application-shell.cjs.entry.js +2 -2
  116. package/dist/cjs/xpl-avatar_3.cjs.entry.js +5 -5
  117. package/dist/cjs/xpl-backdrop.cjs.entry.js +2 -2
  118. package/dist/cjs/xpl-badge.cjs.entry.js +2 -2
  119. package/dist/cjs/xpl-banner.cjs.entry.js +3 -3
  120. package/dist/cjs/xpl-breadcrumb-item.cjs.entry.js +2 -2
  121. package/dist/cjs/xpl-breadcrumbs.cjs.entry.js +2 -2
  122. package/dist/cjs/xpl-button-row.cjs.entry.js +2 -2
  123. package/dist/cjs/xpl-button.cjs.entry.js +3 -3
  124. package/dist/cjs/xpl-calendar.cjs.entry.js +2 -2
  125. package/dist/cjs/xpl-checkbox.cjs.entry.js +4 -4
  126. package/dist/cjs/xpl-choicelist.cjs.entry.js +2 -2
  127. package/dist/cjs/xpl-content-area.cjs.entry.js +2 -2
  128. package/dist/cjs/xpl-dashboard.cjs.entry.js +2 -2
  129. package/dist/cjs/xpl-data-card.cjs.entry.js +3 -3
  130. package/dist/cjs/xpl-dropdown_4.cjs.entry.js +9 -9
  131. package/dist/cjs/xpl-dynamic-table-cell.cjs.entry.js +3 -3
  132. package/dist/cjs/xpl-dynamic-table-row.cjs.entry.js +3 -3
  133. package/dist/cjs/xpl-dynamic-table.cjs.entry.js +3 -3
  134. package/dist/cjs/xpl-grid-item.cjs.entry.js +2 -2
  135. package/dist/cjs/xpl-grid.cjs.entry.js +1 -1
  136. package/dist/cjs/xpl-header-accordion.cjs.entry.js +2 -2
  137. package/dist/cjs/xpl-icon.cjs.entry.js +1 -1
  138. package/dist/cjs/xpl-input-color.cjs.entry.js +1 -1
  139. package/dist/cjs/xpl-input-phone.cjs.entry.js +1 -1
  140. package/dist/cjs/xpl-input-time.cjs.entry.js +1 -1
  141. package/dist/cjs/xpl-input_4.cjs.entry.js +3 -3
  142. package/dist/cjs/xpl-large-card.cjs.entry.js +1 -1
  143. package/dist/cjs/xpl-list-item.cjs.entry.js +1 -1
  144. package/dist/cjs/xpl-list.cjs.entry.js +3 -3
  145. package/dist/cjs/xpl-main-nav.cjs.entry.js +2 -2
  146. package/dist/cjs/xpl-modal.cjs.entry.js +1 -1
  147. package/dist/cjs/xpl-nav-header-menu.cjs.entry.js +3 -3
  148. package/dist/cjs/xpl-nav-item.cjs.entry.js +3 -3
  149. package/dist/cjs/xpl-pagination.cjs.entry.js +1 -1
  150. package/dist/cjs/xpl-panel.cjs.entry.js +1 -1
  151. package/dist/cjs/xpl-popover.cjs.entry.js +5 -5
  152. package/dist/cjs/xpl-progress-bar.cjs.entry.js +1 -1
  153. package/dist/cjs/xpl-progress-indicator.cjs.entry.js +1 -1
  154. package/dist/cjs/xpl-secondary-nav.cjs.entry.js +2 -2
  155. package/dist/cjs/xpl-select_2.cjs.entry.js +11 -11
  156. package/dist/cjs/xpl-side-nav-header-item.cjs.entry.js +1 -1
  157. package/dist/cjs/xpl-side-nav-item.cjs.entry.js +3 -3
  158. package/dist/cjs/xpl-side-nav-section.cjs.entry.js +6 -6
  159. package/dist/cjs/xpl-side-nav.cjs.entry.js +1 -1
  160. package/dist/cjs/xpl-skeleton.cjs.entry.js +1 -1
  161. package/dist/cjs/xpl-slideout.cjs.entry.js +2 -2
  162. package/dist/cjs/xpl-spotlight.cjs.entry.js +1 -1
  163. package/dist/cjs/xpl-tab-panel.cjs.entry.js +1 -1
  164. package/dist/cjs/xpl-tab.cjs.entry.js +1 -1
  165. package/dist/cjs/xpl-table-body.cjs.entry.js +2 -2
  166. package/dist/cjs/xpl-table-cell_3.cjs.entry.js +5 -5
  167. package/dist/cjs/xpl-table-footer.cjs.entry.js +2 -2
  168. package/dist/cjs/xpl-table-header.cjs.entry.js +2 -2
  169. package/dist/cjs/xpl-table-row.cjs.entry.js +3 -3
  170. package/dist/cjs/xpl-table.cjs.entry.js +1 -1
  171. package/dist/cjs/xpl-tabs.cjs.entry.js +1 -1
  172. package/dist/cjs/xpl-toast.cjs.entry.js +3 -3
  173. package/dist/cjs/xpl-toggle.cjs.entry.js +1 -1
  174. package/dist/cjs/xpl-toolbar.cjs.entry.js +3 -3
  175. package/dist/cjs/xpl-tooltip.cjs.entry.js +2 -2
  176. package/dist/cjs/xpl-top-nav-item.cjs.entry.js +3 -3
  177. package/dist/cjs/xpl-top-nav-menu.cjs.entry.js +1 -1
  178. package/dist/cjs/xpl-top-nav.cjs.entry.js +1 -1
  179. package/dist/cjs/xpl-utility-bar.cjs.entry.js +2 -2
  180. package/dist/collection/collection-manifest.json +1 -1
  181. package/dist/collection/components/xpl-accordion/xpl-accordion.js +2 -2
  182. package/dist/collection/components/xpl-application-shell/xpl-application-shell.js +1 -1
  183. package/dist/collection/components/xpl-backdrop/xpl-backdrop.js +1 -1
  184. package/dist/collection/components/xpl-badge/xpl-badge.js +1 -1
  185. package/dist/collection/components/xpl-banner/xpl-banner.js +2 -2
  186. package/dist/collection/components/xpl-breadcrumbs/xpl-breadcrumb-item/xpl-breadcrumb-item.js +1 -1
  187. package/dist/collection/components/xpl-breadcrumbs/xpl-breadcrumbs/xpl-breadcrumbs.js +1 -1
  188. package/dist/collection/components/xpl-button/xpl-button.js +2 -2
  189. package/dist/collection/components/xpl-button-row/xpl-button-row.js +1 -1
  190. package/dist/collection/components/xpl-calendar/xpl-calendar.js +1 -1
  191. package/dist/collection/components/xpl-checkbox/xpl-checkbox.js +3 -3
  192. package/dist/collection/components/xpl-choicelist/xpl-choicelist.js +1 -1
  193. package/dist/collection/components/xpl-content-area/xpl-content-area.js +1 -1
  194. package/dist/collection/components/xpl-dashboard/xpl-dashboard.js +1 -1
  195. package/dist/collection/components/xpl-data-card/xpl-data-card.js +2 -2
  196. package/dist/collection/components/xpl-divider/xpl-divider.js +1 -1
  197. package/dist/collection/components/xpl-dropdown/xpl-dropdown-group/xpl-dropdown-group.js +1 -1
  198. package/dist/collection/components/xpl-dropdown/xpl-dropdown-heading/xpl-dropdown-heading.js +1 -1
  199. package/dist/collection/components/xpl-dropdown/xpl-dropdown-option/xpl-dropdown-option.js +2 -2
  200. package/dist/collection/components/xpl-dropdown/xpl-dropdown.js +7 -6
  201. package/dist/collection/components/xpl-dynamic-table/xpl-dynamic-table.js +2 -2
  202. package/dist/collection/components/xpl-dynamic-table-cell/xpl-dynamic-table-cell.js +2 -2
  203. package/dist/collection/components/xpl-dynamic-table-row/xpl-dynamic-table-row.js +2 -2
  204. package/dist/collection/components/xpl-grid-item/xpl-grid-item.js +1 -1
  205. package/dist/collection/components/xpl-header-accordion/xpl-header-accordion.js +1 -1
  206. package/dist/collection/components/xpl-input/xpl-input.js +2 -2
  207. package/dist/collection/components/xpl-list/xpl-list.js +2 -2
  208. package/dist/collection/components/xpl-main-nav/xpl-main-nav.js +1 -1
  209. package/dist/collection/components/xpl-nav-item/xpl-nav-item.js +2 -2
  210. package/dist/collection/components/xpl-popover/xpl-popover.js +4 -4
  211. package/dist/collection/components/xpl-radio/xpl-radio.js +3 -3
  212. package/dist/collection/components/xpl-secondary-nav/xpl-secondary-nav.js +1 -1
  213. package/dist/collection/components/xpl-select/xpl-select.js +8 -8
  214. package/dist/collection/components/xpl-side-nav/xpl-side-nav-item/xpl-side-nav-item.js +2 -2
  215. package/dist/collection/components/xpl-side-nav/xpl-side-nav-section/xpl-side-nav-section.js +5 -5
  216. package/dist/collection/components/xpl-slideout/xpl-slideout.js +1 -1
  217. package/dist/collection/components/xpl-table/xpl-table-body/xpl-table-body.js +1 -1
  218. package/dist/collection/components/xpl-table/xpl-table-cell/xpl-table-cell.js +2 -2
  219. package/dist/collection/components/xpl-table/xpl-table-footer/xpl-table-footer.js +1 -1
  220. package/dist/collection/components/xpl-table/xpl-table-footer-cell/xpl-table-footer-cell.js +2 -2
  221. package/dist/collection/components/xpl-table/xpl-table-row/xpl-table-row.js +2 -2
  222. package/dist/collection/components/xpl-table-header/xpl-table-header.js +1 -1
  223. package/dist/collection/components/xpl-tag/xpl-tag.js +2 -2
  224. package/dist/collection/components/xpl-toast/xpl-toast.js +2 -2
  225. package/dist/collection/components/xpl-toolbar/xpl-toolbar.js +2 -2
  226. package/dist/collection/components/xpl-tooltip/xpl-tooltip.js +1 -1
  227. package/dist/collection/components/xpl-top-nav/xpl-nav-header-menu/xpl-nav-header-menu.js +2 -2
  228. package/dist/collection/components/xpl-top-nav/xpl-top-nav-item/xpl-top-nav-item.js +2 -2
  229. package/dist/collection/components/xpl-utility-bar/xpl-utility-bar.js +1 -1
  230. package/dist/components/index.js +1 -1
  231. package/dist/components/p-2XxO7M15.js +1 -0
  232. package/dist/components/{p-D09sGbzs.js → p-3R1VKKds.js} +1 -1
  233. package/dist/components/p-BNOkHymY.js +1 -0
  234. package/dist/components/p-BWAytJnz.js +1 -0
  235. package/dist/components/{p-B7aaJikj.js → p-BZrSTAV1.js} +1 -1
  236. package/dist/components/{p-CQjJIHK6.js → p-Bh-U2qmp.js} +1 -1
  237. package/dist/components/{p-BOm_p_mz.js → p-BowQ58XR.js} +1 -1
  238. package/dist/components/p-BqhTjJWq.js +1 -0
  239. package/dist/components/{p-CFXvQqUO.js → p-CWXfIUHo.js} +1 -1
  240. package/dist/components/{p-CdRmVtIN.js → p-CZg3Gb0I.js} +1 -1
  241. package/dist/components/{p-ohy3NrgH.js → p-CcPrBo53.js} +1 -1
  242. package/dist/components/p-CetimlcM.js +1 -0
  243. package/dist/components/{p-CV5nn1zR.js → p-CfZI7bBe.js} +1 -1
  244. package/dist/components/{p-2cJsJK3-.js → p-CjzUVpyd.js} +1 -1
  245. package/dist/components/{p-DoVubWyd.js → p-ClLyaOG7.js} +1 -1
  246. package/dist/components/p-Cop4KmAG.js +1 -0
  247. package/dist/components/{p-C0AbGd4t.js → p-DHxDWgLP.js} +1 -1
  248. package/dist/components/{p-CuVYRPvz.js → p-DORBnmT4.js} +1 -1
  249. package/dist/components/{p-CmbpWpb0.js → p-DbN9vTl7.js} +1 -1
  250. package/dist/components/{p-DTh_zemx.js → p-Dl__XgUD.js} +1 -1
  251. package/dist/components/{p-8zrW9vEC.js → p-JjlTuq8d.js} +1 -1
  252. package/dist/components/p-d4i2Qqvl.js +1 -0
  253. package/dist/components/{p-Btb2UkLz.js → p-n-yq7lf4.js} +1 -1
  254. package/dist/components/{p-1D7CWIJ-.js → p-rQFFXcXl.js} +1 -1
  255. package/dist/components/p-sjDW_E4p.js +1 -0
  256. package/dist/components/{p-B56GeUtW.js → p-yc1qXhah.js} +1 -1
  257. package/dist/components/xpl-accordion.js +1 -1
  258. package/dist/components/xpl-application-shell.js +1 -1
  259. package/dist/components/xpl-avatar.js +1 -1
  260. package/dist/components/xpl-backdrop.js +1 -1
  261. package/dist/components/xpl-badge.js +1 -1
  262. package/dist/components/xpl-banner.js +1 -1
  263. package/dist/components/xpl-breadcrumb-item.js +1 -1
  264. package/dist/components/xpl-breadcrumbs.js +1 -1
  265. package/dist/components/xpl-button-row.js +1 -1
  266. package/dist/components/xpl-button.js +1 -1
  267. package/dist/components/xpl-calendar.js +1 -1
  268. package/dist/components/xpl-checkbox.js +1 -1
  269. package/dist/components/xpl-choicelist.js +1 -1
  270. package/dist/components/xpl-content-area.js +1 -1
  271. package/dist/components/xpl-dashboard.js +1 -1
  272. package/dist/components/xpl-data-card.js +1 -1
  273. package/dist/components/xpl-divider.js +1 -1
  274. package/dist/components/xpl-dropdown-group.js +1 -1
  275. package/dist/components/xpl-dropdown-heading.js +1 -1
  276. package/dist/components/xpl-dropdown-option.js +1 -1
  277. package/dist/components/xpl-dropdown.js +1 -1
  278. package/dist/components/xpl-dynamic-table-cell.js +1 -1
  279. package/dist/components/xpl-dynamic-table-row.js +1 -1
  280. package/dist/components/xpl-dynamic-table.js +1 -1
  281. package/dist/components/xpl-grid-item.js +1 -1
  282. package/dist/components/xpl-grid.js +1 -1
  283. package/dist/components/xpl-header-accordion.js +1 -1
  284. package/dist/components/xpl-icon.js +1 -1
  285. package/dist/components/xpl-input-color.js +1 -1
  286. package/dist/components/xpl-input-date.js +1 -1
  287. package/dist/components/xpl-input-file.js +1 -1
  288. package/dist/components/xpl-input-phone.js +1 -1
  289. package/dist/components/xpl-input-search.js +1 -1
  290. package/dist/components/xpl-input-time.js +1 -1
  291. package/dist/components/xpl-input.js +1 -1
  292. package/dist/components/xpl-large-card.js +1 -1
  293. package/dist/components/xpl-list-item.js +1 -1
  294. package/dist/components/xpl-list.js +1 -1
  295. package/dist/components/xpl-main-nav.js +1 -1
  296. package/dist/components/xpl-modal.js +1 -1
  297. package/dist/components/xpl-nav-header-menu.js +1 -1
  298. package/dist/components/xpl-nav-item.js +1 -1
  299. package/dist/components/xpl-pagination.js +1 -1
  300. package/dist/components/xpl-panel.js +1 -1
  301. package/dist/components/xpl-popover.js +1 -1
  302. package/dist/components/xpl-progress-bar.js +1 -1
  303. package/dist/components/xpl-progress-indicator.js +1 -1
  304. package/dist/components/xpl-radio.js +1 -1
  305. package/dist/components/xpl-secondary-nav.js +1 -1
  306. package/dist/components/xpl-select.js +1 -1
  307. package/dist/components/xpl-side-nav-header-item.js +1 -1
  308. package/dist/components/xpl-side-nav-item.js +1 -1
  309. package/dist/components/xpl-side-nav-section.js +1 -1
  310. package/dist/components/xpl-side-nav.js +1 -1
  311. package/dist/components/xpl-skeleton.js +1 -1
  312. package/dist/components/xpl-slideout.js +1 -1
  313. package/dist/components/xpl-spotlight.js +1 -1
  314. package/dist/components/xpl-tab-panel.js +1 -1
  315. package/dist/components/xpl-tab.js +1 -1
  316. package/dist/components/xpl-table-body.js +1 -1
  317. package/dist/components/xpl-table-cell.js +1 -1
  318. package/dist/components/xpl-table-footer-cell.js +1 -1
  319. package/dist/components/xpl-table-footer.js +1 -1
  320. package/dist/components/xpl-table-header-cell.js +1 -1
  321. package/dist/components/xpl-table-header.js +1 -1
  322. package/dist/components/xpl-table-row.js +1 -1
  323. package/dist/components/xpl-table.js +1 -1
  324. package/dist/components/xpl-tabs.js +1 -1
  325. package/dist/components/xpl-tag.js +1 -1
  326. package/dist/components/xpl-toast.js +1 -1
  327. package/dist/components/xpl-toggle.js +1 -1
  328. package/dist/components/xpl-toolbar.js +1 -1
  329. package/dist/components/xpl-tooltip.js +1 -1
  330. package/dist/components/xpl-top-nav-item.js +1 -1
  331. package/dist/components/xpl-top-nav-menu.js +1 -1
  332. package/dist/components/xpl-top-nav.js +1 -1
  333. package/dist/components/xpl-utility-bar.js +1 -1
  334. package/dist/docs/xpl-accordion/readme.md +5 -4
  335. package/dist/docs/xpl-application-shell/readme.md +10 -0
  336. package/dist/docs/xpl-avatar/readme.md +7 -0
  337. package/dist/docs/xpl-badge/readme.md +7 -0
  338. package/dist/docs/xpl-breadcrumbs/xpl-breadcrumb-item/readme.md +1 -0
  339. package/dist/docs/xpl-breadcrumbs/xpl-breadcrumbs/readme.md +1 -0
  340. package/dist/docs/xpl-button/readme.md +7 -0
  341. package/dist/docs/xpl-checkbox/readme.md +7 -0
  342. package/dist/docs/xpl-content-area/readme.md +7 -0
  343. package/dist/docs/xpl-dashboard/readme.md +7 -0
  344. package/dist/docs/xpl-data-card/readme.md +1 -0
  345. package/dist/docs/xpl-divider/readme.md +7 -0
  346. package/dist/docs/xpl-dropdown/readme.md +8 -0
  347. package/dist/docs/xpl-dropdown/xpl-dropdown-group/readme.md +7 -0
  348. package/dist/docs/xpl-dropdown/xpl-dropdown-heading/readme.md +7 -0
  349. package/dist/docs/xpl-dropdown/xpl-dropdown-option/readme.md +7 -0
  350. package/dist/docs/xpl-dynamic-table/readme.md +8 -0
  351. package/dist/docs/xpl-dynamic-table-cell/readme.md +7 -0
  352. package/dist/docs/xpl-dynamic-table-row/readme.md +7 -0
  353. package/dist/docs/xpl-grid-item/readme.md +7 -0
  354. package/dist/docs/xpl-header-accordion/readme.md +8 -0
  355. package/dist/docs/xpl-icon/readme.md +1 -1
  356. package/dist/docs/xpl-input/readme.md +8 -0
  357. package/dist/docs/xpl-list/readme.md +7 -0
  358. package/dist/docs/xpl-list/xpl-list-item/readme.md +16 -0
  359. package/dist/docs/xpl-main-nav/readme.md +12 -0
  360. package/dist/docs/xpl-nav-item/readme.md +9 -0
  361. package/dist/docs/xpl-popover/readme.md +1 -0
  362. package/dist/docs/xpl-radio/readme.md +7 -0
  363. package/dist/docs/xpl-secondary-nav/readme.md +7 -0
  364. package/dist/docs/xpl-select/readme.md +7 -0
  365. package/dist/docs/xpl-side-nav/xpl-side-nav-item/readme.md +9 -0
  366. package/dist/docs/xpl-side-nav/xpl-side-nav-section/readme.md +8 -0
  367. package/dist/docs/xpl-slideout/readme.md +13 -0
  368. package/dist/docs/xpl-spotlight/readme.md +1 -0
  369. package/dist/docs/xpl-table/readme.md +12 -0
  370. package/dist/docs/xpl-table/xpl-table-body/readme.md +7 -0
  371. package/dist/docs/xpl-table/xpl-table-cell/readme.md +7 -0
  372. package/dist/docs/xpl-table/xpl-table-footer/readme.md +7 -0
  373. package/dist/docs/xpl-table/xpl-table-footer-cell/readme.md +7 -0
  374. package/dist/docs/xpl-table/xpl-table-header-cell/readme.md +59 -52
  375. package/dist/docs/xpl-table/xpl-table-row/readme.md +7 -0
  376. package/dist/docs/xpl-table-header/readme.md +7 -0
  377. package/dist/docs/xpl-tag/readme.md +9 -2
  378. package/dist/docs/xpl-toast/readme.md +8 -0
  379. package/dist/docs/xpl-toolbar/readme.md +8 -0
  380. package/dist/docs/xpl-tooltip/readme.md +7 -0
  381. package/dist/docs/xpl-top-nav/xpl-nav-header-menu/readme.md +1 -0
  382. package/dist/docs/xpl-top-nav/xpl-top-nav-item/readme.md +1 -0
  383. package/dist/docs/xpl-top-nav/xpl-top-nav-menu/readme.md +1 -0
  384. package/dist/docs/xpl-utility-bar/readme.md +8 -0
  385. package/dist/docs.json +770 -88
  386. package/dist/esm/apollo-core.js +3 -3
  387. package/dist/esm/{index-ukZKsqqq.js → index-CJABLO_1.js} +52 -11
  388. package/dist/esm/loader.js +2 -2
  389. package/dist/esm/xpl-accordion.entry.js +3 -3
  390. package/dist/esm/xpl-application-shell.entry.js +2 -2
  391. package/dist/esm/xpl-avatar_3.entry.js +5 -5
  392. package/dist/esm/xpl-backdrop.entry.js +2 -2
  393. package/dist/esm/xpl-badge.entry.js +2 -2
  394. package/dist/esm/xpl-banner.entry.js +3 -3
  395. package/dist/esm/xpl-breadcrumb-item.entry.js +2 -2
  396. package/dist/esm/xpl-breadcrumbs.entry.js +2 -2
  397. package/dist/esm/xpl-button-row.entry.js +2 -2
  398. package/dist/esm/xpl-button.entry.js +3 -3
  399. package/dist/esm/xpl-calendar.entry.js +2 -2
  400. package/dist/esm/xpl-checkbox.entry.js +4 -4
  401. package/dist/esm/xpl-choicelist.entry.js +2 -2
  402. package/dist/esm/xpl-content-area.entry.js +2 -2
  403. package/dist/esm/xpl-dashboard.entry.js +2 -2
  404. package/dist/esm/xpl-data-card.entry.js +3 -3
  405. package/dist/esm/xpl-dropdown_4.entry.js +9 -9
  406. package/dist/esm/xpl-dynamic-table-cell.entry.js +3 -3
  407. package/dist/esm/xpl-dynamic-table-row.entry.js +3 -3
  408. package/dist/esm/xpl-dynamic-table.entry.js +3 -3
  409. package/dist/esm/xpl-grid-item.entry.js +2 -2
  410. package/dist/esm/xpl-grid.entry.js +1 -1
  411. package/dist/esm/xpl-header-accordion.entry.js +2 -2
  412. package/dist/esm/xpl-icon.entry.js +1 -1
  413. package/dist/esm/xpl-input-color.entry.js +1 -1
  414. package/dist/esm/xpl-input-phone.entry.js +1 -1
  415. package/dist/esm/xpl-input-time.entry.js +1 -1
  416. package/dist/esm/xpl-input_4.entry.js +3 -3
  417. package/dist/esm/xpl-large-card.entry.js +1 -1
  418. package/dist/esm/xpl-list-item.entry.js +1 -1
  419. package/dist/esm/xpl-list.entry.js +3 -3
  420. package/dist/esm/xpl-main-nav.entry.js +2 -2
  421. package/dist/esm/xpl-modal.entry.js +1 -1
  422. package/dist/esm/xpl-nav-header-menu.entry.js +3 -3
  423. package/dist/esm/xpl-nav-item.entry.js +3 -3
  424. package/dist/esm/xpl-pagination.entry.js +1 -1
  425. package/dist/esm/xpl-panel.entry.js +1 -1
  426. package/dist/esm/xpl-popover.entry.js +5 -5
  427. package/dist/esm/xpl-progress-bar.entry.js +1 -1
  428. package/dist/esm/xpl-progress-indicator.entry.js +1 -1
  429. package/dist/esm/xpl-secondary-nav.entry.js +2 -2
  430. package/dist/esm/xpl-select_2.entry.js +11 -11
  431. package/dist/esm/xpl-side-nav-header-item.entry.js +1 -1
  432. package/dist/esm/xpl-side-nav-item.entry.js +3 -3
  433. package/dist/esm/xpl-side-nav-section.entry.js +6 -6
  434. package/dist/esm/xpl-side-nav.entry.js +1 -1
  435. package/dist/esm/xpl-skeleton.entry.js +1 -1
  436. package/dist/esm/xpl-slideout.entry.js +2 -2
  437. package/dist/esm/xpl-spotlight.entry.js +1 -1
  438. package/dist/esm/xpl-tab-panel.entry.js +1 -1
  439. package/dist/esm/xpl-tab.entry.js +1 -1
  440. package/dist/esm/xpl-table-body.entry.js +2 -2
  441. package/dist/esm/xpl-table-cell_3.entry.js +5 -5
  442. package/dist/esm/xpl-table-footer.entry.js +2 -2
  443. package/dist/esm/xpl-table-header.entry.js +2 -2
  444. package/dist/esm/xpl-table-row.entry.js +3 -3
  445. package/dist/esm/xpl-table.entry.js +1 -1
  446. package/dist/esm/xpl-tabs.entry.js +1 -1
  447. package/dist/esm/xpl-toast.entry.js +3 -3
  448. package/dist/esm/xpl-toggle.entry.js +1 -1
  449. package/dist/esm/xpl-toolbar.entry.js +3 -3
  450. package/dist/esm/xpl-tooltip.entry.js +2 -2
  451. package/dist/esm/xpl-top-nav-item.entry.js +3 -3
  452. package/dist/esm/xpl-top-nav-menu.entry.js +1 -1
  453. package/dist/esm/xpl-top-nav.entry.js +1 -1
  454. package/dist/esm/xpl-utility-bar.entry.js +2 -2
  455. package/dist/types/components.d.ts +3 -1
  456. package/dist/types/stencil-public-runtime.d.ts +12 -0
  457. package/hydrate/index.js +118 -99
  458. package/hydrate/index.mjs +118 -99
  459. package/loader/index.d.ts +1 -1
  460. package/package.json +13 -13
  461. package/dist/apollo-core/p-045a4538.entry.js +0 -1
  462. package/dist/apollo-core/p-08767607.entry.js +0 -1
  463. package/dist/apollo-core/p-194c0ccd.entry.js +0 -1
  464. package/dist/apollo-core/p-1c031055.entry.js +0 -1
  465. package/dist/apollo-core/p-24f7280a.entry.js +0 -1
  466. package/dist/apollo-core/p-386851a2.entry.js +0 -1
  467. package/dist/apollo-core/p-3cd1238b.entry.js +0 -1
  468. package/dist/apollo-core/p-4e3c6616.entry.js +0 -1
  469. package/dist/apollo-core/p-5b9fd96e.entry.js +0 -1
  470. package/dist/apollo-core/p-5bfac8fd.entry.js +0 -1
  471. package/dist/apollo-core/p-5cbf5d42.entry.js +0 -1
  472. package/dist/apollo-core/p-5ec2b180.entry.js +0 -1
  473. package/dist/apollo-core/p-65f6b66c.entry.js +0 -1
  474. package/dist/apollo-core/p-72e0455f.entry.js +0 -1
  475. package/dist/apollo-core/p-7ac7fbc0.entry.js +0 -1
  476. package/dist/apollo-core/p-7f4463ff.entry.js +0 -1
  477. package/dist/apollo-core/p-87582346.entry.js +0 -1
  478. package/dist/apollo-core/p-a6ecca68.entry.js +0 -1
  479. package/dist/apollo-core/p-a789c272.entry.js +0 -1
  480. package/dist/apollo-core/p-bd493c4a.entry.js +0 -1
  481. package/dist/apollo-core/p-bdcece21.entry.js +0 -1
  482. package/dist/apollo-core/p-bfb7a83a.entry.js +0 -1
  483. package/dist/apollo-core/p-c22f70f4.entry.js +0 -1
  484. package/dist/apollo-core/p-d3230277.entry.js +0 -1
  485. package/dist/apollo-core/p-d7f8ee34.entry.js +0 -1
  486. package/dist/apollo-core/p-d879c349.entry.js +0 -1
  487. package/dist/apollo-core/p-ea9cdd65.entry.js +0 -1
  488. package/dist/apollo-core/p-ukZKsqqq.js +0 -2
  489. package/dist/components/p-B6YLKDUX.js +0 -1
  490. package/dist/components/p-BSHdGbmH.js +0 -1
  491. package/dist/components/p-BZIZSovL.js +0 -1
  492. package/dist/components/p-BtLC-jpk.js +0 -1
  493. package/dist/components/p-CgemLtmL.js +0 -1
  494. package/dist/components/p-Dp62sBqX.js +0 -1
  495. package/dist/components/p-DqgnC_vS.js +0 -1
  496. package/dist/components/p-DtV4i45B.js +0 -1
package/dist/docs.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
- "timestamp": "2026-08-26T18:32:08",
2
+ "timestamp": "2026-09-04T20:03:46",
3
3
  "compiler": {
4
4
  "name": "@stencil/core",
5
- "version": "4.43.5",
5
+ "version": "4.44.2",
6
6
  "typescriptVersion": "5.8.3"
7
7
  },
8
8
  "components": [
@@ -17,6 +17,10 @@
17
17
  "name": "slot",
18
18
  "text": "heading - The header for the accordion component"
19
19
  },
20
+ {
21
+ "name": "slot",
22
+ "text": "description - Additional text displayed under the header. Falls back to the `description` prop."
23
+ },
20
24
  {
21
25
  "name": "slot",
22
26
  "text": "content - Content rendered inside of the collapsible body of the accordion."
@@ -265,6 +269,10 @@
265
269
  "name": "content",
266
270
  "docs": "Content rendered inside of the collapsible body of the accordion."
267
271
  },
272
+ {
273
+ "name": "description",
274
+ "docs": "Additional text displayed under the header. Falls back to the `description` prop."
275
+ },
268
276
  {
269
277
  "name": "heading",
270
278
  "docs": "The header for the accordion component"
@@ -289,6 +297,22 @@
289
297
  "readme": "# xpl-application-shell\n\n`xpl-application-shell` is the top-level layout container for an Apollo application. It arranges the main navigation rail, an optional utility bar, an optional secondary navigation column, and the page content into a responsive CSS grid, and drives the CSS-only open/close animation of the navigation rail.\n\n## Usage\n\n```html\n<xpl-application-shell with-subnav is-nav-open-at-startup=\"true\">\n <xpl-main-nav slot=\"nav\" width=\"default\"><!-- ... --></xpl-main-nav>\n <xpl-utility-bar slot=\"utility\"><!-- ... --></xpl-utility-bar>\n <xpl-secondary-nav slot=\"subnav\"><!-- ... --></xpl-secondary-nav>\n <xpl-content-area slot=\"content\"><!-- ... --></xpl-content-area>\n</xpl-application-shell>\n```\n\n## Design Tokens\n\nTokens used by this component from `@xplortech/apollo-foundation`. All purpose-specific tokens theme automatically in dark mode (no `.dark` overrides in the CSS).\n\n**Color**\n\n| Element | Property | Token | Resolves (light / dark) |\n| --- | --- | --- | --- |\n| Host nav rail | `background-color` | `--xpl-background-surface-inverse-on-light` | neutral-1000 / neutral-1000 (always dark) |\n| `&__subnav` | `background-color` | `--xpl-background-surface-default` | neutral-0 / neutral-1000 |\n| `&__content` | `background-color` | `--xpl-background-surface-subtlest` | neutral-50 / neutral-900 |\n| `&__subnav`, `&__content` | `color` (on-surface text) | `--xpl-text-default` | neutral-900 / neutral-50 |\n| `&__subnav` (top), `&__content` (left/top), `&__utility` (left) | `border-color` | `--xpl-border-default` | neutral-400 / neutral-600 |\n\n**Border width**\n\n- `--xpl-border-small` (0.0625rem / 1px) — all shell dividers.\n\n**Layout literals (not themeable)**\n\nThe nav rail widths / transforms (`74px`, `112px`, `240px`) drive the CSS-only open/close animation and are kept as literals; no Foundation size token set covers animated grid-track widths. `grid-template`, `100vh`, and `-webkit-fill-available` are also kept as literals. See `TECH_DEBT.md`.\n",
290
298
  "docs": "`xpl-application-shell` is the top-level layout container for an Apollo application. It arranges the main navigation rail, an optional utility bar, an optional secondary navigation column, and the page content into a responsive CSS grid, and drives the CSS-only open/close animation of the navigation rail.",
291
299
  "docsTags": [
300
+ {
301
+ "name": "slot",
302
+ "text": "nav - The primary navigation area (e.g. `xpl-main-nav` or `xpl-side-nav`)."
303
+ },
304
+ {
305
+ "name": "slot",
306
+ "text": "subnav - The secondary navigation area (e.g. `xpl-secondary-nav`)."
307
+ },
308
+ {
309
+ "name": "slot",
310
+ "text": "utility - The utility bar area (e.g. `xpl-utility-bar`)."
311
+ },
312
+ {
313
+ "name": "slot",
314
+ "text": "content - The main page content area."
315
+ },
292
316
  {
293
317
  "name": "since",
294
318
  "text": "0.5.0"
@@ -449,7 +473,24 @@
449
473
  }
450
474
  ],
451
475
  "styles": [],
452
- "slots": [],
476
+ "slots": [
477
+ {
478
+ "name": "content",
479
+ "docs": "The main page content area."
480
+ },
481
+ {
482
+ "name": "nav",
483
+ "docs": "The primary navigation area (e.g. `xpl-main-nav` or `xpl-side-nav`)."
484
+ },
485
+ {
486
+ "name": "subnav",
487
+ "docs": "The secondary navigation area (e.g. `xpl-secondary-nav`)."
488
+ },
489
+ {
490
+ "name": "utility",
491
+ "docs": "The utility bar area (e.g. `xpl-utility-bar`)."
492
+ }
493
+ ],
453
494
  "parts": [],
454
495
  "states": [],
455
496
  "dependents": [],
@@ -469,6 +510,10 @@
469
510
  "readme": "# xpl-avatar\n\nThe **Avatar** component shows a person or entity: a photo, initials on a colored surface, or custom slot content. It aligns with Apollo’s Figma patterns for default avatars, profile layouts, stacked groups, and rows with supporting text.\n\n## When to use\n\n- **Default (no `variant`)** — Inline UI: tables, nav, comments, lists. Supports image (`src`), initials from `name`, and an optional status dot (`status`).\n- **Profile (`variant=\"profile\"`)** — Large avatars for profile pages and settings surfaces; optional `status` dot uses the same badge palette as the default avatar.\n- **Group (`variant=\"group\"`)** — Stacked avatar groups with overflow count (`+N`). The `status` prop is not used (status dots are not shown for groups).\n- **With Text (`variant=\"with-text\"`)** — Horizontal layout with avatar paired with name and optional sub-text; optional `status` on the avatar image.\n\n\n| Variant | Purpose | `status` dot |\n| ----------- | ------------------------------------------------------ | ------------ |\n| *(none)* | Standard single avatar | Supported |\n| `profile` | Larger profile-sized avatar for settings/profile pages | Supported |\n| `group` | Stacked avatar group with overflow count (`+N`) | Not shown |\n| `with-text` | Avatar paired with name and optional sub-text | Supported |\n\n\n---\n\n## Quick Start\n\n### Basic Avatar (Image)\n\n```html\n<xpl-avatar name=\"Katie Wilson\" src=\"https://example.com/photo.jpg\"></xpl-avatar>\n```\n\n### Initials Placeholder (No Image)\n\n```html\n<!-- Automatic initials from name -->\n<xpl-avatar name=\"Katie Wilson\" color=\"green\"></xpl-avatar>\n\n<!-- Custom initials via slot -->\n<xpl-avatar color=\"primary\">KW</xpl-avatar>\n```\n\n### With Status Dot\n\nStatus dots are rendered with [`xpl-badge`] using `dot-only` and `bordered`. The `status` prop sets the badge **variant** , so avatar status colors stay aligned with badge semantics across the system.\n\n**Where `status` applies:** default (no `variant`), `profile`, and `with-text`. It does **not** apply to `group` — stacked groups ignore `status`.\n\n**`status` values** (same names as `xpl-badge` `variant` palette values):\n\n\n| `status` | Badge palette | Typical meaning (choose per product copy) |\n| -------- | ---------------- | ----------------------------------------- |\n| `purple` | Purple (default) | Default / general presence |\n| `green` | Green | Positive, available, success-aligned |\n| `yellow` | Yellow | Caution, pending, away, warning-aligned |\n| `orange` | Orange | Attention, busy, secondary emphasis |\n| `gray` | Gray | Neutral, offline, inactive |\n| `red` | Red | Error, do not disturb, critical |\n| `blue` | Blue | Informational, notifications |\n\n\n```html\n<xpl-avatar name=\"Alex Chen\" src=\"photo.jpg\" status=\"green\"></xpl-avatar>\n<xpl-avatar name=\"Jordan Lee\" status=\"red\"></xpl-avatar>\n<xpl-avatar name=\"Sam Smith\" status=\"gray\"></xpl-avatar>\n<xpl-avatar variant=\"with-text\" name=\"Riley Park\" sub-text=\"Designer\" src=\"photo.jpg\" status=\"blue\"></xpl-avatar>\n```\n\n### Clickable Avatar (Link)\n\n```html\n<xpl-avatar name=\"Profile\" src=\"photo.jpg\" href=\"/profile\" target=\"_blank\"></xpl-avatar>\n```\n\n---\n\n## Variant Examples\n\n### Profile Avatar (Larger Size)\n\nFor profile pages and settings surfaces. Optional `status` uses the same badge palette as other non-group variants.\n\n```html\n<!-- Large profile avatar (144px) -->\n<xpl-avatar variant=\"profile\" name=\"Katie Wilson\" src=\"photo.jpg\"></xpl-avatar>\n\n<!-- With status dot -->\n<xpl-avatar variant=\"profile\" name=\"Katie Wilson\" src=\"photo.jpg\" status=\"green\"></xpl-avatar>\n\n<!-- Small profile avatar (80px) -->\n<xpl-avatar variant=\"profile\" name=\"Jordan Lee\" size=\"sm\"></xpl-avatar>\n```\n\n### Avatar Group (Stacked)\n\nDisplays up to 4 visible avatars with a `+N` overflow pill for additional members. The `status` prop is not used in this layout (no per-avatar or group-level status dot).\n\n```html\n<!-- Basic group with JSON data -->\n<xpl-avatar\n variant=\"group\"\n group-data='[\n {\"name\": \"Alex\", \"color\": \"green\"},\n {\"name\": \"Jordan\", \"color\": \"yellow\"},\n {\"name\": \"Sam\", \"color\": \"pink\"},\n {\"src\": \"https://example.com/photo.jpg\", \"name\": \"Dana\"},\n {\"name\": \"Extra User\"}\n ]'\n></xpl-avatar>\n\n<!-- Medium group -->\n<xpl-avatar variant=\"group\" size=\"md\" group-data='[...]'></xpl-avatar>\n\n<!-- Small group -->\n<xpl-avatar variant=\"group\" size=\"sm\" group-data='[...]'></xpl-avatar>\n```\n\n**Group data format:**\n\n```typescript\ninterface AvatarGroupItem {\n src?: string; // Image URL (optional)\n name?: string; // Used for initials and alt text\n color?: 'green' | 'yellow' | 'pink' | 'primary' | 'secondary';\n}\n```\n\n### Avatar with Text\n\nHorizontal layout with avatar + name + optional sub-text (e.g., email).\n\n```html\n<!-- Basic with-text -->\n<xpl-avatar\n variant=\"with-text\"\n name=\"Katie Wilson\"\n sub-text=\"katie@example.com\"\n src=\"photo.jpg\"\n></xpl-avatar>\n\n<!-- Clickable version -->\n<xpl-avatar\n variant=\"with-text\"\n name=\"View Profile\"\n sub-text=\"Product Designer\"\n src=\"photo.jpg\"\n href=\"/profile\"\n></xpl-avatar>\n\n<!-- With status dot on the avatar -->\n<xpl-avatar\n variant=\"with-text\"\n name=\"Katie Wilson\"\n sub-text=\"katie@example.com\"\n src=\"photo.jpg\"\n status=\"green\"\n></xpl-avatar>\n```\n\n---\n\n## Size Options\n\n\n| Size | Default Avatar | Profile Avatar | Group |\n| -------- | -------------- | -------------- | ------------------ |\n| *(none)* | 40×40px | 144×144px | 40×40px per avatar |\n| `md` | 32×32px | — | 32×32px per avatar |\n| `sm` | 24×24px | 80×80px | 24×24px per avatar |\n\n\n---\n\n## Status vs placeholder `color`\n\n- **`status`** — Badge palette for the **status dot** (`purple` | `green` | `yellow` | `orange` | `gray` | `red` | `blue`). Passed through to `xpl-badge` as `variant`. Not used when `variant=\"group\"`.\n- **`color`** — Placeholder **surface** behind initials when there is no `src` (`green` | `yellow` | `pink` | `primary` | `secondary`). Unrelated to the status dot.\n\n## Color Options (Placeholder Backgrounds)\n\nUse `color` when no `src` is provided to set the placeholder background:\n\n- `green` — Positive accent\n- `yellow` — Highlight accent\n- `pink` — Pink accent\n- `primary` — Brand primary\n- `secondary` — Action primary (purple)\n\n---\n\n## Design tokens\n\nThese are the main Apollo Foundation tokens used in `packages/apollo-core/src/css/avatar.css`. Use them when overriding or migrating styles.\n\n| Role | Token(s) |\n| ---- | -------- |\n| Circular shape | `var(--xpl-border-radius-full)` |\n| Placeholder surface (default) | `var(--xpl-background-brand-secondary)`, `var(--xpl-text-inverse)` |\n| Placeholder `green` / `yellow` / `pink` | `var(--xpl-background-accent-positive)` / `var(--xpl-background-accent-highlight)` / `var(--xpl-background-accent-pink)` with `var(--xpl-text-inverse-on-light)` where applicable |\n| Placeholder `primary` / `secondary` | `var(--xpl-background-accent-brand)` / `var(--xpl-background-brand-secondary)` |\n| Link hover overlay | `var(--xpl-background-surface-transparent-25)` |\n| Focus outline | `var(--xpl-border-accent-default)` base; `color` modifiers use `var(--xpl-border-positive)`, `var(--xpl-border-accent-highlight)`, `var(--xpl-border-accent-brand)`, etc. (`outline` / `outline-offset` per `avatar.css`) |\n| With-text primary line | `var(--xpl-text-strong)`, `var(--xpl-font-size-title-5)`, `var(--xpl-font-weight-medium)` |\n| With-text secondary line | `var(--xpl-text-subdued)`, `var(--xpl-font-size-body)` |\n| Group overflow pill | `var(--xpl-background-surface-subtle)`, `var(--xpl-text-subdued)` |\n| Group avatar ring | `box-shadow` using `var(--xpl-border-large)` and `var(--xpl-background-surface-default)` |\n| Width / height | `var(--xpl-size-*)` for standard sizes; profile default uses `var(--xpl-size-900)` (144px) |\n| Spacing & overlap | `var(--xpl-space-*)`, `var(--xpl-z-index-*)` |\n\n**Status dots** are styled by `xpl-badge`; see [`xpl-badge` readme](../xpl-badge/readme.md) for badge token usage.\n\n---\n\n## Accessibility\n\n- `name` is required for proper `alt` text on images and screen reader announcements\n- **Status dots** are implemented via `xpl-badge` and marked `aria-hidden` (purely visual); convey meaning in surrounding UI copy or labels if required\n- **Group overflow** includes `aria-label=\"{count} more\"` for screen readers\n- **Links** use native `<a>` elements with full keyboard support\n- **Disabled state**: Use `disabled` prop to disable link behavior while keeping visual styles\n- **Focus management**: All interactive avatars (links) use `:focus-visible` for keyboard focus indicators\n\n---\n\n## Best Practices\n\n1. **Always provide `name`** — Essential for accessibility (alt text and screen readers; for placeholder initials, slot input has higher priority than the value passed in `name`)\n2. **Use `color` for placeholder consistency** — Assign consistent colors to users based on ID or name hash\n3. **Limit group data** — While the component handles any number, consider capping at reasonable numbers for UX\n4. **Slot content** — Use the default slot sparingly; prefer `name` prop for consistent initials generation\n\n---\n\n## Migration Notes\n\n### Status Dot Implementation:\n\n**What changed:** Status dots are implemented with `xpl-badge` (`dot-only` + `bordered`). The `status` prop uses the **badge color palette** (`purple`, `green`, `yellow`, `orange`, `gray`, `red`, `blue`) — the same names as `xpl-badge` `variant` — so avatar status stays consistent with badge semantics.\n\n**Impact:**\n\n- **Internal markup:** The status dot renders as `<xpl-badge dot-only bordered variant=\"...\">` (with `variant` from `status`), not legacy `.xpl-avatar__dot--` classes.\n- **CSS targeting:** Custom selectors like `.xpl-avatar__dot--active` / `--warning` / `--inactive` are not used. Target `.xpl-avatar .xpl-badge--dot-only` if you must override styles.\n- **Prop values:** Use palette names above. Older examples sometimes used labels like `active` / `warning` / `inactive`; map those concepts to the palette (e.g. available → `green`, caution → `yellow`, offline → `gray`) per your design guidelines.\n\n**Recommended migration:**\n\n1. Replace custom CSS on `.xpl-avatar__dot--` with `.xpl-avatar .xpl-badge--dot-only` if needed\n2. Set `status` to the badge palette value that matches the intended color (see the status table in this readme)\n3. Remember `status` applies to default, `profile`, and `with-text` only — not `group`\n\n### Legacy `status` values — scheduled for removal (next major release)\n\n**These legacy string values are deprecated.** They exist only for backward compatibility with older integrations. They **will be removed** in the **next major** version of `@xplortech/apollo-core`. Plan your migration now; do not introduce new usage of the legacy names.\n\n| Deprecated `status` | Current mapping (temporary) | Replace with (badge palette) |\n| ------------------- | --------------------------- | ---------------------------- |\n| `active` | `green` | `green` |\n| `inactive` | `gray` | `gray` |\n| `warning` | `red` | `red` |\n\n**Why:** Status dots use the same vocabulary as `xpl-badge` variants (`purple`, `green`, `yellow`, `orange`, `gray`, `red`, `blue`). The old semantic aliases (`active` / `warning` / `inactive`) duplicate that meaning with different names and increase maintenance cost, so they are being dropped from the public API.\n\n**Action items before the major release:**\n\n1. Search your codebase for `status=\"active\"`, `status=\"inactive\"`, and `status=\"warning\"` on `<xpl-avatar>` (and framework wrappers).\n2. Replace each with the **`green`**, **`gray`**, or **`red`** value from the table above, matching your product intent.\n3. Re-test visuals: the mapped colors are unchanged today; after removal, only palette names will be accepted.\n\n### Component Dependency\n\nThis component now depends on `xpl-badge`. Ensure `xpl-badge` is loaded in your application when using avatars with status dots.\n\n",
470
511
  "docs": "The **Avatar** component shows a person or entity: a photo, initials on a colored surface, or custom slot content. It aligns with Apollo’s Figma patterns for default avatars, profile layouts, stacked groups, and rows with supporting text.",
471
512
  "docsTags": [
513
+ {
514
+ "name": "slot",
515
+ "text": "- Custom placeholder content (e.g. initials) shown when no `src` image is available."
516
+ },
472
517
  {
473
518
  "name": "since",
474
519
  "text": "0.1.1"
@@ -841,7 +886,12 @@
841
886
  "events": [],
842
887
  "listeners": [],
843
888
  "styles": [],
844
- "slots": [],
889
+ "slots": [
890
+ {
891
+ "name": "",
892
+ "docs": "Custom placeholder content (e.g. initials) shown when no `src` image is available."
893
+ }
894
+ ],
845
895
  "parts": [],
846
896
  "states": [],
847
897
  "dependents": [
@@ -943,6 +993,10 @@
943
993
  "readme": "# xpl-badge\n\n`xpl-badge` is a small, non-interactive status indicator used to communicate counts, states, or supplementary information next to another element. Badges are **never clickable** – use a button or link if you need interaction.\n\nBadges support multiple **variants** (colors), **sizes**, **shapes**, and **dot styles**, so they can be used both as pill labels and as compact status / notification indicators.\n\n## Usage\n\n```html\n<xpl-badge variant=\"green\">Active</xpl-badge>\n<xpl-badge variant=\"yellow\">Pending</xpl-badge>\n<xpl-badge variant=\"red\">Error</xpl-badge>\n<xpl-badge variant=\"gray\" size=\"small\">Draft</xpl-badge>\n```\n\nAttach badges close to the item they describe (a label, row, chip, or control). Prefer **short labels** (often one or two words) for readability.\n\n### Label text and overflow\n\nSlot content is rendered inside an internal `.xpl-badge__label` wrapper (you do not add this in your markup). Labels are **single-line**: text does not wrap to multiple lines. When horizontal space is tight (for example a narrow column or flex layout), overflowing text **truncates with an ellipsis** (`…`). The host respects the parent width (`max-width: 100%`). If truncation makes a label hard to read, shorten the copy or widen the layout; use a tooltip or similar only if your product needs the full string on hover.\n\n## Sizes & shapes\n\n- **size**\n - `default` (default): `24px` tall pill, used for most cases.\n - `small`: `20px` tall, more compact for dense UIs or table rows.\n- **shape**\n - `rounded` (default): full pill with fully rounded ends.\n - `boxy`: rounded corners but not a full pill; better for more neutral / data-heavy contexts.\n\n```html\n<xpl-badge size=\"default\" shape=\"rounded\">Default pill</xpl-badge>\n<xpl-badge size=\"small\" shape=\"boxy\">Small boxy</xpl-badge>\n```\n\n## Dots and dot-only badges\n\n### Dot badge (pill with leading dot)\n\nSet `dot` to add a colored dot before the text, inside the pill:\n\n```html\n<xpl-badge variant=\"green\" dot>Online</xpl-badge>\n<xpl-badge variant=\"yellow\" dot>Syncing</xpl-badge>\n<xpl-badge variant=\"red\" dot>Error</xpl-badge>\n```\n\n### Dot-only badge (compact status / count)\n\n> [!IMPORTANT]\n> Dot-only badges are meant to be used with **numeric values** (or very short strings). For longer text labels, use the standard badge (without `dot-only`).\n\n- When overlaying another element with a background color, use the `bordered` property so the badge separates clearly from what is behind it.\n\nSet `dot-only` when you want a badge built around the dot:\n\n- With **no children**: renders a small circular status dot.\n- With **children**: renders a compact pill with text or a number inside (for example an unread count).\n- **Notification-style counts** are often appended to a label or another component. Dot-only label text follows the same **single-line truncation** rules as standard badges when space is limited.\n- Use **`bordered`** to toggle a ring around the dot badge for separation from an avatar or similar surface.\n\n```html\n<!-- Status dot only -->\n<xpl-badge variant=\"green\" dot-only></xpl-badge>\n\n<!-- Dot with count / text -->\n<xpl-badge variant=\"red\" dot-only>3</xpl-badge>\n<xpl-badge variant=\"blue\" dot-only>99+</xpl-badge>\n```\n\n## Variants\n\nUse variants to communicate state or category. Preferred values for `variant`:\n\n- `purple` (default)\n- `green`\n- `yellow`\n- `orange`\n- `gray`\n- `red`\n- `blue`\n\nThe legacy semantic names `success`, `warning`, `inactive`, and `error` are still accepted and map to `green`, `yellow`, `gray`, and `red` respectively. Prefer the color names for new code.\n\n```html\n<xpl-badge variant=\"purple\">Default</xpl-badge>\n<xpl-badge variant=\"orange\" dot>In progress</xpl-badge>\n<xpl-badge variant=\"blue\" dot-only>12</xpl-badge>\n```\n\n- Each dot has **stronger solid color** for dot-only badges, with dark-mode adjustments handled by CSS.\n\n- Each variant uses Apollo Foundation badge tokens (`--xpl-background-badge-`*, `--xpl-border-badge-*`, `--xpl-icon-badge-*`, `--xpl-text-badge-*`) that auto-switch between light and dark modes. Dot-only badges use the corresponding `--xpl-icon-badge-*` accent color for their solid fill.\n\n### Design Tokens\n\nTokens used by this component from `@xplortech/apollo-foundation`.\n\n**Color**\n- `--xpl-background-badge-{default,green,yellow,grey,blue,red}` — variant backgrounds\n- `--xpl-border-badge-{purple,green,yellow,grey,blue,red}` — variant borders\n- `--xpl-text-badge-{default,green,yellow,gray,blue,red}` — variant text colors\n- `--xpl-icon-badge-{default,green,yellow,grey,blue,red}` — dot / dot-only fill\n- `--xpl-text-inverse` — dot-only text (green, blue, red)\n- `--xpl-text-strong` — orange variant text\n- `--xpl-border-inverse` — dot-only bordered ring\n- `--xpl-color-transparent-0` — dot-only / orange transparent border\n- `--xpl-color-neutral-1100` — dot-only text on yellow, gray, orange\n- `--xpl-background-surface-transparent-0` — orange background (dark mode)\n- `--xpl-background-accent-brand` — orange dot/dot-only fill (tech debt)\n- `--xpl-color-orange-{100,300}` — orange variant primitives (tech debt)\n\n**Spacing**\n- `--xpl-space-{2,4,8,12}` — padding variants\n\n**Sizing**\n- `--xpl-size-{150,125,050}` — height / dot sizes\n- `0.875rem` literal — dot-only badge height/min-width (no 14px Foundation token; tech debt)\n\n**Typography**\n- `--xpl-font-family-default` — font family\n- Default badge extends `.xpl-text-title-5` (Figma `Title/5`); small badge extends `.xpl-text-callout-bold` (Figma `Callout/Bold`)\n- `0.5rem` literal — dot-only (number) badge font-size/line-height (no 8px Foundation token or typography level; tech debt)\n\n**Border**\n- `--xpl-border-small` — border width (1px)\n- `--xpl-border-radius-{full,small}` — shape variants\n",
944
994
  "docs": "`xpl-badge` is a small, non-interactive status indicator used to communicate counts, states, or supplementary information next to another element. Badges are **never clickable** – use a button or link if you need interaction.\n\nBadges support multiple **variants** (colors), **sizes**, **shapes**, and **dot styles**, so they can be used both as pill labels and as compact status / notification indicators.",
945
995
  "docsTags": [
996
+ {
997
+ "name": "slot",
998
+ "text": "- The badge label content."
999
+ },
946
1000
  {
947
1001
  "name": "since",
948
1002
  "text": "0.1.1"
@@ -1180,7 +1234,12 @@
1180
1234
  "events": [],
1181
1235
  "listeners": [],
1182
1236
  "styles": [],
1183
- "slots": [],
1237
+ "slots": [
1238
+ {
1239
+ "name": "",
1240
+ "docs": "The badge label content."
1241
+ }
1242
+ ],
1184
1243
  "parts": [],
1185
1244
  "states": [],
1186
1245
  "dependents": [
@@ -1550,6 +1609,10 @@
1550
1609
  "listeners": [],
1551
1610
  "styles": [],
1552
1611
  "slots": [
1612
+ {
1613
+ "name": "",
1614
+ "docs": "The default slot"
1615
+ },
1553
1616
  {
1554
1617
  "name": "unnamed",
1555
1618
  "docs": "content rendered inside <li>, should be link or button (styled as link)"
@@ -1580,6 +1643,10 @@
1580
1643
  "listeners": [],
1581
1644
  "styles": [],
1582
1645
  "slots": [
1646
+ {
1647
+ "name": "",
1648
+ "docs": "The default slot"
1649
+ },
1583
1650
  {
1584
1651
  "name": "unnamed",
1585
1652
  "docs": "content rendered <ol>, should be a group of <li> or <xpl-breadcrumb-item>"
@@ -1598,6 +1665,10 @@
1598
1665
  "readme": "# xpl-button\n\nButtons allow users to take an action with clear visual hierarchy (variant), semantic meaning (sentiment), and size.\n\n## Usage\n\n> [!IMPORTANT]\n> Older props such as `state` still work, but we recommend using the sentiment described below.\n\n\n### Basic button\n\nPass button text via the default slot (same as native `<button>`):\n\n```html\n<xpl-button>Primary action</xpl-button>\n```\n\n### Variant and sentiment\n\nUse `variant` for style (primary, secondary, tertiary) and `sentiment` for intent (default, positive, negative):\n\n```html\n<xpl-button variant=\"secondary\">Secondary</xpl-button>\n<xpl-button variant=\"tertiary\" sentiment=\"positive\">Positive action</xpl-button>\n<xpl-button sentiment=\"negative\">Destructive</xpl-button>\n```\n\n### Size\n\nUse **`size`** for button size: `default`, `sm` (small), or `xs` (extra small):\n\n```html\n<xpl-button size=\"sm\">Small</xpl-button>\n<xpl-button size=\"xs\">Extra small</xpl-button>\n```\n\n### Icon from Apollo icon library\n\nUse `icon` with a name from the xpl-icon library; use `icon-position` to place the icon at start or end:\n\n```html\n<xpl-button icon=\"download\" icon-position=\"end\">\n Download\n</xpl-button>\n```\n\n### Icon-only button\n\nUse `icon-only` when the button shows only an icon (no label). Always set `icon` and use a descriptive `aria-label` on the host or inner button for accessibility:\n\n```html\n<xpl-button icon-only icon=\"xmark\" aria-label=\"Close\"></xpl-button>\n```\n\n### Loading state\n\nWhen `is-loading` is true, the button shows a spinner, hides the label and icon, stays the same size, and is non-interactive:\n\n```html\n<xpl-button is-loading>Submitting</xpl-button>\n```\n\n### Form submission\n\nThe component renders a native `<button>`. Use `type` (`submit`, `reset`, or `button`), `name`, `value`, and `form` for form behaviour:\n\n```html\n<form id=\"my-form\">\n <xpl-button type=\"submit\" name=\"action\" value=\"save\">Save</xpl-button>\n</form>\n```\n\n### Disabled\n\nUse the `disabled` prop to disable the button (same as native `disabled`):\n\n```html\n<xpl-button disabled>Disabled</xpl-button>\n```\n\n## DOM classes\n\nThe host element includes BEM-style classes, including:\n\n- **Size:** `xpl-button--default` | `xpl-button--sm` | `xpl-button--xs`\n- **Variant:** `xpl-button--primary` | `xpl-button--secondary` | `xpl-button--tertiary` (and `xpl-button--subtle` while that value is still supported)\n- **Sentiment:** `xpl-button--neutral` | `xpl-button--positive` | `xpl-button--negative` when using `sentiment`\n- **Deprecated `state`:** if set, legacy classes `xpl-button--neutral` | `xpl-button--warning` | `xpl-button--success` are applied instead of the sentiment-based classes above (see migration)\n\nPrefer targeting **`sentiment`** + **`variant`** + **`size`** in new code; avoid relying on deprecated `state` class names for new selectors.\n\n## Migration Notes:\n\nThe class **`xpl-button--default`** on the host continues to mean **size** (`size=\"default\"`), not sentiment.\n\n### Deprecated `state` prop\n\nThe **`state`** prop (`neutral` | `warning` | `success`) is deprecated. Use **`sentiment`** instead:\n\n| Old `state` | New `sentiment` |\n| ------------- | ----------------- |\n| `neutral` | `neutral` (default) |\n| `warning` | `negative` |\n| `success` | `positive` |\n\n**Backward compatibility:** if **`state`** is set, it controls the sentiment segment of the host class list (`xpl-button--neutral`, `xpl-button--warning`, `xpl-button--success`) so existing styles or tests that key off those class names can keep working. New work should use **`sentiment`** only (`neutral` | `negative` | `positive`).\n\n### Deprecated `variant=\"subtle\"` → `tertiary`\n\n**`variant=\"subtle\"`** is deprecated and retained only for backward compatibility, applying the **`xpl-button--subtle`** class. Use **`variant=\"tertiary\"`** instead, which applies **`xpl-button--tertiary`**.\n\n### Other API notes\n\n- **`isLoading` / `is-loading`:** loading UI without forcing the disabled (gray) visual treatment.\n- **`icon`**, **`icon-position`**, **`icon-only`:** use Apollo icon names; no inline SVGs required.\n- **`data-*`, `aria-*`, and `role` on the host:** mirrored onto the inner `<button>` after render for accessibility and testing tools. After forwarding, these attributes are removed from the host element. Avoid relying on `role` on the host for external selectors.\n\n## Guidelines\n\n- **Pass button content via the default slot:** `<xpl-button>Save</xpl-button>`. Slots support rich content (formatted text, inline icons, etc.).\n- Use **`iconOnly`** for icon-only buttons and provide an **`aria-label`** (or ensure the inner button has an accessible name).\n- Use **`icon`** for icon names from the xpl-icon library; use **`icon-position`** to place the icon at start or end of the slot content.\n\n## Accessibility\n\n- The component renders a native `<button>`, so it is focusable and keyboard-activable by default.\n- When **`is-loading`** is true, the inner button has `aria-busy=\"true\"` and the loading spinner is marked **`aria-hidden=\"true\"`** so the label remains the accessible name.\n- For **icon-only** buttons, provide an accessible name via **`aria-label`** on the host (e.g. `<xpl-button icon-only icon=\"xmark\" aria-label=\"Close\">`) so screen readers announce the action.\n- For **WAI-ARIA composite widgets** (like tabs), use the **`controlAttrs`** prop to apply `id`, `role`, `aria-controls`, `aria-selected`, and `aria-current` directly to the inner `<button>`. This is preferred over setting these attributes on the host, which would be forwarded and removed from the host.\n",
1599
1666
  "docs": "Buttons allow users to take an action with clear visual hierarchy (variant), semantic meaning (sentiment), and size.",
1600
1667
  "docsTags": [
1668
+ {
1669
+ "name": "slot",
1670
+ "text": "- The button label content."
1671
+ },
1601
1672
  {
1602
1673
  "name": "since",
1603
1674
  "text": "0.1.1"
@@ -2100,7 +2171,12 @@
2100
2171
  }
2101
2172
  ],
2102
2173
  "styles": [],
2103
- "slots": [],
2174
+ "slots": [
2175
+ {
2176
+ "name": "",
2177
+ "docs": "The button label content."
2178
+ }
2179
+ ],
2104
2180
  "parts": [],
2105
2181
  "states": [],
2106
2182
  "dependents": [
@@ -2554,6 +2630,10 @@
2554
2630
  "readme": "# xpl-checkbox\n\nCheckboxes allow users to make selections or choices. Checkboxes can be multi-select, and also act as controls.\n\nProvide visible label text in the default slot, or set `aria-label` when the checkbox has no adjacent label (for example, icon-only toolbars or table selection cells).\n\n## Design Tokens\n\nThe checkbox component uses the following Apollo Foundation tokens:\n\n| Token | Usage |\n|-------|-------|\n| `--xpl-background-surface-default` | Default checkbox background; styled label background (resting/hover/focused) |\n| `--xpl-background-action-primary-default` | Checked/indeterminate state background and border |\n| `--xpl-background-action-disabled` | Disabled state background; disabled label background (styled variant) |\n| `--xpl-background-surface-information` | Checked/indeterminate label background (styled variant) |\n| `--xpl-text-inverse` | Checkmark and indeterminate icon color (checked / indeterminate states; inverse foreground on primary fill) |\n| `--xpl-icon-disabled` | Checkmark and indeterminate icon color (disabled state) |\n| `--xpl-border-strong` | Resting (unchecked) border color |\n| `--xpl-border-hover` | Hover, checked, and indeterminate border color; styled container hover/focus border |\n| `--xpl-border-default` | Styled container border; disabled state border |\n| `--xpl-border-focus` | Focus visible outline |\n| `--xpl-border-radius-small` | Checkbox corner radius and styled container corner radius (4px) |\n| `--xpl-border-small` | Border width (1px) |\n| `--xpl-size-100` | Checkbox dimensions (16px) |\n\n### Label tokens (shared `label.css`)\n\nThe checkbox label and description are styled by the shared `.xpl-label` / `.xpl-description` classes.\n\n| Token | Usage |\n|-------|-------|\n| `--xpl-text-strong` | Label text color (matches `.xpl-input-label`; resolves to white in dark mode) |\n| `--xpl-text-disabled` | Disabled label text color |\n| `--xpl-text-subdued` | Description text color (enabled and disabled) |\n| `.xpl-text-body` | Label typography: `--xpl-font-size-body` (14px) / normal / 1.4286 line-height |\n| `.xpl-text-callout` | Description typography: `--xpl-font-size-callout` (12px) / normal / 1.3333 line-height |\n",
2555
2631
  "docs": "Checkboxes allow users to make selections or choices. Checkboxes can be multi-select, and also act as controls.\n\nProvide visible label text in the default slot, or set `aria-label` when the checkbox has no adjacent label (for example, icon-only toolbars or table selection cells).",
2556
2632
  "docsTags": [
2633
+ {
2634
+ "name": "slot",
2635
+ "text": "- The checkbox label content."
2636
+ },
2557
2637
  {
2558
2638
  "name": "since",
2559
2639
  "text": "0.1.1"
@@ -2817,7 +2897,12 @@
2817
2897
  ],
2818
2898
  "listeners": [],
2819
2899
  "styles": [],
2820
- "slots": [],
2900
+ "slots": [
2901
+ {
2902
+ "name": "",
2903
+ "docs": "The checkbox label content."
2904
+ }
2905
+ ],
2821
2906
  "parts": [],
2822
2907
  "states": [],
2823
2908
  "dependents": [
@@ -3058,6 +3143,10 @@
3058
3143
  "readme": "# xpl-content-area\n\nThe `<xpl-content-area>` sets up the area for the main content of the page. This component renders a `<main>` element with an unnamed `<slot>`, and should not be combined with another `<main>` in the same page.\n\n### Design Tokens\n\nTokens used by this component from `@xplortech/apollo-foundation`.\n\n**Color**\n- `--xpl-background-surface-subtlest` — page surface behind the content (neutral-50 light / neutral-900 dark). Themes automatically — no `.dark` override. Matches the intended application-shell content surface.\n- `--xpl-text-default` — default text color (neutral-900 light / neutral-50 dark).\n",
3059
3144
  "docs": "The `<xpl-content-area>` sets up the area for the main content of the page. This component renders a `<main>` element with an unnamed `<slot>`, and should not be combined with another `<main>` in the same page.",
3060
3145
  "docsTags": [
3146
+ {
3147
+ "name": "slot",
3148
+ "text": "- The page content."
3149
+ },
3061
3150
  {
3062
3151
  "name": "since",
3063
3152
  "text": "0.1.2"
@@ -3112,7 +3201,12 @@
3112
3201
  "events": [],
3113
3202
  "listeners": [],
3114
3203
  "styles": [],
3115
- "slots": [],
3204
+ "slots": [
3205
+ {
3206
+ "name": "",
3207
+ "docs": "The page content."
3208
+ }
3209
+ ],
3116
3210
  "parts": [],
3117
3211
  "states": [],
3118
3212
  "dependents": [],
@@ -3126,6 +3220,10 @@
3126
3220
  "readme": "# xpl-dashboard\n\nThe XPL-Dashboard component is a code-only utility component for responsive layouts using the XPL-Data Card component. \n",
3127
3221
  "docs": "The XPL-Dashboard component is a code-only utility component for responsive layouts using the XPL-Data Card component.",
3128
3222
  "docsTags": [
3223
+ {
3224
+ "name": "slot",
3225
+ "text": "- The dashboard content."
3226
+ },
3129
3227
  {
3130
3228
  "name": "since",
3131
3229
  "text": "1.0.0"
@@ -3137,7 +3235,12 @@
3137
3235
  "events": [],
3138
3236
  "listeners": [],
3139
3237
  "styles": [],
3140
- "slots": [],
3238
+ "slots": [
3239
+ {
3240
+ "name": "",
3241
+ "docs": "The dashboard content."
3242
+ }
3243
+ ],
3141
3244
  "parts": [],
3142
3245
  "states": [],
3143
3246
  "dependents": [],
@@ -3148,7 +3251,7 @@
3148
3251
  "filePath": "src/components/components/xpl-data-card/xpl-data-card.tsx",
3149
3252
  "encapsulation": "none",
3150
3253
  "tag": "xpl-data-card",
3151
- "readme": "# xpl-data-card\n\nData Cards display snippets of information and can be linked to larger data sets. Data Cards give users \"at a glance\" information about their data.\n\nThe `icon` and `smallStatIcon` properties accept icon names as strings. See [xpl-icon available icons](../xpl-icon/ICONS.md) for the complete list.\n\n## Design Tokens\n\nThe card is styled entirely with Apollo Foundation purpose-specific tokens (dark mode is automatic — no `.dark` overrides).\n\n| Element / state | Property | Token |\n| --- | --- | --- |\n| Host surface | `background-color` | `--xpl-background-surface-default` |\n| Host / anchor border | `border-color` | `--xpl-border-default` |\n| Host, header, stats text | `color` | `--xpl-text-default` |\n| Header arrow (chevron) icon | `color` | `--xpl-icon-default` |\n| Detail text | `color` | `--xpl-text-subdued` |\n| `.positive` stat | `color` | `--xpl-text-positive` |\n| `.negative` stat | `color` | `--xpl-text-negative` |\n| `.neutral` stat | `color` | `--xpl-text-subdued` |\n| Primary leading icon | `color` | `--xpl-icon-brand` (orange) |\n| Secondary leading icon | `color` | `--xpl-background-action-primary-default` (purple) |\n| Anchor hover / active border | `border` | `--xpl-border-small` + `--xpl-background-action-primary-default` |\n| Anchor focus-visible border | `border` | `--xpl-border-small` + `--xpl-border-focus` (purple) |\n| Anchor focus-visible outline | `outline` | `--xpl-border-focus`, 2px width / 2px offset |\n| Anchor hover / focus-visible radius | `border-radius` | `--xpl-border-radius-default` |\n| Anchor hover shadow | `box-shadow` | `--xpl-elevation-2` (theme-aware) |\n| Header / stat spacing | `margin` | `--xpl-space-4`, `--xpl-space-6` |\n| Container-query stat | `font-size` | `--xpl-font-size-title-2` |\n\nThe resting anchor overlay keeps a `0.375rem` (6px) radius to match the card's `rounded-md`; Foundation has no 6px radius token (see `TECH_DEBT.md`).\n",
3254
+ "readme": "# xpl-data-card\n\nData Cards display snippets of information and can be linked to larger data sets. Data Cards give users \"at a glance\" information about their data.\n\nThe `icon` and `smallStatIcon` properties accept icon names as strings. See [xpl-icon available icons](../xpl-icon/ICONS.md) for the complete list.\n\n## Design Tokens\n\nThe card is styled entirely with Apollo Foundation purpose-specific tokens (dark mode is automatic — no `.dark` overrides).\n\n| Element / state | Property | Token |\n| --- | --- | --- |\n| Host surface | `background-color` | `--xpl-background-surface-default` |\n| Host / anchor border | `border-color` | `--xpl-border-default` |\n| Host, header, stats text | `color` | `--xpl-text-default` |\n| Header arrow (chevron) icon | `color` | `--xpl-icon-default` |\n| Detail text | `color` | `--xpl-text-subdued` |\n| `.positive` stat | `color` | `--xpl-text-positive` |\n| `.negative` stat | `color` | `--xpl-text-negative` |\n| `.neutral` stat | `color` | `--xpl-text-subdued` |\n| Primary leading icon | `color` | `--xpl-icon-brand` (orange) |\n| Secondary leading icon | `color` | `--xpl-background-action-primary-default` (purple) |\n| Anchor hover / active border | `border` | `--xpl-border-small` + `--xpl-background-action-primary-default` |\n| Anchor focus-visible border | `border` | `--xpl-border-small` + `--xpl-border-focus` (purple) |\n| Anchor focus-visible outline | `outline` | `--xpl-border-focus`, 2px width / 2px offset |\n| Anchor hover / focus-visible radius | `border-radius` | `--xpl-border-radius-default` |\n| Anchor hover shadow | `box-shadow` | `--xpl-elevation-2` (theme-aware) |\n| Header / stat spacing | `margin` | `--xpl-space-4`, `--xpl-space-6` |\n| Container-query stat | `font-size` | `--xpl-font-size-title-2` |\n| Container-query stat | `line-height` | `--xpl-line-height-title-2` |\n\nThe resting anchor overlay keeps a `0.375rem` (6px) radius to match the card's `rounded-md`; Foundation has no 6px radius token (see `TECH_DEBT.md`).\n",
3152
3255
  "docs": "Data Cards display snippets of information and can be linked to larger data sets. Data Cards give users \"at a glance\" information about their data.\n\nThe `icon` and `smallStatIcon` properties accept icon names as strings. See [xpl-icon available icons](../xpl-icon/ICONS.md) for the complete list.",
3153
3256
  "docsTags": [
3154
3257
  {
@@ -3533,6 +3636,10 @@
3533
3636
  "readme": "# xpl-divider\n\nAllows for easy separation and titling of sections within forms or other content.\n\n### Design Tokens\n\nTokens used by this component from `@xplortech/apollo-foundation`.\n\n**Color**\n- `--xpl-border-default` — divider line color\n- `--xpl-text-default` — label text color\n\n**Spacing**\n- `--xpl-space-{0,4,16}` — padding / margin\n\n**Typography**\n- `--xpl-font-size-body` — base label font size\n- Tier typography extends the shared type scale from `typography.css`: tier 3 → `.xpl-text-title-3`, tier 2 → `.xpl-text-title-4`, tier 1 → `.xpl-text-title-5`\n\n**Border**\n- `--xpl-border-small` — line width (1px)\n",
3534
3637
  "docs": "Allows for easy separation and titling of sections within forms or other content.",
3535
3638
  "docsTags": [
3639
+ {
3640
+ "name": "slot",
3641
+ "text": "- Optional text content rendered inside the divider (not displayed for tier 0)."
3642
+ },
3536
3643
  {
3537
3644
  "name": "since",
3538
3645
  "text": "0.3.0"
@@ -3568,7 +3675,12 @@
3568
3675
  "events": [],
3569
3676
  "listeners": [],
3570
3677
  "styles": [],
3571
- "slots": [],
3678
+ "slots": [
3679
+ {
3680
+ "name": "",
3681
+ "docs": "Optional text content rendered inside the divider (not displayed for tier 0)."
3682
+ }
3683
+ ],
3572
3684
  "parts": [],
3573
3685
  "states": [],
3574
3686
  "dependents": [
@@ -3592,6 +3704,14 @@
3592
3704
  "readme": "# xpl-dropdown\n\nA Dropdown is a menu with a list of actions a user can take. Use a dropdown when you want to present a list of actions in a limited space. Dropdowns are single-select ***actions***, can be presented in groups, and can be triggered by other elements such as a button, icon, text etc. \nA selection within a dropdown does not change the parent trigger.\n\n***If you want to present a list of **options** a user can select, or multi-select options, please use [XPL-Select](https://apollo.xplordocs.com/?path=/story/components-select--select).***\n\n### Design Tokens\n\nTokens used by this component from `@xplortech/apollo-foundation`. The panel surface and border are aligned with the migrated [xpl-popover](../xpl-popover).\n\n**Color**\n- `--xpl-background-surface-default` — panel background\n- `--xpl-text-default` — panel text color (inherited default), option label color, and heading color\n- `--xpl-icon-default` — leading option icon color (`--xpl-icon` via the `icon` prop)\n- `--xpl-border-default` — panel border and heading bottom border\n- `--xpl-text-subdued` — list-item subtitle color\n- `--xpl-background-action-tertiary-hover` — option `:focus` background\n- `--xpl-background-surface-subtlest` — option `:hover` background (neutral subtle surface)\n- `--xpl-background-surface-information` — `--selected` background (purple-50 light / neutral-700 dark)\n- `--xpl-background-action-primary-default` — `--selected` right accent border color\n\n**Border**\n- `--xpl-border-small` — panel and heading border width (1px)\n- `--xpl-border-large` — used via `calc(2 * …)` for the 4px `--selected` accent border (see `TECH_DEBT.md`)\n- `--xpl-border-radius-small` — panel corner radius (4px), matching the anchored `xpl-select` trigger\n\n**Typography**\n- `@extend .xpl-text-callout-bold` — heading: 12px / semibold weight / line-height 1.3333\n- `@extend .xpl-text-callout` — list-item subtitle: 12px / normal weight / line-height 1.3333\n\n**Note:** The multi-select checkmark is a data-URI SVG whose `fill` cannot be themed by a CSS variable, so a `.dark` override supplies the dark-mode glyph; and the disabled option keeps `opacity-60`. Both are tracked in `TECH_DEBT.md`.\n",
3593
3705
  "docs": "A Dropdown is a menu with a list of actions a user can take. Use a dropdown when you want to present a list of actions in a limited space. Dropdowns are single-select ***actions***, can be presented in groups, and can be triggered by other elements such as a button, icon, text etc. \nA selection within a dropdown does not change the parent trigger.\n\n***If you want to present a list of **options** a user can select, or multi-select options, please use [XPL-Select](https://apollo.xplordocs.com/?path=/story/components-select--select).***",
3594
3706
  "docsTags": [
3707
+ {
3708
+ "name": "slot",
3709
+ "text": "header - Optional header content rendered above the options list."
3710
+ },
3711
+ {
3712
+ "name": "slot",
3713
+ "text": "options - The `xpl-dropdown-option` / `xpl-dropdown-group` items. Used when the `options` prop is not set."
3714
+ },
3595
3715
  {
3596
3716
  "name": "since",
3597
3717
  "text": "0.4.0"
@@ -3951,8 +4071,9 @@
3951
4071
  "referenceLocation": "Event"
3952
4072
  },
3953
4073
  "Dropdown": {
3954
- "location": "global",
3955
- "id": "global::Dropdown"
4074
+ "location": "local",
4075
+ "path": "/home/runner/work/apollo/apollo/packages/apollo-core/src/components/components/xpl-dropdown/xpl-dropdown.tsx",
4076
+ "id": "src/components/components/xpl-dropdown/xpl-dropdown.tsx::Dropdown"
3956
4077
  }
3957
4078
  }
3958
4079
  },
@@ -4055,7 +4176,16 @@
4055
4176
  ],
4056
4177
  "listeners": [],
4057
4178
  "styles": [],
4058
- "slots": [],
4179
+ "slots": [
4180
+ {
4181
+ "name": "header",
4182
+ "docs": "Optional header content rendered above the options list."
4183
+ },
4184
+ {
4185
+ "name": "options",
4186
+ "docs": "The `xpl-dropdown-option` / `xpl-dropdown-group` items. Used when the `options` prop is not set."
4187
+ }
4188
+ ],
4059
4189
  "parts": [],
4060
4190
  "states": [],
4061
4191
  "dependents": [
@@ -4097,7 +4227,12 @@
4097
4227
  "tag": "xpl-dropdown-group",
4098
4228
  "readme": "# xpl-dropdown-group\n\n\n",
4099
4229
  "docs": "",
4100
- "docsTags": [],
4230
+ "docsTags": [
4231
+ {
4232
+ "name": "slot",
4233
+ "text": "options - The `xpl-dropdown-option` items in this group. Used when the `options` prop is not set."
4234
+ }
4235
+ ],
4101
4236
  "usage": {},
4102
4237
  "props": [
4103
4238
  {
@@ -4157,7 +4292,12 @@
4157
4292
  "events": [],
4158
4293
  "listeners": [],
4159
4294
  "styles": [],
4160
- "slots": [],
4295
+ "slots": [
4296
+ {
4297
+ "name": "options",
4298
+ "docs": "The `xpl-dropdown-option` items in this group. Used when the `options` prop is not set."
4299
+ }
4300
+ ],
4161
4301
  "parts": [],
4162
4302
  "states": [],
4163
4303
  "dependents": [
@@ -4187,7 +4327,12 @@
4187
4327
  "tag": "xpl-dropdown-heading",
4188
4328
  "readme": "# xpl-dropdown-heading\n\n\n",
4189
4329
  "docs": "",
4190
- "docsTags": [],
4330
+ "docsTags": [
4331
+ {
4332
+ "name": "slot",
4333
+ "text": "heading - Custom heading content. Used when the `label` prop is not set."
4334
+ }
4335
+ ],
4191
4336
  "usage": {},
4192
4337
  "props": [
4193
4338
  {
@@ -4218,7 +4363,12 @@
4218
4363
  "events": [],
4219
4364
  "listeners": [],
4220
4365
  "styles": [],
4221
- "slots": [],
4366
+ "slots": [
4367
+ {
4368
+ "name": "heading",
4369
+ "docs": "Custom heading content. Used when the `label` prop is not set."
4370
+ }
4371
+ ],
4222
4372
  "parts": [],
4223
4373
  "states": [],
4224
4374
  "dependents": [
@@ -4237,7 +4387,12 @@
4237
4387
  "tag": "xpl-dropdown-option",
4238
4388
  "readme": "# xpl-dropdown-option\n\n\n",
4239
4389
  "docs": "",
4240
- "docsTags": [],
4390
+ "docsTags": [
4391
+ {
4392
+ "name": "slot",
4393
+ "text": "option - Custom option content. Used when the `label` prop is not set."
4394
+ }
4395
+ ],
4241
4396
  "usage": {},
4242
4397
  "props": [
4243
4398
  {
@@ -4383,7 +4538,12 @@
4383
4538
  "events": [],
4384
4539
  "listeners": [],
4385
4540
  "styles": [],
4386
- "slots": [],
4541
+ "slots": [
4542
+ {
4543
+ "name": "option",
4544
+ "docs": "Custom option content. Used when the `label` prop is not set."
4545
+ }
4546
+ ],
4387
4547
  "parts": [],
4388
4548
  "states": [],
4389
4549
  "dependents": [
@@ -4412,6 +4572,14 @@
4412
4572
  "readme": "# xpl-dynamic-table\n\n> **Note:** This component is currently in beta. Use with caution as it may have breaking changes in future releases.\n\nThe Dynamic Table is a rich content table that can accommodate multiple types of data.\n\n> **[DEPRECATED]** Use the compositional `<xpl-table>` API instead.\n> This component will be removed in a future major release.\n\n## Design Tokens\n\nThe table, cells, and rows use Apollo Foundation purpose-specific tokens (light / dark resolve automatically — no `.dark` overrides):\n\n**Table** (`.xpl-dynamic-table`)\n\n| Element | Property | Token |\n| --- | --- | --- |\n| Grid columns | `grid-template-columns` | `--xpl-column-widths` (set at runtime from header cell widths) |\n| Row gap (with accordions) | `row-gap` (8px) | `--xpl-space-8` |\n\n**Cell** (`.xpl-dynamic-table-cell`)\n\n| Element | Property | Token |\n| --- | --- | --- |\n| Cell | Background | `--xpl-background-surface-default` |\n| Cell | Border | `--xpl-border-default` |\n| Cell | Text | `--xpl-text-default` |\n| Cell | Typography (title-5 / medium / 1.4286) | `@extend .xpl-text-title-5` |\n| Cell | Height (56px) | `--xpl-size-350` |\n| Cell | Min width (48px) | `--xpl-size-300` |\n| Cell rounded corners | Border radius (8px) | `--xpl-border-radius-default` |\n| Cell icons (chevron, kebab) | Color | `--xpl-icon-default` |\n| Dropdown button (`:focus-visible`) | Inset ring | `--xpl-border-focus` |\n\n**Row** (`.xpl-dynamic-table-row`)\n\n| State | Property | Token |\n| --- | --- | --- |\n| First cell | Left border | `--xpl-border-default` |\n| Selected | Cell background | `--xpl-background-surface-information` |\n| Disabled | Cell background | `--xpl-background-surface-subtlest` |\n| Disabled | Cell / badge text | `--xpl-text-disabled` |\n| Disabled | Cell icons | `--xpl-icon-disabled` |\n",
4413
4573
  "docs": "> **Note:** This component is currently in beta. Use with caution as it may have breaking changes in future releases.\n\nThe Dynamic Table is a rich content table that can accommodate multiple types of data.\n\n> **[DEPRECATED]** Use the compositional `<xpl-table>` API instead.\n> This component will be removed in a future major release.",
4414
4574
  "docsTags": [
4575
+ {
4576
+ "name": "slot",
4577
+ "text": "header - The header region of the table."
4578
+ },
4579
+ {
4580
+ "name": "slot",
4581
+ "text": "body - The body region of the table."
4582
+ },
4415
4583
  {
4416
4584
  "name": "since",
4417
4585
  "text": "1.4.0"
@@ -4451,7 +4619,16 @@
4451
4619
  }
4452
4620
  ],
4453
4621
  "styles": [],
4454
- "slots": [],
4622
+ "slots": [
4623
+ {
4624
+ "name": "body",
4625
+ "docs": "The body region of the table."
4626
+ },
4627
+ {
4628
+ "name": "header",
4629
+ "docs": "The header region of the table."
4630
+ }
4631
+ ],
4455
4632
  "parts": [],
4456
4633
  "states": [],
4457
4634
  "dependents": [],
@@ -4465,6 +4642,10 @@
4465
4642
  "readme": "# xpl-dynamic-table-cell\n\n> **Note:** This component is currently in beta. Use with caution as it may have breaking changes in future releases.\n\n> **[DEPRECATED]** Use `<xpl-table-cell>` instead.\n> This component will be removed in a future major release.\n",
4466
4643
  "docs": "> **Note:** This component is currently in beta. Use with caution as it may have breaking changes in future releases.\n\n> **[DEPRECATED]** Use `<xpl-table-cell>` instead.\n> This component will be removed in a future major release.",
4467
4644
  "docsTags": [
4645
+ {
4646
+ "name": "slot",
4647
+ "text": "- The cell content."
4648
+ },
4468
4649
  {
4469
4650
  "name": "since",
4470
4651
  "text": "1.3.0"
@@ -4506,7 +4687,12 @@
4506
4687
  "events": [],
4507
4688
  "listeners": [],
4508
4689
  "styles": [],
4509
- "slots": [],
4690
+ "slots": [
4691
+ {
4692
+ "name": "",
4693
+ "docs": "The cell content."
4694
+ }
4695
+ ],
4510
4696
  "parts": [],
4511
4697
  "states": [],
4512
4698
  "dependents": [],
@@ -4520,6 +4706,10 @@
4520
4706
  "readme": "# xpl-dynamic-table-row\n\n> **Note:** This component is currently in beta. Use with caution as it may have breaking changes in future releases.\n\n> **[DEPRECATED]** Use `<xpl-table-row>` instead.\n> This component will be removed in a future major release.\n",
4521
4707
  "docs": "> **Note:** This component is currently in beta. Use with caution as it may have breaking changes in future releases.\n\n> **[DEPRECATED]** Use `<xpl-table-row>` instead.\n> This component will be removed in a future major release.",
4522
4708
  "docsTags": [
4709
+ {
4710
+ "name": "slot",
4711
+ "text": "- The row cells (`xpl-dynamic-table-cell` elements)."
4712
+ },
4523
4713
  {
4524
4714
  "name": "since",
4525
4715
  "text": "1.3.0"
@@ -4590,7 +4780,12 @@
4590
4780
  "events": [],
4591
4781
  "listeners": [],
4592
4782
  "styles": [],
4593
- "slots": [],
4783
+ "slots": [
4784
+ {
4785
+ "name": "",
4786
+ "docs": "The row cells (`xpl-dynamic-table-cell` elements)."
4787
+ }
4788
+ ],
4594
4789
  "parts": [],
4595
4790
  "states": [],
4596
4791
  "dependents": [],
@@ -4629,6 +4824,10 @@
4629
4824
  "readme": "# xpl-grid-item\n\n\n",
4630
4825
  "docs": "",
4631
4826
  "docsTags": [
4827
+ {
4828
+ "name": "slot",
4829
+ "text": "- Content rendered inside the grid item."
4830
+ },
4632
4831
  {
4633
4832
  "name": "since",
4634
4833
  "text": "0.4.0"
@@ -4757,7 +4956,12 @@
4757
4956
  "events": [],
4758
4957
  "listeners": [],
4759
4958
  "styles": [],
4760
- "slots": [],
4959
+ "slots": [
4960
+ {
4961
+ "name": "",
4962
+ "docs": "Content rendered inside the grid item."
4963
+ }
4964
+ ],
4761
4965
  "parts": [],
4762
4966
  "states": [],
4763
4967
  "dependents": [],
@@ -4771,6 +4975,14 @@
4771
4975
  "readme": "# xpl-header-accordion\n\nThe toolbar component allows users to manipulate selections within the Dynamic Table. It is only visible when a row or cell is in the selected state and bulk actions can be taken on that row or cell. \n\n> **Note:** This component is currently in beta. Use with caution as it may have breaking changes in future releases.\n\n## Design Tokens\n\nThe header uses Apollo Foundation purpose-specific tokens (light / dark resolve automatically — no `.dark` overrides):\n\n| Element | Property | Token |\n| --- | --- | --- |\n| Header bar | Background | `--xpl-background-surface-subtlest` |\n| Header bar | Border | `--xpl-border-default` |\n| Header bar | Text | `--xpl-text-subdued` |\n| Header bar | Typography (title-5 / medium / 1.4286) | `@extend .xpl-text-title-5` |\n| Header bar | Min height (56px) | `--xpl-size-350` |\n| Header bar corners | Border radius (8px) | `--xpl-border-radius-default` |\n| Toggle area (`:focus-visible`) | Outline color | `--xpl-border-focus` |\n| Chevron icon | Color | `--xpl-icon-default` |\n| Content panel | Background | `--xpl-background-surface-default` |\n| Content panel | Border | `--xpl-border-default` |\n| Content panel | Text | `--xpl-text-link-default` |\n| Content grid | `grid-template-columns` | `--xpl-column-widths` (set at runtime by `xpl-dynamic-table`) |\n",
4772
4976
  "docs": "The toolbar component allows users to manipulate selections within the Dynamic Table. It is only visible when a row or cell is in the selected state and bulk actions can be taken on that row or cell. \n\n> **Note:** This component is currently in beta. Use with caution as it may have breaking changes in future releases.",
4773
4977
  "docsTags": [
4978
+ {
4979
+ "name": "slot",
4980
+ "text": "title - The header content of the accordion toggle."
4981
+ },
4982
+ {
4983
+ "name": "slot",
4984
+ "text": "content - Content rendered inside the collapsible body of the accordion."
4985
+ },
4774
4986
  {
4775
4987
  "name": "since",
4776
4988
  "text": "1.2.0"
@@ -4928,7 +5140,16 @@
4928
5140
  }
4929
5141
  ],
4930
5142
  "styles": [],
4931
- "slots": [],
5143
+ "slots": [
5144
+ {
5145
+ "name": "content",
5146
+ "docs": "Content rendered inside the collapsible body of the accordion."
5147
+ },
5148
+ {
5149
+ "name": "title",
5150
+ "docs": "The header content of the accordion toggle."
5151
+ }
5152
+ ],
4932
5153
  "parts": [],
4933
5154
  "states": [],
4934
5155
  "dependents": [],
@@ -4947,7 +5168,7 @@
4947
5168
  "filePath": "src/components/components/xpl-icon/xpl-icon.tsx",
4948
5169
  "encapsulation": "none",
4949
5170
  "tag": "xpl-icon",
4950
- "readme": "# xpl-icon\n\n## Available Icons\n\nFor a complete list of available icon names, see [ICONS.md](./ICONS.md) (164 icons).\n\nYou can also browse icons visually in [Storybook](?path=/story/components-icons--icons) with search functionality.\n\n## Usage\n\n```html\n<xpl-icon icon=\"rocket\" size=\"24\"></xpl-icon>\n```\n\nThe `icon` property accepts any icon name as a string. See the links above for the full list of available icons.\n\n### Design Tokens\n\nTokens used by this component from `@xplortech/apollo-foundation`.\n\n**Color — icon accent backgrounds (`&--with-background`)**\n\nSubtle per-hue accent tints use primitive color tokens (`-50` light / `-900` dark). Foundation has no purpose-specific token for these subtle tints (`--xpl-background-accent-*` are far bolder), so primitives are used and the `.dark` overrides are **deliberately retained** to swap the dark value. This is a documented exception to the \"no `.dark`\" migration DoD (tracked in `TECH_DEBT.md`).\n\n- `--xpl-icon-default` — fallback glyph color for accent-background icons (neutral-900 light / neutral-50 dark). Set explicitly so the glyph themes with the background instead of relying on inherited `currentColor` (which left a dark glyph on the dark `-900` tint in dark mode). Note: `xpl-data-card` also uses the accent-background prop and overrides the glyph color with a hue-matched value directly on `.xpl-icon`, so it is unaffected by this fallback.\n- `--xpl-color-neutral-50` / `--xpl-color-neutral-900` — `--gray` background\n- `--xpl-color-orange-50` / `--xpl-color-orange-900` — `--orange` background\n- `--xpl-color-purple-50` / `--xpl-color-purple-900` — `--purple` background\n- `--xpl-color-red-50` / `--xpl-color-red-900` — `--red` background\n- `--xpl-color-yellow-50` / `--xpl-color-yellow-900` — `--yellow` background\n\n---\n",
5171
+ "readme": "# xpl-icon\n\n## Available Icons\n\nFor a complete list of available icon names, see [ICONS.md](./ICONS.md) (554 icons).\n\nYou can also browse icons visually in [Storybook](?path=/story/components-icons--icons) with search functionality.\n\n## Usage\n\n```html\n<xpl-icon icon=\"rocket\" size=\"24\"></xpl-icon>\n```\n\nThe `icon` property accepts any icon name as a string. See the links above for the full list of available icons.\n\n### Design Tokens\n\nTokens used by this component from `@xplortech/apollo-foundation`.\n\n**Color — icon accent backgrounds (`&--with-background`)**\n\nSubtle per-hue accent tints use primitive color tokens (`-50` light / `-900` dark). Foundation has no purpose-specific token for these subtle tints (`--xpl-background-accent-*` are far bolder), so primitives are used and the `.dark` overrides are **deliberately retained** to swap the dark value. This is a documented exception to the \"no `.dark`\" migration DoD (tracked in `TECH_DEBT.md`).\n\n- `--xpl-icon-default` — fallback glyph color for accent-background icons (neutral-900 light / neutral-50 dark). Set explicitly so the glyph themes with the background instead of relying on inherited `currentColor` (which left a dark glyph on the dark `-900` tint in dark mode). Note: `xpl-data-card` also uses the accent-background prop and overrides the glyph color with a hue-matched value directly on `.xpl-icon`, so it is unaffected by this fallback.\n- `--xpl-color-neutral-50` / `--xpl-color-neutral-900` — `--gray` background\n- `--xpl-color-orange-50` / `--xpl-color-orange-900` — `--orange` background\n- `--xpl-color-purple-50` / `--xpl-color-purple-900` — `--purple` background\n- `--xpl-color-red-50` / `--xpl-color-red-900` — `--red` background\n- `--xpl-color-yellow-50` / `--xpl-color-yellow-900` — `--yellow` background\n\n---\n",
4951
5172
  "docs": "",
4952
5173
  "docsTags": [
4953
5174
  {
@@ -5188,6 +5409,14 @@
5188
5409
  "readme": "# xpl-input\n\nA flexible form input that supports multiple `type` values: `text`, `number`, `password`, `date`, `time`, `file`, `color`, `phone`, and `search`. Each type delegates to a dedicated sub-component (`xpl-input-date`, `xpl-input-time`, `xpl-input-phone`, `xpl-input-search`, etc.) while sharing a common label, description, and error-message wrapper.\n\nFor `type=\"phone\"`, the value is always emitted in **E.164** format (e.g. `+14155551234`). See [`xpl-input-phone`](xpl-input-phone) for full phone-input documentation.\n\n## Design Tokens\n\nThe `xpl-input` component uses Apollo Foundation tokens for consistent theming across light and dark modes:\n\n| Element | Property | Token |\n|---------|----------|-------|\n| Input wrapper border (default) | `border-color` | `var(--xpl-border-default)` |\n| Input wrapper border (focus) | `border-color` | `var(--xpl-border-focus)` |\n| Input wrapper border (error) | `border-color` | `var(--xpl-border-negative)` |\n| Input wrapper background (default) | `background-color` | `var(--xpl-background-surface-default)` |\n| Input/textarea background | `background-color` | `var(--xpl-background-surface-transparent-0)` |\n| Disabled/readonly wrapper background | `background-color` | `var(--xpl-background-action-disabled)` |\n| Post element background | `background-color` | `var(--xpl-background-surface-subtle)` |\n| Input text | `color` | `var(--xpl-text-default)` |\n| Disabled/readonly text | `color` | `var(--xpl-text-disabled)` |\n| Label text | `color` | `var(--xpl-text-strong)` |\n| Description text | `color` | `var(--xpl-text-subdued)` |\n| Error text | `color` | `var(--xpl-text-negative)` |\n| Character counter background (default) | `background-color` | `var(--xpl-background-surface-default)` |\n| Character counter background (disabled/readonly) | `background-color` | `var(--xpl-background-action-disabled)` |\n| Character counter text | `color` | `var(--xpl-text-subdued)` |\n| Calendar/password icon | `color` | `var(--xpl-icon-default)` |\n\n### Spacing Tokens\n\n| Element | Property | Token |\n|---------|----------|-------|\n| Input | `padding-right` | `var(--xpl-space-8)` |\n| Textarea | `padding-top` | `var(--xpl-space-12)` |\n| Textarea | `padding-bottom` | `var(--xpl-space-8)` |\n| Wrapper | `padding-left` | `var(--xpl-space-12)` |\n| Pre element (leading icon/text) | `padding-right` | `var(--xpl-space-8)` |\n| Post element (trailing unit / AM-PM) | `padding-left`, `padding-right` | `var(--xpl-space-12)` |\n| Label | `padding-bottom` | `var(--xpl-space-4)` |\n| Character counter | `padding` | `var(--xpl-space-8) var(--xpl-space-12)` |\n| Error message | `padding`, `gap` | `var(--xpl-space-4)` |\n| Calendar icon | `right` | `var(--xpl-space-12)` |\n| Password button | `padding-left`, `padding-right` | `var(--xpl-space-12)` |\n\n### Dimension Tokens\n\n| Element | Property | Token |\n|---------|----------|-------|\n| Input | `height` | `var(--xpl-size-250)` (40px; matches Figma `form/height`) |\n| Textarea | `min-height` | `var(--xpl-size-250)` (40px) |\n| Textarea | `height` | `var(--xpl-size-700)` (112px; rounded up from 98px) |\n| Wrapper | `border-width` | `var(--xpl-border-small)` |\n| Wrapper | `border-radius` | `var(--xpl-border-radius-small)` (4px; matches Figma `border/radius/small`) |\n| Post element | `height` | `var(--xpl-size-250)` (40px; vertically centered via flex) |\n| Post element | `border-left-width` | `var(--xpl-border-small)` |\n\n### Delegated types (`search`, `phone`, `date`, `time`, …)\n\n`valueChange`, `inputEvent`, `focusEvent`, and `blurEvent` are emitted by the inner component and **bubble** to `<xpl-input>`. For **`type=\"search\"`**, **`focusEvent`** and **`blurEvent`** are **re-emitted from `<xpl-input>`** (parent uses **`onFocusEvent`** / **`onBlurEvent`** on **`<xpl-input-search>`** with **`stopPropagation`**) so **`event.target`** matches **`type=\"text\"`**. For **`valueChange`** and **`inputEvent`** on search, events still **bubble** from **`xpl-input-search`** without parent re-emission; **`event.target`** may be the inner host—prefer **`event.detail`** (the string) or **`event.currentTarget`** when you attach the listener on **`<xpl-input>`**. Other delegated types follow the same **`event.detail`** / **`event.currentTarget`** guidance.\n\nFor **`type=\"search\"`** commit: **`<xpl-input-search>`** defines a Stencil **`@Event() search`** (trimmed query in **`detail`**). **`<xpl-input>`** listens with **`onSearch`** on the inner tag, **`stopPropagation`s**, then **`this.search.emit(detail)`** so the canonical **`search`** **`@Event`** stays on **`<xpl-input>`** and listeners are not doubled. **`setFocus()`** on **`<xpl-input>`** uses a **`ref`** to the inner component to delegate focus.\n",
5189
5410
  "docs": "A flexible form input that supports multiple `type` values: `text`, `number`, `password`, `date`, `time`, `file`, `color`, `phone`, and `search`. Each type delegates to a dedicated sub-component (`xpl-input-date`, `xpl-input-time`, `xpl-input-phone`, `xpl-input-search`, etc.) while sharing a common label, description, and error-message wrapper.\n\nFor `type=\"phone\"`, the value is always emitted in **E.164** format (e.g. `+14155551234`). See [`xpl-input-phone`](xpl-input-phone) for full phone-input documentation.",
5190
5411
  "docsTags": [
5412
+ {
5413
+ "name": "slot",
5414
+ "text": "trigger - Custom trigger element for the file picker. Only used with `type=\"file\"`."
5415
+ },
5416
+ {
5417
+ "name": "slot",
5418
+ "text": "pre - Leading add-on content rendered before the input. Falls back to the `pre` prop text."
5419
+ },
5191
5420
  {
5192
5421
  "name": "since",
5193
5422
  "text": "0.3.0"
@@ -6223,7 +6452,16 @@
6223
6452
  ],
6224
6453
  "listeners": [],
6225
6454
  "styles": [],
6226
- "slots": [],
6455
+ "slots": [
6456
+ {
6457
+ "name": "pre",
6458
+ "docs": "Leading add-on content rendered before the input. Falls back to the `pre` prop text."
6459
+ },
6460
+ {
6461
+ "name": "trigger",
6462
+ "docs": "Custom trigger element for the file picker. Only used with `type=\"file\"`."
6463
+ }
6464
+ ],
6227
6465
  "parts": [],
6228
6466
  "states": [],
6229
6467
  "dependents": [
@@ -8649,6 +8887,10 @@
8649
8887
  "readme": "# xpl-list\n\n## Description\n\n**`xpl-list`** is the list **container** (`role=\"list\"`). It coordinates **single selection** (shared radio group), **reordering** (drag-and-drop and keyboard), and assigns default **`item-id`** values when missing.\n\n**`xpl-list-item`** is one **row**. In **slot-based** mode, `role=\"listitem\"` is on the row surface inside the host (not on `<xpl-list-item>`), so list semantics work with the host’s `display: contents` layout. Legacy **`item`** rows keep `role=\"listitem\"` on the host. Place items **inside** **`xpl-list`** (default slot). Compose each row with **named slots** (recommended) or use the **legacy** `item` prop / list-level `items` array. Row-level props, events, and dependencies: [xpl-list-item readme](./xpl-list-item/readme.md).\n\nWhen the list is **`selectable`**, pressing **Escape** while focus is on the **selected** row clears the radio selection (same as **`clearSelection()`**). Clicking elsewhere does not clear selection. Row radios use the **title** slot for their accessible name when present; with no title slot, the label is **`Select <item-id>`** when **`item-id`** or **`id`** is set, otherwise a generic default.\n\n**Single-select vs multi-select:** **`selectable`** on **`xpl-list`** is for **exactly one chosen row** (implemented with **`xpl-radio`** and a shared group **`name`**). For **multi-select** or **independent** toggles per row, do **not** use this flag; compose **`xpl-checkbox`** (or your own controls) in row slots and manage state in the app.\n\n---\n\n## Slot-based vs legacy integration\n\n\n| | **Slot-based (recommended)** | **Legacy prop-based (deprecated)** |\n| ----------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |\n| **Where content lives** | Light DOM: named slots on each `<xpl-list-item>` | Data: `items` on `<xpl-list>` and/or `item` on `<xpl-list-item>` |\n| **Row markup** | You author `<xpl-list-item>` children with `slot=\"…\"` content | List renders rows from `items`, or item renders from `item` object |\n| **Flexibility** | Full UI composition (avatar, actions, errors, extras) | Fixed layout from `ListItem` fields |\n| **Identity** | Set `item-id` (or `id`) on each row | Legacy rows from `items` use `ListItem.id` when set, else `item-0`, `item-1`, … |\n| **Reordering** | `reorderable` on `xpl-list`: drag handle + keyboard on each slot row | **No reorder UI:** legacy `items` / `item` rows do not render a handle; use slots if you need reorder. |\n| **Notes** | Do not set `item` on rows in this mode | `items` logs a console warning when non-empty; mixing `items` **and** slotted children renders **both** (legacy rows first) |\n\n\n---\n\n## Slot-based integration\n\nPut one or more `<xpl-list-item>` elements **inside** `<xpl-list>`. Do **not** set the **`item`** prop on a row when using slots—if `item` is set, that row uses **legacy** rendering and ignores slot composition.\n\nOn **`xpl-list`**, set `selectable` and/or `reorderable` as needed; the list mirrors `radio-name`, `reorderable`, and `keyboard-active` onto **slot-based** direct child rows only (nested lists in slots are unchanged). Rows using the deprecated **`item`** prop or list-level **`items`** array are **legacy** markup: the list does **not** set `selectable`, `radio-name`, or `reorderable` on them, because that UI exists only in slot-based mode—migrate to slotted `<xpl-list-item>` children if you need selection or reorder.\n\n### Slots (on `xpl-list-item`)\n\nAll slots are **named**. Use `slot=\"<name>\"` on light-DOM children.\n\n\n| Slot | Role | HTML example |\n| ----------------- | --------------------------------------------------------- | ----------------------------------------------------------------- |\n| `title` | Primary title line | `<span slot=\"title\">Account name</span>` |\n| `metadata` | Secondary lines under the title | `<ul slot=\"metadata\"><li>Line one</li></ul>` |\n| `avatar` | Leading avatar or custom media | `<xpl-avatar slot=\"avatar\" name=\"JD\"></xpl-avatar>` |\n| `details` | Right column: supporting text | `<span slot=\"details\">Last updated</span>` |\n| `badges` | Right column: badges / tags | `<span slot=\"badges\"><xpl-badge>New</xpl-badge></span>` |\n| `actions` | Trailing actions; clicks do **not** follow `href` | `<span slot=\"actions\"><xpl-button type=\"button\" variant=\"tertiary\" size=\"sm\">Edit</xpl-button></span>` |\n| `error-text` | Message below the row; affects error styling when present | `<span slot=\"error-text\">This field is required.</span>` |\n| `stacked-extra-1` | Optional stacked block | `<div slot=\"stacked-extra-1\">Extra block 1</div>` |\n| `stacked-extra-2` | Second optional stacked block | `<div slot=\"stacked-extra-2\">Extra block 2</div>` |\n| `stacked-extra-3` | Third optional stacked block | `<div slot=\"stacked-extra-3\">Extra block 3</div>` |\n\n\n**Dot badge next to the title:** set **`title-badge-variant`** on **`xpl-list-item`** (same variants as `xpl-badge`), not inside the title slot.\n\n**Divider under the title row:** set **`show-divider`** on **`xpl-list-item`**.\n\n**Minimal example:**\n\n```html\n<xpl-list selectable reorderable>\n <xpl-list-item item-id=\"row-1\" href=\"/settings\">\n <xpl-avatar slot=\"avatar\" name=\"AC\"></xpl-avatar>\n <span slot=\"title\">Settings</span>\n <span slot=\"metadata\">Workspace</span>\n <span slot=\"details\">Updated today</span>\n <span slot=\"actions\"><xpl-button type=\"button\" variant=\"tertiary\" size=\"sm\">Open</xpl-button></span>\n </xpl-list-item>\n</xpl-list>\n```\n\n---\n\n### Attributes: where to set them\n\n#### `xpl-list` (container)\n\n\n| Attribute | Purpose |\n| -------------- | ------------------------------------------------------------------------------------------------------------------------------------ |\n| `label` | Accessible name: sets `aria-label` on the list host when there is no visible heading. **Not used** if `labelled-by` is also set. |\n| `labelled-by` | Space-separated element ids: sets `aria-labelledby` on the host. Takes precedence over `label` so both are never applied together. |\n| `selectable` | Enables radio selection; list assigns shared `radio-name`. **Escape** on the **selected** row clears selection (no extra attribute). |\n| `reorderable` | Enables drag handles and reorder behavior on **slot-based** rows only (see legacy section). |\n\nUse **`label`** *or* **`labelled-by`**, not both. If both attributes are present, only **`labelled-by`** affects the accessible name.\n\n\n#### `xpl-list-item` (row, slot mode — omit `item`)\n\n\n| Attribute | Purpose |\n| ----------------------------- | -------------------------------------------------------------------------------------------------------------------- |\n| `item-id` | Stable id for selection and reorder events (or use host `id`). If omitted, the list may assign `item-0`, `item-1`, … |\n| `href` | Optional: row acts as a link; `actions` / `avatar` clicks do not navigate. |\n| `disabled`, `error` | Row state: non-interactive row and/or error styling (border, `error-text` slot). |\n| `hidden` | Hides the row from layout and from visible-only list behavior; see **Hidden rows** below. |\n| `selected` | Controlled selected state when the list is `selectable`. |\n| `show-divider` | Divider under the title area. |\n| `title-badge-variant` | Dot badge beside the title. |\n\n\n#### Usually set by `xpl-list` on items (avoid overriding unless needed)\n\n\n| Attribute | Meaning |\n| --------------------------- | --------------------------------------------------- |\n| `selectable`, `reorderable` | Mirrored from the list (**slot-based** rows only; not legacy `item` / `items` rows). |\n| `radio-name` | Shared radio group when the list is `selectable` (slot-based rows only). |\n| `keyboard-active` | Set on the row in keyboard reorder mode (slot-based rows only). |\n\n\n### Hidden rows\n\nSet the **`hidden`** attribute (or **`hidden`** prop) on **`xpl-list-item`** when a row should not appear in the list or take up space, but you still want it in the DOM. Typical use cases:\n\n- **Filtering or search** — hide rows that do not match without tearing down markup or losing local state.\n- **Progressive disclosure or permissions** — keep a stable **`item-id`** and show the row only when a condition becomes true.\n- **Temporary removal** — same as filtering when the row may return without re-creating the element.\n\n**List behavior:** Hidden rows are omitted from **`orderChange`**’s **`orderedItemIds`** and from drag-and-drop / keyboard reorder among **visible** rows only (see tests and list implementation). They do not receive focus while hidden.\n\n**Accessibility:** Native **`hidden`** removes the row from the accessibility tree while it is hidden. If users need an equivalent announcement elsewhere (for example, “3 results hidden by filter”), provide that in your application UI.\n\n---\n\n### Events and programmatic API\n\n#### `xpl-list` — listen on the list host\n\n\n| Event | When | `detail` |\n| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |\n| `itemSelect` | Radio selection changes; when clearing via **`clearSelection()`** or **Escape** on the selected row, emitted **once per** previously selected row (typically one) | `{ itemId?: string }` |\n| `orderChange` | After a successful reorder (drag/drop or keyboard); DOM is already updated | `{ itemId?: string; direction: 'up' \\| 'down'; orderedItemIds: string[] }` |\n\n\n**`orderedItemIds`:** visible rows only (DOM order); hidden `xpl-list-item` rows are omitted.\n\n**TypeScript:** `orderChange` detail is typed as **`XplListOrderChangeDetail`** (exported from `@xplortech/apollo-core` alongside **`ListItem`**).\n\n**Legacy:** `xplListReorder` may also fire (bubbles, composed). Prefer **`orderChange`** for new code. Legacy `detail` includes `orderedItemIds` as well.\n\n\n| Method | Notes |\n| ------------------ | --------------------------------------------------------------------------------------------------- |\n| `clearSelection()` | Promise. Clears radios when `selectable`. Same effect as **Escape** with focus on the selected row. |\n\n\n```html\n<xpl-list id=\"my-list\" selectable reorderable>...</xpl-list>\n<script type=\"module\">\n const list = document.getElementById('my-list');\n list.addEventListener('itemSelect', (e) => {\n /* handle selection: */ void e.detail.itemId;\n });\n list.addEventListener('orderChange', (e) => {\n /* handle reorder: */ void e.detail.direction;\n void e.detail.orderedItemIds;\n });\n</script>\n```\n\n#### `xpl-list-item` — optional row-level listeners\n\n\n| Event | When | `detail` |\n| --------------- | ---------------------------------------------- | ---------------------------------------- |\n| `itemSelect` | Radio change on this row | `{ itemId?: string }` |\n| `itemDragStart` | Drag started from reorder handle | `{ itemId?: string; item: HTMLElement }` |\n| `itemDragEnd` | Drag ended on this row (after drop or cancel) | `{ itemId?: string; item: HTMLElement }` |\n| `itemDrop` | Drop target during reorder | `{ itemId?: string; item: HTMLElement }` |\n\n\n---\n\n## Legacy prop-based integration\n\nUse only for migration or data-driven rows. **Prefer slots for new work.**\n\n### At-a-glance migration\n\n| | **Before (deprecated)** | **After (recommended)** |\n| --- | --- | --- |\n| **Data** | `items` array on `xpl-list`, or `item` object on each row | Light DOM: `<xpl-list-item>` children with named slots |\n| **Identity** | Optional `ListItem.id`; else `item-0`, `item-1`, … | Explicit `item-id` (or host `id`) per row |\n\n```html\n<!-- Before: data-driven rows -->\n<xpl-list id=\"legacy-list\"></xpl-list>\n<script type=\"module\">\n document.getElementById('legacy-list').items = [{ title: 'Acme Co.', href: '/acme' }];\n</script>\n\n<!-- After: slot-based rows -->\n<xpl-list>\n <xpl-list-item item-id=\"acme\" href=\"/acme\">\n <span slot=\"title\">Acme Co.</span>\n </xpl-list-item>\n</xpl-list>\n```\n\n### `xpl-list` — `items` (deprecated)\n\n\n| Prop | Type | Description |\n| ------- | ------------ | ------------------------------------------------------------------------------------------------------- |\n| `items` | `ListItem[]` | **Deprecated.** Renders internal `<xpl-list-item>` rows with `item={...}` and ids `item-0`, `item-1`, … |\n\n\nA **console warning** runs when `items` is non-empty. Using **`items`** and slotted children renders **both**.\n\n### `xpl-list-item` — `item` (deprecated)\n\n\n| Prop | Type | Description |\n| ------ | ---------- | ---------------------------------------------------------------------------- |\n| `item` | `ListItem` | **Deprecated.** Prop-driven row; **no named slots**. Prefer slot-based rows. |\n\n\n### `ListItem` (`listitem.ts`, deprecated)\n\n\n| Field | Type | Notes |\n| ---------- | -------------------------------- | ---------------------- |\n| `id` | `string` | Optional stable row id (`item-id` when rendered from `items`) |\n| `title` | `string` | Required |\n| `href` | `string` | Optional |\n| `avatar` | `string` | Image URL or icon name |\n| `metadata` | `string[]` or `{ icon, text }[]` | |\n| `subtext` | `string` | |\n| `avatars` | `string[]` | |\n| `badges` | `string[]` or badge objects | |\n\n\n**Legacy layout:** Rows rendered from the deprecated `item` / inner `.xpl-list-item` flex markup use the **same layout at all viewport widths** (there is no dedicated narrow-viewport stacking breakpoint in `list-item.css`). If you need stacked mobile behavior for legacy lists, handle it in the app (e.g. layout wrapper or scoped CSS).\n\n**Legacy reordering:** The list does **not** set `reorderable` (or `selectable` / `radio-name`) on legacy **`items`** / **`item`** rows—there is **no** drag handle or keyboard reorder entry point in prop-driven mode. Only **slot-composed** rows participate in reorder and list-level selection UI; migrate from `items` / `item` to slotted `<xpl-list-item>` children if you need those features.\n\n---\n\n## Dependencies\n\n- **Rows:** `xpl-list-item` uses [xpl-avatar](../xpl-avatar), [xpl-icon](../xpl-icon), [xpl-badge](../xpl-badge), [xpl-divider](../xpl-divider).\n\n```mermaid\ngraph TD;\n xpl-list-item --> xpl-avatar\n xpl-list-item --> xpl-icon\n xpl-list-item --> xpl-badge\n xpl-list-item --> xpl-divider\n xpl-list --> xpl-list-item\n```\n\n\n\n## Design tokens\n\nList styling is split across [`list.css`](https://github.com/xplor/apollo/blob/main/packages/apollo-core/src/css/list.css) (the `xpl-list` host), [`list-item.css`](https://github.com/xplor/apollo/blob/main/packages/apollo-core/src/css/list-item.css) (legacy vs slot-based row layout, error/disabled/hidden, title/metadata columns), and [`list-action.css`](https://github.com/xplor/apollo/blob/main/packages/apollo-core/src/css/list-action.css) (reorder handle, selectable radio chrome, legacy action-button modifiers). All are imported from the package `style` entry. Visual values use **Apollo Foundation** CSS variables (`--xpl-*`); no raw hex colors in those stylesheets.\n\n\n| Category | Tokens (representative) |\n| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Layout / spacing** | `--xpl-space-4`, `--xpl-space-8`, `--xpl-space-12`, `--xpl-space-16`, `--xpl-space-40` |\n| **Sizing** | `--xpl-size-050`, `--xpl-size-075`, `--xpl-size-350` |\n| **Borders** | `--xpl-border-small`, `--xpl-border-large`, `--xpl-border-default`, `--xpl-border-hover`, `--xpl-border-focus`, `--xpl-border-negative`, `--xpl-border-subtle`, `--xpl-border-strong` |\n| **Radius** | `--xpl-border-radius-default`, `--xpl-border-radius-small`, `--xpl-border-radius-full` |\n| **Text** | `--xpl-text-default`, `--xpl-text-subdued`, `--xpl-text-disabled`, `--xpl-text-negative` |\n| **Icons / surfaces** | `--xpl-icon-default`, `--xpl-icon-subtle`, `--xpl-background-surface-default`, `--xpl-background-action-disabled`, `--xpl-background-surface-subtle` |\n| **Typography** | `--xpl-font-family-default`, `--xpl-font-size-body`, `--xpl-font-size-title-5`, `--xpl-font-size-callout`, `--xpl-font-size-caption`, `--xpl-font-weight-normal`, `--xpl-font-weight-medium` |\n\n\n## Accessibility\n\n- **`xpl-list`** sets `role=\"list\"` on the host. Use **`label`** / **`labelled-by`** when the list has no visible caption.\n- **`xpl-list-item`**: **`role=\"listitem\"`** is on the **inner row surface** in **slot-based** mode (see intro above); **legacy** `item` rows keep it on the **host**. When the list is **`selectable`**, the row renders **`xpl-radio`** with a shared group **`name`** and visually hidden label text (from the title slot when present, else a default).\n- With **`selectable`**, **Escape** while focus is on the **selected** row **clears selection** (same behavior as **`clearSelection()`**).\n- **Reorder**: the drag handle is a native **`<button>`** inside **`xpl-button`** (focusable, with **`aria-pressed`** in keyboard reorder mode and **`aria-label`** instructions for keyboard vs drag). Row links use `:focus-visible` for focus indication.\n- **`hidden`** on a row removes it from layout and visible reorder semantics; see **[Hidden rows](#hidden-rows)** for use cases and accessibility notes.\n\n---\n",
8650
8888
  "docs": "",
8651
8889
  "docsTags": [
8890
+ {
8891
+ "name": "slot",
8892
+ "text": "- The list items (`xpl-list-item` elements)."
8893
+ },
8652
8894
  {
8653
8895
  "name": "since",
8654
8896
  "text": "0.4.0"
@@ -8895,7 +9137,12 @@
8895
9137
  }
8896
9138
  ],
8897
9139
  "styles": [],
8898
- "slots": [],
9140
+ "slots": [
9141
+ {
9142
+ "name": "",
9143
+ "docs": "The list items (`xpl-list-item` elements)."
9144
+ }
9145
+ ],
8899
9146
  "parts": [],
8900
9147
  "states": [],
8901
9148
  "dependents": [],
@@ -8928,30 +9175,71 @@
8928
9175
  "tag": "xpl-list-item",
8929
9176
  "readme": "# xpl-list-item\n\nSingle row for [`xpl-list`](../xpl-list/readme.md). **Slot-based** rows expose `role=\"listitem\"` on the inner row surface (`.xpl-list-item__wrapper` or `.xpl-list-item__listitem-shell` when `href` is set); **legacy** `item` rows use `role=\"listitem\"` on the host. Hand-written integration notes for slots, attributes, and events live in the [list readme](../xpl-list/readme.md); the section below is Stencil-generated API for this component.\n\n**Reorder handle (slot mode):** When `reorderable` is enabled and the row uses **slot-based** content, the handle is an **`xpl-button`** wrapping a native **`<button>`**. Reorder logic is wired on the **`xpl-button` host** (`mousedown` / `keydown`); those events originate on the inner `<button>` and **bubble** to the host, which is how the list item receives them today. If `xpl-button` ever stopped bubbling for those events, reorder handlers would need to attach directly to the inner button (similar to `componentDidRender` syncing `aria-*` on that element).\n\n**Legacy `item` prop:** Prop-driven rows **do not** render the reorder handle, radios, or keyboard reorder affordances. Parent `xpl-list` **does not** set `selectable`, `radio-name`, or `reorderable` on those rows (those attributes are removed so they are not mistaken for supported behavior). Use slots if you need selection, drag, or keyboard reorder.\n\n### Accessibility\n\n- **`role=\"listitem\"`** is on the inner row surface (`.xpl-list-item__wrapper` for non-link rows, `.xpl-list-item__listitem-shell` for link rows), not on the `<xpl-list-item>` host. This avoids older browser issues with `display: contents` and ARIA.\n- **Hidden rows** render an empty host with the native `hidden` attribute only — no `role=\"listitem\"` — so hidden rows are removed from the accessibility tree entirely.\n- **Selectable rows** render `xpl-radio` with an accessible label derived from the **title** slot text, falling back to `Select <item-id>`, then `Select list item`.\n- **Reorder handle** (`xpl-button`) syncs `aria-label` and `aria-pressed` on the inner native `<button>` via `componentDidRender`. Labels change between drag and keyboard modes.\n- **Focus indicators** use `:focus-visible` with `outline-color: var(--xpl-border-focus)` and `outline-2 outline-offset-2`.\n- **Disabled rows** use `--xpl-background-action-disabled` and `--xpl-text-disabled`; link anchors get `pointer-events: none`.\n- **Keyboard reorder**: Enter/Space activates mode, ArrowUp/ArrowDown moves the row, Escape exits.\n\n### Design tokens\n\n| Role | Token |\n|------|-------|\n| Row background | `--xpl-background-surface-default` |\n| Row border | `--xpl-border-default` (default), `--xpl-border-hover` (hover), `--xpl-border-negative` (error), `--xpl-border-focus` (focus/drag-over) |\n| Row border-radius | `--xpl-border-radius-default` |\n| Text | `--xpl-text-default` (primary), `--xpl-text-subdued` (metadata/details), `--xpl-text-negative` (error), `--xpl-text-disabled` |\n| Icon | `--xpl-icon-subtle` (chevron) |\n| Spacing | `--xpl-space-4` through `--xpl-space-40` (padding, gap, margins) |\n| Sizing | `--xpl-size-050` (dot badge), `--xpl-size-350` (min-height) |\n| Typography | `--xpl-font-size-body`, `--xpl-font-size-title-5`, `--xpl-font-size-callout`, `--xpl-font-size-caption` |\n| Disabled background | `--xpl-background-action-disabled` |\n| Selected background | `--xpl-background-surface-subtle` |\n| Drag opacity | `--xpl-opacity-75` (custom token in `:root`) |\n",
8930
9177
  "docs": "Single row for [`xpl-list`](../xpl-list/readme.md). **Slot-based** rows expose `role=\"listitem\"` on the inner row surface (`.xpl-list-item__wrapper` or `.xpl-list-item__listitem-shell` when `href` is set); **legacy** `item` rows use `role=\"listitem\"` on the host. Hand-written integration notes for slots, attributes, and events live in the [list readme](../xpl-list/readme.md); the section below is Stencil-generated API for this component.\n\n**Reorder handle (slot mode):** When `reorderable` is enabled and the row uses **slot-based** content, the handle is an **`xpl-button`** wrapping a native **`<button>`**. Reorder logic is wired on the **`xpl-button` host** (`mousedown` / `keydown`); those events originate on the inner `<button>` and **bubble** to the host, which is how the list item receives them today. If `xpl-button` ever stopped bubbling for those events, reorder handlers would need to attach directly to the inner button (similar to `componentDidRender` syncing `aria-*` on that element).\n\n**Legacy `item` prop:** Prop-driven rows **do not** render the reorder handle, radios, or keyboard reorder affordances. Parent `xpl-list` **does not** set `selectable`, `radio-name`, or `reorderable` on those rows (those attributes are removed so they are not mistaken for supported behavior). Use slots if you need selection, drag, or keyboard reorder.",
8931
- "docsTags": [],
8932
- "usage": {},
8933
- "props": [
9178
+ "docsTags": [
8934
9179
  {
8935
- "name": "disabled",
8936
- "type": "boolean",
8937
- "complexType": {
8938
- "original": "boolean",
8939
- "resolved": "boolean",
8940
- "references": {}
8941
- },
8942
- "mutable": false,
8943
- "attr": "disabled",
8944
- "reflectToAttr": false,
8945
- "docs": "Whether the item is disabled.",
8946
- "docsTags": [
8947
- {
8948
- "name": "default",
8949
- "text": "false"
8950
- }
8951
- ],
8952
- "default": "false",
8953
- "values": [
8954
- {
9180
+ "name": "slot",
9181
+ "text": "avatar - Leading avatar/media cell."
9182
+ },
9183
+ {
9184
+ "name": "slot",
9185
+ "text": "title - The primary title text."
9186
+ },
9187
+ {
9188
+ "name": "slot",
9189
+ "text": "metadata - Secondary metadata row rendered below the title."
9190
+ },
9191
+ {
9192
+ "name": "slot",
9193
+ "text": "stacked-extra-1 - First additional stacked row, rendered below `metadata`."
9194
+ },
9195
+ {
9196
+ "name": "slot",
9197
+ "text": "stacked-extra-2 - Second additional stacked row, rendered below `stacked-extra-1`."
9198
+ },
9199
+ {
9200
+ "name": "slot",
9201
+ "text": "stacked-extra-3 - Third additional stacked row, rendered below `stacked-extra-2`."
9202
+ },
9203
+ {
9204
+ "name": "slot",
9205
+ "text": "details - Right-side details content."
9206
+ },
9207
+ {
9208
+ "name": "slot",
9209
+ "text": "badges - Right-side badges group."
9210
+ },
9211
+ {
9212
+ "name": "slot",
9213
+ "text": "actions - Right-side action controls."
9214
+ },
9215
+ {
9216
+ "name": "slot",
9217
+ "text": "error-text - Error message shown below the row; puts the item in the error state."
9218
+ }
9219
+ ],
9220
+ "usage": {},
9221
+ "props": [
9222
+ {
9223
+ "name": "disabled",
9224
+ "type": "boolean",
9225
+ "complexType": {
9226
+ "original": "boolean",
9227
+ "resolved": "boolean",
9228
+ "references": {}
9229
+ },
9230
+ "mutable": false,
9231
+ "attr": "disabled",
9232
+ "reflectToAttr": false,
9233
+ "docs": "Whether the item is disabled.",
9234
+ "docsTags": [
9235
+ {
9236
+ "name": "default",
9237
+ "text": "false"
9238
+ }
9239
+ ],
9240
+ "default": "false",
9241
+ "values": [
9242
+ {
8955
9243
  "type": "boolean"
8956
9244
  }
8957
9245
  ],
@@ -9415,7 +9703,48 @@
9415
9703
  }
9416
9704
  ],
9417
9705
  "styles": [],
9418
- "slots": [],
9706
+ "slots": [
9707
+ {
9708
+ "name": "actions",
9709
+ "docs": "Right-side action controls."
9710
+ },
9711
+ {
9712
+ "name": "avatar",
9713
+ "docs": "Leading avatar/media cell."
9714
+ },
9715
+ {
9716
+ "name": "badges",
9717
+ "docs": "Right-side badges group."
9718
+ },
9719
+ {
9720
+ "name": "details",
9721
+ "docs": "Right-side details content."
9722
+ },
9723
+ {
9724
+ "name": "error-text",
9725
+ "docs": "Error message shown below the row; puts the item in the error state."
9726
+ },
9727
+ {
9728
+ "name": "metadata",
9729
+ "docs": "Secondary metadata row rendered below the title."
9730
+ },
9731
+ {
9732
+ "name": "stacked-extra-1",
9733
+ "docs": "First additional stacked row, rendered below `metadata`."
9734
+ },
9735
+ {
9736
+ "name": "stacked-extra-2",
9737
+ "docs": "Second additional stacked row, rendered below `stacked-extra-1`."
9738
+ },
9739
+ {
9740
+ "name": "stacked-extra-3",
9741
+ "docs": "Third additional stacked row, rendered below `stacked-extra-2`."
9742
+ },
9743
+ {
9744
+ "name": "title",
9745
+ "docs": "The primary title text."
9746
+ }
9747
+ ],
9419
9748
  "parts": [],
9420
9749
  "states": [],
9421
9750
  "dependents": [
@@ -9453,9 +9782,25 @@
9453
9782
  "filePath": "src/components/components/xpl-main-nav/xpl-main-nav.tsx",
9454
9783
  "encapsulation": "none",
9455
9784
  "tag": "xpl-main-nav",
9456
- "readme": "# xpl-main-nav\n\nThe Main Nav component contains is split in 3 areas. The top area is where the branding goes. It's divided into 2 slots `logo` and `brand-name`.\nThe middle area is where the main navigation links go. It's a `<nav>` tag with a slot named `navigation`, where a `<ul slot=\"navigation\">` with `<xpl-nav-items>` should be rendered.\n\nThe bottom area is fixed to the bottom and should be used for actions. To render content within this area use the slot `footer` with a `<ul slot=\"footer\">` tag.\n\n### Design Tokens\n\n`xpl-main-nav` is the legacy always-dark side-nav rail; its surface never mode-switches, so colors resolve to the same dark-appropriate value in both page themes.\n\n**Color**\n- `--xpl-background-surface-inverse-on-light` — rail surface (neutral-1000, both modes; matches the application-shell nav host)\n- `--xpl-color-neutral-1100` — footer surface (primitive; no purpose token — see `TECH_DEBT.md`)\n- `--xpl-text-subdued` — rail on-surface base text and nav-item rest label (neutral-600 light / neutral-500 dark; Figma-confirmed; keeps text off the ambient page color on the dark rail)\n- `--xpl-text-inverse-on-dark` — brand name + footer primary text (white, both modes)\n- `--xpl-icon-subtle` — muted footer icon (neutral-500, both modes)\n\n**Typography**\n- `--xpl-font-size-body` — collapsed (`sm`/`md`) nav item label size\n\n**Layout**\n- Collapse-state widths (240 / 112 / 74px) and the 42px brand logo box are raw literals — no matching Foundation size token (see `TECH_DEBT.md`)\n",
9785
+ "readme": "# xpl-main-nav\n\nThe Main Nav component contains is split in 3 areas. The top area is where the branding goes. It's divided into 2 slots `logo` and `brand-name`.\nThe middle area is where the main navigation links go. It's a `<nav>` tag with a slot named `navigation`, where a `<ul slot=\"navigation\">` with `<xpl-nav-items>` should be rendered.\n\nThe bottom area is fixed to the bottom and should be used for actions. To render content within this area use the slot `footer` with a `<ul slot=\"footer\">` tag.\n\n### Design Tokens\n\n`xpl-main-nav` is the legacy always-dark side-nav rail; its surface never mode-switches, so colors resolve to the same dark-appropriate value in both page themes.\n\n**Color**\n- `--xpl-background-surface-inverse-on-light` — rail surface (neutral-1000, both modes; matches the application-shell nav host)\n- `--xpl-color-neutral-1100` — footer surface (primitive; no purpose token — see `TECH_DEBT.md`)\n- `--xpl-text-subdued` — rail on-surface base text and nav-item rest label (neutral-600 light / neutral-500 dark; Figma-confirmed; keeps text off the ambient page color on the dark rail)\n- `--xpl-text-inverse-on-dark` — brand name + footer primary text (white, both modes)\n- `--xpl-icon-subtle` — muted footer icon (neutral-500, both modes)\n\n**Typography**\n- `--xpl-font-size-body` — collapsed (`sm`/`md`) nav item label size\n- `--xpl-line-height-body` — collapsed (`sm`/`md`) nav item label line height\n- `--xpl-line-height-title-4` — brand lockup text line height (title-4)\n\n**Layout**\n- Collapse-state widths (240 / 112 / 74px) and the 42px brand logo box are raw literals — no matching Foundation size token (see `TECH_DEBT.md`)\n",
9457
9786
  "docs": "The Main Nav component contains is split in 3 areas. The top area is where the branding goes. It's divided into 2 slots `logo` and `brand-name`.\nThe middle area is where the main navigation links go. It's a `<nav>` tag with a slot named `navigation`, where a `<ul slot=\"navigation\">` with `<xpl-nav-items>` should be rendered.\n\nThe bottom area is fixed to the bottom and should be used for actions. To render content within this area use the slot `footer` with a `<ul slot=\"footer\">` tag.",
9458
9787
  "docsTags": [
9788
+ {
9789
+ "name": "slot",
9790
+ "text": "logo - Brand logo rendered in the sidebar header."
9791
+ },
9792
+ {
9793
+ "name": "slot",
9794
+ "text": "brand-name - Brand or product name rendered next to the logo in the sidebar header."
9795
+ },
9796
+ {
9797
+ "name": "slot",
9798
+ "text": "navigation - Primary navigation items."
9799
+ },
9800
+ {
9801
+ "name": "slot",
9802
+ "text": "footer - Footer content pinned to the bottom of the sidebar."
9803
+ },
9459
9804
  {
9460
9805
  "name": "since",
9461
9806
  "text": "0.1.2"
@@ -9530,7 +9875,24 @@
9530
9875
  ],
9531
9876
  "listeners": [],
9532
9877
  "styles": [],
9533
- "slots": [],
9878
+ "slots": [
9879
+ {
9880
+ "name": "brand-name",
9881
+ "docs": "Brand or product name rendered next to the logo in the sidebar header."
9882
+ },
9883
+ {
9884
+ "name": "footer",
9885
+ "docs": "Footer content pinned to the bottom of the sidebar."
9886
+ },
9887
+ {
9888
+ "name": "logo",
9889
+ "docs": "Brand logo rendered in the sidebar header."
9890
+ },
9891
+ {
9892
+ "name": "navigation",
9893
+ "docs": "Primary navigation items."
9894
+ }
9895
+ ],
9534
9896
  "parts": [],
9535
9897
  "states": [],
9536
9898
  "dependents": [],
@@ -9748,6 +10110,10 @@
9748
10110
  "readme": "# xpl-nav-header-menu\n\nProfile block (username, email) and a default slot for menu rows. Typically inside **`xpl-popover`** or **`xpl-dropdown`** under a user trigger.\n\n**Styles** for this component live in **`packages/apollo-core/src/css/top-nav.css`** (with **`xpl-top-nav-item`** rules in the same file).\n\n## Usage\n\n```html\n<xpl-nav-header-menu username=\"Katie Wilson\" email=\"katie@example.com\">\n <xpl-top-nav-item label=\"View account\" icon=\"house-4\" href=\"/account\"></xpl-top-nav-item>\n <xpl-top-nav-item label=\"Log out\" icon=\"open-rect-arrow-out\" href=\"/logout\"></xpl-top-nav-item>\n</xpl-nav-header-menu>\n```\n\nOptional narrow-only duplicate of bar actions (e.g. Inbox) between the profile block and the list — use **`slot=\"right-aside-mobile\"`** with **`xpl-top-nav-item`** or **`xpl-button`**. That strip is visible by default only on **narrow viewports** (one pixel below Tailwind **`md`** — see **`top-nav.css`**).\n\n```html\n<xpl-nav-header-menu username=\"Katie Wilson\" email=\"katie@example.com\">\n <xpl-top-nav-item slot=\"right-aside-mobile\" label=\"Inbox\" icon=\"mail-1\" href=\"/inbox\"></xpl-top-nav-item>\n <xpl-top-nav-item label=\"Log out\" icon=\"open-rect-arrow-out\" href=\"/logout\"></xpl-top-nav-item>\n</xpl-nav-header-menu>\n```\n\n## Token Use\n\nSpacing, typography, and colors use Apollo Foundation **`var(--xpl-*)`** tokens (e.g. **`--xpl-text-strong`**, **`--xpl-space-*`**, **`--xpl-font-size-*`**). Panel width uses the component-scoped custom property **`--xpl-top-nav-panel-width`** on **`xpl-top-nav`** / **`xpl-nav-header-menu`** hosts (composed from **`--xpl-size-*`** in **`top-nav.css`**).\n\n## Accessibility\n\n- The default slot is wrapped in a container with **`role=\"list\"`**. Use **`xpl-top-nav-item`** for rows so each item exposes **`role=\"listitem\"`** and keyboard/focus behavior from **`xpl-top-nav-item`** (including **`Enter`** / **`Space`** on non-link controls and **`:focus-visible`** outline on the inner control).\n- The host may include Foundation’s **`dark`** class so purpose tokens match account menu contrast when the page root is light.\n- **`slot=\"right-aside-mobile\"`** is not a list; do not rely on **`role=\"list\"`** for that block.\n\n## Events\n\nThis component does not emit custom events. Use **`xpl-top-nav-item`** **`navItemClick`** (or native link navigation) for row actions.\n",
9749
10111
  "docs": "Profile / account block (username, email, default slot for **`xpl-top-nav-item`** / **`xpl-divider`**).\nTag **`xpl-nav-header-menu`**; implementation folder **`xpl-nav-header-menu`**. **`.xpl-nav-header-menu__slot`** is\n**`role=\"list\"`** for slotted **`xpl-top-nav-item`** (**`role=\"listitem\"`**).\n\n**Slotted content:** do not add **`xpl-nav-header-menu__*`** BEM classes on nodes you pass into the default slot —\nprofile, list shell, and **`xpl-top-nav-item`** **`role=\"listitem\"`** are handled by the component. Use\n**`xpl-top-nav-item`** (or plain elements that are not Apollo **`xpl-*`** BEM hooks) in the slot only.\n\n**`slot=\"right-aside-mobile\"`** — optional block **between** the profile header and the default-slot list (e.g. narrow-only\nduplicate of **every** **`slot=\"right-aside\"`** control per Figma when **`xpl-top-nav`** hides **`right-aside`** on narrow viewports (always)).\nNot **`role=\"list\"`**; use **`xpl-button`** / **`xpl-top-nav-item`** as needed. Shown only on **narrow viewports** (one pixel below Tailwind **`md`** — **`top-nav.css`**) so bar actions stay in **`slot=\"right-aside\"`** on wider viewports.\nLight-DOM **`slotchange`** is unreliable; a host **`MutationObserver`** (child / subtree / **`slot`** attribute) keeps\n**`xpl-nav-header-menu--has-right-aside-mobile`** in sync when nodes are added or retargeted after mount (same pattern as **`xpl-top-nav`**).\nStencil **`newSpecPage`** / mock-doc does not define **`MutationObserver`**; slot presence still updates on the next\ndeferred flush (**`setTimeout(0)`** coalesced with **`componentDidRender`**). In real browsers the observer handles child /\n**`slot`** mutations without stacking uncancellable microtasks.\n\n**Theming:** The host always includes Foundation’s **`dark`** class so **`--xpl-*`** purpose tokens resolve to dark-mode\nvalues even when the page root is light (same contract as **`xpl-top-nav`** / **`xpl-top-nav-item`**).",
9750
10112
  "docsTags": [
10113
+ {
10114
+ "name": "slot",
10115
+ "text": "- Menu items (**`xpl-top-nav-item`** rows and optional **`xpl-divider`** separators) rendered below the profile block."
10116
+ },
9751
10117
  {
9752
10118
  "name": "slot",
9753
10119
  "text": "right-aside-mobile - Optional actions **below** **`username`** / **`email`** and **above** the default-slot list (e.g. mobile Inbox). Hidden on wide viewports by default (see **`top-nav.css`**)."
@@ -9807,6 +10173,10 @@
9807
10173
  "listeners": [],
9808
10174
  "styles": [],
9809
10175
  "slots": [
10176
+ {
10177
+ "name": "",
10178
+ "docs": "Menu items (**`xpl-top-nav-item`** rows and optional **`xpl-divider`** separators) rendered below the profile block."
10179
+ },
9810
10180
  {
9811
10181
  "name": "right-aside-mobile",
9812
10182
  "docs": "Optional actions **below** **`username`** / **`email`** and **above** the default-slot list (e.g. mobile Inbox). Hidden on wide viewports by default (see **`top-nav.css`**)."
@@ -9825,6 +10195,18 @@
9825
10195
  "readme": "# xpl-nav-item\n\nThis component should be used within sections of the `<xpl-main-nav>`. This component yields a slot named `link`, where a `<a>` tag should be used. Within the `<a>` tag, there are two slots, one named `icon`, where any svg can be placed. An an unnamed slot, where the link text should be placed.\n\n### Design Tokens\n\n`xpl-nav-item` renders on the legacy always-dark side-nav rail, so its colors resolve to the same dark-appropriate value in both light and dark page themes.\n\n**Color**\n- `--xpl-icon-subtle` — rest icon (neutral-500, both modes)\n- `--xpl-text-subdued` — rest label text (neutral-600 light / neutral-500 dark; Figma-confirmed)\n- `--xpl-text-inverse-on-dark` / `--xpl-icon-inverse-on-dark` — hover / focus / active / selected label and icon (white, both modes)\n- `--xpl-color-neutral-1200` — hover / focus / active background (primitive; no purpose token — see `TECH_DEBT.md`)\n- `--xpl-color-orange-400` — active brand accent border + icon (primitive; consult-Figma — see `TECH_DEBT.md`)\n\n**Border**\n- `calc(2 * var(--xpl-border-large))` — 4px active accent border width (no 4px Foundation token)\n",
9826
10196
  "docs": "This component should be used within sections of the `<xpl-main-nav>`. This component yields a slot named `link`, where a `<a>` tag should be used. Within the `<a>` tag, there are two slots, one named `icon`, where any svg can be placed. An an unnamed slot, where the link text should be placed.",
9827
10197
  "docsTags": [
10198
+ {
10199
+ "name": "slot",
10200
+ "text": "link - Optional wrapper around the item content (e.g. an anchor element)."
10201
+ },
10202
+ {
10203
+ "name": "slot",
10204
+ "text": "icon - Leading icon content rendered before the label."
10205
+ },
10206
+ {
10207
+ "name": "slot",
10208
+ "text": "- The item label content."
10209
+ },
9828
10210
  {
9829
10211
  "name": "since",
9830
10212
  "text": "0.1.2"
@@ -9890,7 +10272,20 @@
9890
10272
  ],
9891
10273
  "listeners": [],
9892
10274
  "styles": [],
9893
- "slots": [],
10275
+ "slots": [
10276
+ {
10277
+ "name": "",
10278
+ "docs": "The item label content."
10279
+ },
10280
+ {
10281
+ "name": "icon",
10282
+ "docs": "Leading icon content rendered before the label."
10283
+ },
10284
+ {
10285
+ "name": "link",
10286
+ "docs": "Optional wrapper around the item content (e.g. an anchor element)."
10287
+ }
10288
+ ],
9894
10289
  "parts": [],
9895
10290
  "states": [],
9896
10291
  "dependents": [],
@@ -10463,6 +10858,10 @@
10463
10858
  "listeners": [],
10464
10859
  "styles": [],
10465
10860
  "slots": [
10861
+ {
10862
+ "name": "",
10863
+ "docs": "The default slot"
10864
+ },
10466
10865
  {
10467
10866
  "name": "Inner Content",
10468
10867
  "docs": "Content inside the popover"
@@ -10882,6 +11281,10 @@
10882
11281
  "readme": "# xpl-radio\n\nRadio buttons allow users to make selections or choices. Radios are always single select, and can also act as controls.\n\n## Design Tokens\n\nThe radio component uses the following Apollo Foundation tokens:\n\n| Token | Usage |\n|-------|-------|\n| `--xpl-background-surface-default` | Disabled checked state background; styled label background (resting/hover/focused) |\n| `--xpl-background-action-disabled` | Disabled state background; disabled label background (styled variant) |\n| `--xpl-background-surface-information` | Checked label background (styled variant) |\n| `--xpl-border-strong` | Resting (unchecked) border color |\n| `--xpl-border-hover` | Checked/hover/active border color; styled container hover/focus border |\n| `--xpl-border-default` | Disabled state border color |\n| `--xpl-border-focus` | Focus visible outline |\n| `--xpl-border-radius-small` | Styled container corner radius (4px) |\n| `--xpl-border-small` | Default border width (1px) |\n| `--xpl-size-100` | Radio dimensions (16px) |\n\n### Label tokens (shared `label.css`)\n\nThe radio label and description are styled by the shared `.xpl-label` / `.xpl-description` classes.\n\n| Token | Usage |\n|-------|-------|\n| `--xpl-text-strong` | Label text color (matches `.xpl-input-label`; resolves to white in dark mode) |\n| `--xpl-text-disabled` | Disabled label text color |\n| `--xpl-text-subdued` | Description text color (enabled and disabled) |\n| `.xpl-text-body` | Label typography: `--xpl-font-size-body` (14px) / normal / 1.4286 line-height |\n| `.xpl-text-callout` | Description typography: `--xpl-font-size-callout` (12px) / normal / 1.3333 line-height |\n",
10883
11282
  "docs": "Radio buttons allow users to make selections or choices. Radios are always single select, and can also act as controls.",
10884
11283
  "docsTags": [
11284
+ {
11285
+ "name": "slot",
11286
+ "text": "- The radio label content."
11287
+ },
10885
11288
  {
10886
11289
  "name": "since",
10887
11290
  "text": "0.1.1"
@@ -11093,7 +11496,12 @@
11093
11496
  ],
11094
11497
  "listeners": [],
11095
11498
  "styles": [],
11096
- "slots": [],
11499
+ "slots": [
11500
+ {
11501
+ "name": "",
11502
+ "docs": "The radio label content."
11503
+ }
11504
+ ],
11097
11505
  "parts": [],
11098
11506
  "states": [],
11099
11507
  "dependents": [
@@ -11117,6 +11525,10 @@
11117
11525
  "readme": "# secondary-nav\n\nThe Secondary Nav component is a component that allows navigation within the content area. It yields one slot where a <ul> element with <xpl-nav-item> elements should be rendered.\n\n## Design Tokens\n\nThe secondary nav uses Apollo Foundation purpose-specific tokens, which theme automatically in light and dark mode (no `.dark` overrides).\n\n| Element / state | Property | Token |\n| --- | --- | --- |\n| `nav`, `ul`, host, item | `background` | `--xpl-background-surface-default` |\n| item (default) | `color` | `--xpl-text-subdued` |\n| item | `font-size` | `--xpl-font-size-body` (0.875rem / 14px) |\n| item | `font-weight` | `--xpl-font-weight-normal` (400) |\n| item hover | `color` | `--xpl-text-strong` |\n| item hover | `background` | `--xpl-background-surface-transparent-0` |\n| item active | `color` | `--xpl-text-strong` |\n| item active | `background` | `--xpl-background-brand-primary-surface` |\n| item active | accent `border-color` | `--xpl-border-accent-brand` |\n| item `:focus-visible` | `outline-color` | `--xpl-border-focus` |\n| item `:focus-visible` | `z-index` (above scroll fade) | `--xpl-z-index-200` |\n| item accent border | `border-width` (4px) | `calc(2 * var(--xpl-border-large))` |\n| item (desktop) | `height` (48px) | `--xpl-size-300` |\n| scroll fade `nav::after` | `z-index` (below focused item) | `--xpl-z-index-100` |\n\nThe horizontal-scroll fade (`nav::after`, mobile only) fills a strip with `--xpl-background-surface-default` and fades it out with an alpha-only `mask-image`, so it blends into the themed nav background in both modes. It is `pointer-events: none` and sits at `--xpl-z-index-100`, below a keyboard-focused item (`--xpl-z-index-200`), so it never intercepts taps or hides an item's `:focus-visible` ring.\n\n**Known token gaps** (see [`TECH_DEBT.md`](../../../../../../TECH_DEBT.md)): the item `height` (52px) and desktop `width` (200px) have no matching Foundation size token; the 4px accent border and the scroll-fade gradient stops have no dedicated Foundation tokens.\n",
11118
11526
  "docs": "The Secondary Nav component is a component that allows navigation within the content area. It yields one slot where a <ul> element with <xpl-nav-item> elements should be rendered.",
11119
11527
  "docsTags": [
11528
+ {
11529
+ "name": "slot",
11530
+ "text": "- The navigation items."
11531
+ },
11120
11532
  {
11121
11533
  "name": "since",
11122
11534
  "text": "0.3.0"
@@ -11128,7 +11540,12 @@
11128
11540
  "events": [],
11129
11541
  "listeners": [],
11130
11542
  "styles": [],
11131
- "slots": [],
11543
+ "slots": [
11544
+ {
11545
+ "name": "",
11546
+ "docs": "The navigation items."
11547
+ }
11548
+ ],
11132
11549
  "parts": [],
11133
11550
  "states": [],
11134
11551
  "dependents": [],
@@ -11142,6 +11559,10 @@
11142
11559
  "readme": "# xpl-select\n\nSelect allows users to make a single selection or multiple selections from a list of options. Items can be a simple list, grouped, simple multi-select. User selections typically appear within the Select Box once created unless multiple selections are made, where truncation or another method of showing these selections is required.\n\n***When the list of options contains “Actions”, use a ***[Dropdown component](https://apollo.xplordocs.com/?path=/story/components-dropdown--simple).\n\nThe `selectIcon` property accepts icon names as strings. See [xpl-icon available icons](../xpl-icon/ICONS.md) for the complete list.\n\n---\n\n### Design Tokens\n\nTokens used by this component from `@xplortech/apollo-foundation`.\n\n**Color**\n- `--xpl-icon-default` — chevron icon color\n- `--xpl-text-subdued` — trigger and placeholder value color (empty state)\n- `--xpl-text-default` — selected value color (`.has-value`)\n\nThe options panel surface, borders, and option states are styled by [xpl-dropdown](../xpl-dropdown) — see its Design Tokens section.\n\n**Note:** the trigger `height` (44px) and `.xpl-input-wrapper` `min-height` (46px) are kept as literals — no `--xpl-size` token matches (250 = 40px, 300 = 48px). See `TECH_DEBT.md`.\n",
11143
11560
  "docs": "Select allows users to make a single selection or multiple selections from a list of options. Items can be a simple list, grouped, simple multi-select. User selections typically appear within the Select Box once created unless multiple selections are made, where truncation or another method of showing these selections is required.\n\n***When the list of options contains “Actions”, use a ***[Dropdown component](https://apollo.xplordocs.com/?path=/story/components-dropdown--simple).\n\nThe `selectIcon` property accepts icon names as strings. See [xpl-icon available icons](../xpl-icon/ICONS.md) for the complete list.\n\n---",
11144
11561
  "docsTags": [
11562
+ {
11563
+ "name": "slot",
11564
+ "text": "custom-display-value - Custom content rendered as the selected-value display. Only rendered when the `customDisplayValue` prop is set."
11565
+ },
11145
11566
  {
11146
11567
  "name": "since",
11147
11568
  "text": "0.4.0"
@@ -11700,7 +12121,12 @@
11700
12121
  }
11701
12122
  ],
11702
12123
  "styles": [],
11703
- "slots": [],
12124
+ "slots": [
12125
+ {
12126
+ "name": "custom-display-value",
12127
+ "docs": "Custom content rendered as the selected-value display. Only rendered when the `customDisplayValue` prop is set."
12128
+ }
12129
+ ],
11704
12130
  "parts": [],
11705
12131
  "states": [],
11706
12132
  "dependents": [
@@ -12102,7 +12528,20 @@
12102
12528
  "tag": "xpl-side-nav-item",
12103
12529
  "readme": "# xpl-side-nav-item\n\nRow for [`xpl-side-nav`](../xpl-side-nav): place items in `slot=\"section\"` or `slot=\"nav-footer\"`. Supports **leaf** (button), **expandable** (`has-children`), and **link** (slot-only) modes, plus `selected`, `disabled`, and `hidden`.\n\n## Changes in v2 (breaking)\n\n`xpl-side-nav` is in **beta**. v2 makes the following breaking changes — no compat shims are provided.\n\n### Migration table\n\n| API | v1 (before) | v2 (after) | Action required |\n|---|---|---|---|\n| Leading icon slot | `slot=\"icon\"` | `slot=\"leading\"` | Rename the `slot` attribute on slotted icon elements |\n| Parent leading icon | Not rendered (chevron only) | Rendered alongside the trailing chevron | Pass `icon` prop or `slot=\"leading\"` content to parent rows |\n| Trailing slot | Not available | `slot=\"trailing\"` | Add to leaf items for custom trailing content |\n| Chevron position | Leading (left side) | Trailing (right side), auto-rendered for parents | No action needed for auto chevron; remove manual chevron markup if any |\n| Chevron override | Not possible | `slot=\"trailing\"` content replaces auto chevron on parents | Pass `slot=\"trailing\"` to customize the trailing column on a parent |\n| Leaf click event | `navClick` event | `navigate` event | Rename listeners: `navClick` → `navigate` (React `onNavClick` → `onNavigate`) |\n\n### Before / after examples\n\n**Leading icon slot rename:**\n```html\n<!-- v1 -->\n<xpl-side-nav-item label=\"Dashboard\">\n <xpl-icon slot=\"icon\" icon=\"house-4\" size=\"16\"></xpl-icon>\n</xpl-side-nav-item>\n\n<!-- v2 -->\n<xpl-side-nav-item label=\"Dashboard\">\n <xpl-icon slot=\"leading\" icon=\"house-4\" size=\"16\"></xpl-icon>\n</xpl-side-nav-item>\n<!-- or use the icon prop shorthand -->\n<xpl-side-nav-item label=\"Dashboard\" icon=\"house-4\"></xpl-side-nav-item>\n```\n\n**Parent rows now show leading icon + chevron:**\n```html\n<!-- v1: icon prop was ignored on parents -->\n<xpl-side-nav-item has-children label=\"Settings\" icon=\"gear\">...</xpl-side-nav-item>\n\n<!-- v2: icon prop renders in the leading slot; chevron auto-renders in trailing -->\n<xpl-side-nav-item has-children label=\"Settings\" icon=\"gear\">...</xpl-side-nav-item>\n```\n\n**Adding a trailing icon to a leaf item:**\n```html\n<!-- v2: slot=\"trailing\" for custom trailing content -->\n<xpl-side-nav-item label=\"Xplor Growth\" icon=\"chart-line-up\">\n <xpl-icon slot=\"trailing\" icon=\"external-link\" size=\"16\" aria-hidden=\"true\"></xpl-icon>\n</xpl-side-nav-item>\n```\n\n**Leaf click event rename:**\n```js\n// v1\nitem.addEventListener('navClick', handler);\n// v2\nitem.addEventListener('navigate', handler);\n```\nIn React the prop changes from `onNavClick` to `onNavigate`.\n\n---\n\n## Modes\n\n- **Leaf (default)** — Button with optional `label`, `icon` prop / `slot=\"leading\"`, and `slot=\"trailing\"`. Activation (click or keyboard) emits **`navigate`**.\n- **Expandable (`has-children`)** — Put nested `xpl-side-nav-item` elements in the **default slot**. Row renders **leading icon** (from `icon` prop or `slot=\"leading\"`) **+ label + auto chevron** in the trailing position (**`chevron-down`** when collapsed, **`chevron-up`** when expanded). The chevron is replaced if `slot=\"trailing\"` content is provided. Click toggles **`expanded`** and emits **`expandedChange`**.\n- **Link (`link`)** — **Default slot only**: renders a bare `<slot>` with no inner button. Use when the row must be a real `<a>` element — for framework router components, browser right-click / open-in-new-tab behaviour, or when semantic link vs. button distinction matters for screen readers. For typical in-app navigation the default button mode is simpler. The `label` and `icon` props are ignored in link mode; supply all content in your slotted anchor. **Cannot** be combined with `has-children`.\n\n## Rules\n\n| Topic | Behavior |\n|---|---|\n| **`link` vs `has-children`** | Mutually exclusive. When **`link`** is true, **`hasChildren`** is always false; if both attributes appear in markup, `has-children` is cleared. |\n| **Leading icon** | `icon` prop / `slot=\"leading\"` apply to all item types including parents. Nested sub-items (inside a parent) have the leading column hidden in CSS — use label only. |\n| **Trailing chevron** | Auto-rendered for `has-children` items when `slot=\"trailing\"` is empty. Providing `slot=\"trailing\"` content replaces the auto chevron. |\n| **Events** | **`navigate`** — leaf button rows (pointer or keyboard activation). **`expandedChange`** — expandable rows only (detail = new `expanded` boolean). |\n\n## Usage\n\n### Leaf (icon prop)\n```html\n<xpl-side-nav-item label=\"Dashboard\" icon=\"house-4\" selected></xpl-side-nav-item>\n```\n\n### Leaf (slot=\"leading\")\n```html\n<xpl-side-nav-item label=\"Dashboard\">\n <xpl-icon slot=\"leading\" icon=\"house-4\" size=\"16\"></xpl-icon>\n</xpl-side-nav-item>\n```\n\n### Leaf with trailing icon\n```html\n<xpl-side-nav-item label=\"Xplor Growth\" icon=\"chart-line-up\">\n <xpl-icon slot=\"trailing\" icon=\"external-link\" size=\"16\" aria-hidden=\"true\"></xpl-icon>\n</xpl-side-nav-item>\n```\n\n### Expandable parent (leading icon + auto chevron)\n```html\n<xpl-side-nav-item has-children label=\"Settings\" icon=\"gear\" expanded>\n <xpl-side-nav-item label=\"Profile\"></xpl-side-nav-item>\n <xpl-side-nav-item label=\"Billing\"></xpl-side-nav-item>\n</xpl-side-nav-item>\n```\n\n### Link (slot-only)\n\nWrap the anchor's children in the component CSS classes to match the icon color, label color, and spacing of button-mode items. Avoid whitespace text nodes between flex children — they render as a space character and add an unwanted gap.\n\n```html\n<xpl-side-nav-item link>\n <!-- No whitespace between __leading / __label spans and the closing </a> -->\n <a href=\"/reports\" class=\"xpl-side-nav-item__link\"><span class=\"xpl-side-nav-item__leading\"><xpl-icon icon=\"chart-line\" size=\"16\"></xpl-icon></span><span class=\"xpl-side-nav-item__label\">Reports</span></a>\n</xpl-side-nav-item>\n```\n\n### Disabled and hidden\n```html\n<xpl-side-nav-item label=\"Disabled\" disabled></xpl-side-nav-item>\n<xpl-side-nav-item label=\"Hidden\" hidden></xpl-side-nav-item>\n```\n\n## Slots\n\n| Slot | Description |\n|---|---|\n| `leading` | Leading icon column. Default content is an `xpl-icon` from the `icon` prop. Override with slotted content. Applies to all item types including parents. Renamed from `icon` in v2. |\n| `trailing` | Trailing content (e.g. `xpl-icon icon=\"external-link\"`). On `has-children` items, the auto chevron is rendered here when this slot is empty (`chevron-down` collapsed, `chevron-up` expanded); slotted content replaces the chevron. |\n| *(default)* | **`has-children`:** nested `xpl-side-nav-item` children. **`link`:** one `<a>` (or router link component) containing all row content. Unused in leaf button mode. |\n\n## Link mode checklist\n\n- Do **not** use `label` or `icon` props — they are ignored in link mode. Put all text and icons inside your slotted anchor.\n- Use **`class=\"xpl-side-nav-item__link\"`** on the anchor, **`class=\"xpl-side-nav-item__leading\"`** on the icon wrapper, and **`class=\"xpl-side-nav-item__label\"`** on the text span for consistent icon color, label typography, hover, and focus styling.\n- **No whitespace between flex children** — ensure no newlines or spaces sit between `__leading`, `__label`, and the `</a>` tag. Whitespace between flex items renders as a space character and shifts the label to the right.\n- Set **`aria-current=\"page\"`** on the anchor (not the host) when the route is active.\n- For **disabled** links: set `disabled` on the host and `aria-disabled=\"true\"` + `tabindex=\"-1\"` on the anchor (see **Accessibility**).\n\n## Accessibility\n\n- **Selected (non-link):** `aria-current=\"page\"` on the `button` (leaf or expandable row).\n- **Selected (link):** set `aria-current=\"page\"` on your slotted anchor.\n- **Expandable:** `aria-expanded` on the parent `button` matches `expanded`.\n- **Trailing decorative icons:** add `aria-hidden=\"true\"` to trailing `xpl-icon` elements. If the trailing icon conveys meaning (e.g. external link), provide accessible labeling in the surrounding context.\n- **Disabled:** use the `disabled` prop on non-link rows; for link mode, coordinate host `disabled` with the anchor as above.\n- **Hidden:** `hidden` hides the row; it does not disable interaction by itself — use `disabled` when appropriate.\n\n## Mobile behavior (below `md` / 768px)\n\nInside the `xpl-side-nav` mobile drawer, items are automatically restyled by CSS — no props or markup changes needed.\n\n| Property | Desktop | Mobile |\n|---|---|---|\n| Outer wrapper tap target | — | `min-height: 48px; padding: 6px 8px` |\n| Inner button `min-height` | 32px | 0 (tap target comes from wrapper) |\n| Leading / trailing icon size | 16px (`--xpl-size-100`) | 24px (`--xpl-size-150`) |\n| Top-level label | `title-4` (16px, medium, lh 1.5) | `title-3` (20px, medium, lh 1.4) |\n| Child item label | `title-5` | `title-4` (16px, medium, lh 1.5) |\n| Footer item label | `title-5` | `title-4` (16px, medium, lh 1.5) |\n| Footer row min-height | `28px` (one step smaller than section rows) | `--xpl-size-200` (32px) |\n| Footer leading / trailing icon | 16px (`--xpl-size-100`) | 20px (`--xpl-size-125`) |\n| Child label alignment offset | `padding-left: var(--xpl-side-nav-child-indent)` (32px) | Same — child labels stay aligned with the parent label column on every viewport |\n\n**Link mode and mobile icons:** in button mode the component automatically adjusts the `xpl-icon` `size` prop to 24 below `md`. In link mode, where the consumer controls the anchor content, the CSS scales the `svg` element inside `__leading` / `__trailing` via a presentational-attribute override — no `size` prop change needed.\n\n## Token reference\n\n| Token | Usage |\n|---|---|\n| `--xpl-icon-default` | Leading and trailing icon color (default, hover, selected) |\n| `--xpl-icon-disabled` | Leading, trailing, and label color when disabled |\n| `--xpl-text-strong` | Label color |\n| `--xpl-text-disabled` | Label color when disabled |\n| `--xpl-background-brand-secondary-surface` | Selected row background |\n| `--xpl-background-surface-transparent-10` | Hover row background |\n| `--xpl-border-focus` | Focus outline color |\n| `--xpl-space-8` | `margin-right` on the leading icon box (spacing between icon and label); `margin-left` on the trailing box |\n| `--xpl-size-100` | Leading / trailing icon box: 16px on desktop |\n| `--xpl-size-150` | Leading / trailing icon box on mobile (overrides `--xpl-size-100` below `md`) |\n| `--xpl-size-200` | `min-height` of the inner link / button (32px desktop) |\n| `--xpl-size-300` | `min-height` of the outer row wrapper on mobile (48px tap target) |\n\n## Related\n\n- [xpl-side-nav](../xpl-side-nav) — Sidebar container (`slot=\"section\"`, `slot=\"nav-footer\"`).\n",
12104
12530
  "docs": "Navigation row for `xpl-side-nav`. Renders a button (default) or a slot-only\nanchor container (`link` mode). Three slot positions are available:\n\n- `slot=\"leading\"` — leading icon column (renamed from `slot=\"icon\"` in v2).\n The `icon` prop is a convenience shorthand that renders an `xpl-icon` here\n when no slotted `leading` content is present.\n- `slot=\"trailing\"` — trailing content (e.g. `xpl-icon icon=\"external-link\"`).\n When `has-children` is true and no `trailing` slot content is provided, an\n auto chevron is rendered automatically in this position: `chevron-down` when\n collapsed, `chevron-up` when expanded.\n- default slot — nested `xpl-side-nav-item` children (expandable parents only).\n\n**v2 breaking changes (beta):**\n- `slot=\"icon\"` renamed to `slot=\"leading\"` — no alias kept.\n- Parent rows (`has-children`) now render the leading icon alongside the chevron.\n Previously parents showed chevron + label only (no leading icon).\n- The chevron has moved from the leading position to the trailing position.\n- A slotted `slot=\"trailing\"` replaces the auto chevron on parent rows.\n\n**Migration:**\n| Before (v1) | After (v2) | Action |\n|---|---|---|\n| `slot=\"icon\"` | `slot=\"leading\"` | Rename the slot attribute |\n| Parent row: chevron only (no leading) | Parent row: leading icon + chevron | Pass `icon` prop or `slot=\"leading\"` |\n| (no trailing slot) | `slot=\"trailing\"` | Add for custom trailing content |",
12105
- "docsTags": [],
12531
+ "docsTags": [
12532
+ {
12533
+ "name": "slot",
12534
+ "text": "leading - Leading icon column. The `icon` prop renders an `xpl-icon` here when no slotted `leading` content is present."
12535
+ },
12536
+ {
12537
+ "name": "slot",
12538
+ "text": "trailing - Trailing content (e.g. `xpl-icon icon=\"external-link\"`). On parent rows (`has-children`), an auto chevron renders here when no `trailing` slot content is provided."
12539
+ },
12540
+ {
12541
+ "name": "slot",
12542
+ "text": "- Nested `xpl-side-nav-item` children (expandable parents only)."
12543
+ }
12544
+ ],
12106
12545
  "usage": {},
12107
12546
  "props": [
12108
12547
  {
@@ -12359,7 +12798,20 @@
12359
12798
  ],
12360
12799
  "listeners": [],
12361
12800
  "styles": [],
12362
- "slots": [],
12801
+ "slots": [
12802
+ {
12803
+ "name": "",
12804
+ "docs": "Nested `xpl-side-nav-item` children (expandable parents only)."
12805
+ },
12806
+ {
12807
+ "name": "leading",
12808
+ "docs": "Leading icon column. The `icon` prop renders an `xpl-icon` here when no slotted `leading` content is present."
12809
+ },
12810
+ {
12811
+ "name": "trailing",
12812
+ "docs": "Trailing content (e.g. `xpl-icon icon=\"external-link\"`). On parent rows (`has-children`), an auto chevron renders here when no `trailing` slot content is provided."
12813
+ }
12814
+ ],
12363
12815
  "parts": [],
12364
12816
  "states": [],
12365
12817
  "dependents": [],
@@ -12378,7 +12830,16 @@
12378
12830
  "tag": "xpl-side-nav-section",
12379
12831
  "readme": "# xpl-side-nav-section\n\nGrouping / accordion header for [`xpl-side-nav`](../xpl-side-nav). Wraps nested `xpl-side-nav-item` (and optional divider separators) elements in the default slot and collapses them as a unit.\n\n## Usage\n\n### Collapsible (default)\n\n```html\n<xpl-side-nav-section heading=\"Settings\">\n <xpl-side-nav-item label=\"Location\" icon=\"pin\"></xpl-side-nav-item>\n <xpl-side-nav-item label=\"Schedule\" icon=\"calendar\"></xpl-side-nav-item>\n</xpl-side-nav-section>\n```\n\n### Static (label only, `collapsible=false`)\n\n```html\n<xpl-side-nav-section heading=\"Product\" collapsible=\"false\">\n <xpl-side-nav-item label=\"Location\" icon=\"pin\"></xpl-side-nav-item>\n</xpl-side-nav-section>\n```\n\n### With a badge (`slot=\"badge\"`)\n\nCompose the existing `xpl-badge` — there is no `badge` boolean prop. The wrapper is suppressed automatically when the slot is empty.\n\n```html\n<xpl-side-nav-section heading=\"Updates\">\n <xpl-badge slot=\"badge\" variant=\"purple\" size=\"small\">4</xpl-badge>\n <xpl-side-nav-item label=\"Webhooks\"></xpl-side-nav-item>\n</xpl-side-nav-section>\n```\n\n## Rules\n\n| Topic | Behavior |\n|---|---|\n| **Child indent** | Unlike `xpl-side-nav-item[has-children]`, section children are **not indented** — they align with top-level items at the same leading-icon column, and keep their own leading icon. |\n| **Dividers** | Sections do **not** render their own dividers. Place an `<hr class=\"xpl-side-nav-divider\">` as a sibling (e.g. above and below a closed section) when a visual break is needed — no dedicated component. |\n| **Coordination** | Sections behave independently — there is no built-in single-open accordion coordination between sibling sections. |\n| **Chevron** | Auto-rendered for collapsible sections: `chevron-right` collapsed / `chevron-down` expanded (the glyph swaps, no rotation). Not rendered when `collapsible=\"false\"`. |\n\n## Accessibility\n\n- The collapsible header is a real `<button type=\"button\">` with `aria-expanded` (mirrors `expanded`) and `aria-controls` pointing to the children region's `id`.\n- The button's accessible name comes from the visible `heading` text or `slot=\"badge\"` content. It falls back to `aria-label=\"Toggle section\"` only when both are absent or whitespace-only; the chevron is `aria-hidden`.\n- The decorative chevron carries `aria-hidden=\"true\"` — the button's `aria-expanded` already conveys state.\n- Collapsed children are marked `inert`, removing them from the tab order (not just visually hidden) — same pattern as `xpl-side-nav-item[has-children]`.\n- The static variant (`collapsible=\"false\"`) renders a plain `<div>` header — no button semantics, since there is nothing to toggle.\n\n## Token reference\n\n| Token | Usage |\n|---|---|\n| `--xpl-text-subdued` | Title color |\n| `--xpl-icon-subtle` | Chevron color |\n| `--xpl-font-weight-bold` | Title font weight (extends `.xpl-text-callout` for size/line-height) |\n| `--xpl-space-8` | Title-row gap; horizontal header padding (desktop, reconciled against the parent section's own 8px padding) |\n| `--xpl-space-24` / `--xpl-space-4` | Header vertical padding (closed: 24/24; expanded or static: 24/4) |\n| `--xpl-size-100` / `--xpl-size-150` | Chevron box: 16px desktop, 24px mobile |\n| `--xpl-size-250` | Header `min-height` (40px) |\n| `--xpl-border-focus` | Focus outline color |\n\n## Related\n\n- [xpl-side-nav](../xpl-side-nav) — Sidebar container (`slot=\"section\"`, `slot=\"nav-footer\"`).\n- [xpl-side-nav-item](../xpl-side-nav-item) — Navigation row.\n- `<hr class=\"xpl-side-nav-divider\">` — Standalone separator (no dedicated component), often placed around a section.\n- [xpl-badge](../../xpl-badge) — Composed via `slot=\"badge\"`.\n",
12380
12832
  "docs": "Grouping / accordion header for `xpl-side-nav`. Wraps nested `xpl-side-nav-item`\n(and optional divider separators) elements in the default slot and collapses\nthem as a unit.\n\nTwo variants:\n- **Collapsible (default, `collapsible=true`)** — a `<button>` header with a\n chevron (`chevron-right` collapsed / `chevron-down` expanded, no rotation —\n the glyph itself swaps). Click toggles `expanded` and emits `expandedChange`.\n- **Static (`collapsible=false`)** — a non-interactive title row with no chevron;\n children are always shown. Used for a static section label (e.g. a future\n product-name / location-changer header).\n\nUnlike `xpl-side-nav-item[has-children]`, section children are **not indented**\n— they align with top-level items at the same leading-icon column.\n\n`slot=\"badge\"` renders an optional badge (e.g. `xpl-badge`) in the header title\nrow. The wrapper is suppressed when the slot is empty.",
12381
- "docsTags": [],
12833
+ "docsTags": [
12834
+ {
12835
+ "name": "slot",
12836
+ "text": "- Section children (nested `xpl-side-nav-item` elements, optionally separated by dividers)."
12837
+ },
12838
+ {
12839
+ "name": "slot",
12840
+ "text": "badge - Optional badge (e.g. `xpl-badge`) rendered in the header title row. The wrapper is suppressed when the slot is empty."
12841
+ }
12842
+ ],
12382
12843
  "usage": {},
12383
12844
  "props": [
12384
12845
  {
@@ -12482,7 +12943,16 @@
12482
12943
  ],
12483
12944
  "listeners": [],
12484
12945
  "styles": [],
12485
- "slots": [],
12946
+ "slots": [
12947
+ {
12948
+ "name": "",
12949
+ "docs": "Section children (nested `xpl-side-nav-item` elements, optionally separated by dividers)."
12950
+ },
12951
+ {
12952
+ "name": "badge",
12953
+ "docs": "Optional badge (e.g. `xpl-badge`) rendered in the header title row. The wrapper is suppressed when the slot is empty."
12954
+ }
12955
+ ],
12486
12956
  "parts": [],
12487
12957
  "states": [],
12488
12958
  "dependents": [],
@@ -12754,6 +13224,34 @@
12754
13224
  "readme": "# xpl-slideout\n\nContent and Footer subcomponents are optional and can be hidden. Slideout always has a header with an X close button upper right. Slide Out Panel OPTIONALLY uses the [<xpl-backdrop>](https://apollo.xplordocs.com/?path=/story/components-backdrop--backdrop) component to overlay content and create focus on the Slide Out Panel content.\n\n## Token Use\n\n`xpl-slideout` uses Apollo Foundation purpose-specific tokens; light and dark mode are handled by the token definitions, so the component has no `.dark` overrides.\n\n| Element | Property | Token |\n| ------- | -------- | ----- |\n| `.slideout` | background | `--xpl-background-surface-default` |\n| `.slideout` | shadow color | `--xpl-color-transparent-neutral-10` / `--xpl-color-transparent-neutral-5` |\n| `.title` | color | `--xpl-text-default` |\n| `.title` | typography (size / weight / line-height) | `@extend .xpl-text-title-3` → `title-3` / `bold` / `1.4` |\n| `.subtitle` | color | `--xpl-text-subdued` |\n| `.subtitle` | typography (size / weight / line-height) | `@extend .xpl-text-body` → `body` / `normal` / `1.4286` |\n| `.content` | text color | `--xpl-text-default` |\n| `.content.has-shadow` | scroll-overflow shadow (top/bottom only) | `--xpl-color-transparent-neutral-10` / `--xpl-color-transparent-neutral-5` |\n| search `.xpl-input-wrapper` | background | `--xpl-background-surface-default` |\n| search `input` | color | `--xpl-text-default` |\n\nPanel widths are documented CSS literals: `min-width: 21.5rem`, `max-width: 37.5rem` (see `slideout.css` and `TECH_DEBT.md`).\n\nThe `.content.has-shadow` shadow is a scroll affordance applied only to the top and bottom edges (per Figma node 52462-9896) to signal that content is hidden behind the header/footer when it overflows.\n\n> **Elevation shadow (documented deviation):** the panel shadow is a custom single-edge shadow cast left onto the page content (`-10px 0 15px -3px`, `4px 0 6px -2px` over `--xpl-color-transparent-neutral-10 / -5`). Figma specs a leading-edge-only shadow for this right-anchored panel; the shared `--xpl-elevation-*` tokens are vertical (bottom-cast) and do not fit a side-anchored panel, so the hand-authored geometry is kept — see `TECH_DEBT.md`.\n",
12755
13225
  "docs": "Content and Footer subcomponents are optional and can be hidden. Slideout always has a header with an X close button upper right. Slide Out Panel OPTIONALLY uses the [<xpl-backdrop>](https://apollo.xplordocs.com/?path=/story/components-backdrop--backdrop) component to overlay content and create focus on the Slide Out Panel content.",
12756
13226
  "docsTags": [
13227
+ {
13228
+ "name": "slot",
13229
+ "text": "title - The slideout header title."
13230
+ },
13231
+ {
13232
+ "name": "slot",
13233
+ "text": "subtitle - Subtitle content rendered below the title."
13234
+ },
13235
+ {
13236
+ "name": "slot",
13237
+ "text": "search - Search content rendered in the header."
13238
+ },
13239
+ {
13240
+ "name": "slot",
13241
+ "text": "content - The main scrollable body content."
13242
+ },
13243
+ {
13244
+ "name": "slot",
13245
+ "text": "tertiary - The tertiary footer action. Rendered only when `showFooter` is set."
13246
+ },
13247
+ {
13248
+ "name": "slot",
13249
+ "text": "secondary - The secondary footer action. Rendered only when `showFooter` is set."
13250
+ },
13251
+ {
13252
+ "name": "slot",
13253
+ "text": "primary - The primary footer action. Rendered only when `showFooter` is set."
13254
+ },
12757
13255
  {
12758
13256
  "name": "since",
12759
13257
  "text": "1.1.0"
@@ -12909,7 +13407,36 @@
12909
13407
  }
12910
13408
  ],
12911
13409
  "styles": [],
12912
- "slots": [],
13410
+ "slots": [
13411
+ {
13412
+ "name": "content",
13413
+ "docs": "The main scrollable body content."
13414
+ },
13415
+ {
13416
+ "name": "primary",
13417
+ "docs": "The primary footer action. Rendered only when `showFooter` is set."
13418
+ },
13419
+ {
13420
+ "name": "search",
13421
+ "docs": "Search content rendered in the header."
13422
+ },
13423
+ {
13424
+ "name": "secondary",
13425
+ "docs": "The secondary footer action. Rendered only when `showFooter` is set."
13426
+ },
13427
+ {
13428
+ "name": "subtitle",
13429
+ "docs": "Subtitle content rendered below the title."
13430
+ },
13431
+ {
13432
+ "name": "tertiary",
13433
+ "docs": "The tertiary footer action. Rendered only when `showFooter` is set."
13434
+ },
13435
+ {
13436
+ "name": "title",
13437
+ "docs": "The slideout header title."
13438
+ }
13439
+ ],
12913
13440
  "parts": [],
12914
13441
  "states": [],
12915
13442
  "dependents": [],
@@ -12935,7 +13462,7 @@
12935
13462
  "filePath": "src/components/components/xpl-spotlight/xpl-spotlight.tsx",
12936
13463
  "encapsulation": "none",
12937
13464
  "tag": "xpl-spotlight",
12938
- "readme": "# xpl-spotlight\n\n`xpl-spotlight` renders a guided-tour callout with an anchor dot, optional media, step messaging, and prop-driven footer actions. It can either render inline using the requested `anchorPosition` or attach to a target element with Floating UI when `target` is provided.\n\n## Usage Examples\n\nUse the built-in title/body props and default actions for a simple tour step:\n\n```html\n<xpl-spotlight\n heading=\"Review recent activity\"\n body=\"Check status updates here before moving to the next step.\"\n action-layout=\"default\"\n anchor-position=\"top-right\"\n current-step=\"2\"\n total-steps=\"5\"\n is-dismissible=\"true\"\n is-open=\"true\"\n primary-action-label=\"Next\"\n secondary-action-label=\"Back\"\n show-actions=\"true\"\n show-image=\"true\"\n show-steps=\"true\"\n target=\"\"\n></xpl-spotlight>\n```\n\nAnchor the spotlight to a specific element and provide custom media content:\n\n```html\n<button id=\"dashboard-filter-button\" type=\"button\">Filters</button>\n\n<xpl-spotlight\n target=\"#dashboard-filter-button\"\n heading=\"Narrow the results\"\n body=\"Use filters to focus on the accounts or activity that matter most.\"\n current-step=\"1\"\n total-steps=\"3\"\n primary-action-label=\"Next\"\n secondary-action-label=\"Skip\"\n>\n <div slot=\"image\">\n <img src=\"/assets/spotlight-filter-preview.png\" alt=\"Preview of filter controls\" />\n </div>\n</xpl-spotlight>\n```\n\n\nControl visibility from the parent with `is-open` and close it by responding to `dismiss`:\n\n```html\n<xpl-spotlight\n id=\"guided-tour-step\"\n is-open=\"true\"\n heading=\"Review recent activity\"\n body=\"Check status updates here before moving to the next step.\"\n></xpl-spotlight>\n\n<script>\n const spotlight = document.getElementById('guided-tour-step');\n\n spotlight?.addEventListener('dismiss', () => {\n spotlight.setAttribute('is-open', 'false');\n });\n</script>\n```\n\nMirror the same layout in plain HTML when documenting or prototyping the component structure outside the custom element:\n\n```html\n<div class=\"xpl-spotlight xpl-spotlight--top-left xpl-spotlight--visible\"\n role=\"dialog\" aria-modal=\"false\" aria-labelledby=\"spotlight-title\" tabindex=\"-1\">\n <button type=\"button\" class=\"xpl-spotlight__target\" aria-label=\"Spotlight indicator\">\n <div class=\"xpl-spotlight__target-outer\"></div>\n <div class=\"xpl-spotlight__target-inner\"></div>\n </button>\n <div class=\"xpl-spotlight__dismiss\">\n <xpl-button variant=\"secondary\" size=\"sm\" icon-only aria-label=\"Dismiss tour step\">\n <xpl-icon icon=\"xmark\" size=\"16\"></xpl-icon>\n </xpl-button>\n </div>\n <div class=\"xpl-spotlight__frame\">\n <div class=\"xpl-spotlight__image-container\">\n <img src=\"/assets/spotlight-preview.png\" alt=\"Feature preview\" />\n </div>\n <div class=\"xpl-spotlight__content\">\n <div class=\"xpl-spotlight__header\">\n <span class=\"xpl-spotlight__title\" id=\"spotlight-title\">Tour Title</span>\n <p class=\"xpl-spotlight__body\">Copy about the feature and/or functionality the dot is pointing to.</p>\n </div>\n <div class=\"xpl-spotlight__actions xpl-spotlight__actions--dual xpl-spotlight__actions--with-steps\">\n <div class=\"xpl-spotlight__action-slot xpl-spotlight__action-slot--secondary\">\n <xpl-button variant=\"secondary\">Back</xpl-button>\n </div>\n <span class=\"xpl-spotlight__step-text\">2 of 5</span>\n <div class=\"xpl-spotlight__action-slot xpl-spotlight__action-slot--primary\">\n <xpl-button variant=\"primary\">Next</xpl-button>\n </div>\n </div>\n </div>\n </div>\n</div>\n```\n\n## Accessibility\n\n`xpl-spotlight` renders with `role=\"dialog\"` and `aria-modal=\"false\"` so assistive technologies treat it as a **non-modal** tour callout: focus is not trapped inside the card, and users can tab to the rest of the page while the step is open. The accessible name comes from the title region:\n\n- When the default title is used, the generated heading element receives the internal `aria-labelledby` id.\n- When a custom `title` slot is provided, the component assigns that same id to the first slotted title element if it does not already have one. If the slotted element already has an `id`, that value is kept and `aria-labelledby` references it (the host does not point at a missing id).\n- When neither a `heading` prop nor a `title` slot is provided, the dialog falls back to `aria-label=\"Tour step\"` so that assistive technologies always have an accessible name.\n\n### Keyboard navigation\n\n| Key | Behavior |\n|-----|----------|\n| `Tab` / `Shift+Tab` | Moves focus between interactive controls (dismiss button, secondary action, primary action). Focus is **not** trapped — users can tab out of the spotlight to the rest of the page. |\n| `Escape` | Emits `dismiss` when `isDismissible` is true and focus is inside the spotlight. Does nothing when focus is outside the component or the spotlight is closed. |\n| `Enter` / `Space` | Activates the focused button (dismiss, primary, or secondary action) via standard `xpl-button` behavior. |\n\n### Focus management\n\n- **On open:** focus moves automatically to the dismiss button (if `isDismissible` is true) or the first action button. When neither a dismiss button nor action buttons are rendered, the host element itself receives focus via `tabindex=\"-1\"`.\n- **On close:** focus returns to the element that was focused before the spotlight opened, provided that element is still in the DOM.\n- The decorative anchor dot (`.xpl-spotlight__target`) is marked `aria-hidden=\"true\"` so it is not announced or focusable.\n\n\n## Design Tokens\n\nThe component relies on Foundation purpose tokens in `spotlight.css` so light/dark mode works without component-level `.dark` overrides.\n\n| Property | Token |\n|----------|-------|\n| Card width | `var(--xpl-spotlight-width)` on the host (defined in `spotlight.css`; design-specific width until shared layout tokens exist — see `TECH_DEBT.md`) |\n| Image region min-height | `var(--xpl-spotlight-image-min-height)` on `.xpl-spotlight__image-container` (same note as card width) |\n| Card background | `var(--xpl-background-surface-default)` |\n| Card shadow | `var(--xpl-elevation-3)` (theme-aware elevation token) |\n| Host stacking | `var(--xpl-z-index-800)` |\n| Placeholder pattern | `var(--xpl-background-surface-subtle)` |\n| Heading text | `var(--xpl-text-default)` |\n| Body text | `var(--xpl-text-default)` |\n| Step text | `var(--xpl-text-subdued)` |\n| Open/closed visibility | `opacity` transition on `.xpl-spotlight--visible` |\n| Anchor outer ring | `var(--xpl-background-surface-information-bold)` |\n| Anchor inner dot | `var(--xpl-background-accent-default)` |\n| Image placeholder icon/text | `var(--xpl-icon-subtle)` |\n| Focus outline | `var(--xpl-border-focus)` |\n| Anchored host (when `target` is set) | `position: fixed` via `.xpl-spotlight--anchored`; `left` / `top` set from Floating UI (`strategy: 'fixed'`) |\n\n## Icon Mapping\n\n`xpl-spotlight` uses Apollo icons for fixed controls and fallback media placeholders:\n\n| UI area | `xpl-icon` name |\n|---------|------------------|\n| Dismiss button | `xmark` |\n| Empty image placeholder, image affordance | `photo` |\n| Empty image placeholder, video affordance | `video` |\n\n## Deprecated props\n\nNone.\n\n## Notes\n\n- `showImage=\"false\"` hides the media region even if `slot=\"image\"` content is present.\n- When `showImage` is left enabled and no `slot=\"image\"` content is provided, the component renders the built-in placeholder instead of collapsing the media region.\n- `showActions=\"false\"` removes the default actions row entirely.\n- `is-open=\"false\"` keeps the host element mounted but renders no spotlight UI until it is opened again.\n- `action-layout` defaults to `\"default\"`. `action-layout=\"default\"` renders the Back/Next pair with **disabled** edge buttons at step boundaries (Back disabled on step 1, Next disabled on the last step). `action-layout=\"dual\"` renders the Back/Next pair with **hidden** edge buttons at step boundaries. `action-layout=\"single\"` renders a single primary action.\n- `primary-action-label` and `secondary-action-label` control the default button text without needing action slots.\n- `target` enables Floating UI positioning with `offset`, `flip`, `shift`, and `autoUpdate` so the spotlight follows the referenced element on resize and scroll. Default offset and shift padding match Foundation spacing (`--xpl-space-12` / `--xpl-space-8`).\n- If `target` is missing or contains an invalid CSS selector, the component safely falls back to the requested `anchor-position` instead of throwing.\n- **Storybook:** open **Components → Spotlight** to adjust props via controls. Set `target` to `#spotlight-story-anchor` to preview Floating UI anchoring against the demo button on the canvas. The **Design** addon links to the Apollo Web Figma file (replace the `node-id` in Storybook with the Spotlight frame when Design publishes a dedicated link).\n",
13465
+ "readme": "# xpl-spotlight\n\n`xpl-spotlight` renders a guided-tour callout with an anchor dot, optional media, step messaging, and prop-driven footer actions. It can either render inline using the requested `anchorPosition` or attach to a target element with Floating UI when `target` is provided.\n\n## Usage Examples\n\nUse the built-in title/body props and default actions for a simple tour step:\n\n```html\n<xpl-spotlight\n heading=\"Review recent activity\"\n body=\"Check status updates here before moving to the next step.\"\n action-layout=\"default\"\n anchor-position=\"top-right\"\n current-step=\"2\"\n total-steps=\"5\"\n is-dismissible=\"true\"\n is-open=\"true\"\n primary-action-label=\"Next\"\n secondary-action-label=\"Back\"\n show-actions=\"true\"\n show-image=\"true\"\n show-steps=\"true\"\n target=\"\"\n></xpl-spotlight>\n```\n\nAnchor the spotlight to a specific element and provide custom media content:\n\n```html\n<button id=\"dashboard-filter-button\" type=\"button\">Filters</button>\n\n<xpl-spotlight\n target=\"#dashboard-filter-button\"\n heading=\"Narrow the results\"\n body=\"Use filters to focus on the accounts or activity that matter most.\"\n current-step=\"1\"\n total-steps=\"3\"\n primary-action-label=\"Next\"\n secondary-action-label=\"Skip\"\n>\n <div slot=\"image\">\n <img src=\"/assets/spotlight-filter-preview.png\" alt=\"Preview of filter controls\" />\n </div>\n</xpl-spotlight>\n```\n\n\nControl visibility from the parent with `is-open` and close it by responding to `dismiss`:\n\n```html\n<xpl-spotlight\n id=\"guided-tour-step\"\n is-open=\"true\"\n heading=\"Review recent activity\"\n body=\"Check status updates here before moving to the next step.\"\n></xpl-spotlight>\n\n<script>\n const spotlight = document.getElementById('guided-tour-step');\n\n spotlight?.addEventListener('dismiss', () => {\n spotlight.setAttribute('is-open', 'false');\n });\n</script>\n```\n\nMirror the same layout in plain HTML when documenting or prototyping the component structure outside the custom element:\n\n```html\n<div class=\"xpl-spotlight xpl-spotlight--top-left xpl-spotlight--visible\"\n role=\"dialog\" aria-modal=\"false\" aria-labelledby=\"spotlight-title\" tabindex=\"-1\">\n <button type=\"button\" class=\"xpl-spotlight__target\" aria-label=\"Spotlight indicator\">\n <div class=\"xpl-spotlight__target-outer\"></div>\n <div class=\"xpl-spotlight__target-inner\"></div>\n </button>\n <div class=\"xpl-spotlight__dismiss\">\n <xpl-button variant=\"secondary\" size=\"sm\" icon-only aria-label=\"Dismiss tour step\">\n <xpl-icon icon=\"xmark\" size=\"16\"></xpl-icon>\n </xpl-button>\n </div>\n <div class=\"xpl-spotlight__frame\">\n <div class=\"xpl-spotlight__image-container\">\n <img src=\"/assets/spotlight-preview.png\" alt=\"Feature preview\" />\n </div>\n <div class=\"xpl-spotlight__content\">\n <div class=\"xpl-spotlight__header\">\n <span class=\"xpl-spotlight__title\" id=\"spotlight-title\">Tour Title</span>\n <p class=\"xpl-spotlight__body\">Copy about the feature and/or functionality the dot is pointing to.</p>\n </div>\n <div class=\"xpl-spotlight__actions xpl-spotlight__actions--dual xpl-spotlight__actions--with-steps\">\n <div class=\"xpl-spotlight__action-slot xpl-spotlight__action-slot--secondary\">\n <xpl-button variant=\"secondary\">Back</xpl-button>\n </div>\n <span class=\"xpl-spotlight__step-text\">2 of 5</span>\n <div class=\"xpl-spotlight__action-slot xpl-spotlight__action-slot--primary\">\n <xpl-button variant=\"primary\">Next</xpl-button>\n </div>\n </div>\n </div>\n </div>\n</div>\n```\n\n## Accessibility\n\n`xpl-spotlight` renders with `role=\"dialog\"` and `aria-modal=\"false\"` so assistive technologies treat it as a **non-modal** tour callout: focus is not trapped inside the card, and users can tab to the rest of the page while the step is open. The accessible name comes from the title region:\n\n- When the default title is used, the generated heading element receives the internal `aria-labelledby` id.\n- When a custom `title` slot is provided, the component assigns that same id to the first slotted title element if it does not already have one. If the slotted element already has an `id`, that value is kept and `aria-labelledby` references it (the host does not point at a missing id).\n- When neither a `heading` prop nor a `title` slot is provided, the dialog falls back to `aria-label=\"Tour step\"` so that assistive technologies always have an accessible name.\n\n### Keyboard navigation\n\n| Key | Behavior |\n|-----|----------|\n| `Tab` / `Shift+Tab` | Moves focus between interactive controls (dismiss button, secondary action, primary action). Focus is **not** trapped — users can tab out of the spotlight to the rest of the page. |\n| `Escape` | Emits `dismiss` when `isDismissible` is true and focus is inside the spotlight. Does nothing when focus is outside the component or the spotlight is closed. |\n| `Enter` / `Space` | Activates the focused button (dismiss, primary, or secondary action) via standard `xpl-button` behavior. |\n\n### Focus management\n\n- **On open:** focus moves automatically to the dismiss button (if `isDismissible` is true) or the first action button. When neither a dismiss button nor action buttons are rendered, the host element itself receives focus via `tabindex=\"-1\"`.\n- **On close:** focus returns to the element that was focused before the spotlight opened, provided that element is still in the DOM.\n- The decorative anchor dot (`.xpl-spotlight__target`) is marked `aria-hidden=\"true\"` so it is not announced or focusable.\n\n\n## Design Tokens\n\nThe component relies on Foundation purpose tokens in `spotlight.css` so light/dark mode works without component-level `.dark` overrides.\n\n| Property | Token |\n|----------|-------|\n| Card width | `var(--xpl-spotlight-width)` on the host (defined in `spotlight.css`; design-specific width until shared layout tokens exist — see `TECH_DEBT.md`) |\n| Image region min-height | `var(--xpl-spotlight-image-min-height)` on `.xpl-spotlight__image-container` (same note as card width) |\n| Card background | `var(--xpl-background-surface-default)` |\n| Card shadow | `var(--xpl-elevation-3)` (theme-aware elevation token) |\n| Host stacking | `var(--xpl-z-index-800)` |\n| Placeholder pattern | `var(--xpl-background-surface-subtle)` |\n| Heading text | `var(--xpl-text-default)` |\n| Heading line-height | `var(--xpl-line-height-title-3)` (title-3) |\n| Body text | `var(--xpl-text-default)` |\n| Step text | `var(--xpl-text-subdued)` |\n| Open/closed visibility | `opacity` transition on `.xpl-spotlight--visible` |\n| Anchor outer ring | `var(--xpl-background-surface-information-bold)` |\n| Anchor inner dot | `var(--xpl-background-accent-default)` |\n| Image placeholder icon/text | `var(--xpl-icon-subtle)` |\n| Focus outline | `var(--xpl-border-focus)` |\n| Anchored host (when `target` is set) | `position: fixed` via `.xpl-spotlight--anchored`; `left` / `top` set from Floating UI (`strategy: 'fixed'`) |\n\n## Icon Mapping\n\n`xpl-spotlight` uses Apollo icons for fixed controls and fallback media placeholders:\n\n| UI area | `xpl-icon` name |\n|---------|------------------|\n| Dismiss button | `xmark` |\n| Empty image placeholder, image affordance | `photo` |\n| Empty image placeholder, video affordance | `video` |\n\n## Deprecated props\n\nNone.\n\n## Notes\n\n- `showImage=\"false\"` hides the media region even if `slot=\"image\"` content is present.\n- When `showImage` is left enabled and no `slot=\"image\"` content is provided, the component renders the built-in placeholder instead of collapsing the media region.\n- `showActions=\"false\"` removes the default actions row entirely.\n- `is-open=\"false\"` keeps the host element mounted but renders no spotlight UI until it is opened again.\n- `action-layout` defaults to `\"default\"`. `action-layout=\"default\"` renders the Back/Next pair with **disabled** edge buttons at step boundaries (Back disabled on step 1, Next disabled on the last step). `action-layout=\"dual\"` renders the Back/Next pair with **hidden** edge buttons at step boundaries. `action-layout=\"single\"` renders a single primary action.\n- `primary-action-label` and `secondary-action-label` control the default button text without needing action slots.\n- `target` enables Floating UI positioning with `offset`, `flip`, `shift`, and `autoUpdate` so the spotlight follows the referenced element on resize and scroll. Default offset and shift padding match Foundation spacing (`--xpl-space-12` / `--xpl-space-8`).\n- If `target` is missing or contains an invalid CSS selector, the component safely falls back to the requested `anchor-position` instead of throwing.\n- **Storybook:** open **Components → Spotlight** to adjust props via controls. Set `target` to `#spotlight-story-anchor` to preview Floating UI anchoring against the demo button on the canvas. The **Design** addon links to the Apollo Web Figma file (replace the `node-id` in Storybook with the Spotlight frame when Design publishes a dedicated link).\n",
12939
13466
  "docs": "`xpl-spotlight` renders a guided-tour callout with an anchor dot, optional media, step messaging, and prop-driven footer actions. It can either render inline using the requested `anchorPosition` or attach to a target element with Floating UI when `target` is provided.",
12940
13467
  "docsTags": [
12941
13468
  {
@@ -13884,7 +14411,7 @@
13884
14411
  "filePath": "src/components/components/xpl-table/xpl-table.tsx",
13885
14412
  "encapsulation": "none",
13886
14413
  "tag": "xpl-table",
13887
- "readme": "# xpl-table\r\n\r\nTables are used to organize and display information from a data set.\r\n\r\n## Rendering modes\r\n\r\n1. **Compositional (recommended)** — Provide `xpl-table-header`, `xpl-table-body`, and optional `xpl-table-footer` with `xpl-table-row` / `xpl-table-header-cell` / `xpl-table-cell` / `xpl-table-footer-cell`. The host uses CSS grid (`role=\"grid\"`). Column widths come from each **`xpl-table-header-cell`** (`width` in px, or `column-width` for any grid track; default `auto` / content-sized). Use `label` for an accessible name. **Sorting:** set `sortable` (and optional `sort-key`) on label header cells; `xpl-table` emits **`sortChanged`** with sort-key slugs → `asc` / `desc` / `null`. The host does **not** reorder row DOM — listen and update your data (or reorder nodes yourself), same as the main Table Storybook demo.\r\n2. **Legacy data-driven** — Set `columns`, `data`, and optionally `footer` for a built-in `<table>` implementation. That path supports `isSortable` / `sortableColumns`, `multiselect`, and emits `sortChanged` / `tableSelect` (legacy can reorder rows unless you `preventDefault`). Prefer compositional markup for new work.\r\n\r\nComposable **selection** (`selectable`), **toolbar**, and copy-paste examples live under **[Usage](#usage)** below (read the breaking-change note first if you are migrating from auto-injected selection cells).\r\n\r\n### Row reorder (`row-reorderable`, managed grip column)\r\n\r\nWhen `row-reorderable` is set, Apollo can **inject** a leading reorder column with a native **button** + optional `xpl-icon` on each body row (unless you author that column yourself). Keyboard and pointer reorder share the same grip; **keyboard mode** is indicated by `aria-pressed`, updated labels, the class `xpl-table__row-drag-handle--keyboard-mode`, and optionally a **different icon**.\r\n\r\n**Pointer drag (full row):** Body rows use CSS **`subgrid`** so `xpl-table-row` is a real grid box (like `xpl-list-item`’s row surface). When `CSS.supports('grid-template-columns','subgrid')` is true, **`draggable`** is set on **`xpl-table-row`** so the browser’s drag image matches the whole row; drag still **starts only from the grip** (pointer-down there sets the drag token—dragging from other cells is ignored). Engines without subgrid keep **`draggable`** on the grip button only.\r\n\r\n| Attribute | When it applies | Purpose |\r\n| --------- | ---------------- | ------- |\r\n| `reorder-handle-icon` | Default / drag affordance | Icon name for `xpl-icon` (default `grip-dots-vertical`). Use `reorder-handle-icon=\"\"` to omit the injected icon and use CSS or your own markup in a **custom** reorder cell instead. |\r\n| `reorder-handle-icon-keyboard` | While that row is in **keyboard reorder mode** (Enter or Space on the grip) | Icon name for the same grip when the user is using **Arrow Up/Down** to move the row (default `caret-expand-y`, matching `xpl-list`). Use `reorder-handle-icon-keyboard=\"\"` to show **no** icon in keyboard mode only. |\r\n\r\n**Recommended for product UIs:** Set **both** attributes to your design-system icon names so drag vs keyboard modes are visually distinct. You can still add styles targeting `.xpl-table__row-drag-handle--keyboard-mode` (e.g. outline) for extra clarity.\r\n\r\n**Interaction:** Focus the grip → **Enter** or **Space** → **Arrow Up/Down** to reorder → **Escape** to exit. Pointer down on the row exits keyboard mode; starting a drag also exits keyboard mode.\r\n\r\n**Custom reorder column:** If you add reorder cells in your own template (not injected), **you** own icons, keyboard behavior, and `rowOrderChange` — these attributes apply only to **managed** injected grips.\r\n\r\n## Usage\r\n\r\n### Breaking change — compositional `selectable`\r\n\r\n**Before:** Setting `selectable` alone caused the table to **inject** selection header/body/footer cells into the light DOM (including nodes marked `data-xpl-selection-managed`).\r\n\r\n**After:** You **must** add the selection column in your own markup (see **Selection** below). `xpl-table` no longer creates or removes those cells; it only syncs selection state and emits events. If you previously relied on automatic injection, add the header checkbox column, per-row checkbox cells, and (when using a footer) leading footer cells so column counts match.\r\n\r\n### Selection (`selectable`) and `rowSelectionChange`\r\n\r\nSet `selectable` on `xpl-table` and **author the selection column in your template** so the framework that renders the table owns every cell node.\r\n\r\n**Required pieces:**\r\n\r\n- **Header:** one [`xpl-table-header-cell`](xpl-table-header-cell) with `type=\"checkbox\"` (tri-state “select all”).\r\n- **Body:** in each `xpl-table-row`, a cell aligned with that column containing **`xpl-checkbox`** (value is driven from `row-id` on the row).\r\n- **Footer:** if you use `xpl-table-footer`, add a leading `xpl-table-footer-cell` per row for the selection column (often empty) so column counts stay aligned.\r\n\r\n**Behavior:** After `customElements.whenDefined('xpl-table')`, listen for **`rowSelectionChange`** on the **same** `<xpl-table>` element (`bubbles: false`, `composed: true`). The event payload is `{ selectedRowIds: string[] }`. The host maintains `selectedRowIds`, syncs `checked` / `indeterminate` on the header and row checkboxes, sets `selected` on rows, and shows the bulk toolbar while at least one row is selected. Row checkbox **`aria-label`** is derived from the first **non-selection** data cell in that row (e.g. `Select row, Jordan Lee`); the header uses “Select all rows” unless you set **`checkbox-aria-label`** on the checkbox header cell.\r\n\r\n**Example (web component):**\r\n\r\n```html\r\n<xpl-table id=\"roster\" selectable label=\"Team roster\">\r\n <xpl-table-header>\r\n <xpl-table-row>\r\n <xpl-table-header-cell type=\"checkbox\"></xpl-table-header-cell>\r\n <xpl-table-header-cell label=\"Name\" sortable sort-key=\"name\"></xpl-table-header-cell>\r\n </xpl-table-row>\r\n </xpl-table-header>\r\n <xpl-table-body>\r\n <xpl-table-row row-id=\"1\">\r\n <xpl-table-cell><xpl-checkbox></xpl-checkbox></xpl-table-cell>\r\n <xpl-table-cell>Ada</xpl-table-cell>\r\n </xpl-table-row>\r\n </xpl-table-body>\r\n</xpl-table>\r\n<script type=\"module\">\r\n await customElements.whenDefined('xpl-table');\r\n document.getElementById('roster')?.addEventListener('rowSelectionChange', (e) => {\r\n console.log(e.detail.selectedRowIds);\r\n });\r\n</script>\r\n```\r\n\r\n#### Framework notes (Vue / React / etc.)\r\n\r\nUse **`xpl-checkbox`** in the selection column so `xpl-table` can sync `checked`, `indeterminate`, and `value` from `row-id`. On **`xpl-table-header-cell`**, the selection header uses **`type=\"checkbox\"`**; the prop is **reflected** to the host attribute so Vue bindings like `:type=\"'checkbox'\"` still expose `type=\"checkbox\"` in the DOM for the table’s column detection and CSS.\r\n\r\n### Legacy (`columns` / `data`)\r\n\r\nSet `columns`, `data`, and optional `footer` as **properties** after `customElements.whenDefined('xpl-table')`, or use attributes where your bundler passes JSON strings.\r\n\r\n```html\r\n<xpl-table id=\"legacy\"></xpl-table>\r\n<script type=\"module\">\r\n await customElements.whenDefined('xpl-table');\r\n const t = document.getElementById('legacy');\r\n t.columns = ['Name', 'Score'];\r\n t.data = [\r\n ['Ada', '100'],\r\n ['Bob', '95'],\r\n ];\r\n t.multiselect = true;\r\n t.addEventListener('tableSelect', (e) => console.log(e.detail));\r\n</script>\r\n```\r\n\r\n## Accessibility\r\n\r\n| Concern | Behavior |\r\n| ------- | -------- |\r\n| Table landmark | Compositional host uses **`role=\"grid\"`**; set **`label`** (maps to `aria-label`). |\r\n| Columns | Header cells use **`role=\"columnheader\"`** with `aria-colindex`; sortable label headers expose sort state via **`aria-sort`**. |\r\n| Selection | Checkbox column: header reflects **checked / mixed** via `aria-checked`; row checkboxes get **`aria-label`** from the first non-selection cell’s text (or override with `checkbox-aria-label` on the checkbox header cell). |\r\n| Sort | Sorting is a **`<button type=\"button\">`** inside the header cell; focus uses **`:focus-visible`** with focus tokens in `table-header-cell.css`. |\r\n| Row reorder | Managed grip is a **`<button>`** with `aria-label` / `aria-pressed` for keyboard reorder mode; live region announces reorder when enabled. |\r\n\r\nKeyboard: **Tab** through sort buttons, checkboxes, reorder grips, and cell actions. **Enter** / **Space** activate sort and the reorder grip per native button semantics.\r\n\r\n## Design Tokens\r\n\r\nCompositional grid layout tokens live in `table.css`; cell, row, header, and toolbar tokens are split across sibling CSS files.\r\n\r\n| Element | Property | Token | Light | Dark |\r\n| ------- | -------- | ----- | ----- | ---- |\r\n| Host (compositional) | border | `--xpl-border-small`, `--xpl-border-default` | neutral-200 | neutral-800 |\r\n| Host (compositional) | border-radius | `--xpl-border-radius-default` | 8px | 8px |\r\n| Host (compositional) | grid columns | `--column-widths` (runtime from header cells) | — | — |\r\n| Selection toolbar | sticky z-index | `--xpl-z-index-200` | 200 | 200 |\r\n| Selection toolbar | min-height | `--xpl-size-350` | 56px | 56px |\r\n| Freeze shadow | gradient | `--xpl-background-surface-transparent-10` → `-0` | near-black 10% | near-white 10% |\r\n| Freeze column | sticky z-index | `--xpl-z-index-100` | 100 | 100 |\r\n| Freeze scroll hint | width | `--xpl-space-24` | 24px | 24px |\r\n| Striped even rows | cell background | `--xpl-background-surface-subtlest` | neutral-50 | neutral-950 |\r\n| Striped + selected | cell background | `--xpl-background-surface-information-bold` | blue-100 | blue-900 |\r\n\r\n## Icon mapping\r\n\r\nWhen Apollo **injects** the reorder column, icons come from table props (override with `\"\"` to omit):\r\n\r\n| Context | Default `xpl-icon` `icon` value |\r\n| ------- | -------------------------------- |\r\n| Pointer / idle grip | `grip-dots-vertical` |\r\n| Keyboard reorder mode | `caret-expand-y` |\r\n\r\nSort direction uses **`xpl-icon`** names from header-cell internals (`graph`, carets, etc.) — see Storybook **Components → Table**.\r\n\r",
14414
+ "readme": "# xpl-table\r\n\r\nTables are used to organize and display information from a data set.\r\n\r\n## Rendering modes\r\n\r\n1. **Compositional (recommended)** — Provide `xpl-table-header`, `xpl-table-body`, and optional `xpl-table-footer` with `xpl-table-row` / `xpl-table-header-cell` / `xpl-table-cell` / `xpl-table-footer-cell`. The host uses CSS grid (`role=\"grid\"`). Column widths come from each **`xpl-table-header-cell`** (`width` in px, or `column-width` for any grid track; default `auto` / content-sized). Use `label` for an accessible name. **Sorting:** set `sortable` (and optional `sort-key`) on label header cells; `xpl-table` emits **`sortChanged`** with sort-key slugs → `asc` / `desc` / `null`. The host does **not** reorder row DOM — listen and update your data (or reorder nodes yourself), same as the main Table Storybook demo.\r\n2. **Legacy data-driven** — Set `columns`, `data`, and optionally `footer` for a built-in `<table>` implementation. That path supports `isSortable` / `sortableColumns`, `multiselect`, and emits `sortChanged` / `tableSelect` (legacy can reorder rows unless you `preventDefault`). Prefer compositional markup for new work.\r\n\r\nComposable **selection** (`selectable`), **toolbar**, and copy-paste examples live under **[Usage](#usage)** below (read the breaking-change note first if you are migrating from auto-injected selection cells).\r\n\r\n### Breaking change — global table class names (Apollo Core 3.0.0)\r\n\r\nApollo Core 3.0.0 introduced the compositional table ([#982](https://github.com/xplor/apollo/pull/982)) and ships global CSS for `.xpl-table`, `.xpl-table-row`, `.xpl-table-cell`, `.xpl-table-header-*`, `.xpl-table-body`, and `.xpl-table-footer-*`. In 2.x many of these class names had **no Apollo styles** (or only applied on native `<table>` via `.xpl-table`). These styles are **not scoped** to the `<xpl-table>` element — any element carrying these class names picks them up. If your application used these names as styling hooks on its own native table markup, upgrading to 3.x can break layout.\r\n\r\n| Symptom | Typical cause | Fix |\r\n| --- | --- | --- |\r\n| All table rows render side by side in one line | `xpl-table-row` class hook on native `<tr>` applies `display: contents` | Remove the hook class from native rows, or migrate to `<xpl-table-row>` inside `<xpl-table>` |\r\n| Native `<table class=\"xpl-table\">` layout is destroyed | Compositional grid targets the `.xpl-table` **class** (`display: grid` + `grid-template-columns`), so it also matches native `<table class=\"xpl-table\">` | Add `xpl-table--legacy`: `<table class=\"xpl-table xpl-table--legacy\">` |\r\n| Sticky freeze column lost on native tables | Compositional `.xpl-table--freeze` sticky rules target compositional cell classes (`.xpl-table-cell` etc.); the 2.x native-table freeze lives under `.xpl-table--legacy.xpl-table--freeze` | Add `xpl-table--legacy` to the native `<table>` |\r\n\r\n**`xpl-table--legacy`:** opt-in class for native `<table>` markup (or the deprecated `columns` / `data` render path) that restores pre-compositional Apollo table styling. Scheduled for removal in **Apollo Core 4.0.0** — migrate to compositional `<xpl-table>` or application-owned table styles before then.\r\n\r\n### Row reorder (`row-reorderable`, managed grip column)\r\n\r\nWhen `row-reorderable` is set, Apollo can **inject** a leading reorder column with a native **button** + optional `xpl-icon` on each body row (unless you author that column yourself). Keyboard and pointer reorder share the same grip; **keyboard mode** is indicated by `aria-pressed`, updated labels, the class `xpl-table__row-drag-handle--keyboard-mode`, and optionally a **different icon**.\r\n\r\n**Pointer drag (full row):** Body rows use CSS **`subgrid`** so `xpl-table-row` is a real grid box (like `xpl-list-item`’s row surface). When `CSS.supports('grid-template-columns','subgrid')` is true, **`draggable`** is set on **`xpl-table-row`** so the browser’s drag image matches the whole row; drag still **starts only from the grip** (pointer-down there sets the drag token—dragging from other cells is ignored). Engines without subgrid keep **`draggable`** on the grip button only.\r\n\r\n| Attribute | When it applies | Purpose |\r\n| --------- | ---------------- | ------- |\r\n| `reorder-handle-icon` | Default / drag affordance | Icon name for `xpl-icon` (default `grip-dots-vertical`). Use `reorder-handle-icon=\"\"` to omit the injected icon and use CSS or your own markup in a **custom** reorder cell instead. |\r\n| `reorder-handle-icon-keyboard` | While that row is in **keyboard reorder mode** (Enter or Space on the grip) | Icon name for the same grip when the user is using **Arrow Up/Down** to move the row (default `caret-expand-y`, matching `xpl-list`). Use `reorder-handle-icon-keyboard=\"\"` to show **no** icon in keyboard mode only. |\r\n\r\n**Recommended for product UIs:** Set **both** attributes to your design-system icon names so drag vs keyboard modes are visually distinct. You can still add styles targeting `.xpl-table__row-drag-handle--keyboard-mode` (e.g. outline) for extra clarity.\r\n\r\n**Interaction:** Focus the grip → **Enter** or **Space** → **Arrow Up/Down** to reorder → **Escape** to exit. Pointer down on the row exits keyboard mode; starting a drag also exits keyboard mode.\r\n\r\n**Custom reorder column:** If you add reorder cells in your own template (not injected), **you** own icons, keyboard behavior, and `rowOrderChange` — these attributes apply only to **managed** injected grips.\r\n\r\n## Usage\r\n\r\n### Breaking change — compositional `selectable`\r\n\r\n**Before:** Setting `selectable` alone caused the table to **inject** selection header/body/footer cells into the light DOM (including nodes marked `data-xpl-selection-managed`).\r\n\r\n**After:** You **must** add the selection column in your own markup (see **Selection** below). `xpl-table` no longer creates or removes those cells; it only syncs selection state and emits events. If you previously relied on automatic injection, add the header checkbox column, per-row checkbox cells, and (when using a footer) leading footer cells so column counts match.\r\n\r\n### Selection (`selectable`) and `rowSelectionChange`\r\n\r\nSet `selectable` on `xpl-table` and **author the selection column in your template** so the framework that renders the table owns every cell node.\r\n\r\n**Required pieces:**\r\n\r\n- **Header:** one [`xpl-table-header-cell`](xpl-table-header-cell) with `type=\"checkbox\"` (tri-state “select all”).\r\n- **Body:** in each `xpl-table-row`, a cell aligned with that column containing **`xpl-checkbox`** (value is driven from `row-id` on the row).\r\n- **Footer:** if you use `xpl-table-footer`, add a leading `xpl-table-footer-cell` per row for the selection column (often empty) so column counts stay aligned.\r\n\r\n**Behavior:** After `customElements.whenDefined('xpl-table')`, listen for **`rowSelectionChange`** on the **same** `<xpl-table>` element (`bubbles: false`, `composed: true`). The event payload is `{ selectedRowIds: string[] }`. The host maintains `selectedRowIds`, syncs `checked` / `indeterminate` on the header and row checkboxes, sets `selected` on rows, and shows the bulk toolbar while at least one row is selected. Row checkbox **`aria-label`** is derived from the first **non-selection** data cell in that row (e.g. `Select row, Jordan Lee`); the header uses “Select all rows” unless you set **`checkbox-aria-label`** on the checkbox header cell.\r\n\r\n**Example (web component):**\r\n\r\n```html\r\n<xpl-table id=\"roster\" selectable label=\"Team roster\">\r\n <xpl-table-header>\r\n <xpl-table-row>\r\n <xpl-table-header-cell type=\"checkbox\"></xpl-table-header-cell>\r\n <xpl-table-header-cell label=\"Name\" sortable sort-key=\"name\"></xpl-table-header-cell>\r\n </xpl-table-row>\r\n </xpl-table-header>\r\n <xpl-table-body>\r\n <xpl-table-row row-id=\"1\">\r\n <xpl-table-cell><xpl-checkbox></xpl-checkbox></xpl-table-cell>\r\n <xpl-table-cell>Ada</xpl-table-cell>\r\n </xpl-table-row>\r\n </xpl-table-body>\r\n</xpl-table>\r\n<script type=\"module\">\r\n await customElements.whenDefined('xpl-table');\r\n document.getElementById('roster')?.addEventListener('rowSelectionChange', (e) => {\r\n console.log(e.detail.selectedRowIds);\r\n });\r\n</script>\r\n```\r\n\r\n#### Framework notes (Vue / React / etc.)\r\n\r\nUse **`xpl-checkbox`** in the selection column so `xpl-table` can sync `checked`, `indeterminate`, and `value` from `row-id`. On **`xpl-table-header-cell`**, the selection header uses **`type=\"checkbox\"`**; the prop is **reflected** to the host attribute so Vue bindings like `:type=\"'checkbox'\"` still expose `type=\"checkbox\"` in the DOM for the table’s column detection and CSS.\r\n\r\n### Legacy (`columns` / `data`)\r\n\r\nSet `columns`, `data`, and optional `footer` as **properties** after `customElements.whenDefined('xpl-table')`, or use attributes where your bundler passes JSON strings.\r\n\r\n```html\r\n<xpl-table id=\"legacy\"></xpl-table>\r\n<script type=\"module\">\r\n await customElements.whenDefined('xpl-table');\r\n const t = document.getElementById('legacy');\r\n t.columns = ['Name', 'Score'];\r\n t.data = [\r\n ['Ada', '100'],\r\n ['Bob', '95'],\r\n ];\r\n t.multiselect = true;\r\n t.addEventListener('tableSelect', (e) => console.log(e.detail));\r\n</script>\r\n```\r\n\r\n## Accessibility\r\n\r\n| Concern | Behavior |\r\n| ------- | -------- |\r\n| Table landmark | Compositional host uses **`role=\"grid\"`**; set **`label`** (maps to `aria-label`). |\r\n| Columns | Header cells use **`role=\"columnheader\"`** with `aria-colindex`; sortable label headers expose sort state via **`aria-sort`**. |\r\n| Selection | Checkbox column: header reflects **checked / mixed** via `aria-checked`; row checkboxes get **`aria-label`** from the first non-selection cell’s text (or override with `checkbox-aria-label` on the checkbox header cell). |\r\n| Sort | Sorting is a **`<button type=\"button\">`** inside the header cell; focus uses **`:focus-visible`** with focus tokens in `table-header-cell.css`. |\r\n| Row reorder | Managed grip is a **`<button>`** with `aria-label` / `aria-pressed` for keyboard reorder mode; live region announces reorder when enabled. |\r\n\r\nKeyboard: **Tab** through sort buttons, checkboxes, reorder grips, and cell actions. **Enter** / **Space** activate sort and the reorder grip per native button semantics.\r\n\r\n## Design Tokens\r\n\r\nCompositional grid layout tokens live in `table.css`; cell, row, header, and toolbar tokens are split across sibling CSS files.\r\n\r\n| Element | Property | Token | Light | Dark |\r\n| ------- | -------- | ----- | ----- | ---- |\r\n| Host (compositional) | border | `--xpl-border-small`, `--xpl-border-default` | neutral-200 | neutral-800 |\r\n| Host (compositional) | border-radius | `--xpl-border-radius-default` | 8px | 8px |\r\n| Host (compositional) | grid columns | `--column-widths` (runtime from header cells) | — | — |\r\n| Selection toolbar | sticky z-index | `--xpl-z-index-200` | 200 | 200 |\r\n| Selection toolbar | min-height | `--xpl-size-350` | 56px | 56px |\r\n| Freeze shadow | gradient | `--xpl-background-surface-transparent-10` → `-0` | near-black 10% | near-white 10% |\r\n| Freeze column | sticky z-index | `--xpl-z-index-100` | 100 | 100 |\r\n| Freeze scroll hint | width | `--xpl-space-24` | 24px | 24px |\r\n| Striped even rows | cell background | `--xpl-background-surface-subtlest` | neutral-50 | neutral-950 |\r\n| Striped + selected | cell background | `--xpl-background-surface-information-bold` | blue-100 | blue-900 |\r\n\r\n## Icon mapping\r\n\r\nWhen Apollo **injects** the reorder column, icons come from table props (override with `\"\"` to omit):\r\n\r\n| Context | Default `xpl-icon` `icon` value |\r\n| ------- | -------------------------------- |\r\n| Pointer / idle grip | `grip-dots-vertical` |\r\n| Keyboard reorder mode | `caret-expand-y` |\r\n\r\nSort direction uses **`xpl-icon`** names from header-cell internals (`graph`, carets, etc.) — see Storybook **Components → Table**.\r\n\r",
13888
14415
  "docs": "Compositional tables use CSS grid on the host (`role=\"grid\"`). Legacy mode uses a `<table>`.",
13889
14416
  "docsTags": [
13890
14417
  {
@@ -14463,14 +14990,24 @@
14463
14990
  "tag": "xpl-table-body",
14464
14991
  "readme": "# xpl-table-body\n\nRow group for compositional table body content. Uses `display: contents` so slotted rows participate in the parent grid layout.\n\n## Design Tokens\n\n| Element | Property | Token | Light | Dark |\n| ------- | -------- | ----- | ----- | ---- |\n| Host | display | — | `contents` | `contents` |\n",
14465
14992
  "docs": "Row group for compositional table body content. Uses `display: contents` so slotted rows participate in the parent grid layout.",
14466
- "docsTags": [],
14993
+ "docsTags": [
14994
+ {
14995
+ "name": "slot",
14996
+ "text": "- The body rows (`xpl-table-row` elements)."
14997
+ }
14998
+ ],
14467
14999
  "usage": {},
14468
15000
  "props": [],
14469
15001
  "methods": [],
14470
15002
  "events": [],
14471
15003
  "listeners": [],
14472
15004
  "styles": [],
14473
- "slots": [],
15005
+ "slots": [
15006
+ {
15007
+ "name": "",
15008
+ "docs": "The body rows (`xpl-table-row` elements)."
15009
+ }
15010
+ ],
14474
15011
  "parts": [],
14475
15012
  "states": [],
14476
15013
  "dependents": [],
@@ -14483,7 +15020,12 @@
14483
15020
  "tag": "xpl-table-cell",
14484
15021
  "readme": "# xpl-table-cell\n\nBody cell for compositional [`xpl-table`](../readme.md). Place content in the default slot; use `align`, `bold`, and `underline` for typography. Interactive controls inside the cell inherit `disabled` when the parent [`xpl-table-row`](../xpl-table-row) is disabled.\n\n## Design Tokens\n\nExtends `.xpl-table-cell-base` (`table-cell-base.css`); row states (selected, disabled, striped) are applied from `table-row.css`.\n\n| Element | Property | Token | Light | Dark |\n| ------- | -------- | ----- | ----- | ---- |\n| Cell | background | `--xpl-background-surface-default` | neutral-0 | neutral-1000 |\n| Cell | text | `--xpl-text-default` | neutral-900 | neutral-50 |\n| Cell | border (right/bottom) | `--xpl-border-small`, `--xpl-border-default` | 1px, neutral-200 | 1px, neutral-800 |\n| Cell | padding | `--xpl-space-10`, `--xpl-space-16` | 10px / 16px | 10px / 16px |\n| Cell | min-height | `--xpl-size-350` | 56px | 56px |\n| Cell | typography | `@extend .xpl-text-title-5` | title-5 / medium | title-5 / medium |\n| Bold text | font-weight | `--xpl-font-weight-semibold` | 600 | 600 |\n| Disabled | text | `--xpl-text-disabled` | neutral-400 | neutral-600 |\n| Caption slot | text | `--xpl-text-subdued` | neutral-600 | neutral-400 |\n| Icon | color | `--xpl-icon-default` | neutral-900 | neutral-50 |\n| Reorder grip | color | `--xpl-icon-subtle` | neutral-600 | neutral-400 |\n| Reorder grip focus | outline | `--xpl-border-focus` | purple-500 | purple-400 |\n| Checkbox-only column | padding | `--xpl-space-16` + `--xpl-space-4` | 20px / 16px | 20px / 16px |\n",
14485
15022
  "docs": "Body cell for compositional [`xpl-table`](../readme.md). Place content in the default slot; use `align`, `bold`, and `underline` for typography. Interactive controls inside the cell inherit `disabled` when the parent [`xpl-table-row`](../xpl-table-row) is disabled.",
14486
- "docsTags": [],
15023
+ "docsTags": [
15024
+ {
15025
+ "name": "slot",
15026
+ "text": "- The cell content."
15027
+ }
15028
+ ],
14487
15029
  "usage": {},
14488
15030
  "props": [
14489
15031
  {
@@ -14616,7 +15158,12 @@
14616
15158
  "events": [],
14617
15159
  "listeners": [],
14618
15160
  "styles": [],
14619
- "slots": [],
15161
+ "slots": [
15162
+ {
15163
+ "name": "",
15164
+ "docs": "The cell content."
15165
+ }
15166
+ ],
14620
15167
  "parts": [],
14621
15168
  "states": [],
14622
15169
  "dependents": [
@@ -14635,14 +15182,24 @@
14635
15182
  "tag": "xpl-table-footer",
14636
15183
  "readme": "# xpl-table-footer\n\nRow group for compositional table footer content. Uses `display: contents` so slotted footer rows participate in the parent grid layout.\n\n## Design Tokens\n\n| Element | Property | Token | Light | Dark |\n| ------- | -------- | ----- | ----- | ---- |\n| Host | display | — | `contents` | `contents` |\n",
14637
15184
  "docs": "Row group for compositional table footer content. Uses `display: contents` so slotted footer rows participate in the parent grid layout.",
14638
- "docsTags": [],
15185
+ "docsTags": [
15186
+ {
15187
+ "name": "slot",
15188
+ "text": "- The footer content (`xpl-table-footer-cell` elements)."
15189
+ }
15190
+ ],
14639
15191
  "usage": {},
14640
15192
  "props": [],
14641
15193
  "methods": [],
14642
15194
  "events": [],
14643
15195
  "listeners": [],
14644
15196
  "styles": [],
14645
- "slots": [],
15197
+ "slots": [
15198
+ {
15199
+ "name": "",
15200
+ "docs": "The footer content (`xpl-table-footer-cell` elements)."
15201
+ }
15202
+ ],
14646
15203
  "parts": [],
14647
15204
  "states": [],
14648
15205
  "dependents": [],
@@ -14655,7 +15212,12 @@
14655
15212
  "tag": "xpl-table-footer-cell",
14656
15213
  "readme": "# xpl-table-footer-cell\n\nFooter cell for compositional tables. Extends the shared `.xpl-table-cell-base` styles with footer-row border rules from `table.css`.\n\n## Design Tokens\n\n| Element | Property | Token | Light | Dark |\n| ------- | -------- | ----- | ----- | ---- |\n| Cell | extends | `.xpl-table-cell-base` | (shared compositional cell base) | (shared compositional cell base) |\n| Cell | background | `--xpl-background-surface-default` | neutral-0 | neutral-1000 |\n| Cell | text | `--xpl-text-default` | neutral-900 | neutral-50 |\n| Cell | border (right/bottom) | `--xpl-border-small`, `--xpl-border-default` | 1px, neutral-200 | 1px, neutral-800 |\n| Last footer row | border-bottom | none (host draws frame) | — | — |\n",
14657
15214
  "docs": "Footer cell for compositional tables. Extends the shared `.xpl-table-cell-base` styles with footer-row border rules from `table.css`.",
14658
- "docsTags": [],
15215
+ "docsTags": [
15216
+ {
15217
+ "name": "slot",
15218
+ "text": "- The footer cell content."
15219
+ }
15220
+ ],
14659
15221
  "usage": {},
14660
15222
  "props": [
14661
15223
  {
@@ -14701,7 +15263,12 @@
14701
15263
  "events": [],
14702
15264
  "listeners": [],
14703
15265
  "styles": [],
14704
- "slots": [],
15266
+ "slots": [
15267
+ {
15268
+ "name": "",
15269
+ "docs": "The footer cell content."
15270
+ }
15271
+ ],
14705
15272
  "parts": [],
14706
15273
  "states": [],
14707
15274
  "dependents": [
@@ -14721,6 +15288,10 @@
14721
15288
  "readme": "# xpl-table-header\n\nComponent parts used to assemble the Dynamic Table.\n\n> **Note:** This component is currently in beta. Use with caution as it may have breaking changes in future releases.\n\n",
14722
15289
  "docs": "Component parts used to assemble the Dynamic Table.\n\n> **Note:** This component is currently in beta. Use with caution as it may have breaking changes in future releases.",
14723
15290
  "docsTags": [
15291
+ {
15292
+ "name": "slot",
15293
+ "text": "- The header content (`xpl-table-header-cell` elements)."
15294
+ },
14724
15295
  {
14725
15296
  "name": "since",
14726
15297
  "text": "1.0.0"
@@ -14732,7 +15303,12 @@
14732
15303
  "events": [],
14733
15304
  "listeners": [],
14734
15305
  "styles": [],
14735
- "slots": [],
15306
+ "slots": [
15307
+ {
15308
+ "name": "",
15309
+ "docs": "The header content (`xpl-table-header-cell` elements)."
15310
+ }
15311
+ ],
14736
15312
  "parts": [],
14737
15313
  "states": [],
14738
15314
  "dependents": [],
@@ -14745,7 +15321,12 @@
14745
15321
  "tag": "xpl-table-header-cell",
14746
15322
  "readme": "# xpl-table-header-cell\r\n\r\n> **Note:** This component is currently in beta. Use with caution as it may have breaking changes in future releases.\r\n\r\nThe `icon` property accepts icon names as strings. See [xpl-icon available icons](../xpl-icon/ICONS.md) for the complete list.\r\n\r\nWhen this cell is used inside a compositional [`xpl-table`](../xpl-table) with `selectable`, **you** add a `type=\"checkbox\"` header cell for select-all; `xpl-table` keeps `checked` and `indeterminate` in sync with row selection.\r\n\r\n**Frameworks (Vue / etc.):** `type` is **reflected** to the DOM. You may bind with `:type=\"'checkbox'\"` or `type=\"checkbox\"`; the host will still expose `type=\"checkbox\"` as an attribute so the table’s selection column logic and styles stay correct.\r\n\r\nFor **sortable** columns (`type=\"label\"`), set **`sortable`** and optionally **`sort-key`**; the parent table emits **`sortChanged`** (see [`xpl-table` events](../xpl-table#events)).\r\n\r\n**Column width (compositional grid):** Each header cell defines one grid column on the parent `xpl-table`. By default the track is **`auto`** (sizes from content). Set **`width`** for a fixed pixel width (minimum 48px), or **`column-width`** for any CSS grid track string (`1fr`, `minmax(120px, 1fr)`, etc.). When both are set, **`column-width` wins**.\r\n\r\n## Design Tokens\r\n\r\n| Element | Property | Token | Light | Dark |\r\n| ------- | -------- | ----- | ----- | ---- |\r\n| Cell | background | `--xpl-background-surface-subtlest` | neutral-50 | neutral-950 |\r\n| Cell | text | `--xpl-text-subdued` | neutral-600 | neutral-400 |\r\n| Cell | border (right/bottom) | `--xpl-border-small`, `--xpl-border-default` | 1px, neutral-200 | 1px, neutral-800 |\r\n| Cell | min-width | `--xpl-size-300` | 48px | 48px |\r\n| Cell | min-height | `--xpl-size-350` | 56px | 56px |\r\n| Cell | typography | `@extend .xpl-text-title-5` | title-5 / medium | title-5 / medium |\r\n| Sort button | padding | `--xpl-space-12`, `--xpl-space-16` | 12px / 16px | 12px / 16px |\r\n| Sort button focus | outline | `--xpl-border-focus` | purple-500 | purple-400 |\r\n| Sort icon (default) | color | `--xpl-text-subdued` | neutral-600 | neutral-400 |\r\n| Sort icon (active) | color | `--xpl-icon-information` | blue-500 | blue-400 |\r\n| Checkbox column | padding | `--xpl-space-16` + `--xpl-space-4` | 20px / 16px | 20px / 16px |\r\n\r\n---\r\n\r",
14747
15323
  "docs": "> **Note:** This component is currently in beta. Use with caution as it may have breaking changes in future releases.\r\n\r\nThe `icon` property accepts icon names as strings. See [xpl-icon available icons](../xpl-icon/ICONS.md) for the complete list.\r\n\r\nWhen this cell is used inside a compositional [`xpl-table`](../xpl-table) with `selectable`, **you** add a `type=\"checkbox\"` header cell for select-all; `xpl-table` keeps `checked` and `indeterminate` in sync with row selection.\r\n\r\n**Frameworks (Vue / etc.):** `type` is **reflected** to the DOM. You may bind with `:type=\"'checkbox'\"` or `type=\"checkbox\"`; the host will still expose `type=\"checkbox\"` as an attribute so the table’s selection column logic and styles stay correct.\r\n\r\nFor **sortable** columns (`type=\"label\"`), set **`sortable`** and optionally **`sort-key`**; the parent table emits **`sortChanged`** (see [`xpl-table` events](../xpl-table#events)).\r\n\r\n**Column width (compositional grid):** Each header cell defines one grid column on the parent `xpl-table`. By default the track is **`auto`** (sizes from content). Set **`width`** for a fixed pixel width (minimum 48px), or **`column-width`** for any CSS grid track string (`1fr`, `minmax(120px, 1fr)`, etc.). When both are set, **`column-width` wins**.",
14748
- "docsTags": [],
15324
+ "docsTags": [
15325
+ {
15326
+ "name": "slot",
15327
+ "text": "- The header cell content (column label)."
15328
+ }
15329
+ ],
14749
15330
  "usage": {},
14750
15331
  "props": [
14751
15332
  {
@@ -15127,7 +15708,12 @@
15127
15708
  ],
15128
15709
  "listeners": [],
15129
15710
  "styles": [],
15130
- "slots": [],
15711
+ "slots": [
15712
+ {
15713
+ "name": "",
15714
+ "docs": "The header cell content (column label)."
15715
+ }
15716
+ ],
15131
15717
  "parts": [],
15132
15718
  "states": [],
15133
15719
  "dependents": [
@@ -15153,7 +15739,12 @@
15153
15739
  "tag": "xpl-table-row",
15154
15740
  "readme": "# xpl-table-row\n\nData row for compositional tables. Uses `display: contents` by default so cells align to the parent grid; when the body supports subgrid, the row becomes a full-width subgrid for drag interactions.\n\n## Design Tokens\n\nRow state styles target descendant `.xpl-table-cell` nodes (see `table-row.css`).\n\n| Element | Property | Token | Light | Dark |\n| ------- | -------- | ----- | ----- | ---- |\n| Host (default) | display | — | `contents` | `contents` |\n| Host (subgrid body) | display | — | `grid` with `subgrid` | `grid` with `subgrid` |\n| Disabled row | background | `--xpl-background-surface-subtlest` | neutral-50 | neutral-950 |\n| Disabled row | text | `--xpl-text-disabled` | neutral-400 | neutral-600 |\n| Dragging row | background | `--xpl-background-surface-subtle` | neutral-100 | neutral-900 |\n| Dragging row | opacity | `--xpl-opacity-75` | 0.75 | 0.75 |\n| Selected row | background | `--xpl-background-surface-information` | blue-50 | blue-950 |\n| Selected grip (reorder) | icon color | `--xpl-icon-information` | blue-500 | blue-400 |\n| Drag-over outline | inset ring | `--xpl-border-focus`, `--xpl-border-small` | purple-500 | purple-400 |\n| Striped even | background | `--xpl-background-surface-subtlest` | neutral-50 | neutral-950 |\n| Striped selected | background | `--xpl-background-surface-information-bold` | blue-100 | blue-900 |\n| Keyboard reorder grip | outline | `--xpl-border-focus` | purple-500 | purple-400 |\n",
15155
15741
  "docs": "Data row for compositional tables. Uses `display: contents` by default so cells align to the parent grid; when the body supports subgrid, the row becomes a full-width subgrid for drag interactions.",
15156
- "docsTags": [],
15742
+ "docsTags": [
15743
+ {
15744
+ "name": "slot",
15745
+ "text": "- The row cells (`xpl-table-cell` elements)."
15746
+ }
15747
+ ],
15157
15748
  "usage": {},
15158
15749
  "props": [
15159
15750
  {
@@ -15242,7 +15833,12 @@
15242
15833
  "events": [],
15243
15834
  "listeners": [],
15244
15835
  "styles": [],
15245
- "slots": [],
15836
+ "slots": [
15837
+ {
15838
+ "name": "",
15839
+ "docs": "The row cells (`xpl-table-cell` elements)."
15840
+ }
15841
+ ],
15246
15842
  "parts": [],
15247
15843
  "states": [],
15248
15844
  "dependents": [],
@@ -15459,9 +16055,13 @@
15459
16055
  "filePath": "src/components/components/xpl-tag/xpl-tag.tsx",
15460
16056
  "encapsulation": "none",
15461
16057
  "tag": "xpl-tag",
15462
- "readme": "# xpl-tag\n\nTags represent a set of interactive keywords that help label, organize, and categorize objects. Tags can be added or removed from an object by the user. Unlike Badges, Tags are clickable, used in dropdowns, multi-select, search bars, etc.\n\n## Usage Examples\n\n### Basic tag with dismiss\n\n```html\n<xpl-tag>Label</xpl-tag>\n```\n\n### Small size variant\n\n```html\n<xpl-tag size=\"sm\">Small Tag</xpl-tag>\n```\n\n### Non-dismissible tag (no close button)\n\n```html\n<xpl-tag dismissible=\"false\">Static Tag</xpl-tag>\n```\n\n### Disabled tag\n\n```html\n<xpl-tag disabled>Disabled Tag</xpl-tag>\n```\n\n### Handling events\n\n> **⚠️ Deprecation Notice:** The `close` event is **deprecated** and will be removed in a future major version. Use `tagRemove` for dismiss button clicks only.\n\n#### Why This Change?\n\nThe `close` event name conflicts with a **native HTML event** used by `<dialog>` elements. The React wrapper's `isCoveredByReact()` function detects `onclose` as a native event and passes handlers to React's synthetic event system — but React doesn't actually handle `close` events this way, so handlers silently fail in React applications.\n\n**Why `tagRemove` works:** `ontagremove` is not a native event, so the wrapper correctly falls back to `addEventListener`.\n\n#### Event Behavior\n\n| Interaction | `close` (deprecated) | `tagRemove` (use this) |\n|-------------|------------------------|------------------------|\n| Dismiss (×) button clicked | ✅ Fires | ✅ Fires |\n| Content clicked (non-draggable) | ✅ Fires | ❌ Does NOT fire |\n| Content clicked (draggable) | ❌ Does NOT fire | ❌ Does NOT fire |\n| Keyboard Space/Enter on content (non-draggable) | ✅ Fires | ❌ Does NOT fire |\n| Keyboard Space/Enter on draggable label | ❌ Does NOT fire | ❌ Does NOT fire |\n\n#### Migration Examples\n\n**Before (deprecated):**\n```tsx\n// React — this may silently fail!\n<XplTag onClose={() => tag.remove()}>Label</XplTag>\n\n// Vanilla JS\nconst tag = document.querySelector('xpl-tag');\ntag.addEventListener('close', () => tag.remove());\n```\n\n**After (recommended):**\n```tsx\n// React — works reliably\n<XplTag onTagRemove={() => tag.remove()}>Label</XplTag>\n\n// Vanilla JS — same pattern, clearer intent\nconst tag = document.querySelector('xpl-tag');\ntag.addEventListener('tagRemove', () => tag.remove());\n```\n\n**React with Refs (if you must use deprecated `close`):**\n```tsx\nimport { useEffect, useRef } from 'react';\nimport { XplTag } from '@xplortech/apollo-react';\n\nfunction MyComponent() {\n const tagRef = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n const el = tagRef.current;\n if (!el) return;\n\n const handleClose = () => el.remove();\n el.addEventListener('close', handleClose);\n return () => el.removeEventListener('close', handleClose);\n }, []);\n\n return <XplTag ref={tagRef}>Label</XplTag>;\n}\n```\n\n## Drag and drop\n\nTags support **native HTML5 drag and drop**. Set the boolean prop **`isDraggable`** (HTML attribute **`is-draggable`**) to enable dragging. Optionally set **`tag-id`** so `dataTransfer` and `tagDragStart` / `tagDragEnd` detail carry a stable id.\n\n### How it works (important for handlers)\n\n- The **drag source** is an inner **label** element (a `div` with `role=\"button\"`), not the `<xpl-tag>` host. Browsers do not start drags reliably on the custom element host; the **`is-draggable`** prop does **not** mirror the native host **`draggable`** attribute (see the `isDraggable` prop docs below).\n- On **`dragstart`**, the component uses **`setDragImage`** so the floating preview (“ghost”) looks like the **whole tag** (label + dismiss control), not only the label strip.\n- **Start the drag from the label text**, not from the dismiss (×) button.\n- In container listeners, **`dragstart`** may bubble with `event.target` equal to that inner node. Resolve the tag with:\n\n```js\nconst tag = event.target instanceof Element ? event.target.closest('xpl-tag') : null;\n```\n\n### Making a tag draggable\n\n```html\n<xpl-tag is-draggable>Draggable tag</xpl-tag>\n\n<xpl-tag is-draggable tag-id=\"my-tag-1\">Design</xpl-tag>\n```\n\n### Reordering tags within a container\n\n```html\n<div id=\"tag-container\" style=\"display: flex; flex-wrap: wrap; gap: 8px\">\n <xpl-tag tag-id=\"t1\" is-draggable>Design</xpl-tag>\n <xpl-tag tag-id=\"t2\" is-draggable>Development</xpl-tag>\n <xpl-tag tag-id=\"t3\" is-draggable>QA</xpl-tag>\n</div>\n\n<script>\n const container = document.getElementById('tag-container');\n let draggedEl = null;\n\n container.addEventListener('dragstart', (e) => {\n const tag = e.target instanceof Element ? e.target.closest('xpl-tag') : null;\n if (tag) draggedEl = tag;\n });\n\n container.addEventListener('dragend', () => {\n draggedEl = null;\n });\n\n container.addEventListener('dragover', (e) => {\n e.preventDefault();\n if (!draggedEl) return;\n const afterEl = getDragAfterElement(container, e.clientX);\n if (afterEl == null) container.appendChild(draggedEl);\n else container.insertBefore(draggedEl, afterEl);\n });\n\n function getDragAfterElement(container, x) {\n const tags = [...container.querySelectorAll('xpl-tag')].filter((el) => el !== draggedEl);\n return tags.reduce(\n (closest, child) => {\n const box = child.getBoundingClientRect();\n const offset = x - box.left - box.width / 2;\n return offset < 0 && offset > closest.offset ? { offset, element: child } : closest;\n },\n { offset: Number.NEGATIVE_INFINITY },\n ).element;\n }\n</script>\n```\n\n### Dragging tags between containers\n\n```html\n<div\n class=\"drop-zone\"\n style=\"display: flex; gap: 8px; padding: 16px; border: 2px dashed var(--xpl-border-default); border-radius: 8px\"\n>\n <xpl-tag tag-id=\"a1\" is-draggable>Frontend</xpl-tag>\n <xpl-tag tag-id=\"a2\" is-draggable>Backend</xpl-tag>\n</div>\n\n<div\n class=\"drop-zone\"\n style=\"\n display: flex;\n gap: 8px;\n padding: 16px;\n border: 2px dashed var(--xpl-border-default);\n border-radius: 8px;\n margin-top: 8px;\n \"\n>\n <xpl-tag tag-id=\"b1\" is-draggable>DevOps</xpl-tag>\n</div>\n\n<script>\n document.querySelectorAll('.drop-zone').forEach((zone) => {\n let draggedEl = null;\n\n zone.addEventListener('dragstart', (e) => {\n const tag = e.target instanceof Element ? e.target.closest('xpl-tag') : null;\n if (tag) draggedEl = tag;\n });\n\n zone.addEventListener('dragend', () => {\n draggedEl = null;\n });\n\n zone.addEventListener('dragover', (e) => e.preventDefault());\n\n zone.addEventListener('drop', (e) => {\n e.preventDefault();\n if (draggedEl && draggedEl.parentNode !== zone) zone.appendChild(draggedEl);\n });\n });\n</script>\n```\n\n### `tagDragStart` / `tagDragEnd` events\n\nThe component emits **`tagDragStart`** and **`tagDragEnd`** (Stencil camelCase). They bubble and are composed.\n\n```html\n<xpl-tag is-draggable tag-id=\"my-tag\">Drag me</xpl-tag>\n\n<script>\n const tag = document.querySelector('xpl-tag[tag-id=\"my-tag\"]');\n\n tag.addEventListener('tagDragStart', (e) => {\n const { tagId, content, originalEvent } = e.detail;\n });\n\n tag.addEventListener('tagDragEnd', (e) => {\n const { dropped, tagId, originalEvent } = e.detail;\n if (dropped) {\n /* dropEffect was 'move' */\n }\n });\n</script>\n```\n\n### React (`@xplortech/apollo-react`)\n\nEnsure the app loads Apollo **defineCustomElements** (see the main React package docs). Import **`XplTag`** and use **`isDraggable`**, **`tagId`**, and children for the label.\n\n`HTMLXplTagElement` is provided by Apollo’s Stencil typings (the same setup that types `defineCustomElements`). If your editor cannot resolve it, use `useRef<HTMLElement | null>(null)` and attach listeners on `ref.current`.\n\n`XplTag` is a thin wrapper around the web component. Custom events use the same names as in the DOM (`tagDragStart`, `tagDragEnd`, `tagRemove`). If your TypeScript definitions use different prop names for listeners, attach them with a **ref** (always works):\n\n```tsx\nimport { useEffect, useRef } from 'react';\nimport { XplTag } from '@xplortech/apollo-react';\n\nfunction TagBoard() {\n const tagRef = useRef<HTMLXplTagElement | null>(null);\n\n useEffect(() => {\n const el = tagRef.current;\n if (!el) return;\n\n const onDragStart = (e: CustomEvent) => {\n const { tagId, content } = e.detail;\n };\n const onDragEnd = (e: CustomEvent) => {\n const { dropped, tagId } = e.detail;\n };\n const onTagRemove = () => {\n el.remove(); // Handle dismiss button click\n };\n\n el.addEventListener('tagDragStart', onDragStart as EventListener);\n el.addEventListener('tagDragEnd', onDragEnd as EventListener);\n el.addEventListener('tagRemove', onTagRemove);\n return () => {\n el.removeEventListener('tagDragStart', onDragStart as EventListener);\n el.removeEventListener('tagDragEnd', onDragEnd as EventListener);\n el.removeEventListener('tagRemove', onTagRemove);\n };\n }, []);\n\n return (\n <XplTag ref={tagRef} isDraggable tagId=\"react-1\">\n Movable\n </XplTag>\n );\n}\n```\n\nReorder / multi-container logic is unchanged: listen for native `dragstart`, `dragover`, `drop` on a React parent `div` (use refs) and call `closest('xpl-tag')` as in the HTML examples.\n\n### `DataTransfer` payload\n\n| MIME type | Value | Purpose |\n|-----------|-------|---------|\n| `application/x-tag-id` | `tag-id` prop, if set | Identify the tag in drop handlers |\n| `text/plain` | Trimmed text content of the tag | Fallback for external targets |\n\n`effectAllowed` is set to `move`.\n\n**`originalEvent` in `tagDragStart` / `tagDragEnd`:** the native `DragEvent` is included for convenience, but browsers may clear `dataTransfer` after the synchronous handler completes. Use `tagId`, `content`, and `dropped` from the custom event detail for anything needed after the current turn; only read `originalEvent.dataTransfer` synchronously in the listener.\n\n### Disabled tags\n\n`disabled` wins: the tag is not draggable and keep using a normal `<button>` label (no HTML5 drag on the host).\n\n```html\n<xpl-tag is-draggable disabled>Cannot drag</xpl-tag>\n```\n\n## Design Tokens\n\n| Property | Token |\n|----------|-------|\n| Border (default) | `var(--xpl-border-accent-information)` |\n| Border (disabled) | `var(--xpl-border-accent-subtle)` |\n| Background (default) | `var(--xpl-background-surface-default)` |\n| Background (hover) | `var(--xpl-background-surface-information-bold)` |\n| Background (disabled) | `var(--xpl-background-surface-subtle)` |\n| Text | `var(--xpl-text-strong)` |\n| Text (disabled) | `var(--xpl-text-disabled)` |\n| Dismiss icon | `var(--xpl-icon-default)` |\n| Dismiss icon (disabled) | `var(--xpl-icon-disabled)` |\n| Spacing (default) | `var(--xpl-space-2)` vertical, `var(--xpl-space-12)` horizontal |\n| Spacing (small) | `var(--xpl-space-2)` vertical, `var(--xpl-space-8)` horizontal |\n| Height (default) | `var(--xpl-size-150)` (24px) |\n| Height (small) | `var(--xpl-size-125)` (20px) |\n| Border radius | `var(--xpl-border-radius-button)` |\n| Typography (default) | `var(--xpl-font-size-body)` / `var(--xpl-font-weight-normal)` |\n| Typography (small) | `var(--xpl-font-size-callout)` / `var(--xpl-font-weight-semibold)` |\n| Background (dragging) | `var(--xpl-background-surface-information-bold)` |\n| Cursor (draggable) | `cursor: grab` → `cursor: grabbing` while dragging |\n\n## Accessibility\n\n### Keyboard Navigation\n\n| Key | Action |\n|-----|--------|\n| Tab | Focus tag content or dismiss button |\n| Enter/Space | Activate the focused control: on **dismiss**, emits deprecated `close` and `tagRemove`; on **content**, emits deprecated `close` only when the tag is **not** in drag mode (native `<button>` behavior). |\n\n### Drag mode\n\n- With `isDraggable`, focus moves to the **label** (`role=\"button\"`). Enter/Space on the label does **not** dismiss the tag (same as click on draggable content).\n- Start drags from the **label**; the dismiss control remains a separate focus stop and is not the drag handle.\n\n### ARIA Attributes\n\n- Dismiss button sets `aria-label` to `Dismiss` plus the host element’s `textContent` (trimmed), or the word `tag` when empty, so multiple tags stay distinguishable in screen readers\n- When `disabled={true}`, buttons have `disabled` attribute preventing interaction\n- When `isDraggable` is set, **`aria-grabbed`** on the **label** reflects drag state (`\"false\"` at rest, `\"true\"` while dragging)\n\n### Icon Mapping\n\n| Element | Icon |\n|---------|------|\n| Dismiss button | `xmark` (size 10) |\n\n## Backward Compatibility (CSS class aliases)\n\nThe refactored component preserves legacy CSS classes so existing consumer selectors keep working:\n\n| Legacy class | Element | Replaces / alias for |\n|---|---|---|\n| `xpl-tag-container` | Host (`<xpl-tag>`) | Old root class — now an alias; styles come from `.xpl-tag` |\n| `xpl-tag` | Content `<button>` / `<div>` | Old content selector (`button.xpl-tag`) — kept so `.xpl-tag-container .xpl-tag` selectors match |\n| `xpl-tag__close` | Dismiss `<button>` | Old dismiss selector — alias for `.xpl-tag__dismiss` |\n\nThese aliases will be removed in a future major version. New code should target `.xpl-tag`, `.xpl-tag__content`, and `.xpl-tag__dismiss`.\n",
16058
+ "readme": "# xpl-tag\n\nTags represent a set of interactive keywords that help label, organize, and categorize objects. Tags can be added or removed from an object by the user. Unlike Badges, Tags are clickable, used in dropdowns, multi-select, search bars, etc.\n\n## Usage Examples\n\n### Basic tag with dismiss\n\n```html\n<xpl-tag>Label</xpl-tag>\n```\n\n### Small size variant\n\n```html\n<xpl-tag size=\"sm\">Small Tag</xpl-tag>\n```\n\n### Non-dismissible tag (no close button)\n\n```html\n<xpl-tag dismissible=\"false\">Static Tag</xpl-tag>\n```\n\n### Disabled tag\n\n```html\n<xpl-tag disabled>Disabled Tag</xpl-tag>\n```\n\n### Handling events\n\n> **⚠️ Deprecation Notice:** The `close` event is **deprecated** and will be removed in a future major version. Use `tagRemove` for dismiss button clicks only.\n\n#### Why This Change?\n\nThe `close` event name conflicts with a **native HTML event** used by `<dialog>` elements. The React wrapper's `isCoveredByReact()` function detects `onclose` as a native event and passes handlers to React's synthetic event system — but React doesn't actually handle `close` events this way, so handlers silently fail in React applications.\n\n**Why `tagRemove` works:** `ontagremove` is not a native event, so the wrapper correctly falls back to `addEventListener`.\n\n#### Event Behavior\n\n| Interaction | `close` (deprecated) | `tagRemove` (use this) |\n|-------------|------------------------|------------------------|\n| Dismiss (×) button clicked | ✅ Fires | ✅ Fires |\n| Content clicked (non-draggable) | ✅ Fires | ❌ Does NOT fire |\n| Content clicked (draggable) | ❌ Does NOT fire | ❌ Does NOT fire |\n| Keyboard Space/Enter on content (non-draggable) | ✅ Fires | ❌ Does NOT fire |\n| Keyboard Space/Enter on draggable label | ❌ Does NOT fire | ❌ Does NOT fire |\n\n#### Migration Examples\n\n**Before (deprecated):**\n```tsx\n// React — this may silently fail!\n<XplTag onClose={() => tag.remove()}>Label</XplTag>\n\n// Vanilla JS\nconst tag = document.querySelector('xpl-tag');\ntag.addEventListener('close', () => tag.remove());\n```\n\n**After (recommended):**\n```tsx\n// React — works reliably\n<XplTag onTagRemove={() => tag.remove()}>Label</XplTag>\n\n// Vanilla JS — same pattern, clearer intent\nconst tag = document.querySelector('xpl-tag');\ntag.addEventListener('tagRemove', () => tag.remove());\n```\n\n**React with Refs (if you must use deprecated `close`):**\n```tsx\nimport { useEffect, useRef } from 'react';\nimport { XplTag } from '@xplortech/apollo-react';\n\nfunction MyComponent() {\n const tagRef = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n const el = tagRef.current;\n if (!el) return;\n\n const handleClose = () => el.remove();\n el.addEventListener('close', handleClose);\n return () => el.removeEventListener('close', handleClose);\n }, []);\n\n return <XplTag ref={tagRef}>Label</XplTag>;\n}\n```\n\n## Drag and drop\n\nTags support **native HTML5 drag and drop**. Set the boolean prop **`isDraggable`** (HTML attribute **`is-draggable`**) to enable dragging. Optionally set **`tag-id`** so `dataTransfer` and `tagDragStart` / `tagDragEnd` detail carry a stable id.\n\n### How it works (important for handlers)\n\n- The **drag source** is an inner **label** element (a `div` with `role=\"button\"`), not the `<xpl-tag>` host. Browsers do not start drags reliably on the custom element host; the **`is-draggable`** prop does **not** mirror the native host **`draggable`** attribute (see the `isDraggable` prop docs below).\n- On **`dragstart`**, the component uses **`setDragImage`** so the floating preview (“ghost”) looks like the **whole tag** (label + dismiss control), not only the label strip.\n- **Start the drag from the label text**, not from the dismiss (×) button.\n- In container listeners, **`dragstart`** may bubble with `event.target` equal to that inner node. Resolve the tag with:\n\n```js\nconst tag = event.target instanceof Element ? event.target.closest('xpl-tag') : null;\n```\n\n### Making a tag draggable\n\n```html\n<xpl-tag is-draggable>Draggable tag</xpl-tag>\n\n<xpl-tag is-draggable tag-id=\"my-tag-1\">Design</xpl-tag>\n```\n\n### Reordering tags within a container\n\n```html\n<div id=\"tag-container\" style=\"display: flex; flex-wrap: wrap; gap: 8px\">\n <xpl-tag tag-id=\"t1\" is-draggable>Design</xpl-tag>\n <xpl-tag tag-id=\"t2\" is-draggable>Development</xpl-tag>\n <xpl-tag tag-id=\"t3\" is-draggable>QA</xpl-tag>\n</div>\n\n<script>\n const container = document.getElementById('tag-container');\n let draggedEl = null;\n\n container.addEventListener('dragstart', (e) => {\n const tag = e.target instanceof Element ? e.target.closest('xpl-tag') : null;\n if (tag) draggedEl = tag;\n });\n\n container.addEventListener('dragend', () => {\n draggedEl = null;\n });\n\n container.addEventListener('dragover', (e) => {\n e.preventDefault();\n if (!draggedEl) return;\n const afterEl = getDragAfterElement(container, e.clientX);\n if (afterEl == null) container.appendChild(draggedEl);\n else container.insertBefore(draggedEl, afterEl);\n });\n\n function getDragAfterElement(container, x) {\n const tags = [...container.querySelectorAll('xpl-tag')].filter((el) => el !== draggedEl);\n return tags.reduce(\n (closest, child) => {\n const box = child.getBoundingClientRect();\n const offset = x - box.left - box.width / 2;\n return offset < 0 && offset > closest.offset ? { offset, element: child } : closest;\n },\n { offset: Number.NEGATIVE_INFINITY },\n ).element;\n }\n</script>\n```\n\n### Dragging tags between containers\n\n```html\n<div\n class=\"drop-zone\"\n style=\"display: flex; gap: 8px; padding: 16px; border: 2px dashed var(--xpl-border-default); border-radius: 8px\"\n>\n <xpl-tag tag-id=\"a1\" is-draggable>Frontend</xpl-tag>\n <xpl-tag tag-id=\"a2\" is-draggable>Backend</xpl-tag>\n</div>\n\n<div\n class=\"drop-zone\"\n style=\"\n display: flex;\n gap: 8px;\n padding: 16px;\n border: 2px dashed var(--xpl-border-default);\n border-radius: 8px;\n margin-top: 8px;\n \"\n>\n <xpl-tag tag-id=\"b1\" is-draggable>DevOps</xpl-tag>\n</div>\n\n<script>\n document.querySelectorAll('.drop-zone').forEach((zone) => {\n let draggedEl = null;\n\n zone.addEventListener('dragstart', (e) => {\n const tag = e.target instanceof Element ? e.target.closest('xpl-tag') : null;\n if (tag) draggedEl = tag;\n });\n\n zone.addEventListener('dragend', () => {\n draggedEl = null;\n });\n\n zone.addEventListener('dragover', (e) => e.preventDefault());\n\n zone.addEventListener('drop', (e) => {\n e.preventDefault();\n if (draggedEl && draggedEl.parentNode !== zone) zone.appendChild(draggedEl);\n });\n });\n</script>\n```\n\n### `tagDragStart` / `tagDragEnd` events\n\nThe component emits **`tagDragStart`** and **`tagDragEnd`** (Stencil camelCase). They bubble and are composed.\n\n```html\n<xpl-tag is-draggable tag-id=\"my-tag\">Drag me</xpl-tag>\n\n<script>\n const tag = document.querySelector('xpl-tag[tag-id=\"my-tag\"]');\n\n tag.addEventListener('tagDragStart', (e) => {\n const { tagId, content, originalEvent } = e.detail;\n });\n\n tag.addEventListener('tagDragEnd', (e) => {\n const { dropped, tagId, originalEvent } = e.detail;\n if (dropped) {\n /* dropEffect was 'move' */\n }\n });\n</script>\n```\n\n### React (`@xplortech/apollo-react`)\n\nEnsure the app loads Apollo **defineCustomElements** (see the main React package docs). Import **`XplTag`** and use **`isDraggable`**, **`tagId`**, and children for the label.\n\n`HTMLXplTagElement` is provided by Apollo’s Stencil typings (the same setup that types `defineCustomElements`). If your editor cannot resolve it, use `useRef<HTMLElement | null>(null)` and attach listeners on `ref.current`.\n\n`XplTag` is a thin wrapper around the web component. Custom events use the same names as in the DOM (`tagDragStart`, `tagDragEnd`, `tagRemove`). If your TypeScript definitions use different prop names for listeners, attach them with a **ref** (always works):\n\n```tsx\nimport { useEffect, useRef } from 'react';\nimport { XplTag } from '@xplortech/apollo-react';\n\nfunction TagBoard() {\n const tagRef = useRef<HTMLXplTagElement | null>(null);\n\n useEffect(() => {\n const el = tagRef.current;\n if (!el) return;\n\n const onDragStart = (e: CustomEvent) => {\n const { tagId, content } = e.detail;\n };\n const onDragEnd = (e: CustomEvent) => {\n const { dropped, tagId } = e.detail;\n };\n const onTagRemove = () => {\n el.remove(); // Handle dismiss button click\n };\n\n el.addEventListener('tagDragStart', onDragStart as EventListener);\n el.addEventListener('tagDragEnd', onDragEnd as EventListener);\n el.addEventListener('tagRemove', onTagRemove);\n return () => {\n el.removeEventListener('tagDragStart', onDragStart as EventListener);\n el.removeEventListener('tagDragEnd', onDragEnd as EventListener);\n el.removeEventListener('tagRemove', onTagRemove);\n };\n }, []);\n\n return (\n <XplTag ref={tagRef} isDraggable tagId=\"react-1\">\n Movable\n </XplTag>\n );\n}\n```\n\nReorder / multi-container logic is unchanged: listen for native `dragstart`, `dragover`, `drop` on a React parent `div` (use refs) and call `closest('xpl-tag')` as in the HTML examples.\n\n### `DataTransfer` payload\n\n| MIME type | Value | Purpose |\n|-----------|-------|---------|\n| `application/x-tag-id` | `tag-id` prop, if set | Identify the tag in drop handlers |\n| `text/plain` | Trimmed text content of the tag | Fallback for external targets |\n\n`effectAllowed` is set to `move`.\n\n**`originalEvent` in `tagDragStart` / `tagDragEnd`:** the native `DragEvent` is included for convenience, but browsers may clear `dataTransfer` after the synchronous handler completes. Use `tagId`, `content`, and `dropped` from the custom event detail for anything needed after the current turn; only read `originalEvent.dataTransfer` synchronously in the listener.\n\n### Disabled tags\n\n`disabled` wins: the tag is not draggable and keep using a normal `<button>` label (no HTML5 drag on the host).\n\n```html\n<xpl-tag is-draggable disabled>Cannot drag</xpl-tag>\n```\n\n## Design Tokens\n\n| Property | Token |\n|----------|-------|\n| Border (default) | `var(--xpl-border-accent-information)` |\n| Border (disabled) | `var(--xpl-border-accent-subtle)` |\n| Background (default) | `var(--xpl-background-surface-default)` |\n| Background (hover) | `var(--xpl-background-surface-information-bold)` |\n| Background (disabled) | `var(--xpl-background-surface-subtle)` |\n| Text | `var(--xpl-text-strong)` |\n| Text (disabled) | `var(--xpl-text-disabled)` |\n| Dismiss icon | `var(--xpl-icon-default)` |\n| Dismiss icon (disabled) | `var(--xpl-icon-disabled)` |\n| Spacing (default) | `var(--xpl-space-2)` vertical, `var(--xpl-space-12)` horizontal |\n| Spacing (small) | `var(--xpl-space-2)` vertical, `var(--xpl-space-8)` horizontal |\n| Height (default) | `var(--xpl-size-150)` (24px) |\n| Height (small) | `var(--xpl-size-125)` (20px) |\n| Border radius | `var(--xpl-border-radius-button)` |\n| Typography (default) | `var(--xpl-font-size-body)` / `var(--xpl-font-weight-normal)` / `var(--xpl-line-height-body)` |\n| Typography (small) | `var(--xpl-font-size-callout)` / `var(--xpl-font-weight-semibold)` / `var(--xpl-line-height-callout)` |\n| Background (dragging) | `var(--xpl-background-surface-information-bold)` |\n| Cursor (draggable) | `cursor: grab` → `cursor: grabbing` while dragging |\n\n## Accessibility\n\n### Keyboard Navigation\n\n| Key | Action |\n|-----|--------|\n| Tab | Focus tag content or dismiss button |\n| Enter/Space | Activate the focused control: on **dismiss**, emits deprecated `close` and `tagRemove`; on **content**, emits deprecated `close` only when the tag is **not** in drag mode (native `<button>` behavior). |\n\n### Drag mode\n\n- With `isDraggable`, focus moves to the **label** (`role=\"button\"`). Enter/Space on the label does **not** dismiss the tag (same as click on draggable content).\n- Start drags from the **label**; the dismiss control remains a separate focus stop and is not the drag handle.\n\n### ARIA Attributes\n\n- Dismiss button sets `aria-label` to `Dismiss` plus the host element’s `textContent` (trimmed), or the word `tag` when empty, so multiple tags stay distinguishable in screen readers\n- When `disabled={true}`, buttons have `disabled` attribute preventing interaction\n- When `isDraggable` is set, **`aria-grabbed`** on the **label** reflects drag state (`\"false\"` at rest, `\"true\"` while dragging)\n\n### Icon Mapping\n\n| Element | Icon |\n|---------|------|\n| Dismiss button | `xmark` (size 10) |\n\n## Backward Compatibility (CSS class aliases)\n\nThe refactored component preserves legacy CSS classes so existing consumer selectors keep working:\n\n| Legacy class | Element | Replaces / alias for |\n|---|---|---|\n| `xpl-tag-container` | Host (`<xpl-tag>`) | Old root class — now an alias; styles come from `.xpl-tag` |\n| `xpl-tag` | Content `<button>` / `<div>` | Old content selector (`button.xpl-tag`) — kept so `.xpl-tag-container .xpl-tag` selectors match |\n| `xpl-tag__close` | Dismiss `<button>` | Old dismiss selector — alias for `.xpl-tag__dismiss` |\n\nThese aliases will be removed in a future major version. New code should target `.xpl-tag`, `.xpl-tag__content`, and `.xpl-tag__dismiss`.\n",
15463
16059
  "docs": "Tags represent a set of interactive keywords that help label, organize, and categorize objects. Tags can be added or removed from an object by the user. Unlike Badges, Tags are clickable, used in dropdowns, multi-select, search bars, etc.",
15464
16060
  "docsTags": [
16061
+ {
16062
+ "name": "slot",
16063
+ "text": "- The tag label content."
16064
+ },
15465
16065
  {
15466
16066
  "name": "since",
15467
16067
  "text": "0.4.0"
@@ -15693,7 +16293,12 @@
15693
16293
  ],
15694
16294
  "listeners": [],
15695
16295
  "styles": [],
15696
- "slots": [],
16296
+ "slots": [
16297
+ {
16298
+ "name": "",
16299
+ "docs": "The tag label content."
16300
+ }
16301
+ ],
15697
16302
  "parts": [],
15698
16303
  "states": [],
15699
16304
  "dependents": [
@@ -15718,6 +16323,14 @@
15718
16323
  "readme": "# xpl-toast\n\nToasts are brief, non-disruptive messages that provide quick, at-a-glance feedback. They can have 3 sentiments; neutral, positive and negative and contain actions such as a button or link, or a close button. Messaging can include a title and caption limited to 85 characters. Keep your toast messaging short and to the point and tone in line with the sentiment of the Toast. \n\nThe Toast component is configurable by Sentiment plus show/hide properties for Icon, Caption, Button and Dismissible. Use the nested component’s properties to customize further as needed. \n\nWhen possible, Toasts should auto-dismiss after 8 seconds. When an action is present, it may not be desirable to time out the Toast depending on your specific context. \n\nMatch your messaging with the sentiment of the toast. Most messages will have a neutral sentiment. Reserve negative sentiments for when an explicit failure has occurred. Give users the information they need to correct the issue. \n\nToasts only support a single action and up to 85 characters. If multiple actions can be taken, or the message is longer than a few words, use a [Modal component](https://apollo.xplordocs.com/?path=/story/components-modal--modal) instead.\n\n## Usage\n\n```html\n<!-- Neutral toast with a caption, auto-dismissing after the default 8s -->\n<xpl-toast caption=\"Your changes have been saved.\">\n <p slot=\"title\">Saved</p>\n</xpl-toast>\n\n<!-- With an action. Auto-dismiss is suppressed while the action slot has content -->\n<xpl-toast variant=\"warning\" caption=\"We couldn't reach the server.\">\n <p slot=\"title\">Sync failed</p>\n <xpl-button slot=\"action\" size=\"sm\">Retry</xpl-button>\n</xpl-toast>\n\n<!-- No close button; disable auto-dismiss with timeout=\"0\" -->\n<xpl-toast dismissible=\"false\" timeout=\"0\">\n <p slot=\"title\">Uploading…</p>\n</xpl-toast>\n```\n\n## Deprecations\n\n### `dismissable` → `dismissible`\n\nThe **`dismissable`** prop is misspelled and is **deprecated**. Use **`dismissible`** instead.\n\n```html\n<!-- Before -->\n<xpl-toast dismissable=\"false\">…</xpl-toast>\n\n<!-- After -->\n<xpl-toast dismissible=\"false\">…</xpl-toast>\n```\n\nBoth props are supported during the deprecation window, so no consumer change is required immediately:\n\n| `dismissable` | `dismissible` | Close button shown | Notes |\n| ------------- | ------------- | ------------------ | ----- |\n| unset | unset | ✅ | Default (`dismissible` defaults to `true`) |\n| unset | `false` | ❌ | Recommended usage |\n| `false` | unset | ❌ | Existing markup keeps working |\n| `false` | `true` | ❌ | `dismissable` wins while it exists |\n\n`dismissable` has **no default** (`undefined` when unset) so the component can tell \"not set\" from \"set to `false`\". When it is used, the component logs a one-time deprecation warning to the console. `dismissable` will be removed in the next major version, at which point `dismissible` becomes the only option.\n\n## Positioning\n\nThe default position is arbitrarily set to `40px` from top and `40px` from right of the browser viewport on desktop and bottom-center on mobile. This position should be adjusted to ensure it's not covering your App's header. Apollo, by design, does not prescribe a position for toasts because this will be dependent on the app that it is being used in.\n\nPositioning of toasts should be global to your app. For example, if it is decided that your app will always position the toast in the top right corner, then this convention should be followed throughout. It should not be positioned top-right on one screen and bottom-center on another.\n\n*****Consult your designer for the best placement for your product.**\n\n### Overriding the default position\n\nThe toast uses `position: fixed` and can be repositioned by overriding the `.xpl-toast` CSS class in your app's stylesheet. Set `top`, `right`, `bottom`, and `left` as needed and use `unset` or `initial` to clear any defaults you don't want.\n\n**Top-left example:**\n```css\n.xpl-toast {\n top: 24px;\n right: unset;\n left: 24px;\n}\n```\n\n**Bottom-right example:**\n```css\n.xpl-toast {\n top: unset;\n right: 24px;\n bottom: 24px;\n}\n```\n\n**Centered at the top:**\n```css\n.xpl-toast {\n top: 24px;\n right: unset;\n left: 50%;\n transform: translateX(-50%);\n}\n```\n\nThe mobile breakpoint (≤ 768px) resets the toast to bottom-center. If your app needs a different mobile position, override the media query as well:\n\n```css\n@media screen and (max-width: 768px) {\n .xpl-toast {\n /* your mobile overrides */\n bottom: 16px;\n left: 50%;\n transform: translateX(-50%);\n }\n}\n```\n\n> **Tip:** Place these overrides in a global stylesheet so every toast in your app appears in the same location.\n\n## Token Use\n\n`xpl-toast` follows the same messaging-component token pattern as [`xpl-banner`](../xpl-banner) (background `--xpl-background-messaging-*` + accent/sentiment border). Light and dark mode are handled by the Foundation token definitions, so the component has no `.dark` overrides.\n\n| Variant / element | Property | Token |\n| ----------------- | -------- | ----- |\n| `neutral` (default) | background | `--xpl-background-messaging-information` |\n| `neutral` (default) | border | `--xpl-border-accent-information` |\n| `&--warning` | background | `--xpl-background-messaging-negative` |\n| `&--warning` | border | `--xpl-border-negative` |\n| `&--success` | background | `--xpl-background-messaging-positive` |\n| `&--success` | border | `--xpl-border-positive` |\n| `&__icon--neutral` | color | `--xpl-icon-default` |\n| `&__icon--warning` | color | `--xpl-icon-negative` |\n| `&__icon--success` | color | `--xpl-icon-positive` |\n| `&` | body radius | `--xpl-border-radius-small` |\n| `&` | shadow | `--xpl-elevation-3` (theme-aware) |\n| `&__title` | typography (size / weight / line-height) | `@extend .xpl-text-title-5` → `title-5` / `medium` / `1.4286` |\n| `&__caption` | typography (size / weight / line-height) | `@extend .xpl-text-callout` → `callout` / `normal` / `1.3333` |\n| `&__close` | margin | `@apply ml-4` |\n\nThe close control is an [`xpl-button`](../xpl-button) (`variant=\"tertiary\"`, `size=\"sm\"`, `iconOnly`), so its radius, colors, and hover / focus / active states come from `button.css` rather than `toast.css`.\n\nLike `xpl-banner`, the messaging backgrounds resolve to transparent in dark mode; the sentiment border and page surface carry the visual treatment in that theme.\n\n## Accessibility\n\n- The close control is an [`xpl-button`](../xpl-button), so it is focusable and keyboard-activable (Tab, then Enter or Space) and gets the shared `:focus-visible` outline.\n- It carries `aria-label=\"Dismiss notification\"` and `type=\"button\"` — the explicit type keeps a toast rendered inside a `<form>` from submitting it when the toast is closed.\n- Toast content is projected through the `title` and `action` slots. Provide the title as text (e.g. `<p slot=\"title\">`) so screen readers announce the message.\n- Dismissing runs a fade-out animation and then removes the host element from the DOM, so a dismissed toast leaves nothing behind in the accessibility tree.\n\n> **Elevation shadow:** the toast uses `--xpl-elevation-3` (theme-aware), re-specified by design during the elevation 1–4 formalization — an earlier note citing the single \"Shadow 4\" style was superseded. See `TECH_DEBT.md`.\n",
15719
16324
  "docs": "Toasts are brief, non-disruptive messages that provide quick, at-a-glance feedback. They can have 3 sentiments; neutral, positive and negative and contain actions such as a button or link, or a close button. Messaging can include a title and caption limited to 85 characters. Keep your toast messaging short and to the point and tone in line with the sentiment of the Toast. \n\nThe Toast component is configurable by Sentiment plus show/hide properties for Icon, Caption, Button and Dismissible. Use the nested component’s properties to customize further as needed. \n\nWhen possible, Toasts should auto-dismiss after 8 seconds. When an action is present, it may not be desirable to time out the Toast depending on your specific context. \n\nMatch your messaging with the sentiment of the toast. Most messages will have a neutral sentiment. Reserve negative sentiments for when an explicit failure has occurred. Give users the information they need to correct the issue. \n\nToasts only support a single action and up to 85 characters. If multiple actions can be taken, or the message is longer than a few words, use a [Modal component](https://apollo.xplordocs.com/?path=/story/components-modal--modal) instead.",
15720
16325
  "docsTags": [
16326
+ {
16327
+ "name": "slot",
16328
+ "text": "title - The toast title text."
16329
+ },
16330
+ {
16331
+ "name": "slot",
16332
+ "text": "action - Optional action content (e.g. a button) rendered on the right side of the toast."
16333
+ },
15721
16334
  {
15722
16335
  "name": "since",
15723
16336
  "text": "1.2.0"
@@ -15913,7 +16526,16 @@
15913
16526
  "events": [],
15914
16527
  "listeners": [],
15915
16528
  "styles": [],
15916
- "slots": [],
16529
+ "slots": [
16530
+ {
16531
+ "name": "action",
16532
+ "docs": "Optional action content (e.g. a button) rendered on the right side of the toast."
16533
+ },
16534
+ {
16535
+ "name": "title",
16536
+ "docs": "The toast title text."
16537
+ }
16538
+ ],
15917
16539
  "parts": [],
15918
16540
  "states": [],
15919
16541
  "dependents": [],
@@ -16151,6 +16773,14 @@
16151
16773
  "readme": "# xpl-toolbar\n\n> **Note:** This component is currently in beta. Use with caution as it may have breaking changes in future releases.\n\n## Design Tokens\n\nThe toolbar is an **always-dark** selection bar (nav-rail pattern) — its surface stays dark\nin both light and dark themes. Styling comes from Apollo Foundation tokens\n(`packages/apollo-core/src/css/toolbar.css`):\n\n| Element | Property | Token | Resolves (light / dark) |\n| --- | --- | --- | --- |\n| `.toolbar` surface | `background-color` | `--xpl-background-surface-inverse-on-light` | neutral-1000 / neutral-1000 |\n| `.toolbar` top/bottom border | `border-color` | `--xpl-border-default` | neutral-600 (always-dark host) |\n| `.toolbar` elevation | `box-shadow` | `--xpl-elevation-1` | theme-aware (near-black light / near-white dark) |\n| `.toolbar` height | `height` | `--xpl-size-350` | 56px |\n| `.selected-item-count` text | `color` | `--xpl-text-inverse-on-dark` | white / white |\n| `.selected-item-count` divider | `border-color` | `--xpl-border-default` | neutral-600 (always-dark host) |\n| `.selected-item-count` label | `font-size` / `font-weight` / `line-height` | `@extend .xpl-text-title-5` (14px, medium, 1.4286) | — |\n\n> **Design note:** the always-dark surface decision is confirmed (there is no\n> toolbar node in `figma-manifest.json`). The elevation uses the theme-aware\n> `--xpl-elevation-1` token: in dark mode the shadow color flips to near-white, so\n> the always-dark bar gets a light halo beneath it — flagged for design verification\n> (see `TECH_DEBT.md`).\n",
16152
16774
  "docs": "> **Note:** This component is currently in beta. Use with caution as it may have breaking changes in future releases.",
16153
16775
  "docsTags": [
16776
+ {
16777
+ "name": "slot",
16778
+ "text": "actions-left - Actions rendered in the left group of the toolbar."
16779
+ },
16780
+ {
16781
+ "name": "slot",
16782
+ "text": "actions-right - Actions rendered in the right group of the toolbar."
16783
+ },
16154
16784
  {
16155
16785
  "name": "since",
16156
16786
  "text": "1.2.0"
@@ -16199,7 +16829,16 @@
16199
16829
  }
16200
16830
  ],
16201
16831
  "styles": [],
16202
- "slots": [],
16832
+ "slots": [
16833
+ {
16834
+ "name": "actions-left",
16835
+ "docs": "Actions rendered in the left group of the toolbar."
16836
+ },
16837
+ {
16838
+ "name": "actions-right",
16839
+ "docs": "Actions rendered in the right group of the toolbar."
16840
+ }
16841
+ ],
16203
16842
  "parts": [],
16204
16843
  "states": [],
16205
16844
  "dependents": [],
@@ -16213,6 +16852,10 @@
16213
16852
  "readme": "# xpl-tooltip\n\nTooltips are intended to be short one to two sentence messages adding contextual information for the user. Keep messages short and to the point. Tooltips are static and do not contain actions, and only appear when the context is hovered.\n\nTooltip variants control one of 8 arrow positions indicating the direction the tooltip will appear in relation to it's trigger:\n\"bottom-left\" | \"bottom-middle\" | \"bottom-right\" | \"left\" | \"right\" | \"top-left\" | \"top-middle\" | \"top-right\"\n\n\n### Design Tokens\n\nTokens used by this component from `@xplortech/apollo-foundation`.\n\n**Color**\n- `--xpl-background-surface-default` — tooltip background and arrow fill\n- `--xpl-elevation-3` — tooltip shadow (theme-aware)\n- `--xpl-text-default` — tooltip text color\n- `--xpl-border-default` — tooltip border and arrow stroke (keeps the arrow edge theme-aware in dark mode)\n\n**Spacing**\n- `--xpl-space-6` — vertical padding\n- `--xpl-space-8` — horizontal padding\n- `--xpl-space-12` — offset from trigger, via `--xpl-tooltip-margin`\n\n**Typography**\n- `.xpl-text-callout` — text style (via `@extend`)\n\n**Border**\n- `--xpl-border-small` — border width (1px)\n- `--xpl-border-radius-small` — corner radius (4px)\n\n**Z-index**\n- `--xpl-z-index-900` — tooltip stacking order\n- `--xpl-z-index-100` — arrow base stacking order (elevated to `--xpl-z-index-900` per position variant)\n",
16214
16853
  "docs": "Tooltips are intended to be short one to two sentence messages adding contextual information for the user. Keep messages short and to the point. Tooltips are static and do not contain actions, and only appear when the context is hovered.\n\nTooltip variants control one of 8 arrow positions indicating the direction the tooltip will appear in relation to it's trigger:\n\"bottom-left\" | \"bottom-middle\" | \"bottom-right\" | \"left\" | \"right\" | \"top-left\" | \"top-middle\" | \"top-right\"",
16215
16854
  "docsTags": [
16855
+ {
16856
+ "name": "slot",
16857
+ "text": "- The trigger element the tooltip is attached to."
16858
+ },
16216
16859
  {
16217
16860
  "name": "since",
16218
16861
  "text": "1.7.0"
@@ -16335,7 +16978,12 @@
16335
16978
  "events": [],
16336
16979
  "listeners": [],
16337
16980
  "styles": [],
16338
- "slots": [],
16981
+ "slots": [
16982
+ {
16983
+ "name": "",
16984
+ "docs": "The trigger element the tooltip is attached to."
16985
+ }
16986
+ ],
16339
16987
  "parts": [],
16340
16988
  "states": [],
16341
16989
  "dependents": [
@@ -16612,6 +17260,10 @@
16612
17260
  "readme": "# xpl-top-nav-item\n\n## Behavior\n\n- **Plain item:** **`label`** / **`icon`** (or **`slot=\"leading-icon\"`**), default slot, or **`href`** link. Renders a native **`<button type=\"button\">`** (or **`<a href>`** in link mode) and fires **`navItemClick`** (**`detail`**: **`MouseEvent`**) on click and **Enter** / **Space** — the native button handles keyboard activation, and link-mode **Space** maps to a programmatic **`.click()`**.\n- **Dropdown trigger:** Put content in **`slot=\"dropdown\"`** (e.g. `<xpl-top-nav-menu>`). Shows an auto **`chevron-down`** (unless **`slot=\"trailing-icon\"`** is provided) and toggles **`.xpl-top-nav-item__dropdown`** in the host light DOM — **not** **`xpl-dropdown`** / **`xpl-popover`**; use those for standalone floating menus (e.g. account in top-nav stories). **`navItemClick`** is not emitted. See **`TECH_DEBT.md`** for tradeoffs.\n- **Disclosure ARIA:** A dropdown trigger's inner control carries **`aria-expanded`** (open state), **`aria-haspopup=\"true\"`**, and **`aria-controls`** pointing at the **`.xpl-top-nav-item__dropdown`** panel's generated id. Plain items omit all three. **`aria-haspopup`** is the generic **`true`** (disclosure), not **`menu`** — the panel (**`xpl-top-nav-menu`**) is a **`role=\"list\"`** of link rows navigated by **Tab**, not an ARIA menu with arrow-key roving.\n- **Menu row:** The same component reused inside **`xpl-nav-header-menu`** / **`xpl-top-nav-menu`** — add **`description`** for a two-line row, and **`slot=\"trailing-icon\"`** for a functional icon (e.g. `external-link` for \"opens in new window\").\n- **Open API:** **`open`** (controlled/uncontrolled, reflected) + **`openChange`** event + **`openMenu()`** / **`closeMenu()`** / **`toggleMenu()`** methods — all equivalent ways to drive the dropdown panel.\n- **`openMode`:** **`click`** (default) or **`hover`** — hover only activates on hover-capable, fine-pointer, **`md`**-and-up devices; keyboard (Enter/Space) always toggles regardless.\n- **One open dropdown:** Outside click uses **`ownerDocument`** capture; siblings close via internal **`CustomEvent`** (reliable with JSDOM / **`stopPropagation`**).\n- **Escape:** Document **capture** **`keydown`** — **`preventDefault`** / **`stopPropagation`** so parent shells (**`xpl-top-nav`** mobile drawer, **`xpl-popover`**, modal) do not also react on the same keypress, then returns focus to the trigger control. **`xpl-top-nav`** closes its drawer on **Escape** in **bubble** phase, after this handler runs.\n\n## Host modifier classes (BEM)\n\n| Class | Source |\n| --- | --- |\n| **`xpl-top-nav-item--selected`** | **`selected`** prop |\n| **`xpl-top-nav-item--has-dropdown`** | **`slot=\"dropdown\"`** content present |\n| **`xpl-top-nav-item--dropdown-open`** | Dropdown panel is open (**`open`** prop) |\n| **`xpl-top-nav-item--active`** | **`active`** prop (or add the class on the host for edge cases) |\n| **`xpl-top-nav-item--has-active-descendant`** | A descendant row inside **`slot=\"dropdown\"`** is **`active`** / **`selected`** — accents the trigger like **`--active`**. Synced at connect / load / this item's own re-renders, not via a live descendant-class observer (see the JSDoc on **`hasActiveDescendant`** for why). |\n\n## Slots\n\n| Slot | Description |\n| --- | --- |\n| (default) | Custom trigger when **`label`** is omitted. |\n| `leading-icon` | Illustrative icon before the label. Overrides the **`icon`** prop when present. |\n| `trailing-icon` | Functional icon after the label. Overrides the auto **`chevron-down`** on a dropdown trigger. |\n| `dropdown` | Panel content (e.g. `<xpl-top-nav-menu>…</xpl-top-nav-menu>`). |\n",
16613
17261
  "docs": "Single nav row for **`xpl-top-nav`** / **`xpl-nav-header-menu`** / **`xpl-top-nav-menu`**. The host uses\n**`role=\"listitem\"`** when the row participates in a list: a **`role=\"list\"`** parent, **`slot=\"left-aside\"`** on\n**`xpl-top-nav`** (list host is **`.xpl-top-nav__left-primary`** while slotted nodes stay light-DOM children of\n**`xpl-top-nav`**), or default-slot rows under **`xpl-nav-header-menu`** / **`xpl-top-nav-menu`**. That flag is\nkept in **`@State`** and refreshed after render plus via **`MutationObserver`** on the host (**`class`**, **`slot`**) and\non **`parentElement`** (**`role`**) or on **`.xpl-top-nav__left-primary`** (**`role`**) for **`slot=\"left-aside\"`** under **`xpl-top-nav`**) so DOM context changes\nwithout a full reconnect still re-sync. The internal\n**`xpl-top-nav__brand`** row omits **`listitem`** (it sits in **`.xpl-top-nav__brand-bar`**, not **`.xpl-top-nav__left-primary`**),\nas do **`slot=\"right-aside-mobile\"`** rows (not a list). **`slot=\"dropdown\"`** uses a local **`.xpl-top-nav-item__dropdown`**\npanel (not **`xpl-dropdown`**); see **`TECH_DEBT.md`**.\n\n**This component does triple duty:** a plain bar item, a dropdown trigger (**`slot=\"dropdown\"`** present), and a\ndropdown/menu **row** (nested inside **`xpl-nav-header-menu`** or **`xpl-top-nav-menu`**). The **`description`** prop\nonly applies in the row context — bar items and triggers do not show it in Figma.\n\n**Default slot (no `label`):** do not put **`xpl-top-nav-item__inner`** or other **`xpl-top-nav-item__*`** classes on\ncustom trigger content — the component renders the inner wrapper and BEM elements.\n\n**Theming:** The host always includes Foundation’s **`dark`** class so **`--xpl-*`** purpose tokens resolve to dark-mode\nvalues even when the page root is light (consistent with **`xpl-top-nav`**).",
16614
17262
  "docsTags": [
17263
+ {
17264
+ "name": "slot",
17265
+ "text": "- The row label content. Used when the **`label`** prop is not set."
17266
+ },
16615
17267
  {
16616
17268
  "name": "slot",
16617
17269
  "text": "leading-icon - Illustrative icon before the label. Overrides the **`icon`** prop when present."
@@ -16982,6 +17634,10 @@
16982
17634
  ],
16983
17635
  "styles": [],
16984
17636
  "slots": [
17637
+ {
17638
+ "name": "",
17639
+ "docs": "The row label content. Used when the **`label`** prop is not set."
17640
+ },
16985
17641
  {
16986
17642
  "name": "dropdown",
16987
17643
  "docs": "Panel content (e.g. `<xpl-top-nav-menu>`). Presence of this slot makes the row a disclosure trigger."
@@ -17059,6 +17715,10 @@
17059
17715
  "listeners": [],
17060
17716
  "styles": [],
17061
17717
  "slots": [
17718
+ {
17719
+ "name": "",
17720
+ "docs": "The default slot"
17721
+ },
17062
17722
  {
17063
17723
  "name": "(default)",
17064
17724
  "docs": "**`xpl-top-nav-item`** rows."
@@ -17088,6 +17748,14 @@
17088
17748
  "readme": "# xpl-utility-bar\n\nThe utility bar is a top navigation bar. It contains 2 slots:\n\n`<slot name=\"main\"></slot>`\n\n- appears on the left side of the bar\n- items are justified to the start\n\n`<slot name=\"aside\"></slot>`\n\n- appears on the right side of the bar\n- items are justified to the end\n\nThe utility bar can have navigation items in just one or both slots.\nTo add content to the slots, just create a `<ul>` tag and target the desired slot:\n\n`<ul slot=\"main\">...</ul>`\n\n`<ul slot=\"aside\">...</ul>`\n\n## Design Tokens\n\nThe utility bar uses Apollo Foundation purpose-specific tokens, which theme automatically in light and dark mode (no `.dark` overrides).\n\n| Element | Property | Token |\n| --- | --- | --- |\n| `.xpl-utility-bar` | `background-color` | `--xpl-background-surface-default` |\n| `.xpl-utility-bar` | `color` (muted text) | `--xpl-text-subdued` |\n| `.xpl-utility-nav__bars` | `color` (nav-control icon) | `--xpl-icon-default` |\n| `.xpl-utility-nav__bars` | `height` | `--xpl-size-100` |\n| `.xpl-utility-bar__nav-control` `:focus-visible` | `outline-color` | `--xpl-border-focus` |\n",
17089
17749
  "docs": "The utility bar is a top navigation bar. It contains 2 slots:\n\n`<slot name=\"main\"></slot>`\n\n- appears on the left side of the bar\n- items are justified to the start\n\n`<slot name=\"aside\"></slot>`\n\n- appears on the right side of the bar\n- items are justified to the end\n\nThe utility bar can have navigation items in just one or both slots.\nTo add content to the slots, just create a `<ul>` tag and target the desired slot:\n\n`<ul slot=\"main\">...</ul>`\n\n`<ul slot=\"aside\">...</ul>`",
17090
17750
  "docsTags": [
17751
+ {
17752
+ "name": "slot",
17753
+ "text": "aside - Content rendered at the start of the bar, next to the nav-control toggle when present."
17754
+ },
17755
+ {
17756
+ "name": "slot",
17757
+ "text": "main - Main content of the utility bar."
17758
+ },
17091
17759
  {
17092
17760
  "name": "since",
17093
17761
  "text": "0.1.1"
@@ -17176,7 +17844,16 @@
17176
17844
  ],
17177
17845
  "listeners": [],
17178
17846
  "styles": [],
17179
- "slots": [],
17847
+ "slots": [
17848
+ {
17849
+ "name": "aside",
17850
+ "docs": "Content rendered at the start of the bar, next to the nav-control toggle when present."
17851
+ },
17852
+ {
17853
+ "name": "main",
17854
+ "docs": "Main content of the utility bar."
17855
+ }
17856
+ ],
17180
17857
  "parts": [],
17181
17858
  "states": [],
17182
17859
  "dependents": [],
@@ -17227,15 +17904,20 @@
17227
17904
  "path": "src/components/components/xpl-dropdown/dropdown-option.ts"
17228
17905
  },
17229
17906
  "src/components/utils/floating.ts::Placement": {
17230
- "declaration": "any",
17907
+ "declaration": "export declare type Placement = Prettify<Side | AlignedPlacement>;",
17231
17908
  "docstring": "",
17232
17909
  "path": "src/components/utils/floating.ts"
17233
17910
  },
17234
17911
  "../../node_modules/@stencil/core/internal/stencil-core/index.d.ts::Event": {
17235
- "declaration": "any",
17236
- "docstring": "",
17912
+ "declaration": "interface Event {\n /**\n * Returns true or false depending on how event was initialized. True if event goes through its target's ancestors in reverse tree order, and false otherwise.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/bubbles)\n */\n readonly bubbles: boolean;\n /**\n * @deprecated\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelBubble)\n */\n cancelBubble: boolean;\n /**\n * Returns true or false depending on how event was initialized. Its return value does not always carry meaning, but true can indicate that part of the operation during which event was dispatched, can be canceled by invoking the preventDefault() method.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelable)\n */\n readonly cancelable: boolean;\n /**\n * Returns true or false depending on how event was initialized. True if event invokes listeners past a ShadowRoot node that is the root of its target, and false otherwise.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/composed)\n */\n readonly composed: boolean;\n /**\n * Returns the object whose event listener's callback is currently being invoked.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/currentTarget)\n */\n readonly currentTarget: EventTarget | null;\n /**\n * Returns true if preventDefault() was invoked successfully to indicate cancelation, and false otherwise.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/defaultPrevented)\n */\n readonly defaultPrevented: boolean;\n /**\n * Returns the event's phase, which is one of NONE, CAPTURING_PHASE, AT_TARGET, and BUBBLING_PHASE.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/eventPhase)\n */\n readonly eventPhase: number;\n /**\n * Returns true if event was dispatched by the user agent, and false otherwise.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/isTrusted)\n */\n readonly isTrusted: boolean;\n /**\n * @deprecated\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/returnValue)\n */\n returnValue: boolean;\n /**\n * @deprecated\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/srcElement)\n */\n readonly srcElement: EventTarget | null;\n /**\n * Returns the object to which event is dispatched (its target).\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/target)\n */\n readonly target: EventTarget | null;\n /**\n * Returns the event's timestamp as the number of milliseconds measured relative to the time origin.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/timeStamp)\n */\n readonly timeStamp: DOMHighResTimeStamp;\n /**\n * Returns the type of event, e.g. \"click\", \"hashchange\", or \"submit\".\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/type)\n */\n readonly type: string;\n /**\n * Returns the invocation target objects of event's path (objects on which listeners will be invoked), except for any nodes in shadow trees of which the shadow root's mode is \"closed\" that are not reachable from event's currentTarget.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/composedPath)\n */\n composedPath(): EventTarget[];\n /**\n * @deprecated\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/initEvent)\n */\n initEvent(type: string, bubbles?: boolean, cancelable?: boolean): void;\n /**\n * If invoked when the cancelable attribute value is true, and while executing a listener for the event with passive set to false, signals to the operation that caused event to be dispatched that it needs to be canceled.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/preventDefault)\n */\n preventDefault(): void;\n /**\n * Invoking this method prevents event from reaching any registered event listeners after the current one finishes running and, when dispatched in a tree, also prevents event from reaching any other objects.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/stopImmediatePropagation)\n */\n stopImmediatePropagation(): void;\n /**\n * When dispatched in a tree, invoking this method prevents event from reaching any objects other than the current object.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/stopPropagation)\n */\n stopPropagation(): void;\n readonly NONE: 0;\n readonly CAPTURING_PHASE: 1;\n readonly AT_TARGET: 2;\n readonly BUBBLING_PHASE: 3;\n}",
17913
+ "docstring": "An event which takes place in the DOM.\n\n[MDN Reference](https://developer.mozilla.org/docs/Web/API/Event)",
17237
17914
  "path": "../../node_modules/@stencil/core/internal/stencil-core/index.d.ts"
17238
17915
  },
17916
+ "src/components/components/xpl-dropdown/xpl-dropdown.tsx::Dropdown": {
17917
+ "declaration": "@Component({ tag: 'xpl-dropdown' })\nexport class Dropdown {\n /** Exclusive last rendered option index when virtual scrolling is active. */\n @State() private virtualEnd = 0;\n\n /** First rendered option index when virtual scrolling is active. */\n @State() private virtualStart = 0;\n\n /** text to display as heading */\n @Prop({ attribute: 'open', mutable: true, reflect: true }) isOpen = false;\n\n /** options that are listed */\n @Prop() closeOnSelect = true;\n\n /** constant representing number of options that can be selected */\n @Prop() mode: 'single' | 'multi' = 'single';\n\n /** options that are listed */\n @Prop({ mutable: true }) options?: DropdownItem[];\n\n @Watch('options')\n onOptionsChanged() {\n // A selection toggle reassigns `options` with the same length (only\n // `isSelected` flips), whereas a genuinely new dataset changes the\n // count. Track it so only real list replacements recenter — in-place\n // selection updates must never move the user's scroll position (in\n // multi mode that could otherwise jump to the first selected row via\n // `findIndex`).\n const total = this.options?.length ?? 0;\n const listReplaced = total !== this.previousOptionCount;\n this.previousOptionCount = total;\n\n // When the list shrinks, cached range/scroll can point past the new\n // end (render would clamp `start` to `total` and slice to empty, or\n // leave a high scroll offset showing blank space). Reset for\n // non-virtualized lists and otherwise clamp scroll + recompute range.\n if (!this.isVirtualized) {\n this.virtualStart = 0;\n this.virtualEnd = 0;\n return;\n }\n\n const list = this.listEl;\n if (list) {\n const maxScroll = Math.max(0, total * VIRTUAL_LIST.rowHeight - list.clientHeight);\n if (list.scrollTop > maxScroll) {\n list.scrollTop = maxScroll;\n }\n\n // If the menu is open and a newly assigned list left the selected\n // row outside the current window (e.g. a large list assigned\n // asynchronously leaves scrollTop at 0), recenter so the selected\n // option is mounted. Gated on `listReplaced` so selection toggles\n // never trigger it.\n if (this.isOpen && listReplaced) {\n const selectedIndex = this.options?.findIndex((option) => option.isSelected) ?? -1;\n const outOfWindow =\n selectedIndex >= 0 &&\n (selectedIndex < this.virtualStart || selectedIndex >= this.virtualEnd);\n if (outOfWindow) {\n this.scrollSelectedIntoView();\n }\n }\n }\n\n this.updateVirtualRange();\n }\n\n /** whether to anchor the dropdown to the trigger element, requires triggerId to be set */\n @Prop() anchorToTrigger = false;\n\n @Watch('anchorToTrigger')\n onAnchorToTriggerChanged() {\n if (this.isOpen) {\n this.startPositioning();\n }\n }\n\n /**\n * When `anchorToTrigger` is true, additional placements to try when the\n * default `placement` does not fit (passed to Floating UI `flip`).\n */\n @Prop() flipFallbackPlacements?: Placement[];\n\n @Watch('flipFallbackPlacements')\n onFlipFallbackPlacementsChanged() {\n if (this.isOpen && this.anchorToTrigger) {\n this.startPositioning();\n }\n }\n\n /**\n * Floating UI placement when `anchorToTrigger` is true (e.g. `bottom-start`, `top-start`).\n */\n @Prop() placement: Placement = 'bottom-start';\n\n @Watch('placement')\n onPlacementChanged() {\n if (this.isOpen && this.anchorToTrigger) {\n this.startPositioning();\n }\n }\n\n /**\n * The currently selected value(s) in the dropdown.\n */\n @Prop() selectedValues?: string | string[];\n\n /** whether selection should be made when focusing on interactive element */\n @Prop() selectOnFocus = false;\n\n /** the id for the element that triggers the open state */\n @Prop() triggerId: string;\n\n @Watch('triggerId')\n onTriggerIdChanged() {\n if (this.isOpen) {\n this.startPositioning();\n }\n }\n\n /**\n * @callback update\n * @param {Object} e - event object\n * @param {Object} options - component\n */\n /** call back function invoked when a selection is made */\n @Prop() update?: (e: Event | null, context: { component: Dropdown }) => void;\n\n @Element() hostElement: HTMLXplDropdownElement;\n\n /** Emitted when the open state of the dropdown changes. */\n @Event() isOpenChange: EventEmitter<boolean>;\n\n private floating: FloatingController | null = null;\n\n private virtualScrollRaf: number | null = null;\n\n private openAlignmentRaf: number | null = null;\n\n private listEl?: HTMLElement;\n\n private listResizeObserver?: ResizeObserver;\n\n private previousOptionCount = 0;\n\n @Watch('selectedValues')\n onSelectedValuesChanged(newValues: string | string[], initialRender = false) {\n if (this.options && this.options.length > 0) {\n // Flatten options to handle both grouped and individual options\n const flatOptions = this.options.flatMap((curr) =>\n curr.groupName && curr.options ? curr.options : [curr],\n );\n\n // Update isSelected flags based on new selected values\n flatOptions.forEach((option) => {\n const optionValue = option.value || option.label || '';\n\n if (this.mode === 'single') {\n option.isSelected = optionValue === newValues;\n } else {\n const selectedArray = Array.isArray(newValues) ? newValues : [];\n option.isSelected = selectedArray.includes(optionValue);\n }\n });\n\n // Update the options array to trigger re-render\n const mapOptions = (option: DropdownItem): DropdownItem => {\n option.options = option.groupName ? option.options?.map(mapOptions) : undefined;\n return option;\n };\n this.options = this.options.map(mapOptions);\n\n // Call update callback if it exists\n if (this.update && !initialRender) {\n this.update(null, { component: this });\n }\n }\n }\n\n @Watch('isOpen')\n onIsOpenStateChange(newValue: boolean) {\n this.isOpenChange.emit(newValue);\n if (newValue) {\n this.startPositioning();\n this.alignOnOpen();\n } else {\n this.stopPositioning();\n this.cancelVirtualScroll();\n this.virtualStart = 0;\n this.virtualEnd = 0;\n }\n }\n\n connectedCallback() {\n if (!isBrowser()) {\n return;\n }\n\n document.addEventListener('mousedown', this.handleClickOutside);\n document.addEventListener('keydown', this.handleKeyDown);\n }\n\n disconnectedCallback() {\n document.removeEventListener('mousedown', this.handleClickOutside);\n document.removeEventListener('keydown', this.handleKeyDown);\n this.stopPositioning();\n this.cancelVirtualScroll();\n this.listResizeObserver?.disconnect();\n }\n\n componentWillLoad() {\n this.handleSelect = this.handleSelect.bind(this);\n if (this.selectedValues) {\n this.onSelectedValuesChanged(this.selectedValues, true);\n }\n // Seed the baseline so the first in-place selection change isn't\n // mistaken for a list replacement.\n this.previousOptionCount = this.options?.length ?? 0;\n }\n\n componentDidLoad() {\n if (this.isOpen) {\n // The `isOpen` watcher does not fire for an initial `open` attribute,\n // so align here too once the list has rendered.\n this.startPositioning();\n this.alignOnOpen();\n }\n }\n\n /**\n * called on dropdown option click and keydown events to update isSelected states\n */\n @Method()\n async handleSelect(e: Event | null, { component }: DropdownSelectContext): Promise<void> {\n const { hostElement, mode, options, update } = this;\n const isSingle = mode === 'single';\n const isMulti = mode === 'multi';\n\n if (options) {\n const targetValue = component.hostElement.value || component.hostElement.label;\n const hasGroups = options.some((option) => option.groupName);\n\n if (!hasGroups) {\n // Fast path for flat lists (e.g. very large selects): a single\n // linear pass plus a shallow copy to trigger the re-render,\n // instead of flatten + forEach + map over every option.\n options.forEach((option) => {\n const isMatch = (option.value || option.label) === targetValue;\n if (isSingle) {\n option.isSelected = isMatch;\n if (isMatch) component.hostElement.isSelected = true;\n } else if (isMulti && isMatch) {\n option.isSelected = !option.isSelected;\n component.hostElement.isSelected = option.isSelected;\n }\n });\n this.options = [...options];\n } else {\n const flatOptions = options.flatMap((curr) =>\n curr.groupName && curr.options ? curr.options : [curr],\n );\n\n flatOptions.forEach((option) => {\n const isMatch = (option.value || option.label) === targetValue;\n if (isSingle) {\n option.isSelected = isMatch;\n component.hostElement.isSelected = true;\n } else if (isMulti && isMatch) {\n option.isSelected = !option.isSelected;\n component.hostElement.isSelected = option.isSelected;\n }\n });\n\n const mapOptions = (option: DropdownItem): DropdownItem => {\n option.options = option.groupName ? option.options?.map(mapOptions) : undefined;\n return option;\n };\n this.options = options.map(mapOptions);\n }\n } else if (isSingle) {\n const optionNodes = Array.from(\n hostElement.querySelectorAll<HTMLXplDropdownOptionElement>('xpl-dropdown-option'),\n );\n optionNodes.forEach((node) => {\n node.isSelected = node === component.hostElement;\n });\n } else if (isMulti) {\n component.isSelected = !component.isSelected;\n }\n\n if (update) update(e, { component: this });\n }\n\n private startPositioning() {\n if (!this.anchorToTrigger || !this.triggerId) {\n return;\n }\n\n const triggerEl = document.getElementById(this.triggerId);\n if (!triggerEl) return;\n\n this.stopPositioning();\n\n this.floating = startFloating(triggerEl, this.hostElement, {\n placement: this.placement,\n strategy: 'fixed',\n middleware: dropdownMiddleware({\n flipFallbackPlacements: this.flipFallbackPlacements,\n sizeApply: ({ availableHeight, rects }) => {\n const listEl =\n this.hostElement.querySelector<HTMLElement>('.xpl-dropdown-list');\n let usedBeforeList = 0;\n if (listEl) {\n const children = Array.from(this.hostElement.children);\n const listIndex = children.indexOf(listEl);\n children.slice(0, listIndex).forEach((child) => {\n if (child instanceof HTMLElement) {\n usedBeforeList += child.offsetHeight;\n }\n });\n }\n const cs = getComputedStyle(this.hostElement);\n const hostVerticalChrome =\n parseFloat(cs.paddingTop) +\n parseFloat(cs.paddingBottom) +\n parseFloat(cs.borderTopWidth) +\n parseFloat(cs.borderBottomWidth);\n const cssMaxHeight = listEl\n ? parseFloat(\n getComputedStyle(listEl).getPropertyValue(\n '--xpl-dropdown-list-max-height',\n ),\n )\n : Infinity;\n const listMaxHeight = Math.min(\n cssMaxHeight,\n Math.max(0, availableHeight - usedBeforeList - hostVerticalChrome),\n );\n Object.assign(this.hostElement.style, {\n width: `${rects.reference.width}px`,\n });\n if (listEl) {\n listEl.style.maxHeight = `${listMaxHeight}px`;\n }\n },\n }),\n onPosition: ({ x, y, placement: resolvedPlacement }) => {\n if (!this.floating) {\n return;\n }\n\n const isAbove = getSide(resolvedPlacement) === 'top';\n\n Object.assign(this.hostElement.style, {\n left: `${x}px`,\n top: `${y}px`,\n });\n\n this.hostElement.classList.toggle('xpl-dropdown--above', isAbove);\n this.hostElement.classList.toggle('xpl-dropdown--below', !isAbove);\n },\n onReferenceHidden: () => {\n this.isOpen = false;\n return true;\n },\n });\n }\n\n private stopPositioning() {\n this.floating?.stop();\n this.floating = null;\n\n this.hostElement.style.left = '';\n this.hostElement.style.top = '';\n this.hostElement.style.width = '';\n this.hostElement\n .querySelector<HTMLElement>('.xpl-dropdown-list')\n ?.style.removeProperty('max-height');\n this.hostElement.classList.remove('xpl-dropdown--above', 'xpl-dropdown--below');\n }\n\n private handleClickOutside = ({ target }: MouseEvent): void => {\n const { triggerId } = this;\n const targetEl = target as HTMLElement | null;\n const triggerElement = triggerId ? document.getElementById(triggerId) : null;\n const isTriggerOrChild = triggerElement?.contains(targetEl);\n const isInside = this.hostElement.contains(targetEl);\n\n // A `<label for=\"{triggerId}\">` is part of the trigger: clicking it\n // forwards a synthetic click to the trigger to toggle the menu. Treating\n // its `mousedown` as an outside click would close the menu here, and the\n // forwarded click would immediately reopen it — so the label could only\n // ever open, never close. Exclude the associated label from outside detection.\n const labelEl = targetEl?.closest('label');\n const isTriggerLabel =\n !!labelEl && !!triggerId && labelEl.getAttribute('for') === triggerId;\n\n if (!isInside && !isTriggerOrChild && !isTriggerLabel && this.isOpen) {\n this.isOpen = false;\n }\n };\n\n private handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && this.isOpen) {\n this.isOpen = false;\n }\n };\n\n /**\n * Recomputes the rendered range from the scroll position. Throttled to one\n * update per animation frame.\n */\n private handleVirtualScroll = () => {\n if (this.virtualScrollRaf !== null) return;\n\n this.virtualScrollRaf = requestAnimationFrame(() => {\n this.virtualScrollRaf = null;\n this.updateVirtualRange();\n });\n };\n\n private cancelVirtualScroll() {\n if (this.virtualScrollRaf !== null) {\n cancelAnimationFrame(this.virtualScrollRaf);\n this.virtualScrollRaf = null;\n }\n if (this.openAlignmentRaf !== null) {\n cancelAnimationFrame(this.openAlignmentRaf);\n this.openAlignmentRaf = null;\n }\n }\n\n /**\n * Tracks the list element and observes its size. Floating UI's `size`\n * middleware (and viewport/layout changes) can grow or shrink the list\n * `clientHeight` without firing a scroll event, which would otherwise\n * leave a stale visible range until the user scrolls. Recompute the range\n * (throttled via the tracked scroll scheduler) whenever the list resizes.\n */\n private setListEl = (el?: HTMLElement) => {\n if (el === this.listEl) return;\n\n this.listEl = el;\n this.listResizeObserver?.disconnect();\n\n if (el && typeof ResizeObserver !== 'undefined') {\n this.listResizeObserver ??= new ResizeObserver(() => {\n if (this.isVirtualized) this.handleVirtualScroll();\n });\n this.listResizeObserver.observe(el);\n }\n };\n\n /**\n * Align the virtualized viewport to the selected option when the menu\n * opens. Shared by the `isOpen` watcher and `componentDidLoad` (the watcher\n * does not fire for an initial `open` attribute).\n */\n private alignOnOpen() {\n // Align synchronously so the first paint already renders the window\n // around the selected option. The list is always in the DOM with its\n // CSS max-height applied, so the scroll math is correct for\n // non-anchored lists and a close approximation for anchored ones.\n this.scrollSelectedIntoView();\n this.updateVirtualRange();\n // Re-align after layout, once the size middleware has set the final\n // list height. Routed through the tracked scroll scheduler so the frame\n // is cancelled on close/teardown.\n this.scheduleOpenAlignment();\n }\n\n /**\n * On open, align the viewport to the selected option (once the list has\n * been laid out) and recompute the rendered range. Uses its own tracked\n * frame (not the scroll slot) so the synchronous scroll fired by\n * `scrollSelectedIntoView` cannot let `handleVirtualScroll` preempt it and\n * skip the post-layout centering. Cancelled on close/teardown.\n */\n private scheduleOpenAlignment = () => {\n if (this.openAlignmentRaf !== null) return;\n\n this.openAlignmentRaf = requestAnimationFrame(() => {\n this.openAlignmentRaf = null;\n this.scrollSelectedIntoView();\n this.updateVirtualRange();\n });\n };\n\n /**\n * Scrolls the virtualized list so the selected option is centered in the\n * viewport. No-op for non-virtualized lists (their rows have natural\n * heights and the browser handles visibility).\n */\n private scrollSelectedIntoView() {\n const { listEl: list, options } = this;\n\n if (!list || !options?.length || !this.isVirtualized) return;\n\n const selectedIndex = options.findIndex((option) => option.isSelected);\n if (selectedIndex < 0) return;\n\n const { rowHeight } = VIRTUAL_LIST;\n const centered = selectedIndex * rowHeight - (list.clientHeight - rowHeight) / 2;\n const maxScroll = Math.max(0, options.length * rowHeight - list.clientHeight);\n\n list.scrollTop = Math.max(0, Math.min(centered, maxScroll));\n }\n\n private updateVirtualRange() {\n const list = this.listEl;\n const total = this.options?.length ?? 0;\n\n if (!list || !total) return;\n\n const { rowHeight, overscan } = VIRTUAL_LIST;\n const firstVisible = Math.floor(list.scrollTop / rowHeight);\n const visibleCount = Math.ceil(list.clientHeight / rowHeight) || 1;\n\n const start = Math.max(0, firstVisible - overscan);\n const end = Math.min(total, firstVisible + visibleCount + overscan);\n\n if (start !== this.virtualStart || end !== this.virtualEnd) {\n this.virtualStart = start;\n this.virtualEnd = end;\n }\n }\n\n private get isVirtualized(): boolean {\n const { options } = this;\n\n return (\n !!options &&\n options.length > VIRTUAL_LIST.virtualizeFrom &&\n // Virtualized rows use a fixed `rowHeight` with `overflow: hidden`,\n // which would silently clip subtitles. Exclude grouped and\n // subtitled lists so they keep their natural row height.\n options.every((option) => !option.groupName && !option.subtitle)\n );\n }\n\n render() {\n const { isOpen, mode, options } = this;\n\n const hasOptions = !!(options && options.length);\n const shouldVirtualize = this.isVirtualized;\n\n let listContent;\n if (!hasOptions) {\n listContent = <slot name=\"options\"></slot>;\n } else if (shouldVirtualize) {\n const { rowHeight, overscan } = VIRTUAL_LIST;\n const total = options.length;\n const start = Math.min(this.virtualStart, total);\n\n // Fall back to a sensible window before the list has been measured\n // (e.g. the very first render right after opening).\n const fallbackEnd = Math.min(total, Math.ceil(300 / rowHeight) + overscan * 2);\n const end = this.virtualEnd > start ? this.virtualEnd : fallbackEnd;\n\n listContent = (\n <li\n class=\"xpl-dropdown__virtual-sizer\"\n style={{ height: `${total * rowHeight}px` }}\n >\n {options.slice(start, end).map((option, i) => renderOption(option, start + i))}\n </li>\n );\n } else {\n listContent = options.map((option) => renderOption(option));\n }\n\n return (\n <Host\n aria-expanded={isOpen ? 'true' : 'false'}\n class={{\n 'xpl-dropdown': true,\n 'xpl-dropdown--multi': mode === 'multi',\n 'xpl-dropdown--open': isOpen,\n 'xpl-dropdown--anchor-to-trigger': this.anchorToTrigger,\n 'xpl-dropdown--virtualized': shouldVirtualize,\n }}\n role=\"listbox\"\n >\n <slot name=\"header\"></slot>\n <ul\n class=\"xpl-dropdown-list\"\n ref={(el) => {\n this.setListEl(el);\n }}\n onScroll={shouldVirtualize ? this.handleVirtualScroll : undefined}\n >\n {listContent}\n </ul>\n </Host>\n );\n }\n}",
17918
+ "docstring": "",
17919
+ "path": "src/components/components/xpl-dropdown/xpl-dropdown.tsx"
17920
+ },
17239
17921
  "src/components/components/xpl-dropdown/dropdown-option.ts::DropdownOptionGroup": {
17240
17922
  "declaration": "export interface DropdownOptionGroup {\n groupName: string;\n options: DropdownItem[];\n}",
17241
17923
  "docstring": "",
@@ -17257,7 +17939,7 @@
17257
17939
  "path": "src/components/components/xpl-popover/xpl-popover.tsx"
17258
17940
  },
17259
17941
  "../../node_modules/@floating-ui/dom/dist/floating-ui.dom.d.mts::Placement": {
17260
- "declaration": "any",
17942
+ "declaration": "export declare type Placement = Prettify<Side | AlignedPlacement>;",
17261
17943
  "docstring": "",
17262
17944
  "path": "../../node_modules/@floating-ui/dom/dist/floating-ui.dom.d.mts"
17263
17945
  },