@ds-mo/ui 14.31.3 → 14.32.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 (290) hide show
  1. package/dist/.package-ready.json +1 -1
  2. package/dist/agent-patterns.json +15 -5
  3. package/dist/agent.json +228 -23
  4. package/dist/angular/ds-bar-page-title.d.ts +28 -0
  5. package/dist/angular/ds-bar-page-title.js +48 -0
  6. package/dist/angular/ds-card-settings-scope.d.ts +1 -1
  7. package/dist/angular/ds-card-settings-scope.js +3 -3
  8. package/dist/angular/index.d.ts +1 -1
  9. package/dist/angular/index.js +1 -0
  10. package/dist/angular/proxies.d.ts +1 -0
  11. package/dist/angular/proxies.js +1 -0
  12. package/dist/components/ds-agent-questionnaire.js +1 -1
  13. package/dist/components/ds-agent-response.js +1 -1
  14. package/dist/components/ds-avatar.js +1 -1
  15. package/dist/components/ds-avatar.js.map +1 -1
  16. package/dist/components/ds-banner.js +1 -1
  17. package/dist/components/ds-banner.js.map +1 -1
  18. package/dist/components/ds-bar-action.js +1 -1
  19. package/dist/components/ds-bar-action.js.map +1 -1
  20. package/dist/components/ds-bar-nav.js +1 -1
  21. package/dist/components/ds-bar-page-title.d.ts +11 -0
  22. package/dist/components/ds-bar-page-title.js +2 -0
  23. package/dist/components/ds-bar-page-title.js.map +1 -0
  24. package/dist/components/ds-bar-title.js +1 -1
  25. package/dist/components/ds-bar-workflow.js +1 -1
  26. package/dist/components/ds-bar-workflow.js.map +1 -1
  27. package/dist/components/ds-button-filled.js +1 -1
  28. package/dist/components/ds-button-unfilled.js +1 -1
  29. package/dist/components/ds-card-chart.js +1 -1
  30. package/dist/components/ds-card-chart.js.map +1 -1
  31. package/dist/components/ds-card-navigation.js +1 -1
  32. package/dist/components/ds-card-navigation.js.map +1 -1
  33. package/dist/components/ds-card-overview.js +1 -1
  34. package/dist/components/ds-card-overview.js.map +1 -1
  35. package/dist/components/ds-card-setting.js +1 -1
  36. package/dist/components/ds-card-setting.js.map +1 -1
  37. package/dist/components/ds-card-settings-scope.js +1 -1
  38. package/dist/components/ds-card-settings-scope.js.map +1 -1
  39. package/dist/components/ds-chart-legend.js +1 -1
  40. package/dist/components/ds-chart-legend.js.map +1 -1
  41. package/dist/components/ds-chart.js +1 -1
  42. package/dist/components/ds-checkbox.js +1 -1
  43. package/dist/components/ds-chip.js +1 -1
  44. package/dist/components/ds-code-block.js +1 -1
  45. package/dist/components/ds-conversation-list-item.js +1 -1
  46. package/dist/components/ds-conversation-list-item.js.map +1 -1
  47. package/dist/components/ds-conversation-list-section.js +1 -1
  48. package/dist/components/ds-empty-state.js +1 -1
  49. package/dist/components/ds-field.js +1 -1
  50. package/dist/components/ds-filter-menu.js +1 -1
  51. package/dist/components/ds-input.js +1 -1
  52. package/dist/components/ds-markdown.js +1 -1
  53. package/dist/components/ds-menu.js +1 -1
  54. package/dist/components/ds-message-actions.js +1 -1
  55. package/dist/components/ds-message-composer.js +1 -1
  56. package/dist/components/ds-message-scroller.js +1 -1
  57. package/dist/components/ds-message.js +1 -1
  58. package/dist/components/ds-mobile-bar-nav.js +1 -1
  59. package/dist/components/ds-mobile-header.js +1 -1
  60. package/dist/components/ds-mobile-section-switcher.js +1 -1
  61. package/dist/components/ds-mobile-sheet-nav.js +1 -1
  62. package/dist/components/ds-modal.js +1 -1
  63. package/dist/components/ds-pagination.js +1 -1
  64. package/dist/components/ds-panel-nav.js +1 -1
  65. package/dist/components/ds-panel-sub-nav.js +1 -1
  66. package/dist/components/ds-panel-sub-nav.js.map +1 -1
  67. package/dist/components/ds-panel-tool-header.js +1 -1
  68. package/dist/components/ds-panel-tool-search.js +1 -1
  69. package/dist/components/ds-panel-tool-search.js.map +1 -1
  70. package/dist/components/ds-panel-tools.js +1 -1
  71. package/dist/components/ds-paper-texture.js +1 -1
  72. package/dist/components/ds-radio.js +1 -1
  73. package/dist/components/ds-score.js +1 -1
  74. package/dist/components/ds-select.js +1 -1
  75. package/dist/components/ds-shell-app.js +1 -1
  76. package/dist/components/ds-shell-app.js.map +1 -1
  77. package/dist/components/ds-shell-page.js +1 -1
  78. package/dist/components/ds-shell-tools.js +1 -1
  79. package/dist/components/ds-skeleton.js +1 -1
  80. package/dist/components/ds-swatch-picker.js +1 -1
  81. package/dist/components/ds-tab-group.js +1 -1
  82. package/dist/components/ds-table-filter.js +1 -1
  83. package/dist/components/ds-table-group.js +1 -1
  84. package/dist/components/ds-table-group.js.map +1 -1
  85. package/dist/components/ds-table-saved-views.js +1 -1
  86. package/dist/components/ds-table-search.js +1 -1
  87. package/dist/components/ds-table-search.js.map +1 -1
  88. package/dist/components/ds-table-sort.js +1 -1
  89. package/dist/components/ds-table-toolbar.js +1 -1
  90. package/dist/components/ds-table.js +1 -1
  91. package/dist/components/ds-table.js.map +1 -1
  92. package/dist/components/ds-tag.js +1 -1
  93. package/dist/components/ds-textarea.js +1 -1
  94. package/dist/components/ds-textarea.js.map +1 -1
  95. package/dist/components/ds-toast.js +1 -1
  96. package/dist/components/ds-toast.js.map +1 -1
  97. package/dist/components/ds-tooltip-chart.js +1 -1
  98. package/dist/components/ds-tooltip.js +1 -1
  99. package/dist/components/ds-typing-indicator.js +1 -1
  100. package/dist/components/p--VKxPUoV.js +2 -0
  101. package/dist/components/p--VKxPUoV.js.map +1 -0
  102. package/dist/components/p-14sMoeTr.js +2 -0
  103. package/dist/components/p-14sMoeTr.js.map +1 -0
  104. package/dist/components/p-9rzbZP-F.js +2 -0
  105. package/dist/components/p-9rzbZP-F.js.map +1 -0
  106. package/dist/components/p-B5W_r4vV.js +2 -0
  107. package/dist/components/p-B5W_r4vV.js.map +1 -0
  108. package/dist/components/{p-DjF2m7bf.js → p-B8ZonS3S.js} +2 -2
  109. package/dist/components/{p-BrW77sNB.js → p-B9WwU5vu.js} +2 -2
  110. package/dist/components/p-BCRPg1Xs.js +2 -0
  111. package/dist/components/p-BCRPg1Xs.js.map +1 -0
  112. package/dist/components/{p-KV3_5Cgo.js → p-BFd18avA.js} +2 -2
  113. package/dist/components/{p-DLDFPn9F.js → p-BGGIEmQj.js} +2 -2
  114. package/dist/components/p-BG_n-zQT.js +2 -0
  115. package/dist/components/p-BG_n-zQT.js.map +1 -0
  116. package/dist/components/p-BINlT3xz.js +2 -0
  117. package/dist/components/p-BINlT3xz.js.map +1 -0
  118. package/dist/components/p-BPTqn5zL.js +2 -0
  119. package/dist/components/p-BPTqn5zL.js.map +1 -0
  120. package/dist/components/p-BnWxbhFP.js +2 -0
  121. package/dist/components/p-BnWxbhFP.js.map +1 -0
  122. package/dist/components/{p-DhibZEq-.js → p-BonnrRtE.js} +2 -2
  123. package/dist/components/p-BxduVc5g.js +2 -0
  124. package/dist/components/p-BxduVc5g.js.map +1 -0
  125. package/dist/components/p-CD9AbaHQ.js +2 -0
  126. package/dist/components/p-CD9AbaHQ.js.map +1 -0
  127. package/dist/components/p-CN3roaMg.js +2 -0
  128. package/dist/components/p-CN3roaMg.js.map +1 -0
  129. package/dist/components/p-CR7G2BI6.js +2 -0
  130. package/dist/components/p-CR7G2BI6.js.map +1 -0
  131. package/dist/components/p-CakRq92x.js +2 -0
  132. package/dist/components/p-CakRq92x.js.map +1 -0
  133. package/dist/components/p-CumGY3uN.js +2 -0
  134. package/dist/components/p-CumGY3uN.js.map +1 -0
  135. package/dist/components/p-D59xX7JN.js +2 -0
  136. package/dist/components/p-D59xX7JN.js.map +1 -0
  137. package/dist/components/p-DFWJmuaR.js +2 -0
  138. package/dist/components/p-DFWJmuaR.js.map +1 -0
  139. package/dist/components/p-DGQUm3WL.js +2 -0
  140. package/dist/components/p-DGQUm3WL.js.map +1 -0
  141. package/dist/components/{p-DmTnBu5r.js → p-DJHshyyC.js} +2 -2
  142. package/dist/components/{p-BtUYEMwA.js → p-DPPM5T7q.js} +2 -2
  143. package/dist/components/p-DVRL3oem.js +2 -0
  144. package/dist/components/p-DVRL3oem.js.map +1 -0
  145. package/dist/components/p-DceM6xWR.js +2 -0
  146. package/dist/components/p-DceM6xWR.js.map +1 -0
  147. package/dist/components/p-Dhw_W8nk.js +2 -0
  148. package/dist/components/p-Dhw_W8nk.js.map +1 -0
  149. package/dist/components/{p-pkGqqLm-.js → p-DwxKwC5i.js} +2 -2
  150. package/dist/components/{p-DnhDgPJv.js → p-DzHXk0IG.js} +2 -2
  151. package/dist/components/p-OHT7lQVt.js +2 -0
  152. package/dist/components/p-OHT7lQVt.js.map +1 -0
  153. package/dist/components/{p-ln3dJxQT.js → p-P60MGUqZ.js} +2 -2
  154. package/dist/components/{p-Bu-gu0mN.js → p-UxIgD24y.js} +2 -2
  155. package/dist/components/{p-BiIDQ5RW.js → p-YFrD62Ml.js} +2 -2
  156. package/dist/components/p-gR6G5azA.js +2 -0
  157. package/dist/components/p-gR6G5azA.js.map +1 -0
  158. package/dist/components/{p-ehZFKett.js → p-oFbI_M5B.js} +2 -2
  159. package/dist/components/p-r5td84fg.js +2 -0
  160. package/dist/components/p-r5td84fg.js.map +1 -0
  161. package/dist/components/p-skVhQk9d.js +2 -0
  162. package/dist/components/p-skVhQk9d.js.map +1 -0
  163. package/dist/components/p-z7Zj26BA.js +2 -0
  164. package/dist/components/p-z7Zj26BA.js.map +1 -0
  165. package/dist/components/{p-YDUM8xDg.js → p-zunBF381.js} +2 -2
  166. package/dist/docs/components.json +584 -38
  167. package/dist/mcp-data/registry/avatar.json +1 -1
  168. package/dist/mcp-data/registry/banner.json +1 -1
  169. package/dist/mcp-data/registry/bar-action.json +5 -5
  170. package/dist/mcp-data/registry/bar-nav.json +1 -1
  171. package/dist/mcp-data/registry/bar-page-title.json +433 -0
  172. package/dist/mcp-data/registry/bar-title.json +21 -5
  173. package/dist/mcp-data/registry/bar-workflow.json +1 -1
  174. package/dist/mcp-data/registry/button-filled.json +1 -1
  175. package/dist/mcp-data/registry/button-unfilled.json +1 -1
  176. package/dist/mcp-data/registry/card-chart.json +1 -1
  177. package/dist/mcp-data/registry/card-navigation.json +1 -1
  178. package/dist/mcp-data/registry/card-overview.json +1 -1
  179. package/dist/mcp-data/registry/card-setting.json +1 -1
  180. package/dist/mcp-data/registry/card-settings-scope.json +23 -5
  181. package/dist/mcp-data/registry/chart-legend.json +1 -1
  182. package/dist/mcp-data/registry/checkbox.json +1 -1
  183. package/dist/mcp-data/registry/chip.json +3 -2
  184. package/dist/mcp-data/registry/conversation-list-item.json +1 -1
  185. package/dist/mcp-data/registry/filter-menu.json +1 -1
  186. package/dist/mcp-data/registry/input.json +1 -1
  187. package/dist/mcp-data/registry/menu.json +1 -1
  188. package/dist/mcp-data/registry/mobile-section-switcher.json +1 -1
  189. package/dist/mcp-data/registry/mobile-sheet-nav.json +1 -1
  190. package/dist/mcp-data/registry/modal.json +1 -1
  191. package/dist/mcp-data/registry/pagination.json +10 -10
  192. package/dist/mcp-data/registry/panel-nav.json +1 -1
  193. package/dist/mcp-data/registry/panel-sub-nav.json +1 -1
  194. package/dist/mcp-data/registry/panel-tool-header.json +1 -1
  195. package/dist/mcp-data/registry/panel-tool-search.json +1 -1
  196. package/dist/mcp-data/registry/panel-tools.json +4 -2
  197. package/dist/mcp-data/registry/radio.json +1 -1
  198. package/dist/mcp-data/registry/registry.json +230 -22
  199. package/dist/mcp-data/registry/select.json +1 -1
  200. package/dist/mcp-data/registry/shell-app.json +9 -7
  201. package/dist/mcp-data/registry/skeleton.json +1 -1
  202. package/dist/mcp-data/registry/tab-group.json +1 -1
  203. package/dist/mcp-data/registry/table-group.json +1 -1
  204. package/dist/mcp-data/registry/table-search.json +1 -1
  205. package/dist/mcp-data/registry/table.json +2 -2
  206. package/dist/mcp-data/registry/tag.json +1 -1
  207. package/dist/mcp-data/registry/textarea.json +1 -1
  208. package/dist/mcp-data/registry/toast.json +1 -1
  209. package/dist/mcp-data/registry/tooltip-chart.json +1 -1
  210. package/dist/mcp-data/registry/tooltip.json +1 -1
  211. package/dist/react/components.d.ts +1 -0
  212. package/dist/react/components.js +1 -0
  213. package/dist/react/ds-bar-page-title.d.ts +14 -0
  214. package/dist/react/ds-bar-page-title.js +16 -0
  215. package/dist/styles/table.css +1 -1
  216. package/dist/types/components/BarPageTitle/BarPageTitle.d.ts +124 -0
  217. package/dist/types/components/BarPageTitle/index.d.ts +2 -0
  218. package/dist/types/components/BarTitle/BarTitle.d.ts +1 -7
  219. package/dist/types/components/BarTitle/bar-title-action-chrome.d.ts +76 -0
  220. package/dist/types/components/BarTitle/bar-title-actions.d.ts +1 -0
  221. package/dist/types/components/BarTitle/bar-title-sections.d.ts +11 -0
  222. package/dist/types/components/CardSettingsScope/CardSettingsScope.d.ts +3 -1
  223. package/dist/types/components/ShellApp/ShellApp.d.ts +1 -0
  224. package/dist/types/components.d.ts +181 -4
  225. package/dist/vue/components.d.ts +1 -0
  226. package/dist/vue/components.js +1 -0
  227. package/dist/vue/ds-bar-page-title.d.ts +3 -0
  228. package/dist/vue/ds-bar-page-title.js +26 -0
  229. package/dist/vue/ds-card-settings-scope.js +1 -0
  230. package/package.json +1 -1
  231. package/dist/components/p-B4ZrhWSH.js +0 -2
  232. package/dist/components/p-B4ZrhWSH.js.map +0 -1
  233. package/dist/components/p-BTsFFzfR.js +0 -2
  234. package/dist/components/p-BTsFFzfR.js.map +0 -1
  235. package/dist/components/p-BpSPAlOx.js +0 -2
  236. package/dist/components/p-BpSPAlOx.js.map +0 -1
  237. package/dist/components/p-C-MaZPyA.js +0 -2
  238. package/dist/components/p-C-MaZPyA.js.map +0 -1
  239. package/dist/components/p-CB5-uIY2.js +0 -2
  240. package/dist/components/p-CB5-uIY2.js.map +0 -1
  241. package/dist/components/p-CG4Fp3SY.js +0 -2
  242. package/dist/components/p-CG4Fp3SY.js.map +0 -1
  243. package/dist/components/p-CHCsDvJK.js +0 -2
  244. package/dist/components/p-CHCsDvJK.js.map +0 -1
  245. package/dist/components/p-CImwNex7.js +0 -2
  246. package/dist/components/p-CImwNex7.js.map +0 -1
  247. package/dist/components/p-CNZwk176.js +0 -2
  248. package/dist/components/p-CNZwk176.js.map +0 -1
  249. package/dist/components/p-CSZlbQ8_.js +0 -2
  250. package/dist/components/p-CSZlbQ8_.js.map +0 -1
  251. package/dist/components/p-CVNYRTEH.js +0 -2
  252. package/dist/components/p-CVNYRTEH.js.map +0 -1
  253. package/dist/components/p-D3sJXjcg.js +0 -2
  254. package/dist/components/p-D3sJXjcg.js.map +0 -1
  255. package/dist/components/p-DFOhxUQP.js +0 -2
  256. package/dist/components/p-DFOhxUQP.js.map +0 -1
  257. package/dist/components/p-DI7qbahC.js +0 -2
  258. package/dist/components/p-DI7qbahC.js.map +0 -1
  259. package/dist/components/p-DP75OhfU.js +0 -2
  260. package/dist/components/p-DP75OhfU.js.map +0 -1
  261. package/dist/components/p-DR1jV6Q5.js +0 -2
  262. package/dist/components/p-DR1jV6Q5.js.map +0 -1
  263. package/dist/components/p-DuJuqbcg.js +0 -2
  264. package/dist/components/p-DuJuqbcg.js.map +0 -1
  265. package/dist/components/p-E3RCUE7a.js +0 -2
  266. package/dist/components/p-E3RCUE7a.js.map +0 -1
  267. package/dist/components/p-JuKGR7Ay.js +0 -2
  268. package/dist/components/p-JuKGR7Ay.js.map +0 -1
  269. package/dist/components/p-MuZ6B9gu.js +0 -2
  270. package/dist/components/p-MuZ6B9gu.js.map +0 -1
  271. package/dist/components/p-Ui0Ir5li.js +0 -2
  272. package/dist/components/p-Ui0Ir5li.js.map +0 -1
  273. package/dist/components/p-eAX_M_QY.js +0 -2
  274. package/dist/components/p-eAX_M_QY.js.map +0 -1
  275. package/dist/components/p-eaCtnww1.js +0 -2
  276. package/dist/components/p-eaCtnww1.js.map +0 -1
  277. /package/dist/components/{p-DjF2m7bf.js.map → p-B8ZonS3S.js.map} +0 -0
  278. /package/dist/components/{p-BrW77sNB.js.map → p-B9WwU5vu.js.map} +0 -0
  279. /package/dist/components/{p-KV3_5Cgo.js.map → p-BFd18avA.js.map} +0 -0
  280. /package/dist/components/{p-DLDFPn9F.js.map → p-BGGIEmQj.js.map} +0 -0
  281. /package/dist/components/{p-DhibZEq-.js.map → p-BonnrRtE.js.map} +0 -0
  282. /package/dist/components/{p-DmTnBu5r.js.map → p-DJHshyyC.js.map} +0 -0
  283. /package/dist/components/{p-BtUYEMwA.js.map → p-DPPM5T7q.js.map} +0 -0
  284. /package/dist/components/{p-pkGqqLm-.js.map → p-DwxKwC5i.js.map} +0 -0
  285. /package/dist/components/{p-DnhDgPJv.js.map → p-DzHXk0IG.js.map} +0 -0
  286. /package/dist/components/{p-ln3dJxQT.js.map → p-P60MGUqZ.js.map} +0 -0
  287. /package/dist/components/{p-Bu-gu0mN.js.map → p-UxIgD24y.js.map} +0 -0
  288. /package/dist/components/{p-BiIDQ5RW.js.map → p-YFrD62Ml.js.map} +0 -0
  289. /package/dist/components/{p-ehZFKett.js.map → p-oFbI_M5B.js.map} +0 -0
  290. /package/dist/components/{p-YDUM8xDg.js.map → p-zunBF381.js.map} +0 -0
@@ -22,12 +22,18 @@
22
22
  ],
23
23
  "avoidWhen": [
24
24
  "The title belongs to a PanelTools drawer or fullscreen pane; use PanelToolHeader through PanelTools instead.",
25
+ "The chrome is the managed panel-mode shell bar; use BarPageTitle.",
25
26
  "The choices are primary application destinations or BarNav destinations rather than sections within the current page.",
26
27
  "The heading belongs to a card, modal, or other bounded region rather than the page.",
27
28
  "Local panels inside page content should remain directly selectable; use TabGroup instead.",
28
29
  "Automatic sticky or responsive page orchestration is required; compose BarTitle in ShellPage rather than implementing it in the header."
29
30
  ],
30
31
  "alternatives": [
32
+ {
33
+ "when": "The chrome is the managed panel-mode shell bar with page-level sections.",
34
+ "component": "component:ds-bar-page-title",
35
+ "reason": "BarPageTitle owns compact shell-bar identity and all-or-nothing page-section tabs."
36
+ },
31
37
  {
32
38
  "when": "A create or edit workflow needs Exit, persistent step navigation, and a final Save or Submit action.",
33
39
  "component": "component:ds-bar-workflow",
@@ -74,13 +80,13 @@
74
80
  "expanded renders the larger heading, optional description, inset divider, and a full-width caption breadcrumb row above the title. The breadcrumb begins after 32px top padding; the following title and right-side actions share one top alignment. Compact renders 48px chrome with the existing icon Back action; constrained uses compact chrome and moves an auto-collapse primary action into overflow.",
75
81
  "The one visible ordered action collapses by default in constrained presentation. Use never for an exceptional action whose hierarchy must remain visible; explicit overflow actions never leave the shared menu.",
76
82
  "showDivider=false removes the bottom divider when the surrounding page composition supplies the boundary; showCompactDivider can independently restore or remove that boundary in compact and constrained variants.",
77
- "placement=shell-bar is reserved for ShellApp panel section navigation. It remains compact, omits description and breadcrumb presentation, and preserves applicable Back, section, primary, and overflow controls.",
83
+ "placement=shell-bar remains compact slotted chrome for advanced ShellApp composition. Managed panel-mode ShellApp uses BarPageTitle instead. It omits description and breadcrumb presentation and preserves applicable Back, section, primary, and overflow controls.",
78
84
  "Section, action, split-menu, and overflow triggers automatically retain the shared surface-open wash through exit. The expanded header presentation is layout, not surface ownership, and does not itself acquire a pressed wash."
79
85
  ],
80
86
  "responsiveBehavior": [
81
87
  "BarTitle does not observe viewport, shell, scroll, or container state. Its owner selects an explicit variant.",
82
88
  "ShellPage is the standard owner for automatic expanded, compact, and constrained selection.",
83
- "ShellApp owns shell-bar placement and always selects compact presentation there; mobile continues to use MobileHeader.",
89
+ "ShellApp owns shell-bar placement for slotted composition and always selects compact presentation there; managed panel mode uses BarPageTitle. Mobile continues to use MobileHeader.",
84
90
  "When ShellPage owns BarTitle on a secondary desktop or tablet page, its expanded, compact, and constrained presentations all match that canvas; the separate MobileHeader component remains primary.",
85
91
  "Compact titles use the shared compact-header geometry and md control inset so top-level headings align with BarNav and inline Back geometry aligns with PanelToolHeader. Long headings and section labels truncate independently while Back and action controls retain complete hit targets and accessible names.",
86
92
  "A never-collapse action remains visible in constrained mode; the title and section label yield space first.",
@@ -557,17 +563,27 @@
557
563
  "files": [
558
564
  {
559
565
  "path": "src/wc/components/BarTitle/BarTitle.css",
560
- "content": "@import '../../utils/control-density.css';\n@import '../../utils/chrome-header.css';\n@import '../../utils/focus-ring.css';\n@import '../../utils/interaction-fill.css';\n\n:host {\n display: block;\n min-width: 0;\n box-sizing: border-box;\n background: var(--_shell-page-header-background, var(--color-background-primary));\n}\n\n:host([placement='shell-bar']) {\n --_shell-page-header-background: var(--color-background-secondary);\n}\n\n:host-context(ds-shell-app:not([gradient-preset='none']))[placement='shell-bar'],\n:host-context(ds-shell-app:not([gradient-preset='none']))[placement='shell-bar'] .bar-title,\n:host-context(ds-shell-app.shell-app--paper-texture)[placement='shell-bar'],\n:host-context(ds-shell-app.shell-app--paper-texture)[placement='shell-bar'] .bar-title {\n background: transparent;\n}\n\n:host([data-shell-page-syncing]) {\n visibility: hidden;\n}\n\n.bar-title {\n position: relative;\n min-width: 0;\n box-sizing: border-box;\n background: var(--_shell-page-header-background, var(--color-background-primary));\n}\n\n.bar-title::after {\n content: '';\n position: absolute;\n inset: auto var(--ds-bar-title-divider-inset, var(--dimension-space-400)) 0;\n height: var(--dimension-stroke-width-012);\n background: var(--color-border-tertiary);\n pointer-events: none;\n}\n\n.bar-title--divider-hidden::after {\n display: none;\n}\n\n.bar-title__inner {\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: var(--dimension-space-100);\n padding: var(--dimension-space-400);\n box-sizing: border-box;\n}\n\n.bar-title__row {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: var(--dimension-space-100);\n min-height: var(--dimension-size-400);\n min-width: 0;\n}\n\n:host(.bar-title-host--expanded) .bar-title__row {\n gap: var(--dimension-space-400);\n}\n\n.bar-title__leading {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n gap: var(--dimension-space-100);\n min-width: 0;\n}\n\n.bar-title__title-row {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-100);\n min-width: 0;\n min-height: var(--dimension-size-400);\n}\n\n.bar-title__identity {\n display: flex;\n flex: 0 1 auto;\n align-items: center;\n gap: var(--dimension-space-050);\n min-width: 0;\n}\n\n.bar-title__back,\n.bar-title__section-chevron {\n flex: 0 0 auto;\n}\n\n.bar-title__breadcrumb {\n display: block;\n width: 100%;\n min-width: 0;\n}\n\n.bar-title__heading {\n display: block;\n width: auto;\n min-width: 0;\n user-select: none;\n}\n\n.bar-title__divider {\n width: var(--dimension-stroke-width-012);\n height: var(--dimension-size-250);\n margin-inline-start: var(--dimension-space-100);\n flex: 0 0 var(--dimension-stroke-width-012);\n background: var(--color-border-tertiary);\n}\n\n.bar-title__section-selector {\n display: flex;\n flex: 0 1 auto;\n min-width: 0;\n max-width: var(--dimension-panel-width-sm);\n}\n\n.bar-title__section-trigger {\n --ds-control-radius: var(--dimension-radius-half);\n\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--ds-control-gap);\n height: var(--ds-control-height);\n min-width: 0;\n max-width: 100%;\n padding-inline: var(--ds-control-padding-inline);\n border: none;\n border-radius: var(--ds-control-radius);\n box-sizing: border-box;\n background: transparent;\n color: var(--color-foreground-secondary);\n cursor: default;\n font-family: inherit;\n user-select: none;\n}\n\n.bar-title__section-label {\n min-width: 0;\n padding-inline: var(--ds-control-label-inset);\n}\n\n.bar-title__actions {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-100);\n flex: 0 0 auto;\n}\n\n.bar-title__description {\n display: block;\n min-width: 0;\n max-width: 100%;\n}\n\n:host(.bar-title-host--compact) .bar-title {\n height: var(--dimension-size-600);\n}\n\n:host(.bar-title-host--compact) .bar-title::after {\n inset-inline: 0;\n}\n\n:host(.bar-title-host--compact) .bar-title__inner {\n flex: 1 1 auto;\n height: 100%;\n min-width: 0;\n padding: 0;\n gap: 0;\n}\n\n:host(.bar-title-host--compact) .bar-title__row {\n height: 100%;\n align-items: center;\n}\n\n:host(.bar-title-host--compact) .bar-title__identity {\n height: var(--dimension-size-400);\n}\n\n:host(.bar-title-host--compact) .bar-title__divider {\n margin-inline-start: 0;\n}\n\n:host(.bar-title-host--compact) .bar-title__heading {\n display: inline-flex;\n flex: 1 1 auto;\n align-items: center;\n height: var(--dimension-size-400);\n padding-inline: calc(var(--ds-control-padding-inline) + var(--ds-control-label-inset));\n box-sizing: border-box;\n}\n",
566
+ "content": "@import '../../utils/radius.css';\n@import '../../utils/control-density.css';\n@import '../../utils/chrome-header.css';\n@import '../../utils/focus-ring.css';\n@import '../../utils/interaction-fill.css';\n\n:host {\n display: block;\n min-width: 0;\n box-sizing: border-box;\n background: var(--_shell-page-header-background, var(--color-background-primary));\n}\n\n:host([placement='shell-bar']) {\n --_shell-page-header-background: var(--color-background-secondary);\n}\n\n:host-context(ds-shell-app:not([gradient-preset='none']))[placement='shell-bar'],\n:host-context(ds-shell-app:not([gradient-preset='none']))[placement='shell-bar'] .bar-title,\n:host-context(ds-shell-app.shell-app--paper-texture)[placement='shell-bar'],\n:host-context(ds-shell-app.shell-app--paper-texture)[placement='shell-bar'] .bar-title {\n background: transparent;\n}\n\n:host([data-shell-page-syncing]) {\n visibility: hidden;\n}\n\n.bar-title {\n position: relative;\n min-width: 0;\n box-sizing: border-box;\n background: var(--_shell-page-header-background, var(--color-background-primary));\n}\n\n.bar-title::after {\n content: '';\n position: absolute;\n inset: auto var(--ds-bar-title-divider-inset, var(--dimension-space-400)) 0;\n height: var(--dimension-stroke-width-012);\n background: var(--color-border-tertiary);\n pointer-events: none;\n}\n\n.bar-title--divider-hidden::after {\n display: none;\n}\n\n.bar-title__inner {\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: var(--dimension-space-100);\n padding: var(--dimension-space-400);\n box-sizing: border-box;\n}\n\n.bar-title__row {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: var(--dimension-space-100);\n min-height: var(--dimension-size-400);\n min-width: 0;\n}\n\n:host(.bar-title-host--expanded) .bar-title__row {\n gap: var(--dimension-space-400);\n}\n\n.bar-title__leading {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n gap: var(--dimension-space-100);\n min-width: 0;\n}\n\n.bar-title__title-row {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-100);\n min-width: 0;\n min-height: var(--dimension-size-400);\n}\n\n.bar-title__identity {\n display: flex;\n flex: 0 1 auto;\n align-items: center;\n gap: var(--dimension-space-050);\n min-width: 0;\n}\n\n.bar-title__back,\n.bar-title__section-chevron {\n flex: 0 0 auto;\n}\n\n.bar-title__breadcrumb {\n display: block;\n width: 100%;\n min-width: 0;\n}\n\n.bar-title__heading {\n display: block;\n width: auto;\n min-width: 0;\n user-select: none;\n}\n\n.bar-title__divider {\n width: var(--dimension-stroke-width-012);\n height: var(--dimension-size-250);\n margin-inline-start: var(--dimension-space-100);\n flex: 0 0 var(--dimension-stroke-width-012);\n background: var(--color-border-tertiary);\n}\n\n.bar-title__section-selector {\n display: flex;\n flex: 0 1 auto;\n min-width: 0;\n max-width: var(--dimension-panel-width-sm);\n}\n\n.bar-title__section-trigger {\n --ds-control-radius: var(--dimension-radius-half);\n\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--ds-control-gap);\n height: var(--ds-control-height);\n min-width: 0;\n max-width: 100%;\n padding-inline: var(--ds-control-padding-inline);\n border: none;\n border-radius: var(--ds-control-radius);\n box-sizing: border-box;\n background: transparent;\n color: var(--color-foreground-secondary);\n cursor: default;\n font-family: inherit;\n user-select: none;\n}\n\n.bar-title__section-label {\n min-width: 0;\n padding-inline: var(--ds-control-label-inset);\n}\n\n.bar-title__actions {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-100);\n flex: 0 0 auto;\n}\n\n.bar-title__description {\n display: block;\n min-width: 0;\n max-width: 100%;\n}\n\n:host(.bar-title-host--compact) .bar-title {\n height: var(--dimension-size-600);\n}\n\n:host(.bar-title-host--compact) .bar-title::after {\n inset-inline: 0;\n}\n\n:host(.bar-title-host--compact) .bar-title__inner {\n flex: 1 1 auto;\n height: 100%;\n min-width: 0;\n padding: 0;\n gap: 0;\n}\n\n:host(.bar-title-host--compact) .bar-title__row {\n height: 100%;\n align-items: center;\n}\n\n:host(.bar-title-host--compact) .bar-title__identity {\n height: var(--dimension-size-400);\n}\n\n:host(.bar-title-host--compact) .bar-title__divider {\n margin-inline-start: 0;\n}\n\n:host(.bar-title-host--compact) .bar-title__heading {\n display: inline-flex;\n flex: 1 1 auto;\n align-items: center;\n height: var(--dimension-size-400);\n padding-inline: calc(var(--ds-control-padding-inline) + var(--ds-control-label-inset));\n box-sizing: border-box;\n}\n",
561
567
  "type": "registry:ui"
562
568
  },
563
569
  {
564
570
  "path": "src/wc/components/BarTitle/BarTitle.tsx",
565
- "content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Prop,\n State,\n Watch,\n} from '@stencil/core';\nimport type { BreadcrumbItem, BreadcrumbSelectDetail } from '../Breadcrumb/breadcrumb-types';\nimport type { MenuItemData, MenuSection } from '../Menu/menu-types';\nimport {\n isBarTitleDivider,\n type BarTitleActionConfigItem,\n type BarTitleActionItem,\n type BarTitleMenuAction,\n type BarTitlePrimaryAction,\n type BarTitleSection,\n type BarTitleSectionItem,\n type BarTitlePlacement,\n type BarTitleSplitAction,\n type BarTitleVariant,\n} from './bar-title-types';\nimport {\n barTitleActionIdIssues,\n barTitleChoiceSections,\n findBarTitleAction,\n overflowBarTitleActionSections,\n resolveBarTitleActionItems,\n visibleBarTitleActions,\n} from './bar-title-actions';\n\nlet nextBarTitleId = 0;\n\ntype FocusableButton = HTMLElement & {\n setFocus?: (segment?: 'primary' | 'menu') => Promise<void>;\n};\n\n@Component({\n tag: 'ds-bar-title',\n styleUrl: 'BarTitle.css',\n scoped: true,\n})\nexport class BarTitle {\n @Element() el!: HTMLElement;\n\n /** The page's single visible h1. */\n @Prop() heading!: string;\n\n /** Optional supporting copy shown in the expanded presentation. */\n @Prop() description: string = '';\n\n /** Show the leading page-level Back action. */\n @Prop({ attribute: 'show-back' }) showBack: boolean = false;\n\n /** Accessible name for the leading Back action. */\n @Prop() backAriaLabel: string = 'Back';\n\n /** Visible parent-page label used by the expanded breadcrumb when breadcrumbs is empty. */\n @Prop() backLabel: string = 'Back';\n\n /** Optional expanded ancestor path. Compact variants continue to use the Back action. */\n @Prop() breadcrumbs: BreadcrumbItem[] = [];\n\n /** Accessible name for the expanded breadcrumb navigation landmark. */\n @Prop() breadcrumbAriaLabel: string = 'Breadcrumb';\n\n /** Optional page sections exposed through the active-section menu. */\n @Prop() sections: BarTitleSectionItem[] = [];\n\n /** Id of the active page section. */\n @Prop() value: string = '';\n\n /** Accessible name for the page-section menu. */\n @Prop() sectionsAriaLabel: string = 'Change page section';\n\n /** The one highest-emphasis page action. */\n @Prop() primaryAction: BarTitlePrimaryAction | null = null;\n\n /** Secondary page actions shown in the overflow menu. Dividers create groups. */\n @Prop() actions: BarTitleActionItem[] = [];\n\n /**\n * Ordered page-header actions. When supplied, this replaces the legacy\n * primaryAction/actions presentation while preserving the same dsAction event.\n */\n @Prop() actionItems?: BarTitleActionConfigItem[];\n\n /** Accessible name for the page-actions menu. */\n @Prop() actionsAriaLabel: string = 'More page actions';\n\n /** Explicit visual/capacity variant. ShellPage owns automatic selection. */\n @Prop() variant: BarTitleVariant = 'expanded';\n\n /** Page-header placement, or compact application-shell bar placement. */\n @Prop({ reflect: true }) placement: BarTitlePlacement = 'page';\n\n /** Draw the page-title divider beneath the header. */\n @Prop() showDivider: boolean = true;\n\n /** Override divider visibility for compact and constrained variants. */\n @Prop() showCompactDivider?: boolean;\n\n /** Emitted when the leading Back action is activated. */\n @Event() dsBack!: EventEmitter<MouseEvent>;\n\n /** Emitted when an authored expanded breadcrumb item is activated. */\n @Event({ cancelable: true }) dsBreadcrumbSelect!: EventEmitter<BreadcrumbSelectDetail>;\n\n /** Emitted with the newly selected page-section id. */\n @Event() dsSectionChange!: EventEmitter<string>;\n\n /** Emitted with the activated primary or overflow action id. */\n @Event() dsAction!: EventEmitter<string>;\n\n @State() private sectionMenuOpen = false;\n @State() private openActionMenuId = '';\n @State() private surfaceActionMenuId = '';\n @State() private sectionSurfaceOpen = false;\n @State() private sectionMenuInitialFocusVisible = false;\n @State() private actionMenuInitialFocusVisible = false;\n\n private readonly instanceId = nextBarTitleId++;\n private readonly sectionMenuTriggerId = `bar-title-section-trigger-${this.instanceId}`;\n private readonly sectionMenuId = `bar-title-section-menu-${this.instanceId}`;\n private readonly actionMenuTriggerId = `bar-title-action-trigger-${this.instanceId}`;\n private readonly actionMenuId = `bar-title-action-menu-${this.instanceId}`;\n private sectionTriggerEl: HTMLButtonElement | null = null;\n private actionTriggerEl: FocusableButton | null = null;\n private actionTriggerEls = new Map<string, FocusableButton>();\n\n componentWillLoad() {\n if (this.el.closest('ds-shell-page')) {\n this.el.setAttribute('data-shell-page-syncing', '');\n }\n this.reportActionIdIssues();\n }\n\n @Watch('sections')\n handleSectionsChange() {\n if (!this.hasSectionSelector) {\n this.closeSectionMenu();\n this.sectionSurfaceOpen = false;\n }\n }\n\n @Watch('actions')\n @Watch('primaryAction')\n @Watch('actionItems')\n handleActionsChange() {\n this.reportActionIdIssues();\n if (!this.availableActionMenuIds.has(this.openActionMenuId)) this.closeActionMenu();\n if (!this.availableActionMenuIds.has(this.surfaceActionMenuId)) this.surfaceActionMenuId = '';\n }\n\n private reportActionIdIssues() {\n for (const issue of barTitleActionIdIssues(this.resolvedActionItems)) {\n console.warn(`[ds-bar-title] ${issue}`);\n }\n }\n\n private get compact(): boolean {\n return this.effectiveVariant !== 'expanded';\n }\n\n private get effectiveVariant(): BarTitleVariant {\n return this.placement === 'shell-bar' ? 'compact' : this.variant;\n }\n\n private get dividerVisible(): boolean {\n return this.compact ? (this.showCompactDivider ?? this.showDivider) : this.showDivider;\n }\n\n private get expandedBreadcrumbItems(): BreadcrumbItem[] {\n if (this.breadcrumbs.length > 0) return this.breadcrumbs;\n if (!this.showBack) return [];\n return [\n {\n id: 'back',\n label: this.backLabel,\n ariaLabel: this.backAriaLabel,\n },\n ];\n }\n\n /** Tooltip copy is fixed and generic; backAriaLabel/actionsAriaLabel stay page-specific. */\n private get backTooltipLabel(): string {\n return 'Go back';\n }\n\n private get primaryCollapsed(): boolean {\n return (\n this.actionItems === undefined &&\n this.effectiveVariant === 'constrained' &&\n this.primaryAction !== null &&\n (this.primaryAction.collapse ?? 'auto') === 'auto'\n );\n }\n\n private get selectableSections(): BarTitleSection[] {\n return this.sections.filter((item): item is BarTitleSection => !isBarTitleDivider(item));\n }\n\n private get hasSectionSelector(): boolean {\n return this.selectableSections.length > 1;\n }\n\n private get effectiveValue(): string {\n return this.selectableSections.some(section => section.id === this.value)\n ? this.value\n : (this.selectableSections[0]?.id ?? '');\n }\n\n private get selectedSectionLabel(): string {\n return this.selectableSections.find(section => section.id === this.effectiveValue)?.label ?? '';\n }\n\n private get sectionTriggerAriaLabel(): string {\n return `${this.sectionsAriaLabel}. Current section: ${this.selectedSectionLabel}`;\n }\n\n private groupsFromSections(): MenuSection[] {\n const groups: MenuSection[] = [];\n let items: MenuItemData[] = [];\n const commit = () => {\n if (items.length > 0) groups.push({ items });\n items = [];\n };\n\n for (const item of this.sections) {\n if (isBarTitleDivider(item)) {\n commit();\n } else {\n items.push({\n label: item.label,\n value: item.id,\n isSelected: item.id === this.effectiveValue,\n isInactive: item.isInactive,\n });\n }\n }\n commit();\n return groups;\n }\n\n private get sectionMenuSections(): MenuSection[] {\n return this.groupsFromSections();\n }\n\n private get resolvedActionItems(): BarTitleActionConfigItem[] {\n return resolveBarTitleActionItems(this.actionItems, this.primaryAction, this.actions);\n }\n\n private get visibleActions(): BarTitleActionConfigItem[] {\n return visibleBarTitleActions(this.resolvedActionItems, this.effectiveVariant);\n }\n\n private get actionMenuSections(): MenuSection[] {\n return overflowBarTitleActionSections(this.resolvedActionItems, this.effectiveVariant);\n }\n\n private get showActionMenuTrigger(): boolean {\n return this.actionMenuSections.length > 0;\n }\n\n private get availableActionMenuIds(): Set<string> {\n const ids = new Set<string>();\n if (this.showActionMenuTrigger) ids.add('__overflow');\n for (const item of this.visibleActions) {\n if (!isBarTitleDivider(item) && (item.type === 'menu' || item.type === 'split')) {\n ids.add(item.id);\n }\n }\n return ids;\n }\n\n private actionMenuDomId(id: string): string {\n const index = this.resolvedActionItems.findIndex(\n item => !isBarTitleDivider(item) && item.id === id\n );\n return `bar-title-action-menu-${this.instanceId}-${index}`;\n }\n\n private actionMenuAnchor(id: string): HTMLElement | undefined {\n if (id === '__overflow') return this.actionTriggerEl ?? undefined;\n const trigger = this.actionTriggerEls.get(id);\n if (!trigger) return undefined;\n const splitMenu = trigger.querySelector<HTMLElement>('.ds-button-split__menu');\n return splitMenu ?? trigger;\n }\n\n private menuSectionsForAction(action: BarTitleMenuAction | BarTitleSplitAction): MenuSection[] {\n return barTitleChoiceSections(action.choices);\n }\n\n private toggleSectionMenu = (event: MouseEvent) => {\n this.sectionMenuInitialFocusVisible = event.detail === 0;\n this.openActionMenuId = '';\n this.sectionMenuOpen = !this.sectionMenuOpen;\n if (this.sectionMenuOpen) this.sectionSurfaceOpen = true;\n };\n\n private closeSectionMenu = () => {\n this.sectionMenuOpen = false;\n };\n\n private handleSectionSelect = (event: CustomEvent<MenuItemData>) => {\n const id = String(event.detail?.value ?? '');\n const selected = this.selectableSections.find(section => section.id === id);\n if (!selected || selected.isInactive) return;\n this.closeSectionMenu();\n this.dsSectionChange.emit(id);\n requestAnimationFrame(() => this.sectionTriggerEl?.focus());\n };\n\n private toggleActionMenu = (id: string, event: MouseEvent) => {\n this.actionMenuInitialFocusVisible = event.detail === 0;\n this.sectionMenuOpen = false;\n this.openActionMenuId = this.openActionMenuId === id ? '' : id;\n if (this.openActionMenuId) this.surfaceActionMenuId = this.openActionMenuId;\n };\n\n private finishActionMenuClose = (event: CustomEvent<void>) => {\n const id = this.surfaceActionMenuId;\n const domId = id === '__overflow' ? this.actionMenuId : this.actionMenuDomId(id);\n if (this.openActionMenuId !== id && (event.target as HTMLElement).id === domId) {\n this.surfaceActionMenuId = '';\n }\n };\n\n private closeActionMenu = () => {\n this.openActionMenuId = '';\n };\n\n private handleActionSelect = (event: CustomEvent<MenuItemData>) => {\n const id = String(event.detail?.value ?? '');\n const action = findBarTitleAction(this.resolvedActionItems, id);\n if (!action || action.isInactive || ('isLoading' in action && action.isLoading)) return;\n const menuId = this.openActionMenuId;\n this.closeActionMenu();\n this.dsAction.emit(id);\n requestAnimationFrame(() => {\n if (menuId === '__overflow') {\n void this.actionTriggerEl?.setFocus?.();\n return;\n }\n const trigger = this.actionTriggerEls.get(menuId);\n if ('setFocus' in (trigger ?? {})) {\n const split = this.resolvedActionItems.some(\n item => !isBarTitleDivider(item) && item.id === menuId && item.type === 'split'\n );\n void trigger?.setFocus?.(split ? 'menu' : undefined);\n }\n });\n };\n\n private renderSectionSelector() {\n if (!this.hasSectionSelector) return null;\n return [\n <div class=\"bar-title__divider\" aria-hidden=\"true\" />,\n <div class=\"bar-title__section-selector\">\n <button\n ref={el => {\n this.sectionTriggerEl = el ?? null;\n }}\n id={this.sectionMenuTriggerId}\n class={{\n 'bar-title__section-trigger': true,\n 'bar-title__section-trigger--expanded': this.sectionMenuOpen,\n 'ds-interaction-fill--surface-open': this.sectionSurfaceOpen,\n 'ds-control--md': true,\n 'ds-focus-ring-inset': true,\n 'ds-interaction-fill': true,\n }}\n type=\"button\"\n aria-haspopup=\"menu\"\n aria-controls={this.sectionMenuId}\n aria-expanded={String(this.sectionMenuOpen)}\n aria-label={this.sectionTriggerAriaLabel}\n onClick={this.toggleSectionMenu}\n >\n <ds-text\n class=\"bar-title__section-label ds-interaction-fill__content\"\n as=\"span\"\n variant=\"text-body-medium\"\n emphasis\n color=\"primary\"\n lineTruncation={1}\n >\n {this.selectedSectionLabel}\n </ds-text>\n <ds-icon\n class=\"bar-title__section-chevron ds-interaction-fill__content\"\n name=\"ChevronUpDown\"\n size=\"md\"\n color=\"inherit\"\n aria-hidden=\"true\"\n />\n </button>\n </div>,\n ];\n }\n\n private renderVisibleAction(action: BarTitleActionConfigItem) {\n if (isBarTitleDivider(action) || action.type === 'overflow') return null;\n const appearance = action.appearance ?? (action.type === 'split' ? 'filled' : 'unfilled');\n const variant: 'icon' | 'icon-label' | 'label' =\n action.type === 'icon' ? 'icon' : action.icon ? 'icon-label' : 'label';\n const menu = action.type === 'menu';\n const menuId = menu ? this.actionMenuDomId(action.id) : undefined;\n\n if (action.type === 'split') {\n const splitMenuId = this.actionMenuDomId(action.id);\n const splitProps = {\n key: action.id,\n ref: (el: FocusableButton | undefined) => {\n if (el) this.actionTriggerEls.set(action.id, el);\n else this.actionTriggerEls.delete(action.id);\n },\n class: 'bar-title__action bar-title__split-action',\n split: true,\n variant,\n label: action.label,\n icon: action.icon ?? '',\n menuAriaLabel: action.menuAriaLabel,\n controls: splitMenuId,\n expanded: this.openActionMenuId === action.id,\n surfaceOpen: this.surfaceActionMenuId === action.id,\n size: 'md' as const,\n type: action.buttonType ?? 'button',\n isInactive: action.isInactive,\n isLoading: action.isLoading,\n onDsClick: () => this.dsAction.emit(action.id),\n onDsMenuClick: (event: CustomEvent<MouseEvent>) =>\n this.toggleActionMenu(action.id, event.detail),\n };\n return appearance === 'filled' ? (\n <ds-button-filled\n {...splitProps}\n intent={action.intent ?? 'brand'}\n contrast={action.contrast ?? 'bold'}\n />\n ) : (\n <ds-button-unfilled {...splitProps} />\n );\n }\n\n const buttonProps = {\n key: action.id,\n ref: (el: FocusableButton | undefined) => {\n if (el) this.actionTriggerEls.set(action.id, el);\n else this.actionTriggerEls.delete(action.id);\n },\n class: `bar-title__action bar-title__action--${action.type}${\n this.actionItems === undefined && action.id === this.primaryAction?.id\n ? ' bar-title__primary-action'\n : ''\n }`,\n variant,\n icon: action.icon ?? '',\n label: action.label,\n ariaLabel: action.ariaLabel ?? (action.type === 'icon' ? action.label : undefined),\n size: 'md' as const,\n type: action.buttonType ?? 'button',\n isInactive: action.isInactive,\n isLoading: action.isLoading,\n controls: menuId,\n expanded: menu ? this.openActionMenuId === action.id : undefined,\n surfaceOpen: menu && this.surfaceActionMenuId === action.id,\n hasMenu: menu,\n onDsClick: (event: CustomEvent<MouseEvent>) => {\n if (menu) this.toggleActionMenu(action.id, event.detail);\n else this.dsAction.emit(action.id);\n },\n };\n\n const button =\n appearance === 'filled' ? (\n <ds-button-filled\n {...buttonProps}\n intent={action.intent ?? 'brand'}\n contrast={action.contrast ?? 'bold'}\n />\n ) : (\n <ds-button-unfilled {...buttonProps} />\n );\n\n return action.type === 'icon' ? (\n <ds-tooltip key={action.id} label={action.label} side=\"bottom\" size=\"sm\">\n {button}\n </ds-tooltip>\n ) : (\n button\n );\n }\n\n private renderActions() {\n if (this.visibleActions.length === 0 && !this.showActionMenuTrigger) return null;\n\n return (\n <div class=\"bar-title__actions\">\n {this.visibleActions.map(action => this.renderVisibleAction(action))}\n {this.showActionMenuTrigger ? (\n <ds-tooltip label=\"Page options\" side=\"bottom\" size=\"sm\">\n <ds-button-unfilled\n ref={el => {\n this.actionTriggerEl = el ?? null;\n }}\n id={this.actionMenuTriggerId}\n class=\"bar-title__more-actions\"\n variant=\"icon\"\n icon=\"Ellipses\"\n aria-label={this.actionsAriaLabel}\n size=\"md\"\n activeFill={!this.compact}\n hasBorder={this.visibleActions.length > 0}\n haspopup=\"menu\"\n controls={this.actionMenuId}\n expanded={this.openActionMenuId === '__overflow'}\n surfaceOpen={this.surfaceActionMenuId === '__overflow'}\n onDsClick={(event: CustomEvent<MouseEvent>) =>\n this.toggleActionMenu('__overflow', event.detail)\n }\n />\n </ds-tooltip>\n ) : null}\n </div>\n );\n }\n\n private renderActionMenus() {\n return this.visibleActions.flatMap(action => {\n if (isBarTitleDivider(action) || (action.type !== 'menu' && action.type !== 'split')) {\n return [];\n }\n const menuLabel = action.menuAriaLabel ?? action.ariaLabel ?? action.label;\n return [\n <ds-menu\n key={action.id}\n id={this.actionMenuDomId(action.id)}\n class=\"bar-title__action-menu\"\n anchor={this.actionMenuAnchor(action.id)}\n align=\"end\"\n menuLabel={menuLabel}\n open={this.openActionMenuId === action.id}\n initialFocusVisible={this.actionMenuInitialFocusVisible}\n sections={this.menuSectionsForAction(action)}\n onDsClose={this.closeActionMenu}\n onDsAfterClose={this.finishActionMenuClose}\n onDsSelect={this.handleActionSelect}\n />,\n ];\n });\n }\n\n private handleBreadcrumbSelect = (event: CustomEvent<BreadcrumbSelectDetail>) => {\n if (this.breadcrumbs.length === 0) {\n this.dsBack.emit(event.detail.originalEvent);\n return;\n }\n const forwarded = this.dsBreadcrumbSelect.emit(event.detail);\n if (forwarded.defaultPrevented) event.preventDefault();\n };\n\n private renderBreadcrumb() {\n const items = this.expandedBreadcrumbItems;\n if (items.length === 0) return null;\n return (\n <ds-breadcrumb\n class=\"bar-title__breadcrumb\"\n items={items}\n ariaLabel={this.breadcrumbAriaLabel}\n onDsSelect={this.handleBreadcrumbSelect}\n />\n );\n }\n\n private renderBack() {\n if (!this.showBack) return null;\n return (\n <ds-tooltip label={this.backTooltipLabel} side=\"bottom\" size=\"sm\">\n <ds-button-unfilled\n class=\"bar-title__back\"\n variant=\"icon\"\n icon=\"ChevronLeft\"\n aria-label={this.backAriaLabel}\n size=\"md\"\n activeFill={false}\n hasBorder={false}\n onDsClick={(event: CustomEvent<MouseEvent>) => this.dsBack.emit(event.detail)}\n />\n </ds-tooltip>\n );\n }\n\n render() {\n const compact = this.compact;\n return (\n <Host\n class={{\n 'bar-title-host--compact': compact,\n 'bar-title-host--expanded': this.effectiveVariant === 'expanded',\n 'bar-title-host--constrained': this.effectiveVariant === 'constrained',\n 'bar-title-host--has-description': !!this.description,\n 'bar-title-host--has-back': this.showBack,\n 'bar-title-host--has-breadcrumb': !compact && this.expandedBreadcrumbItems.length > 0,\n 'bar-title-host--shell-bar': this.placement === 'shell-bar',\n }}\n >\n <div\n class={{\n 'bar-title': true,\n 'bar-title--divider-hidden': !this.dividerVisible,\n 'ds-chrome-header': compact,\n }}\n >\n <div class=\"bar-title__inner\">\n {!compact ? this.renderBreadcrumb() : null}\n <div class=\"bar-title__row\">\n <div class=\"bar-title__leading\">\n <div class=\"bar-title__title-row\" data-shell-page-header-anchor>\n <div class=\"bar-title__identity\">\n {compact ? this.renderBack() : null}\n <ds-text\n class=\"bar-title__heading ds-control--md\"\n variant={compact ? 'text-title-small' : 'text-title-medium'}\n emphasis\n color=\"primary\"\n as=\"h1\"\n lineTruncation={1}\n >\n {this.heading}\n </ds-text>\n </div>\n {this.renderSectionSelector()}\n </div>\n {this.description && !compact ? (\n <ds-text\n class=\"bar-title__description\"\n variant=\"text-body-small\"\n color=\"secondary\"\n lineTruncation={2}\n wrap=\"wrap\"\n as=\"p\"\n >\n {this.description}\n </ds-text>\n ) : null}\n </div>\n {this.renderActions()}\n </div>\n </div>\n </div>\n\n {this.hasSectionSelector ? (\n <ds-menu\n id={this.sectionMenuId}\n class=\"bar-title__section-menu\"\n anchorId={this.sectionMenuTriggerId}\n menuLabel={this.sectionsAriaLabel}\n open={this.sectionMenuOpen}\n initialFocusVisible={this.sectionMenuInitialFocusVisible}\n sections={this.sectionMenuSections}\n onDsClose={this.closeSectionMenu}\n onDsAfterClose={() => {\n if (!this.sectionMenuOpen) this.sectionSurfaceOpen = false;\n }}\n onDsSelect={this.handleSectionSelect}\n />\n ) : null}\n\n {this.renderActionMenus()}\n\n {this.showActionMenuTrigger ? (\n <ds-menu\n id={this.actionMenuId}\n class=\"bar-title__action-menu\"\n anchorId={this.actionMenuTriggerId}\n align=\"end\"\n menuLabel={this.actionsAriaLabel}\n open={this.openActionMenuId === '__overflow'}\n initialFocusVisible={this.actionMenuInitialFocusVisible}\n sections={this.actionMenuSections}\n onDsClose={this.closeActionMenu}\n onDsAfterClose={this.finishActionMenuClose}\n onDsSelect={this.handleActionSelect}\n />\n ) : null}\n </Host>\n );\n }\n}\n",
571
+ "content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Prop,\n State,\n Watch,\n} from '@stencil/core';\nimport type { BreadcrumbItem, BreadcrumbSelectDetail } from '../Breadcrumb/breadcrumb-types';\nimport type { MenuItemData, MenuSection } from '../Menu/menu-types';\nimport {\n type BarTitleActionConfigItem,\n type BarTitleActionItem,\n type BarTitlePrimaryAction,\n type BarTitleSectionItem,\n type BarTitlePlacement,\n type BarTitleVariant,\n} from './bar-title-types';\nimport {\n availableBarTitleActionMenuIds,\n barTitleActionIdIssues,\n findBarTitleAction,\n overflowBarTitleActionSections,\n resolveBarTitleActionItems,\n visibleBarTitleActions,\n} from './bar-title-actions';\nimport {\n type FocusableBarTitleButton,\n barTitleActionMenuDomId,\n renderBarTitleActionMenus,\n renderBarTitleActions,\n renderBarTitleBack,\n renderBarTitleOverflowMenu,\n renderBarTitleSectionTrigger,\n restoreBarTitleActionFocus,\n} from './bar-title-action-chrome';\nimport {\n barTitleSectionMenuSections,\n barTitleSectionTriggerAriaLabel,\n effectiveBarTitleSectionValue,\n selectableBarTitleSections,\n selectedBarTitleSectionLabel,\n} from './bar-title-sections';\n\nlet nextBarTitleId = 0;\n\n@Component({\n tag: 'ds-bar-title',\n styleUrl: 'BarTitle.css',\n scoped: true,\n})\nexport class BarTitle {\n @Element() el!: HTMLElement;\n\n /** The page's single visible h1. */\n @Prop() heading!: string;\n\n /** Optional supporting copy shown in the expanded presentation. */\n @Prop() description: string = '';\n\n /** Show the leading page-level Back action. */\n @Prop({ attribute: 'show-back' }) showBack: boolean = false;\n\n /** Accessible name for the leading Back action. */\n @Prop() backAriaLabel: string = 'Back';\n\n /** Visible parent-page label used by the expanded breadcrumb when breadcrumbs is empty. */\n @Prop() backLabel: string = 'Back';\n\n /** Optional expanded ancestor path. Compact variants continue to use the Back action. */\n @Prop() breadcrumbs: BreadcrumbItem[] = [];\n\n /** Accessible name for the expanded breadcrumb navigation landmark. */\n @Prop() breadcrumbAriaLabel: string = 'Breadcrumb';\n\n /** Optional page sections exposed through the active-section menu. */\n @Prop() sections: BarTitleSectionItem[] = [];\n\n /** Id of the active page section. */\n @Prop() value: string = '';\n\n /** Accessible name for the page-section menu. */\n @Prop() sectionsAriaLabel: string = 'Change page section';\n\n /** The one highest-emphasis page action. */\n @Prop() primaryAction: BarTitlePrimaryAction | null = null;\n\n /** Secondary page actions shown in the overflow menu. Dividers create groups. */\n @Prop() actions: BarTitleActionItem[] = [];\n\n /**\n * Ordered page-header actions. When supplied, this replaces the legacy\n * primaryAction/actions presentation while preserving the same dsAction event.\n */\n @Prop() actionItems?: BarTitleActionConfigItem[];\n\n /** Accessible name for the page-actions menu. */\n @Prop() actionsAriaLabel: string = 'More page actions';\n\n /** Explicit visual/capacity variant. ShellPage owns automatic selection. */\n @Prop() variant: BarTitleVariant = 'expanded';\n\n /** Page-header placement, or compact application-shell bar placement. */\n @Prop({ reflect: true }) placement: BarTitlePlacement = 'page';\n\n /** Draw the page-title divider beneath the header. */\n @Prop() showDivider: boolean = true;\n\n /** Override divider visibility for compact and constrained variants. */\n @Prop() showCompactDivider?: boolean;\n\n /** Emitted when the leading Back action is activated. */\n @Event() dsBack!: EventEmitter<MouseEvent>;\n\n /** Emitted when an authored expanded breadcrumb item is activated. */\n @Event({ cancelable: true }) dsBreadcrumbSelect!: EventEmitter<BreadcrumbSelectDetail>;\n\n /** Emitted with the newly selected page-section id. */\n @Event() dsSectionChange!: EventEmitter<string>;\n\n /** Emitted with the activated primary or overflow action id. */\n @Event() dsAction!: EventEmitter<string>;\n\n @State() private sectionMenuOpen = false;\n @State() private openActionMenuId = '';\n @State() private surfaceActionMenuId = '';\n @State() private sectionSurfaceOpen = false;\n @State() private sectionMenuInitialFocusVisible = false;\n @State() private actionMenuInitialFocusVisible = false;\n\n private readonly instanceId = nextBarTitleId++;\n private readonly sectionMenuTriggerId = `bar-title-section-trigger-${this.instanceId}`;\n private readonly sectionMenuId = `bar-title-section-menu-${this.instanceId}`;\n private readonly actionMenuTriggerId = `bar-title-action-trigger-${this.instanceId}`;\n private readonly actionMenuId = `bar-title-action-menu-${this.instanceId}`;\n private sectionTriggerEl: HTMLButtonElement | null = null;\n private actionTriggerEl: FocusableBarTitleButton | null = null;\n private actionTriggerEls = new Map<string, FocusableBarTitleButton>();\n\n componentWillLoad() {\n if (this.el.closest('ds-shell-page')) {\n this.el.setAttribute('data-shell-page-syncing', '');\n }\n this.reportActionIdIssues();\n }\n\n @Watch('sections')\n handleSectionsChange() {\n if (!this.hasSectionSelector) {\n this.closeSectionMenu();\n this.sectionSurfaceOpen = false;\n }\n }\n\n @Watch('actions')\n @Watch('primaryAction')\n @Watch('actionItems')\n handleActionsChange() {\n this.reportActionIdIssues();\n if (!this.availableActionMenuIds.has(this.openActionMenuId)) this.closeActionMenu();\n if (!this.availableActionMenuIds.has(this.surfaceActionMenuId)) this.surfaceActionMenuId = '';\n }\n\n private reportActionIdIssues() {\n for (const issue of barTitleActionIdIssues(this.resolvedActionItems)) {\n console.warn(`[ds-bar-title] ${issue}`);\n }\n }\n\n private get compact(): boolean {\n return this.effectiveVariant !== 'expanded';\n }\n\n private get effectiveVariant(): BarTitleVariant {\n return this.placement === 'shell-bar' ? 'compact' : this.variant;\n }\n\n private get dividerVisible(): boolean {\n return this.compact ? (this.showCompactDivider ?? this.showDivider) : this.showDivider;\n }\n\n private get expandedBreadcrumbItems(): BreadcrumbItem[] {\n if (this.breadcrumbs.length > 0) return this.breadcrumbs;\n if (!this.showBack) return [];\n return [\n {\n id: 'back',\n label: this.backLabel,\n ariaLabel: this.backAriaLabel,\n },\n ];\n }\n\n private get primaryCollapsed(): boolean {\n return (\n this.actionItems === undefined &&\n this.effectiveVariant === 'constrained' &&\n this.primaryAction !== null &&\n (this.primaryAction.collapse ?? 'auto') === 'auto'\n );\n }\n\n private get selectableSections() {\n return selectableBarTitleSections(this.sections);\n }\n\n private get hasSectionSelector(): boolean {\n return this.selectableSections.length > 1;\n }\n\n private get effectiveValue(): string {\n return effectiveBarTitleSectionValue(this.sections, this.value);\n }\n\n private get selectedSectionLabel(): string {\n return selectedBarTitleSectionLabel(this.sections, this.value);\n }\n\n private get sectionTriggerAriaLabel(): string {\n return barTitleSectionTriggerAriaLabel(this.sectionsAriaLabel, this.selectedSectionLabel);\n }\n\n private get sectionMenuSections() {\n return barTitleSectionMenuSections(this.sections, this.effectiveValue);\n }\n\n private get resolvedActionItems(): BarTitleActionConfigItem[] {\n return resolveBarTitleActionItems(this.actionItems, this.primaryAction, this.actions);\n }\n\n private get visibleActions(): BarTitleActionConfigItem[] {\n return visibleBarTitleActions(this.resolvedActionItems, this.effectiveVariant);\n }\n\n private get actionMenuSections(): MenuSection[] {\n return overflowBarTitleActionSections(this.resolvedActionItems, this.effectiveVariant);\n }\n\n private get showActionMenuTrigger(): boolean {\n return this.actionMenuSections.length > 0;\n }\n\n private get availableActionMenuIds(): Set<string> {\n return availableBarTitleActionMenuIds(this.visibleActions, this.showActionMenuTrigger);\n }\n\n private toggleSectionMenu = (event: MouseEvent) => {\n this.sectionMenuInitialFocusVisible = event.detail === 0;\n this.openActionMenuId = '';\n this.sectionMenuOpen = !this.sectionMenuOpen;\n if (this.sectionMenuOpen) this.sectionSurfaceOpen = true;\n };\n\n private closeSectionMenu = () => {\n this.sectionMenuOpen = false;\n };\n\n private handleSectionSelect = (event: CustomEvent<MenuItemData>) => {\n const id = String(event.detail?.value ?? '');\n const selected = this.selectableSections.find(section => section.id === id);\n if (!selected || selected.isInactive) return;\n this.closeSectionMenu();\n this.dsSectionChange.emit(id);\n requestAnimationFrame(() => this.sectionTriggerEl?.focus());\n };\n\n private toggleActionMenu = (id: string, event: MouseEvent) => {\n this.actionMenuInitialFocusVisible = event.detail === 0;\n this.sectionMenuOpen = false;\n this.openActionMenuId = this.openActionMenuId === id ? '' : id;\n if (this.openActionMenuId) this.surfaceActionMenuId = this.openActionMenuId;\n };\n\n private finishActionMenuClose = (event: CustomEvent<void>) => {\n const id = this.surfaceActionMenuId;\n const domId =\n id === '__overflow'\n ? this.actionMenuId\n : barTitleActionMenuDomId('bar-title', this.instanceId, this.resolvedActionItems, id);\n if (this.openActionMenuId !== id && (event.target as HTMLElement).id === domId) {\n this.surfaceActionMenuId = '';\n }\n };\n\n private closeActionMenu = () => {\n this.openActionMenuId = '';\n };\n\n private handleActionSelect = (event: CustomEvent<MenuItemData>) => {\n const id = String(event.detail?.value ?? '');\n const action = findBarTitleAction(this.resolvedActionItems, id);\n if (!action || action.isInactive || ('isLoading' in action && action.isLoading)) return;\n const menuId = this.openActionMenuId;\n this.closeActionMenu();\n this.dsAction.emit(id);\n requestAnimationFrame(() => {\n void restoreBarTitleActionFocus({\n menuId,\n overflowTrigger: this.actionTriggerEl,\n actionTriggers: this.actionTriggerEls,\n resolvedActionItems: this.resolvedActionItems,\n });\n });\n };\n\n private chromeActionOptions() {\n return {\n classPrefix: 'bar-title',\n compact: this.compact,\n visibleActions: this.visibleActions,\n showOverflowTrigger: this.showActionMenuTrigger,\n actionItems: this.actionItems,\n primaryAction: this.primaryAction,\n resolvedActionItems: this.resolvedActionItems,\n instanceId: this.instanceId,\n openActionMenuId: this.openActionMenuId,\n surfaceActionMenuId: this.surfaceActionMenuId,\n actionsAriaLabel: this.actionsAriaLabel,\n actionMenuTriggerId: this.actionMenuTriggerId,\n actionMenuId: this.actionMenuId,\n actionTriggers: this.actionTriggerEls,\n setOverflowTriggerEl: (el: FocusableBarTitleButton | null) => {\n this.actionTriggerEl = el;\n },\n toggleActionMenu: this.toggleActionMenu,\n emitAction: (id: string) => this.dsAction.emit(id),\n };\n }\n\n private renderSectionSelector() {\n if (!this.hasSectionSelector) return null;\n return [\n <div class=\"bar-title__divider\" aria-hidden=\"true\" />,\n renderBarTitleSectionTrigger({\n classPrefix: 'bar-title',\n triggerId: this.sectionMenuTriggerId,\n menuId: this.sectionMenuId,\n open: this.sectionMenuOpen,\n surfaceOpen: this.sectionSurfaceOpen,\n ariaLabel: this.sectionTriggerAriaLabel,\n selectedLabel: this.selectedSectionLabel,\n setTriggerEl: el => {\n this.sectionTriggerEl = el;\n },\n onToggle: this.toggleSectionMenu,\n }),\n ];\n }\n\n private renderActions() {\n return renderBarTitleActions(this.chromeActionOptions());\n }\n\n private renderActionMenus() {\n return renderBarTitleActionMenus({\n classPrefix: 'bar-title',\n visibleActions: this.visibleActions,\n instanceId: this.instanceId,\n resolvedActionItems: this.resolvedActionItems,\n openActionMenuId: this.openActionMenuId,\n actionMenuInitialFocusVisible: this.actionMenuInitialFocusVisible,\n overflowTrigger: this.actionTriggerEl,\n actionTriggers: this.actionTriggerEls,\n closeActionMenu: this.closeActionMenu,\n finishActionMenuClose: this.finishActionMenuClose,\n handleActionSelect: this.handleActionSelect,\n });\n }\n\n private handleBreadcrumbSelect = (event: CustomEvent<BreadcrumbSelectDetail>) => {\n if (this.breadcrumbs.length === 0) {\n this.dsBack.emit(event.detail.originalEvent);\n return;\n }\n const forwarded = this.dsBreadcrumbSelect.emit(event.detail);\n if (forwarded.defaultPrevented) event.preventDefault();\n };\n\n private renderBreadcrumb() {\n const items = this.expandedBreadcrumbItems;\n if (items.length === 0) return null;\n return (\n <ds-breadcrumb\n class=\"bar-title__breadcrumb\"\n items={items}\n ariaLabel={this.breadcrumbAriaLabel}\n onDsSelect={this.handleBreadcrumbSelect}\n />\n );\n }\n\n private renderBack() {\n return renderBarTitleBack({\n classPrefix: 'bar-title',\n showBack: this.showBack,\n backAriaLabel: this.backAriaLabel,\n onBack: event => this.dsBack.emit(event),\n });\n }\n\n render() {\n const compact = this.compact;\n return (\n <Host\n class={{\n 'bar-title-host--compact': compact,\n 'bar-title-host--expanded': this.effectiveVariant === 'expanded',\n 'bar-title-host--constrained': this.effectiveVariant === 'constrained',\n 'bar-title-host--has-description': !!this.description,\n 'bar-title-host--has-back': this.showBack,\n 'bar-title-host--has-breadcrumb': !compact && this.expandedBreadcrumbItems.length > 0,\n 'bar-title-host--shell-bar': this.placement === 'shell-bar',\n }}\n >\n <div\n class={{\n 'bar-title': true,\n 'bar-title--divider-hidden': !this.dividerVisible,\n 'ds-chrome-header': compact,\n }}\n >\n <div class=\"bar-title__inner\">\n {!compact ? this.renderBreadcrumb() : null}\n <div class=\"bar-title__row\">\n <div class=\"bar-title__leading\">\n <div class=\"bar-title__title-row\" data-shell-page-header-anchor>\n <div class=\"bar-title__identity\">\n {compact ? this.renderBack() : null}\n <ds-text\n class=\"bar-title__heading ds-control--md\"\n variant={compact ? 'text-title-small' : 'text-title-medium'}\n emphasis\n color=\"primary\"\n as=\"h1\"\n lineTruncation={1}\n >\n {this.heading}\n </ds-text>\n </div>\n {this.renderSectionSelector()}\n </div>\n {this.description && !compact ? (\n <ds-text\n class=\"bar-title__description\"\n variant=\"text-body-small\"\n color=\"secondary\"\n lineTruncation={2}\n wrap=\"wrap\"\n as=\"p\"\n >\n {this.description}\n </ds-text>\n ) : null}\n </div>\n {this.renderActions()}\n </div>\n </div>\n </div>\n\n {this.hasSectionSelector ? (\n <ds-menu\n id={this.sectionMenuId}\n class=\"bar-title__section-menu\"\n anchorId={this.sectionMenuTriggerId}\n menuLabel={this.sectionsAriaLabel}\n open={this.sectionMenuOpen}\n initialFocusVisible={this.sectionMenuInitialFocusVisible}\n sections={this.sectionMenuSections}\n onDsClose={this.closeSectionMenu}\n onDsAfterClose={() => {\n if (!this.sectionMenuOpen) this.sectionSurfaceOpen = false;\n }}\n onDsSelect={this.handleSectionSelect}\n />\n ) : null}\n\n {this.renderActionMenus()}\n\n {renderBarTitleOverflowMenu({\n classPrefix: 'bar-title',\n showOverflowTrigger: this.showActionMenuTrigger,\n actionMenuId: this.actionMenuId,\n actionMenuTriggerId: this.actionMenuTriggerId,\n actionsAriaLabel: this.actionsAriaLabel,\n open: this.openActionMenuId === '__overflow',\n actionMenuInitialFocusVisible: this.actionMenuInitialFocusVisible,\n actionMenuSections: this.actionMenuSections,\n closeActionMenu: this.closeActionMenu,\n finishActionMenuClose: this.finishActionMenuClose,\n handleActionSelect: this.handleActionSelect,\n })}\n </Host>\n );\n }\n}\n",
572
+ "type": "registry:ui"
573
+ },
574
+ {
575
+ "path": "src/wc/components/BarTitle/bar-title-action-chrome.tsx",
576
+ "content": "import { h } from '@stencil/core';\nimport type { MenuItemData, MenuSection } from '../Menu/menu-types';\nimport { isBarTitleDivider } from './bar-title-types';\nimport type {\n BarTitleActionConfigItem,\n BarTitleMenuAction,\n BarTitlePrimaryAction,\n BarTitleSplitAction,\n} from './bar-title-types';\nimport { barTitleChoiceSections } from './bar-title-actions';\n\nexport type FocusableBarTitleButton = HTMLElement & {\n setFocus?: (segment?: 'primary' | 'menu') => Promise<void>;\n};\n\nexport function barTitleActionMenuDomId(\n classPrefix: string,\n instanceId: number,\n resolvedActionItems: BarTitleActionConfigItem[],\n id: string\n): string {\n const index = resolvedActionItems.findIndex(item => !isBarTitleDivider(item) && item.id === id);\n return `${classPrefix}-action-menu-${instanceId}-${index}`;\n}\n\nexport function barTitleActionMenuAnchor(\n id: string,\n overflowTrigger: FocusableBarTitleButton | null,\n actionTriggers: Map<string, FocusableBarTitleButton>\n): HTMLElement | undefined {\n if (id === '__overflow') return overflowTrigger ?? undefined;\n const trigger = actionTriggers.get(id);\n if (!trigger) return undefined;\n const splitMenu = trigger.querySelector<HTMLElement>('.ds-button-split__menu');\n return splitMenu ?? trigger;\n}\n\nexport async function restoreBarTitleActionFocus(options: {\n menuId: string;\n overflowTrigger: FocusableBarTitleButton | null;\n actionTriggers: Map<string, FocusableBarTitleButton>;\n resolvedActionItems: BarTitleActionConfigItem[];\n}): Promise<void> {\n if (options.menuId === '__overflow') {\n await options.overflowTrigger?.setFocus?.();\n return;\n }\n const trigger = options.actionTriggers.get(options.menuId);\n if (!trigger || !('setFocus' in trigger)) return;\n const split = options.resolvedActionItems.some(\n item => !isBarTitleDivider(item) && item.id === options.menuId && item.type === 'split'\n );\n await trigger.setFocus?.(split ? 'menu' : undefined);\n}\n\nfunction className(prefix: string, part: string): string {\n return `${prefix}__${part}`;\n}\n\nexport function renderBarTitleBack(options: {\n classPrefix: string;\n showBack: boolean;\n backAriaLabel: string;\n onBack: (event: MouseEvent) => void;\n}) {\n if (!options.showBack) return null;\n return (\n <ds-tooltip label=\"Go back\" side=\"bottom\" size=\"sm\">\n <ds-button-unfilled\n class={className(options.classPrefix, 'back')}\n variant=\"icon\"\n icon=\"ChevronLeft\"\n aria-label={options.backAriaLabel}\n size=\"md\"\n activeFill={false}\n hasBorder={false}\n onDsClick={(event: CustomEvent<MouseEvent>) => options.onBack(event.detail)}\n />\n </ds-tooltip>\n );\n}\n\nexport function renderBarTitleSectionTrigger(options: {\n classPrefix: string;\n triggerId: string;\n menuId: string;\n open: boolean;\n surfaceOpen: boolean;\n ariaLabel: string;\n selectedLabel: string;\n setTriggerEl: (el: HTMLButtonElement | null) => void;\n onToggle: (event: MouseEvent) => void;\n}) {\n const prefix = options.classPrefix;\n return (\n <div class={className(prefix, 'section-selector')}>\n <button\n ref={el => {\n options.setTriggerEl((el as HTMLButtonElement | null) ?? null);\n }}\n id={options.triggerId}\n class={{\n [className(prefix, 'section-trigger')]: true,\n [`${className(prefix, 'section-trigger')}--expanded`]: options.open,\n 'ds-interaction-fill--surface-open': options.surfaceOpen,\n 'ds-control--md': true,\n 'ds-focus-ring-inset': true,\n 'ds-interaction-fill': true,\n }}\n type=\"button\"\n aria-haspopup=\"menu\"\n aria-controls={options.menuId}\n aria-expanded={String(options.open)}\n aria-label={options.ariaLabel}\n onClick={options.onToggle}\n >\n <ds-text\n class={`${className(prefix, 'section-label')} ds-interaction-fill__content`}\n as=\"span\"\n variant=\"text-body-medium\"\n emphasis\n color=\"primary\"\n lineTruncation={1}\n >\n {options.selectedLabel}\n </ds-text>\n <ds-icon\n class={`${className(prefix, 'section-chevron')} ds-interaction-fill__content`}\n name=\"ChevronUpDown\"\n size=\"md\"\n color=\"inherit\"\n aria-hidden=\"true\"\n />\n </button>\n </div>\n );\n}\n\nfunction renderVisibleAction(\n action: BarTitleActionConfigItem,\n options: {\n classPrefix: string;\n compact: boolean;\n actionItems: BarTitleActionConfigItem[] | undefined;\n primaryAction: BarTitlePrimaryAction | null;\n resolvedActionItems: BarTitleActionConfigItem[];\n instanceId: number;\n openActionMenuId: string;\n surfaceActionMenuId: string;\n actionTriggers: Map<string, FocusableBarTitleButton>;\n toggleActionMenu: (id: string, event: MouseEvent) => void;\n emitAction: (id: string) => void;\n }\n) {\n if (isBarTitleDivider(action) || action.type === 'overflow') return null;\n const appearance = action.appearance ?? (action.type === 'split' ? 'filled' : 'unfilled');\n const variant: 'icon' | 'icon-label' | 'label' =\n action.type === 'icon' ? 'icon' : action.icon ? 'icon-label' : 'label';\n const menu = action.type === 'menu';\n const menuId = menu\n ? barTitleActionMenuDomId(\n options.classPrefix,\n options.instanceId,\n options.resolvedActionItems,\n action.id\n )\n : undefined;\n const prefix = options.classPrefix;\n\n if (action.type === 'split') {\n const splitMenuId = barTitleActionMenuDomId(\n options.classPrefix,\n options.instanceId,\n options.resolvedActionItems,\n action.id\n );\n const splitProps = {\n key: action.id,\n ref: (el: FocusableBarTitleButton | undefined) => {\n if (el) options.actionTriggers.set(action.id, el);\n else options.actionTriggers.delete(action.id);\n },\n class: `${className(prefix, 'action')} ${className(prefix, 'split-action')}`,\n split: true,\n variant,\n label: action.label,\n icon: action.icon ?? '',\n menuAriaLabel: action.menuAriaLabel,\n controls: splitMenuId,\n expanded: options.openActionMenuId === action.id,\n surfaceOpen: options.surfaceActionMenuId === action.id,\n size: 'md' as const,\n type: action.buttonType ?? 'button',\n isInactive: action.isInactive,\n isLoading: action.isLoading,\n onDsClick: () => options.emitAction(action.id),\n onDsMenuClick: (event: CustomEvent<MouseEvent>) =>\n options.toggleActionMenu(action.id, event.detail),\n };\n return appearance === 'filled' ? (\n <ds-button-filled\n {...splitProps}\n intent={action.intent ?? 'brand'}\n contrast={action.contrast ?? 'bold'}\n />\n ) : (\n <ds-button-unfilled {...splitProps} />\n );\n }\n\n const buttonProps = {\n key: action.id,\n ref: (el: FocusableBarTitleButton | undefined) => {\n if (el) options.actionTriggers.set(action.id, el);\n else options.actionTriggers.delete(action.id);\n },\n class: `${className(prefix, 'action')} ${className(prefix, 'action')}--${action.type}${\n options.actionItems === undefined && action.id === options.primaryAction?.id\n ? ` ${className(prefix, 'primary-action')}`\n : ''\n }`,\n variant,\n icon: action.icon ?? '',\n label: action.label,\n ariaLabel: action.ariaLabel ?? (action.type === 'icon' ? action.label : undefined),\n size: 'md' as const,\n type: action.buttonType ?? 'button',\n isInactive: action.isInactive,\n isLoading: action.isLoading,\n controls: menuId,\n expanded: menu ? options.openActionMenuId === action.id : undefined,\n surfaceOpen: menu && options.surfaceActionMenuId === action.id,\n hasMenu: menu,\n onDsClick: (event: CustomEvent<MouseEvent>) => {\n if (menu) options.toggleActionMenu(action.id, event.detail);\n else options.emitAction(action.id);\n },\n };\n\n const button =\n appearance === 'filled' ? (\n <ds-button-filled\n {...buttonProps}\n intent={action.intent ?? 'brand'}\n contrast={action.contrast ?? 'bold'}\n />\n ) : (\n <ds-button-unfilled {...buttonProps} />\n );\n\n return action.type === 'icon' ? (\n <ds-tooltip key={action.id} label={action.label} side=\"bottom\" size=\"sm\">\n {button}\n </ds-tooltip>\n ) : (\n button\n );\n}\n\nexport function renderBarTitleActions(options: {\n classPrefix: string;\n compact: boolean;\n visibleActions: BarTitleActionConfigItem[];\n showOverflowTrigger: boolean;\n actionItems: BarTitleActionConfigItem[] | undefined;\n primaryAction: BarTitlePrimaryAction | null;\n resolvedActionItems: BarTitleActionConfigItem[];\n instanceId: number;\n openActionMenuId: string;\n surfaceActionMenuId: string;\n actionsAriaLabel: string;\n actionMenuTriggerId: string;\n actionMenuId: string;\n actionTriggers: Map<string, FocusableBarTitleButton>;\n setOverflowTriggerEl: (el: FocusableBarTitleButton | null) => void;\n toggleActionMenu: (id: string, event: MouseEvent) => void;\n emitAction: (id: string) => void;\n}) {\n if (options.visibleActions.length === 0 && !options.showOverflowTrigger) return null;\n const prefix = options.classPrefix;\n\n return (\n <div class={className(prefix, 'actions')}>\n {options.visibleActions.map(action => renderVisibleAction(action, options))}\n {options.showOverflowTrigger ? (\n <ds-tooltip label=\"Page options\" side=\"bottom\" size=\"sm\">\n <ds-button-unfilled\n ref={el => {\n options.setOverflowTriggerEl((el as FocusableBarTitleButton | null) ?? null);\n }}\n id={options.actionMenuTriggerId}\n class={className(prefix, 'more-actions')}\n variant=\"icon\"\n icon=\"Ellipses\"\n aria-label={options.actionsAriaLabel}\n size=\"md\"\n activeFill={!options.compact}\n hasBorder={options.visibleActions.length > 0}\n haspopup=\"menu\"\n controls={options.actionMenuId}\n expanded={options.openActionMenuId === '__overflow'}\n surfaceOpen={options.surfaceActionMenuId === '__overflow'}\n onDsClick={(event: CustomEvent<MouseEvent>) =>\n options.toggleActionMenu('__overflow', event.detail)\n }\n />\n </ds-tooltip>\n ) : null}\n </div>\n );\n}\n\nexport function renderBarTitleActionMenus(options: {\n classPrefix: string;\n visibleActions: BarTitleActionConfigItem[];\n instanceId: number;\n resolvedActionItems: BarTitleActionConfigItem[];\n openActionMenuId: string;\n actionMenuInitialFocusVisible: boolean;\n overflowTrigger: FocusableBarTitleButton | null;\n actionTriggers: Map<string, FocusableBarTitleButton>;\n closeActionMenu: () => void;\n finishActionMenuClose: (event: CustomEvent<void>) => void;\n handleActionSelect: (event: CustomEvent<MenuItemData>) => void;\n}) {\n const prefix = options.classPrefix;\n return options.visibleActions.flatMap(action => {\n if (isBarTitleDivider(action) || (action.type !== 'menu' && action.type !== 'split')) {\n return [];\n }\n const menuAction = action as BarTitleMenuAction | BarTitleSplitAction;\n const menuLabel = menuAction.menuAriaLabel ?? menuAction.ariaLabel ?? menuAction.label;\n return [\n <ds-menu\n key={action.id}\n id={barTitleActionMenuDomId(\n options.classPrefix,\n options.instanceId,\n options.resolvedActionItems,\n action.id\n )}\n class={className(prefix, 'action-menu')}\n anchor={barTitleActionMenuAnchor(\n action.id,\n options.overflowTrigger,\n options.actionTriggers\n )}\n align=\"end\"\n menuLabel={menuLabel}\n open={options.openActionMenuId === action.id}\n initialFocusVisible={options.actionMenuInitialFocusVisible}\n sections={barTitleChoiceSections(menuAction.choices)}\n onDsClose={options.closeActionMenu}\n onDsAfterClose={options.finishActionMenuClose}\n onDsSelect={options.handleActionSelect}\n />,\n ];\n });\n}\n\nexport function renderBarTitleOverflowMenu(options: {\n classPrefix: string;\n showOverflowTrigger: boolean;\n actionMenuId: string;\n actionMenuTriggerId: string;\n actionsAriaLabel: string;\n open: boolean;\n actionMenuInitialFocusVisible: boolean;\n actionMenuSections: MenuSection[];\n closeActionMenu: () => void;\n finishActionMenuClose: (event: CustomEvent<void>) => void;\n handleActionSelect: (event: CustomEvent<MenuItemData>) => void;\n}) {\n if (!options.showOverflowTrigger) return null;\n return (\n <ds-menu\n id={options.actionMenuId}\n class={className(options.classPrefix, 'action-menu')}\n anchorId={options.actionMenuTriggerId}\n align=\"end\"\n menuLabel={options.actionsAriaLabel}\n open={options.open}\n initialFocusVisible={options.actionMenuInitialFocusVisible}\n sections={options.actionMenuSections}\n onDsClose={options.closeActionMenu}\n onDsAfterClose={options.finishActionMenuClose}\n onDsSelect={options.handleActionSelect}\n />\n );\n}\n",
566
577
  "type": "registry:ui"
567
578
  },
568
579
  {
569
580
  "path": "src/wc/components/BarTitle/bar-title-actions.ts",
570
- "content": "import type { MenuItemData, MenuSection } from '../Menu/menu-types';\nimport type {\n BarTitleAction,\n BarTitleActionConfigItem,\n BarTitleActionItem,\n BarTitleDivider,\n BarTitleMenuChoiceItem,\n BarTitlePrimaryAction,\n BarTitleVariant,\n} from './bar-title-types';\n\nexport type BarTitleActionPresentation = BarTitleVariant | 'mobile';\n\nexport function isBarTitleActionDivider(\n item: BarTitleActionConfigItem | BarTitleMenuChoiceItem\n): item is BarTitleDivider {\n return 'type' in item && item.type === 'divider';\n}\n\nexport function resolveBarTitleActionItems(\n actionItems: BarTitleActionConfigItem[] | undefined,\n primaryAction: BarTitlePrimaryAction | null,\n actions: BarTitleActionItem[]\n): BarTitleActionConfigItem[] {\n if (actionItems !== undefined) return actionItems;\n\n const resolved: BarTitleActionConfigItem[] = [];\n if (primaryAction) {\n const { type, ...primary } = primaryAction;\n resolved.push({\n ...primary,\n type: 'button',\n buttonType: type,\n appearance: 'filled',\n });\n }\n for (const action of actions) {\n resolved.push(\n 'type' in action\n ? action\n : {\n ...action,\n type: 'overflow',\n }\n );\n }\n return resolved;\n}\n\nfunction nestedChoices(item: BarTitleActionConfigItem): BarTitleMenuChoiceItem[] | undefined {\n return item.type === 'menu' || item.type === 'split' ? item.choices : undefined;\n}\n\nexport function barTitleActionIdIssues(items: BarTitleActionConfigItem[]): string[] {\n const seen = new Set<string>();\n const issues: string[] = [];\n const visit = (id: string) => {\n if (!id.trim()) {\n issues.push('Action ids must be non-empty.');\n return;\n }\n if (seen.has(id)) {\n issues.push(`Duplicate action id: ${id}`);\n return;\n }\n seen.add(id);\n };\n\n for (const item of items) {\n if (isBarTitleActionDivider(item)) continue;\n visit(item.id);\n for (const choice of nestedChoices(item) ?? []) {\n if (!isBarTitleActionDivider(choice)) visit(choice.id);\n }\n }\n return issues;\n}\n\nfunction actionCollapses(\n item: BarTitleActionConfigItem,\n presentation: BarTitleActionPresentation\n): boolean {\n if (isBarTitleActionDivider(item) || item.type === 'overflow') return false;\n if (presentation === 'mobile') {\n if (item.type !== 'icon') return true;\n return (item.mobile ?? 'auto') === 'overflow';\n }\n return presentation === 'constrained' && (item.collapse ?? 'auto') === 'auto';\n}\n\nexport function visibleBarTitleActions(\n items: BarTitleActionConfigItem[],\n presentation: BarTitleActionPresentation\n): BarTitleActionConfigItem[] {\n const primary = items.find(\n item =>\n !isBarTitleActionDivider(item) &&\n item.type !== 'overflow' &&\n !actionCollapses(item, presentation)\n );\n return primary ? [primary] : [];\n}\n\nfunction menuItem(action: BarTitleAction, includeIcon: boolean = true): MenuItemData {\n return {\n label: action.label,\n value: action.id,\n icon: includeIcon ? action.icon : undefined,\n isInactive: action.isInactive,\n isDestructive: action.isDestructive,\n };\n}\n\nfunction appendChoices(\n output: Array<MenuItemData | BarTitleDivider>,\n choices: BarTitleMenuChoiceItem[],\n includeIcons: boolean = true\n) {\n for (const choice of choices) {\n output.push(isBarTitleActionDivider(choice) ? choice : menuItem(choice, includeIcons));\n }\n}\n\nfunction toSections(items: Array<MenuItemData | BarTitleDivider>): MenuSection[] {\n const sections: MenuSection[] = [];\n let current: MenuItemData[] = [];\n const commit = () => {\n if (current.length > 0) sections.push({ items: current });\n current = [];\n };\n\n for (const item of items) {\n if ('type' in item && item.type === 'divider') commit();\n else current.push(item as MenuItemData);\n }\n commit();\n return sections;\n}\n\nexport function barTitleChoiceSections(choices: BarTitleMenuChoiceItem[]): MenuSection[] {\n const rows: Array<MenuItemData | BarTitleDivider> = [];\n appendChoices(rows, choices);\n return toSections(rows);\n}\n\nexport function overflowBarTitleActionSections(\n items: BarTitleActionConfigItem[],\n presentation: BarTitleActionPresentation\n): MenuSection[] {\n const rows: Array<MenuItemData | BarTitleDivider> = [];\n const [visibleAction] = visibleBarTitleActions(items, presentation);\n\n for (const item of items) {\n if (isBarTitleActionDivider(item)) {\n rows.push(item);\n continue;\n }\n if (item.type === 'overflow') {\n rows.push(menuItem(item, false));\n continue;\n }\n if (item === visibleAction) continue;\n\n if (item.type === 'menu') {\n appendChoices(rows, item.choices, false);\n continue;\n }\n if (item.type === 'split') {\n rows.push(menuItem(item, false));\n appendChoices(rows, item.choices, false);\n continue;\n }\n rows.push(menuItem(item, false));\n }\n\n return toSections(rows);\n}\n\nexport function findBarTitleAction(\n items: BarTitleActionConfigItem[],\n id: string\n): BarTitleAction | undefined {\n for (const item of items) {\n if (isBarTitleActionDivider(item)) continue;\n if (item.id === id) return item;\n const choice = nestedChoices(item)?.find(\n candidate => !isBarTitleActionDivider(candidate) && candidate.id === id\n );\n if (choice && !isBarTitleActionDivider(choice)) return choice;\n }\n return undefined;\n}\n",
581
+ "content": "import type { MenuItemData, MenuSection } from '../Menu/menu-types';\nimport type {\n BarTitleAction,\n BarTitleActionConfigItem,\n BarTitleActionItem,\n BarTitleDivider,\n BarTitleMenuChoiceItem,\n BarTitlePrimaryAction,\n BarTitleVariant,\n} from './bar-title-types';\n\nexport type BarTitleActionPresentation = BarTitleVariant | 'mobile';\n\nexport function isBarTitleActionDivider(\n item: BarTitleActionConfigItem | BarTitleMenuChoiceItem\n): item is BarTitleDivider {\n return 'type' in item && item.type === 'divider';\n}\n\nexport function resolveBarTitleActionItems(\n actionItems: BarTitleActionConfigItem[] | undefined,\n primaryAction: BarTitlePrimaryAction | null,\n actions: BarTitleActionItem[]\n): BarTitleActionConfigItem[] {\n if (actionItems !== undefined) return actionItems;\n\n const resolved: BarTitleActionConfigItem[] = [];\n if (primaryAction) {\n const { type, ...primary } = primaryAction;\n resolved.push({\n ...primary,\n type: 'button',\n buttonType: type,\n appearance: 'filled',\n });\n }\n for (const action of actions) {\n resolved.push(\n 'type' in action\n ? action\n : {\n ...action,\n type: 'overflow',\n }\n );\n }\n return resolved;\n}\n\nfunction nestedChoices(item: BarTitleActionConfigItem): BarTitleMenuChoiceItem[] | undefined {\n return item.type === 'menu' || item.type === 'split' ? item.choices : undefined;\n}\n\nexport function barTitleActionIdIssues(items: BarTitleActionConfigItem[]): string[] {\n const seen = new Set<string>();\n const issues: string[] = [];\n const visit = (id: string) => {\n if (!id.trim()) {\n issues.push('Action ids must be non-empty.');\n return;\n }\n if (seen.has(id)) {\n issues.push(`Duplicate action id: ${id}`);\n return;\n }\n seen.add(id);\n };\n\n for (const item of items) {\n if (isBarTitleActionDivider(item)) continue;\n visit(item.id);\n for (const choice of nestedChoices(item) ?? []) {\n if (!isBarTitleActionDivider(choice)) visit(choice.id);\n }\n }\n return issues;\n}\n\nfunction actionCollapses(\n item: BarTitleActionConfigItem,\n presentation: BarTitleActionPresentation\n): boolean {\n if (isBarTitleActionDivider(item) || item.type === 'overflow') return false;\n if (presentation === 'mobile') {\n if (item.type !== 'icon') return true;\n return (item.mobile ?? 'auto') === 'overflow';\n }\n return presentation === 'constrained' && (item.collapse ?? 'auto') === 'auto';\n}\n\nexport function visibleBarTitleActions(\n items: BarTitleActionConfigItem[],\n presentation: BarTitleActionPresentation\n): BarTitleActionConfigItem[] {\n const primary = items.find(\n item =>\n !isBarTitleActionDivider(item) &&\n item.type !== 'overflow' &&\n !actionCollapses(item, presentation)\n );\n return primary ? [primary] : [];\n}\n\nfunction menuItem(action: BarTitleAction, includeIcon: boolean = true): MenuItemData {\n return {\n label: action.label,\n value: action.id,\n icon: includeIcon ? action.icon : undefined,\n isInactive: action.isInactive,\n isDestructive: action.isDestructive,\n };\n}\n\nfunction appendChoices(\n output: Array<MenuItemData | BarTitleDivider>,\n choices: BarTitleMenuChoiceItem[],\n includeIcons: boolean = true\n) {\n for (const choice of choices) {\n output.push(isBarTitleActionDivider(choice) ? choice : menuItem(choice, includeIcons));\n }\n}\n\nfunction toSections(items: Array<MenuItemData | BarTitleDivider>): MenuSection[] {\n const sections: MenuSection[] = [];\n let current: MenuItemData[] = [];\n const commit = () => {\n if (current.length > 0) sections.push({ items: current });\n current = [];\n };\n\n for (const item of items) {\n if ('type' in item && item.type === 'divider') commit();\n else current.push(item as MenuItemData);\n }\n commit();\n return sections;\n}\n\nexport function barTitleChoiceSections(choices: BarTitleMenuChoiceItem[]): MenuSection[] {\n const rows: Array<MenuItemData | BarTitleDivider> = [];\n appendChoices(rows, choices);\n return toSections(rows);\n}\n\nexport function overflowBarTitleActionSections(\n items: BarTitleActionConfigItem[],\n presentation: BarTitleActionPresentation\n): MenuSection[] {\n const rows: Array<MenuItemData | BarTitleDivider> = [];\n const [visibleAction] = visibleBarTitleActions(items, presentation);\n\n for (const item of items) {\n if (isBarTitleActionDivider(item)) {\n rows.push(item);\n continue;\n }\n if (item.type === 'overflow') {\n rows.push(menuItem(item, false));\n continue;\n }\n if (item === visibleAction) continue;\n\n if (item.type === 'menu') {\n appendChoices(rows, item.choices, false);\n continue;\n }\n if (item.type === 'split') {\n rows.push(menuItem(item, false));\n appendChoices(rows, item.choices, false);\n continue;\n }\n rows.push(menuItem(item, false));\n }\n\n return toSections(rows);\n}\n\nexport function availableBarTitleActionMenuIds(\n visibleActions: BarTitleActionConfigItem[],\n showOverflowTrigger: boolean\n): Set<string> {\n const ids = new Set<string>();\n if (showOverflowTrigger) ids.add('__overflow');\n for (const item of visibleActions) {\n if (!isBarTitleActionDivider(item) && (item.type === 'menu' || item.type === 'split')) {\n ids.add(item.id);\n }\n }\n return ids;\n}\n\nexport function findBarTitleAction(\n items: BarTitleActionConfigItem[],\n id: string\n): BarTitleAction | undefined {\n for (const item of items) {\n if (isBarTitleActionDivider(item)) continue;\n if (item.id === id) return item;\n const choice = nestedChoices(item)?.find(\n candidate => !isBarTitleActionDivider(candidate) && candidate.id === id\n );\n if (choice && !isBarTitleActionDivider(choice)) return choice;\n }\n return undefined;\n}\n",
582
+ "type": "registry:ui"
583
+ },
584
+ {
585
+ "path": "src/wc/components/BarTitle/bar-title-sections.ts",
586
+ "content": "import type { MenuItemData, MenuSection } from '../Menu/menu-types';\nimport type { TabItem } from '../TabGroup/tab-item-utils';\nimport {\n isBarTitleDivider,\n type BarTitleSection,\n type BarTitleSectionItem,\n} from './bar-title-types';\n\nexport function selectableBarTitleSections(sections: BarTitleSectionItem[]): BarTitleSection[] {\n return sections.filter((item): item is BarTitleSection => !isBarTitleDivider(item));\n}\n\nexport function effectiveBarTitleSectionValue(\n sections: BarTitleSectionItem[],\n value: string\n): string {\n const selectable = selectableBarTitleSections(sections);\n return selectable.some(section => section.id === value) ? value : (selectable[0]?.id ?? '');\n}\n\nexport function selectedBarTitleSectionLabel(\n sections: BarTitleSectionItem[],\n value: string\n): string {\n const selectedId = effectiveBarTitleSectionValue(sections, value);\n return (\n selectableBarTitleSections(sections).find(section => section.id === selectedId)?.label ?? ''\n );\n}\n\nexport function barTitleSectionTriggerAriaLabel(\n sectionsAriaLabel: string,\n selectedLabel: string\n): string {\n return `${sectionsAriaLabel}. Current section: ${selectedLabel}`;\n}\n\nexport function barTitleSectionMenuSections(\n sections: BarTitleSectionItem[],\n selectedId: string\n): MenuSection[] {\n const groups: MenuSection[] = [];\n let items: MenuItemData[] = [];\n const commit = () => {\n if (items.length > 0) groups.push({ items });\n items = [];\n };\n\n for (const item of sections) {\n if (isBarTitleDivider(item)) {\n commit();\n } else {\n items.push({\n label: item.label,\n value: item.id,\n isSelected: item.id === selectedId,\n isInactive: item.isInactive,\n });\n }\n }\n commit();\n return groups;\n}\n\nexport function barTitleSectionsToTabs(sections: BarTitleSectionItem[]): TabItem[] {\n return sections.map(item =>\n isBarTitleDivider(item)\n ? { type: 'divider' }\n : {\n id: item.id,\n label: item.label,\n isInactive: item.isInactive,\n }\n );\n}\n\nexport function barTitleSectionsDigest(sections: BarTitleSectionItem[] | undefined): string {\n return (sections ?? [])\n .map(item =>\n isBarTitleDivider(item) ? '|' : `${item.id}\\0${item.label}\\0${item.isInactive ? '1' : '0'}`\n )\n .join('\\n');\n}\n",
571
587
  "type": "registry:ui"
572
588
  },
573
589
  {
@@ -377,7 +377,7 @@
377
377
  "files": [
378
378
  {
379
379
  "path": "src/wc/components/BarWorkflow/BarWorkflow.css",
380
- "content": "@import '../../utils/control-density.css';\n@import '../../utils/chrome-layout.css';\n\n:host {\n display: block;\n min-width: 0;\n box-sizing: border-box;\n background: var(--color-background-bold-brand);\n}\n\n.bar-workflow {\n justify-content: space-between;\n height: var(--dimension-size-600);\n min-width: 0;\n background: var(--color-background-bold-brand);\n}\n\n.bar-workflow__identity {\n display: flex;\n flex: 1 1 auto;\n align-items: center;\n gap: var(--dimension-space-050);\n height: var(--dimension-size-400);\n min-width: 0;\n}\n\n.bar-workflow__heading {\n display: inline-flex;\n flex: 1 1 auto;\n align-items: center;\n width: auto;\n min-width: 0;\n height: var(--dimension-size-400);\n padding-inline: calc(var(--ds-control-padding-inline) + var(--ds-control-label-inset));\n box-sizing: border-box;\n user-select: none;\n}\n\n.bar-workflow__actions {\n display: flex;\n flex: 0 0 auto;\n align-items: center;\n gap: var(--dimension-space-100);\n}\n",
380
+ "content": "@import '../../utils/radius.css';\n@import '../../utils/control-density.css';\n@import '../../utils/chrome-layout.css';\n\n:host {\n display: block;\n min-width: 0;\n box-sizing: border-box;\n background: var(--color-background-bold-brand);\n}\n\n.bar-workflow {\n justify-content: space-between;\n height: var(--dimension-size-600);\n min-width: 0;\n background: var(--color-background-bold-brand);\n}\n\n.bar-workflow__identity {\n display: flex;\n flex: 1 1 auto;\n align-items: center;\n gap: var(--dimension-space-050);\n height: var(--dimension-size-400);\n min-width: 0;\n}\n\n.bar-workflow__heading {\n display: inline-flex;\n flex: 1 1 auto;\n align-items: center;\n width: auto;\n min-width: 0;\n height: var(--dimension-size-400);\n padding-inline: calc(var(--ds-control-padding-inline) + var(--ds-control-label-inset));\n box-sizing: border-box;\n user-select: none;\n}\n\n.bar-workflow__actions {\n display: flex;\n flex: 0 0 auto;\n align-items: center;\n gap: var(--dimension-space-100);\n}\n",
381
381
  "type": "registry:ui"
382
382
  },
383
383
  {
@@ -649,7 +649,7 @@
649
649
  "files": [
650
650
  {
651
651
  "path": "src/wc/components/ButtonFilled/ButtonFilled.css",
652
- "content": "@import '../../utils/button-base.css';\n@import '../../utils/button-split.css';\n\n:host {\n --ds-button-filled-bg: var(--color-background-bold-brand);\n --ds-button-filled-fg: var(--color-foreground-faint-brand);\n --ds-button-filled-border: var(--color-border-secondary);\n --ds-button-split-border: var(--color-border-secondary);\n --ds-button-split-divider: var(--color-border-on-bold-background-tertiary);\n}\n\n:host(.button-filled-host--contrast-strong) {\n --ds-button-split-divider: var(--color-border-on-strong-background-tertiary);\n}\n\n:host(.button-filled-host--contrast-medium) {\n --ds-button-split-divider: var(--color-border-on-medium-background-tertiary);\n}\n\n:host(.button-filled-host--contrast-faint) {\n --ds-button-split-divider: var(--color-border-tertiary);\n}\n\n:host(.button-filled-host--background-medium) {\n --ds-button-split-border: var(--color-border-on-medium-background-secondary);\n}\n\n:host(.button-filled-host--background-bold) {\n --ds-button-split-border: var(--color-border-on-bold-background-secondary);\n}\n\n:host(.button-filled-host--background-strong) {\n --ds-button-split-border: var(--color-border-on-strong-background-secondary);\n}\n\n:host(.button-filled-host--background-translucent) {\n --ds-button-split-border: var(--color-translucent-border-secondary);\n}\n\n:host(.button-filled-host--background-inverted) {\n --ds-button-split-border: var(--color-inverted-border-secondary);\n}\n\n:host(.button-filled-host--background-media) {\n --ds-button-split-border: var(--color-media-border-secondary);\n}\n\n:host(.button-filled-host--background-always-dark) {\n --ds-button-split-border: var(--color-always-dark-border-secondary);\n}\n\n.button-filled {\n background-color: var(--ds-button-filled-bg);\n color: var(--ds-button-filled-fg);\n transition:\n background-color var(--effect-motion-short-2),\n color var(--effect-motion-short-2);\n /* Hover/press: `.ds-interaction-fill` + contrast → `--on-*` (token overlay, not color-mix). */\n}\n\n/*\n * An open popup is a transient pressed state, not a selected state. ButtonFilled\n * deliberately has no active state, so hold the pressed wash for the popup's\n * complete rendered lifecycle instead of promoting any selected treatment.\n */\n/* Inset stroke via interaction-fill ::after (above hover and pressed washes). */\n.button-filled--bordered {\n --ds-interaction-border-color: var(--ds-button-filled-border);\n}\n\n/*\n * Parent-surface context affects only the optional inset border.\n * Intent + contrast continue to own fill, foreground, and interaction overlays.\n */\n.button-filled--background-medium {\n --ds-button-filled-border: var(--color-border-on-medium-background-secondary);\n}\n\n.button-filled--background-bold {\n --ds-button-filled-border: var(--color-border-on-bold-background-secondary);\n}\n\n.button-filled--background-strong {\n --ds-button-filled-border: var(--color-border-on-strong-background-secondary);\n}\n\n.button-filled--background-translucent {\n --ds-button-filled-border: var(--color-translucent-border-secondary);\n}\n\n.button-filled--background-inverted {\n --ds-button-filled-border: var(--color-inverted-border-secondary);\n}\n\n.button-filled--background-media {\n --ds-button-filled-border: var(--color-media-border-secondary);\n}\n\n.button-filled--background-always-dark {\n --ds-button-filled-border: var(--color-always-dark-border-secondary);\n}\n\n/* Inactive: `.ds-control-inactive` (50% opacity). */\n\n/* ── Intent + contrast (bold default) ─────────────────────────────────────── */\n\n.button-filled--intent-neutral {\n --ds-button-filled-bg: var(--color-background-bold-neutral);\n --ds-button-filled-fg: var(--color-foreground-faint-neutral);\n}\n\n.button-filled--intent-brand {\n --ds-button-filled-bg: var(--color-background-bold-brand);\n --ds-button-filled-fg: var(--color-foreground-faint-brand);\n}\n\n.button-filled--intent-ai {\n --ds-button-filled-bg: var(--color-background-bold-ai);\n --ds-button-filled-fg: var(--color-foreground-faint-ai);\n}\n\n.button-filled--intent-negative {\n --ds-button-filled-bg: var(--color-background-bold-negative);\n --ds-button-filled-fg: var(--color-foreground-faint-negative);\n}\n\n.button-filled--intent-warning {\n --ds-button-filled-bg: var(--color-background-bold-warning);\n --ds-button-filled-fg: var(--color-foreground-faint-warning);\n}\n\n.button-filled--intent-caution {\n --ds-button-filled-bg: var(--color-background-bold-caution);\n --ds-button-filled-fg: var(--color-foreground-faint-caution);\n}\n\n.button-filled--intent-positive {\n --ds-button-filled-bg: var(--color-background-bold-positive);\n --ds-button-filled-fg: var(--color-foreground-faint-positive);\n}\n\n.button-filled--intent-guide {\n --ds-button-filled-bg: var(--color-background-bold-guide);\n --ds-button-filled-fg: var(--color-foreground-faint-guide);\n}\n\n.button-filled--intent-walkthrough {\n --ds-button-filled-bg: var(--color-background-bold-walkthrough);\n --ds-button-filled-fg: var(--color-foreground-faint-walkthrough);\n}\n\n/* strong → medium fg */\n.button-filled--contrast-strong.button-filled--intent-neutral {\n --ds-button-filled-bg: var(--color-background-strong-neutral);\n --ds-button-filled-fg: var(--color-foreground-medium-neutral);\n}\n\n.button-filled--contrast-strong.button-filled--intent-brand {\n --ds-button-filled-bg: var(--color-background-strong-brand);\n --ds-button-filled-fg: var(--color-foreground-medium-brand);\n}\n\n.button-filled--contrast-strong.button-filled--intent-ai {\n --ds-button-filled-bg: var(--color-background-strong-ai);\n --ds-button-filled-fg: var(--color-foreground-medium-ai);\n}\n\n.button-filled--contrast-strong.button-filled--intent-negative {\n --ds-button-filled-bg: var(--color-background-strong-negative);\n --ds-button-filled-fg: var(--color-foreground-medium-negative);\n}\n\n.button-filled--contrast-strong.button-filled--intent-warning {\n --ds-button-filled-bg: var(--color-background-strong-warning);\n --ds-button-filled-fg: var(--color-foreground-medium-warning);\n}\n\n.button-filled--contrast-strong.button-filled--intent-caution {\n --ds-button-filled-bg: var(--color-background-strong-caution);\n --ds-button-filled-fg: var(--color-foreground-medium-caution);\n}\n\n.button-filled--contrast-strong.button-filled--intent-positive {\n --ds-button-filled-bg: var(--color-background-strong-positive);\n --ds-button-filled-fg: var(--color-foreground-medium-positive);\n}\n\n.button-filled--contrast-strong.button-filled--intent-guide {\n --ds-button-filled-bg: var(--color-background-strong-guide);\n --ds-button-filled-fg: var(--color-foreground-medium-guide);\n}\n\n.button-filled--contrast-strong.button-filled--intent-walkthrough {\n --ds-button-filled-bg: var(--color-background-strong-walkthrough);\n --ds-button-filled-fg: var(--color-foreground-medium-walkthrough);\n}\n\n/* medium → strong fg */\n.button-filled--contrast-medium.button-filled--intent-neutral {\n --ds-button-filled-bg: var(--color-background-medium-neutral);\n --ds-button-filled-fg: var(--color-foreground-strong-neutral);\n}\n\n.button-filled--contrast-medium.button-filled--intent-brand {\n --ds-button-filled-bg: var(--color-background-medium-brand);\n --ds-button-filled-fg: var(--color-foreground-strong-brand);\n}\n\n.button-filled--contrast-medium.button-filled--intent-ai {\n --ds-button-filled-bg: var(--color-background-medium-ai);\n --ds-button-filled-fg: var(--color-foreground-strong-ai);\n}\n\n.button-filled--contrast-medium.button-filled--intent-negative {\n --ds-button-filled-bg: var(--color-background-medium-negative);\n --ds-button-filled-fg: var(--color-foreground-strong-negative);\n}\n\n.button-filled--contrast-medium.button-filled--intent-warning {\n --ds-button-filled-bg: var(--color-background-medium-warning);\n --ds-button-filled-fg: var(--color-foreground-strong-warning);\n}\n\n.button-filled--contrast-medium.button-filled--intent-caution {\n --ds-button-filled-bg: var(--color-background-medium-caution);\n --ds-button-filled-fg: var(--color-foreground-strong-caution);\n}\n\n.button-filled--contrast-medium.button-filled--intent-positive {\n --ds-button-filled-bg: var(--color-background-medium-positive);\n --ds-button-filled-fg: var(--color-foreground-strong-positive);\n}\n\n.button-filled--contrast-medium.button-filled--intent-guide {\n --ds-button-filled-bg: var(--color-background-medium-guide);\n --ds-button-filled-fg: var(--color-foreground-strong-guide);\n}\n\n.button-filled--contrast-medium.button-filled--intent-walkthrough {\n --ds-button-filled-bg: var(--color-background-medium-walkthrough);\n --ds-button-filled-fg: var(--color-foreground-strong-walkthrough);\n}\n\n/* faint → bold fg */\n.button-filled--contrast-faint.button-filled--intent-neutral {\n --ds-button-filled-bg: var(--color-background-faint-neutral);\n --ds-button-filled-fg: var(--color-foreground-bold-neutral);\n}\n\n.button-filled--contrast-faint.button-filled--intent-brand {\n --ds-button-filled-bg: var(--color-background-faint-brand);\n --ds-button-filled-fg: var(--color-foreground-bold-brand);\n}\n\n.button-filled--contrast-faint.button-filled--intent-ai {\n --ds-button-filled-bg: var(--color-background-faint-ai);\n --ds-button-filled-fg: var(--color-foreground-bold-ai);\n}\n\n.button-filled--contrast-faint.button-filled--intent-negative {\n --ds-button-filled-bg: var(--color-background-faint-negative);\n --ds-button-filled-fg: var(--color-foreground-bold-negative);\n}\n\n.button-filled--contrast-faint.button-filled--intent-warning {\n --ds-button-filled-bg: var(--color-background-faint-warning);\n --ds-button-filled-fg: var(--color-foreground-bold-warning);\n}\n\n.button-filled--contrast-faint.button-filled--intent-caution {\n --ds-button-filled-bg: var(--color-background-faint-caution);\n --ds-button-filled-fg: var(--color-foreground-bold-caution);\n}\n\n.button-filled--contrast-faint.button-filled--intent-positive {\n --ds-button-filled-bg: var(--color-background-faint-positive);\n --ds-button-filled-fg: var(--color-foreground-bold-positive);\n}\n\n.button-filled--contrast-faint.button-filled--intent-guide {\n --ds-button-filled-bg: var(--color-background-faint-guide);\n --ds-button-filled-fg: var(--color-foreground-bold-guide);\n}\n\n.button-filled--contrast-faint.button-filled--intent-walkthrough {\n --ds-button-filled-bg: var(--color-background-faint-walkthrough);\n --ds-button-filled-fg: var(--color-foreground-bold-walkthrough);\n}\n\n/*\n * Bold, strong, and medium fills already communicate semantic intent. Their\n * on-background foreground tokens preserve AA text contrast across themes,\n * including brand pairs whose chromatic counterparts sit on the 4.5:1 edge.\n */\n.button-filled--contrast-bold.button-filled {\n --ds-button-filled-fg: var(--color-foreground-on-bold-background-primary);\n}\n\n.button-filled--contrast-strong.button-filled {\n --ds-button-filled-fg: var(--color-foreground-on-strong-background-primary);\n}\n\n.button-filled--contrast-medium.button-filled {\n --ds-button-filled-fg: var(--color-foreground-on-medium-background-primary);\n}\n",
652
+ "content": "@import '../../utils/radius.css';\n@import '../../utils/button-base.css';\n@import '../../utils/button-split.css';\n\n:host {\n --ds-button-filled-bg: var(--color-background-bold-brand);\n --ds-button-filled-fg: var(--color-foreground-faint-brand);\n --ds-button-filled-border: var(--color-border-secondary);\n --ds-button-split-border: var(--color-border-secondary);\n --ds-button-split-divider: var(--color-border-on-bold-background-tertiary);\n}\n\n:host(.button-filled-host--contrast-strong) {\n --ds-button-split-divider: var(--color-border-on-strong-background-tertiary);\n}\n\n:host(.button-filled-host--contrast-medium) {\n --ds-button-split-divider: var(--color-border-on-medium-background-tertiary);\n}\n\n:host(.button-filled-host--contrast-faint) {\n --ds-button-split-divider: var(--color-border-tertiary);\n}\n\n:host(.button-filled-host--background-medium) {\n --ds-button-split-border: var(--color-border-on-medium-background-secondary);\n}\n\n:host(.button-filled-host--background-bold) {\n --ds-button-split-border: var(--color-border-on-bold-background-secondary);\n}\n\n:host(.button-filled-host--background-strong) {\n --ds-button-split-border: var(--color-border-on-strong-background-secondary);\n}\n\n:host(.button-filled-host--background-translucent) {\n --ds-button-split-border: var(--color-translucent-border-secondary);\n}\n\n:host(.button-filled-host--background-inverted) {\n --ds-button-split-border: var(--color-inverted-border-secondary);\n}\n\n:host(.button-filled-host--background-media) {\n --ds-button-split-border: var(--color-media-border-secondary);\n}\n\n:host(.button-filled-host--background-always-dark) {\n --ds-button-split-border: var(--color-always-dark-border-secondary);\n}\n\n.button-filled {\n background-color: var(--ds-button-filled-bg);\n color: var(--ds-button-filled-fg);\n transition:\n background-color var(--effect-motion-short-2),\n color var(--effect-motion-short-2);\n /* Hover/press: `.ds-interaction-fill` + contrast → `--on-*` (token overlay, not color-mix). */\n}\n\n/*\n * An open popup is a transient pressed state, not a selected state. ButtonFilled\n * deliberately has no active state, so hold the pressed wash for the popup's\n * complete rendered lifecycle instead of promoting any selected treatment.\n */\n/* Inset stroke via interaction-fill ::after (above hover and pressed washes). */\n.button-filled--bordered {\n --ds-interaction-border-color: var(--ds-button-filled-border);\n}\n\n/*\n * Parent-surface context affects only the optional inset border.\n * Intent + contrast continue to own fill, foreground, and interaction overlays.\n */\n.button-filled--background-medium {\n --ds-button-filled-border: var(--color-border-on-medium-background-secondary);\n}\n\n.button-filled--background-bold {\n --ds-button-filled-border: var(--color-border-on-bold-background-secondary);\n}\n\n.button-filled--background-strong {\n --ds-button-filled-border: var(--color-border-on-strong-background-secondary);\n}\n\n.button-filled--background-translucent {\n --ds-button-filled-border: var(--color-translucent-border-secondary);\n}\n\n.button-filled--background-inverted {\n --ds-button-filled-border: var(--color-inverted-border-secondary);\n}\n\n.button-filled--background-media {\n --ds-button-filled-border: var(--color-media-border-secondary);\n}\n\n.button-filled--background-always-dark {\n --ds-button-filled-border: var(--color-always-dark-border-secondary);\n}\n\n/* Inactive: `.ds-control-inactive` (50% opacity). */\n\n/* ── Intent + contrast (bold default) ─────────────────────────────────────── */\n\n.button-filled--intent-neutral {\n --ds-button-filled-bg: var(--color-background-bold-neutral);\n --ds-button-filled-fg: var(--color-foreground-faint-neutral);\n}\n\n.button-filled--intent-brand {\n --ds-button-filled-bg: var(--color-background-bold-brand);\n --ds-button-filled-fg: var(--color-foreground-faint-brand);\n}\n\n.button-filled--intent-ai {\n --ds-button-filled-bg: var(--color-background-bold-ai);\n --ds-button-filled-fg: var(--color-foreground-faint-ai);\n}\n\n.button-filled--intent-negative {\n --ds-button-filled-bg: var(--color-background-bold-negative);\n --ds-button-filled-fg: var(--color-foreground-faint-negative);\n}\n\n.button-filled--intent-warning {\n --ds-button-filled-bg: var(--color-background-bold-warning);\n --ds-button-filled-fg: var(--color-foreground-faint-warning);\n}\n\n.button-filled--intent-caution {\n --ds-button-filled-bg: var(--color-background-bold-caution);\n --ds-button-filled-fg: var(--color-foreground-faint-caution);\n}\n\n.button-filled--intent-positive {\n --ds-button-filled-bg: var(--color-background-bold-positive);\n --ds-button-filled-fg: var(--color-foreground-faint-positive);\n}\n\n.button-filled--intent-guide {\n --ds-button-filled-bg: var(--color-background-bold-guide);\n --ds-button-filled-fg: var(--color-foreground-faint-guide);\n}\n\n.button-filled--intent-walkthrough {\n --ds-button-filled-bg: var(--color-background-bold-walkthrough);\n --ds-button-filled-fg: var(--color-foreground-faint-walkthrough);\n}\n\n/* strong → medium fg */\n.button-filled--contrast-strong.button-filled--intent-neutral {\n --ds-button-filled-bg: var(--color-background-strong-neutral);\n --ds-button-filled-fg: var(--color-foreground-medium-neutral);\n}\n\n.button-filled--contrast-strong.button-filled--intent-brand {\n --ds-button-filled-bg: var(--color-background-strong-brand);\n --ds-button-filled-fg: var(--color-foreground-medium-brand);\n}\n\n.button-filled--contrast-strong.button-filled--intent-ai {\n --ds-button-filled-bg: var(--color-background-strong-ai);\n --ds-button-filled-fg: var(--color-foreground-medium-ai);\n}\n\n.button-filled--contrast-strong.button-filled--intent-negative {\n --ds-button-filled-bg: var(--color-background-strong-negative);\n --ds-button-filled-fg: var(--color-foreground-medium-negative);\n}\n\n.button-filled--contrast-strong.button-filled--intent-warning {\n --ds-button-filled-bg: var(--color-background-strong-warning);\n --ds-button-filled-fg: var(--color-foreground-medium-warning);\n}\n\n.button-filled--contrast-strong.button-filled--intent-caution {\n --ds-button-filled-bg: var(--color-background-strong-caution);\n --ds-button-filled-fg: var(--color-foreground-medium-caution);\n}\n\n.button-filled--contrast-strong.button-filled--intent-positive {\n --ds-button-filled-bg: var(--color-background-strong-positive);\n --ds-button-filled-fg: var(--color-foreground-medium-positive);\n}\n\n.button-filled--contrast-strong.button-filled--intent-guide {\n --ds-button-filled-bg: var(--color-background-strong-guide);\n --ds-button-filled-fg: var(--color-foreground-medium-guide);\n}\n\n.button-filled--contrast-strong.button-filled--intent-walkthrough {\n --ds-button-filled-bg: var(--color-background-strong-walkthrough);\n --ds-button-filled-fg: var(--color-foreground-medium-walkthrough);\n}\n\n/* medium → strong fg */\n.button-filled--contrast-medium.button-filled--intent-neutral {\n --ds-button-filled-bg: var(--color-background-medium-neutral);\n --ds-button-filled-fg: var(--color-foreground-strong-neutral);\n}\n\n.button-filled--contrast-medium.button-filled--intent-brand {\n --ds-button-filled-bg: var(--color-background-medium-brand);\n --ds-button-filled-fg: var(--color-foreground-strong-brand);\n}\n\n.button-filled--contrast-medium.button-filled--intent-ai {\n --ds-button-filled-bg: var(--color-background-medium-ai);\n --ds-button-filled-fg: var(--color-foreground-strong-ai);\n}\n\n.button-filled--contrast-medium.button-filled--intent-negative {\n --ds-button-filled-bg: var(--color-background-medium-negative);\n --ds-button-filled-fg: var(--color-foreground-strong-negative);\n}\n\n.button-filled--contrast-medium.button-filled--intent-warning {\n --ds-button-filled-bg: var(--color-background-medium-warning);\n --ds-button-filled-fg: var(--color-foreground-strong-warning);\n}\n\n.button-filled--contrast-medium.button-filled--intent-caution {\n --ds-button-filled-bg: var(--color-background-medium-caution);\n --ds-button-filled-fg: var(--color-foreground-strong-caution);\n}\n\n.button-filled--contrast-medium.button-filled--intent-positive {\n --ds-button-filled-bg: var(--color-background-medium-positive);\n --ds-button-filled-fg: var(--color-foreground-strong-positive);\n}\n\n.button-filled--contrast-medium.button-filled--intent-guide {\n --ds-button-filled-bg: var(--color-background-medium-guide);\n --ds-button-filled-fg: var(--color-foreground-strong-guide);\n}\n\n.button-filled--contrast-medium.button-filled--intent-walkthrough {\n --ds-button-filled-bg: var(--color-background-medium-walkthrough);\n --ds-button-filled-fg: var(--color-foreground-strong-walkthrough);\n}\n\n/* faint → bold fg */\n.button-filled--contrast-faint.button-filled--intent-neutral {\n --ds-button-filled-bg: var(--color-background-faint-neutral);\n --ds-button-filled-fg: var(--color-foreground-bold-neutral);\n}\n\n.button-filled--contrast-faint.button-filled--intent-brand {\n --ds-button-filled-bg: var(--color-background-faint-brand);\n --ds-button-filled-fg: var(--color-foreground-bold-brand);\n}\n\n.button-filled--contrast-faint.button-filled--intent-ai {\n --ds-button-filled-bg: var(--color-background-faint-ai);\n --ds-button-filled-fg: var(--color-foreground-bold-ai);\n}\n\n.button-filled--contrast-faint.button-filled--intent-negative {\n --ds-button-filled-bg: var(--color-background-faint-negative);\n --ds-button-filled-fg: var(--color-foreground-bold-negative);\n}\n\n.button-filled--contrast-faint.button-filled--intent-warning {\n --ds-button-filled-bg: var(--color-background-faint-warning);\n --ds-button-filled-fg: var(--color-foreground-bold-warning);\n}\n\n.button-filled--contrast-faint.button-filled--intent-caution {\n --ds-button-filled-bg: var(--color-background-faint-caution);\n --ds-button-filled-fg: var(--color-foreground-bold-caution);\n}\n\n.button-filled--contrast-faint.button-filled--intent-positive {\n --ds-button-filled-bg: var(--color-background-faint-positive);\n --ds-button-filled-fg: var(--color-foreground-bold-positive);\n}\n\n.button-filled--contrast-faint.button-filled--intent-guide {\n --ds-button-filled-bg: var(--color-background-faint-guide);\n --ds-button-filled-fg: var(--color-foreground-bold-guide);\n}\n\n.button-filled--contrast-faint.button-filled--intent-walkthrough {\n --ds-button-filled-bg: var(--color-background-faint-walkthrough);\n --ds-button-filled-fg: var(--color-foreground-bold-walkthrough);\n}\n\n/*\n * Bold, strong, and medium fills already communicate semantic intent. Their\n * on-background foreground tokens preserve AA text contrast across themes,\n * including brand pairs whose chromatic counterparts sit on the 4.5:1 edge.\n */\n.button-filled--contrast-bold.button-filled {\n --ds-button-filled-fg: var(--color-foreground-on-bold-background-primary);\n}\n\n.button-filled--contrast-strong.button-filled {\n --ds-button-filled-fg: var(--color-foreground-on-strong-background-primary);\n}\n\n.button-filled--contrast-medium.button-filled {\n --ds-button-filled-fg: var(--color-foreground-on-medium-background-primary);\n}\n",
653
653
  "type": "registry:ui"
654
654
  },
655
655
  {
@@ -732,7 +732,7 @@
732
732
  "files": [
733
733
  {
734
734
  "path": "src/wc/components/ButtonUnfilled/ButtonUnfilled.css",
735
- "content": "@import '../../utils/button-base.css';\n@import '../../utils/button-split.css';\n@import '../../utils/table-caption-control.css';\n\n:host {\n --ds-button-unfilled-fg: var(--color-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-background-secondary);\n --ds-button-unfilled-border: var(--color-border-secondary);\n --ds-button-split-border: var(--color-border-secondary);\n --ds-button-split-divider: var(--color-border-tertiary);\n}\n\n:host(.button-unfilled-host--background-medium) {\n --ds-button-split-border: var(--color-border-on-medium-background-secondary);\n --ds-button-split-divider: var(--color-border-on-medium-background-tertiary);\n}\n\n:host(.button-unfilled-host--background-bold) {\n --ds-button-split-border: var(--color-border-on-bold-background-secondary);\n --ds-button-split-divider: var(--color-border-on-bold-background-tertiary);\n}\n\n:host(.button-unfilled-host--background-strong) {\n --ds-button-split-border: var(--color-border-on-strong-background-secondary);\n --ds-button-split-divider: var(--color-border-on-strong-background-tertiary);\n}\n\n:host(.button-unfilled-host--background-translucent) {\n --ds-button-split-border: var(--color-translucent-border-secondary);\n --ds-button-split-divider: var(--color-translucent-border-tertiary);\n}\n\n:host(.button-unfilled-host--background-inverted) {\n --ds-button-split-border: var(--color-inverted-border-secondary);\n --ds-button-split-divider: var(--color-inverted-border-tertiary);\n}\n\n:host(.button-unfilled-host--background-media) {\n --ds-button-split-border: var(--color-media-border-secondary);\n --ds-button-split-divider: var(--color-media-border-tertiary);\n}\n\n:host(.button-unfilled-host--background-always-dark) {\n --ds-button-split-border: var(--color-always-dark-border-secondary);\n --ds-button-split-divider: var(--color-always-dark-border-tertiary);\n}\n\n.button-unfilled {\n background: transparent;\n color: var(--ds-button-unfilled-fg);\n /* Hover/press/selected fill: `.ds-interaction-fill` (+ `--selected` / `--on-*`).\n Active foreground changes remain independent from popup-open pressed paint. */\n}\n\n.button-unfilled--active {\n color: var(--ds-button-unfilled-fg-active);\n}\n\n/*\n * An open popup is a transient pressed state, not a selected state. Keep the\n * pressed wash visible for the popup's complete rendered lifecycle without\n * promoting the resting foreground or selected fill.\n */\n/* Inset stroke via interaction-fill ::after (above selected / hover washes). */\n.button-unfilled--bordered {\n --ds-interaction-border-color: var(--ds-button-unfilled-border);\n}\n\n/* Inactive: `.ds-control-inactive` (50% opacity). */\n\n/* Foreground / chrome tokens per surface — interaction tokens come from util `--on-*`. */\n.button-unfilled--background-medium {\n --ds-button-unfilled-fg: var(--color-foreground-on-medium-background-secondary);\n --ds-button-unfilled-fg-active: var(--color-foreground-on-medium-background-primary);\n --ds-button-unfilled-dot-ring: var(--color-background-medium-neutral);\n --ds-button-unfilled-border: var(--color-border-on-medium-background-secondary);\n}\n\n.button-unfilled--background-bold {\n --ds-button-unfilled-fg: var(--color-foreground-on-bold-background-secondary);\n --ds-button-unfilled-fg-active: var(--color-foreground-on-bold-background-primary);\n --ds-button-unfilled-dot-ring: var(--color-background-bold-neutral);\n --ds-button-unfilled-border: var(--color-border-on-bold-background-secondary);\n}\n\n.button-unfilled--background-strong {\n --ds-button-unfilled-fg: var(--color-foreground-on-strong-background-secondary);\n --ds-button-unfilled-fg-active: var(--color-foreground-on-strong-background-primary);\n --ds-button-unfilled-dot-ring: var(--color-background-strong-neutral);\n --ds-button-unfilled-border: var(--color-border-on-strong-background-secondary);\n}\n\n.button-unfilled--background-translucent {\n --ds-button-unfilled-fg: var(--color-translucent-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-translucent-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-translucent-translucent);\n --ds-button-unfilled-border: var(--color-translucent-border-secondary);\n}\n\n.button-unfilled--background-inverted {\n --ds-button-unfilled-fg: var(--color-inverted-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-inverted-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-inverted-background);\n --ds-button-unfilled-border: var(--color-inverted-border-secondary);\n}\n\n.button-unfilled--background-media {\n --ds-button-unfilled-fg: var(--color-media-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-media-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-media-background);\n --ds-button-unfilled-border: var(--color-media-border-secondary);\n}\n\n.button-unfilled--on-always-dark {\n --ds-button-unfilled-fg: var(--color-always-dark-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-always-dark-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-always-dark-background);\n --ds-button-unfilled-border: var(--color-always-dark-border-secondary);\n}\n\n/*\n * After surface ring defaults: selected fill remaps the knock-out to the active\n * wash so the badge halo matches the selected background.\n */\n.button-unfilled.ds-interaction-fill--selected {\n --ds-button-unfilled-dot-ring: var(--ds-interaction-active);\n}\n",
735
+ "content": "@import '../../utils/radius.css';\n@import '../../utils/button-base.css';\n@import '../../utils/button-split.css';\n@import '../../utils/table-caption-control.css';\n\n:host {\n --ds-button-unfilled-fg: var(--color-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-background-secondary);\n --ds-button-unfilled-border: var(--color-border-secondary);\n --ds-button-split-border: var(--color-border-secondary);\n --ds-button-split-divider: var(--color-border-tertiary);\n}\n\n:host(.button-unfilled-host--background-medium) {\n --ds-button-split-border: var(--color-border-on-medium-background-secondary);\n --ds-button-split-divider: var(--color-border-on-medium-background-tertiary);\n}\n\n:host(.button-unfilled-host--background-bold) {\n --ds-button-split-border: var(--color-border-on-bold-background-secondary);\n --ds-button-split-divider: var(--color-border-on-bold-background-tertiary);\n}\n\n:host(.button-unfilled-host--background-strong) {\n --ds-button-split-border: var(--color-border-on-strong-background-secondary);\n --ds-button-split-divider: var(--color-border-on-strong-background-tertiary);\n}\n\n:host(.button-unfilled-host--background-translucent) {\n --ds-button-split-border: var(--color-translucent-border-secondary);\n --ds-button-split-divider: var(--color-translucent-border-tertiary);\n}\n\n:host(.button-unfilled-host--background-inverted) {\n --ds-button-split-border: var(--color-inverted-border-secondary);\n --ds-button-split-divider: var(--color-inverted-border-tertiary);\n}\n\n:host(.button-unfilled-host--background-media) {\n --ds-button-split-border: var(--color-media-border-secondary);\n --ds-button-split-divider: var(--color-media-border-tertiary);\n}\n\n:host(.button-unfilled-host--background-always-dark) {\n --ds-button-split-border: var(--color-always-dark-border-secondary);\n --ds-button-split-divider: var(--color-always-dark-border-tertiary);\n}\n\n.button-unfilled {\n background: transparent;\n color: var(--ds-button-unfilled-fg);\n /* Hover/press/selected fill: `.ds-interaction-fill` (+ `--selected` / `--on-*`).\n Active foreground changes remain independent from popup-open pressed paint. */\n}\n\n.button-unfilled--active {\n color: var(--ds-button-unfilled-fg-active);\n}\n\n/*\n * An open popup is a transient pressed state, not a selected state. Keep the\n * pressed wash visible for the popup's complete rendered lifecycle without\n * promoting the resting foreground or selected fill.\n */\n/* Inset stroke via interaction-fill ::after (above selected / hover washes). */\n.button-unfilled--bordered {\n --ds-interaction-border-color: var(--ds-button-unfilled-border);\n}\n\n/* Inactive: `.ds-control-inactive` (50% opacity). */\n\n/* Foreground / chrome tokens per surface — interaction tokens come from util `--on-*`. */\n.button-unfilled--background-medium {\n --ds-button-unfilled-fg: var(--color-foreground-on-medium-background-secondary);\n --ds-button-unfilled-fg-active: var(--color-foreground-on-medium-background-primary);\n --ds-button-unfilled-dot-ring: var(--color-background-medium-neutral);\n --ds-button-unfilled-border: var(--color-border-on-medium-background-secondary);\n}\n\n.button-unfilled--background-bold {\n --ds-button-unfilled-fg: var(--color-foreground-on-bold-background-secondary);\n --ds-button-unfilled-fg-active: var(--color-foreground-on-bold-background-primary);\n --ds-button-unfilled-dot-ring: var(--color-background-bold-neutral);\n --ds-button-unfilled-border: var(--color-border-on-bold-background-secondary);\n}\n\n.button-unfilled--background-strong {\n --ds-button-unfilled-fg: var(--color-foreground-on-strong-background-secondary);\n --ds-button-unfilled-fg-active: var(--color-foreground-on-strong-background-primary);\n --ds-button-unfilled-dot-ring: var(--color-background-strong-neutral);\n --ds-button-unfilled-border: var(--color-border-on-strong-background-secondary);\n}\n\n.button-unfilled--background-translucent {\n --ds-button-unfilled-fg: var(--color-translucent-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-translucent-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-translucent-translucent);\n --ds-button-unfilled-border: var(--color-translucent-border-secondary);\n}\n\n.button-unfilled--background-inverted {\n --ds-button-unfilled-fg: var(--color-inverted-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-inverted-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-inverted-background);\n --ds-button-unfilled-border: var(--color-inverted-border-secondary);\n}\n\n.button-unfilled--background-media {\n --ds-button-unfilled-fg: var(--color-media-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-media-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-media-background);\n --ds-button-unfilled-border: var(--color-media-border-secondary);\n}\n\n.button-unfilled--on-always-dark {\n --ds-button-unfilled-fg: var(--color-always-dark-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-always-dark-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-always-dark-background);\n --ds-button-unfilled-border: var(--color-always-dark-border-secondary);\n}\n\n/*\n * After surface ring defaults: selected fill remaps the knock-out to the active\n * wash so the badge halo matches the selected background.\n */\n.button-unfilled.ds-interaction-fill--selected {\n --ds-button-unfilled-dot-ring: var(--ds-interaction-active);\n}\n",
736
736
  "type": "registry:ui"
737
737
  },
738
738
  {
@@ -231,7 +231,7 @@
231
231
  "files": [
232
232
  {
233
233
  "path": "src/wc/components/CardChart/CardChart.css",
234
- "content": "@import '../../utils/chrome-header.css';\n@import '../../utils/forced-colors.css';\n\n:host {\n position: relative;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n width: var(--_card-chart-width);\n min-height: var(--_card-chart-min-height);\n max-width: 100%;\n overflow: visible;\n border: none;\n border-radius: var(--dimension-radius-125);\n background-color: var(--color-background-primary);\n box-shadow: var(--effect-shadow-elevated-sm);\n}\n\n:host::after {\n content: '';\n position: absolute;\n inset: 0;\n z-index: 4;\n border-radius: inherit;\n box-shadow: var(--effect-highlight-elevated-sm);\n pointer-events: none;\n}\n\n.card-chart__header {\n flex-shrink: 0;\n border-bottom: var(--dimension-stroke-width-012) solid var(--color-border-tertiary);\n}\n\n.card-chart__copy {\n align-items: center;\n}\n\nds-text.card-chart__title {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n -webkit-user-select: none;\n user-select: none;\n}\n\n.card-chart__actions {\n flex-shrink: 0;\n}\n\n.card-chart__actions:empty {\n display: none;\n}\n\n.card-chart__body,\n.card-chart__layout {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n box-sizing: border-box;\n min-height: 0;\n}\n\n.card-chart__body {\n border-radius: 0 0 var(--dimension-radius-125) var(--dimension-radius-125);\n background-color: var(--color-background-primary);\n}\n\n.card-chart__layout {\n height: 100%;\n gap: var(--dimension-space-200);\n}\n\n.card-chart__chart {\n display: grid;\n flex: 1 1 auto;\n align-items: center;\n box-sizing: border-box;\n min-height: 0;\n padding: var(--dimension-space-200) var(--dimension-space-200) 0 var(--dimension-space-200);\n}\n\n.card-chart__chart > * {\n width: 100%;\n min-width: 0;\n max-width: 100%;\n}\n\n.card-chart__chart > ds-chart {\n --ds-chart-container-height: 100%;\n}\n\n.card-chart__legend {\n flex: 0 0 auto;\n box-sizing: border-box;\n padding: 0 var(--dimension-space-100) var(--dimension-space-100) var(--dimension-space-100);\n}\n\n@media (forced-colors: active) {\n :host {\n box-shadow: none;\n outline: var(--dimension-stroke-width-012) solid var(--ds-forced-color-content);\n outline-offset: calc(-1 * var(--dimension-stroke-width-012));\n }\n\n :host::after {\n box-shadow: none;\n }\n}\n",
234
+ "content": "@import '../../utils/radius.css';\n@import '../../utils/chrome-header.css';\n@import '../../utils/forced-colors.css';\n\n:host {\n position: relative;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n width: var(--_card-chart-width);\n min-height: var(--_card-chart-min-height);\n max-width: 100%;\n overflow: visible;\n border: none;\n border-radius: var(--ds-radius-card);\n background-color: var(--color-background-primary);\n box-shadow: var(--effect-shadow-elevated-sm);\n}\n\n:host::after {\n content: '';\n position: absolute;\n inset: 0;\n z-index: 4;\n border-radius: inherit;\n box-shadow: var(--effect-highlight-elevated-sm);\n pointer-events: none;\n}\n\n.card-chart__header {\n flex-shrink: 0;\n border-bottom: var(--dimension-stroke-width-012) solid var(--color-border-tertiary);\n}\n\n.card-chart__copy {\n align-items: center;\n}\n\nds-text.card-chart__title {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n -webkit-user-select: none;\n user-select: none;\n}\n\n.card-chart__actions {\n flex-shrink: 0;\n}\n\n.card-chart__actions:empty {\n display: none;\n}\n\n.card-chart__body,\n.card-chart__layout {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n box-sizing: border-box;\n min-height: 0;\n}\n\n.card-chart__body {\n border-radius: 0 0 var(--ds-radius-card) var(--ds-radius-card);\n background-color: var(--color-background-primary);\n}\n\n.card-chart__layout {\n height: 100%;\n gap: var(--dimension-space-200);\n}\n\n.card-chart__chart {\n display: grid;\n flex: 1 1 auto;\n align-items: center;\n box-sizing: border-box;\n min-height: 0;\n padding: var(--dimension-space-200) var(--dimension-space-200) 0 var(--dimension-space-200);\n}\n\n.card-chart__chart > * {\n width: 100%;\n min-width: 0;\n max-width: 100%;\n}\n\n.card-chart__chart > ds-chart {\n --ds-chart-container-height: 100%;\n}\n\n.card-chart__legend {\n flex: 0 0 auto;\n box-sizing: border-box;\n padding: 0 var(--dimension-space-100) var(--dimension-space-100) var(--dimension-space-100);\n}\n\n@media (forced-colors: active) {\n :host {\n box-shadow: none;\n outline: var(--dimension-stroke-width-012) solid var(--ds-forced-color-content);\n outline-offset: calc(-1 * var(--dimension-stroke-width-012));\n }\n\n :host::after {\n box-shadow: none;\n }\n}\n",
235
235
  "type": "registry:ui"
236
236
  },
237
237
  {
@@ -243,7 +243,7 @@
243
243
  "files": [
244
244
  {
245
245
  "path": "src/wc/components/CardNavigation/CardNavigation.css",
246
- "content": "@import '../../utils/chrome-header.css';\n@import '../../utils/interaction-fill.css';\n@import '../../utils/focus-ring.css';\n@import '../../utils/forced-colors.css';\n\n:host {\n position: relative;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n width: var(--_card-navigation-width);\n max-width: 100%;\n overflow: hidden;\n border: none;\n border-radius: var(--dimension-radius-125);\n background-color: var(--color-background-primary);\n box-shadow: var(--effect-shadow-elevated-sm);\n}\n\n:host(.card-navigation--content) {\n min-height: var(--_card-navigation-min-height);\n}\n\n:host::after {\n content: '';\n position: absolute;\n inset: 0;\n z-index: 4;\n border-radius: inherit;\n box-shadow: var(--effect-highlight-elevated-sm);\n pointer-events: none;\n}\n\n.card-navigation__target {\n color: inherit;\n text-decoration: none;\n cursor: pointer;\n}\n\n.card-navigation__header {\n flex: 0 0 auto;\n border-radius: inherit;\n}\n\n:host(.card-navigation--content) .card-navigation__header {\n border-radius: var(--dimension-radius-125) var(--dimension-radius-125) 0 0;\n --ds-interaction-divider-width: var(--dimension-stroke-width-012);\n --ds-interaction-divider-color: var(--color-border-tertiary);\n}\n\n.card-navigation__copy {\n min-height: var(--dimension-size-400);\n justify-content: center;\n}\n\nds-text.card-navigation__title,\nds-text.card-navigation__description {\n overflow-wrap: anywhere;\n}\n\n.card-navigation__chevron {\n justify-content: center;\n width: var(--dimension-size-400);\n color: var(--color-foreground-secondary);\n}\n\n.card-navigation__body {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n min-height: 0;\n box-sizing: border-box;\n background-color: var(--color-background-primary);\n border-radius: 0 0 var(--dimension-radius-125) var(--dimension-radius-125);\n}\n\n@media (forced-colors: active) {\n :host {\n box-shadow: none;\n outline: var(--dimension-stroke-width-012) solid var(--ds-forced-color-content);\n outline-offset: calc(-1 * var(--dimension-stroke-width-012));\n }\n\n :host::after {\n box-shadow: none;\n }\n}\n",
246
+ "content": "@import '../../utils/radius.css';\n@import '../../utils/chrome-header.css';\n@import '../../utils/interaction-fill.css';\n@import '../../utils/focus-ring.css';\n@import '../../utils/forced-colors.css';\n\n:host {\n position: relative;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n width: var(--_card-navigation-width);\n max-width: 100%;\n overflow: hidden;\n border: none;\n border-radius: var(--ds-radius-card);\n background-color: var(--color-background-primary);\n box-shadow: var(--effect-shadow-elevated-sm);\n}\n\n:host(.card-navigation--content) {\n min-height: var(--_card-navigation-min-height);\n}\n\n:host::after {\n content: '';\n position: absolute;\n inset: 0;\n z-index: 4;\n border-radius: inherit;\n box-shadow: var(--effect-highlight-elevated-sm);\n pointer-events: none;\n}\n\n.card-navigation__target {\n color: inherit;\n text-decoration: none;\n cursor: pointer;\n}\n\n.card-navigation__header {\n flex: 0 0 auto;\n border-radius: inherit;\n}\n\n:host(.card-navigation--content) .card-navigation__header {\n border-radius: var(--ds-radius-card) var(--ds-radius-card) 0 0;\n --ds-interaction-divider-width: var(--dimension-stroke-width-012);\n --ds-interaction-divider-color: var(--color-border-tertiary);\n}\n\n.card-navigation__copy {\n min-height: var(--dimension-size-400);\n justify-content: center;\n}\n\nds-text.card-navigation__title,\nds-text.card-navigation__description {\n overflow-wrap: anywhere;\n}\n\n.card-navigation__chevron {\n justify-content: center;\n width: var(--dimension-size-400);\n color: var(--color-foreground-secondary);\n}\n\n.card-navigation__body {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n min-height: 0;\n box-sizing: border-box;\n background-color: var(--color-background-primary);\n border-radius: 0 0 var(--ds-radius-card) var(--ds-radius-card);\n}\n\n@media (forced-colors: active) {\n :host {\n box-shadow: none;\n outline: var(--dimension-stroke-width-012) solid var(--ds-forced-color-content);\n outline-offset: calc(-1 * var(--dimension-stroke-width-012));\n }\n\n :host::after {\n box-shadow: none;\n }\n}\n",
247
247
  "type": "registry:ui"
248
248
  },
249
249
  {
@@ -371,7 +371,7 @@
371
371
  "files": [
372
372
  {
373
373
  "path": "src/wc/components/CardOverview/CardOverview.css",
374
- "content": "@import '../../utils/interaction-fill.css';\n@import '../../utils/focus-ring.css';\n@import '../../utils/control-density.css';\n@import '../../utils/control-parts.css';\n@import '../../styles/control-elevation.css';\n\n:host {\n display: block;\n box-sizing: border-box;\n container-type: inline-size;\n -webkit-user-select: none;\n user-select: none;\n --ds-card-overview-metric-min: calc(var(--dimension-size-400) * 5);\n --ds-card-overview-divider-width: var(--dimension-stroke-width-012);\n --ds-card-overview-divider-color: var(--color-always-dark-border-tertiary);\n color: var(--color-always-dark-foreground-primary);\n}\n\n.card-overview__surface {\n width: 100%;\n min-width: 0;\n box-sizing: border-box;\n border-radius: var(--dimension-radius-125);\n background: var(--color-always-dark-background);\n color: var(--color-always-dark-foreground-primary);\n overflow-anchor: none;\n}\n\n.card-overview__clip {\n height: 100%;\n min-width: 0;\n border-radius: inherit;\n background: inherit;\n overflow: hidden;\n}\n\n:host(.card-overview--scroll-collapsing) {\n height: var(--ds-card-overview-expanded-height);\n pointer-events: none;\n}\n\n:host(.card-overview--scroll-collapsing) .card-overview__surface {\n height: var(--ds-card-overview-visible-height);\n pointer-events: auto;\n will-change: height;\n}\n\n:host(.card-overview--scroll-collapsing) .card-overview__body {\n transform: translateY(calc(-1 * var(--ds-card-overview-content-offset)));\n will-change: transform;\n}\n\n.card-overview__layout {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.card-overview__summary {\n position: relative;\n z-index: 1;\n flex: 0 0 auto;\n min-width: 0;\n background: var(--color-always-dark-background);\n}\n\n.card-overview__header {\n display: flex;\n position: relative;\n align-items: center;\n gap: 0;\n width: 100%;\n height: var(--dimension-size-600);\n min-width: 0;\n padding: var(--dimension-space-100);\n box-sizing: border-box;\n}\n\n.card-overview__header::after {\n content: '';\n position: absolute;\n inset-inline: 0;\n inset-block-end: 0;\n height: var(--ds-card-overview-divider-width);\n background: var(--ds-card-overview-divider-color);\n pointer-events: none;\n}\n\n.card-overview__period {\n display: flex;\n flex: 0 1 auto;\n align-items: center;\n gap: 0;\n min-width: 0;\n min-height: var(--ds-control-height);\n overflow: hidden;\n white-space: nowrap;\n}\n\n.card-overview__period-current,\n.card-overview__period-comparison {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.card-overview__period-current {\n display: flex;\n flex: 0 1 auto;\n align-items: center;\n}\n\n.card-overview__period-fixed {\n flex: 0 1 auto;\n min-width: 0;\n}\n\n.card-overview__period-comparison {\n flex: 0 0 auto;\n}\n\n.card-overview__filter {\n display: flex;\n flex: 0 1 auto;\n align-items: center;\n min-width: 0;\n}\n\n.card-overview__body {\n display: flex;\n position: relative;\n z-index: 0;\n flex-direction: column;\n min-width: 0;\n transform: translateY(0);\n}\n\n.card-overview__metrics {\n display: grid;\n flex: 1 1 auto;\n grid-template-columns: repeat(\n var(--ds-card-overview-grid-columns, auto-fit),\n minmax(min(100%, var(--ds-card-overview-metric-min)), 1fr)\n );\n align-content: stretch;\n min-width: 0;\n overflow: clip;\n}\n\n.card-overview__score,\n.card-overview__metric {\n display: flex;\n min-width: 0;\n padding: var(--dimension-space-100);\n box-shadow:\n var(--ds-card-overview-divider-width) 0 0 0 var(--ds-card-overview-divider-color),\n 0 var(--ds-card-overview-divider-width) 0 0 var(--ds-card-overview-divider-color);\n box-sizing: border-box;\n}\n\n.card-overview__score-content,\n.card-overview__metric-action {\n display: flex;\n flex: 1 1 auto;\n min-width: 0;\n padding: var(--dimension-space-075);\n border-radius: var(--dimension-radius-025);\n box-sizing: border-box;\n}\n\n.card-overview__score-content {\n align-items: center;\n justify-content: flex-start;\n /*\n * Sits the fill 16px from the cell's left edge, on top of the 8px cell padding.\n * Metric cells keep the shared inner inset because their content is text; the\n * fill is a filled box and needs more room around it to read as inset.\n */\n padding-inline-start: var(--dimension-space-100);\n gap: var(--dimension-space-100);\n}\n\n/* Mirrors .card-overview__metric-content so labels and trends line up across cells. */\n.card-overview__score-copy {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n justify-content: center;\n align-items: flex-start;\n gap: var(--dimension-space-050);\n min-width: 0;\n}\n\n.card-overview__score-label-spacer {\n visibility: hidden;\n}\n\n.card-overview__score-content--error {\n align-items: center;\n justify-content: center;\n /* No fill to align, so this box keeps the metric cells' inner inset. */\n padding-inline-start: var(--dimension-space-075);\n border: var(--ds-card-overview-divider-width) solid var(--color-always-dark-border-tertiary);\n background: transparent;\n color: var(--color-always-dark-foreground-negative);\n text-align: center;\n}\n\n.card-overview__metric-action {\n flex-direction: column;\n justify-content: center;\n gap: var(--dimension-space-050);\n}\n\n.card-overview__metric-action[role='button'] {\n cursor: default;\n}\n\n.card-overview__metric-content {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n justify-content: center;\n gap: var(--dimension-space-050);\n min-width: 0;\n}\n\n.card-overview__metric-label {\n min-width: 0;\n}\n\n.card-overview__metric-figure {\n display: flex;\n align-items: baseline;\n gap: var(--dimension-space-050);\n min-width: 0;\n}\n\n.card-overview__trend {\n flex: 0 0 auto;\n white-space: nowrap;\n}\n\n:host(.card-overview--stacked) .card-overview__metrics {\n grid-template-columns: minmax(0, 1fr);\n}\n\n:host(.card-overview--compact) {\n height: var(--dimension-size-600);\n min-height: var(--dimension-size-600);\n}\n\n:host(.card-overview--compact) .card-overview__surface,\n:host(.card-overview--compact) .card-overview__clip,\n:host(.card-overview--compact) .card-overview__layout,\n:host(.card-overview--compact) .card-overview__summary {\n width: 100%;\n height: 100%;\n}\n\n:host(.card-overview--compact) .card-overview__header::after {\n display: none;\n}\n\n@media (forced-colors: active) {\n .card-overview__header::after {\n background: var(--ds-forced-color-content);\n }\n\n .card-overview__score-content--error {\n border: var(--dimension-stroke-width-012) solid var(--ds-forced-color-content);\n background: var(--ds-forced-color-surface);\n color: var(--ds-forced-color-content);\n }\n}\n",
374
+ "content": "@import '../../utils/interaction-fill.css';\n@import '../../utils/focus-ring.css';\n@import '../../utils/radius.css';\n@import '../../utils/control-density.css';\n@import '../../utils/control-parts.css';\n@import '../../styles/control-elevation.css';\n\n:host {\n display: block;\n box-sizing: border-box;\n container-type: inline-size;\n -webkit-user-select: none;\n user-select: none;\n --ds-card-overview-metric-min: calc(var(--dimension-size-400) * 5);\n --ds-card-overview-divider-width: var(--dimension-stroke-width-012);\n --ds-card-overview-divider-color: var(--color-always-dark-border-tertiary);\n color: var(--color-always-dark-foreground-primary);\n}\n\n.card-overview__surface {\n width: 100%;\n min-width: 0;\n box-sizing: border-box;\n border-radius: var(--ds-radius-card);\n background: var(--color-always-dark-background);\n color: var(--color-always-dark-foreground-primary);\n overflow-anchor: none;\n}\n\n.card-overview__clip {\n height: 100%;\n min-width: 0;\n border-radius: inherit;\n background: inherit;\n overflow: hidden;\n}\n\n:host(.card-overview--scroll-collapsing) {\n height: var(--ds-card-overview-expanded-height);\n pointer-events: none;\n}\n\n:host(.card-overview--scroll-collapsing) .card-overview__surface {\n height: var(--ds-card-overview-visible-height);\n pointer-events: auto;\n will-change: height;\n}\n\n:host(.card-overview--scroll-collapsing) .card-overview__body {\n transform: translateY(calc(-1 * var(--ds-card-overview-content-offset)));\n will-change: transform;\n}\n\n.card-overview__layout {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.card-overview__summary {\n position: relative;\n z-index: 1;\n flex: 0 0 auto;\n min-width: 0;\n background: var(--color-always-dark-background);\n}\n\n.card-overview__header {\n display: flex;\n position: relative;\n align-items: center;\n gap: 0;\n width: 100%;\n height: var(--dimension-size-600);\n min-width: 0;\n padding: var(--dimension-space-100);\n box-sizing: border-box;\n}\n\n.card-overview__header::after {\n content: '';\n position: absolute;\n inset-inline: 0;\n inset-block-end: 0;\n height: var(--ds-card-overview-divider-width);\n background: var(--ds-card-overview-divider-color);\n pointer-events: none;\n}\n\n.card-overview__period {\n display: flex;\n flex: 0 1 auto;\n align-items: center;\n gap: 0;\n min-width: 0;\n min-height: var(--ds-control-height);\n overflow: hidden;\n white-space: nowrap;\n}\n\n.card-overview__period-current,\n.card-overview__period-comparison {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.card-overview__period-current {\n display: flex;\n flex: 0 1 auto;\n align-items: center;\n}\n\n.card-overview__period-fixed {\n flex: 0 1 auto;\n min-width: 0;\n}\n\n.card-overview__period-comparison {\n flex: 0 0 auto;\n}\n\n.card-overview__filter {\n display: flex;\n flex: 0 1 auto;\n align-items: center;\n min-width: 0;\n}\n\n.card-overview__body {\n display: flex;\n position: relative;\n z-index: 0;\n flex-direction: column;\n min-width: 0;\n transform: translateY(0);\n}\n\n.card-overview__metrics {\n display: grid;\n flex: 1 1 auto;\n grid-template-columns: repeat(\n var(--ds-card-overview-grid-columns, auto-fit),\n minmax(min(100%, var(--ds-card-overview-metric-min)), 1fr)\n );\n align-content: stretch;\n min-width: 0;\n overflow: clip;\n}\n\n.card-overview__score,\n.card-overview__metric {\n display: flex;\n min-width: 0;\n padding: var(--dimension-space-100);\n box-shadow:\n var(--ds-card-overview-divider-width) 0 0 0 var(--ds-card-overview-divider-color),\n 0 var(--ds-card-overview-divider-width) 0 0 var(--ds-card-overview-divider-color);\n box-sizing: border-box;\n}\n\n.card-overview__score-content,\n.card-overview__metric-action {\n display: flex;\n flex: 1 1 auto;\n min-width: 0;\n padding: var(--dimension-space-075);\n border-radius: var(--dimension-radius-025);\n box-sizing: border-box;\n}\n\n.card-overview__score-content {\n align-items: center;\n justify-content: flex-start;\n /*\n * Sits the fill 16px from the cell's left edge, on top of the 8px cell padding.\n * Metric cells keep the shared inner inset because their content is text; the\n * fill is a filled box and needs more room around it to read as inset.\n */\n padding-inline-start: var(--dimension-space-100);\n gap: var(--dimension-space-100);\n}\n\n/* Mirrors .card-overview__metric-content so labels and trends line up across cells. */\n.card-overview__score-copy {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n justify-content: center;\n align-items: flex-start;\n gap: var(--dimension-space-050);\n min-width: 0;\n}\n\n.card-overview__score-label-spacer {\n visibility: hidden;\n}\n\n.card-overview__score-content--error {\n align-items: center;\n justify-content: center;\n /* No fill to align, so this box keeps the metric cells' inner inset. */\n padding-inline-start: var(--dimension-space-075);\n border: var(--ds-card-overview-divider-width) solid var(--color-always-dark-border-tertiary);\n background: transparent;\n color: var(--color-always-dark-foreground-negative);\n text-align: center;\n}\n\n.card-overview__metric-action {\n flex-direction: column;\n justify-content: center;\n gap: var(--dimension-space-050);\n}\n\n.card-overview__metric-action[role='button'] {\n cursor: default;\n}\n\n.card-overview__metric-content {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n justify-content: center;\n gap: var(--dimension-space-050);\n min-width: 0;\n}\n\n.card-overview__metric-label {\n min-width: 0;\n}\n\n.card-overview__metric-figure {\n display: flex;\n align-items: baseline;\n gap: var(--dimension-space-050);\n min-width: 0;\n}\n\n.card-overview__trend {\n flex: 0 0 auto;\n white-space: nowrap;\n}\n\n:host(.card-overview--stacked) .card-overview__metrics {\n grid-template-columns: minmax(0, 1fr);\n}\n\n:host(.card-overview--compact) {\n height: var(--dimension-size-600);\n min-height: var(--dimension-size-600);\n}\n\n:host(.card-overview--compact) .card-overview__surface,\n:host(.card-overview--compact) .card-overview__clip,\n:host(.card-overview--compact) .card-overview__layout,\n:host(.card-overview--compact) .card-overview__summary {\n width: 100%;\n height: 100%;\n}\n\n:host(.card-overview--compact) .card-overview__header::after {\n display: none;\n}\n\n@media (forced-colors: active) {\n .card-overview__header::after {\n background: var(--ds-forced-color-content);\n }\n\n .card-overview__score-content--error {\n border: var(--dimension-stroke-width-012) solid var(--ds-forced-color-content);\n background: var(--ds-forced-color-surface);\n color: var(--ds-forced-color-content);\n }\n}\n",
375
375
  "type": "registry:ui"
376
376
  },
377
377
  {
@@ -273,7 +273,7 @@
273
273
  "files": [
274
274
  {
275
275
  "path": "src/wc/components/CardSetting/CardSetting.css",
276
- "content": "@import '../../utils/chrome-header.css';\n@import '../../utils/forced-colors.css';\n\n:host {\n position: relative;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n width: var(--_card-setting-width);\n min-height: var(--_card-setting-min-height);\n max-width: 100%;\n overflow: hidden;\n border: none;\n border-radius: var(--dimension-radius-125);\n background-color: var(--color-background-primary);\n box-shadow: var(--effect-shadow-elevated-sm);\n}\n\n:host::after {\n content: '';\n position: absolute;\n inset: 0;\n z-index: 4;\n border-radius: inherit;\n box-shadow: var(--effect-highlight-elevated-sm);\n pointer-events: none;\n}\n\n:host(.card-setting--editing) {\n background-color: var(--color-background-bold-brand);\n box-shadow: 0 0 0 var(--dimension-stroke-width-025) var(--color-border-bold-brand);\n}\n\n:host(.card-setting--editing)::after {\n box-shadow: none;\n}\n\n.card-setting__header {\n flex: 0 0 auto;\n border-bottom: var(--dimension-stroke-width-012) solid var(--color-border-tertiary);\n}\n\n:host(.card-setting--editing) .card-setting__header {\n border-bottom-color: transparent;\n}\n\n.card-setting__copy {\n align-items: center;\n}\n\nds-text.card-setting__title {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n -webkit-user-select: none;\n user-select: none;\n}\n\n.card-setting__actions {\n flex-shrink: 0;\n}\n\n.card-setting__body {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n min-height: 0;\n box-sizing: border-box;\n background-color: var(--color-background-primary);\n border-radius: 0 0 var(--dimension-radius-125) var(--dimension-radius-125);\n}\n\n:host(.card-setting--editing) .card-setting__body {\n border-radius: var(--dimension-radius-125);\n}\n\n@media (forced-colors: active) {\n :host {\n box-shadow: none;\n outline: var(--dimension-stroke-width-012) solid var(--ds-forced-color-content);\n outline-offset: calc(-1 * var(--dimension-stroke-width-012));\n }\n\n :host::after {\n box-shadow: none;\n }\n}\n",
276
+ "content": "@import '../../utils/radius.css';\n@import '../../utils/chrome-header.css';\n@import '../../utils/forced-colors.css';\n\n:host {\n position: relative;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n width: var(--_card-setting-width);\n min-height: var(--_card-setting-min-height);\n max-width: 100%;\n overflow: hidden;\n border: none;\n border-radius: var(--ds-radius-card);\n background-color: var(--color-background-primary);\n box-shadow: var(--effect-shadow-elevated-sm);\n}\n\n:host::after {\n content: '';\n position: absolute;\n inset: 0;\n z-index: 4;\n border-radius: inherit;\n box-shadow: var(--effect-highlight-elevated-sm);\n pointer-events: none;\n}\n\n:host(.card-setting--editing) {\n background-color: var(--color-background-bold-brand);\n box-shadow: 0 0 0 var(--dimension-stroke-width-025) var(--color-border-bold-brand);\n}\n\n:host(.card-setting--editing)::after {\n box-shadow: none;\n}\n\n.card-setting__header {\n flex: 0 0 auto;\n border-bottom: var(--dimension-stroke-width-012) solid var(--color-border-tertiary);\n}\n\n:host(.card-setting--editing) .card-setting__header {\n border-bottom-color: transparent;\n}\n\n.card-setting__copy {\n align-items: center;\n}\n\nds-text.card-setting__title {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n -webkit-user-select: none;\n user-select: none;\n}\n\n.card-setting__actions {\n flex-shrink: 0;\n}\n\n.card-setting__body {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n min-height: 0;\n box-sizing: border-box;\n background-color: var(--color-background-primary);\n border-radius: 0 0 var(--ds-radius-card) var(--ds-radius-card);\n}\n\n:host(.card-setting--editing) .card-setting__body {\n border-radius: var(--ds-radius-card);\n}\n\n@media (forced-colors: active) {\n :host {\n box-shadow: none;\n outline: var(--dimension-stroke-width-012) solid var(--ds-forced-color-content);\n outline-offset: calc(-1 * var(--dimension-stroke-width-012));\n }\n\n :host::after {\n box-shadow: none;\n }\n}\n",
277
277
  "type": "registry:ui"
278
278
  },
279
279
  {
@@ -32,7 +32,7 @@
32
32
  "The shared text-action recipe supplies brand foreground, hover underline, and keyboard focus indication."
33
33
  ],
34
34
  "states": [
35
- "The application owns the selected area, profile, available choices, permissions, and empty-state copy.",
35
+ "The application owns the selected area, profile, available choices, permissions, and empty-state copy. Existing complete area-trigger labels remain compatible. Applications that separate the localizable settings noun from the control supply the area-only trigger label and noun together.",
36
36
  "The card reports popup requests without changing selection or expanded state.",
37
37
  "The surface follows the active theme rather than imposing always-dark colors."
38
38
  ],
@@ -78,7 +78,7 @@
78
78
  "default": "'All settings'",
79
79
  "required": false,
80
80
  "mutable": false,
81
- "description": "Current product area or the label for the combined settings view."
81
+ "description": "Current product area or complete combined-view label in the sentence trigger."
82
82
  },
83
83
  "forLabel": {
84
84
  "type": "string",
@@ -141,6 +141,15 @@
141
141
  "required": false,
142
142
  "mutable": false,
143
143
  "description": "Accessible name for this context region."
144
+ },
145
+ "settingsLabel": {
146
+ "type": "string",
147
+ "resolvedType": "string",
148
+ "attribute": "settings-label",
149
+ "default": "''",
150
+ "required": false,
151
+ "mutable": false,
152
+ "description": "Optional sentence noun after an area-only trigger label, supplied separately for localization."
144
153
  }
145
154
  },
146
155
  "events": [
@@ -181,7 +190,7 @@
181
190
  "default": "'All settings'",
182
191
  "required": false,
183
192
  "mutable": false,
184
- "description": "Current product area or the label for the combined settings view."
193
+ "description": "Current product area or complete combined-view label in the sentence trigger."
185
194
  },
186
195
  "forLabel": {
187
196
  "type": "string",
@@ -244,6 +253,15 @@
244
253
  "required": false,
245
254
  "mutable": false,
246
255
  "description": "Accessible name for this context region."
256
+ },
257
+ "settingsLabel": {
258
+ "type": "string",
259
+ "resolvedType": "string",
260
+ "attribute": "settings-label",
261
+ "default": "''",
262
+ "required": false,
263
+ "mutable": false,
264
+ "description": "Optional sentence noun after an area-only trigger label, supplied separately for localization."
247
265
  }
248
266
  },
249
267
  "events": [
@@ -302,12 +320,12 @@
302
320
  "files": [
303
321
  {
304
322
  "path": "src/wc/components/CardSettingsScope/CardSettingsScope.css",
305
- "content": "@import '../../utils/text-decoration.css';\n@import '../../utils/focus-ring.css';\n@import '../../styles/control-elevation.css';\n\n:host {\n display: block;\n min-width: 0;\n width: var(--dimension-card-width-md);\n max-width: 100%;\n}\n\n.card-settings-scope {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 0 var(--dimension-space-050);\n box-sizing: border-box;\n min-height: var(--dimension-size-600);\n padding: var(--dimension-space-100) var(--dimension-space-200);\n border-radius: var(--dimension-radius-125);\n background: var(--color-background-primary);\n color: var(--color-foreground-primary);\n}\n\n.card-settings-scope__action {\n display: inline-flex;\n align-items: center;\n min-height: var(--dimension-size-400);\n max-width: 100%;\n margin: 0;\n padding: 0;\n border: 0;\n border-radius: var(--dimension-radius-025);\n background: transparent;\n cursor: pointer;\n}\n\n.card-settings-scope__action ds-text {\n overflow-wrap: anywhere;\n text-align: start;\n}\n\n@media (forced-colors: active) {\n .card-settings-scope {\n outline: var(--dimension-stroke-width-012) solid var(--ds-forced-color-content);\n outline-offset: calc(-1 * var(--dimension-stroke-width-012));\n }\n}\n",
323
+ "content": "@import '../../utils/text-decoration.css';\n@import '../../utils/focus-ring.css';\n@import '../../utils/radius.css';\n@import '../../styles/control-elevation.css';\n\n:host {\n display: block;\n min-width: 0;\n width: var(--dimension-card-width-md);\n max-width: 100%;\n}\n\n.card-settings-scope {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 0 var(--dimension-space-050);\n box-sizing: border-box;\n min-height: var(--dimension-size-600);\n padding: var(--dimension-space-100) var(--dimension-space-200);\n border-radius: var(--ds-radius-card);\n background: var(--color-background-primary);\n color: var(--color-foreground-primary);\n}\n\n.card-settings-scope__action {\n display: inline-flex;\n align-items: center;\n min-height: var(--dimension-size-400);\n max-width: 100%;\n margin: 0;\n padding: 0;\n border: 0;\n border-radius: var(--dimension-radius-025);\n background: transparent;\n cursor: pointer;\n}\n\n.card-settings-scope__action ds-text {\n overflow-wrap: anywhere;\n text-align: start;\n}\n\n@media (forced-colors: active) {\n .card-settings-scope {\n outline: var(--dimension-stroke-width-012) solid var(--ds-forced-color-content);\n outline-offset: calc(-1 * var(--dimension-stroke-width-012));\n }\n}\n",
306
324
  "type": "registry:ui"
307
325
  },
308
326
  {
309
327
  "path": "src/wc/components/CardSettingsScope/CardSettingsScope.tsx",
310
- "content": "import { Component, Event, EventEmitter, h, Host, Prop } from '@stencil/core';\n\nexport interface SettingsScopeRequest {\n scope: 'area' | 'profile';\n anchor: HTMLButtonElement;\n originalEvent: MouseEvent;\n}\n\n@Component({\n tag: 'ds-card-settings-scope',\n styleUrl: 'CardSettingsScope.css',\n scoped: true,\n})\nexport class CardSettingsScope {\n /** Current product area or the label for the combined settings view. */\n @Prop() areaLabel: string = 'All settings';\n /** Current organization or settings profile. */\n @Prop() profileLabel: string = 'Organization';\n /** Sentence prefix, supplied separately for localization. */\n @Prop() managingLabel: string = 'Managing';\n /** Sentence connector, supplied separately for localization. */\n @Prop() forLabel: string = 'for';\n /** Accessible name for this context region. */\n @Prop() scopeLabel: string = 'Settings scope';\n /** ID of the application-owned product-area menu. */\n @Prop() areaControls: string | undefined;\n /** ID of the application-owned profiles popup. */\n @Prop() profileControls: string | undefined;\n /** Whether the product-area menu is open. */\n @Prop() areaExpanded: boolean = false;\n /** Whether the profiles popup is open. */\n @Prop() profileExpanded: boolean = false;\n /** Empty informational profile popups use dialog semantics instead of a menu. */\n @Prop() profilePopup: 'menu' | 'dialog' = 'menu';\n\n /** Requests a popup; the owner supplies choices, open state, and selection. */\n @Event() dsScopeRequest!: EventEmitter<SettingsScopeRequest>;\n\n private request(scope: SettingsScopeRequest['scope'], event: MouseEvent) {\n this.dsScopeRequest.emit({\n scope,\n anchor: event.currentTarget as HTMLButtonElement,\n originalEvent: event,\n });\n }\n\n render() {\n return (\n <Host>\n <div\n class=\"card-settings-scope ds-control-elevation ds-control-elevation--sm\"\n role=\"region\"\n aria-label={this.scopeLabel}\n >\n <ds-text as=\"span\" variant=\"text-body-medium\" color=\"primary\">\n {this.managingLabel}\n </ds-text>\n <button\n type=\"button\"\n class=\"card-settings-scope__action ds-text-action ds-focus-ring\"\n aria-haspopup=\"menu\"\n aria-controls={this.areaControls}\n aria-expanded={String(this.areaExpanded)}\n onClick={event => this.request('area', event)}\n >\n <ds-text as=\"span\" variant=\"text-body-medium\" color=\"inherit\">\n {this.areaLabel}\n </ds-text>\n </button>\n <ds-text as=\"span\" variant=\"text-body-medium\" color=\"primary\">\n {this.forLabel}\n </ds-text>\n <button\n type=\"button\"\n class=\"card-settings-scope__action ds-text-action ds-focus-ring\"\n aria-haspopup={this.profilePopup}\n aria-controls={this.profileControls}\n aria-expanded={String(this.profileExpanded)}\n onClick={event => this.request('profile', event)}\n >\n <ds-text as=\"span\" variant=\"text-body-medium\" color=\"inherit\">\n {this.profileLabel}\n </ds-text>\n </button>\n </div>\n </Host>\n );\n }\n}\n",
328
+ "content": "import { Component, Event, EventEmitter, h, Host, Prop } from '@stencil/core';\n\nexport interface SettingsScopeRequest {\n scope: 'area' | 'profile';\n anchor: HTMLButtonElement;\n originalEvent: MouseEvent;\n}\n\n@Component({\n tag: 'ds-card-settings-scope',\n styleUrl: 'CardSettingsScope.css',\n scoped: true,\n})\nexport class CardSettingsScope {\n /** Current product area or complete combined-view label in the sentence trigger. */\n @Prop() areaLabel: string = 'All settings';\n /** Current organization or settings profile. */\n @Prop() profileLabel: string = 'Organization';\n /** Sentence prefix, supplied separately for localization. */\n @Prop() managingLabel: string = 'Managing';\n /** Optional sentence noun after an area-only trigger label, supplied separately for localization. */\n @Prop() settingsLabel: string = '';\n /** Sentence connector, supplied separately for localization. */\n @Prop() forLabel: string = 'for';\n /** Accessible name for this context region. */\n @Prop() scopeLabel: string = 'Settings scope';\n /** ID of the application-owned product-area menu. */\n @Prop() areaControls: string | undefined;\n /** ID of the application-owned profiles popup. */\n @Prop() profileControls: string | undefined;\n /** Whether the product-area menu is open. */\n @Prop() areaExpanded: boolean = false;\n /** Whether the profiles popup is open. */\n @Prop() profileExpanded: boolean = false;\n /** Empty informational profile popups use dialog semantics instead of a menu. */\n @Prop() profilePopup: 'menu' | 'dialog' = 'menu';\n\n /** Requests a popup; the owner supplies choices, open state, and selection. */\n @Event() dsScopeRequest!: EventEmitter<SettingsScopeRequest>;\n\n private request(scope: SettingsScopeRequest['scope'], event: MouseEvent) {\n this.dsScopeRequest.emit({\n scope,\n anchor: event.currentTarget as HTMLButtonElement,\n originalEvent: event,\n });\n }\n\n render() {\n return (\n <Host>\n <div\n class=\"card-settings-scope ds-control-elevation ds-control-elevation--sm\"\n role=\"region\"\n aria-label={this.scopeLabel}\n >\n <ds-text as=\"span\" variant=\"text-body-medium\" color=\"primary\">\n {this.managingLabel}\n </ds-text>\n <button\n type=\"button\"\n class=\"card-settings-scope__action ds-text-action ds-focus-ring\"\n aria-haspopup=\"menu\"\n aria-controls={this.areaControls}\n aria-expanded={String(this.areaExpanded)}\n onClick={event => this.request('area', event)}\n >\n <ds-text as=\"span\" variant=\"text-body-medium\" color=\"inherit\">\n {this.areaLabel}\n </ds-text>\n </button>\n {this.settingsLabel.trim() ? (\n <ds-text as=\"span\" variant=\"text-body-medium\" color=\"primary\">\n {this.settingsLabel}\n </ds-text>\n ) : null}\n <ds-text as=\"span\" variant=\"text-body-medium\" color=\"primary\">\n {this.forLabel}\n </ds-text>\n <button\n type=\"button\"\n class=\"card-settings-scope__action ds-text-action ds-focus-ring\"\n aria-haspopup={this.profilePopup}\n aria-controls={this.profileControls}\n aria-expanded={String(this.profileExpanded)}\n onClick={event => this.request('profile', event)}\n >\n <ds-text as=\"span\" variant=\"text-body-medium\" color=\"inherit\">\n {this.profileLabel}\n </ds-text>\n </button>\n </div>\n </Host>\n );\n }\n}\n",
311
329
  "type": "registry:ui"
312
330
  }
313
331
  ]
@@ -289,7 +289,7 @@
289
289
  "files": [
290
290
  {
291
291
  "path": "src/wc/components/ChartLegend/ChartLegend.css",
292
- "content": "@import '../../utils/control-density.css';\n@import '../../utils/interaction-fill.css';\n@import '../../utils/focus-ring.css';\n@import '../../utils/forced-colors.css';\n\n:host {\n display: block;\n font-family: var(--typography-font-family-ui);\n user-select: none;\n}\n\n.chart-legend__list {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n/* Non-visual wrapper — the real box is .chart-legend__item so optional highlight/link\n affordances cover the whole row while children align to the shared grid columns. */\n.chart-legend__list-item {\n display: contents;\n}\n\n:host(.chart-legend--vertical) .chart-legend__list {\n display: grid;\n grid-template-columns: auto minmax(0, 1fr) var(--dimension-size-600);\n column-gap: var(--dimension-space-075);\n row-gap: var(--dimension-space-050);\n}\n\n:host(.chart-legend--vertical.chart-legend--show-percentage) .chart-legend__list {\n grid-template-columns: auto minmax(0, 1fr) var(--dimension-size-600) var(--dimension-size-800);\n}\n\n:host(.chart-legend--vertical) .chart-legend__item {\n grid-column: 1 / -1;\n display: grid;\n grid-template-columns: subgrid;\n}\n\n:host(.chart-legend--horizontal) .chart-legend__list {\n display: flex;\n flex-wrap: wrap;\n gap: var(--dimension-space-200);\n}\n\n:host(.chart-legend--horizontal) .chart-legend__item {\n display: flex;\n gap: var(--dimension-space-100);\n}\n\n/*\n * Density: md control recipe (height / pad / radius / label inset).\n * Hover/press: `.ds-interaction-fill` (token overlay on ::after).\n */\n.chart-legend__item {\n box-sizing: border-box;\n height: var(--ds-control-height, var(--dimension-size-400));\n align-items: center;\n padding: 0 var(--ds-control-padding-inline, var(--dimension-space-075));\n border-radius: var(--ds-control-radius, var(--dimension-radius-025));\n text-decoration: none;\n color: inherit;\n transition: opacity var(--effect-motion-short-2);\n}\n\n.chart-legend__item--dimmed {\n opacity: var(--effect-opacity-medium);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .chart-legend__item {\n transition: none;\n }\n}\n\n.chart-legend__item--interactive {\n cursor: pointer;\n}\n\n/* Raise row content above interaction fills (positive z-index on util layers). */\n.chart-legend__item > * {\n position: relative;\n z-index: 2;\n}\n\n.chart-legend__swatch-box {\n width: var(--dimension-size-250);\n height: var(--dimension-size-250);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.chart-legend__swatch {\n width: var(--dimension-space-150);\n height: var(--dimension-space-150);\n border-radius: var(--dimension-radius-025);\n flex-shrink: 0;\n}\n\n.chart-legend__label {\n min-width: 0;\n}\n\n.chart-legend__percentage {\n width: 100%;\n min-width: 0;\n text-align: right;\n}\n\n.chart-legend__value {\n width: 100%;\n text-align: right;\n min-width: 0;\n}\n\n@media (forced-colors: active) {\n .chart-legend__swatch {\n forced-color-adjust: none;\n outline: var(--dimension-stroke-width-012) solid var(--ds-forced-color-content);\n }\n}\n",
292
+ "content": "@import '../../utils/radius.css';\n@import '../../utils/control-density.css';\n@import '../../utils/interaction-fill.css';\n@import '../../utils/focus-ring.css';\n@import '../../utils/forced-colors.css';\n\n:host {\n display: block;\n font-family: var(--typography-font-family-ui);\n user-select: none;\n}\n\n.chart-legend__list {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n/* Non-visual wrapper — the real box is .chart-legend__item so optional highlight/link\n affordances cover the whole row while children align to the shared grid columns. */\n.chart-legend__list-item {\n display: contents;\n}\n\n:host(.chart-legend--vertical) .chart-legend__list {\n display: grid;\n grid-template-columns: auto minmax(0, 1fr) var(--dimension-size-600);\n column-gap: var(--dimension-space-075);\n row-gap: var(--dimension-space-050);\n}\n\n:host(.chart-legend--vertical.chart-legend--show-percentage) .chart-legend__list {\n grid-template-columns: auto minmax(0, 1fr) var(--dimension-size-600) var(--dimension-size-800);\n}\n\n:host(.chart-legend--vertical) .chart-legend__item {\n grid-column: 1 / -1;\n display: grid;\n grid-template-columns: subgrid;\n}\n\n:host(.chart-legend--horizontal) .chart-legend__list {\n display: flex;\n flex-wrap: wrap;\n gap: var(--dimension-space-200);\n}\n\n:host(.chart-legend--horizontal) .chart-legend__item {\n display: flex;\n gap: var(--dimension-space-100);\n}\n\n/*\n * Density: md control recipe (height / pad / radius / label inset).\n * Hover/press: `.ds-interaction-fill` (token overlay on ::after).\n */\n.chart-legend__item {\n box-sizing: border-box;\n height: var(--ds-control-height, var(--dimension-size-400));\n align-items: center;\n padding: 0 var(--ds-control-padding-inline, var(--dimension-space-075));\n border-radius: var(--ds-control-radius, var(--dimension-radius-025));\n text-decoration: none;\n color: inherit;\n transition: opacity var(--effect-motion-short-2);\n}\n\n.chart-legend__item--dimmed {\n opacity: var(--effect-opacity-medium);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .chart-legend__item {\n transition: none;\n }\n}\n\n.chart-legend__item--interactive {\n cursor: pointer;\n}\n\n/* Raise row content above interaction fills (positive z-index on util layers). */\n.chart-legend__item > * {\n position: relative;\n z-index: 2;\n}\n\n.chart-legend__swatch-box {\n width: var(--dimension-size-250);\n height: var(--dimension-size-250);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.chart-legend__swatch {\n width: var(--dimension-space-150);\n height: var(--dimension-space-150);\n border-radius: var(--dimension-radius-025);\n flex-shrink: 0;\n}\n\n.chart-legend__label {\n min-width: 0;\n}\n\n.chart-legend__percentage {\n width: 100%;\n min-width: 0;\n text-align: right;\n}\n\n.chart-legend__value {\n width: 100%;\n text-align: right;\n min-width: 0;\n}\n\n@media (forced-colors: active) {\n .chart-legend__swatch {\n forced-color-adjust: none;\n outline: var(--dimension-stroke-width-012) solid var(--ds-forced-color-content);\n }\n}\n",
293
293
  "type": "registry:ui"
294
294
  },
295
295
  {