@keyfunc/keybricks-ds 1.0.4 → 2.3.1

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 (381) hide show
  1. package/DEPLOY.md +14 -5
  2. package/DESIGN.md +245 -59
  3. package/INTEGRATION.md +243 -49
  4. package/SKILL.md +13 -11
  5. package/_adherence.oxlintrc.json +183 -70
  6. package/_scripts/build-index.mjs +24 -4
  7. package/_scripts/build-tokens.mjs +5 -2
  8. package/assets/icons/icon-index.js +1 -1
  9. package/assets/icons/icon-index.json +1 -1
  10. package/components/_cards/accordion.card.html +2 -2
  11. package/components/_cards/action-chip.card.html +11 -9
  12. package/components/_cards/activity.card.html +3 -3
  13. package/components/_cards/alert.card.html +3 -3
  14. package/components/_cards/app-chrome.card.html +4 -4
  15. package/components/_cards/banner.card.html +1 -1
  16. package/components/_cards/breadcrumb.card.html +5 -5
  17. package/components/_cards/button-1-base.card.html +23 -26
  18. package/components/_cards/button-2-states.card.html +27 -25
  19. package/components/_cards/button-3-sizes-icons.card.html +20 -19
  20. package/components/_cards/button-4-group.card.html +14 -31
  21. package/components/_cards/canvas-zoom-controls.card.html +1 -1
  22. package/components/_cards/cards-indicators.card.html +2 -2
  23. package/components/_cards/charts-1-sparkline.card.html +5 -5
  24. package/components/_cards/charts-10-gauge.card.html +5 -5
  25. package/components/_cards/charts-11-funnel.card.html +3 -3
  26. package/components/_cards/charts-12-treemap.card.html +3 -3
  27. package/components/_cards/charts-14-radar.card.html +3 -3
  28. package/components/_cards/charts-15-distribution.card.html +2 -2
  29. package/components/_cards/charts-16-pareto.card.html +3 -3
  30. package/components/_cards/charts-17-candlestick.card.html +3 -3
  31. package/components/_cards/charts-18-sankey.card.html +3 -3
  32. package/components/_cards/charts-19-chord.card.html +3 -3
  33. package/components/_cards/charts-2-line.card.html +4 -4
  34. package/components/_cards/charts-20-sunburst.card.html +3 -3
  35. package/components/_cards/charts-21-worldmap.card.html +4 -4
  36. package/components/_cards/charts-22-gantt.card.html +3 -3
  37. package/components/_cards/charts-23-bar3d.card.html +3 -3
  38. package/components/_cards/charts-24-pie3d.card.html +3 -3
  39. package/components/_cards/charts-25-surface3d.card.html +3 -3
  40. package/components/_cards/charts-26-scatter3d.card.html +3 -3
  41. package/components/_cards/charts-27-ribbon3d.card.html +3 -3
  42. package/components/_cards/charts-3-bar.card.html +7 -7
  43. package/components/_cards/charts-4-barlist.card.html +3 -3
  44. package/components/_cards/charts-5-donut.card.html +4 -4
  45. package/components/_cards/charts-6-scatter.card.html +3 -3
  46. package/components/_cards/charts-7-heatmap.card.html +3 -3
  47. package/components/_cards/charts-8-waterfall.card.html +3 -3
  48. package/components/_cards/charts-9-bullet.card.html +3 -3
  49. package/components/_cards/charts-99-fullpage.card.html +5 -5
  50. package/components/_cards/checkbox.card.html +9 -6
  51. package/components/_cards/codeblock.card.html +2 -2
  52. package/components/_cards/combobox.card.html +6 -3
  53. package/components/_cards/context-menu.card.html +6 -3
  54. package/components/_cards/date-picker.card.html +10 -4
  55. package/components/_cards/diffvalue.card.html +3 -3
  56. package/components/_cards/divider.card.html +4 -4
  57. package/components/_cards/drawer.card.html +4 -4
  58. package/components/_cards/dropdown-menu.card.html +7 -7
  59. package/components/_cards/dropzone.card.html +5 -3
  60. package/components/_cards/empty-state.card.html +6 -6
  61. package/components/_cards/filter-bar.card.html +5 -5
  62. package/components/_cards/icon-button.card.html +14 -14
  63. package/components/_cards/icons-1-core.card.html +4 -4
  64. package/components/_cards/icons-2-feather.card.html +4 -4
  65. package/components/_cards/icons-3-unicons.card.html +4 -4
  66. package/components/_cards/input.card.html +5 -2
  67. package/components/_cards/kbd.card.html +2 -2
  68. package/components/_cards/keyvalue.card.html +2 -2
  69. package/components/_cards/listrow.card.html +2 -2
  70. package/components/_cards/loading.card.html +3 -3
  71. package/components/_cards/log-viewer.card.html +3 -3
  72. package/components/_cards/modal.card.html +9 -9
  73. package/components/_cards/multi-select.card.html +6 -3
  74. package/components/_cards/number-stepper.card.html +9 -5
  75. package/components/_cards/pagination.card.html +6 -3
  76. package/components/_cards/people.card.html +4 -4
  77. package/components/_cards/pipeline-node.card.html +8 -8
  78. package/components/_cards/pipeline-palette.card.html +5 -5
  79. package/components/_cards/radio.card.html +12 -7
  80. package/components/_cards/search.card.html +6 -3
  81. package/components/_cards/segmented-control.card.html +11 -10
  82. package/components/_cards/select.card.html +8 -4
  83. package/components/_cards/slider.card.html +2 -2
  84. package/components/_cards/smart-table.card.html +3 -3
  85. package/components/_cards/spreadsheet.card.html +3 -3
  86. package/components/_cards/stepper.card.html +3 -3
  87. package/components/_cards/table.card.html +165 -0
  88. package/components/_cards/tabs.card.html +6 -3
  89. package/components/_cards/tag.card.html +6 -3
  90. package/components/_cards/textarea.card.html +2 -2
  91. package/components/_cards/timeline.card.html +3 -3
  92. package/components/_cards/toast.card.html +10 -8
  93. package/components/_cards/toggles.card.html +15 -10
  94. package/components/_cards/tooltip.card.html +7 -7
  95. package/components/_cards/tree.card.html +3 -3
  96. package/components/_shared/card-kit.css +25 -0
  97. package/components/_shared/card-kit.js +50 -0
  98. package/components/_shared/src-buttons.js +1 -1
  99. package/components/_shared/src-charts.js +1 -1
  100. package/components/_shared/src-data.js +1 -1
  101. package/components/_shared/src-display.js +1 -1
  102. package/components/_shared/src-feedback.js +1 -1
  103. package/components/_shared/src-forms.js +1 -1
  104. package/components/_shared/src-icons.js +1 -1
  105. package/components/_shared/src-navigation.js +1 -1
  106. package/components/_shared/src-pipeline.js +1 -1
  107. package/components/_shared/src-tabs.js +1 -1
  108. package/components/_shared/view-source.js +3 -3
  109. package/components/_vue/index.mjs +16 -5
  110. package/components/buttons/ActionChip.d.ts +3 -1
  111. package/components/buttons/ActionChip.jsx +4 -4
  112. package/components/buttons/ActionChip.vue +4 -4
  113. package/components/buttons/Button.d.ts +5 -2
  114. package/components/buttons/Button.jsx +15 -18
  115. package/components/buttons/Button.vue +17 -18
  116. package/components/buttons/ButtonGroup.vue +1 -1
  117. package/components/buttons/IconButton.d.ts +2 -1
  118. package/components/buttons/IconButton.jsx +13 -9
  119. package/components/buttons/IconButton.vue +12 -9
  120. package/components/buttons/buttons.prompt.md +6 -5
  121. package/components/charts/Bar3D.jsx +2 -2
  122. package/components/charts/Bar3D.vue +3 -3
  123. package/components/charts/BarChart.jsx +4 -4
  124. package/components/charts/BarChart.vue +6 -6
  125. package/components/charts/BarList.jsx +4 -4
  126. package/components/charts/BarList.vue +4 -4
  127. package/components/charts/BoxPlot.jsx +1 -1
  128. package/components/charts/BoxPlot.vue +3 -3
  129. package/components/charts/BulletChart.jsx +6 -6
  130. package/components/charts/BulletChart.vue +6 -6
  131. package/components/charts/CandlestickChart.jsx +2 -2
  132. package/components/charts/CandlestickChart.vue +5 -5
  133. package/components/charts/ChartKit.jsx +8 -7
  134. package/components/charts/ChartLegend.vue +16 -0
  135. package/components/charts/ChartTooltip.vue +16 -0
  136. package/components/charts/ChordChart.jsx +2 -2
  137. package/components/charts/ChordChart.vue +3 -3
  138. package/components/charts/DonutChart.jsx +1 -1
  139. package/components/charts/DonutChart.vue +2 -2
  140. package/components/charts/FunnelChart.jsx +3 -3
  141. package/components/charts/FunnelChart.vue +4 -4
  142. package/components/charts/GanttChart.jsx +2 -2
  143. package/components/charts/GanttChart.vue +3 -3
  144. package/components/charts/GaugeChart.jsx +2 -2
  145. package/components/charts/GaugeChart.vue +2 -2
  146. package/components/charts/HeatmapChart.jsx +2 -2
  147. package/components/charts/HeatmapChart.vue +2 -2
  148. package/components/charts/Histogram.jsx +2 -2
  149. package/components/charts/Histogram.vue +4 -4
  150. package/components/charts/LineChart.jsx +1 -1
  151. package/components/charts/LineChart.vue +4 -4
  152. package/components/charts/ParetoChart.jsx +3 -3
  153. package/components/charts/ParetoChart.vue +5 -5
  154. package/components/charts/Pie3D.jsx +1 -1
  155. package/components/charts/Pie3D.vue +2 -2
  156. package/components/charts/PlotGrid.vue +18 -0
  157. package/components/charts/RadarChart.jsx +1 -1
  158. package/components/charts/RadarChart.vue +2 -2
  159. package/components/charts/Ribbon3D.jsx +2 -2
  160. package/components/charts/Ribbon3D.vue +3 -3
  161. package/components/charts/SankeyChart.jsx +2 -2
  162. package/components/charts/SankeyChart.vue +3 -3
  163. package/components/charts/Scatter3D.jsx +1 -1
  164. package/components/charts/Scatter3D.vue +2 -2
  165. package/components/charts/ScatterChart.jsx +3 -3
  166. package/components/charts/ScatterChart.vue +5 -5
  167. package/components/charts/Sparkline.jsx +1 -1
  168. package/components/charts/Sparkline.vue +1 -1
  169. package/components/charts/SunburstChart.jsx +1 -1
  170. package/components/charts/SunburstChart.vue +2 -2
  171. package/components/charts/Surface3D.jsx +2 -2
  172. package/components/charts/Surface3D.vue +3 -3
  173. package/components/charts/TreemapChart.jsx +2 -2
  174. package/components/charts/TreemapChart.vue +3 -3
  175. package/components/charts/WaterfallChart.jsx +2 -2
  176. package/components/charts/WaterfallChart.vue +6 -5
  177. package/components/charts/WorldMap.jsx +4 -4
  178. package/components/charts/WorldMap.vue +5 -5
  179. package/components/charts/XLabels.vue +11 -0
  180. package/components/data/CodeBlock.jsx +1 -1
  181. package/components/data/CodeBlock.vue +1 -1
  182. package/components/data/DiffValue.d.ts +1 -0
  183. package/components/data/DiffValue.jsx +1 -1
  184. package/components/data/DiffValue.vue +1 -1
  185. package/components/data/FilterBar.d.ts +2 -0
  186. package/components/data/FilterBar.jsx +10 -9
  187. package/components/data/FilterBar.vue +9 -8
  188. package/components/data/KeyValue.d.ts +1 -1
  189. package/components/data/KeyValue.jsx +4 -4
  190. package/components/data/KeyValue.vue +4 -4
  191. package/components/data/ListRow.jsx +2 -2
  192. package/components/data/ListRow.vue +2 -2
  193. package/components/data/Pagination.d.ts +5 -1
  194. package/components/data/Pagination.jsx +16 -12
  195. package/components/data/Pagination.vue +14 -9
  196. package/components/data/SmartTable.d.ts +4 -1
  197. package/components/data/SmartTable.jsx +16 -14
  198. package/components/data/SmartTable.vue +26 -23
  199. package/components/data/Spreadsheet.d.ts +2 -0
  200. package/components/data/Spreadsheet.jsx +16 -15
  201. package/components/data/Spreadsheet.vue +19 -18
  202. package/components/data/Table.d.ts +101 -0
  203. package/components/data/Table.jsx +223 -0
  204. package/components/data/Table.vue +153 -0
  205. package/components/data/data.prompt.md +3 -3
  206. package/components/display/Avatar.jsx +2 -2
  207. package/components/display/Avatar.vue +3 -2
  208. package/components/display/AvatarGroup.vue +3 -2
  209. package/components/display/Badge.d.ts +4 -1
  210. package/components/display/Badge.jsx +10 -5
  211. package/components/display/Badge.vue +1 -1
  212. package/components/display/Divider.vue +3 -3
  213. package/components/display/Donut.vue +3 -2
  214. package/components/display/IconTile.vue +10 -4
  215. package/components/display/Kbd.d.ts +1 -1
  216. package/components/display/Kbd.jsx +1 -1
  217. package/components/display/Kbd.vue +2 -2
  218. package/components/display/ProgressBar.jsx +1 -1
  219. package/components/display/ProgressBar.vue +1 -1
  220. package/components/display/StatCard.vue +1 -1
  221. package/components/display/Tag.d.ts +3 -1
  222. package/components/display/Tag.jsx +4 -4
  223. package/components/display/Tag.vue +4 -4
  224. package/components/feedback/Accordion.jsx +2 -2
  225. package/components/feedback/Accordion.vue +2 -2
  226. package/components/feedback/ActivityItem.d.ts +3 -1
  227. package/components/feedback/ActivityItem.jsx +6 -6
  228. package/components/feedback/ActivityItem.vue +5 -4
  229. package/components/feedback/Alert.jsx +4 -4
  230. package/components/feedback/Alert.vue +3 -3
  231. package/components/feedback/Banner.jsx +5 -5
  232. package/components/feedback/Banner.vue +5 -5
  233. package/components/feedback/ContextMenu.d.ts +3 -1
  234. package/components/feedback/ContextMenu.jsx +5 -5
  235. package/components/feedback/ContextMenu.vue +5 -5
  236. package/components/feedback/DotActivity.vue +3 -3
  237. package/components/feedback/Drawer.d.ts +2 -0
  238. package/components/feedback/Drawer.jsx +6 -6
  239. package/components/feedback/Drawer.vue +22 -15
  240. package/components/feedback/EmptyState.vue +2 -2
  241. package/components/feedback/LogViewer.vue +1 -1
  242. package/components/feedback/Modal.d.ts +2 -0
  243. package/components/feedback/Modal.jsx +12 -11
  244. package/components/feedback/Modal.vue +10 -8
  245. package/components/feedback/Skeleton.vue +2 -1
  246. package/components/feedback/Spinner.vue +2 -1
  247. package/components/feedback/Stepper.vue +1 -1
  248. package/components/feedback/Timeline.jsx +1 -1
  249. package/components/feedback/Timeline.vue +3 -3
  250. package/components/feedback/Toast.vue +4 -4
  251. package/components/feedback/ToastStack.vue +11 -0
  252. package/components/feedback/Tooltip.vue +19 -0
  253. package/components/feedback/feedback.prompt.md +4 -4
  254. package/components/forms/Checkbox.d.ts +3 -2
  255. package/components/forms/Checkbox.jsx +7 -7
  256. package/components/forms/Checkbox.vue +8 -8
  257. package/components/forms/Combobox.d.ts +2 -0
  258. package/components/forms/Combobox.jsx +12 -10
  259. package/components/forms/Combobox.vue +11 -10
  260. package/components/forms/DatePicker.d.ts +4 -0
  261. package/components/forms/DatePicker.jsx +16 -12
  262. package/components/forms/DatePicker.vue +15 -11
  263. package/components/forms/Dropzone.jsx +2 -2
  264. package/components/forms/Dropzone.vue +5 -5
  265. package/components/forms/Field.d.ts +5 -1
  266. package/components/forms/Field.jsx +4 -4
  267. package/components/forms/Field.vue +4 -4
  268. package/components/forms/Input.d.ts +5 -1
  269. package/components/forms/Input.jsx +8 -8
  270. package/components/forms/Input.vue +3 -3
  271. package/components/forms/MultiSelect.d.ts +2 -0
  272. package/components/forms/MultiSelect.jsx +13 -11
  273. package/components/forms/MultiSelect.vue +12 -11
  274. package/components/forms/NumberStepper.d.ts +3 -1
  275. package/components/forms/NumberStepper.jsx +7 -7
  276. package/components/forms/NumberStepper.vue +8 -7
  277. package/components/forms/Radio.d.ts +5 -3
  278. package/components/forms/Radio.jsx +13 -15
  279. package/components/forms/Radio.vue +9 -10
  280. package/components/forms/RadioGroup.vue +5 -5
  281. package/components/forms/SearchInput.d.ts +3 -1
  282. package/components/forms/SearchInput.jsx +6 -6
  283. package/components/forms/SearchInput.vue +7 -6
  284. package/components/forms/Select.d.ts +3 -2
  285. package/components/forms/Select.jsx +6 -7
  286. package/components/forms/Select.vue +6 -6
  287. package/components/forms/Slider.jsx +3 -3
  288. package/components/forms/Slider.vue +3 -3
  289. package/components/forms/Switch.d.ts +2 -2
  290. package/components/forms/Switch.jsx +20 -22
  291. package/components/forms/Switch.vue +22 -18
  292. package/components/forms/Textarea.vue +5 -5
  293. package/components/forms/forms.prompt.md +1 -1
  294. package/components/forms/input-base.js +2 -1
  295. package/components/icons/Icon.jsx +17 -1
  296. package/components/icons/Icon.prompt.md +2 -0
  297. package/components/icons/Icon.vue +1 -1
  298. package/components/icons/icon-paths.js +15 -0
  299. package/components/index.d.ts +1 -2
  300. package/components/index.js +4 -5
  301. package/components/navigation/Breadcrumb.jsx +6 -10
  302. package/components/navigation/Breadcrumb.vue +6 -12
  303. package/components/navigation/DocTree.jsx +3 -3
  304. package/components/navigation/DocTree.vue +3 -3
  305. package/components/navigation/PageHeading.vue +1 -1
  306. package/components/navigation/PageToolbar.vue +7 -6
  307. package/components/navigation/Sidebar.jsx +4 -23
  308. package/components/navigation/Sidebar.vue +5 -10
  309. package/components/navigation/ThemeToggle.vue +2 -1
  310. package/components/navigation/TopBar.d.ts +3 -1
  311. package/components/navigation/TopBar.jsx +6 -6
  312. package/components/navigation/Tree.jsx +5 -5
  313. package/components/navigation/Tree.vue +13 -8
  314. package/components/navigation/nav-model.js +23 -0
  315. package/components/navigation/theme-store.js +9 -0
  316. package/components/pipeline/ConfigPanel.d.ts +2 -0
  317. package/components/pipeline/ConfigPanel.jsx +9 -9
  318. package/components/pipeline/ConfigPanel.vue +9 -9
  319. package/components/pipeline/DataPreview.d.ts +3 -1
  320. package/components/pipeline/DataPreview.jsx +7 -7
  321. package/components/pipeline/DataPreview.vue +7 -7
  322. package/components/pipeline/NodeCard.jsx +3 -3
  323. package/components/pipeline/NodeCard.vue +3 -3
  324. package/components/pipeline/PaletteItem.vue +2 -2
  325. package/components/pipeline/PaletteListItem.vue +1 -1
  326. package/components/pipeline/PalettePanel.jsx +2 -2
  327. package/components/pipeline/PalettePanel.vue +1 -1
  328. package/components/pipeline/Port.vue +15 -0
  329. package/components/pipeline/PortLabel.vue +15 -0
  330. package/components/pipeline/ZoomControls.vue +1 -1
  331. package/components/pipeline/port-skin.js +13 -0
  332. package/components/px.js +4 -0
  333. package/components/tabs/PanelTabs.vue +5 -4
  334. package/components/tabs/PillTabs.vue +5 -4
  335. package/components/tabs/SegmentedControl.d.ts +2 -2
  336. package/components/tabs/SegmentedControl.jsx +7 -7
  337. package/components/tabs/SegmentedControl.vue +7 -7
  338. package/components/tabs/Tabs.d.ts +5 -3
  339. package/components/tabs/Tabs.jsx +15 -15
  340. package/components/tabs/Tabs.vue +5 -5
  341. package/components/tabs/ViewToggle.d.ts +3 -1
  342. package/components/tabs/ViewToggle.jsx +5 -5
  343. package/components/tabs/ViewToggle.vue +5 -5
  344. package/components/tabs/tabs.prompt.md +1 -1
  345. package/keybricks.css +4 -0
  346. package/package.json +6 -3
  347. package/readme.md +91 -122
  348. package/readme.zh-CN.md +121 -0
  349. package/tokens/alpha.css +3 -1
  350. package/tokens/charts.css +20 -17
  351. package/tokens/colors.css +3 -3
  352. package/tokens/dark.css +3 -3
  353. package/tokens/hover.css +32 -0
  354. package/tokens/interaction.css +3 -1
  355. package/tokens/layout.css +31 -7
  356. package/tokens/tokens.d.ts +58 -0
  357. package/tokens/tokens.js +179 -92
  358. package/tokens/tokens.json +327 -114
  359. package/tokens/typography.css +45 -15
  360. package/components/_cards/brand-visual.card.html +0 -43
  361. package/components/_cards/datatable.card.html +0 -48
  362. package/components/_shared/src-brand.js +0 -1
  363. package/components/brand/BrandVisual.d.ts +0 -17
  364. package/components/brand/BrandVisual.jsx +0 -29
  365. package/components/brand/BrandVisual.vue +0 -34
  366. package/components/brand/_dir.card.html +0 -13
  367. package/components/brand/brandMotifs.js +0 -210
  368. package/components/buttons/_dir.card.html +0 -13
  369. package/components/charts/ChartKit.vue +0 -47
  370. package/components/charts/_dir.card.html +0 -7
  371. package/components/data/DataTable.d.ts +0 -28
  372. package/components/data/DataTable.jsx +0 -32
  373. package/components/data/DataTable.vue +0 -25
  374. package/components/data/_dir.card.html +0 -13
  375. package/components/display/_dir.card.html +0 -13
  376. package/components/feedback/_dir.card.html +0 -13
  377. package/components/forms/_dir.card.html +0 -13
  378. package/components/icons/_dir.card.html +0 -13
  379. package/components/navigation/_dir.card.html +0 -13
  380. package/components/pipeline/_dir.card.html +0 -13
  381. package/components/tabs/_dir.card.html +0 -13
@@ -38,23 +38,23 @@ function move(e){
38
38
  <template v-if="axes">
39
39
  <g v-for="t in g.ticks" :key="t">
40
40
  <line :x1="g.pad.l" :x2="width-g.pad.r" :y1="g.sy(t)" :y2="g.sy(t)" :stroke="t===0?'var(--chart-axis)':'var(--chart-grid)'" stroke-width="1"/>
41
- <text :x="g.pad.l-8" :y="g.sy(t)+3" text-anchor="end" style="font-family:var(--font-mono);font-size:10px;fill:var(--chart-label)">{{ valueFormat(t) }}</text>
41
+ <text :x="g.pad.l-8" :y="g.sy(t)+3" text-anchor="end" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);fill:var(--chart-label)">{{ valueFormat(t) }}</text>
42
42
  </g>
43
43
  </template>
44
44
  <rect v-if="hover!=null" :x="g.pad.l+g.slot*hover" :y="g.pad.t" :width="g.slot" :height="height-g.pad.t-g.axisB" fill="var(--bg-hover)"/>
45
- <path v-if="g.maPath" :d="g.maPath" fill="none" stroke="var(--series-8)" stroke-width="1.3"/>
45
+ <path v-if="g.maPath" :d="g.maPath" fill="none" stroke="var(--chart-total)" stroke-width="1.3"/>
46
46
  <g v-for="b in g.bars" :key="b.i" :opacity="hover==null||hover===b.i?1:0.5" style="transition:opacity var(--dur-fast) var(--ease-out)">
47
47
  <line :x1="b.x" :x2="b.x" :y1="b.yH" :y2="b.yL" :stroke="b.col" stroke-width="1"/>
48
48
  <rect :x="b.x-b.bw/2" :y="b.top" :width="b.bw" :height="b.body" :fill="b.solid?b.col:'var(--bg-surface)'" :stroke="b.col" stroke-width="1"/>
49
49
  <rect v-if="volume && b.vY!=null" :x="b.x-b.bw/2" :y="b.vY" :width="b.bw" :height="b.vH" :fill="b.col" opacity="0.35"/>
50
50
  </g>
51
51
  <line v-if="volume" :x1="g.pad.l" :x2="width-g.pad.r" :y1="height-g.axisB" :y2="height-g.axisB" stroke="var(--chart-axis)" stroke-width="1"/>
52
- <text v-if="axes" v-for="b in g.bars.filter(b=>b.i%g.every===0)" :key="'t'+b.i" :x="b.x" :y="height-6" text-anchor="middle" style="font-family:var(--font-sans);font-size:10px;fill:var(--chart-label)">{{ b.c.t }}</text>
52
+ <text v-if="axes" v-for="b in g.bars.filter(b=>b.i%g.every===0)" :key="'t'+b.i" :x="b.x" :y="height-6" text-anchor="middle" style="font-family:var(--font-sans);font-size:var(--text-2xs);fill:var(--chart-label)">{{ b.c.t }}</text>
53
53
  </svg>
54
54
  <div v-if="hover!=null && interactive" :style="{position:'absolute',left:g.cx(hover)+'px',top:(height/2-20)+'px',transform:g.cx(hover)>width*0.62?'translate(calc(-100% - 10px),-50%)':'translate(10px,-50%)',pointerEvents:'none',zIndex:5,background:'var(--bg-surface)',border:'1px solid var(--border-card)',borderRadius:'var(--radius-md)',boxShadow:'var(--shadow-menu)',padding:'7px 10px',lineHeight:'normal',whiteSpace:'nowrap'}">
55
55
  <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px">{{ candles[hover].t }}</div>
56
- <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">O {{ valueFormat(candles[hover].o) }} · H {{ valueFormat(candles[hover].h) }} · L {{ valueFormat(candles[hover].l) }} · C {{ valueFormat(candles[hover].c) }}</div>
57
- <div :style="{fontFamily:'var(--font-mono)',fontSize:'var(--text-2xs)',color:candles[hover].c>=candles[hover].o?'var(--chart-pos)':'var(--chart-neg)'}">
56
+ <div style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading)">O {{ valueFormat(candles[hover].o) }} · H {{ valueFormat(candles[hover].h) }} · L {{ valueFormat(candles[hover].l) }} · C {{ valueFormat(candles[hover].c) }}</div>
57
+ <div :style="{fontFamily: 'var(--font-sans)', fontVariantNumeric: 'tabular-nums',fontSize:'var(--text-2xs-mono)',color:candles[hover].c>=candles[hover].o?'var(--chart-pos)':'var(--chart-neg)'}">
58
58
  {{ (candles[hover].c-candles[hover].o>=0?'+':'')+(((candles[hover].c-candles[hover].o)/(candles[hover].o||1))*100).toFixed(2) }}%<span v-if="candles[hover].v!=null" style="color:var(--text-muted)"> · vol {{ fmtNum(candles[hover].v) }}</span>
59
59
  </div>
60
60
  </div>
@@ -1,7 +1,8 @@
1
1
  import React from "react";
2
2
  import { fmtNum, seriesColor } from "./chart-geometry.js";
3
3
 
4
- const sans = "var(--font-sans)", mono = "var(--font-mono)";
4
+ /* Chart numbers use the UI font with tabular figures; the monospace font is reserved for code. */
5
+ const sans = "var(--font-sans)", num = { fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums" };
5
6
 
6
7
  /** Legend row. items: [{label,color}] — with onToggle, clicking mutes a series. */
7
8
  export function ChartLegend({ items = [], hidden = [], onToggle, align = "left", style }) {
@@ -11,10 +12,10 @@ export function ChartLegend({ items = [], hidden = [], onToggle, align = "left",
11
12
  const off = hidden.includes(it.label);
12
13
  return (
13
14
  <span key={it.label} role={onToggle ? "button" : undefined} tabIndex={onToggle ? 0 : undefined}
14
- onClick={onToggle ? () => onToggle(it.label) : undefined}
15
+ onClick={onToggle ? () => onToggle(it.label) : undefined} data-ink={onToggle ? "" : undefined}
15
16
  style={{ display: "inline-flex", alignItems: "center", gap: 6, cursor: onToggle ? "pointer" : "default", opacity: off ? 0.4 : 1, transition: "var(--t-hover)" }}>
16
17
  <span style={{ width: 8, height: 8, borderRadius: 2, background: it.color || seriesColor(i), flexShrink: 0 }} />
17
- <span style={{ fontFamily: sans, fontSize: "var(--text-xs)", color: "var(--text-secondary)", textDecoration: off ? "line-through" : "none" }}>{it.label}</span>
18
+ <span style={{ fontFamily: sans, fontSize: "var(--text-xs)", color: onToggle ? "var(--ink, var(--text-secondary))" : "var(--text-secondary)", textDecoration: off ? "line-through" : "none" }}>{it.label}</span>
18
19
  </span>
19
20
  );
20
21
  })}
@@ -31,7 +32,7 @@ export function ChartTooltip({ x, y, title, rows = [], flip }) {
31
32
  <div key={i} style={{ display: "flex", alignItems: "center", gap: 6, padding: "1px 0" }}>
32
33
  {r.color ? <span style={{ width: 7, height: 7, borderRadius: 2, background: r.color, flexShrink: 0 }} /> : null}
33
34
  <span style={{ fontFamily: sans, fontSize: "var(--text-xs)", color: "var(--text-secondary)", whiteSpace: "nowrap" }}>{r.label}</span>
34
- <span style={{ fontFamily: mono, fontSize: "var(--text-xs)", color: "var(--text-heading)", marginLeft: "auto", paddingLeft: 10 }}>{r.value}</span>
35
+ <span style={{ ...num, fontSize: "var(--text-xs)", color: "var(--text-heading)", marginLeft: "auto", paddingLeft: 10 }}>{r.value}</span>
35
36
  </div>
36
37
  ))}
37
38
  </div>
@@ -45,12 +46,12 @@ export function PlotGrid({ w, h, pad, ticks, sy, xLabels = [], sxLabel, showAxis
45
46
  {ticks.map((t) => (
46
47
  <g key={t}>
47
48
  <line x1={pad.l} x2={w - pad.r} y1={sy(t)} y2={sy(t)} stroke={t === 0 ? "var(--chart-axis)" : "var(--chart-grid)"} strokeWidth="1" />
48
- <text x={pad.l - 8} y={sy(t) + 3} textAnchor="end" style={{ fontFamily: mono, fontSize: 10, fill: "var(--chart-label)" }}>{yFormat(t)}</text>
49
+ <text x={pad.l - 8} y={sy(t) + 3} textAnchor="end" style={{ ...num, fontSize: "var(--text-2xs-mono)", fill: "var(--chart-label)" }}>{yFormat(t)}</text>
49
50
  </g>
50
51
  ))}
51
52
  {showAxisLine ? <line x1={pad.l} x2={pad.l} y1={pad.t} y2={h - pad.b} stroke="var(--chart-axis)" strokeWidth="1" /> : null}
52
53
  {xLabels.map((lb, i) => (lb == null ? null : (
53
- <text key={i} x={sxLabel(i)} y={h - pad.b + 16} textAnchor="middle" style={{ fontFamily: sans, fontSize: 10, fill: "var(--chart-label)" }}>{lb}</text>
54
+ <text key={i} x={sxLabel(i)} y={h - pad.b + 16} textAnchor="middle" style={{ fontFamily: sans, fontSize: "var(--text-2xs)", fill: "var(--chart-label)" }}>{lb}</text>
54
55
  )))}
55
56
  </g>
56
57
  );
@@ -58,7 +59,7 @@ export function PlotGrid({ w, h, pad, ticks, sy, xLabels = [], sxLabel, showAxis
58
59
 
59
60
  /** Thin x labels only (sm tier / no y axis). */
60
61
  export function XLabels({ h, pad, xLabels = [], sxLabel }) {
61
- return <g>{xLabels.map((lb, i) => (lb == null ? null : <text key={i} x={sxLabel(i)} y={h - pad.b + 16} textAnchor="middle" style={{ fontFamily: sans, fontSize: 10, fill: "var(--chart-label)" }}>{lb}</text>))}</g>;
62
+ return <g>{xLabels.map((lb, i) => (lb == null ? null : <text key={i} x={sxLabel(i)} y={h - pad.b + 16} textAnchor="middle" style={{ fontFamily: sans, fontSize: "var(--text-2xs)", fill: "var(--chart-label)" }}>{lb}</text>))}</g>;
62
63
  }
63
64
 
64
65
  /** Skip x labels so they never collide: keeps ~every nth. */
@@ -0,0 +1,16 @@
1
+ <script setup>
2
+ import { seriesColor } from './chart-geometry.js'
3
+ // Legend row. items: [{ label, color? }]. Pass toggleable + listen to @toggle(label) to mute a series; hidden lists muted labels.
4
+ defineProps({ items: { type: Array, default: () => [] }, hidden: { type: Array, default: () => [] }, toggleable: Boolean, align: { type: String, default: 'left' }, style: Object })
5
+ const emit = defineEmits(['toggle'])
6
+ </script>
7
+ <template>
8
+ <div :style="{ display: 'flex', flexWrap: 'wrap', gap: '4px 14px', justifyContent: align === 'right' ? 'flex-end' : 'flex-start', lineHeight: 'normal', ...style }">
9
+ <span v-for="(it, i) in items" :key="it.label" :role="toggleable ? 'button' : undefined" :tabindex="toggleable ? 0 : undefined"
10
+ @click="toggleable && emit('toggle', it.label)" :data-ink="toggleable ? '' : undefined" @keydown.enter="toggleable && emit('toggle', it.label)"
11
+ :style="{ display: 'inline-flex', alignItems: 'center', gap: '6px', cursor: toggleable ? 'pointer' : 'default', opacity: hidden.includes(it.label) ? 0.4 : 1, transition: 'var(--t-hover)' }">
12
+ <span :style="{ width: '8px', height: '8px', borderRadius: '2px', background: it.color || seriesColor(i), flexShrink: 0 }"></span>
13
+ <span :style="{ fontFamily: 'var(--font-sans)', fontSize: 'var(--text-xs)', color: toggleable ? 'var(--ink, var(--text-secondary))' : 'var(--text-secondary)', textDecoration: hidden.includes(it.label) ? 'line-through' : 'none' }">{{ it.label }}</span>
14
+ </span>
15
+ </div>
16
+ </template>
@@ -0,0 +1,16 @@
1
+ <script setup>
2
+ import { px } from '../px.js'
3
+ // Floating chart tooltip. rows: [{ label, value, color? }]; pins near (x, y) inside a position:relative chart wrapper.
4
+ // flip puts it to the left of the point (use near the right edge).
5
+ defineProps({ x: [Number, String], y: [Number, String], title: [String, Number], rows: { type: Array, default: () => [] }, flip: Boolean })
6
+ </script>
7
+ <template>
8
+ <div :style="{ position: 'absolute', left: px(x), top: px(y), transform: `translate(${flip ? 'calc(-100% - 10px)' : '10px'}, -50%)`, pointerEvents: 'none', zIndex: 5, background: 'var(--bg-surface)', border: '1px solid var(--border-card)', borderRadius: 'var(--radius-md)', boxShadow: 'var(--shadow-menu)', padding: '7px 10px', minWidth: '90px', lineHeight: 'normal', whiteSpace: 'nowrap' }">
9
+ <div v-if="title != null" :style="{ fontFamily: 'var(--font-sans)', fontSize: 'var(--text-2xs)', color: 'var(--text-muted)', paddingBottom: rows.length ? '4px' : 0, whiteSpace: 'nowrap' }">{{ title }}</div>
10
+ <div v-for="(r, i) in rows" :key="i" style="display:flex;align-items:center;gap:6px;padding:1px 0">
11
+ <span v-if="r.color" :style="{ width: '7px', height: '7px', borderRadius: '2px', background: r.color, flexShrink: 0 }"></span>
12
+ <span style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-secondary);white-space:nowrap">{{ r.label }}</span>
13
+ <span style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading);margin-left:auto;padding-left:10px">{{ r.value }}</span>
14
+ </div>
15
+ </div>
16
+ </template>
@@ -43,8 +43,8 @@ export function ChordChart({ labels = [], matrix = [], size = 380, thickness = 1
43
43
  <g key={a.i} onMouseEnter={interactive ? () => setHover({ kind: "node", i: a.i }) : undefined} onMouseLeave={interactive ? () => setHover(null) : undefined}
44
44
  style={{ cursor: interactive ? "default" : undefined }}>
45
45
  <path d={arcPath(cx, cy, rO, rI, a.a0, a.a1)} fill={colorOf(a.i)} opacity={hover == null || on ? 1 : 0.4} style={{ transition: "opacity var(--dur-fast) var(--ease-out)" }} />
46
- <text x={lx} y={ly + 3} textAnchor={anchor} style={{ fontFamily: "var(--font-sans)", fontSize: 10.5, fill: on ? "var(--text-heading)" : "var(--text-secondary)" }}>{labels[a.i]}</text>
47
- <text x={lx} y={ly + 14} textAnchor={anchor} style={{ fontFamily: "var(--font-mono)", fontSize: 9, fill: "var(--chart-label)" }}>{valueFormat(a.value)}</text>
46
+ <text x={lx} y={ly + 3} textAnchor={anchor} style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-2xs)", fill: on ? "var(--text-heading)" : "var(--text-secondary)" }}>{labels[a.i]}</text>
47
+ <text x={lx} y={ly + 14} textAnchor={anchor} style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", fill: "var(--chart-label)" }}>{valueFormat(a.value)}</text>
48
48
  </g>
49
49
  );
50
50
  })}
@@ -42,13 +42,13 @@ const tip = computed(()=>{
42
42
  style="transition:opacity var(--dur-fast) var(--ease-out)"/>
43
43
  <g v-for="a in g.arcs" :key="a.i" @mouseenter="interactive && (hover={kind:'node',i:a.i})" @mouseleave="interactive && (hover=null)">
44
44
  <path :d="a.d" :fill="a.color" :opacity="hover==null||(hover.kind==='node'&&hover.i===a.i)?1:0.4" style="transition:opacity var(--dur-fast) var(--ease-out)"/>
45
- <text :x="a.lx" :y="a.ly+3" :text-anchor="a.anchor" style="font-family:var(--font-sans);font-size:10.5px;fill:var(--text-secondary)">{{ labels[a.i] }}</text>
46
- <text :x="a.lx" :y="a.ly+14" :text-anchor="a.anchor" style="font-family:var(--font-mono);font-size:9px;fill:var(--chart-label)">{{ valueFormat(a.value) }}</text>
45
+ <text :x="a.lx" :y="a.ly+3" :text-anchor="a.anchor" style="font-family:var(--font-sans);font-size:var(--text-2xs);fill:var(--text-secondary)">{{ labels[a.i] }}</text>
46
+ <text :x="a.lx" :y="a.ly+14" :text-anchor="a.anchor" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);fill:var(--chart-label)">{{ valueFormat(a.value) }}</text>
47
47
  </g>
48
48
  </svg>
49
49
  <div v-if="tip && interactive" :style="{position:'absolute',left:tip.x+'px',top:tip.y+'px',transform:tip.x>size*0.55?'translate(calc(-100% - 10px),-50%)':'translate(10px,-50%)',pointerEvents:'none',zIndex:5,background:'var(--bg-surface)',border:'1px solid var(--border-card)',borderRadius:'var(--radius-md)',boxShadow:'var(--shadow-menu)',padding:'7px 10px',lineHeight:'normal',whiteSpace:'nowrap'}">
50
50
  <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px">{{ tip.title }}</div>
51
- <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">{{ tip.value }}</div>
51
+ <div style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading)">{{ tip.value }}</div>
52
52
  </div>
53
53
  </div>
54
54
  </template>
@@ -62,7 +62,7 @@ export function DonutChart({ segments = [], size = 180, thickness = 22, pie = fa
62
62
  style={{ display: "inline-flex", alignItems: "center", gap: 7, cursor: interactive ? "default" : undefined, opacity: hover == null || hover === i ? 1 : 0.45, transition: "var(--t-hover)" }}>
63
63
  <span style={{ width: 8, height: 8, borderRadius: 2, background: colorOf(s, i), flexShrink: 0 }} />
64
64
  <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", color: "var(--text-secondary)" }}>{s.label}</span>
65
- <span style={{ fontFamily: "var(--font-mono)", fontSize: "var(--text-xs)", color: "var(--text-heading)", marginLeft: 6 }}>{((s.value / total) * 100).toFixed(0)}%</span>
65
+ <span style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-xs)", color: "var(--text-heading)", marginLeft: 6 }}>{((s.value / total) * 100).toFixed(0)}%</span>
66
66
  </span>
67
67
  ))}
68
68
  </div>
@@ -45,7 +45,7 @@ const tipStyle = computed(() => {
45
45
  </div>
46
46
  <div v-if="hover!=null && interactive" :style="tipStyle" >
47
47
  <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px;white-space:nowrap">{{ segments[hover].label }}</div>
48
- <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading);white-space:nowrap">{{ valueFormat(segments[hover].value) }} · {{ ((segments[hover].value/total)*100).toFixed(1) }}%</div>
48
+ <div style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading);white-space:nowrap">{{ valueFormat(segments[hover].value) }} · {{ ((segments[hover].value/total)*100).toFixed(1) }}%</div>
49
49
  </div>
50
50
  </div>
51
51
  <div v-if="legend" style="display:flex;flex-direction:column;gap:6px">
@@ -53,7 +53,7 @@ const tipStyle = computed(() => {
53
53
  :style="{display:'inline-flex',alignItems:'center',gap:'7px',opacity:hover==null||hover===a.i?1:.45,transition:'var(--t-hover)'}">
54
54
  <span :style="{width:'8px',height:'8px',borderRadius:'2px',background:a.color,flexShrink:0}"/>
55
55
  <span style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-secondary)">{{ a.s.label }}</span>
56
- <span style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading);margin-left:6px">{{ ((a.s.value/total)*100).toFixed(0) }}%</span>
56
+ <span style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading);margin-left:6px">{{ ((a.s.value/total)*100).toFixed(0) }}%</span>
57
57
  </span>
58
58
  </div>
59
59
  </div>
@@ -20,12 +20,12 @@ export function FunnelChart({ stages = [], width = 420, bandHeight = 34, gap = 1
20
20
  <div style={{ display: "flex", justifyContent: "center" }}>
21
21
  <div style={{ width: Math.max(12, share * 100) + "%", height: bandHeight, borderRadius: "var(--radius-sm)", background: "var(--series-1)", opacity: 1 - i * (0.55 / Math.max(1, stages.length - 1)), display: "flex", alignItems: "center", justifyContent: "center", gap: 8, minWidth: 150, transition: "width var(--dur) var(--ease-out)" }}>
22
22
  <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", color: "var(--on-accent)", whiteSpace: "nowrap" }}>{s.label}</span>
23
- <span style={{ fontFamily: "var(--font-mono)", fontSize: "var(--text-xs)", color: "var(--on-accent)" }}>{valueFormat(s.value)}</span>
24
- {hover === i ? <span style={{ fontFamily: "var(--font-mono)", fontSize: "var(--text-2xs)", color: "var(--on-accent)", opacity: 0.8 }}>{((share) * 100).toFixed(0)}% of top</span> : null}
23
+ <span style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-xs)", color: "var(--on-accent)" }}>{valueFormat(s.value)}</span>
24
+ {hover === i ? <span style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", color: "var(--on-accent)", opacity: 0.8 }}>{((share) * 100).toFixed(0)}% of top</span> : null}
25
25
  </div>
26
26
  </div>
27
27
  {prev != null ? (
28
- <div style={{ textAlign: "center", paddingTop: 3, fontFamily: "var(--font-mono)", fontSize: "var(--text-2xs)", color: prev < 0.5 ? "var(--chart-neg)" : "var(--text-muted)" }}>
28
+ <div style={{ textAlign: "center", paddingTop: 3, fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", color: prev < 0.5 ? "var(--chart-neg)" : "var(--text-muted)" }}>
29
29
  ↓ {(prev * 100).toFixed(0)}%
30
30
  </div>
31
31
  ) : null}
@@ -17,13 +17,13 @@ const rows = computed(() => {
17
17
  <div v-for="(s,i) in rows" :key="s.label" @mouseenter="interactive && (hover=i)" @mouseleave="interactive && (hover=null)"
18
18
  :style="{paddingBottom:i<rows.length-1?gap+'px':0,opacity:hover==null||hover===i?1:0.55,transition:'var(--t-hover)'}">
19
19
  <div style="display:flex;justify-content:center">
20
- <div :style="{width:s.share+'%',height:bandHeight+'px',borderRadius:'var(--radius-sm)',background:'var(--series-1)',opacity:s.opacity,display:'flex',alignItems:'center',justifyContent:'center',gap:'8px',minWidth:'150px'}">
20
+ <div :style="{width:s.share+'%',height:bandHeight+'px',borderRadius:'var(--radius-sm)',background:'var(--series-1)',opacity:s.opacity,display:'flex',alignItems:'center',justifyContent:'center',gap:'8px',minWidth:'150px',transition:'width var(--dur) var(--ease-out)'}">
21
21
  <span style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--on-accent);white-space:nowrap">{{ s.label }}</span>
22
- <span style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--on-accent)">{{ valueFormat(s.value) }}</span>
23
- <span v-if="hover===i" style="font-family:var(--font-mono);font-size:var(--text-2xs);color:var(--on-accent);opacity:.8">{{ s.share.toFixed(0) }}% of top</span>
22
+ <span style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--on-accent)">{{ valueFormat(s.value) }}</span>
23
+ <span v-if="hover===i" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);color:var(--on-accent);opacity:.8">{{ s.share.toFixed(0) }}% of top</span>
24
24
  </div>
25
25
  </div>
26
- <div v-if="s.conv!=null" :style="{textAlign:'center',paddingTop:'3px',fontFamily:'var(--font-mono)',fontSize:'var(--text-2xs)',color:s.conv<0.5?'var(--chart-neg)':'var(--text-muted)'}">↓ {{ (s.conv*100).toFixed(0) }}%</div>
26
+ <div v-if="s.conv!=null" :style="{textAlign:'center',paddingTop:'3px',fontFamily: 'var(--font-sans)', fontVariantNumeric: 'tabular-nums',fontSize:'var(--text-2xs-mono)',color:s.conv<0.5?'var(--chart-neg)':'var(--text-muted)'}">↓ {{ (s.conv*100).toFixed(0) }}%</div>
27
27
  </div>
28
28
  </div>
29
29
  </template>
@@ -33,7 +33,7 @@ export function GanttChart({ tasks = [], width = 660, rowHeight = 26, today, uni
33
33
  {ticks.map((d, i) => (
34
34
  <g key={i}>
35
35
  <line x1={x(d)} x2={x(d)} y1={20} y2={height - 4} stroke="var(--chart-grid)" strokeWidth="1" />
36
- <text x={x(d)} y={12} textAnchor="middle" style={{ fontFamily: "var(--font-sans)", fontSize: 9.5, fill: "var(--chart-label)" }}>{fmt(d)}</text>
36
+ <text x={x(d)} y={12} textAnchor="middle" style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-2xs)", fill: "var(--chart-label)" }}>{fmt(d)}</text>
37
37
  </g>
38
38
  ))}
39
39
  {[...Array(days)].map((_, i) => {
@@ -49,7 +49,7 @@ export function GanttChart({ tasks = [], width = 660, rowHeight = 26, today, uni
49
49
  return (
50
50
  <g key={t.label + i} onMouseEnter={interactive ? () => setHover(i) : undefined} onMouseLeave={interactive ? () => setHover(null) : undefined}
51
51
  opacity={hover == null || on ? 1 : 0.55} style={{ transition: "opacity var(--dur-fast) var(--ease-out)", cursor: interactive ? "default" : undefined }}>
52
- <text x={0} y={y + rowHeight / 2 + 3} style={{ fontFamily: "var(--font-sans)", fontSize: 11, fill: on ? "var(--text-heading)" : "var(--text-secondary)" }}>
52
+ <text x={0} y={y + rowHeight / 2 + 3} style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", fill: on ? "var(--text-heading)" : "var(--text-secondary)" }}>
53
53
  {t.label.length > 22 ? t.label.slice(0, 21) + "…" : t.label}
54
54
  </text>
55
55
  {t.milestone ? (
@@ -35,12 +35,12 @@ const g = computed(() => {
35
35
  <svg :width="width" :height="g.height" role="img">
36
36
  <g v-for="(d,i) in g.ticks" :key="'t'+i">
37
37
  <line :x1="g.x(d)" :x2="g.x(d)" :y1="20" :y2="g.height-4" stroke="var(--chart-grid)" stroke-width="1"/>
38
- <text :x="g.x(d)" :y="12" text-anchor="middle" style="font-family:var(--font-sans);font-size:9.5px;fill:var(--chart-label)">{{ fmt(d) }}</text>
38
+ <text :x="g.x(d)" :y="12" text-anchor="middle" style="font-family:var(--font-sans);font-size:var(--text-2xs);fill:var(--chart-label)">{{ fmt(d) }}</text>
39
39
  </g>
40
40
  <rect v-for="(d,i) in g.weekends" :key="'w'+i" :x="g.x(d)" :y="20" :width="g.colW" :height="g.height-24" fill="var(--bg-inset)" opacity="0.6"/>
41
41
  <g v-for="r in g.rows" :key="r.t.label+r.i" @mouseenter="interactive && (hover=r.i)" @mouseleave="interactive && (hover=null)"
42
42
  :opacity="hover==null||hover===r.i?1:0.55" style="transition:opacity var(--dur-fast) var(--ease-out)">
43
- <text :x="0" :y="r.y+rowHeight/2+3" :style="{fontFamily:'var(--font-sans)',fontSize:'11px',fill:hover===r.i?'var(--text-heading)':'var(--text-secondary)'}">{{ r.label }}</text>
43
+ <text :x="0" :y="r.y+rowHeight/2+3" :style="{fontFamily:'var(--font-sans)',fontSize:'var(--text-xs)',fill:hover===r.i?'var(--text-heading)':'var(--text-secondary)'}">{{ r.label }}</text>
44
44
  <path v-if="r.t.milestone" :d="'M'+r.bx+' '+(r.y+rowHeight/2-6)+' L'+(r.bx+6)+' '+(r.y+rowHeight/2)+' L'+r.bx+' '+(r.y+rowHeight/2+6)+' L'+(r.bx-6)+' '+(r.y+rowHeight/2)+' Z'" :fill="r.color"/>
45
45
  <template v-else>
46
46
  <rect :x="r.bx" :y="r.y+4" :width="r.bw" :height="rowHeight-12" rx="3" :fill="r.color" opacity="0.32"/>
@@ -54,7 +54,7 @@ const g = computed(() => {
54
54
  </svg>
55
55
  <div v-if="hover!=null && interactive" :style="{position:'absolute',left:g.rows[hover].bx+'px',top:(g.rows[hover].y+rowHeight/2)+'px',transform:g.rows[hover].bx>width*0.6?'translate(calc(-100% - 10px),-50%)':'translate(10px,-50%)',pointerEvents:'none',zIndex:5,background:'var(--bg-surface)',border:'1px solid var(--border-card)',borderRadius:'var(--radius-md)',boxShadow:'var(--shadow-menu)',padding:'7px 10px',lineHeight:'normal',whiteSpace:'nowrap'}">
56
56
  <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px">{{ tasks[hover].label }}</div>
57
- <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">{{ fmt(tasks[hover].start) }} → {{ fmt(tasks[hover].end||tasks[hover].start) }} · {{ g.rows[hover].dur }}d<span v-if="tasks[hover].progress!=null"> · {{ Math.round(tasks[hover].progress*100) }}%</span></div>
57
+ <div style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading)">{{ fmt(tasks[hover].start) }} → {{ fmt(tasks[hover].end||tasks[hover].start) }} · {{ g.rows[hover].dur }}d<span v-if="tasks[hover].progress!=null"> · {{ Math.round(tasks[hover].progress*100) }}%</span></div>
58
58
  </div>
59
59
  </div>
60
60
  </template>
@@ -21,8 +21,8 @@ export function GaugeChart({ value = 0, max = 100, size = 180, thickness = 14, l
21
21
  {t > 0 ? <path d={arcPath(cx, cy, rO, rI, A0, A0 + t * 180)} fill={color}
22
22
  onMouseEnter={interactive ? () => setHover(true) : undefined} onMouseLeave={interactive ? () => setHover(false) : undefined}
23
23
  style={{ filter: hover ? "brightness(1.1)" : "none", transition: "filter var(--dur-fast) var(--ease-out)" }} /> : null}
24
- <text x={cx - rO + thickness / 2} y={cy + 13} textAnchor="middle" style={{ fontFamily: "var(--font-mono)", fontSize: 9, fill: "var(--chart-label)" }}>0</text>
25
- <text x={cx + rO - thickness / 2} y={cy + 13} textAnchor="middle" style={{ fontFamily: "var(--font-mono)", fontSize: 9, fill: "var(--chart-label)" }}>{fmtNum(max)}</text>
24
+ <text x={cx - rO + thickness / 2} y={cy + 13} textAnchor="middle" style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", fill: "var(--chart-label)" }}>0</text>
25
+ <text x={cx + rO - thickness / 2} y={cy + 13} textAnchor="middle" style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", fill: "var(--chart-label)" }}>{fmtNum(max)}</text>
26
26
  </svg>
27
27
  <div style={{ position: "absolute", left: 0, right: 0, top: cy - size / 7, textAlign: "center", pointerEvents: "none" }}>
28
28
  <div style={{ fontFamily: "var(--font-sans)", fontWeight: "var(--weight-light)", fontSize: size / 7, lineHeight: 1, color: "var(--text-heading)" }}>{valueFormat(value)}</div>
@@ -18,8 +18,8 @@ const g = computed(() => {
18
18
  <path :d="g.track" fill="var(--chart-track)"/>
19
19
  <path v-if="g.fill" :d="g.fill" :fill="g.color" @mouseenter="interactive && (hover=true)" @mouseleave="interactive && (hover=false)"
20
20
  :style="{filter:hover?'brightness(1.1)':'none',transition:'filter var(--dur-fast) var(--ease-out)'}"/>
21
- <text :x="g.cx-g.rO+thickness/2" :y="g.cy+13" text-anchor="middle" style="font-family:var(--font-mono);font-size:9px;fill:var(--chart-label)">0</text>
22
- <text :x="g.cx+g.rO-thickness/2" :y="g.cy+13" text-anchor="middle" style="font-family:var(--font-mono);font-size:9px;fill:var(--chart-label)">{{ fmtNum(max) }}</text>
21
+ <text :x="g.cx-g.rO+thickness/2" :y="g.cy+13" text-anchor="middle" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);fill:var(--chart-label)">0</text>
22
+ <text :x="g.cx+g.rO-thickness/2" :y="g.cy+13" text-anchor="middle" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);fill:var(--chart-label)">{{ fmtNum(max) }}</text>
23
23
  </svg>
24
24
  <div :style="{position:'absolute',left:0,right:0,top:(g.cy-size/7)+'px',textAlign:'center',pointerEvents:'none'}">
25
25
  <div :style="{fontFamily:'var(--font-sans)',fontWeight:'var(--weight-light)',fontSize:size/7+'px',lineHeight:1,color:'var(--text-heading)'}">{{ valueFormat(value) }}</div>
@@ -34,7 +34,7 @@ export function HeatmapChart({ xLabels = [], yLabels = [], values = [], width =
34
34
  <div style={{ ...wrapStyle, width, ...style }}>
35
35
  <div style={{ display: "grid", gridTemplateColumns: `${labelW}px repeat(${cols}, 1fr)`, gap: gap + "px", lineHeight: "normal" }}>
36
36
  <span />
37
- {xLabels.map((x) => <span key={x} style={{ fontFamily: "var(--font-sans)", fontSize: 10, color: "var(--chart-label)", textAlign: "center", paddingBottom: 2 }}>{x}</span>)}
37
+ {xLabels.map((x) => <span key={x} style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-2xs)", color: "var(--chart-label)", textAlign: "center", paddingBottom: 2 }}>{x}</span>)}
38
38
  {yLabels.map((y, r) => (
39
39
  <React.Fragment key={y}>
40
40
  <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", color: "var(--text-secondary)", display: "flex", alignItems: "center", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", paddingRight: 6 }}>{y}</span>
@@ -46,7 +46,7 @@ export function HeatmapChart({ xLabels = [], yLabels = [], values = [], width =
46
46
  onMouseEnter={interactive ? () => setHover({ r, c }) : undefined} onMouseLeave={interactive ? () => setHover(null) : undefined}
47
47
  style={{ height: cellHeight, borderRadius: 4, background: cellFill(v), display: "flex", alignItems: "center", justifyContent: "center",
48
48
  outline: on ? "2px solid var(--focus-border)" : "none", outlineOffset: -1, transition: "var(--t-hover)", cursor: interactive ? "default" : undefined }}>
49
- {showValues && v != null ? <span style={{ fontFamily: "var(--font-mono)", fontSize: 10, color: textOn(v) }}>{valueFormat(v)}</span> : null}
49
+ {showValues && v != null ? <span style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", color: textOn(v) }}>{valueFormat(v)}</span> : null}
50
50
  </span>
51
51
  );
52
52
  })}
@@ -24,13 +24,13 @@ function ink(v){
24
24
  <div :style="{width:width+'px',...style}">
25
25
  <div :style="{display:'grid',gridTemplateColumns:'86px repeat('+xLabels.length+', 1fr)',gap:gap+'px'}">
26
26
  <span/>
27
- <span v-for="x in xLabels" :key="x" style="font-family:var(--font-sans);font-size:10px;color:var(--chart-label);text-align:center;padding-bottom:2px">{{ x }}</span>
27
+ <span v-for="x in xLabels" :key="x" style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--chart-label);text-align:center;padding-bottom:2px">{{ x }}</span>
28
28
  <template v-for="(y,r) in yLabels" :key="y">
29
29
  <span style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-secondary);display:flex;align-items:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding-right:6px">{{ y }}</span>
30
30
  <span v-for="(x,c) in xLabels" :key="c" :title="y+' · '+x+(values[r]&&values[r][c]!=null?' = '+valueFormat(values[r][c]):'')"
31
31
  @mouseenter="interactive && (hover={r,c})" @mouseleave="interactive && (hover=null)"
32
32
  :style="{height:cellHeight+'px',borderRadius:'4px',background:fill(values[r]?values[r][c]:null),display:'flex',alignItems:'center',justifyContent:'center',outline:hover&&hover.r===r&&hover.c===c?'2px solid var(--focus-border)':'none',outlineOffset:'-1px',transition:'var(--t-hover)'}">
33
- <span v-if="showValues && values[r] && values[r][c]!=null" :style="{fontFamily:'var(--font-mono)',fontSize:'10px',color:ink(values[r][c])}">{{ valueFormat(values[r][c]) }}</span>
33
+ <span v-if="showValues && values[r] && values[r][c]!=null" :style="{fontFamily: 'var(--font-sans)', fontVariantNumeric: 'tabular-nums',fontSize:'var(--text-2xs-mono)',color:ink(values[r][c])}">{{ valueFormat(values[r][c]) }}</span>
34
34
  </span>
35
35
  </template>
36
36
  </div>
@@ -40,10 +40,10 @@ export function Histogram({ values = [], buckets, bins = 12, width = 560, height
40
40
  ))}
41
41
  {mean != null ? <g>
42
42
  <line x1={sxVal(mean)} x2={sxVal(mean)} y1={pad.t} y2={height - pad.b} stroke="var(--chart-guide)" strokeWidth="1" strokeDasharray="3 3" />
43
- <text x={Math.min(sxVal(mean) + 4, width - 60)} y={pad.t - 8} style={{ fontFamily: "var(--font-mono)", fontSize: 10, fill: "var(--chart-label)" }}>mean {valueFormat(mean)}</text>
43
+ <text x={Math.min(sxVal(mean) + 4, width - 60)} y={pad.t - 8} style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", fill: "var(--chart-label)" }}>mean {valueFormat(mean)}</text>
44
44
  </g> : null}
45
45
  {axes ? [0, Math.floor(bkt.length / 2), bkt.length - 1].map((i) => (
46
- <text key={i} x={pad.l + slot * i + slot / 2} y={height - pad.b + 16} textAnchor="middle" style={{ fontFamily: "var(--font-mono)", fontSize: 10, fill: "var(--chart-label)" }}>{valueFormat(bkt[i].from)}</text>
46
+ <text key={i} x={pad.l + slot * i + slot / 2} y={height - pad.b + 16} textAnchor="middle" style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", fill: "var(--chart-label)" }}>{valueFormat(bkt[i].from)}</text>
47
47
  )) : null}
48
48
  </svg>
49
49
  {hover != null && interactive ? (
@@ -34,20 +34,20 @@ function move(e){
34
34
  <template v-if="axes">
35
35
  <g v-for="t in g.ticks" :key="t">
36
36
  <line :x1="g.pad.l" :x2="width-g.pad.r" :y1="g.sy(t)" :y2="g.sy(t)" :stroke="t===0?'var(--chart-axis)':'var(--chart-grid)'" stroke-width="1"/>
37
- <text :x="g.pad.l-8" :y="g.sy(t)+3" text-anchor="end" style="font-family:var(--font-mono);font-size:10px;fill:var(--chart-label)">{{ countFormat(t) }}</text>
37
+ <text :x="g.pad.l-8" :y="g.sy(t)+3" text-anchor="end" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);fill:var(--chart-label)">{{ countFormat(t) }}</text>
38
38
  </g>
39
39
  </template>
40
40
  <rect v-for="(b,i) in g.bkt" :key="i" :x="g.pad.l+g.slot*i+0.5" :y="g.sy(b.n)" :width="Math.max(1,g.slot-1.5)" :height="Math.max(1,g.sy(0)-g.sy(b.n))" rx="2"
41
41
  :fill="color" :opacity="hover==null?0.9:(hover===i?1:0.4)" style="transition:opacity var(--dur-fast) var(--ease-out)"/>
42
42
  <template v-if="g.mean!=null">
43
43
  <line :x1="g.meanLine" :x2="g.meanLine" :y1="g.pad.t" :y2="height-g.pad.b" stroke="var(--chart-guide)" stroke-width="1" stroke-dasharray="3 3"/>
44
- <text :x="g.meanX" :y="g.pad.t-8" style="font-family:var(--font-mono);font-size:10px;fill:var(--chart-label)">mean {{ valueFormat(g.mean) }}</text>
44
+ <text :x="g.meanX" :y="g.pad.t-8" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);fill:var(--chart-label)">mean {{ valueFormat(g.mean) }}</text>
45
45
  </template>
46
- <text v-if="axes" v-for="i in g.labels" :key="'l'+i" :x="g.pad.l+g.slot*i+g.slot/2" :y="height-g.pad.b+16" text-anchor="middle" style="font-family:var(--font-mono);font-size:10px;fill:var(--chart-label)">{{ valueFormat(g.bkt[i].from) }}</text>
46
+ <text v-if="axes" v-for="i in g.labels" :key="'l'+i" :x="g.pad.l+g.slot*i+g.slot/2" :y="height-g.pad.b+16" text-anchor="middle" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);fill:var(--chart-label)">{{ valueFormat(g.bkt[i].from) }}</text>
47
47
  </svg>
48
48
  <div v-if="hover!=null && interactive" :style="{position:'absolute',left:(g.pad.l+g.slot*hover+g.slot/2)+'px',top:(height/2-10)+'px',transform:(g.pad.l+g.slot*hover)>width*0.62?'translate(calc(-100% - 10px),-50%)':'translate(10px,-50%)',pointerEvents:'none',zIndex:5,background:'var(--bg-surface)',border:'1px solid var(--border-card)',borderRadius:'var(--radius-md)',boxShadow:'var(--shadow-menu)',padding:'7px 10px',lineHeight:'normal',whiteSpace:'nowrap'}">
49
49
  <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px">{{ valueFormat(g.bkt[hover].from) }} – {{ valueFormat(g.bkt[hover].to) }}</div>
50
- <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">{{ countFormat(g.bkt[hover].n) }} · {{ ((g.bkt[hover].n/g.total)*100).toFixed(1) }}%</div>
50
+ <div style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading)">{{ countFormat(g.bkt[hover].n) }} · {{ ((g.bkt[hover].n/g.total)*100).toFixed(1) }}%</div>
51
51
  </div>
52
52
  </div>
53
53
  </template>
@@ -58,7 +58,7 @@ export function LineChart({ series = [], xLabels = [], width = 560, height = 240
58
58
  {axes ? <PlotGrid w={width} h={height} pad={pad} ticks={ticks} sy={sy} xLabels={thinLabels(xLabels)} sxLabel={sx} yFormat={yFormat} /> : null}
59
59
  {target != null ? <g>
60
60
  <line x1={pad.l} x2={width - pad.r} y1={sy(target)} y2={sy(target)} stroke="var(--chart-guide)" strokeWidth="1" strokeDasharray="3 3" />
61
- <text x={width - pad.r} y={sy(target) - 4} textAnchor="end" style={{ fontFamily: "var(--font-sans)", fontSize: 10, fill: "var(--chart-label)" }}>{targetLabel || "Target " + yFormat(target)}</text>
61
+ <text x={width - pad.r} y={sy(target) - 4} textAnchor="end" style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-2xs)", fill: "var(--chart-label)" }}>{targetLabel || "Target " + yFormat(target)}</text>
62
62
  </g> : null}
63
63
  {painted.map((L) => (
64
64
  <g key={L.s.name}>
@@ -52,13 +52,13 @@ function toggle(name){
52
52
  <template v-if="axes">
53
53
  <g v-for="t in g.ticks" :key="t">
54
54
  <line :x1="g.pad.l" :x2="width-g.pad.r" :y1="g.sy(t)" :y2="g.sy(t)" :stroke="t===0?'var(--chart-axis)':'var(--chart-grid)'" stroke-width="1"/>
55
- <text :x="g.pad.l-8" :y="g.sy(t)+3" text-anchor="end" style="font-family:var(--font-mono);font-size:10px;fill:var(--chart-label)">{{ yFormat(t) }}</text>
55
+ <text :x="g.pad.l-8" :y="g.sy(t)+3" text-anchor="end" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);fill:var(--chart-label)">{{ yFormat(t) }}</text>
56
56
  </g>
57
- <text v-for="(lb,i) in xLabels" :key="'x'+i" :x="g.sx(i)" :y="height-g.pad.b+16" text-anchor="middle" style="font-family:var(--font-sans);font-size:10px;fill:var(--chart-label)">{{ lb }}</text>
57
+ <text v-for="(lb,i) in xLabels" :key="'x'+i" :x="g.sx(i)" :y="height-g.pad.b+16" text-anchor="middle" style="font-family:var(--font-sans);font-size:var(--text-2xs);fill:var(--chart-label)">{{ lb }}</text>
58
58
  </template>
59
59
  <template v-if="target!=null">
60
60
  <line :x1="g.pad.l" :x2="width-g.pad.r" :y1="g.sy(target)" :y2="g.sy(target)" stroke="var(--chart-guide)" stroke-width="1" stroke-dasharray="3 3"/>
61
- <text :x="width-g.pad.r" :y="g.sy(target)-4" text-anchor="end" style="font-family:var(--font-sans);font-size:10px;fill:var(--chart-label)">{{ targetLabel || 'Target '+yFormat(target) }}</text>
61
+ <text :x="width-g.pad.r" :y="g.sy(target)-4" text-anchor="end" style="font-family:var(--font-sans);font-size:var(--text-2xs);fill:var(--chart-label)">{{ targetLabel || 'Target '+yFormat(target) }}</text>
62
62
  </template>
63
63
  <g v-for="ln in g.lines" :key="ln.s.name">
64
64
  <path v-if="area||stacked" :d="ln.fill" :fill="'url(#'+uid+'-'+series.indexOf(ln.s)+')'"/>
@@ -72,7 +72,7 @@ function toggle(name){
72
72
  <div v-for="ln in g.lines" :key="ln.s.name" style="display:flex;align-items:center;gap:6px;padding:1px 0">
73
73
  <span :style="{width:'7px',height:'7px',borderRadius:'2px',background:ln.color,flexShrink:0}"/>
74
74
  <span style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-secondary);white-space:nowrap">{{ ln.s.name }}</span>
75
- <span style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading);margin-left:auto;padding-left:10px">{{ yFormat(ln.s.values[hover]) }}</span>
75
+ <span style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading);margin-left:auto;padding-left:10px">{{ yFormat(ln.s.values[hover]) }}</span>
76
76
  </div>
77
77
  </div>
78
78
  <div v-if="legend && series.length>1" style="display:flex;flex-wrap:wrap;gap:4px 14px;padding-top:8px;line-height:normal">
@@ -32,7 +32,7 @@ export function ParetoChart({ items = [], width = 560, height = 260, axes = true
32
32
  {axes ? <PlotGrid w={width} h={height} pad={pad} ticks={ticks} sy={sy} xLabels={[]} sxLabel={() => 0} yFormat={valueFormat} /> : null}
33
33
  {hover != null ? <rect x={pad.l + slot * hover} y={pad.t} width={slot} height={height - pad.t - pad.b} fill="var(--bg-hover)" /> : null}
34
34
  <line x1={pad.l} x2={width - pad.r} y1={sc(threshold)} y2={sc(threshold)} stroke="var(--chart-guide)" strokeWidth="1" strokeDasharray="3 3" />
35
- <text x={width - pad.r - 2} y={sc(threshold) - 4} textAnchor="end" style={{ fontFamily: "var(--font-sans)", fontSize: 10, fill: "var(--chart-label)" }}>{threshold}%</text>
35
+ <text x={width - pad.r - 2} y={sc(threshold) - 4} textAnchor="end" style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-2xs)", fill: "var(--chart-label)" }}>{threshold}%</text>
36
36
  {rows.map((r, i) => (
37
37
  <rect key={r.label} x={cx(i) - bw / 2} y={sy(r.value)} width={bw} height={Math.max(1, sy(0) - sy(r.value))} rx="2"
38
38
  fill={i <= vital ? "var(--series-1)" : "var(--chart-track)"} stroke={i > vital ? "var(--border-control)" : "none"}
@@ -40,8 +40,8 @@ export function ParetoChart({ items = [], width = 560, height = 260, axes = true
40
40
  ))}
41
41
  <path d={linePath(rows.map((r, i) => [cx(i), sc(r.cum)]))} fill="none" stroke="var(--chart-total)" strokeWidth="1.5" />
42
42
  {rows.map((r, i) => <circle key={i} cx={cx(i)} cy={sc(r.cum)} r={hover === i ? 3.4 : 2.4} fill="var(--chart-total)" />)}
43
- {axes ? [0, 25, 50, 75, 100].map((t) => <text key={t} x={width - pad.r + 6} y={sc(t) + 3} style={{ fontFamily: "var(--font-mono)", fontSize: 10, fill: "var(--chart-label)" }}>{t}%</text>) : null}
44
- {rows.map((r, i) => <text key={"l" + i} x={cx(i)} y={height - pad.b + 15} textAnchor="middle" style={{ fontFamily: "var(--font-sans)", fontSize: 10, fill: "var(--chart-label)" }}>{r.label}</text>)}
43
+ {axes ? [0, 25, 50, 75, 100].map((t) => <text key={t} x={width - pad.r + 6} y={sc(t) + 3} style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", fill: "var(--chart-label)" }}>{t}%</text>) : null}
44
+ {rows.map((r, i) => <text key={"l" + i} x={cx(i)} y={height - pad.b + 15} textAnchor="middle" style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-2xs)", fill: "var(--chart-label)" }}>{r.label}</text>)}
45
45
  </svg>
46
46
  {hover != null && interactive ? (
47
47
  <ChartTooltip x={cx(hover)} y={height / 2 - 10} flip={cx(hover) > width * 0.62} title={rows[hover].label}
@@ -33,23 +33,23 @@ function move(e){
33
33
  <template v-if="axes">
34
34
  <g v-for="t in g.ticks" :key="t">
35
35
  <line :x1="g.pad.l" :x2="width-g.pad.r" :y1="g.sy(t)" :y2="g.sy(t)" :stroke="t===0?'var(--chart-axis)':'var(--chart-grid)'" stroke-width="1"/>
36
- <text :x="g.pad.l-8" :y="g.sy(t)+3" text-anchor="end" style="font-family:var(--font-mono);font-size:10px;fill:var(--chart-label)">{{ valueFormat(t) }}</text>
36
+ <text :x="g.pad.l-8" :y="g.sy(t)+3" text-anchor="end" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);fill:var(--chart-label)">{{ valueFormat(t) }}</text>
37
37
  </g>
38
38
  </template>
39
39
  <rect v-if="hover!=null" :x="g.pad.l+g.slot*hover" :y="g.pad.t" :width="g.slot" :height="height-g.pad.t-g.pad.b" fill="var(--bg-hover)"/>
40
40
  <line :x1="g.pad.l" :x2="width-g.pad.r" :y1="g.sc(threshold)" :y2="g.sc(threshold)" stroke="var(--chart-guide)" stroke-width="1" stroke-dasharray="3 3"/>
41
- <text :x="width-g.pad.r-2" :y="g.sc(threshold)-4" text-anchor="end" style="font-family:var(--font-sans);font-size:10px;fill:var(--chart-label)">{{ threshold }}%</text>
41
+ <text :x="width-g.pad.r-2" :y="g.sc(threshold)-4" text-anchor="end" style="font-family:var(--font-sans);font-size:var(--text-2xs);fill:var(--chart-label)">{{ threshold }}%</text>
42
42
  <rect v-for="(r,i) in g.rows" :key="r.label" :x="g.cx(i)-g.bw/2" :y="g.sy(r.value)" :width="g.bw" :height="Math.max(1,g.sy(0)-g.sy(r.value))" rx="2"
43
43
  :fill="i<=g.vital?'var(--series-1)':'var(--chart-track)'" :stroke="i>g.vital?'var(--border-control)':'none'"
44
44
  :opacity="hover==null||hover===i?1:0.5" style="transition:opacity var(--dur-fast) var(--ease-out)"/>
45
45
  <path :d="g.line" fill="none" stroke="var(--chart-total)" stroke-width="1.5"/>
46
46
  <circle v-for="(r,i) in g.rows" :key="'c'+i" :cx="g.cx(i)" :cy="g.sc(r.cum)" :r="hover===i?3.4:2.4" fill="var(--chart-total)"/>
47
- <text v-if="axes" v-for="t in g.pct" :key="'p'+t" :x="width-g.pad.r+6" :y="g.sc(t)+3" style="font-family:var(--font-mono);font-size:10px;fill:var(--chart-label)">{{ t }}%</text>
48
- <text v-for="(r,i) in g.rows" :key="'l'+i" :x="g.cx(i)" :y="height-g.pad.b+15" text-anchor="middle" style="font-family:var(--font-sans);font-size:10px;fill:var(--chart-label)">{{ r.label }}</text>
47
+ <text v-if="axes" v-for="t in g.pct" :key="'p'+t" :x="width-g.pad.r+6" :y="g.sc(t)+3" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);fill:var(--chart-label)">{{ t }}%</text>
48
+ <text v-for="(r,i) in g.rows" :key="'l'+i" :x="g.cx(i)" :y="height-g.pad.b+15" text-anchor="middle" style="font-family:var(--font-sans);font-size:var(--text-2xs);fill:var(--chart-label)">{{ r.label }}</text>
49
49
  </svg>
50
50
  <div v-if="hover!=null && interactive" :style="{position:'absolute',left:g.cx(hover)+'px',top:(height/2-10)+'px',transform:g.cx(hover)>width*0.62?'translate(calc(-100% - 10px),-50%)':'translate(10px,-50%)',pointerEvents:'none',zIndex:5,background:'var(--bg-surface)',border:'1px solid var(--border-card)',borderRadius:'var(--radius-md)',boxShadow:'var(--shadow-menu)',padding:'7px 10px',lineHeight:'normal',whiteSpace:'nowrap'}">
51
51
  <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px">{{ g.rows[hover].label }}</div>
52
- <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">{{ valueFormat(g.rows[hover].value) }} · {{ ((g.rows[hover].value/g.total)*100).toFixed(1) }}% · cum {{ g.rows[hover].cum.toFixed(1) }}%</div>
52
+ <div style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading)">{{ valueFormat(g.rows[hover].value) }} · {{ ((g.rows[hover].value/g.total)*100).toFixed(1) }}% · cum {{ g.rows[hover].cum.toFixed(1) }}%</div>
53
53
  </div>
54
54
  </div>
55
55
  </template>
@@ -132,7 +132,7 @@ export function Pie3D({ segments = [], size = 300, thickness = 26, innerRatio =
132
132
  style={{ display: "inline-flex", alignItems: "center", gap: 7, opacity: hover == null || hover === i ? 1 : 0.45 }}>
133
133
  <span style={{ width: 8, height: 8, borderRadius: 2, background: color, flexShrink: 0 }} />
134
134
  <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", color: "var(--text-secondary)" }}>{s.label}</span>
135
- <span style={{ fontFamily: "var(--font-mono)", fontSize: "var(--text-xs)", color: "var(--text-heading)", marginLeft: 6 }}>
135
+ <span style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-xs)", color: "var(--text-heading)", marginLeft: 6 }}>
136
136
  {hover === i ? valueFormat(s.value) : ((s.value / total) * 100).toFixed(0) + "%"}
137
137
  </span>
138
138
  </span>
@@ -98,7 +98,7 @@ function enter(i){ if (props.interactive && !drag.value) hover.value = i }
98
98
  </svg>
99
99
  <div v-if="g.tip && interactive" :style="{position:'absolute',left:g.tip.p[0]+'px',top:g.tip.p[1]+'px',transform:g.tip.p[0]>size*0.55?'translate(calc(-100% - 10px),-50%)':'translate(10px,-50%)',pointerEvents:'none',zIndex:5,background:'var(--bg-surface)',border:'1px solid var(--border-card)',borderRadius:'var(--radius-md)',boxShadow:'var(--shadow-menu)',padding:'7px 10px',lineHeight:'normal',whiteSpace:'nowrap'}">
100
100
  <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px">{{ g.tip.s.label }}</div>
101
- <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">{{ valueFormat(g.tip.s.value) }} · {{ ((g.tip.s.value/g.total)*100).toFixed(1) }}%</div>
101
+ <div style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading)">{{ valueFormat(g.tip.s.value) }} · {{ ((g.tip.s.value/g.total)*100).toFixed(1) }}%</div>
102
102
  </div>
103
103
  </div>
104
104
  <div v-if="legend" style="display:flex;flex-direction:column;gap:6px;line-height:normal">
@@ -106,7 +106,7 @@ function enter(i){ if (props.interactive && !drag.value) hover.value = i }
106
106
  :style="{display:'inline-flex',alignItems:'center',gap:'7px',opacity:dim(sl.i)}">
107
107
  <span :style="{width:'8px',height:'8px',borderRadius:'2px',background:sl.color,flexShrink:0}"/>
108
108
  <span style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-secondary)">{{ sl.s.label }}</span>
109
- <span style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading);margin-left:6px">
109
+ <span style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading);margin-left:6px">
110
110
  {{ hover===sl.i ? valueFormat(sl.s.value) : ((sl.s.value/g.total)*100).toFixed(0)+'%' }}</span>
111
111
  </span>
112
112
  </div>
@@ -0,0 +1,18 @@
1
+ <script setup>
2
+ import { fmtNum } from './chart-geometry.js'
3
+ // SVG plot furniture: y tick labels + horizontal gridlines (zero line darker), x category labels.
4
+ // Render inside an <svg>. sy / sxLabel are the chart's scale functions.
5
+ defineProps({ w: Number, h: Number, pad: Object, ticks: { type: Array, default: () => [] }, sy: Function, xLabels: { type: Array, default: () => [] }, sxLabel: Function, showAxisLine: Boolean, yFormat: { type: Function, default: fmtNum } })
6
+ </script>
7
+ <template>
8
+ <g>
9
+ <g v-for="t in ticks" :key="t">
10
+ <line :x1="pad.l" :x2="w - pad.r" :y1="sy(t)" :y2="sy(t)" :stroke="t === 0 ? 'var(--chart-axis)' : 'var(--chart-grid)'" stroke-width="1" />
11
+ <text :x="pad.l - 8" :y="sy(t) + 3" text-anchor="end" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);fill:var(--chart-label)">{{ yFormat(t) }}</text>
12
+ </g>
13
+ <line v-if="showAxisLine" :x1="pad.l" :x2="pad.l" :y1="pad.t" :y2="h - pad.b" stroke="var(--chart-axis)" stroke-width="1" />
14
+ <template v-for="(lb, i) in xLabels" :key="'x' + i">
15
+ <text v-if="lb != null" :x="sxLabel(i)" :y="h - pad.b + 16" text-anchor="middle" style="font-family:var(--font-sans);font-size:var(--text-2xs);fill:var(--chart-label)">{{ lb }}</text>
16
+ </template>
17
+ </g>
18
+ </template>
@@ -51,7 +51,7 @@ export function RadarChart({ axes = [], series = [], size = 260, levels = 4, leg
51
51
  <g key={a.label}>
52
52
  <line x1={cx} y1={cy} x2={x} y2={y} stroke={on ? "var(--chart-guide)" : "var(--chart-axis)"} strokeWidth="1" />
53
53
  <text x={lx} y={ly + 3} textAnchor={anchor}
54
- style={{ fontFamily: "var(--font-sans)", fontSize: 10, fill: on ? "var(--text-heading)" : "var(--chart-label)" }}>{a.label}</text>
54
+ style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-2xs)", fill: on ? "var(--text-heading)" : "var(--chart-label)" }}>{a.label}</text>
55
55
  </g>
56
56
  );
57
57
  })}