@ds-mo/ui 13.6.1 → 13.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (315) hide show
  1. package/dist/.package-ready.json +1 -1
  2. package/dist/agent-patterns.json +1 -1
  3. package/dist/agent.json +109 -371
  4. package/dist/angular/ds-card-chart.d.ts +18 -0
  5. package/dist/angular/{ds-card-data-viz.js → ds-card-chart.js} +10 -10
  6. package/dist/angular/ds-chart.d.ts +19 -0
  7. package/dist/angular/{ds-chart-donut.js → ds-chart.js} +15 -15
  8. package/dist/angular/ds-tooltip-chart.d.ts +12 -0
  9. package/dist/angular/{ds-chart-line.js → ds-tooltip-chart.js} +12 -12
  10. package/dist/angular/index.d.ts +1 -1
  11. package/dist/angular/index.js +3 -5
  12. package/dist/angular/proxies.d.ts +3 -5
  13. package/dist/angular/proxies.js +3 -5
  14. package/dist/components/ds-agent-activity.js +1 -1
  15. package/dist/components/ds-agent-response.js +1 -1
  16. package/dist/components/ds-agent-source-list.js +1 -1
  17. package/dist/components/ds-agent-tool-call.js +1 -1
  18. package/dist/components/ds-attachment-list.js +1 -1
  19. package/dist/components/ds-badge.js +1 -1
  20. package/dist/components/ds-banner.js +1 -1
  21. package/dist/components/ds-banner.js.map +1 -1
  22. package/dist/components/ds-bar-nav.js +1 -1
  23. package/dist/components/ds-bar-title.js +1 -1
  24. package/dist/components/ds-bar-workflow.js +1 -1
  25. package/dist/components/ds-breadcrumb.js +1 -1
  26. package/dist/components/ds-button-filled.js +1 -1
  27. package/dist/components/ds-button-unfilled.js +1 -1
  28. package/dist/components/{ds-chart-line.d.ts → ds-card-chart.d.ts} +4 -4
  29. package/dist/components/ds-card-chart.js +2 -0
  30. package/dist/components/ds-card-chart.js.map +1 -0
  31. package/dist/components/ds-card-overview.js +1 -1
  32. package/dist/components/ds-card-overview.js.map +1 -1
  33. package/dist/components/ds-card-setting.js +1 -1
  34. package/dist/components/ds-card-setting.js.map +1 -1
  35. package/dist/components/ds-chart-legend.js +1 -1
  36. package/dist/components/ds-chart-legend.js.map +1 -1
  37. package/dist/components/{ds-chart-bar.d.ts → ds-chart.d.ts} +4 -4
  38. package/dist/components/ds-chart.js +2 -0
  39. package/dist/components/ds-chart.js.map +1 -0
  40. package/dist/components/ds-checkbox.js +1 -1
  41. package/dist/components/ds-chip.js +1 -1
  42. package/dist/components/ds-code-block.js +1 -1
  43. package/dist/components/ds-conversation-list-item.js +1 -1
  44. package/dist/components/ds-conversation-list-section.js +1 -1
  45. package/dist/components/ds-conversation-list.js +1 -1
  46. package/dist/components/ds-divider.js +1 -1
  47. package/dist/components/ds-empty-state.js +1 -1
  48. package/dist/components/ds-field.js +1 -1
  49. package/dist/components/ds-input.js +1 -1
  50. package/dist/components/ds-markdown.js +1 -1
  51. package/dist/components/ds-menu.js +1 -1
  52. package/dist/components/ds-message-actions.js +1 -1
  53. package/dist/components/ds-message-bubble.js +1 -1
  54. package/dist/components/ds-message-composer.js +1 -1
  55. package/dist/components/ds-message-scroller.js +1 -1
  56. package/dist/components/ds-message.js +1 -1
  57. package/dist/components/ds-mobile-bar-nav.js +1 -1
  58. package/dist/components/ds-mobile-header.js +1 -1
  59. package/dist/components/ds-mobile-section-switcher.js +1 -1
  60. package/dist/components/ds-mobile-sheet-nav.js +1 -1
  61. package/dist/components/ds-modal.js +1 -1
  62. package/dist/components/ds-modal.js.map +1 -1
  63. package/dist/components/ds-panel-nav.js +1 -1
  64. package/dist/components/ds-panel-sub-nav.js +1 -1
  65. package/dist/components/ds-panel-tool-header.js +1 -1
  66. package/dist/components/ds-panel-tool-search.js +1 -1
  67. package/dist/components/ds-panel-tools.js +1 -1
  68. package/dist/components/ds-radio.js +1 -1
  69. package/dist/components/ds-scroll-overlay.js +1 -1
  70. package/dist/components/ds-select.js +1 -1
  71. package/dist/components/ds-select.js.map +1 -1
  72. package/dist/components/ds-shell-app.js +1 -1
  73. package/dist/components/ds-shell-page.js +1 -1
  74. package/dist/components/ds-shell-tools.js +1 -1
  75. package/dist/components/ds-skeleton.js +1 -1
  76. package/dist/components/ds-slider.js +1 -1
  77. package/dist/components/ds-swatch-picker.js +1 -1
  78. package/dist/components/ds-switch.js +1 -1
  79. package/dist/components/ds-tab-group.js +1 -1
  80. package/dist/components/ds-tag.js +1 -1
  81. package/dist/components/ds-text.js +1 -1
  82. package/dist/components/ds-toast.js +1 -1
  83. package/dist/components/ds-toast.js.map +1 -1
  84. package/dist/components/{ds-card-data-viz.d.ts → ds-tooltip-chart.d.ts} +4 -4
  85. package/dist/components/ds-tooltip-chart.js +2 -0
  86. package/dist/components/ds-tooltip.js +1 -1
  87. package/dist/components/ds-typing-indicator.js +1 -1
  88. package/dist/components/{p-iCjUWcp7.js → p-2inC7Jm-.js} +2 -2
  89. package/dist/components/{p-iCjUWcp7.js.map → p-2inC7Jm-.js.map} +1 -1
  90. package/dist/components/{p-DaqFuVKZ.js → p-B16OuqyJ.js} +2 -2
  91. package/dist/components/{p-BmNpPyzf.js → p-B4VlfBNc.js} +2 -2
  92. package/dist/components/{p-og48Kh2i.js → p-B6z51yBf.js} +2 -2
  93. package/dist/components/{p-Bb4f5SuF.js → p-BNiIyZCn.js} +2 -2
  94. package/dist/components/{p-BFfHIu6y.js → p-BP-EtYbR.js} +2 -2
  95. package/dist/components/{p-DHF5cLzr.js → p-BQ7Nzx9A.js} +2 -2
  96. package/dist/components/{p-B-kRByho.js → p-BQDi0v_h.js} +2 -2
  97. package/dist/components/{p-BBtjABBS.js → p-BYB0f1_F.js} +2 -2
  98. package/dist/components/p-BjIYFiuQ.js +2 -0
  99. package/dist/components/{p-D0bM3cKj.js.map → p-BjIYFiuQ.js.map} +1 -1
  100. package/dist/components/{p-DfvDNcoq.js → p-BjoyHNaM.js} +2 -2
  101. package/dist/components/{p-Bl6A5AJq.js → p-BkWhTEqe.js} +2 -2
  102. package/dist/components/p-BnGJrBPG.js +2 -0
  103. package/dist/components/{p-1S9AW2MA.js.map → p-BnGJrBPG.js.map} +1 -1
  104. package/dist/components/p-BtS5na9O.js +2 -0
  105. package/dist/components/{p-DX4c3kZ1.js.map → p-BtS5na9O.js.map} +1 -1
  106. package/dist/components/p-CCYgBUUS.js +2 -0
  107. package/dist/components/{p-D2cjoj2r.js.map → p-CCYgBUUS.js.map} +1 -1
  108. package/dist/components/p-CXduurfd.js +2 -0
  109. package/dist/components/p-CXduurfd.js.map +1 -0
  110. package/dist/components/{p-CxgMUvqg.js → p-CnbrksLl.js} +2 -2
  111. package/dist/components/{p-Eep5mxtv.js → p-Cog5NTLa.js} +2 -2
  112. package/dist/components/{p-Bmo4T96l.js → p-D2dQyoyK.js} +2 -2
  113. package/dist/components/{p-BPyymeuR.js → p-D6g070eY.js} +2 -2
  114. package/dist/components/{p-wOOAzVMC.js → p-D8qgVmHn.js} +2 -2
  115. package/dist/components/{p-BR_oMaas.js → p-DJ0XMH9E.js} +2 -2
  116. package/dist/components/{p-B6RrZQqw.js → p-DK4PGrHm.js} +2 -2
  117. package/dist/components/{p-DVuxzrCC.js → p-DLwM1RNL.js} +2 -2
  118. package/dist/components/p-DW9p068S.js +2 -0
  119. package/dist/components/{p-CuJ3J6OJ.js.map → p-DW9p068S.js.map} +1 -1
  120. package/dist/components/{p-Bsfpxto1.js → p-Dg9qiMDa.js} +2 -2
  121. package/dist/components/p-Dg9qiMDa.js.map +1 -0
  122. package/dist/components/{p-B4_0Ys4w.js → p-DgglLVCj.js} +2 -2
  123. package/dist/components/p-DmblUQ4D.js +2 -0
  124. package/dist/components/p-DmblUQ4D.js.map +1 -0
  125. package/dist/components/{p-6B411hQA.js → p-Dv8_hmUZ.js} +2 -2
  126. package/dist/components/{p-IYy-jODg.js → p-DwKC_9u2.js} +2 -2
  127. package/dist/components/p-GbTasczF.js +2 -0
  128. package/dist/components/{p-CzraIk34.js → p-HIg1XX62.js} +2 -2
  129. package/dist/components/{p-Di81y9Q2.js → p-KFHFBznh.js} +2 -2
  130. package/dist/components/{p-CKRJX0Y0.js → p-PQgDzzn9.js} +2 -2
  131. package/dist/components/{p-Ca6s5fLR.js → p-iyiOFsws.js} +2 -2
  132. package/dist/docs/components.json +180 -662
  133. package/dist/lib/utils/chart-grammar.d.ts +359 -0
  134. package/dist/lib/utils/index.d.ts +2 -0
  135. package/dist/lib/utils/index.js +504 -0
  136. package/dist/lib/utils/index.js.map +4 -4
  137. package/dist/lib/utils/token-defaults.d.ts +3 -0
  138. package/dist/mcp-data/registry/agent-activity.json +1 -1
  139. package/dist/mcp-data/registry/agent-response.json +1 -1
  140. package/dist/mcp-data/registry/agent-source-list.json +1 -1
  141. package/dist/mcp-data/registry/agent-tool-call.json +1 -1
  142. package/dist/mcp-data/registry/attachment-list.json +1 -1
  143. package/dist/mcp-data/registry/avatar.json +1 -1
  144. package/dist/mcp-data/registry/badge.json +1 -1
  145. package/dist/mcp-data/registry/banner.json +1 -1
  146. package/dist/mcp-data/registry/bar-nav.json +2 -2
  147. package/dist/mcp-data/registry/bar-title.json +1 -1
  148. package/dist/mcp-data/registry/bar-workflow.json +1 -1
  149. package/dist/mcp-data/registry/breadcrumb.json +1 -1
  150. package/dist/mcp-data/registry/button-filled.json +1 -1
  151. package/dist/mcp-data/registry/button-unfilled.json +1 -1
  152. package/dist/mcp-data/registry/card-chart.json +238 -0
  153. package/dist/mcp-data/registry/card-overview.json +7 -7
  154. package/dist/mcp-data/registry/card-setting.json +5 -5
  155. package/dist/mcp-data/registry/chart-legend.json +12 -19
  156. package/dist/mcp-data/registry/chart.json +258 -0
  157. package/dist/mcp-data/registry/checkbox.json +1 -1
  158. package/dist/mcp-data/registry/chip.json +1 -1
  159. package/dist/mcp-data/registry/code-block.json +1 -1
  160. package/dist/mcp-data/registry/conversation-list-item.json +1 -1
  161. package/dist/mcp-data/registry/conversation-list-section.json +1 -1
  162. package/dist/mcp-data/registry/conversation-list.json +1 -1
  163. package/dist/mcp-data/registry/divider.json +1 -1
  164. package/dist/mcp-data/registry/empty-state.json +1 -1
  165. package/dist/mcp-data/registry/field.json +1 -1
  166. package/dist/mcp-data/registry/icon.json +1 -1
  167. package/dist/mcp-data/registry/input.json +1 -1
  168. package/dist/mcp-data/registry/loader.json +1 -1
  169. package/dist/mcp-data/registry/markdown.json +1 -1
  170. package/dist/mcp-data/registry/menu.json +1 -1
  171. package/dist/mcp-data/registry/message-actions.json +1 -1
  172. package/dist/mcp-data/registry/message-bubble.json +1 -1
  173. package/dist/mcp-data/registry/message-composer.json +1 -1
  174. package/dist/mcp-data/registry/message-scroller.json +1 -1
  175. package/dist/mcp-data/registry/message.json +1 -1
  176. package/dist/mcp-data/registry/mobile-bar-nav.json +1 -1
  177. package/dist/mcp-data/registry/mobile-header.json +1 -1
  178. package/dist/mcp-data/registry/mobile-section-switcher.json +1 -1
  179. package/dist/mcp-data/registry/mobile-sheet-nav.json +1 -1
  180. package/dist/mcp-data/registry/modal.json +1 -1
  181. package/dist/mcp-data/registry/panel-nav.json +1 -1
  182. package/dist/mcp-data/registry/panel-sub-nav.json +1 -1
  183. package/dist/mcp-data/registry/panel-tool-header.json +1 -1
  184. package/dist/mcp-data/registry/panel-tool-search.json +1 -1
  185. package/dist/mcp-data/registry/panel-tools.json +1 -1
  186. package/dist/mcp-data/registry/radio.json +1 -1
  187. package/dist/mcp-data/registry/registry.json +109 -377
  188. package/dist/mcp-data/registry/scroll-overlay.json +1 -1
  189. package/dist/mcp-data/registry/select.json +1 -1
  190. package/dist/mcp-data/registry/shell-app.json +1 -1
  191. package/dist/mcp-data/registry/shell-page.json +1 -1
  192. package/dist/mcp-data/registry/shell-tools.json +1 -1
  193. package/dist/mcp-data/registry/skeleton.json +3 -3
  194. package/dist/mcp-data/registry/slider.json +1 -1
  195. package/dist/mcp-data/registry/swatch-picker.json +1 -1
  196. package/dist/mcp-data/registry/switch.json +1 -1
  197. package/dist/mcp-data/registry/tab-group.json +1 -1
  198. package/dist/mcp-data/registry/tag.json +1 -1
  199. package/dist/mcp-data/registry/text.json +1 -1
  200. package/dist/mcp-data/registry/toast.json +1 -1
  201. package/dist/mcp-data/registry/tooltip-chart.json +253 -0
  202. package/dist/mcp-data/registry/tooltip.json +3 -3
  203. package/dist/mcp-data/registry/typing-indicator.json +1 -1
  204. package/dist/react/components.d.ts +3 -5
  205. package/dist/react/components.js +3 -5
  206. package/dist/react/ds-card-chart.d.ts +12 -0
  207. package/dist/react/ds-card-chart.js +12 -0
  208. package/dist/react/ds-chart.d.ts +12 -0
  209. package/dist/react/ds-chart.js +12 -0
  210. package/dist/react/{ds-chart-bar.d.ts → ds-tooltip-chart.d.ts} +3 -3
  211. package/dist/react/ds-tooltip-chart.js +12 -0
  212. package/dist/types/components/{CardDataViz/CardDataViz.d.ts → CardChart/CardChart.d.ts} +9 -13
  213. package/dist/types/components/CardChart/index.d.ts +2 -0
  214. package/dist/types/components/CardOverview/CardOverview.d.ts +2 -0
  215. package/dist/types/components/Chart/Chart.d.ts +66 -0
  216. package/dist/types/components/Chart/index.d.ts +2 -0
  217. package/dist/types/components/ChartLegend/ChartLegend.d.ts +1 -1
  218. package/dist/types/components/{TooltipDataViz/TooltipDataViz.d.ts → TooltipChart/TooltipChart.d.ts} +15 -6
  219. package/dist/types/components/TooltipChart/index.d.ts +2 -0
  220. package/dist/types/components.d.ts +129 -294
  221. package/dist/types/utils/chart-grammar.d.ts +360 -0
  222. package/dist/types/utils/chart-scene.d.ts +128 -0
  223. package/dist/types/utils/chart-theme.d.ts +27 -0
  224. package/dist/types/utils/chart-types.d.ts +0 -13
  225. package/dist/types/utils/index.d.ts +2 -0
  226. package/dist/types/utils/token-defaults.d.ts +3 -0
  227. package/package.json +3 -3
  228. package/dist/angular/ds-card-data-viz.d.ts +0 -18
  229. package/dist/angular/ds-chart-bar.d.ts +0 -12
  230. package/dist/angular/ds-chart-bar.js +0 -38
  231. package/dist/angular/ds-chart-donut.d.ts +0 -19
  232. package/dist/angular/ds-chart-line.d.ts +0 -12
  233. package/dist/angular/ds-tooltip-data-viz.d.ts +0 -12
  234. package/dist/angular/ds-tooltip-data-viz.js +0 -38
  235. package/dist/components/ds-card-data-viz.js +0 -2
  236. package/dist/components/ds-card-data-viz.js.map +0 -1
  237. package/dist/components/ds-chart-bar.js +0 -2
  238. package/dist/components/ds-chart-bar.js.map +0 -1
  239. package/dist/components/ds-chart-donut.d.ts +0 -11
  240. package/dist/components/ds-chart-donut.js +0 -2
  241. package/dist/components/ds-chart-donut.js.map +0 -1
  242. package/dist/components/ds-chart-line.js +0 -2
  243. package/dist/components/ds-chart-line.js.map +0 -1
  244. package/dist/components/ds-tooltip-data-viz.d.ts +0 -11
  245. package/dist/components/ds-tooltip-data-viz.js +0 -2
  246. package/dist/components/p-1S9AW2MA.js +0 -2
  247. package/dist/components/p-B6Vuayt6.js +0 -2
  248. package/dist/components/p-B6Vuayt6.js.map +0 -1
  249. package/dist/components/p-BBpASejH.js +0 -2
  250. package/dist/components/p-BBpASejH.js.map +0 -1
  251. package/dist/components/p-Bsfpxto1.js.map +0 -1
  252. package/dist/components/p-BybqPD8K.js +0 -2
  253. package/dist/components/p-BybqPD8K.js.map +0 -1
  254. package/dist/components/p-C4uYvyq6.js +0 -2
  255. package/dist/components/p-C4uYvyq6.js.map +0 -1
  256. package/dist/components/p-CbjFUpOj.js +0 -2
  257. package/dist/components/p-CbjFUpOj.js.map +0 -1
  258. package/dist/components/p-CrCGIx8s.js +0 -2
  259. package/dist/components/p-CrCGIx8s.js.map +0 -1
  260. package/dist/components/p-CuJ3J6OJ.js +0 -2
  261. package/dist/components/p-D0bM3cKj.js +0 -2
  262. package/dist/components/p-D2cjoj2r.js +0 -2
  263. package/dist/components/p-DX4c3kZ1.js +0 -2
  264. package/dist/components/p-DXewjzkM.js +0 -2
  265. package/dist/components/p-DXewjzkM.js.map +0 -1
  266. package/dist/components/p-yhj1lIPx.js +0 -2
  267. package/dist/mcp-data/registry/card-data-viz.json +0 -241
  268. package/dist/mcp-data/registry/chart-bar.json +0 -245
  269. package/dist/mcp-data/registry/chart-donut.json +0 -344
  270. package/dist/mcp-data/registry/chart-line.json +0 -223
  271. package/dist/mcp-data/registry/tooltip-data-viz.json +0 -224
  272. package/dist/react/ds-card-data-viz.d.ts +0 -12
  273. package/dist/react/ds-card-data-viz.js +0 -12
  274. package/dist/react/ds-chart-bar.js +0 -12
  275. package/dist/react/ds-chart-donut.d.ts +0 -12
  276. package/dist/react/ds-chart-donut.js +0 -12
  277. package/dist/react/ds-chart-line.d.ts +0 -9
  278. package/dist/react/ds-chart-line.js +0 -12
  279. package/dist/react/ds-tooltip-data-viz.d.ts +0 -9
  280. package/dist/react/ds-tooltip-data-viz.js +0 -12
  281. package/dist/types/components/CardDataViz/index.d.ts +0 -2
  282. package/dist/types/components/ChartBar/ChartBar.d.ts +0 -31
  283. package/dist/types/components/ChartBar/index.d.ts +0 -3
  284. package/dist/types/components/ChartDonut/ChartDonut.d.ts +0 -72
  285. package/dist/types/components/ChartDonut/index.d.ts +0 -2
  286. package/dist/types/components/ChartLine/ChartLine.d.ts +0 -23
  287. package/dist/types/components/ChartLine/index.d.ts +0 -2
  288. package/dist/types/components/TooltipDataViz/index.d.ts +0 -2
  289. /package/dist/components/{ds-tooltip-data-viz.js.map → ds-tooltip-chart.js.map} +0 -0
  290. /package/dist/components/{p-DaqFuVKZ.js.map → p-B16OuqyJ.js.map} +0 -0
  291. /package/dist/components/{p-BmNpPyzf.js.map → p-B4VlfBNc.js.map} +0 -0
  292. /package/dist/components/{p-og48Kh2i.js.map → p-B6z51yBf.js.map} +0 -0
  293. /package/dist/components/{p-Bb4f5SuF.js.map → p-BNiIyZCn.js.map} +0 -0
  294. /package/dist/components/{p-BFfHIu6y.js.map → p-BP-EtYbR.js.map} +0 -0
  295. /package/dist/components/{p-DHF5cLzr.js.map → p-BQ7Nzx9A.js.map} +0 -0
  296. /package/dist/components/{p-B-kRByho.js.map → p-BQDi0v_h.js.map} +0 -0
  297. /package/dist/components/{p-BBtjABBS.js.map → p-BYB0f1_F.js.map} +0 -0
  298. /package/dist/components/{p-DfvDNcoq.js.map → p-BjoyHNaM.js.map} +0 -0
  299. /package/dist/components/{p-Bl6A5AJq.js.map → p-BkWhTEqe.js.map} +0 -0
  300. /package/dist/components/{p-CxgMUvqg.js.map → p-CnbrksLl.js.map} +0 -0
  301. /package/dist/components/{p-Eep5mxtv.js.map → p-Cog5NTLa.js.map} +0 -0
  302. /package/dist/components/{p-Bmo4T96l.js.map → p-D2dQyoyK.js.map} +0 -0
  303. /package/dist/components/{p-BPyymeuR.js.map → p-D6g070eY.js.map} +0 -0
  304. /package/dist/components/{p-wOOAzVMC.js.map → p-D8qgVmHn.js.map} +0 -0
  305. /package/dist/components/{p-BR_oMaas.js.map → p-DJ0XMH9E.js.map} +0 -0
  306. /package/dist/components/{p-B6RrZQqw.js.map → p-DK4PGrHm.js.map} +0 -0
  307. /package/dist/components/{p-DVuxzrCC.js.map → p-DLwM1RNL.js.map} +0 -0
  308. /package/dist/components/{p-B4_0Ys4w.js.map → p-DgglLVCj.js.map} +0 -0
  309. /package/dist/components/{p-6B411hQA.js.map → p-Dv8_hmUZ.js.map} +0 -0
  310. /package/dist/components/{p-IYy-jODg.js.map → p-DwKC_9u2.js.map} +0 -0
  311. /package/dist/components/{p-yhj1lIPx.js.map → p-GbTasczF.js.map} +0 -0
  312. /package/dist/components/{p-CzraIk34.js.map → p-HIg1XX62.js.map} +0 -0
  313. /package/dist/components/{p-Di81y9Q2.js.map → p-KFHFBznh.js.map} +0 -0
  314. /package/dist/components/{p-CKRJX0Y0.js.map → p-PQgDzzn9.js.map} +0 -0
  315. /package/dist/components/{p-Ca6s5fLR.js.map → p-iyiOFsws.js.map} +0 -0
@@ -449,7 +449,7 @@
449
449
  "complexPropertyNote": "Assign these non-primitive values as JavaScript properties: actions, breadcrumbs, primaryAction, sections.",
450
450
  "peerDependencies": {
451
451
  "required": [
452
- "@ds-mo/tokens ^5.1.1",
452
+ "@ds-mo/tokens ^5.1.2",
453
453
  "@ds-mo/icons ^6.4.0"
454
454
  ],
455
455
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -351,7 +351,7 @@
351
351
  "complexPropertyNote": "Assign these non-primitive values as JavaScript properties: steps, submitAction.",
352
352
  "peerDependencies": {
353
353
  "required": [
354
- "@ds-mo/tokens ^5.1.1",
354
+ "@ds-mo/tokens ^5.1.2",
355
355
  "@ds-mo/icons ^6.4.0"
356
356
  ],
357
357
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -171,7 +171,7 @@
171
171
  "complexPropertyNote": "Assign these non-primitive values as JavaScript properties: items.",
172
172
  "peerDependencies": {
173
173
  "required": [
174
- "@ds-mo/tokens ^5.1.1",
174
+ "@ds-mo/tokens ^5.1.2",
175
175
  "@ds-mo/icons ^6.4.0"
176
176
  ],
177
177
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -491,7 +491,7 @@
491
491
  },
492
492
  "peerDependencies": {
493
493
  "required": [
494
- "@ds-mo/tokens ^5.1.1",
494
+ "@ds-mo/tokens ^5.1.2",
495
495
  "@ds-mo/icons ^6.4.0"
496
496
  ],
497
497
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -552,7 +552,7 @@
552
552
  },
553
553
  "peerDependencies": {
554
554
  "required": [
555
- "@ds-mo/tokens ^5.1.1",
555
+ "@ds-mo/tokens ^5.1.2",
556
556
  "@ds-mo/icons ^6.4.0"
557
557
  ],
558
558
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -0,0 +1,238 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "card-chart",
4
+ "title": "CardChart",
5
+ "description": "Standard chart card chrome with variant-owned chart and legend composition.",
6
+ "type": "registry:ui",
7
+ "meta": {
8
+ "library": "@ds-mo/ui",
9
+ "distribution": "npm",
10
+ "storybook": "https://zainadeel.github.io/compomo/?path=/story/card-chart",
11
+ "source": "src/wc/components/CardChart/CardChart.tsx",
12
+ "intentStatus": "complete",
13
+ "intent": {
14
+ "audience": "specialized",
15
+ "status": "stable",
16
+ "summary": "Standard chart card chrome with variant-owned chart and legend composition.",
17
+ "useWhen": [
18
+ "A chart, legend, metric visualization, or custom chart composition needs standard card chrome.",
19
+ "A chart and static external legend need one standard card layout.",
20
+ "A chart card needs a visible heading, optional filter action, and application-owned chart data."
21
+ ],
22
+ "avoidWhen": [
23
+ "The card contains editable settings; use CardSetting.",
24
+ "The surface contains ordinary non-visualization content.",
25
+ "The application needs to replace the card chrome rather than compose its chart and actions."
26
+ ],
27
+ "alternatives": [
28
+ {
29
+ "when": "The surface contains editable settings.",
30
+ "component": "component:ds-card-setting",
31
+ "reason": "CardSetting owns the controlled settings-section workflow."
32
+ }
33
+ ],
34
+ "commonlyComposedWith": [
35
+ "component:ds-chart",
36
+ "component:ds-chart-legend",
37
+ "component:ds-button-unfilled"
38
+ ],
39
+ "accessibility": [
40
+ "Provide a concise visible heading; the component renders it as a section heading.",
41
+ "Slotted actions must provide accessible names and controlled-state semantics.",
42
+ "Charts retain their own image names and data summaries.",
43
+ "A visible legend provides persistent labels while the chart retains its own accessible name and focus behavior."
44
+ ],
45
+ "states": [
46
+ "Custom mode provides flexible body chrome and does not mutate chart or legend interaction.",
47
+ "Chart mode keeps a slotted external legend static by disabling hover emphasis.",
48
+ "showFilter adds the standard filter action and emits dsFilterClick without owning filter state."
49
+ ],
50
+ "responsiveBehavior": [
51
+ "The sm, md, or lg width sets a matching token-based minimum height and remains constrained by its parent.",
52
+ "The body allocates remaining height to an unconfigured ds-chart while legends retain content height; authored chart height and aspectRatio remain authoritative.",
53
+ "Parent layouts own wrapping, stacking, and breakpoint-dependent card width."
54
+ ],
55
+ "frameworkCaveats": {
56
+ "customElements": [
57
+ "Set variant to chart for the standard chart and static legend layout; slot chart, legend, actions, and supporting content by name."
58
+ ],
59
+ "react": [
60
+ "Keep chart data in React and pass chart, legend, and action elements through named slots."
61
+ ],
62
+ "angular": [
63
+ "Keep chart data in Angular and project chart, legend, and action elements through named slots."
64
+ ]
65
+ },
66
+ "references": [
67
+ {
68
+ "label": "Storybook examples",
69
+ "path": "src/wc/components/CardChart/CardChart.stories.ts"
70
+ }
71
+ ]
72
+ },
73
+ "api": {
74
+ "props": {
75
+ "cardWidth": {
76
+ "type": "CardChartWidth",
77
+ "resolvedType": "\"lg\" | \"md\" | \"sm\"",
78
+ "attribute": "card-width",
79
+ "default": "'md'",
80
+ "required": false,
81
+ "mutable": false,
82
+ "description": "Width token with the matching chart-card min-height."
83
+ },
84
+ "filterLabel": {
85
+ "type": "string",
86
+ "resolvedType": "string",
87
+ "attribute": "filter-label",
88
+ "default": "'Filter'",
89
+ "required": false,
90
+ "mutable": false
91
+ },
92
+ "heading": {
93
+ "type": "string",
94
+ "resolvedType": "string",
95
+ "attribute": "heading",
96
+ "required": true,
97
+ "mutable": false,
98
+ "description": "Chart heading shown in the card header."
99
+ },
100
+ "showFilter": {
101
+ "type": "boolean",
102
+ "resolvedType": "boolean",
103
+ "attribute": "show-filter",
104
+ "default": "false",
105
+ "required": false,
106
+ "mutable": false,
107
+ "description": "Renders the standard filter action before custom actions."
108
+ },
109
+ "variant": {
110
+ "type": "CardChartVariant",
111
+ "resolvedType": "\"chart\" | \"custom\"",
112
+ "attribute": "variant",
113
+ "default": "'custom'",
114
+ "required": false,
115
+ "mutable": false,
116
+ "description": "Chart composition behavior."
117
+ }
118
+ },
119
+ "events": [
120
+ {
121
+ "name": "dsFilterClick",
122
+ "detail": "void",
123
+ "bubbles": true,
124
+ "cancelable": true,
125
+ "composed": true,
126
+ "description": "Emits when the standard header filter control is activated."
127
+ }
128
+ ],
129
+ "methods": [],
130
+ "slots": []
131
+ },
132
+ "props": {
133
+ "cardWidth": {
134
+ "type": "CardChartWidth",
135
+ "resolvedType": "\"lg\" | \"md\" | \"sm\"",
136
+ "attribute": "card-width",
137
+ "default": "'md'",
138
+ "required": false,
139
+ "mutable": false,
140
+ "description": "Width token with the matching chart-card min-height."
141
+ },
142
+ "filterLabel": {
143
+ "type": "string",
144
+ "resolvedType": "string",
145
+ "attribute": "filter-label",
146
+ "default": "'Filter'",
147
+ "required": false,
148
+ "mutable": false
149
+ },
150
+ "heading": {
151
+ "type": "string",
152
+ "resolvedType": "string",
153
+ "attribute": "heading",
154
+ "required": true,
155
+ "mutable": false,
156
+ "description": "Chart heading shown in the card header."
157
+ },
158
+ "showFilter": {
159
+ "type": "boolean",
160
+ "resolvedType": "boolean",
161
+ "attribute": "show-filter",
162
+ "default": "false",
163
+ "required": false,
164
+ "mutable": false,
165
+ "description": "Renders the standard filter action before custom actions."
166
+ },
167
+ "variant": {
168
+ "type": "CardChartVariant",
169
+ "resolvedType": "\"chart\" | \"custom\"",
170
+ "attribute": "variant",
171
+ "default": "'custom'",
172
+ "required": false,
173
+ "mutable": false,
174
+ "description": "Chart composition behavior."
175
+ }
176
+ },
177
+ "events": [
178
+ {
179
+ "name": "dsFilterClick",
180
+ "detail": "void",
181
+ "bubbles": true,
182
+ "cancelable": true,
183
+ "composed": true,
184
+ "description": "Emits when the standard header filter control is activated."
185
+ }
186
+ ],
187
+ "methods": [],
188
+ "slots": [],
189
+ "exports": {
190
+ "customElement": "ds-card-chart",
191
+ "react": "DsCardChart",
192
+ "angular": "DsCardChart"
193
+ },
194
+ "consumption": {
195
+ "install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
196
+ "cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
197
+ "customElements": {
198
+ "import": "import '@ds-mo/ui/dist/components/ds-card-chart.js';",
199
+ "example": "<ds-card-chart></ds-card-chart>"
200
+ },
201
+ "react": {
202
+ "import": "import { DsCardChart } from '@ds-mo/ui/react';",
203
+ "example": "<DsCardChart />"
204
+ },
205
+ "angular": {
206
+ "import": "import { DsCardChart } from '@ds-mo/ui/angular/ds-card-chart';",
207
+ "example": "<ds-card-chart></ds-card-chart>"
208
+ },
209
+ "peerDependencies": {
210
+ "required": [
211
+ "@ds-mo/tokens ^5.1.2",
212
+ "@ds-mo/icons ^6.4.0"
213
+ ],
214
+ "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
215
+ }
216
+ }
217
+ },
218
+ "dependencies": [
219
+ "@ds-mo/ui",
220
+ "@ds-mo/tokens"
221
+ ],
222
+ "registryDependencies": [
223
+ "button-unfilled",
224
+ "text"
225
+ ],
226
+ "files": [
227
+ {
228
+ "path": "src/wc/components/CardChart/CardChart.css",
229
+ "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\n 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)\n 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",
230
+ "type": "registry:ui"
231
+ },
232
+ {
233
+ "path": "src/wc/components/CardChart/CardChart.tsx",
234
+ "content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Prop,\n State,\n} from '@stencil/core';\n\nexport type CardChartWidth = 'sm' | 'md' | 'lg';\nexport type CardChartVariant = 'custom' | 'chart';\n\ntype LegendSlot = HTMLElement & {\n activeLabel?: string | null;\n highlightOnHover?: boolean;\n};\n\nconst CARD_WIDTH_VARS: Record<CardChartWidth, string> = {\n sm: 'var(--dimension-card-width-sm)',\n md: 'var(--dimension-card-width-md)',\n lg: 'var(--dimension-card-width-lg)',\n};\n\nconst CARD_HEIGHT_VARS: Record<CardChartWidth, string> = {\n sm: 'var(--dimension-card-height-sm)',\n md: 'var(--dimension-card-height-md)',\n lg: 'var(--dimension-card-height-lg)',\n};\n\n/**\n * Standard chart card chrome and composition. The variant owns\n * only the chart/legend relationship; applications continue to own data.\n */\n@Component({\n tag: 'ds-card-chart',\n styleUrl: 'CardChart.css',\n scoped: true,\n})\nexport class CardChart {\n @Element() el!: HTMLElement;\n\n /** Chart heading shown in the card header. */\n @Prop() heading!: string;\n /** Chart composition behavior. */\n @Prop() variant: CardChartVariant = 'custom';\n /** Width token with the matching chart-card min-height. */\n @Prop() cardWidth: CardChartWidth = 'md';\n /** Renders the standard filter action before custom actions. */\n @Prop() showFilter: boolean = false;\n @Prop() filterLabel: string = 'Filter';\n\n /** Emits when the standard header filter control is activated. */\n @Event() dsFilterClick!: EventEmitter<void>;\n\n @State() private hasChartSlot = false;\n @State() private hasLegendSlot = false;\n\n componentWillRender() {\n this.hasChartSlot = !!this.el.querySelector('[slot=\"chart\"]');\n this.hasLegendSlot = !!this.el.querySelector('[slot=\"legend\"]');\n }\n\n componentDidRender() {\n const legend = this.el.querySelector(\n 'ds-chart-legend[slot=\"legend\"]'\n ) as LegendSlot | null;\n if (this.variant === 'chart' && legend) {\n legend.setAttribute('highlight-on-hover', 'false');\n if ('highlightOnHover' in legend) legend.highlightOnHover = false;\n }\n }\n\n private handleFilterClick = () => {\n this.dsFilterClick.emit();\n };\n\n render() {\n const usesChartLayout = this.variant !== 'custom' || this.hasChartSlot;\n return (\n <Host\n class={{\n 'card-chart': true,\n [`card-chart--${this.variant}`]: true,\n }}\n style={{\n '--_card-chart-width': CARD_WIDTH_VARS[this.cardWidth],\n '--_card-chart-min-height': CARD_HEIGHT_VARS[this.cardWidth],\n }}\n >\n <header class=\"card-chart__header ds-chrome-header\">\n {/* eslint-disable-next-line local/prefer-direct-ds-text -- Shared header copy owns the control-density geometry around the semantic heading. */}\n <div class=\"card-chart__copy ds-chrome-header__copy ds-control--md\">\n <ds-text\n class=\"card-chart__title ds-chrome-header__heading\"\n variant=\"text-title-small\"\n emphasis\n color=\"primary\"\n as=\"h2\"\n >\n {this.heading}\n </ds-text>\n </div>\n <div class=\"card-chart__actions ds-chrome-header__trailing\">\n {this.showFilter ? (\n <ds-button-unfilled\n variant=\"icon\"\n type=\"button\"\n icon=\"Filters\"\n aria-label={this.filterLabel}\n onDsClick={this.handleFilterClick}\n />\n ) : null}\n <slot name=\"actions\" />\n </div>\n </header>\n <div class=\"card-chart__body\">\n {usesChartLayout ? (\n <div class=\"card-chart__layout\">\n {this.hasChartSlot ? (\n <div\n class={{\n 'card-chart__chart': true,\n }}\n >\n <slot name=\"chart\" />\n </div>\n ) : null}\n {this.hasLegendSlot ? (\n <div class=\"card-chart__legend\">\n <slot name=\"legend\" />\n </div>\n ) : null}\n <slot />\n </div>\n ) : (\n <slot />\n )}\n </div>\n </Host>\n );\n }\n}\n",
235
+ "type": "registry:ui"
236
+ }
237
+ ]
238
+ }
@@ -28,8 +28,8 @@
28
28
  "alternatives": [
29
29
  {
30
30
  "when": "A measure needs a chart or other data visualization.",
31
- "component": "component:ds-card-data-viz",
32
- "reason": "CardDataViz owns the data-visualization card contract; the overview grid presents values and change only."
31
+ "component": "component:ds-card-chart",
32
+ "reason": "CardChart owns the data-visualization card contract; the overview grid presents values and change only."
33
33
  },
34
34
  {
35
35
  "when": "The surface is a settings section with an edit workflow.",
@@ -55,11 +55,11 @@
55
55
  "Trend is supplied already resolved as a direction, a formatted value, and a tone. Whether a rise reads well is a product decision, so the component never infers tone from the numbers. Derive it with resolveMetricTrend.",
56
56
  "Omit a trend when there is nothing to report; resolveMetricTrend returns null in that case so no zero change is rendered.",
57
57
  "Omit score to render a centered Score unavailable cell with a tertiary border and no score fill. Set scoreErrorMessage to replace that copy while the measures remain readable.",
58
- "Loading replaces the score and the measures with skeletons and does not alter the slotted period control.",
58
+ "Loading replaces the score and measures with skeletons and does not alter the slotted period control. When measures are already supplied, their count preserves the resolved grid shape; otherwise five measure placeholders join the score in the standard six-cell grid.",
59
59
  "At most seven measures are shown. Keep larger collections outside the summary bar rather than turning it into a dense dashboard.",
60
60
  "The period and filter controls are slotted rather than owned. Use the period slot when the current range is selectable; otherwise periodLabel supplies fixed current copy in the same md control-frame and label-inset anatomy. Use the filter slot for the application-owned comparison-window control. Slotted Selects use hasBorder false so the 48px bar owns their surrounding boundary.",
61
61
  "A numeric safety score infers fair from 0–50, good from 51–80, and excellent from 81–100. The level can be supplied explicitly when the displayed value is not numeric. The deprecated band field is ignored.",
62
- "The safety score is the first grid cell and uses the same track, 8px outer inset, 6px inner padding, dividers, and responsive behavior as metric cells. The display value removes 12px from both block edges of its line box so it does not increase the shared row height. Score remains nonselectable and excluded from metric roving focus.",
62
+ "The safety score is the first grid cell and uses the same track, 8px outer inset, 6px inner padding, dividers, and responsive behavior as metric cells. The display value removes 8px from both block edges of its line box so it does not increase the shared row height. Score remains nonselectable and excluded from metric roving focus.",
63
63
  "During page-controlled scroll collapse, the component preserves its expanded flow height and stationary reporting bar while the metric grid moves and clips beneath it. Elevation remains fitted to the visible surface.",
64
64
  "compact is exactly 48px high and renders only the reporting bar. It omits Score, measures, and footer."
65
65
  ],
@@ -345,7 +345,7 @@
345
345
  "complexPropertyNote": "Assign these non-primitive values as JavaScript properties: metrics, score.",
346
346
  "peerDependencies": {
347
347
  "required": [
348
- "@ds-mo/tokens ^5.1.1",
348
+ "@ds-mo/tokens ^5.1.2",
349
349
  "@ds-mo/icons ^6.4.0"
350
350
  ],
351
351
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -364,12 +364,12 @@
364
364
  "files": [
365
365
  {
366
366
  "path": "src/wc/components/CardOverview/CardOverview.css",
367
- "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: space-between;\n gap: var(--dimension-space-100);\n background: var(--color-always-dark-interaction-active);\n color: var(--color-always-dark-foreground-primary);\n}\n\n.card-overview__score-content--fair {\n background: var(--color-safety-score-background-fair);\n color: var(--color-safety-score-foreground-on-fair);\n}\n\n.card-overview__score-content--good {\n background: var(--color-safety-score-background-good);\n color: var(--color-safety-score-foreground-on-good);\n}\n\n.card-overview__score-content--excellent {\n background: var(--color-safety-score-background-excellent);\n color: var(--color-safety-score-foreground-on-excellent);\n}\n\n.card-overview__score-copy {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--dimension-space-025);\n min-width: 0;\n}\n\n.card-overview__score-value,\n.card-overview__score-figure {\n flex: 0 0 auto;\n margin-block: calc(-1 * var(--dimension-space-150));\n}\n\n.card-overview__score-content--error {\n align-items: center;\n justify-content: center;\n border: var(--ds-card-overview-divider-width) solid\n 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-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-100);\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 {\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",
367
+ "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: space-between;\n gap: var(--dimension-space-100);\n background: var(--color-always-dark-interaction-active);\n color: var(--color-always-dark-foreground-primary);\n}\n\n.card-overview__score-content--fair {\n background: var(--color-safety-score-background-fair);\n color: var(--color-safety-score-foreground-on-fair);\n}\n\n.card-overview__score-content--good {\n background: var(--color-safety-score-background-good);\n color: var(--color-safety-score-foreground-on-good);\n}\n\n.card-overview__score-content--excellent {\n background: var(--color-safety-score-background-excellent);\n color: var(--color-safety-score-foreground-on-excellent);\n}\n\n.card-overview__score-copy {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--dimension-space-025);\n min-width: 0;\n}\n\n.card-overview__score-value,\n.card-overview__score-figure {\n flex: 0 0 auto;\n margin-block: calc(-1 * var(--dimension-space-100));\n}\n\n.card-overview__score-content--error {\n align-items: center;\n justify-content: center;\n border: var(--ds-card-overview-divider-width) solid\n 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-100);\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 {\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",
368
368
  "type": "registry:ui"
369
369
  },
370
370
  {
371
371
  "path": "src/wc/components/CardOverview/CardOverview.tsx",
372
- "content": "import { Component, Element, Event, EventEmitter, h, Host, Prop, State } from '@stencil/core';\nimport { resolveCssLengthPx, TOKEN_DEFAULTS } from '../../utils';\nimport type { TextColor, TextVariant } from '../Text/text-types';\nimport type { MetricTrend } from '../../utils/metric-change';\nimport type {\n CardOverviewLayout,\n CardOverviewVariant,\n OverviewMetric,\n OverviewScore,\n} from './card-overview-types';\nimport {\n findNextOverviewMetricIndex,\n resolveCardOverviewCollapseGeometry,\n resolveOverviewGridColumns,\n resolveOverviewRovingIndex,\n resolveSafetyScoreLevel,\n} from './card-overview-controller';\n\n/** Metric cells below this width drop a grid column rather than compress further. */\nconst DEFAULT_METRIC_MIN_WIDTH = 'var(--dimension-menu-width-xs)';\n\n/** Skeleton cells rendered while loading with no metrics supplied yet. */\nconst LOADING_PLACEHOLDER_COUNT = 4;\n\n/** Maximum number of comparable measures the summary bar presents at once. */\nconst MAX_METRIC_COUNT = 7;\n\nconst TREND_COLORS: Record<MetricTrend['tone'], TextColor> = {\n positive: 'var(--color-always-dark-foreground-positive)',\n negative: 'var(--color-always-dark-foreground-negative)',\n neutral: 'var(--color-always-dark-foreground-secondary)',\n};\n\nconst ALWAYS_DARK_PRIMARY: TextColor = 'var(--color-always-dark-foreground-primary)';\nconst ALWAYS_DARK_SECONDARY: TextColor = 'var(--color-always-dark-foreground-secondary)';\n\n@Component({\n tag: 'ds-card-overview',\n styleUrl: 'CardOverview.css',\n scoped: true,\n})\nexport class CardOverview {\n @Element() el!: HTMLElement;\n\n /** Full summary card, or the condensed 48px summary bar. */\n @Prop() variant: CardOverviewVariant = 'default';\n\n /** Intrinsic responsive layout, or a page-owned single-column stack. */\n @Prop() layout: CardOverviewLayout = 'auto';\n\n /** Optional nonselectable safety score rendered as the first grid cell. */\n @Prop() score: OverviewScore | undefined;\n\n /** Fixed current date or range. Replaced by content in the `period` slot. */\n @Prop() periodLabel: string = '';\n\n /** Copy between the current period and comparison control, usually `vs.`. */\n @Prop() comparisonLabel: string = '';\n\n /** Measures rendered in the responsive grid. Only the first seven are shown. */\n @Prop() metrics: OverviewMetric[] = [];\n\n /**\n * Width a metric cell may shrink to before the grid drops a column. The grid\n * uses this threshold while choosing an evenly distributed column count.\n */\n @Prop() metricMinWidth: string = DEFAULT_METRIC_MIN_WIDTH;\n\n /** Replace the score and metrics with skeletons while data resolves. */\n @Prop() isLoading: boolean = false;\n\n /** Message shown in place of the score when its figure cannot be resolved. */\n @Prop() scoreErrorMessage: string | undefined;\n\n /** Accessible name for the region. */\n @Prop() overviewLabel: string = 'Overview';\n\n /**\n * Page-controlled visual collapse from the full card (`0`) toward its 48px\n * compact handoff height (`1`). The component preserves its expanded flow\n * height, keeps the period bar stationary, and moves/clips the grid beneath\n * it. The page still owns sticky positioning and the final compact handoff.\n */\n @Prop() scrollCollapseProgress: number = 0;\n\n /** Emitted when a metric that is not inactive is activated. */\n @Event() dsMetricSelect!: EventEmitter<OverviewMetric>;\n\n /** Roving tab stop across selectable metrics. */\n @State() private focusedMetricIndex: number = 0;\n @State() private expandedHeight: number = 0;\n @State() private gridColumns: number | undefined;\n\n private layoutEl?: HTMLElement;\n private layoutResizeObserver: ResizeObserver | null = null;\n\n componentDidLoad() {\n this.observeLayout();\n }\n\n componentDidRender() {\n // Re-bind after HMR when DidLoad's observer was dropped.\n if (!this.layoutResizeObserver) this.observeLayout();\n else this.updateLayoutGeometry();\n }\n\n disconnectedCallback() {\n this.layoutResizeObserver?.disconnect();\n this.layoutResizeObserver = null;\n }\n\n private get visibleMetrics(): OverviewMetric[] {\n return this.metrics.slice(0, MAX_METRIC_COUNT);\n }\n\n private get visibleCellCount(): number {\n const metricCount =\n this.isLoading && this.metrics.length === 0\n ? LOADING_PLACEHOLDER_COUNT\n : this.visibleMetrics.length;\n return 1 + metricCount;\n }\n\n private get resolvedScrollCollapseProgress(): number {\n if (this.variant === 'compact' || !Number.isFinite(this.scrollCollapseProgress)) return 0;\n return Math.min(1, Math.max(0, this.scrollCollapseProgress));\n }\n\n private get scrollCollapseGeometry() {\n const compactHeight = resolveCssLengthPx(TOKEN_DEFAULTS.size600, 48);\n return resolveCardOverviewCollapseGeometry({\n variant: this.variant,\n progress: this.resolvedScrollCollapseProgress,\n expandedHeight: this.expandedHeight,\n compactHeight,\n });\n }\n\n private observeLayout() {\n const layout = this.layoutEl;\n if (!layout) return;\n\n const bounds = layout.getBoundingClientRect();\n this.updateLayoutGeometry(bounds.width, bounds.height);\n if (typeof ResizeObserver === 'undefined') return;\n\n this.layoutResizeObserver?.disconnect();\n this.layoutResizeObserver = new ResizeObserver(entries => {\n const rect = entries[0]?.contentRect;\n if (rect) this.updateLayoutGeometry(rect.width, rect.height);\n });\n this.layoutResizeObserver.observe(layout);\n }\n\n private updateLayoutGeometry(width?: number, height?: number) {\n const layout = this.layoutEl;\n if (!layout) return;\n\n const bounds = width === undefined || height === undefined ? layout.getBoundingClientRect() : null;\n const resolvedWidth = width ?? bounds?.width ?? 0;\n const resolvedHeight = height ?? bounds?.height ?? 0;\n\n if (resolvedHeight > 0 && Math.abs(resolvedHeight - this.expandedHeight) >= 0.5) {\n this.expandedHeight = resolvedHeight;\n }\n\n const columns = resolveOverviewGridColumns({\n cellCount: this.visibleCellCount,\n availableWidth: resolvedWidth,\n minCellWidth: resolveCssLengthPx(this.metricMinWidth, TOKEN_DEFAULTS.menuWidthXs),\n });\n if (columns !== this.gridColumns) this.gridColumns = columns;\n }\n\n /** Index of the roving tab stop, skipping inactive metrics. */\n private get rovingIndex(): number {\n return resolveOverviewRovingIndex(this.visibleMetrics, this.focusedMetricIndex);\n }\n\n private selectMetric(metric: OverviewMetric, index: number) {\n if (metric.isInactive) return;\n this.focusedMetricIndex = index;\n this.dsMetricSelect.emit(metric);\n }\n\n /** Move the roving tab stop, wrapping and skipping inactive metrics. */\n private moveFocus(from: number, step: number) {\n const metrics = this.visibleMetrics;\n const next = findNextOverviewMetricIndex(metrics, from, step < 0 ? -1 : 1);\n if (next < 0) return;\n\n this.focusedMetricIndex = next;\n const cells = this.el.querySelectorAll<HTMLElement>('.card-overview__metric-action');\n cells[next]?.focus();\n }\n\n private handleMetricKeyDown = (event: KeyboardEvent, index: number) => {\n switch (event.key) {\n case 'ArrowRight':\n case 'ArrowDown':\n event.preventDefault();\n this.moveFocus(index, 1);\n break;\n case 'ArrowLeft':\n case 'ArrowUp':\n event.preventDefault();\n this.moveFocus(index, -1);\n break;\n default:\n break;\n }\n };\n\n /**\n * A skeleton bar matching the type metrics of the text it stands in for, so the\n * loading state holds the same shape and rhythm as the resolved content.\n *\n * `background` is always passed so the base and shimmer follow the card's\n * permanently dark surface in both themes.\n */\n private bar(textVariant: TextVariant, width: string, className?: string) {\n return (\n <ds-skeleton\n class={className}\n variant=\"text\"\n textVariant={textVariant}\n width={width}\n background=\"always-dark\"\n />\n );\n }\n\n /**\n * Arrow and change in one text node.\n *\n * The arrow is a typeface glyph rather than an icon so it inherits the exact\n * size, weight, and baseline of the value beside it; an icon would need its\n * own size scale and would drift whenever the type scale changed.\n *\n * Tone is supplied by the caller and never inferred here.\n */\n private renderTrend(\n trend: MetricTrend | undefined,\n variant: TextVariant,\n emphasis = false,\n color?: TextColor\n ) {\n if (!trend) return null;\n return (\n <ds-text\n as=\"span\"\n class={{\n 'card-overview__trend': true,\n 'ds-control-label-box': true,\n [`card-overview__trend--${trend.tone}`]: true,\n }}\n variant={variant}\n emphasis={emphasis}\n color={color ?? TREND_COLORS[trend.tone]}\n fontFeature=\"tabular-nums\"\n >\n {trend.direction === 'up' ? '↑' : '↓'} {trend.value}\n </ds-text>\n );\n }\n\n private renderScore() {\n if (this.isLoading) {\n return (\n <div class=\"card-overview__score\" part=\"score\">\n <div class=\"card-overview__score-content\">\n <div class=\"card-overview__score-copy\">\n {this.bar('text-body-small', '64px', 'card-overview__score-label')}\n {this.bar('text-body-medium', '28px')}\n </div>\n <div class=\"card-overview__score-figure\">\n {this.bar('text-display-medium', '56px')}\n </div>\n </div>\n </div>\n );\n }\n\n const unavailableMessage =\n this.scoreErrorMessage || (!this.score ? 'Score unavailable' : undefined);\n if (unavailableMessage) {\n return (\n /* eslint-disable-next-line local/prefer-direct-ds-text -- The score track owns the grid divider and 8px outer inset. */\n <div class=\"card-overview__score\" part=\"score\">\n <ds-text\n as=\"span\"\n class=\"card-overview__score-content card-overview__score-content--error\"\n variant=\"text-body-medium\"\n color=\"inherit\"\n role=\"alert\"\n >\n {unavailableMessage}\n </ds-text>\n </div>\n );\n }\n\n const score = this.score;\n if (!score) return null;\n const level = score.level ?? resolveSafetyScoreLevel(score.value);\n\n return (\n <div class=\"card-overview__score\" part=\"score\">\n <div\n class={{\n 'card-overview__score-content': true,\n [`card-overview__score-content--${level}`]: Boolean(level),\n }}\n >\n <div class=\"card-overview__score-copy\">\n <ds-text\n as=\"span\"\n class=\"card-overview__score-label ds-control-label-box\"\n variant=\"text-body-small\"\n color=\"inherit\"\n >\n {score.label}\n </ds-text>\n {this.renderTrend(score.trend, 'text-body-medium', false, 'inherit')}\n </div>\n <ds-text\n as=\"span\"\n class=\"card-overview__score-value ds-control-label-box\"\n variant=\"text-display-medium\"\n emphasis\n color=\"inherit\"\n fontFeature=\"tabular-nums\"\n >\n {score.value}\n </ds-text>\n </div>\n </div>\n );\n }\n\n private renderMetric(metric: OverviewMetric, index: number) {\n const selectable = !metric.isInactive;\n const label = (\n <ds-text\n as=\"span\"\n class=\"card-overview__metric-label ds-control-label-box\"\n variant=\"text-body-small\"\n color={ALWAYS_DARK_SECONDARY}\n >\n {metric.label}\n </ds-text>\n );\n\n return (\n <div class=\"card-overview__metric\" part=\"metric\">\n <div\n class={{\n 'card-overview__metric-action': true,\n 'card-overview__metric-action--inactive': !selectable,\n // Selection targets get the shared wash; press scaling is not used here\n // so the grid keeps its columns aligned. See docs/control-press-policy.md.\n 'ds-interaction-fill': selectable,\n 'ds-interaction-fill--on-always-dark': selectable,\n 'ds-focus-ring-inset': selectable,\n }}\n role={selectable ? 'button' : undefined}\n tabIndex={selectable && index === this.rovingIndex ? 0 : -1}\n onClick={() => this.selectMetric(metric, index)}\n onKeyDown={event => this.handleMetricKeyDown(event, index)}\n onFocusin={() => {\n if (selectable) this.focusedMetricIndex = index;\n }}\n >\n <div class=\"ds-interaction-fill__content card-overview__metric-content\">\n {metric.labelTooltip ? (\n <ds-tooltip label={metric.labelTooltip} side=\"top\" size=\"sm\">\n {label}\n </ds-tooltip>\n ) : (\n label\n )}\n <div class=\"card-overview__metric-figure\">\n <ds-text\n as=\"span\"\n class=\"card-overview__metric-value ds-control-label-box\"\n variant=\"text-body-medium\"\n color={ALWAYS_DARK_PRIMARY}\n fontFeature=\"tabular-nums\"\n >\n {metric.value}\n </ds-text>\n {this.renderTrend(metric.trend, 'text-body-medium')}\n </div>\n </div>\n </div>\n </div>\n );\n }\n\n private renderMetrics() {\n if (this.isLoading && this.metrics.length === 0) {\n return [\n this.renderScore(),\n ...Array.from({ length: LOADING_PLACEHOLDER_COUNT }, (_, index) => (\n <div class=\"card-overview__metric\" key={`loading-${index}`}>\n <div class=\"card-overview__metric-action card-overview__metric-action--inactive\">\n {this.bar('text-body-small', '70%', 'card-overview__metric-label')}\n <div class=\"card-overview__metric-figure\">\n {this.bar('text-body-medium', '36px')}\n {/* text-body-medium, matching renderTrend for a metric — a smaller\n * variant here leaves the trend bar short of the value beside it. */}\n {this.bar('text-body-medium', '40px')}\n </div>\n </div>\n </div>\n )),\n ];\n }\n\n return [\n this.renderScore(),\n ...this.visibleMetrics.map((metric, index) => this.renderMetric(metric, index)),\n ];\n }\n\n render() {\n const compact = this.variant === 'compact';\n const collapse = this.scrollCollapseGeometry;\n\n return (\n <Host\n class={{\n 'card-overview': true,\n 'card-overview--compact': compact,\n 'card-overview--stacked': !compact && this.layout === 'stacked',\n 'card-overview--scroll-collapsing': collapse.active,\n }}\n role=\"region\"\n aria-label={this.overviewLabel}\n aria-busy={this.isLoading ? 'true' : undefined}\n style={{\n '--ds-card-overview-metric-min': this.metricMinWidth,\n '--ds-card-overview-grid-columns': this.gridColumns\n ? String(this.gridColumns)\n : undefined,\n '--ds-card-overview-expanded-height': collapse.active\n ? `${collapse.expandedHeight}px`\n : undefined,\n '--ds-card-overview-visible-height': collapse.active\n ? `${collapse.visibleHeight}px`\n : undefined,\n '--ds-card-overview-content-offset': collapse.active\n ? `${collapse.offset}px`\n : undefined,\n }}\n >\n <div\n class={{\n 'card-overview__surface': true,\n // Split shadow and inset highlight, so the opaque surface cannot cover\n // the highlight the way a combined elevation shadow would.\n 'ds-control-elevation': true,\n 'ds-control-elevation--sm': !compact,\n 'ds-control-elevation--floating': compact,\n }}\n >\n <div class=\"card-overview__clip\">\n <div\n ref={element => {\n this.layoutEl = (element as HTMLElement) ?? undefined;\n }}\n class={{\n 'card-overview__layout': true,\n }}\n >\n <div class=\"card-overview__summary\">\n <div class=\"card-overview__header ds-control--md\">\n {this.isLoading ? (\n <div class=\"card-overview__period\">\n {this.bar('text-body-medium', '184px')}\n {this.bar('text-body-medium', '248px')}\n </div>\n ) : (\n <div class=\"card-overview__period\">\n <div class=\"card-overview__period-current\">\n <slot name=\"period\">\n {this.periodLabel && (\n /* eslint-disable-next-line local/prefer-direct-ds-text -- Fixed copy uses the same structural frame and label inset as a slotted Select. */\n <div class=\"card-overview__period-fixed ds-control-frame\">\n <ds-text\n as=\"span\"\n class=\"ds-control-label-box\"\n variant=\"text-body-medium\"\n emphasis\n color={ALWAYS_DARK_PRIMARY}\n >\n {this.periodLabel}\n </ds-text>\n </div>\n )}\n </slot>\n </div>\n {this.comparisonLabel && (\n <ds-text\n as=\"span\"\n class=\"card-overview__period-comparison ds-control-label-box\"\n variant=\"text-body-medium\"\n color={ALWAYS_DARK_SECONDARY}\n >\n {this.comparisonLabel}\n </ds-text>\n )}\n </div>\n )}\n <div class=\"card-overview__filter\">\n {/*\n Period control is application owned: it varies by product surface.\n While loading it is stood in for by a control-shaped skeleton so the\n header keeps its resolved proportions.\n */}\n {this.isLoading ? (\n <ds-skeleton\n variant=\"control\"\n controlSize=\"md\"\n width=\"128px\"\n background=\"always-dark\"\n />\n ) : (\n <slot name=\"filter\" />\n )}\n </div>\n </div>\n </div>\n\n {!compact && (\n <div class=\"card-overview__body\">\n <div class=\"card-overview__metrics\" part=\"metrics\">\n {this.renderMetrics()}\n </div>\n\n <slot name=\"footer\" />\n </div>\n )}\n </div>\n </div>\n </div>\n </Host>\n );\n }\n}\n",
372
+ "content": "import { Component, Element, Event, EventEmitter, h, Host, Prop, State } from '@stencil/core';\nimport { resolveCssLengthPx, TOKEN_DEFAULTS } from '../../utils';\nimport type { TextColor, TextVariant } from '../Text/text-types';\nimport type { MetricTrend } from '../../utils/metric-change';\nimport type {\n CardOverviewLayout,\n CardOverviewVariant,\n OverviewMetric,\n OverviewScore,\n} from './card-overview-types';\nimport {\n findNextOverviewMetricIndex,\n resolveCardOverviewCollapseGeometry,\n resolveOverviewGridColumns,\n resolveOverviewRovingIndex,\n resolveSafetyScoreLevel,\n} from './card-overview-controller';\n\n/** Metric cells below this width drop a grid column rather than compress further. */\nconst DEFAULT_METRIC_MIN_WIDTH = 'var(--dimension-menu-width-xs)';\n\n/** Skeleton cells rendered while loading with no metrics supplied yet. */\nconst DEFAULT_LOADING_METRIC_COUNT = 5;\n\n/** Maximum number of comparable measures the summary bar presents at once. */\nconst MAX_METRIC_COUNT = 7;\n\nconst TREND_COLORS: Record<MetricTrend['tone'], TextColor> = {\n positive: 'var(--color-always-dark-foreground-positive)',\n negative: 'var(--color-always-dark-foreground-negative)',\n neutral: 'var(--color-always-dark-foreground-secondary)',\n};\n\nconst ALWAYS_DARK_PRIMARY: TextColor = 'var(--color-always-dark-foreground-primary)';\nconst ALWAYS_DARK_SECONDARY: TextColor = 'var(--color-always-dark-foreground-secondary)';\n\n@Component({\n tag: 'ds-card-overview',\n styleUrl: 'CardOverview.css',\n scoped: true,\n})\nexport class CardOverview {\n @Element() el!: HTMLElement;\n\n /** Full summary card, or the condensed 48px summary bar. */\n @Prop() variant: CardOverviewVariant = 'default';\n\n /** Intrinsic responsive layout, or a page-owned single-column stack. */\n @Prop() layout: CardOverviewLayout = 'auto';\n\n /** Optional nonselectable safety score rendered as the first grid cell. */\n @Prop() score: OverviewScore | undefined;\n\n /** Fixed current date or range. Replaced by content in the `period` slot. */\n @Prop() periodLabel: string = '';\n\n /** Copy between the current period and comparison control, usually `vs.`. */\n @Prop() comparisonLabel: string = '';\n\n /** Measures rendered in the responsive grid. Only the first seven are shown. */\n @Prop() metrics: OverviewMetric[] = [];\n\n /**\n * Width a metric cell may shrink to before the grid drops a column. The grid\n * uses this threshold while choosing an evenly distributed column count.\n */\n @Prop() metricMinWidth: string = DEFAULT_METRIC_MIN_WIDTH;\n\n /** Replace the score and metrics with skeletons while data resolves. */\n @Prop() isLoading: boolean = false;\n\n /** Message shown in place of the score when its figure cannot be resolved. */\n @Prop() scoreErrorMessage: string | undefined;\n\n /** Accessible name for the region. */\n @Prop() overviewLabel: string = 'Overview';\n\n /**\n * Page-controlled visual collapse from the full card (`0`) toward its 48px\n * compact handoff height (`1`). The component preserves its expanded flow\n * height, keeps the period bar stationary, and moves/clips the grid beneath\n * it. The page still owns sticky positioning and the final compact handoff.\n */\n @Prop() scrollCollapseProgress: number = 0;\n\n /** Emitted when a metric that is not inactive is activated. */\n @Event() dsMetricSelect!: EventEmitter<OverviewMetric>;\n\n /** Roving tab stop across selectable metrics. */\n @State() private focusedMetricIndex: number = 0;\n @State() private expandedHeight: number = 0;\n @State() private gridColumns: number | undefined;\n\n private layoutEl?: HTMLElement;\n private layoutResizeObserver: ResizeObserver | null = null;\n\n componentDidLoad() {\n this.observeLayout();\n }\n\n componentDidRender() {\n // Re-bind after HMR when DidLoad's observer was dropped.\n if (!this.layoutResizeObserver) this.observeLayout();\n else this.updateLayoutGeometry();\n }\n\n disconnectedCallback() {\n this.layoutResizeObserver?.disconnect();\n this.layoutResizeObserver = null;\n }\n\n private get visibleMetrics(): OverviewMetric[] {\n return this.metrics.slice(0, MAX_METRIC_COUNT);\n }\n\n private get visibleCellCount(): number {\n const metricCount = this.isLoading ? this.loadingMetricCount : this.visibleMetrics.length;\n return 1 + metricCount;\n }\n\n /** Preserve an authored data shape while loading, or use the standard six-cell grid. */\n private get loadingMetricCount(): number {\n return this.visibleMetrics.length || DEFAULT_LOADING_METRIC_COUNT;\n }\n\n private get resolvedScrollCollapseProgress(): number {\n if (this.variant === 'compact' || !Number.isFinite(this.scrollCollapseProgress)) return 0;\n return Math.min(1, Math.max(0, this.scrollCollapseProgress));\n }\n\n private get scrollCollapseGeometry() {\n const compactHeight = resolveCssLengthPx(TOKEN_DEFAULTS.size600, 48);\n return resolveCardOverviewCollapseGeometry({\n variant: this.variant,\n progress: this.resolvedScrollCollapseProgress,\n expandedHeight: this.expandedHeight,\n compactHeight,\n });\n }\n\n private observeLayout() {\n const layout = this.layoutEl;\n if (!layout) return;\n\n const bounds = layout.getBoundingClientRect();\n this.updateLayoutGeometry(bounds.width, bounds.height);\n if (typeof ResizeObserver === 'undefined') return;\n\n this.layoutResizeObserver?.disconnect();\n this.layoutResizeObserver = new ResizeObserver(entries => {\n const rect = entries[0]?.contentRect;\n if (rect) this.updateLayoutGeometry(rect.width, rect.height);\n });\n this.layoutResizeObserver.observe(layout);\n }\n\n private updateLayoutGeometry(width?: number, height?: number) {\n const layout = this.layoutEl;\n if (!layout) return;\n\n const bounds = width === undefined || height === undefined ? layout.getBoundingClientRect() : null;\n const resolvedWidth = width ?? bounds?.width ?? 0;\n const resolvedHeight = height ?? bounds?.height ?? 0;\n\n if (resolvedHeight > 0 && Math.abs(resolvedHeight - this.expandedHeight) >= 0.5) {\n this.expandedHeight = resolvedHeight;\n }\n\n const columns = resolveOverviewGridColumns({\n cellCount: this.visibleCellCount,\n availableWidth: resolvedWidth,\n minCellWidth: resolveCssLengthPx(this.metricMinWidth, TOKEN_DEFAULTS.menuWidthXs),\n });\n if (columns !== this.gridColumns) this.gridColumns = columns;\n }\n\n /** Index of the roving tab stop, skipping inactive metrics. */\n private get rovingIndex(): number {\n return resolveOverviewRovingIndex(this.visibleMetrics, this.focusedMetricIndex);\n }\n\n private selectMetric(metric: OverviewMetric, index: number) {\n if (metric.isInactive) return;\n this.focusedMetricIndex = index;\n this.dsMetricSelect.emit(metric);\n }\n\n /** Move the roving tab stop, wrapping and skipping inactive metrics. */\n private moveFocus(from: number, step: number) {\n const metrics = this.visibleMetrics;\n const next = findNextOverviewMetricIndex(metrics, from, step < 0 ? -1 : 1);\n if (next < 0) return;\n\n this.focusedMetricIndex = next;\n const cells = this.el.querySelectorAll<HTMLElement>('.card-overview__metric-action');\n cells[next]?.focus();\n }\n\n private handleMetricKeyDown = (event: KeyboardEvent, index: number) => {\n switch (event.key) {\n case 'ArrowRight':\n case 'ArrowDown':\n event.preventDefault();\n this.moveFocus(index, 1);\n break;\n case 'ArrowLeft':\n case 'ArrowUp':\n event.preventDefault();\n this.moveFocus(index, -1);\n break;\n default:\n break;\n }\n };\n\n /**\n * A skeleton bar matching the type metrics of the text it stands in for, so the\n * loading state holds the same shape and rhythm as the resolved content.\n *\n * `background` is always passed so the base and shimmer follow the card's\n * permanently dark surface in both themes.\n */\n private bar(textVariant: TextVariant, width: string, className?: string) {\n return (\n <ds-skeleton\n class={className}\n variant=\"text\"\n textVariant={textVariant}\n width={width}\n background=\"always-dark\"\n />\n );\n }\n\n /**\n * Arrow and change in one text node.\n *\n * The arrow is a typeface glyph rather than an icon so it inherits the exact\n * size, weight, and baseline of the value beside it; an icon would need its\n * own size scale and would drift whenever the type scale changed.\n *\n * Tone is supplied by the caller and never inferred here.\n */\n private renderTrend(\n trend: MetricTrend | undefined,\n variant: TextVariant,\n emphasis = false,\n color?: TextColor\n ) {\n if (!trend) return null;\n return (\n <ds-text\n as=\"span\"\n class={{\n 'card-overview__trend': true,\n 'ds-control-label-box': true,\n [`card-overview__trend--${trend.tone}`]: true,\n }}\n variant={variant}\n emphasis={emphasis}\n color={color ?? TREND_COLORS[trend.tone]}\n fontFeature=\"tabular-nums\"\n >\n {trend.direction === 'up' ? '↑' : '↓'} {trend.value}\n </ds-text>\n );\n }\n\n private renderScore() {\n if (this.isLoading) {\n return (\n <div class=\"card-overview__score\" part=\"score\">\n <div class=\"card-overview__score-content\">\n <div class=\"card-overview__score-copy\">\n {this.bar('text-body-small', '64px', 'card-overview__score-label')}\n {this.bar('text-body-medium', '28px')}\n </div>\n <div class=\"card-overview__score-figure\">\n {this.bar('text-display-medium', '56px')}\n </div>\n </div>\n </div>\n );\n }\n\n const unavailableMessage =\n this.scoreErrorMessage || (!this.score ? 'Score unavailable' : undefined);\n if (unavailableMessage) {\n return (\n /* eslint-disable-next-line local/prefer-direct-ds-text -- The score track owns the grid divider and 8px outer inset. */\n <div class=\"card-overview__score\" part=\"score\">\n <ds-text\n as=\"span\"\n class=\"card-overview__score-content card-overview__score-content--error\"\n variant=\"text-body-medium\"\n color=\"inherit\"\n role=\"alert\"\n >\n {unavailableMessage}\n </ds-text>\n </div>\n );\n }\n\n const score = this.score;\n if (!score) return null;\n const level = score.level ?? resolveSafetyScoreLevel(score.value);\n\n return (\n <div class=\"card-overview__score\" part=\"score\">\n <div\n class={{\n 'card-overview__score-content': true,\n [`card-overview__score-content--${level}`]: Boolean(level),\n }}\n >\n <div class=\"card-overview__score-copy\">\n <ds-text\n as=\"span\"\n class=\"card-overview__score-label ds-control-label-box\"\n variant=\"text-body-small\"\n color=\"inherit\"\n >\n {score.label}\n </ds-text>\n {this.renderTrend(score.trend, 'text-body-medium', false, 'inherit')}\n </div>\n <ds-text\n as=\"span\"\n class=\"card-overview__score-value ds-control-label-box\"\n variant=\"text-display-medium\"\n emphasis\n color=\"inherit\"\n fontFeature=\"tabular-nums\"\n >\n {score.value}\n </ds-text>\n </div>\n </div>\n );\n }\n\n private renderMetric(metric: OverviewMetric, index: number) {\n const selectable = !metric.isInactive;\n const label = (\n <ds-text\n as=\"span\"\n class=\"card-overview__metric-label ds-control-label-box\"\n variant=\"text-body-small\"\n color={ALWAYS_DARK_SECONDARY}\n >\n {metric.label}\n </ds-text>\n );\n\n return (\n <div class=\"card-overview__metric\" part=\"metric\">\n <div\n class={{\n 'card-overview__metric-action': true,\n 'card-overview__metric-action--inactive': !selectable,\n // Selection targets get the shared wash; press scaling is not used here\n // so the grid keeps its columns aligned. See docs/control-press-policy.md.\n 'ds-interaction-fill': selectable,\n 'ds-interaction-fill--on-always-dark': selectable,\n 'ds-focus-ring-inset': selectable,\n }}\n role={selectable ? 'button' : undefined}\n tabIndex={selectable && index === this.rovingIndex ? 0 : -1}\n onClick={() => this.selectMetric(metric, index)}\n onKeyDown={event => this.handleMetricKeyDown(event, index)}\n onFocusin={() => {\n if (selectable) this.focusedMetricIndex = index;\n }}\n >\n <div class=\"ds-interaction-fill__content card-overview__metric-content\">\n {metric.labelTooltip ? (\n <ds-tooltip label={metric.labelTooltip} side=\"top\" size=\"sm\">\n {label}\n </ds-tooltip>\n ) : (\n label\n )}\n <div class=\"card-overview__metric-figure\">\n <ds-text\n as=\"span\"\n class=\"card-overview__metric-value ds-control-label-box\"\n variant=\"text-body-medium\"\n color={ALWAYS_DARK_PRIMARY}\n fontFeature=\"tabular-nums\"\n >\n {metric.value}\n </ds-text>\n {this.renderTrend(metric.trend, 'text-body-medium')}\n </div>\n </div>\n </div>\n </div>\n );\n }\n\n private renderMetrics() {\n if (this.isLoading) {\n return [\n this.renderScore(),\n ...Array.from({ length: this.loadingMetricCount }, (_, index) => (\n <div class=\"card-overview__metric\" key={`loading-${index}`}>\n <div class=\"card-overview__metric-action card-overview__metric-action--inactive\">\n <div class=\"card-overview__metric-content\">\n {this.bar('text-body-small', '35%', 'card-overview__metric-label')}\n <div class=\"card-overview__metric-figure\">\n {this.bar('text-body-medium', '40px')}\n {/* Match renderTrend's type canvas and the score trend's 28px width. */}\n {this.bar('text-body-medium', '28px')}\n </div>\n </div>\n </div>\n </div>\n )),\n ];\n }\n\n return [\n this.renderScore(),\n ...this.visibleMetrics.map((metric, index) => this.renderMetric(metric, index)),\n ];\n }\n\n render() {\n const compact = this.variant === 'compact';\n const collapse = this.scrollCollapseGeometry;\n\n return (\n <Host\n class={{\n 'card-overview': true,\n 'card-overview--compact': compact,\n 'card-overview--stacked': !compact && this.layout === 'stacked',\n 'card-overview--scroll-collapsing': collapse.active,\n }}\n role=\"region\"\n aria-label={this.overviewLabel}\n aria-busy={this.isLoading ? 'true' : undefined}\n style={{\n '--ds-card-overview-metric-min': this.metricMinWidth,\n '--ds-card-overview-grid-columns': this.gridColumns\n ? String(this.gridColumns)\n : undefined,\n '--ds-card-overview-expanded-height': collapse.active\n ? `${collapse.expandedHeight}px`\n : undefined,\n '--ds-card-overview-visible-height': collapse.active\n ? `${collapse.visibleHeight}px`\n : undefined,\n '--ds-card-overview-content-offset': collapse.active\n ? `${collapse.offset}px`\n : undefined,\n }}\n >\n <div\n class={{\n 'card-overview__surface': true,\n // Split shadow and inset highlight, so the opaque surface cannot cover\n // the highlight the way a combined elevation shadow would.\n 'ds-control-elevation': true,\n 'ds-control-elevation--sm': !compact,\n 'ds-control-elevation--floating': compact,\n }}\n >\n <div class=\"card-overview__clip\">\n <div\n ref={element => {\n this.layoutEl = (element as HTMLElement) ?? undefined;\n }}\n class={{\n 'card-overview__layout': true,\n }}\n >\n <div class=\"card-overview__summary\">\n <div class=\"card-overview__header ds-control--md\">\n <div class=\"card-overview__period\">\n <div class=\"card-overview__period-current\">\n <slot name=\"period\">\n {this.periodLabel && (\n /* eslint-disable-next-line local/prefer-direct-ds-text -- Fixed copy uses the same structural frame and label inset as a slotted Select. */\n <div class=\"card-overview__period-fixed ds-control-frame\">\n <ds-text\n as=\"span\"\n class=\"ds-control-label-box\"\n variant=\"text-body-medium\"\n emphasis\n color={ALWAYS_DARK_PRIMARY}\n >\n {this.periodLabel}\n </ds-text>\n </div>\n )}\n </slot>\n </div>\n {this.comparisonLabel && (\n <ds-text\n as=\"span\"\n class=\"card-overview__period-comparison ds-control-label-box\"\n variant=\"text-body-medium\"\n color={ALWAYS_DARK_SECONDARY}\n >\n {this.comparisonLabel}\n </ds-text>\n )}\n </div>\n <div class=\"card-overview__filter\">\n <slot name=\"filter\" />\n </div>\n </div>\n </div>\n\n {!compact && (\n <div class=\"card-overview__body\">\n <div class=\"card-overview__metrics\" part=\"metrics\">\n {this.renderMetrics()}\n </div>\n\n <slot name=\"footer\" />\n </div>\n )}\n </div>\n </div>\n </div>\n </Host>\n );\n }\n}\n",
373
373
  "type": "registry:ui"
374
374
  },
375
375
  {
@@ -29,8 +29,8 @@
29
29
  "alternatives": [
30
30
  {
31
31
  "when": "The surface contains a chart or specialized data-visualization composition.",
32
- "component": "component:ds-card-data-viz",
33
- "reason": "CardDataViz owns the independently evolving data-visualization card contract."
32
+ "component": "component:ds-card-chart",
33
+ "reason": "CardChart owns the independently evolving chart-card contract."
34
34
  },
35
35
  {
36
36
  "when": "The task must block the interface for one focused decision or short workflow.",
@@ -249,7 +249,7 @@
249
249
  },
250
250
  "peerDependencies": {
251
251
  "required": [
252
- "@ds-mo/tokens ^5.1.1",
252
+ "@ds-mo/tokens ^5.1.2",
253
253
  "@ds-mo/icons ^6.4.0"
254
254
  ],
255
255
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -268,12 +268,12 @@
268
268
  "files": [
269
269
  {
270
270
  "path": "src/wc/components/CardSetting/CardSetting.css",
271
- "content": "@import '../../utils/chrome-layout.css';\n\n:host {\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 background-color: var(--color-background-primary);\n border: none;\n border-radius: var(--dimension-radius-025);\n box-shadow: 0 0 0 var(--dimension-stroke-width-012) var(--color-border-tertiary);\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.card-setting__header {\n justify-content: space-between;\n height: var(--dimension-size-600);\n flex: 0 0 auto;\n background-color: var(--color-background-secondary);\n border-bottom: var(--dimension-stroke-width-012) solid var(--color-border-tertiary);\n}\n\n:host(.card-setting--editing) .card-setting__header {\n background-color: transparent;\n border-bottom-color: transparent;\n}\n\nds-text.card-setting__title {\n display: flex;\n flex: 1 1 auto;\n align-items: center;\n min-width: 0;\n padding: calc(var(--dimension-space-075) + var(--dimension-space-025));\n box-sizing: border-box;\n -webkit-user-select: none;\n user-select: none;\n}\n\n.card-setting__actions {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n gap: var(--dimension-space-100);\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-025) var(--dimension-radius-025);\n}\n\n:host(.card-setting--editing) .card-setting__body {\n border-radius: var(--dimension-radius-025);\n}\n",
271
+ "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)\n 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",
272
272
  "type": "registry:ui"
273
273
  },
274
274
  {
275
275
  "path": "src/wc/components/CardSetting/CardSetting.tsx",
276
- "content": "import { Component, Event, EventEmitter, h, Host, Prop } from '@stencil/core';\n\nexport type CardSettingWidth = 'sm' | 'md' | 'lg';\nexport type CardSettingAction = 'edit' | 'save' | 'cancel';\n\nexport interface CardSettingActionDetail {\n action: CardSettingAction;\n originalEvent: MouseEvent;\n}\n\nconst CARD_WIDTH_VARS: Record<CardSettingWidth, string> = {\n sm: 'var(--dimension-card-width-sm)',\n md: 'var(--dimension-card-width-md)',\n lg: 'var(--dimension-card-width-lg)',\n};\n\nconst CARD_HEIGHT_VARS: Record<CardSettingWidth, string> = {\n sm: 'var(--dimension-card-height-sm)',\n md: 'var(--dimension-card-height-md)',\n lg: 'var(--dimension-card-height-lg)',\n};\n\nconst FAINT_BRAND_TITLE_COLOR = 'var(--color-foreground-faint-brand)';\n\n@Component({\n tag: 'ds-card-setting',\n styleUrl: 'CardSetting.css',\n scoped: true,\n})\nexport class CardSetting {\n /** Section heading shown in the card header. */\n @Prop() heading!: string;\n\n /**\n * Card width token (`sm` / `md` / `lg`). Also sets host `min-height` to the\n * matching `--dimension-card-height-*` so the body fills available space\n * even when the slot is empty.\n */\n @Prop() cardWidth: CardSettingWidth = 'md';\n\n /** Controlled edit state — parent owns single-edit orchestration. */\n @Prop() editing = false;\n @Prop() editLabel: string = 'Edit';\n @Prop() cancelLabel: string = 'Cancel';\n @Prop() saveLabel: string = 'Save';\n\n /** Emits a controlled edit, save, or cancel request. */\n @Event() dsAction!: EventEmitter<CardSettingActionDetail>;\n\n private emitAction(action: CardSettingAction, originalEvent: MouseEvent) {\n this.dsAction.emit({ action, originalEvent });\n }\n\n render() {\n const editing = this.editing;\n\n return (\n <Host\n class={{\n 'card-setting': true,\n 'card-setting--editing': editing,\n }}\n style={{\n '--_card-setting-width': CARD_WIDTH_VARS[this.cardWidth],\n '--_card-setting-min-height': CARD_HEIGHT_VARS[this.cardWidth],\n }}\n >\n <header class=\"card-setting__header ds-chrome-row ds-chrome-space--md\">\n <ds-text\n class=\"card-setting__title\"\n variant=\"text-title-small\"\n emphasis\n color={editing ? FAINT_BRAND_TITLE_COLOR : 'primary'}\n as=\"h2\"\n >\n {this.heading}\n </ds-text>\n <div class=\"card-setting__actions\">\n {!editing ? (\n <ds-button-unfilled\n variant=\"icon\"\n type=\"button\"\n icon=\"Pencil\"\n aria-label={this.editLabel}\n onDsClick={(event: CustomEvent<MouseEvent>) =>\n this.emitAction('edit', event.detail)\n }\n />\n ) : (\n [\n <ds-button-unfilled\n key=\"cancel\"\n variant=\"icon\"\n type=\"button\"\n icon=\"Cross\"\n background=\"bold\"\n aria-label={this.cancelLabel}\n onDsClick={(event: CustomEvent<MouseEvent>) =>\n this.emitAction('cancel', event.detail)\n }\n />,\n <ds-button-filled\n key=\"save\"\n variant=\"icon\"\n type=\"button\"\n icon=\"Check\"\n intent=\"brand\"\n contrast=\"faint\"\n aria-label={this.saveLabel}\n onDsClick={(event: CustomEvent<MouseEvent>) =>\n this.emitAction('save', event.detail)\n }\n />,\n ]\n )}\n </div>\n </header>\n <div class=\"card-setting__body\">\n <slot />\n </div>\n </Host>\n );\n }\n}\n",
276
+ "content": "import { Component, Event, EventEmitter, h, Host, Prop } from '@stencil/core';\n\nexport type CardSettingWidth = 'sm' | 'md' | 'lg';\nexport type CardSettingAction = 'edit' | 'save' | 'cancel';\n\nexport interface CardSettingActionDetail {\n action: CardSettingAction;\n originalEvent: MouseEvent;\n}\n\nconst CARD_WIDTH_VARS: Record<CardSettingWidth, string> = {\n sm: 'var(--dimension-card-width-sm)',\n md: 'var(--dimension-card-width-md)',\n lg: 'var(--dimension-card-width-lg)',\n};\n\nconst CARD_HEIGHT_VARS: Record<CardSettingWidth, string> = {\n sm: 'var(--dimension-card-height-sm)',\n md: 'var(--dimension-card-height-md)',\n lg: 'var(--dimension-card-height-lg)',\n};\n\nconst FAINT_BRAND_TITLE_COLOR = 'var(--color-foreground-faint-brand)';\n\n@Component({\n tag: 'ds-card-setting',\n styleUrl: 'CardSetting.css',\n scoped: true,\n})\nexport class CardSetting {\n /** Section heading shown in the card header. */\n @Prop() heading!: string;\n\n /**\n * Card width token (`sm` / `md` / `lg`). Also sets host `min-height` to the\n * matching `--dimension-card-height-*` so the body fills available space\n * even when the slot is empty.\n */\n @Prop() cardWidth: CardSettingWidth = 'md';\n\n /** Controlled edit state — parent owns single-edit orchestration. */\n @Prop() editing = false;\n @Prop() editLabel: string = 'Edit';\n @Prop() cancelLabel: string = 'Cancel';\n @Prop() saveLabel: string = 'Save';\n\n /** Emits a controlled edit, save, or cancel request. */\n @Event() dsAction!: EventEmitter<CardSettingActionDetail>;\n\n private emitAction(action: CardSettingAction, originalEvent: MouseEvent) {\n this.dsAction.emit({ action, originalEvent });\n }\n\n render() {\n const editing = this.editing;\n\n return (\n <Host\n class={{\n 'card-setting': true,\n 'card-setting--editing': editing,\n }}\n style={{\n '--_card-setting-width': CARD_WIDTH_VARS[this.cardWidth],\n '--_card-setting-min-height': CARD_HEIGHT_VARS[this.cardWidth],\n }}\n >\n <header class=\"card-setting__header ds-chrome-header\">\n {/* eslint-disable-next-line local/prefer-direct-ds-text -- Shared header copy owns the control-density geometry around the semantic heading. */}\n <div class=\"card-setting__copy ds-chrome-header__copy ds-control--md\">\n <ds-text\n class=\"card-setting__title ds-chrome-header__heading\"\n variant=\"text-title-small\"\n emphasis\n color={editing ? FAINT_BRAND_TITLE_COLOR : 'primary'}\n as=\"h2\"\n >\n {this.heading}\n </ds-text>\n </div>\n <div class=\"card-setting__actions ds-chrome-header__trailing\">\n {!editing ? (\n <ds-button-unfilled\n variant=\"icon\"\n type=\"button\"\n icon=\"Pencil\"\n aria-label={this.editLabel}\n onDsClick={(event: CustomEvent<MouseEvent>) =>\n this.emitAction('edit', event.detail)\n }\n />\n ) : (\n [\n <ds-button-unfilled\n key=\"cancel\"\n variant=\"icon\"\n type=\"button\"\n icon=\"Cross\"\n background=\"bold\"\n aria-label={this.cancelLabel}\n onDsClick={(event: CustomEvent<MouseEvent>) =>\n this.emitAction('cancel', event.detail)\n }\n />,\n <ds-button-filled\n key=\"save\"\n variant=\"icon\"\n type=\"button\"\n icon=\"Check\"\n intent=\"brand\"\n contrast=\"faint\"\n aria-label={this.saveLabel}\n onDsClick={(event: CustomEvent<MouseEvent>) =>\n this.emitAction('save', event.detail)\n }\n />,\n ]\n )}\n </div>\n </header>\n <div class=\"card-setting__body\">\n <slot />\n </div>\n </Host>\n );\n }\n}\n",
277
277
  "type": "registry:ui"
278
278
  }
279
279
  ]