basalt-ui 1.41.0 → 2.0.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 (915) hide show
  1. package/AGENTS.md +46 -43
  2. package/CHANGELOG.md +542 -518
  3. package/MIGRATING.md +964 -37
  4. package/README.md +171 -366
  5. package/agent/rules/basalt-batteries.md +19 -74
  6. package/agent/rules/basalt-charts.md +21 -95
  7. package/agent/rules/basalt-controls.md +23 -131
  8. package/agent/rules/basalt-mantine.md +26 -99
  9. package/agent/rules/basalt-state.md +21 -103
  10. package/agent/rules/basalt-tokens.md +33 -114
  11. package/agent/skills/basalt-app/SKILL.md +229 -72
  12. package/agent/skills/basalt-charts/SKILL.md +23 -57
  13. package/agent/skills/basalt-design/SKILL.md +12 -70
  14. package/agent/templates/AGENTS-block.md.tpl +16 -26
  15. package/agent/templates/DESIGN.md.tpl +1 -1
  16. package/agent/templates/__root.tpl +9 -16
  17. package/agent/templates/main.tpl +21 -0
  18. package/agent/templates/nav.tpl +18 -0
  19. package/configs/oxlint-basalt.json +4 -10
  20. package/configs/oxlint-plugin.js +465 -844
  21. package/configs/oxlint-plugin.test.ts +661 -709
  22. package/configs/oxlint.json +10 -2
  23. package/dist/agent/ai-sdk-transport.d.ts +19 -7
  24. package/dist/agent/ai-sdk-transport.d.ts.map +1 -1
  25. package/dist/agent/ai-sdk-transport.js +10 -7
  26. package/dist/{agent-chat → agent/chat}/composer.d.ts +16 -16
  27. package/dist/agent/chat/composer.d.ts.map +1 -0
  28. package/dist/{agent-chat → agent/chat}/composer.js +10 -9
  29. package/dist/{agent-chat → agent/chat}/message-affordances.d.ts +7 -6
  30. package/dist/agent/chat/message-affordances.d.ts.map +1 -0
  31. package/dist/agent/chat/relative-time.d.ts.map +1 -0
  32. package/dist/{agent-chat → agent/chat}/thread-feed-row.d.ts +18 -16
  33. package/dist/agent/chat/thread-feed-row.d.ts.map +1 -0
  34. package/dist/{agent-chat → agent/chat}/thread-feed-row.js +12 -11
  35. package/dist/{agent-chat → agent/chat}/thread-feed.d.ts +8 -8
  36. package/dist/agent/chat/thread-feed.d.ts.map +1 -0
  37. package/dist/{agent-chat → agent/chat}/thread-feed.js +12 -10
  38. package/dist/{agent-chat → agent/chat}/thread-message.d.ts +12 -9
  39. package/dist/agent/chat/thread-message.d.ts.map +1 -0
  40. package/dist/{agent-chat → agent/chat}/thread-message.js +15 -13
  41. package/dist/{agent-chat → agent/chat}/thread-outcome-card.d.ts +2 -2
  42. package/dist/agent/chat/thread-outcome-card.d.ts.map +1 -0
  43. package/dist/{agent-chat → agent/chat}/thread-outcome-card.js +3 -3
  44. package/dist/{agent-chat → agent/chat}/tool-chip.d.ts +5 -5
  45. package/dist/agent/chat/tool-chip.d.ts.map +1 -0
  46. package/dist/{agent-chat → agent/chat}/tool-chip.js +4 -4
  47. package/dist/agent/chat/use-keyboard-inset.d.ts.map +1 -0
  48. package/dist/{agent-chat → agent/chat}/use-keyboard-inset.js +1 -1
  49. package/dist/{agent-chat → agent/chat}/virtualize.d.ts +7 -7
  50. package/dist/agent/chat/virtualize.d.ts.map +1 -0
  51. package/dist/agent/error.d.ts +31 -0
  52. package/dist/agent/error.d.ts.map +1 -0
  53. package/dist/agent/error.js +18 -0
  54. package/dist/agent/history.d.ts +2 -36
  55. package/dist/agent/history.d.ts.map +1 -1
  56. package/dist/agent/history.js +0 -32
  57. package/dist/agent/id.d.ts +4 -5
  58. package/dist/agent/id.d.ts.map +1 -1
  59. package/dist/agent/index.d.ts +19 -52
  60. package/dist/agent/index.d.ts.map +1 -1
  61. package/dist/agent/index.js +10 -32
  62. package/dist/agent/outcome.d.ts +3 -14
  63. package/dist/agent/outcome.d.ts.map +1 -1
  64. package/dist/agent/outcome.js +0 -30
  65. package/dist/agent/part-list.d.ts +3 -3
  66. package/dist/agent/part-list.d.ts.map +1 -1
  67. package/dist/agent/parts.d.ts +23 -24
  68. package/dist/agent/parts.d.ts.map +1 -1
  69. package/dist/agent/run-committer.d.ts +23 -0
  70. package/dist/agent/run-committer.d.ts.map +1 -0
  71. package/dist/agent/run-committer.js +41 -0
  72. package/dist/agent/run-records.d.ts +77 -0
  73. package/dist/agent/run-records.d.ts.map +1 -0
  74. package/dist/agent/run-records.js +69 -0
  75. package/dist/agent/thread.d.ts +23 -47
  76. package/dist/agent/thread.d.ts.map +1 -1
  77. package/dist/agent/thread.js +3 -134
  78. package/dist/agent/transport.d.ts +6 -69
  79. package/dist/agent/transport.d.ts.map +1 -1
  80. package/dist/agent/transport.js +0 -18
  81. package/dist/agent/use-agent-thread-runs.d.ts +63 -26
  82. package/dist/agent/use-agent-thread-runs.d.ts.map +1 -1
  83. package/dist/agent/use-agent-thread-runs.js +202 -66
  84. package/dist/charts/BandStrip.d.ts +46 -0
  85. package/dist/charts/BandStrip.d.ts.map +1 -0
  86. package/dist/charts/BandStrip.js +151 -0
  87. package/dist/charts/Bars.d.ts +19 -0
  88. package/dist/charts/Bars.d.ts.map +1 -0
  89. package/dist/charts/Bars.js +77 -0
  90. package/dist/charts/CartesianChart.d.ts +57 -0
  91. package/dist/charts/CartesianChart.d.ts.map +1 -0
  92. package/dist/charts/CartesianChart.js +121 -0
  93. package/dist/charts/ChartCard.d.ts +34 -0
  94. package/dist/charts/ChartCard.d.ts.map +1 -0
  95. package/dist/charts/ChartCard.js +115 -0
  96. package/dist/charts/Donut.d.ts +25 -0
  97. package/dist/charts/Donut.d.ts.map +1 -0
  98. package/dist/charts/Donut.js +191 -0
  99. package/dist/charts/DualPanel.d.ts +47 -0
  100. package/dist/charts/DualPanel.d.ts.map +1 -0
  101. package/dist/charts/DualPanel.js +214 -0
  102. package/dist/charts/Heatmap.d.ts +41 -0
  103. package/dist/charts/Heatmap.d.ts.map +1 -0
  104. package/dist/charts/Heatmap.js +216 -0
  105. package/dist/charts/Lines.d.ts +11 -0
  106. package/dist/charts/Lines.d.ts.map +1 -0
  107. package/dist/charts/Lines.js +114 -0
  108. package/dist/charts/MirroredBars.d.ts +44 -0
  109. package/dist/charts/MirroredBars.d.ts.map +1 -0
  110. package/dist/charts/MirroredBars.js +190 -0
  111. package/dist/charts/Sparkline.d.ts +22 -0
  112. package/dist/charts/Sparkline.d.ts.map +1 -0
  113. package/dist/charts/Sparkline.js +129 -0
  114. package/dist/charts/StackedArea.d.ts +15 -0
  115. package/dist/charts/StackedArea.d.ts.map +1 -0
  116. package/dist/charts/StackedArea.js +46 -0
  117. package/dist/charts/box.d.ts +38 -0
  118. package/dist/charts/box.d.ts.map +1 -0
  119. package/dist/charts/box.js +72 -0
  120. package/dist/charts/card-context.d.ts +3 -0
  121. package/dist/charts/card-context.d.ts.map +1 -0
  122. package/dist/charts/card-context.js +7 -0
  123. package/dist/charts/chart-box.module.css +64 -0
  124. package/{src/content/article-card.module.css.d.ts → dist/charts/chart-box.module.css.d.ts} +5 -9
  125. package/dist/charts/chart-card.module.css +25 -0
  126. package/dist/{dashboard/widget-grid.module.css.d.ts → charts/chart-card.module.css.d.ts} +1 -3
  127. package/dist/charts/chart-legend.module.css +38 -0
  128. package/{src/dashboard/widget-grid.module.css.d.ts → dist/charts/chart-legend.module.css.d.ts} +3 -3
  129. package/dist/charts/compile.d.ts +62 -0
  130. package/dist/charts/compile.d.ts.map +1 -0
  131. package/dist/charts/compile.js +386 -0
  132. package/dist/charts/cursor.d.ts +50 -0
  133. package/dist/charts/cursor.d.ts.map +1 -0
  134. package/dist/charts/cursor.js +157 -0
  135. package/dist/charts/curves.d.ts +12 -0
  136. package/dist/charts/curves.d.ts.map +1 -0
  137. package/dist/charts/curves.js +86 -0
  138. package/dist/charts/donut.module.css +54 -0
  139. package/dist/{content/article-card.module.css.d.ts → charts/donut.module.css.d.ts} +2 -9
  140. package/dist/charts/host.d.ts +17 -0
  141. package/dist/charts/host.d.ts.map +1 -0
  142. package/dist/charts/host.js +35 -0
  143. package/dist/charts/index.d.ts +18 -53
  144. package/dist/charts/index.d.ts.map +1 -1
  145. package/dist/charts/index.js +17 -189
  146. package/dist/charts/legend.d.ts +23 -0
  147. package/dist/charts/legend.d.ts.map +1 -0
  148. package/dist/charts/legend.js +160 -0
  149. package/dist/charts/marks.d.ts +25 -0
  150. package/dist/charts/marks.d.ts.map +1 -0
  151. package/dist/charts/marks.js +128 -0
  152. package/dist/charts/panel.d.ts +56 -0
  153. package/dist/charts/panel.d.ts.map +1 -0
  154. package/dist/charts/panel.js +123 -0
  155. package/dist/charts/scales.d.ts +44 -0
  156. package/dist/charts/scales.d.ts.map +1 -0
  157. package/dist/charts/scales.js +163 -0
  158. package/dist/charts/sizing.d.ts +12 -0
  159. package/dist/charts/sizing.d.ts.map +1 -0
  160. package/dist/charts/sizing.js +33 -0
  161. package/dist/charts/state.d.ts +17 -0
  162. package/dist/charts/state.d.ts.map +1 -0
  163. package/dist/charts/state.js +53 -0
  164. package/dist/charts/theme.d.ts +38 -19
  165. package/dist/charts/theme.d.ts.map +1 -1
  166. package/dist/charts/theme.js +48 -26
  167. package/dist/charts/types.d.ts +227 -0
  168. package/dist/charts/types.d.ts.map +1 -0
  169. package/dist/charts/types.js +4 -0
  170. package/dist/cli/audit-allows.d.ts +4 -17
  171. package/dist/cli/audit-allows.d.ts.map +1 -1
  172. package/dist/cli/config.d.ts +18 -43
  173. package/dist/cli/config.d.ts.map +1 -1
  174. package/dist/cli/config.js +6 -0
  175. package/dist/cli/doctor-upgrade.d.ts +43 -0
  176. package/dist/cli/doctor-upgrade.d.ts.map +1 -0
  177. package/dist/cli/doctor-upgrade.js +314 -0
  178. package/dist/cli/doctor.d.ts +14 -29
  179. package/dist/cli/doctor.d.ts.map +1 -1
  180. package/dist/cli/doctor.js +58 -9
  181. package/dist/cli/help.d.ts.map +1 -1
  182. package/dist/cli/help.js +2 -4
  183. package/dist/cli/index.d.ts +48 -172
  184. package/dist/cli/index.d.ts.map +1 -1
  185. package/dist/cli/index.js +39 -132
  186. package/dist/cli/init.d.ts.map +1 -1
  187. package/dist/cli/init.js +43 -37
  188. package/dist/cli/sync.d.ts +5 -21
  189. package/dist/cli/sync.d.ts.map +1 -1
  190. package/dist/cli/sync.js +4 -23
  191. package/dist/cli/tokens-css.d.ts +7 -28
  192. package/dist/cli/tokens-css.d.ts.map +1 -1
  193. package/dist/commands/ShortcutsHelp.d.ts.map +1 -1
  194. package/dist/commands/ShortcutsHelp.js +4 -5
  195. package/dist/commands/define-commands.d.ts +15 -31
  196. package/dist/commands/define-commands.d.ts.map +1 -1
  197. package/dist/commands/define-commands.js +1 -11
  198. package/dist/commands/define-overlays.d.ts +5 -19
  199. package/dist/commands/define-overlays.d.ts.map +1 -1
  200. package/dist/commands/define-overlays.js +3 -11
  201. package/dist/commands/index.d.ts +10 -40
  202. package/dist/commands/index.d.ts.map +1 -1
  203. package/dist/commands/index.js +9 -46
  204. package/dist/{notifications → commands/notifications}/bell.d.ts +1 -1
  205. package/dist/commands/notifications/bell.d.ts.map +1 -0
  206. package/dist/{notifications → commands/notifications}/center.d.ts +2 -2
  207. package/dist/commands/notifications/center.d.ts.map +1 -0
  208. package/dist/{notifications → commands/notifications}/center.js +2 -2
  209. package/dist/{notifications → commands/notifications}/define-notifications.d.ts +3 -3
  210. package/dist/commands/notifications/define-notifications.d.ts.map +1 -0
  211. package/dist/commands/notifications/notify.d.ts +44 -0
  212. package/dist/commands/notifications/notify.d.ts.map +1 -0
  213. package/dist/commands/notifications/notify.js +79 -0
  214. package/dist/{notifications → commands/notifications}/store.d.ts +2 -0
  215. package/dist/commands/notifications/store.d.ts.map +1 -0
  216. package/dist/{notifications → commands/notifications}/store.js +4 -0
  217. package/dist/commands/projectors.d.ts +1 -131
  218. package/dist/commands/projectors.d.ts.map +1 -1
  219. package/dist/commands/projectors.js +2 -73
  220. package/dist/commands/shell-bridge.d.ts +3 -3
  221. package/dist/commands/shortcut-format.d.ts +5 -4
  222. package/dist/commands/shortcut-format.d.ts.map +1 -1
  223. package/dist/common/errors.d.ts +3 -3
  224. package/dist/common/use-measured-widths.d.ts.map +1 -1
  225. package/dist/common/use-measured-widths.js +5 -3
  226. package/dist/common/validate.d.ts +2 -2
  227. package/dist/content/article-layout.d.ts.map +1 -1
  228. package/dist/content/article-layout.js +3 -3
  229. package/dist/content/article-model.d.ts +1 -46
  230. package/dist/content/article-model.d.ts.map +1 -1
  231. package/dist/content/article-model.js +1 -29
  232. package/dist/content/index.d.ts +11 -41
  233. package/dist/content/index.d.ts.map +1 -1
  234. package/dist/content/index.js +3 -35
  235. package/dist/content/markdown.d.ts +1 -1
  236. package/dist/content/markdown.d.ts.map +1 -1
  237. package/dist/content/markdown.js +1 -1
  238. package/dist/content/mermaid.d.ts.map +1 -1
  239. package/dist/content/mermaid.js +7 -7
  240. package/dist/content/slug.d.ts +1 -8
  241. package/dist/content/slug.d.ts.map +1 -1
  242. package/dist/content/slug.js +1 -8
  243. package/dist/content/url-hardening.d.ts +2 -2
  244. package/dist/content/url-hardening.d.ts.map +1 -1
  245. package/dist/content/url-hardening.js +2 -0
  246. package/dist/controls/actions.d.ts +16 -1
  247. package/dist/controls/actions.d.ts.map +1 -1
  248. package/dist/controls/enum-filter.d.ts +1 -1
  249. package/dist/controls/enum-filter.d.ts.map +1 -1
  250. package/dist/controls/filter-context.d.ts.map +1 -1
  251. package/dist/controls/filter-context.js +3 -1
  252. package/dist/controls/filter-set.js +4 -6
  253. package/dist/controls/form-layout.d.ts +1 -1
  254. package/dist/controls/form-layout.d.ts.map +1 -1
  255. package/dist/controls/form-layout.js +1 -1
  256. package/dist/controls/glyphs.d.ts +0 -2
  257. package/dist/controls/glyphs.d.ts.map +1 -1
  258. package/dist/controls/glyphs.js +0 -7
  259. package/dist/controls/index.d.ts +12 -17
  260. package/dist/controls/index.d.ts.map +1 -1
  261. package/dist/controls/index.js +5 -39
  262. package/dist/controls/multi-select-filter.d.ts +4 -3
  263. package/dist/controls/multi-select-filter.d.ts.map +1 -1
  264. package/dist/controls/number-filter.d.ts +4 -3
  265. package/dist/controls/number-filter.d.ts.map +1 -1
  266. package/dist/controls/number-filter.js +3 -1
  267. package/dist/controls/range-filter.d.ts +4 -3
  268. package/dist/controls/range-filter.d.ts.map +1 -1
  269. package/dist/controls/search-filter.d.ts +4 -3
  270. package/dist/controls/search-filter.d.ts.map +1 -1
  271. package/dist/controls/search-filter.js +19 -14
  272. package/dist/controls/select-filter.d.ts +4 -3
  273. package/dist/controls/select-filter.d.ts.map +1 -1
  274. package/dist/controls/sync-button.d.ts.map +1 -1
  275. package/dist/controls/sync-button.js +12 -15
  276. package/dist/controls/toggle-filter.d.ts +4 -3
  277. package/dist/controls/toggle-filter.d.ts.map +1 -1
  278. package/dist/controls/uncontrolled.d.ts +12 -0
  279. package/dist/controls/uncontrolled.d.ts.map +1 -0
  280. package/dist/controls/view-tabs.d.ts +2 -2
  281. package/dist/controls/view-tabs.d.ts.map +1 -1
  282. package/dist/controls-dates/index.d.ts +0 -1
  283. package/dist/controls-dates/index.d.ts.map +1 -1
  284. package/dist/dashboard/index.d.ts +7 -9
  285. package/dist/dashboard/index.d.ts.map +1 -1
  286. package/dist/dashboard/index.js +3 -23
  287. package/dist/dashboard/section.d.ts +4 -6
  288. package/dist/dashboard/section.d.ts.map +1 -1
  289. package/dist/dashboard/section.js +4 -10
  290. package/dist/dashboard/stat-card.d.ts +4 -4
  291. package/dist/dashboard/stat-card.d.ts.map +1 -1
  292. package/dist/dashboard/stat-card.js +3 -3
  293. package/dist/dashboard/widget-header.module.css +9 -7
  294. package/dist/dev-dock.d.ts.map +1 -1
  295. package/dist/dev-dock.js +6 -1
  296. package/dist/format/date.d.ts +0 -34
  297. package/dist/format/date.d.ts.map +1 -1
  298. package/dist/format/date.js +0 -54
  299. package/dist/format/index.d.ts +7 -22
  300. package/dist/format/index.d.ts.map +1 -1
  301. package/dist/format/index.js +2 -43
  302. package/dist/format/number.d.ts +3 -14
  303. package/dist/format/number.d.ts.map +1 -1
  304. package/dist/format/number.js +0 -13
  305. package/dist/format/shared.d.ts +2 -3
  306. package/dist/format/shared.d.ts.map +1 -1
  307. package/dist/forms/index.d.ts +0 -11
  308. package/dist/forms/index.d.ts.map +1 -1
  309. package/dist/forms/index.js +1 -14
  310. package/dist/guard/index.d.ts +6 -21
  311. package/dist/guard/index.d.ts.map +1 -1
  312. package/dist/guard/index.js +27 -115
  313. package/dist/guard/types.d.ts +7 -18
  314. package/dist/guard/types.d.ts.map +1 -1
  315. package/dist/index.d.ts +16 -48
  316. package/dist/index.d.ts.map +1 -1
  317. package/dist/index.js +21 -117
  318. package/dist/provider/create-router.d.ts +44 -0
  319. package/dist/provider/create-router.d.ts.map +1 -0
  320. package/dist/provider/create-router.js +62 -0
  321. package/dist/provider/hotkeys.d.ts +8 -0
  322. package/dist/provider/hotkeys.d.ts.map +1 -0
  323. package/dist/provider/hotkeys.js +50 -0
  324. package/dist/provider/index.d.ts +33 -110
  325. package/dist/provider/index.d.ts.map +1 -1
  326. package/dist/provider/index.js +182 -82
  327. package/dist/provider/lab-theme.d.ts.map +1 -1
  328. package/dist/provider/route-transition.d.ts +76 -0
  329. package/dist/provider/route-transition.d.ts.map +1 -0
  330. package/dist/provider/route-transition.js +98 -0
  331. package/dist/provider/spotlight-actions.d.ts +10 -0
  332. package/dist/provider/spotlight-actions.d.ts.map +1 -0
  333. package/dist/provider/spotlight-actions.js +55 -0
  334. package/dist/provider/test-router.d.ts +6 -0
  335. package/dist/provider/test-router.d.ts.map +1 -0
  336. package/dist/provider/test-router.js +18 -0
  337. package/dist/query-client.d.ts +4 -20
  338. package/dist/query-client.d.ts.map +1 -1
  339. package/dist/query-client.js +2 -2
  340. package/dist/router-tanstack/index.d.ts +18 -64
  341. package/dist/router-tanstack/index.d.ts.map +1 -1
  342. package/dist/router-tanstack/index.js +2 -16
  343. package/dist/router-tanstack/nav.d.ts +12 -1
  344. package/dist/router-tanstack/nav.d.ts.map +1 -1
  345. package/dist/router-tanstack/nav.js +8 -1
  346. package/dist/router-tanstack/search-store.d.ts +1 -1
  347. package/dist/router-tanstack/search-store.d.ts.map +1 -1
  348. package/dist/router-tanstack/use-nav.d.ts +8 -9
  349. package/dist/router-tanstack/use-nav.d.ts.map +1 -1
  350. package/dist/router-tanstack/use-nav.js +20 -1
  351. package/dist/shell/app-mobile-nav.d.ts.map +1 -1
  352. package/dist/shell/app-sidebar.js +1 -1
  353. package/dist/shell/index.d.ts +31 -10
  354. package/dist/shell/index.d.ts.map +1 -1
  355. package/dist/shell/index.js +14 -3
  356. package/dist/shell/page-aside.d.ts +2 -2
  357. package/dist/shell/page-aside.d.ts.map +1 -1
  358. package/dist/shell/page-aside.js +31 -12
  359. package/dist/shell/page-bar.d.ts +2 -2
  360. package/dist/shell/page-bar.d.ts.map +1 -1
  361. package/dist/shell/page-bar.js +1 -1
  362. package/dist/state.d.ts +10 -12
  363. package/dist/state.d.ts.map +1 -1
  364. package/dist/state.js +4 -6
  365. package/dist/styles.css +107 -7
  366. package/dist/surfaces.d.ts +28 -103
  367. package/dist/surfaces.d.ts.map +1 -1
  368. package/dist/surfaces.js +54 -143
  369. package/dist/{data → table}/data-table-state.d.ts +1 -1
  370. package/dist/table/data-table-state.d.ts.map +1 -0
  371. package/dist/{data → table}/data-table-state.js +4 -3
  372. package/dist/{data → table}/data-table.d.ts +28 -49
  373. package/dist/table/data-table.d.ts.map +1 -0
  374. package/dist/{data → table}/data-table.js +74 -60
  375. package/dist/table/index.d.ts +18 -0
  376. package/dist/table/index.d.ts.map +1 -0
  377. package/dist/{data/table.js → table/index.js} +1 -1
  378. package/dist/table/query-branch.d.ts.map +1 -0
  379. package/dist/table/table-features.d.ts +69 -0
  380. package/dist/table/table-features.d.ts.map +1 -0
  381. package/dist/table/table-features.js +49 -0
  382. package/dist/telemetry.d.ts +77 -0
  383. package/dist/telemetry.d.ts.map +1 -0
  384. package/dist/telemetry.js +33 -0
  385. package/dist/theme/index.d.ts +10 -21
  386. package/dist/theme/index.d.ts.map +1 -1
  387. package/dist/theme/index.js +3 -14
  388. package/dist/theme/theme-toggle.d.ts.map +1 -1
  389. package/dist/theme/theme-toggle.js +7 -33
  390. package/dist/theme/theme-toggle.module.css +30 -0
  391. package/dist/theme/theme-toggle.module.css.d.ts +10 -0
  392. package/dist/theme-lab/derive-controls.d.ts.map +1 -1
  393. package/dist/theme-lab/derive-controls.js +5 -3
  394. package/dist/theme-lab/derive-state.d.ts.map +1 -1
  395. package/dist/theme-lab/derive-state.js +2 -1
  396. package/dist/tokens/core.d.ts +360 -0
  397. package/dist/tokens/core.d.ts.map +1 -0
  398. package/dist/tokens/core.js +708 -0
  399. package/dist/tokens/derive.d.ts +1 -1
  400. package/dist/tokens/derive.d.ts.map +1 -1
  401. package/dist/tokens/index.d.ts +5 -373
  402. package/dist/tokens/index.d.ts.map +1 -1
  403. package/dist/tokens/index.js +8 -700
  404. package/dist/tokens/palette.d.ts +2 -6
  405. package/dist/tokens/palette.d.ts.map +1 -1
  406. package/dist/tokens/palette.js +1 -4
  407. package/dist/vite.d.ts +4 -3
  408. package/dist/vite.d.ts.map +1 -1
  409. package/llms.txt +43 -85
  410. package/package.json +80 -173
  411. package/src/agent/ai-sdk-transport.ts +57 -37
  412. package/src/{agent-chat → agent/chat}/composer.tsx +29 -30
  413. package/src/{agent-chat → agent/chat}/message-affordances.ts +8 -5
  414. package/src/{agent-chat → agent/chat}/thread-feed-row.tsx +30 -35
  415. package/src/{agent-chat → agent/chat}/thread-feed.tsx +40 -35
  416. package/src/{agent-chat → agent/chat}/thread-message.tsx +37 -35
  417. package/src/{agent-chat → agent/chat}/thread-outcome-card.tsx +5 -5
  418. package/src/{agent-chat → agent/chat}/tool-chip.tsx +12 -12
  419. package/src/{agent-chat → agent/chat}/use-keyboard-inset.ts +1 -1
  420. package/src/{agent-chat → agent/chat}/virtualize.ts +5 -5
  421. package/src/agent/error.ts +43 -0
  422. package/src/agent/history.ts +2 -107
  423. package/src/agent/id.ts +4 -5
  424. package/src/agent/index.ts +23 -112
  425. package/src/agent/outcome.ts +3 -56
  426. package/src/agent/part-list.tsx +3 -3
  427. package/src/agent/parts.ts +23 -22
  428. package/src/agent/run-committer.ts +64 -0
  429. package/src/agent/run-records.ts +147 -0
  430. package/src/agent/thread.ts +24 -231
  431. package/src/agent/transport.ts +6 -96
  432. package/src/agent/use-agent-thread-runs.ts +364 -199
  433. package/src/charts/BandStrip.tsx +276 -0
  434. package/src/charts/Bars.tsx +141 -0
  435. package/src/charts/CartesianChart.tsx +206 -0
  436. package/src/charts/ChartCard.tsx +178 -0
  437. package/src/charts/Donut.tsx +256 -0
  438. package/src/charts/DualPanel.tsx +326 -0
  439. package/src/charts/Heatmap.tsx +282 -0
  440. package/src/charts/Lines.tsx +151 -0
  441. package/src/charts/MirroredBars.tsx +303 -0
  442. package/src/charts/Sparkline.tsx +176 -0
  443. package/src/charts/StackedArea.tsx +80 -0
  444. package/src/charts/box.tsx +96 -0
  445. package/src/charts/card-context.ts +11 -0
  446. package/src/charts/chart-box.module.css +64 -0
  447. package/src/charts/chart-box.module.css.d.ts +12 -0
  448. package/src/charts/chart-card.module.css +25 -0
  449. package/src/charts/chart-card.module.css.d.ts +8 -0
  450. package/src/charts/chart-legend.module.css +38 -0
  451. package/src/charts/chart-legend.module.css.d.ts +10 -0
  452. package/src/charts/compile.ts +585 -0
  453. package/src/charts/cursor.ts +282 -0
  454. package/src/charts/curves.ts +111 -0
  455. package/src/charts/donut.module.css +54 -0
  456. package/src/charts/donut.module.css.d.ts +9 -0
  457. package/src/charts/host.tsx +60 -0
  458. package/src/charts/index.ts +41 -178
  459. package/src/charts/legend.tsx +204 -0
  460. package/src/charts/marks.ts +201 -0
  461. package/src/charts/panel.tsx +227 -0
  462. package/src/charts/scales.ts +237 -0
  463. package/src/charts/sizing.ts +47 -0
  464. package/src/charts/state.tsx +80 -0
  465. package/src/charts/theme.ts +65 -0
  466. package/src/charts/types.ts +242 -0
  467. package/src/cli/audit-allows.ts +8 -30
  468. package/src/cli/config.ts +26 -43
  469. package/src/cli/doctor-upgrade.ts +457 -0
  470. package/src/cli/doctor.ts +89 -72
  471. package/src/cli/help.ts +2 -4
  472. package/src/cli/index.ts +100 -384
  473. package/src/cli/init.ts +60 -51
  474. package/src/cli/sync.ts +13 -68
  475. package/src/cli/tokens-css.ts +7 -28
  476. package/src/commands/ShortcutsHelp.tsx +8 -9
  477. package/src/commands/define-commands.ts +16 -43
  478. package/src/commands/define-overlays.ts +12 -36
  479. package/src/commands/index.ts +11 -94
  480. package/src/{notifications → commands/notifications}/bell.tsx +2 -2
  481. package/src/{notifications → commands/notifications}/center.tsx +7 -5
  482. package/src/{notifications → commands/notifications}/define-notifications.ts +3 -3
  483. package/src/commands/notifications/notify.tsx +158 -0
  484. package/src/{notifications → commands/notifications}/store.ts +10 -2
  485. package/src/commands/projectors.ts +5 -225
  486. package/src/commands/shell-bridge.ts +5 -5
  487. package/src/commands/shortcut-format.ts +5 -4
  488. package/src/common/errors.ts +3 -3
  489. package/src/common/use-measured-widths.ts +6 -3
  490. package/src/common/validate.ts +2 -2
  491. package/src/content/article-layout.tsx +37 -38
  492. package/src/content/article-model.ts +1 -84
  493. package/src/content/index.ts +12 -83
  494. package/src/content/markdown.tsx +4 -3
  495. package/src/content/mermaid.tsx +9 -7
  496. package/src/content/slug.ts +1 -16
  497. package/src/content/url-hardening.ts +8 -2
  498. package/src/controls/actions.tsx +24 -1
  499. package/src/controls/enum-filter.tsx +1 -1
  500. package/src/controls/filter-context.tsx +3 -1
  501. package/src/controls/filter-set.tsx +5 -7
  502. package/src/controls/form-layout.tsx +2 -2
  503. package/src/controls/glyphs.tsx +0 -10
  504. package/src/controls/index.ts +13 -64
  505. package/src/controls/multi-select-filter.tsx +5 -4
  506. package/src/controls/number-filter.tsx +8 -4
  507. package/src/controls/range-filter.tsx +5 -4
  508. package/src/controls/search-filter.tsx +25 -19
  509. package/src/controls/select-filter.tsx +5 -4
  510. package/src/controls/sync-button.tsx +18 -20
  511. package/src/controls/toggle-filter.tsx +5 -4
  512. package/src/controls/uncontrolled.ts +12 -0
  513. package/src/controls/view-tabs.tsx +3 -3
  514. package/src/controls-dates/date-range-picker.tsx +1 -1
  515. package/src/controls-dates/index.ts +0 -1
  516. package/src/dashboard/index.ts +7 -48
  517. package/src/dashboard/section.tsx +11 -40
  518. package/src/dashboard/stat-card.tsx +14 -16
  519. package/src/dashboard/widget-header.module.css +9 -7
  520. package/src/dev-dock.tsx +14 -27
  521. package/src/format/date.ts +2 -92
  522. package/src/format/index.ts +7 -53
  523. package/src/format/number.ts +3 -26
  524. package/src/format/shared.ts +2 -3
  525. package/src/forms/index.ts +0 -23
  526. package/src/guard/index.ts +36 -191
  527. package/src/guard/types.ts +6 -19
  528. package/src/index.ts +30 -282
  529. package/src/provider/create-router.tsx +138 -0
  530. package/src/provider/hotkeys.ts +70 -0
  531. package/src/provider/index.tsx +260 -230
  532. package/src/provider/lab-theme.ts +3 -2
  533. package/src/provider/route-transition.ts +199 -0
  534. package/src/provider/spotlight-actions.tsx +69 -0
  535. package/src/provider/test-router.tsx +25 -0
  536. package/src/query-client.ts +4 -22
  537. package/src/router-tanstack/index.ts +18 -112
  538. package/src/router-tanstack/nav.ts +28 -2
  539. package/src/router-tanstack/search-store.ts +9 -2
  540. package/src/router-tanstack/use-nav.ts +47 -13
  541. package/src/shell/app-mobile-nav.tsx +3 -2
  542. package/src/shell/app-sidebar.tsx +1 -1
  543. package/src/shell/aside-docking.ts +1 -1
  544. package/src/shell/index.tsx +61 -20
  545. package/src/shell/page-aside.tsx +47 -23
  546. package/src/shell/page-bar.tsx +3 -3
  547. package/src/shell/page-title.tsx +1 -1
  548. package/src/state.ts +11 -37
  549. package/src/styles.css +107 -7
  550. package/src/surfaces.ts +56 -151
  551. package/src/{data → table}/data-table-state.ts +5 -4
  552. package/src/{data → table}/data-table.tsx +170 -161
  553. package/src/table/index.ts +18 -0
  554. package/src/table/table-features.ts +102 -0
  555. package/src/telemetry.ts +110 -0
  556. package/src/theme/index.ts +22 -47
  557. package/src/theme/theme-toggle.module.css +30 -0
  558. package/src/theme/theme-toggle.module.css.d.ts +10 -0
  559. package/src/theme/theme-toggle.tsx +15 -43
  560. package/src/theme-lab/derive-controls.tsx +5 -3
  561. package/src/theme-lab/derive-state.ts +2 -1
  562. package/src/tokens/core.ts +1163 -0
  563. package/src/tokens/derive.ts +1 -1
  564. package/src/tokens/index.ts +14 -1181
  565. package/src/tokens/palette.ts +3 -7
  566. package/src/vite.ts +3 -3
  567. package/agent/templates/query-client.ts.tpl +0 -26
  568. package/dist/agent/adapter.d.ts +0 -150
  569. package/dist/agent/adapter.d.ts.map +0 -1
  570. package/dist/agent/adapter.js +0 -482
  571. package/dist/agent/use-agent-stream.d.ts +0 -35
  572. package/dist/agent/use-agent-stream.d.ts.map +0 -1
  573. package/dist/agent/use-agent-stream.js +0 -59
  574. package/dist/agent-chat/composer.d.ts.map +0 -1
  575. package/dist/agent-chat/index.d.ts +0 -31
  576. package/dist/agent-chat/index.d.ts.map +0 -1
  577. package/dist/agent-chat/index.js +0 -19
  578. package/dist/agent-chat/message-affordances.d.ts.map +0 -1
  579. package/dist/agent-chat/relative-time.d.ts.map +0 -1
  580. package/dist/agent-chat/thread-detail-panel.d.ts +0 -28
  581. package/dist/agent-chat/thread-detail-panel.d.ts.map +0 -1
  582. package/dist/agent-chat/thread-detail-panel.js +0 -115
  583. package/dist/agent-chat/thread-feed-row.d.ts.map +0 -1
  584. package/dist/agent-chat/thread-feed.d.ts.map +0 -1
  585. package/dist/agent-chat/thread-message.d.ts.map +0 -1
  586. package/dist/agent-chat/thread-outcome-card.d.ts.map +0 -1
  587. package/dist/agent-chat/thread-workspace.d.ts +0 -44
  588. package/dist/agent-chat/thread-workspace.d.ts.map +0 -1
  589. package/dist/agent-chat/thread-workspace.js +0 -153
  590. package/dist/agent-chat/tool-chip.d.ts.map +0 -1
  591. package/dist/agent-chat/use-keyboard-inset.d.ts.map +0 -1
  592. package/dist/agent-chat/virtualize.d.ts.map +0 -1
  593. package/dist/charts/cursor/resolve.d.ts +0 -75
  594. package/dist/charts/cursor/resolve.d.ts.map +0 -1
  595. package/dist/charts/cursor/resolve.js +0 -91
  596. package/dist/charts/cursor/scope.d.ts +0 -20
  597. package/dist/charts/cursor/scope.d.ts.map +0 -1
  598. package/dist/charts/cursor/scope.js +0 -20
  599. package/dist/charts/cursor/store.d.ts +0 -35
  600. package/dist/charts/cursor/store.d.ts.map +0 -1
  601. package/dist/charts/cursor/store.js +0 -25
  602. package/dist/charts/cursor/touch-pin.d.ts +0 -45
  603. package/dist/charts/cursor/touch-pin.d.ts.map +0 -1
  604. package/dist/charts/cursor/touch-pin.js +0 -79
  605. package/dist/charts/hooks/use-discrete-cursor.d.ts +0 -66
  606. package/dist/charts/hooks/use-discrete-cursor.d.ts.map +0 -1
  607. package/dist/charts/hooks/use-discrete-cursor.js +0 -198
  608. package/dist/charts/hooks/use-hysteresis.d.ts +0 -18
  609. package/dist/charts/hooks/use-hysteresis.d.ts.map +0 -1
  610. package/dist/charts/hooks/use-hysteresis.js +0 -12
  611. package/dist/charts/hooks/useBandPlot.d.ts +0 -145
  612. package/dist/charts/hooks/useBandPlot.d.ts.map +0 -1
  613. package/dist/charts/hooks/useBandPlot.js +0 -178
  614. package/dist/charts/hooks/useChartCursor.d.ts +0 -75
  615. package/dist/charts/hooks/useChartCursor.d.ts.map +0 -1
  616. package/dist/charts/hooks/useChartCursor.js +0 -200
  617. package/dist/charts/hooks/useChartSize.d.ts +0 -29
  618. package/dist/charts/hooks/useChartSize.d.ts.map +0 -1
  619. package/dist/charts/hooks/useChartSize.js +0 -8
  620. package/dist/charts/kinds/BandStrip.d.ts +0 -132
  621. package/dist/charts/kinds/BandStrip.d.ts.map +0 -1
  622. package/dist/charts/kinds/BandStrip.js +0 -241
  623. package/dist/charts/kinds/Bars.d.ts +0 -141
  624. package/dist/charts/kinds/Bars.d.ts.map +0 -1
  625. package/dist/charts/kinds/Bars.js +0 -343
  626. package/dist/charts/kinds/Donut.d.ts +0 -77
  627. package/dist/charts/kinds/Donut.d.ts.map +0 -1
  628. package/dist/charts/kinds/Donut.js +0 -245
  629. package/dist/charts/kinds/DualPanel.d.ts +0 -111
  630. package/dist/charts/kinds/DualPanel.d.ts.map +0 -1
  631. package/dist/charts/kinds/DualPanel.js +0 -471
  632. package/dist/charts/kinds/Heatmap.d.ts +0 -85
  633. package/dist/charts/kinds/Heatmap.d.ts.map +0 -1
  634. package/dist/charts/kinds/Heatmap.js +0 -243
  635. package/dist/charts/kinds/MirroredBars.d.ts +0 -98
  636. package/dist/charts/kinds/MirroredBars.d.ts.map +0 -1
  637. package/dist/charts/kinds/MirroredBars.js +0 -292
  638. package/dist/charts/kinds/MultiLine.d.ts +0 -87
  639. package/dist/charts/kinds/MultiLine.d.ts.map +0 -1
  640. package/dist/charts/kinds/MultiLine.js +0 -176
  641. package/dist/charts/kinds/StackedArea.d.ts +0 -73
  642. package/dist/charts/kinds/StackedArea.d.ts.map +0 -1
  643. package/dist/charts/kinds/StackedArea.js +0 -135
  644. package/dist/charts/kinds/ZonedLine.d.ts +0 -97
  645. package/dist/charts/kinds/ZonedLine.d.ts.map +0 -1
  646. package/dist/charts/kinds/ZonedLine.js +0 -155
  647. package/dist/charts/kinds/band-chrome.d.ts +0 -31
  648. package/dist/charts/kinds/band-chrome.d.ts.map +0 -1
  649. package/dist/charts/kinds/band-chrome.js +0 -71
  650. package/dist/charts/kinds/donut-layout.d.ts +0 -47
  651. package/dist/charts/kinds/donut-layout.d.ts.map +0 -1
  652. package/dist/charts/kinds/donut-layout.js +0 -52
  653. package/dist/charts/layout/auto-margin.d.ts +0 -122
  654. package/dist/charts/layout/auto-margin.d.ts.map +0 -1
  655. package/dist/charts/layout/auto-margin.js +0 -81
  656. package/dist/charts/layout/end-labels.d.ts +0 -64
  657. package/dist/charts/layout/end-labels.d.ts.map +0 -1
  658. package/dist/charts/layout/end-labels.js +0 -76
  659. package/dist/charts/layout/log-ticks.d.ts +0 -27
  660. package/dist/charts/layout/log-ticks.d.ts.map +0 -1
  661. package/dist/charts/layout/log-ticks.js +0 -42
  662. package/dist/charts/primitives/AreaGradient.d.ts +0 -18
  663. package/dist/charts/primitives/AreaGradient.d.ts.map +0 -1
  664. package/dist/charts/primitives/AreaGradient.js +0 -13
  665. package/dist/charts/primitives/Axes.d.ts +0 -83
  666. package/dist/charts/primitives/Axes.d.ts.map +0 -1
  667. package/dist/charts/primitives/Axes.js +0 -168
  668. package/dist/charts/primitives/CartesianChart.d.ts +0 -257
  669. package/dist/charts/primitives/CartesianChart.d.ts.map +0 -1
  670. package/dist/charts/primitives/CartesianChart.js +0 -549
  671. package/dist/charts/primitives/ChartCard.d.ts +0 -66
  672. package/dist/charts/primitives/ChartCard.d.ts.map +0 -1
  673. package/dist/charts/primitives/ChartCard.js +0 -143
  674. package/dist/charts/primitives/ChartFrame.d.ts +0 -156
  675. package/dist/charts/primitives/ChartFrame.d.ts.map +0 -1
  676. package/dist/charts/primitives/ChartFrame.js +0 -241
  677. package/dist/charts/primitives/ChartLegend.d.ts +0 -67
  678. package/dist/charts/primitives/ChartLegend.d.ts.map +0 -1
  679. package/dist/charts/primitives/ChartLegend.js +0 -292
  680. package/dist/charts/primitives/ChartPending.d.ts +0 -113
  681. package/dist/charts/primitives/ChartPending.d.ts.map +0 -1
  682. package/dist/charts/primitives/ChartPending.js +0 -117
  683. package/dist/charts/primitives/ChartTooltip.d.ts +0 -59
  684. package/dist/charts/primitives/ChartTooltip.d.ts.map +0 -1
  685. package/dist/charts/primitives/ChartTooltip.js +0 -197
  686. package/dist/charts/primitives/Crosshair.d.ts +0 -26
  687. package/dist/charts/primitives/Crosshair.d.ts.map +0 -1
  688. package/dist/charts/primitives/Crosshair.js +0 -23
  689. package/dist/charts/primitives/HatchPattern.d.ts +0 -31
  690. package/dist/charts/primitives/HatchPattern.d.ts.map +0 -1
  691. package/dist/charts/primitives/HatchPattern.js +0 -31
  692. package/dist/charts/primitives/HoverOverlay.d.ts +0 -45
  693. package/dist/charts/primitives/HoverOverlay.d.ts.map +0 -1
  694. package/dist/charts/primitives/HoverOverlay.js +0 -72
  695. package/dist/charts/primitives/HoverOverlay.module.css +0 -13
  696. package/dist/charts/primitives/HoverOverlay.module.css.d.ts +0 -8
  697. package/dist/charts/primitives/XZoneRects.d.ts +0 -54
  698. package/dist/charts/primitives/XZoneRects.d.ts.map +0 -1
  699. package/dist/charts/primitives/XZoneRects.js +0 -65
  700. package/dist/charts/primitives/ZoneRects.d.ts +0 -38
  701. package/dist/charts/primitives/ZoneRects.d.ts.map +0 -1
  702. package/dist/charts/primitives/ZoneRects.js +0 -30
  703. package/dist/charts/primitives/chart-card-context.d.ts +0 -33
  704. package/dist/charts/primitives/chart-card-context.d.ts.map +0 -1
  705. package/dist/charts/primitives/chart-card-context.js +0 -18
  706. package/dist/charts/primitives/chart-frame-layout.d.ts +0 -185
  707. package/dist/charts/primitives/chart-frame-layout.d.ts.map +0 -1
  708. package/dist/charts/primitives/chart-frame-layout.js +0 -175
  709. package/dist/charts/primitives/chart-layout.d.ts +0 -112
  710. package/dist/charts/primitives/chart-layout.d.ts.map +0 -1
  711. package/dist/charts/primitives/chart-layout.js +0 -175
  712. package/dist/charts/primitives/chart-tier.d.ts +0 -16
  713. package/dist/charts/primitives/chart-tier.d.ts.map +0 -1
  714. package/dist/charts/primitives/chart-tier.js +0 -44
  715. package/dist/charts/primitives/legend-disclosure.d.ts +0 -27
  716. package/dist/charts/primitives/legend-disclosure.d.ts.map +0 -1
  717. package/dist/charts/primitives/legend-disclosure.js +0 -153
  718. package/dist/charts/series.d.ts +0 -189
  719. package/dist/charts/series.d.ts.map +0 -1
  720. package/dist/charts/series.js +0 -84
  721. package/dist/charts/sparklines/BarSparkline.d.ts +0 -37
  722. package/dist/charts/sparklines/BarSparkline.d.ts.map +0 -1
  723. package/dist/charts/sparklines/BarSparkline.js +0 -53
  724. package/dist/charts/sparklines/LineSparkline.d.ts +0 -15
  725. package/dist/charts/sparklines/LineSparkline.d.ts.map +0 -1
  726. package/dist/charts/sparklines/LineSparkline.js +0 -51
  727. package/dist/charts/sparklines/index.d.ts +0 -3
  728. package/dist/charts/sparklines/index.d.ts.map +0 -1
  729. package/dist/charts/sparklines/index.js +0 -6
  730. package/dist/charts/utils/domain.d.ts +0 -35
  731. package/dist/charts/utils/domain.d.ts.map +0 -1
  732. package/dist/charts/utils/domain.js +0 -12
  733. package/dist/charts/utils/format.d.ts +0 -8
  734. package/dist/charts/utils/format.d.ts.map +0 -1
  735. package/dist/charts/utils/format.js +0 -20
  736. package/dist/charts/utils/measure-text.d.ts +0 -25
  737. package/dist/charts/utils/measure-text.d.ts.map +0 -1
  738. package/dist/charts/utils/measure-text.js +0 -64
  739. package/dist/charts/utils/ticks.d.ts +0 -103
  740. package/dist/charts/utils/ticks.d.ts.map +0 -1
  741. package/dist/charts/utils/ticks.js +0 -92
  742. package/dist/commands/overlays-mount.d.ts +0 -122
  743. package/dist/commands/overlays-mount.d.ts.map +0 -1
  744. package/dist/commands/overlays-mount.js +0 -125
  745. package/dist/commands/useCommandHotkeys.d.ts +0 -14
  746. package/dist/commands/useCommandHotkeys.d.ts.map +0 -1
  747. package/dist/commands/useCommandHotkeys.js +0 -45
  748. package/dist/content/article-actions.d.ts +0 -39
  749. package/dist/content/article-actions.d.ts.map +0 -1
  750. package/dist/content/article-actions.js +0 -28
  751. package/dist/content/article-card.d.ts +0 -58
  752. package/dist/content/article-card.d.ts.map +0 -1
  753. package/dist/content/article-card.js +0 -79
  754. package/dist/content/article-card.module.css +0 -111
  755. package/dist/content/mdx.d.ts +0 -20
  756. package/dist/content/mdx.d.ts.map +0 -1
  757. package/dist/content/mdx.js +0 -64
  758. package/dist/controls/compare-filter.d.ts +0 -42
  759. package/dist/controls/compare-filter.d.ts.map +0 -1
  760. package/dist/controls/compare-filter.js +0 -38
  761. package/dist/controls/slider-control.d.ts +0 -36
  762. package/dist/controls/slider-control.d.ts.map +0 -1
  763. package/dist/controls/slider-control.js +0 -64
  764. package/dist/dashboard/widget-grid.d.ts +0 -27
  765. package/dist/dashboard/widget-grid.d.ts.map +0 -1
  766. package/dist/dashboard/widget-grid.js +0 -40
  767. package/dist/dashboard/widget-grid.module.css +0 -69
  768. package/dist/data/data-table-state.d.ts.map +0 -1
  769. package/dist/data/data-table.d.ts.map +0 -1
  770. package/dist/data/query-branch.d.ts.map +0 -1
  771. package/dist/data/table.d.ts +0 -24
  772. package/dist/data/table.d.ts.map +0 -1
  773. package/dist/data/virtual-list.d.ts +0 -133
  774. package/dist/data/virtual-list.d.ts.map +0 -1
  775. package/dist/data/virtual-list.js +0 -157
  776. package/dist/data/virtual.d.ts +0 -21
  777. package/dist/data/virtual.d.ts.map +0 -1
  778. package/dist/data/virtual.js +0 -4
  779. package/dist/forms/FormErrorSummary.d.ts +0 -30
  780. package/dist/forms/FormErrorSummary.d.ts.map +0 -1
  781. package/dist/forms/FormErrorSummary.js +0 -30
  782. package/dist/forms/use-field-array.d.ts +0 -60
  783. package/dist/forms/use-field-array.d.ts.map +0 -1
  784. package/dist/forms/use-field-array.js +0 -20
  785. package/dist/forms/use-form-draft.d.ts +0 -76
  786. package/dist/forms/use-form-draft.d.ts.map +0 -1
  787. package/dist/forms/use-form-draft.js +0 -92
  788. package/dist/forms/use-form-submit.d.ts +0 -88
  789. package/dist/forms/use-form-submit.d.ts.map +0 -1
  790. package/dist/forms/use-form-submit.js +0 -63
  791. package/dist/notifications/bell.d.ts.map +0 -1
  792. package/dist/notifications/center.d.ts.map +0 -1
  793. package/dist/notifications/define-notifications.d.ts.map +0 -1
  794. package/dist/notifications/index.d.ts +0 -40
  795. package/dist/notifications/index.d.ts.map +0 -1
  796. package/dist/notifications/index.js +0 -54
  797. package/dist/notifications/mount-guard.d.ts +0 -8
  798. package/dist/notifications/mount-guard.d.ts.map +0 -1
  799. package/dist/notifications/mount-guard.js +0 -18
  800. package/dist/notifications/notify.d.ts +0 -149
  801. package/dist/notifications/notify.d.ts.map +0 -1
  802. package/dist/notifications/notify.js +0 -171
  803. package/dist/notifications/store.d.ts.map +0 -1
  804. package/dist/provider/connectivity-indicator.d.ts +0 -13
  805. package/dist/provider/connectivity-indicator.d.ts.map +0 -1
  806. package/dist/provider/connectivity-indicator.js +0 -205
  807. package/dist/provider/connectivity-provider.d.ts +0 -7
  808. package/dist/provider/connectivity-provider.d.ts.map +0 -1
  809. package/dist/provider/connectivity-provider.js +0 -128
  810. package/dist/provider/connectivity-types.d.ts +0 -36
  811. package/dist/provider/connectivity-types.d.ts.map +0 -1
  812. package/dist/provider/use-connectivity.d.ts +0 -3
  813. package/dist/provider/use-connectivity.d.ts.map +0 -1
  814. package/dist/provider/use-connectivity.js +0 -14
  815. package/dist/query-devtools.d.ts +0 -21
  816. package/dist/query-devtools.d.ts.map +0 -1
  817. package/dist/query-devtools.js +0 -13
  818. package/src/agent/adapter.ts +0 -945
  819. package/src/agent/use-agent-stream.ts +0 -154
  820. package/src/agent-chat/index.ts +0 -54
  821. package/src/agent-chat/thread-detail-panel.tsx +0 -201
  822. package/src/agent-chat/thread-workspace.tsx +0 -279
  823. package/src/charts/cursor/resolve.ts +0 -198
  824. package/src/charts/cursor/scope.tsx +0 -35
  825. package/src/charts/cursor/store.ts +0 -58
  826. package/src/charts/cursor/touch-pin.ts +0 -138
  827. package/src/charts/hooks/use-discrete-cursor.ts +0 -335
  828. package/src/charts/hooks/use-hysteresis.ts +0 -26
  829. package/src/charts/hooks/useBandPlot.ts +0 -383
  830. package/src/charts/hooks/useChartCursor.ts +0 -367
  831. package/src/charts/hooks/useChartSize.ts +0 -35
  832. package/src/charts/kinds/BandStrip.tsx +0 -449
  833. package/src/charts/kinds/Bars.tsx +0 -589
  834. package/src/charts/kinds/Donut.tsx +0 -368
  835. package/src/charts/kinds/DualPanel.tsx +0 -712
  836. package/src/charts/kinds/Heatmap.tsx +0 -396
  837. package/src/charts/kinds/MirroredBars.tsx +0 -469
  838. package/src/charts/kinds/MultiLine.tsx +0 -322
  839. package/src/charts/kinds/StackedArea.tsx +0 -267
  840. package/src/charts/kinds/ZonedLine.tsx +0 -301
  841. package/src/charts/kinds/band-chrome.tsx +0 -91
  842. package/src/charts/kinds/donut-layout.ts +0 -82
  843. package/src/charts/layout/auto-margin.ts +0 -242
  844. package/src/charts/layout/end-labels.ts +0 -135
  845. package/src/charts/layout/log-ticks.ts +0 -90
  846. package/src/charts/primitives/AreaGradient.tsx +0 -26
  847. package/src/charts/primitives/Axes.tsx +0 -271
  848. package/src/charts/primitives/CartesianChart.tsx +0 -1002
  849. package/src/charts/primitives/ChartCard.tsx +0 -280
  850. package/src/charts/primitives/ChartFrame.tsx +0 -493
  851. package/src/charts/primitives/ChartLegend.tsx +0 -422
  852. package/src/charts/primitives/ChartPending.tsx +0 -243
  853. package/src/charts/primitives/ChartTooltip.tsx +0 -265
  854. package/src/charts/primitives/Crosshair.tsx +0 -43
  855. package/src/charts/primitives/HatchPattern.tsx +0 -55
  856. package/src/charts/primitives/HoverOverlay.module.css +0 -13
  857. package/src/charts/primitives/HoverOverlay.module.css.d.ts +0 -8
  858. package/src/charts/primitives/HoverOverlay.tsx +0 -109
  859. package/src/charts/primitives/XZoneRects.tsx +0 -132
  860. package/src/charts/primitives/ZoneRects.tsx +0 -67
  861. package/src/charts/primitives/chart-card-context.ts +0 -46
  862. package/src/charts/primitives/chart-frame-layout.ts +0 -430
  863. package/src/charts/primitives/chart-layout.ts +0 -318
  864. package/src/charts/primitives/chart-tier.tsx +0 -76
  865. package/src/charts/primitives/legend-disclosure.tsx +0 -178
  866. package/src/charts/series.ts +0 -276
  867. package/src/charts/sparklines/BarSparkline.tsx +0 -94
  868. package/src/charts/sparklines/LineSparkline.tsx +0 -71
  869. package/src/charts/sparklines/index.ts +0 -2
  870. package/src/charts/theme.tsx +0 -53
  871. package/src/charts/utils/domain.ts +0 -41
  872. package/src/charts/utils/format.ts +0 -19
  873. package/src/charts/utils/measure-text.ts +0 -109
  874. package/src/charts/utils/ticks.ts +0 -239
  875. package/src/commands/overlays-mount.tsx +0 -333
  876. package/src/commands/useCommandHotkeys.ts +0 -114
  877. package/src/content/article-actions.ts +0 -72
  878. package/src/content/article-card.module.css +0 -111
  879. package/src/content/article-card.tsx +0 -174
  880. package/src/content/mdx.tsx +0 -127
  881. package/src/controls/compare-filter.tsx +0 -81
  882. package/src/controls/slider-control.tsx +0 -159
  883. package/src/dashboard/widget-grid.module.css +0 -69
  884. package/src/dashboard/widget-grid.tsx +0 -146
  885. package/src/data/table.ts +0 -39
  886. package/src/data/virtual-list.tsx +0 -356
  887. package/src/data/virtual.ts +0 -26
  888. package/src/forms/FormErrorSummary.tsx +0 -71
  889. package/src/forms/use-field-array.ts +0 -94
  890. package/src/forms/use-form-draft.ts +0 -262
  891. package/src/forms/use-form-submit.ts +0 -199
  892. package/src/notifications/index.ts +0 -88
  893. package/src/notifications/mount-guard.ts +0 -38
  894. package/src/notifications/notify.tsx +0 -379
  895. package/src/provider/connectivity-indicator.tsx +0 -267
  896. package/src/provider/connectivity-provider.tsx +0 -189
  897. package/src/provider/connectivity-types.ts +0 -42
  898. package/src/provider/use-connectivity.ts +0 -26
  899. package/src/query-devtools.tsx +0 -40
  900. /package/dist/{agent-chat → agent/chat}/message-affordances.js +0 -0
  901. /package/dist/{agent-chat → agent/chat}/relative-time.d.ts +0 -0
  902. /package/dist/{agent-chat → agent/chat}/relative-time.js +0 -0
  903. /package/dist/{agent-chat → agent/chat}/use-keyboard-inset.d.ts +0 -0
  904. /package/dist/{agent-chat → agent/chat}/virtualize.js +0 -0
  905. /package/dist/{notifications → commands/notifications}/bell.js +0 -0
  906. /package/dist/{notifications → commands/notifications}/define-notifications.js +0 -0
  907. /package/dist/{provider/connectivity-types.js → controls/uncontrolled.js} +0 -0
  908. /package/dist/{data → table}/data-table.module.css +0 -0
  909. /package/dist/{data → table}/data-table.module.css.d.ts +0 -0
  910. /package/dist/{data → table}/query-branch.d.ts +0 -0
  911. /package/dist/{data → table}/query-branch.js +0 -0
  912. /package/src/{agent-chat → agent/chat}/relative-time.ts +0 -0
  913. /package/src/{data → table}/data-table.module.css +0 -0
  914. /package/src/{data → table}/data-table.module.css.d.ts +0 -0
  915. /package/src/{data → table}/query-branch.ts +0 -0
@@ -0,0 +1,1163 @@
1
+ /**
2
+ * Mantine-free token + palette layer.
3
+ *
4
+ * This module is PURE DATA + string helpers — ZERO `@mantine/*` imports (lint-enforced).
5
+ * Colors are CSS custom properties under the `--vx-*` prefix (do NOT rename the prefix).
6
+ * They resolve per color scheme in CSS, so `VX.*` works in components AND in non-component
7
+ * files. Apply opacity via `alpha(token, a)` (never raw `rgba()`) so the hue keeps resolving.
8
+ *
9
+ * Grounded in argo `packages/charts/src/{tokens,palette,theme-vars,utils/color}.ts`.
10
+ */
11
+
12
+ import {
13
+ buildPaletteData,
14
+ RADIUS,
15
+ RADIUS_STEP,
16
+ ROW_LINE_HEIGHT,
17
+ SHADOW,
18
+ SPACE,
19
+ SPACE_SCALE,
20
+ SPACE_STEP,
21
+ } from './palette'
22
+ import type { PaletteData, RadiusValues, SpaceValues } from './palette'
23
+ import { SIZE_CLASSES, sizeClassMaxEm } from './size-classes'
24
+ import { HIT_COARSE, HIT_FINE } from '../common/hit-floor'
25
+ import { BASALT_VX_FAMILIES } from '../common/vx-families'
26
+ import { vxFamilyOf } from '../common/vx-family'
27
+
28
+ // The raw hue families + pair-picker — the building blocks a consumer's series module composes
29
+ // (`hrv: p(BP.blue)`). The doctrine sends every consumer to `basalt-ui/tokens`, so they are public
30
+ // surface, not palette internals; dropping them from the public barrel (`./index`) hard-fails the
31
+ // consumer's build (1.0.0 bug, pinned by scripts/export-surface.json).
32
+ export { BP, p } from './palette'
33
+
34
+ // The six-knob derive engine — the generator behind the shipped default palette. Internal: the
35
+ // public barrel (`./index`) does not re-export it; a consumer retunes the palette identity via
36
+ // `createBasaltTheme(overrides, { derive })` (see `../theme`) without hand-picking hexes, and
37
+ // reaches a derived palette through that, not by building `--vx-*` CSS for a custom config directly.
38
+ export { DEFAULT_DERIVE_CONFIG, deriveTokens, resolveDeriveConfig } from './derive'
39
+ export type { DeriveConfig, DerivedPalette } from './derive'
40
+
41
+ // The radius analog of the derive engine — the law behind `createBasaltTheme(overrides, { radius })`
42
+ // (see `../theme`). `RADIUS`/`RADIUS_STEP` stay internal to the theme layer; a consumer retunes the
43
+ // two anchors through `createBasaltTheme`, not by calling `deriveRadius` directly (it is exposed for
44
+ // the law itself to be inspectable/testable, same rationale as `deriveTokens`).
45
+ export { deriveRadius } from './palette'
46
+ export type { RadiusValues } from './palette'
47
+
48
+ // The density analog of the derive engine — the law behind `createBasaltTheme(overrides, {
49
+ // density })` (see `../theme`). `SPACE`/`SPACE_SCALE`/`SPACE_STEP`/`ROW_LINE_HEIGHT` stay internal
50
+ // to the theme layer; a consumer retunes them through `createBasaltTheme`, not by calling
51
+ // `deriveSpacing` directly (it is exposed for the law itself to be inspectable/testable, same
52
+ // rationale as `deriveRadius`).
53
+ export { deriveSpacing } from './palette'
54
+ export type { SpaceValues } from './palette'
55
+
56
+ // The two responsive tables — MIGRATING recipes and the raw-breakpoint guard messages send
57
+ // consumers here for them; `migrating-imports.test.ts` pins that every documented import resolves.
58
+ export {
59
+ CONTAINER_CLASSES,
60
+ CONTAINER_GRID_BREAKPOINTS,
61
+ CONTAINER_KEYS,
62
+ resolveContainerClass,
63
+ SIZE_CLASSES,
64
+ } from './size-classes'
65
+ export type { ContainerClass } from './size-classes'
66
+
67
+ /** A per-theme color pair: a hue keeps its identity but shifts shade across schemes. */
68
+ export type ColorPair = { light: string; dark: string }
69
+
70
+ /** A map of token name → per-theme color pair (the input shape for series/group builders). */
71
+ export type SeriesMap = Record<string, ColorPair>
72
+
73
+ /**
74
+ * Apply opacity to any palette token, theme-aware. Use instead of raw `rgba()` so the
75
+ * underlying hue still resolves per color scheme.
76
+ *
77
+ * alpha(VX.neutral, 0.5) // muted hairline
78
+ * alpha(VX.status.warn, 0.1) // zone-band fill
79
+ */
80
+ export const alpha = (token: string, a: number): string =>
81
+ `color-mix(in srgb, ${token} ${Math.round(a * 100)}%, transparent)`
82
+
83
+ /**
84
+ * VX tokens — `var(--vx-*)` string refs + non-color sizing constants. Colors resolve per
85
+ * color scheme in CSS, so `VX.*` works in components AND in non-component files. Never
86
+ * reference raw hex in chart files — use `VX.*` / `alpha()`.
87
+ *
88
+ * The framework ships ONLY generic primitives — no domain `VX.series` tree (apps rebuild
89
+ * that with `seriesTokens` / `groupTokens` against their own series maps).
90
+ */
91
+ /**
92
+ * The type scale — the ONE owned ladder, in px.
93
+ *
94
+ * Type is a token axis like color / spacing / radius / motion: a single edit point, never a
95
+ * hardcoded literal at a call site. Every font-size in the framework resolves to a step here.
96
+ *
97
+ * Two representations are DERIVED from this one object, so they cannot drift:
98
+ * • `VX.text.*` — plain numbers, for inline `style` objects and visx SVG props (React appends
99
+ * `px`; SVG presentation attributes can't resolve `var()`, so a ref string would break charts).
100
+ * • `--vx-text-*` — the CSS-var form (`11px`, …), for CSS modules and the Mantine theme.
101
+ *
102
+ * The Mantine theme additionally re-expresses the ladder through Mantine's `rem()` (see
103
+ * `theme/index.ts`), so the component surface scales with the user's browser font-size AND with
104
+ * `--mantine-scale`. `md` is the body step; `lg` is pinned at exactly 16px because it doubles as
105
+ * the iOS input floor (see the `styles.css` floor — Safari zooms the viewport on focus below 16px).
106
+ *
107
+ * ## Adding a rung
108
+ *
109
+ * The ladder is hand-tuned per ROLE, not generated — but it is not arbitrary either, and the
110
+ * constraint is what keeps a "just add the size I need" request from dissolving it. Every adjacent
111
+ * pair sits between **1.06× and 1.17×**, so a new rung must too:
112
+ *
113
+ * • Below the range, two rungs are visually the same size and the ladder stops being a choice —
114
+ * the guard would then report a size for which two tokens are equally "right".
115
+ * • Above it, the jump reads as a different type system rather than the next step of this one.
116
+ *
117
+ * That test is what admitted `nano` (11/10 = 1.10) and `display` (30/26 = 1.154, the second-widest
118
+ * step, which is the shape a display tier is supposed to have), and what rejected a 20px rung
119
+ * between `xl` (18) and `h2` (21): 21/20 = 1.05 is below the floor, and `h2` is already 1px away
120
+ * from any 20px site. A size the ladder declines to grow a rung for is a size that should snap to
121
+ * its nearest step — not a `theme-allow`.
122
+ *
123
+ * A rung is also a ROLE, not just a number. `nano` and `display` are both deliberately named for
124
+ * where they belong (a label engraved inside a drawn object; a numeral read from across a room)
125
+ * rather than for a position, because the failure mode of a bare `xxs`/`xxl` is that it becomes the
126
+ * escape hatch for every size the ladder was meant to prevent.
127
+ */
128
+ const TEXT = {
129
+ nano: 10, // engraved/graphic label INSIDE a drawn object (a plate face, a dial, a chip) — never UI copy or prose
130
+ micro: 11, // mono uppercase micro-label (table th, Menu.Label, sidebar/section headers, axis ticks)
131
+ xs: 12.5, // delta badges, tooltip meta, dense chrome, StatCard labels
132
+ sm: 13.5, // table/stat numerals, chart tooltip, legend
133
+ md: 15, // BODY — nav rows, menu items, timeline, labels, prose; chart/card titles
134
+ lg: 16, // breadcrumb current — ALSO the iOS input floor (do not lower)
135
+ xl: 18, // section titles
136
+ h2: 21, // article-density Prose h2 (docs/CONTENT-SPEC.md §5)
137
+ kpi: 24, // the StatCard hero numeral (density pass: 31 → 24, tighter/less shouty)
138
+ h1: 26, // article-density Prose h1 (docs/CONTENT-SPEC.md §5)
139
+ display: 30, // a numeral read at arm's length (a keypad readout, a timer, a kiosk figure) — not a heading
140
+ } as const
141
+
142
+ export const VX = {
143
+ // Non-color sizing constants
144
+ // Stroke WEIGHTS, not spacing — deliberately fixed, never density-tracking (see
145
+ // `SPACE_STEP_BASE`'s "charts" group doc in tokens/palette.ts).
146
+ lineWidth: 2.5,
147
+ line2Width: 2,
148
+ axisFont: TEXT.micro,
149
+ // A discrete visual footprint (not a stroke weight), so — unlike `lineWidth`/`line2Width` above —
150
+ // it DOES track density; single-sourced from `SPACE_STEP.chartDotR` (see that constant's doc).
151
+ dotR: SPACE_STEP.chartDotR,
152
+ // The ONE dashed pattern — plotted stroke, legend swatch, tooltip swatch all read this.
153
+ dashArray: '6 4' as const,
154
+ // Legend layout — single-sourced from `SPACE_STEP.chartLegendGap` (density-tracking; see that
155
+ // constant's doc). Still a raw number here (not `var()`) — the chart legend applies it via inline
156
+ // `style`, and this is the one static (level-0) value; `createBasaltTheme({ density })` has no
157
+ // wiring into chart render props yet, so this number does NOT move live with the knob (same
158
+ // "dev slider/production JS defaultProps" gap as Timeline's `bulletSize` — see `deriveSpacing`'s
159
+ // doc in tokens/palette.ts).
160
+ legendGap: SPACE_STEP.chartLegendGap,
161
+ legendFontSize: TEXT.sm,
162
+
163
+ // The type scale (px numbers — see TEXT above). Use in inline styles and visx SVG props;
164
+ // use `var(--vx-text-*)` in CSS modules.
165
+ text: TEXT,
166
+
167
+ // Single source for the card corner radius — shared by the Mantine-free ChartCard and the
168
+ // Mantine chrome (Card/Paper resolve to this SAME token). One knob, so cards never diverge.
169
+ radiusCard: 'var(--vx-radius-card)',
170
+ // Single source for the control corner radius (inputs, search, buttons, segmented track).
171
+ radiusCtrl: 'var(--vx-radius-ctrl)',
172
+ // Fully-rounded pill affordance (scroll-to-bottom button, chips) — fixed, does not track the
173
+ // radius knob (see `RADIUS_STEP.pill`'s doc in `tokens/palette.ts`).
174
+ radiusPill: 'var(--vx-radius-pill)',
175
+ // The pointer-tier hit floor: 44px under `(pointer: coarse)` (WCAG 2.5.5), 24px otherwise
176
+ // (2.5.8). Density-exempt; `[data-basalt-hit]::after` in `styles.css` reads it.
177
+ hit: 'var(--vx-hit)',
178
+
179
+ // Controls tier sizing (`docs/CONTROLS-SPEC.md` §5) — `var()` refs onto the density-tracking
180
+ // `--vx-space-*` declarations `spaceDecls` emits above (same ref pattern as `radiusCard`/
181
+ // `radiusCtrl`), so a home/control reads ONE source for its tier height whether it's Mantine
182
+ // chrome (`cssVariablesResolver`/`CTL_THEME`) or a Mantine-free composed layout.
183
+ space: {
184
+ controlHeightTag: 'var(--vx-space-control-height-tag)',
185
+ controlHeightWidget: 'var(--vx-space-control-height-widget)',
186
+ controlHeightCtl: 'var(--vx-space-control-height-ctl)',
187
+ pageBarRowHeight: 'var(--vx-space-page-bar-row-height)',
188
+ sectionHeaderHeight: 'var(--vx-space-section-header-height)',
189
+ widgetHeaderHeight: 'var(--vx-space-widget-header-height)',
190
+ sidebarBlockRowHeight: 'var(--vx-space-sidebar-block-row-height)',
191
+ controlGap: 'var(--vx-space-control-gap)',
192
+ sheetRowHeight: 'var(--vx-space-sheet-row-height)',
193
+ },
194
+
195
+ // Secondary-line color (back-compat alias; now theme-aware via --vx-line2)
196
+ line2Dark: 'var(--vx-line2)',
197
+
198
+ // Semantic fills — consistent opacity across all charts
199
+ good: 'var(--vx-good)',
200
+ goodSoft: 'var(--vx-good-soft)',
201
+ bad: 'var(--vx-bad)',
202
+ warn: 'var(--vx-warn)',
203
+ goodSolid: 'var(--vx-good-solid)',
204
+ badSolid: 'var(--vx-bad-solid)',
205
+ warnSolid: 'var(--vx-warn-solid)',
206
+
207
+ // Reference/dashed lines for thresholds
208
+ goodRef: 'var(--vx-good-ref)',
209
+ badRef: 'var(--vx-bad-ref)',
210
+ warnRef: 'var(--vx-warn-ref)',
211
+
212
+ // Neutral primary line/text color — the default for single-series, no-signal marks
213
+ // ("white/gray per theme"). Same value as the NEUTRAL.line pair.
214
+ line: 'var(--vx-line)',
215
+ line2: 'var(--vx-line2)',
216
+
217
+ // Axis label + axis stroke colors (theme-aware). Exposed as flat refs so a bespoke chart that
218
+ // renders raw <text> labels or axis lines (without the Axis* primitives) needs no hook.
219
+ axis: 'var(--vx-axis)',
220
+ axisStroke: 'var(--vx-axis-stroke)',
221
+
222
+ // Grid, hover, legend
223
+ grid: 'var(--vx-grid)',
224
+ crosshair: 'var(--vx-crosshair)',
225
+ dotStroke: 'var(--vx-dot-stroke)',
226
+ legendText: 'var(--vx-legend-text)',
227
+
228
+ // Tooltip chrome — muted (secondary) text inside a bespoke tooltip. The other tooltip vars
229
+ // (bg/text/border/shadow) reach the engine tooltip as `--ts-chart-tooltip-*` (`charts/theme.ts`);
230
+ // this one is flattened onto `VX` too because bespoke-tooltip authors reached for `VX.muted` (the
231
+ // general ink-ramp muted) instead and got the wrong token — same pattern as `VX.muted` below.
232
+ tooltipMuted: 'var(--vx-tooltip-muted)',
233
+
234
+ // Base neutral for hairlines / muted text / overlays — apply opacity via alpha()
235
+ neutral: 'var(--vx-neutral)',
236
+
237
+ // Ink text ramp (docs/DESIGN-SPEC.md §2) — primary/emphasis/secondary/tertiary body text.
238
+ ink: 'var(--vx-ink)',
239
+ ink2: 'var(--vx-ink2)',
240
+ muted: 'var(--vx-muted)',
241
+ faint: 'var(--vx-faint)',
242
+
243
+ // The accent as INK — primary series, active-nav icon, links, focus rings. Read against the PAGE,
244
+ // so it inverts across schemes (light on dark, deep on light).
245
+ accent: 'var(--vx-accent)',
246
+ accentHover: 'var(--vx-accent-hover)',
247
+ // The accent as SURFACE — a filled control that carries a label. Squeezed between white-text
248
+ // contrast and page contrast, so it is the same hex in both schemes (see ACCENT in palette.ts).
249
+ // Mantine's `--mantine-color-<primary>-filled` is bridged onto this, so it drives ALL filled
250
+ // chrome; a raw `--vx-accent` fill would be unreadable on dark. Fills use THIS, never `accent`.
251
+ accentFill: 'var(--vx-accent-fill)',
252
+ accentFillHover: 'var(--vx-accent-fill-hover)',
253
+ onAccent: 'var(--vx-on-accent)',
254
+
255
+ // Layout separators (header bottom rule, sidebar child indent) — distinct from `surface.border`.
256
+ divider: 'var(--vx-divider)',
257
+
258
+ // Depth shadows — a whisper shadow + a 1px ring baked into the value (never a `border` prop).
259
+ shadowCard: 'var(--vx-shadow-card)',
260
+ shadowCtrl: 'var(--vx-shadow-ctrl)',
261
+ // Raised interactive controls — drop + a 1px INSET edge, the one depth every raised Button/
262
+ // ActionIcon variant shares (works over a fill, a tint or a panel alike; see SHADOW.raised).
263
+ shadowRaised: 'var(--vx-shadow-raised)',
264
+ // Floating-layer elevation (menus, popovers, tooltips, modals, drawers) — a real drop shadow.
265
+ shadowOverlay: 'var(--vx-shadow-overlay)',
266
+
267
+ // App surfaces (cards, borders) — shared with the Mantine chrome
268
+ surface: {
269
+ bg: 'var(--vx-surface-bg)',
270
+ panel: 'var(--vx-surface-panel)',
271
+ panelHover: 'var(--vx-surface-panel-hover)',
272
+ elevated: 'var(--vx-surface-elevated)',
273
+ subtle: 'var(--vx-surface-subtle)',
274
+ overlay: 'var(--vx-surface-overlay)', // floating layer (menus, popovers, modals, drawers)
275
+ field: 'var(--vx-surface-field)', // input field surface (slightly inset on a panel)
276
+ border: 'var(--vx-surface-border)', // "line" — strong border
277
+ hairline: 'var(--vx-surface-hairline)', // card ring only
278
+ },
279
+
280
+ // Score / zone status scale (excellent → poor)
281
+ status: {
282
+ excellent: 'var(--vx-status-excellent)',
283
+ good: 'var(--vx-status-good)',
284
+ warn: 'var(--vx-status-warn)',
285
+ bad: 'var(--vx-status-bad)',
286
+ neutral: 'var(--vx-status-neutral)',
287
+ },
288
+
289
+ // Shared sizing — single-sourced from the `SPACE_STEP` chart-margin steps (density-tracking; same
290
+ // static-value/no-live-wiring note as `legendGap` above).
291
+ margin: {
292
+ top: SPACE_STEP.chartMarginTop,
293
+ right: SPACE_STEP.chartMarginRight,
294
+ bottom: SPACE_STEP.chartMarginBottom,
295
+ left: SPACE_STEP.chartMarginLeft,
296
+ },
297
+ minPxPerTick: 55,
298
+ /**
299
+ * Floor on a chart's plot rect height, in px (the 1.x chart frame). A legend wrapping to five rows at
300
+ * phone width used to eat a fixed `height={240}` toward zero and the plot simply stopped
301
+ * rendering — the plot never shrinks past this, the frame grows instead (or, under `fill`, the
302
+ * legend rolls up).
303
+ */
304
+ minPlotHeight: 120,
305
+ /** Every floating chart overlay portalled to `document.body` — a pinned tooltip, the legend's
306
+ * `All N` disclosure and its backdrop — shares one stacking value, so they always stack the same
307
+ * way relative to page chrome and to each other. */
308
+ zIndexFloating: 9999,
309
+ } as const
310
+
311
+ /**
312
+ * Layout breakpoints, in px, for the places CSS cannot read a custom property.
313
+ *
314
+ * A `@media` query's condition is resolved before custom properties exist, so
315
+ * `@media (max-width: var(--x))` is invalid CSS — a stylesheet with a responsive rule has no
316
+ * choice but to write the number. This constant is the other half of that literal: the CSS states
317
+ * the number and names the token beside it, and a test asserts the two agree
318
+ * (`content/article-layout.breakpoint.test.ts`, the same "literal must match the token" idiom as
319
+ * `styles.floor.test.ts`).
320
+ *
321
+ * Only breakpoints that are genuinely a CSS-authored `@media` belong here. A component that can
322
+ * measure its own box uses the measurement instead (`CONTAINER_CLASSES`), and Mantine chrome uses
323
+ * `theme.breakpoints`.
324
+ */
325
+ export const BREAKPOINTS = {
326
+ /**
327
+ * `content/article-layout.module.css` — below this the TOC rail is dropped and the article
328
+ * collapses to a single centred column. Well above the `sm` (52.5em/840px) shell breakpoint on
329
+ * purpose: the rail needs the prose measure PLUS its own width plus the column gap, which runs
330
+ * out long before the shell goes mobile.
331
+ */
332
+ article: 1200,
333
+ } as const
334
+
335
+ type Side = 'light' | 'dark'
336
+
337
+ /** A CSS declaration line for a single `--vx-*` custom property. */
338
+ const decl = (name: string, value: string): string => ` --vx-${name}: ${value};`
339
+
340
+ /**
341
+ * A px number expressed as the plain rem STRING literal (16px base: `pxRem(6)` → `'0.375rem'`) —
342
+ * NOT Mantine's own `rem()` helper, which instead emits a `calc(...)` expression bound to
343
+ * `--mantine-scale` (a different value; that scaling happens at the Mantine-coupled call site
344
+ * instead — see `theme/index.ts`'s `Input.extend` `vars` for why `--vx-space-input-height` needs
345
+ * both halves). The SINGLE source for this conversion — `theme/index.ts` imports this instead of
346
+ * keeping its own copy, so the radius/spacing size-scale steps and this module's own rem-emitting
347
+ * vars (`--vx-space-input-height`) can never compute the conversion two different ways.
348
+ */
349
+ export function pxRem(px: number): string {
350
+ return `${px / 16}rem`
351
+ }
352
+
353
+ /** Emit one prefixed group of pairs for a given scheme side. */
354
+ const groupSide = (prefix: string, map: SeriesMap, side: Side): string =>
355
+ Object.entries(map)
356
+ .map(([key, pair]) => decl(`${prefix}${key}`, pair[side]))
357
+ .join('\n')
358
+
359
+ /**
360
+ * The 16 deprecated camelCase aliases for `frameworkPrimitives`' per-scheme canonicals, each
361
+ * defined as `var(--vx-<canonical-kebab-name>)` so it can never drift from the value above it.
362
+ * Emitted as its own commented, trailing group (never interleaved with the canonicals) — gated by
363
+ * {@link BuildPaletteOpts.legacyAliases}, default on. See that option's doc for the migration
364
+ * rationale (1.4.0 kebab-case normalization).
365
+ */
366
+ function frameworkPrimitivesAliases(): string {
367
+ return [
368
+ ' /* Deprecated camelCase aliases — removed in a future minor; migrate to the kebab names above. */',
369
+ decl('goodSolid', 'var(--vx-good-solid)'),
370
+ decl('badSolid', 'var(--vx-bad-solid)'),
371
+ decl('warnSolid', 'var(--vx-warn-solid)'),
372
+ decl('axisStroke', 'var(--vx-axis-stroke)'),
373
+ decl('dotStroke', 'var(--vx-dot-stroke)'),
374
+ decl('tooltipBg', 'var(--vx-tooltip-bg)'),
375
+ decl('tooltipText', 'var(--vx-tooltip-text)'),
376
+ decl('tooltipMuted', 'var(--vx-tooltip-muted)'),
377
+ decl('tooltipBorder', 'var(--vx-tooltip-border)'),
378
+ decl('tooltipShadow', 'var(--vx-tooltip-shadow)'),
379
+ decl('legendText', 'var(--vx-legend-text)'),
380
+ decl('accentHover', 'var(--vx-accent-hover)'),
381
+ decl('accentFill', 'var(--vx-accent-fill)'),
382
+ decl('accentFillHover', 'var(--vx-accent-fill-hover)'),
383
+ decl('onAccent', 'var(--vx-on-accent)'),
384
+ decl('surface-panelHover', 'var(--vx-surface-panel-hover)'),
385
+ ].join('\n')
386
+ }
387
+
388
+ /**
389
+ * Built-in framework primitives for a given scheme side — STATUS group, semantic solids,
390
+ * neutral line/axis/grid/tooltip chrome, legend text, and the surface ramp. Domain series
391
+ * (SERIES/ACTIVITY/USAGE) are NOT shipped — apps append them via `opts.groups`.
392
+ */
393
+ function frameworkPrimitives(side: Side, data: PaletteData, legacyAliases: boolean): string {
394
+ const n = data.NEUTRAL
395
+ const s = data.SEMANTIC
396
+ const su = data.SURFACE
397
+ const ink = data.INK
398
+ const ac = data.ACCENT
399
+ const canonical = [
400
+ groupSide('status-', data.STATUS as SeriesMap, side),
401
+ decl('good-solid', s.good[side]),
402
+ decl('bad-solid', s.bad[side]),
403
+ decl('warn-solid', s.warn[side]),
404
+ decl('neutral', n.neutral[side]),
405
+ decl('line', n.line[side]),
406
+ decl('line2', n.line2[side]),
407
+ decl('axis', n.axis[side]),
408
+ decl('axis-stroke', n.axisStroke[side]),
409
+ decl('grid', n.grid[side]),
410
+ decl('crosshair', n.crosshair[side]),
411
+ decl('dot-stroke', n.dotStroke[side]),
412
+ decl('tooltip-bg', n.tooltipBg[side]),
413
+ decl('tooltip-text', n.tooltipText[side]),
414
+ decl('tooltip-muted', n.tooltipMuted[side]),
415
+ decl('tooltip-border', n.tooltipBorder[side]),
416
+ decl('tooltip-shadow', n.tooltipShadow[side]),
417
+ decl('legend-text', n.legendText[side]),
418
+ decl('ink', ink.ink[side]),
419
+ decl('ink2', ink.ink2[side]),
420
+ decl('muted', ink.muted[side]),
421
+ decl('faint', ink.faint[side]),
422
+ decl('accent', ac.accent[side]),
423
+ decl('accent-hover', ac.accentHover[side]),
424
+ decl('accent-fill', ac.accentFill[side]),
425
+ decl('accent-fill-hover', ac.accentFillHover[side]),
426
+ decl('on-accent', ac.onAccent[side]),
427
+ decl('divider', su.divider[side]),
428
+ decl('shadow-card', SHADOW.card[side]),
429
+ decl('shadow-ctrl', SHADOW.ctrl[side]),
430
+ decl('shadow-raised', SHADOW.raised[side]),
431
+ decl('shadow-overlay', SHADOW.overlay[side]),
432
+ decl('surface-bg', su.bg[side]),
433
+ decl('surface-panel', su.panel[side]),
434
+ decl('surface-panel-hover', su.panelHover[side]),
435
+ decl('surface-elevated', su.elevated[side]),
436
+ decl('surface-subtle', su.subtle[side]),
437
+ decl('surface-overlay', su.overlay[side]),
438
+ decl('surface-field', su.field[side]),
439
+ decl('surface-border', su.border[side]),
440
+ decl('surface-hairline', su.hairline[side]),
441
+ ].join('\n')
442
+ return legacyAliases ? `${canonical}\n${frameworkPrimitivesAliases()}` : canonical
443
+ }
444
+
445
+ /**
446
+ * The `--vx-space-*` declaration list for a resolved {@link SpaceValues} — every anchor and one-off
447
+ * that has at least one real `var()` consumer, plus the row line-height. Shared by
448
+ * `frameworkDerived` (emits the shipped defaults unconditionally, called with `DEFAULT_SPACE_
449
+ * VALUES`) and {@link buildDensityCss} (emits an OVERRIDING block for a retuned density level), so
450
+ * the two lists can never drift apart — mirrors `frameworkDerived`'s own single-source reasoning for
451
+ * the radii.
452
+ *
453
+ * The nine controls-tier anchors/one-offs (`controlHeightTag`/`Widget`/`Ctl`,
454
+ * `pageBarRowHeight`, `sectionHeaderHeight`, `widgetHeaderHeight`, `sidebarBlockRowHeight`,
455
+ * `controlGap`, `sheetRowHeight` — `docs/CONTROLS-SPEC.md` §5) are a DELIBERATE exception to the
456
+ * "has at least one real consumer today" rule below: `theme/ctl-theme.tsx`'s `CTL_THEME` and the
457
+ * `cssVariablesResolver`'s `-ctl`/`-icon` var sets consume the four control-height anchors this
458
+ * commit; `PageBar`/`WidgetHeader`/the sidebar blocks (wave 3/4) consume the six one-offs. Emitted
459
+ * now, ahead of that second wave, rather than left as JS-only — unlike the seventeen genuinely
460
+ * dead-weight keys below, these are forward-declared framework surface, not vestigial.
461
+ *
462
+ * `SPACE_FIXED` (Timeline `lineWidth`, 1px borders, the ReadingProgress bar height, SegmentedControl's
463
+ * `segmentedTrackInset`) is deliberately NEVER part of this list — it never moves, so a var would
464
+ * only invite someone to think it does.
465
+ *
466
+ * Deliberately DOES NOT emit a var for every `SpaceValues` number — `space.scale.*` (the generic
467
+ * Mantine spacing scale) and seventeen `space.step.*` keys have ZERO `var()` consumers anywhere in
468
+ * the framework or its tests beyond the CSS-emission test itself. Each is read straight off the
469
+ * resolved `SpaceValues` as a JS NUMBER instead, in one of three ways:
470
+ * `{timelineBullet,progressBarSize}` are Mantine `defaultProps` (`theme/index.ts`'s Timeline/
471
+ * Progress); `{sidebarAccountMenuWidth,sidebarSettingsMenuWidth,mobileNavMenuWidth,
472
+ * appShellHeaderHeight,appShellNavbarWidth,appShellNavbarRailWidth,appShellAsideWidth,
473
+ * appShellAsideRailWidth,
474
+ * mobileNavBarHeight}` are numeric Mantine component props (Menu `width`, AppShell `header`/
475
+ * `navbar`/`footer`); and `{chartLegendGap,chartMarginTop,chartMarginRight,
476
+ * chartMarginBottom,chartMarginLeft,chartDotR}` are visx SVG props via
477
+ * `VX.legendGap`/`VX.margin`/`VX.dotR` in this file's own `VX` object. The full list is locked
478
+ * against the `SpaceValues` key set by `tokens/density.test.ts`'s declaration-set test, so adding a
479
+ * one-off silently omits nothing. A `--vx-space-*` declaration for any of them would be dead weight
480
+ * that only looked like the fix for
481
+ * the dev-slider/production gap those JS reads actually have (see `deriveSpacing`'s JSDoc in
482
+ * `tokens/palette.ts` for the real scope of that gap — it is materially bigger than "a var these
483
+ * numbers could round-trip through"). `theme.spacing` (the generic scale, JS) already resolves
484
+ * through `--mantine-spacing-*` on the PRODUCTION `createBasaltTheme({ density })` path
485
+ * (`theme/index.ts`'s `buildTheme`), so nothing downstream of the generic scale actually needed
486
+ * `--vx-space-scale-*` either — deleted along with the rest rather than kept as an unread alias.
487
+ */
488
+ function spaceDecls(space: SpaceValues): string[] {
489
+ return [
490
+ decl('space-row-inset-x', `${space.anchors.rowInsetX}px`),
491
+ decl('space-row-inset-y', `${space.anchors.rowInsetY}px`),
492
+ // Additive law, not the multiplier — see `deriveSpacing`'s doc in `tokens/palette.ts`. No unit:
493
+ // `line-height` is a unitless CSS value.
494
+ decl('space-row-line-height', `${space.rowLineHeight}`),
495
+ // A `size: 'md'` Input's resolved height. Mantine's own `--input-height-md` is
496
+ // `calc(2.625rem * var(--mantine-scale))` (BOTH `styles.css` and `styles.layer.css`, verified
497
+ // in the installed package) — REM-relative to the root font size AND scale-aware, not a flat
498
+ // px. Emitting this in REM (via `pxRem`, not a bare `${…}px`) preserves the rem half; the
499
+ // `* var(--mantine-scale))` half is reconstructed at the Mantine-coupled call site
500
+ // (`theme/index.ts`'s `Input.extend` `vars`, which wraps this var in the matching `calc(...)`)
501
+ // — this module stays Mantine-free, so it cannot reference `--mantine-scale` itself. Flattening
502
+ // either half into a flat px would desync input height from the user's root font size (an
503
+ // accessibility regression) and from Mantine's global `--mantine-scale` knob.
504
+ decl('space-input-height', pxRem(space.anchors.inputHeight)),
505
+ // A `size: 'md'` Button's/ActionIcon's resolved height — SAME rem-plus-scale reasoning and
506
+ // reconstruction as `space-input-height` above (`theme/index.ts`'s `Button.extend`/
507
+ // `ActionIcon.extend` `vars`), single-sourced from the SAME `controlHeight` anchor
508
+ // `space-input-height` reads from (`SPACE_ANCHORS_BASE`'s doc in `tokens/palette.ts`).
509
+ decl('space-control-height', pxRem(space.anchors.controlHeight)),
510
+ // Controls tier heights (`docs/CONTROLS-SPEC.md` §5) — same rem-plus-`--mantine-scale`
511
+ // reconstruction as `space-input-height`/`space-control-height` above; `theme/ctl-theme.tsx`'s
512
+ // `CTL_THEME` and the `cssVariablesResolver`'s `-ctl`/`-icon` var sets both wrap these in the
513
+ // matching `calc(... * var(--mantine-scale))`.
514
+ decl('space-control-height-tag', pxRem(space.anchors.controlHeightTag)),
515
+ decl('space-control-height-widget', pxRem(space.anchors.controlHeightWidget)),
516
+ decl('space-control-height-ctl', pxRem(space.anchors.controlHeightCtl)),
517
+ decl('space-stack-xs', `${space.anchors.stackXs}px`),
518
+ decl('space-stack-sm', `${space.anchors.stackSm}px`),
519
+ decl('space-stack-md', `${space.anchors.stackMd}px`),
520
+ decl('space-stack-lg', `${space.anchors.stackLg}px`),
521
+ decl('space-stack-xl', `${space.anchors.stackXl}px`),
522
+ // The CSS-module spacing sweep's one-offs (docs/STATUS.md) — same single-source reasoning,
523
+ // grouped to match `SPACE_STEP_BASE`'s own grouping in tokens/palette.ts.
524
+ // ONE value at every viewport since 1.26.0 — the AppShell header is a single
525
+ // `appShellHeaderHeight` row (law C14), so the `-mobile` override of the old Decision-3
526
+ // responsive pair is gone.
527
+ decl('space-sticky-header-clearance', `${space.step.stickyHeaderClearance}px`),
528
+ decl('space-nav-icon-gap', `${space.step.navIconGap}px`),
529
+ decl('space-sidebar-region-gap', `${space.step.sidebarRegionGap}px`),
530
+ decl('space-prose-quote-inset-y', `${space.step.proseQuoteInsetY}px`),
531
+ decl('space-prose-quote-indent', `${space.step.proseQuoteIndent}px`),
532
+ decl('space-prose-inline-code-inset-y', `${space.step.proseInlineCodeInsetY}px`),
533
+ decl('space-prose-inline-code-inset-x', `${space.step.proseInlineCodeInsetX}px`),
534
+ decl('space-prose-code-block-inset-y', `${space.step.proseCodeBlockInsetY}px`),
535
+ decl('space-prose-code-block-inset-x', `${space.step.proseCodeBlockInsetX}px`),
536
+ decl('space-prose-heading-anchor-gap', `${space.step.proseHeadingAnchorGap}px`),
537
+ decl('space-prose-table-cell-inset-y', `${space.step.proseTableCellInsetY}px`),
538
+ decl('space-prose-table-cell-inset-x', `${space.step.proseTableCellInsetX}px`),
539
+ decl('space-prose-chat-list-gap-bottom', `${space.step.proseChatListGapBottom}px`),
540
+ decl('space-prose-chat-list-indent', `${space.step.proseChatListIndent}px`),
541
+ decl('space-prose-chat-list-item-gap', `${space.step.proseChatListItemGap}px`),
542
+ decl('space-prose-chat-nested-list-gap', `${space.step.proseChatNestedListGap}px`),
543
+ decl('space-prose-chat-heading-gap-top', `${space.step.proseChatHeadingGapTop}px`),
544
+ decl('space-prose-chat-heading-gap-bottom', `${space.step.proseChatHeadingGapBottom}px`),
545
+ decl('space-prose-article-paragraph-gap', `${space.step.proseArticleParagraphGap}px`),
546
+ decl('space-prose-article-list-gap-top', `${space.step.proseArticleListGapTop}px`),
547
+ decl('space-prose-article-list-gap-bottom', `${space.step.proseArticleListGapBottom}px`),
548
+ decl('space-prose-article-list-indent', `${space.step.proseArticleListIndent}px`),
549
+ decl('space-prose-article-h1-gap-top', `${space.step.proseArticleH1GapTop}px`),
550
+ decl('space-prose-article-h1-gap-bottom', `${space.step.proseArticleH1GapBottom}px`),
551
+ decl('space-prose-article-heading-gap-top', `${space.step.proseArticleHeadingGapTop}px`),
552
+ decl('space-prose-article-heading-gap-bottom', `${space.step.proseArticleHeadingGapBottom}px`),
553
+ decl('space-prose-article-h2-rule-gap', `${space.step.proseArticleH2RuleGap}px`),
554
+ decl('space-prose-article-block-gap', `${space.step.proseArticleBlockGap}px`),
555
+ decl('space-article-column-gap', `${space.step.articleColumnGap}px`),
556
+ decl('space-article-toc-rail-width', `${space.step.articleTocRailWidth}px`),
557
+ decl('space-article-header-gap', `${space.step.articleHeaderGap}px`),
558
+ decl('space-article-header-padding-bottom', `${space.step.articleHeaderPaddingBottom}px`),
559
+ decl('space-article-header-margin-bottom', `${space.step.articleHeaderMarginBottom}px`),
560
+ decl('space-article-meta-row-gap', `${space.step.articleMetaRowGap}px`),
561
+ decl('space-article-footer-gap', `${space.step.articleFooterGap}px`),
562
+ decl('space-article-footer-margin-top', `${space.step.articleFooterMarginTop}px`),
563
+ decl('space-article-footer-padding-top', `${space.step.articleFooterPaddingTop}px`),
564
+ decl('space-article-nav-target-gap', `${space.step.articleNavTargetGap}px`),
565
+ decl('space-code-block-header-gap', `${space.step.codeBlockHeaderGap}px`),
566
+ decl('space-code-block-header-inset-y', `${space.step.codeBlockHeaderInsetY}px`),
567
+ decl('space-code-block-header-inset-right', `${space.step.codeBlockHeaderInsetRight}px`),
568
+ decl('space-code-block-content-inset-x', `${space.step.codeBlockContentInsetX}px`),
569
+ decl('space-code-block-header-right-gap', `${space.step.codeBlockHeaderRightGap}px`),
570
+ decl('space-code-block-body-inset-y', `${space.step.codeBlockBodyInsetY}px`),
571
+ decl('space-code-block-floating-copy-offset', `${space.step.codeBlockFloatingCopyOffset}px`),
572
+ decl('space-callout-inset-y', `${space.step.calloutInsetY}px`),
573
+ decl('space-callout-inset-x', `${space.step.calloutInsetX}px`),
574
+ decl('space-callout-title-row-gap', `${space.step.calloutTitleRowGap}px`),
575
+ decl('space-toc-root-gap', `${space.step.tocRootGap}px`),
576
+ decl('space-toc-link-inset-y', `${space.step.tocLinkInsetY}px`),
577
+ decl('space-toc-link-indent', `${space.step.tocLinkIndent}px`),
578
+ decl('space-toc-sub-indent', `${space.step.tocSubIndent}px`),
579
+ decl('space-article-card-tags-gap', `${space.step.articleCardTagsGap}px`),
580
+ decl('space-article-card-tag-inset-y', `${space.step.articleCardTagInsetY}px`),
581
+ decl('space-article-card-tag-inset-x', `${space.step.articleCardTagInsetX}px`),
582
+ decl('space-article-card-meta-gap-top', `${space.step.articleCardMetaGapTop}px`),
583
+ decl('space-guide-body-gap-bottom', `${space.step.guideBodyGapBottom}px`),
584
+ decl('space-guide-footer-gap-top', `${space.step.guideFooterGapTop}px`),
585
+ decl('space-guide-footer-inset-top', `${space.step.guideFooterInsetTop}px`),
586
+ decl('space-sidebar-search-gap', `${space.step.sidebarSearchGap}px`),
587
+ decl('space-sidebar-search-trigger-height', `${space.step.sidebarSearchTriggerHeight}px`),
588
+ decl('space-settings-row-inset-y', `${space.step.settingsRowInsetY}px`),
589
+ decl('space-settings-row-gap', `${space.step.settingsRowGap}px`),
590
+ decl('space-mermaid-container-inset', `${space.step.mermaidContainerInset}px`),
591
+ decl('space-sidebar-brand-inset-x', `${space.step.sidebarBrandInsetX}px`),
592
+ decl('space-sidebar-section-gap', `${space.step.sidebarSectionGap}px`),
593
+ decl('space-sidebar-account-inset-top', `${space.step.sidebarAccountInsetTop}px`),
594
+ decl('space-sidebar-account-inset-x', `${space.step.sidebarAccountInsetX}px`),
595
+ decl('space-sidebar-account-inset-bottom', `${space.step.sidebarAccountInsetBottom}px`),
596
+ decl('space-sidebar-avatar-size', `${space.step.sidebarAvatarSize}px`),
597
+ decl('space-sidebar-section-label-gap', `${space.step.sidebarSectionLabelGap}px`),
598
+ decl('space-sidebar-child-list-gap-top', `${space.step.sidebarChildListGapTop}px`),
599
+ decl('space-sidebar-child-list-gap-bottom', `${space.step.sidebarChildListGapBottom}px`),
600
+ decl('space-sidebar-child-list-indent', `${space.step.sidebarChildListIndent}px`),
601
+ decl('space-sidebar-child-row-inset-y', `${space.step.sidebarChildRowInsetY}px`),
602
+ decl('space-sidebar-child-row-indent', `${space.step.sidebarChildRowIndent}px`),
603
+ // The page gutter — the ONE `appShell*` dimension that IS emitted, against the JS-number-only
604
+ // rule its five neighbours follow (see the note at the bottom of this list). It earns the
605
+ // exception because it is the most visible spacing number the framework ships — every page's
606
+ // left edge — and it was, until now, the one number the `--vx-*` layer did not expose at all, so
607
+ // a consumer retuning its identity through `createBasaltTheme` could not reach it and had to
608
+ // reach past the token layer into `AppShell.Main`'s own styles. Emitted in PLAIN PX, like every
609
+ // other inset/gap in this list and unlike the `pxRem` heights above it: the rem-plus-
610
+ // `var(--mantine-scale)` reconstruction exists so a CONTROL's box tracks the root font size and
611
+ // Mantine's global scale knob; a page gutter is a layout margin against a viewport, not a box
612
+ // sized to text, so scaling it with the font would make a large-type user's content column
613
+ // narrower rather than more legible.
614
+ decl('space-app-shell-inset', `${space.step.appShellInset}px`),
615
+ decl('space-app-shell-inset-mobile', `${space.step.appShellInsetMobile}px`),
616
+ // The header's breadcrumb floor — emitted for the same reason as the gutter pair above it and
617
+ // not for the JS-number reason its `appShell*` neighbours below are withheld for: this one has
618
+ // a real CSS consumer (`shell/app-header.module.css`'s `.lead`), which is the whole test this
619
+ // list applies. Plain px, like every other layout dimension here.
620
+ decl('space-app-shell-header-lead-min', `${space.step.appShellHeaderLeadMin}px`),
621
+ decl('space-mobile-nav-tab-gap', `${space.step.mobileNavTabGap}px`),
622
+ decl('space-mobile-nav-icon-size', `${space.step.mobileNavIconSize}px`),
623
+ decl('space-mobile-nav-tab-inset-y', `${space.step.mobileNavTabInsetY}px`),
624
+ decl('space-mobile-nav-tab-inset-x', `${space.step.mobileNavTabInsetX}px`),
625
+ decl('space-mobile-nav-row-height', `${space.step.mobileNavRowHeight}px`),
626
+ decl('space-agent-rail-inset-x', `${space.step.agentRailInsetX}px`),
627
+ decl('space-agent-part-gap-top', `${space.step.agentPartGapTop}px`),
628
+ decl('space-agent-code-inset', `${space.step.agentCodeInset}px`),
629
+ decl('space-agent-error-inset-y', `${space.step.agentErrorInsetY}px`),
630
+ decl('space-agent-error-inset-x', `${space.step.agentErrorInsetX}px`),
631
+ decl('space-agent-scroll-button-gap-top', `${space.step.agentScrollButtonGapTop}px`),
632
+ decl('space-agent-message-inset-y', `${space.step.agentMessageInsetY}px`),
633
+ decl('space-agent-message-inset-x', `${space.step.agentMessageInsetX}px`),
634
+ decl('space-agent-transcript-inset', `${space.step.agentTranscriptInset}px`),
635
+ decl('space-badge-inset-y', `${space.step.badgeInsetY}px`),
636
+ decl('space-badge-inset-x', `${space.step.badgeInsetX}px`),
637
+ decl('space-stat-card-gap', `${space.step.statCardGap}px`),
638
+ // The card/section inset ladder — the three values `spaceMobileDecls` below re-points at `< sm`
639
+ // (see its doc for where the law lives and why it lives there).
640
+ decl('space-card-inset-y', `${space.step.cardInsetY}px`),
641
+ decl('space-card-inset-x', `${space.step.cardInsetX}px`),
642
+ decl('space-section-gap', `${space.step.sectionGap}px`),
643
+ decl('space-virtual-row-inset-y', `${space.step.virtualRowInsetY}px`),
644
+ decl('space-virtual-row-inset-x', `${space.step.virtualRowInsetX}px`),
645
+ // Controls tier row/gap heights (`docs/CONTROLS-SPEC.md` §5) — the current commit's consumers
646
+ // are `theme/ctl-theme.tsx` (`--vx-space-control-gap`) and the wave-3/4 homes (`PageBar`,
647
+ // `WidgetHeader`, sidebar blocks, the mobile `Filters (n)` sheet) landing next.
648
+ decl('space-page-bar-row-height', pxRem(space.step.pageBarRowHeight)),
649
+ decl('space-section-header-height', pxRem(space.step.sectionHeaderHeight)),
650
+ decl('space-widget-header-height', pxRem(space.step.widgetHeaderHeight)),
651
+ decl('space-sidebar-block-row-height', pxRem(space.step.sidebarBlockRowHeight)),
652
+ decl('space-control-gap', `${space.step.controlGap}px`),
653
+ decl('space-sheet-row-height', pxRem(space.step.sheetRowHeight)),
654
+ // `chartLegendGap`/`chartMarginTop`/`chartMarginRight`/`chartMarginBottom`/`chartMarginLeft`/
655
+ // `chartDotR`/`progressBarSize`/`timelineBullet` are DELIBERATELY absent — see this function's
656
+ // doc for why (JS-number-only consumers, zero `var()` reads). `appShellHeaderHeight`/
657
+ // `appShellNavbarWidth`/`appShellNavbarRailWidth`/`appShellAsideWidth`/`appShellAsideRailWidth`
658
+ // join that list — but NOT `appShellInset`/`appShellInsetMobile`/`appShellHeaderLeadMin`, which
659
+ // are emitted above and carry their own reason for the exception —
660
+ // `shell/index.tsx` reads all five as JS numbers via `useBasaltSpacing()` (Mantine's `AppShell`
661
+ // `header`/`navbar`/`aside` props take numbers, not `var()` strings), so a `--vx-space-app-shell-*`
662
+ // decl here would have had zero consumers, framework-wide, the same dead-weight shape this function's
663
+ // doc already calls out for the chart/Progress/Timeline group. `sidebarAccountMenuWidth`/
664
+ // `sidebarSettingsMenuWidth` join it too — `app-sidebar-account.tsx`/`app-sidebar.tsx` read both
665
+ // as JS numbers via `useBasaltSpacing()` (Mantine's `<Menu width={…}>` also takes a number).
666
+ ]
667
+ }
668
+
669
+ /**
670
+ * The `--vx-space-*` names that survive `only: 'core'` — DERIVED from the `SPACE` anchor keys, never
671
+ * hand-listed, so a new `SPACE_STEP` one-off is excluded the moment it is added and a renamed anchor
672
+ * follows automatically. `SPACE` is the generic ladder (the stack rhythm, the two control heights,
673
+ * the row insets); everything else `spaceDecls` emits is named for a basalt COMPONENT and is dead
674
+ * weight outside this framework — including `--vx-space-row-line-height`, which is the NavLink row's
675
+ * line-height rather than a general anchor and so is (deliberately) in neither set.
676
+ */
677
+ const CORE_SPACE_VARS = new Set(
678
+ Object.keys(SPACE).map((key) => `space-${key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)}`),
679
+ )
680
+
681
+ /**
682
+ * Does this `spaceDecls` line declare a core anchor? The name is read back out of the emitted line
683
+ * because {@link decl} is the single formatter for every one of them — the shape is fixed at one
684
+ * call site, so parsing it cannot drift, whereas tagging 104 declarations by hand could.
685
+ */
686
+ function isCoreSpaceDecl(line: string): boolean {
687
+ const name = /^ {2}--vx-([\w-]+):/.exec(line)?.[1]
688
+ return name !== undefined && CORE_SPACE_VARS.has(name)
689
+ }
690
+
691
+ /** The shipped, level-0 spacing values — `frameworkDerived`'s `spaceDecls(...)` call below. No
692
+ * `createBasaltTheme({ density })` retuning applies here; that path calls `buildDensityCss` with a
693
+ * different resolved {@link SpaceValues} instead (see `../theme` and `../provider`). */
694
+ const DEFAULT_SPACE_VALUES: SpaceValues = {
695
+ anchors: SPACE,
696
+ scale: SPACE_SCALE,
697
+ step: SPACE_STEP,
698
+ rowLineHeight: ROW_LINE_HEIGHT,
699
+ }
700
+
701
+ /**
702
+ * The 16 deprecated camelCase aliases for `frameworkDerived`'s theme-independent (`:root`)
703
+ * canonicals — the 12 `fill-hover-<family>` fills plus `good-soft`/`good-ref`/`bad-ref`/`warn-ref`.
704
+ * Each is a `var(--vx-<canonical-kebab-name>)` ref so it can never drift. Emitted as its own
705
+ * commented, trailing group — gated by {@link BuildPaletteOpts.legacyAliases}, default on.
706
+ */
707
+ function frameworkDerivedAliases(data: PaletteData): string {
708
+ return [
709
+ ' /* Deprecated camelCase aliases — removed in a future minor; migrate to the kebab names above. */',
710
+ ...Object.keys(data.FILL).map((name) =>
711
+ decl(`fillHover-${name}`, `var(--vx-fill-hover-${name})`),
712
+ ),
713
+ decl('goodSoft', 'var(--vx-good-soft)'),
714
+ decl('goodRef', 'var(--vx-good-ref)'),
715
+ decl('badRef', 'var(--vx-bad-ref)'),
716
+ decl('warnRef', 'var(--vx-warn-ref)'),
717
+ ].join('\n')
718
+ }
719
+
720
+ /**
721
+ * Theme-independent scalars + semantic fills, defined once on `:root`.
722
+ *
723
+ * Area-gradient strength is a global knob (the theme lab overrides these two on `:root`
724
+ * to retune every line-area fill live). 0%/0% disables gradients app-wide.
725
+ */
726
+ function frameworkDerived(data: PaletteData, only: 'core' | 'all', legacyAliases: boolean): string {
727
+ const space = spaceDecls(DEFAULT_SPACE_VALUES)
728
+ const canonical = [
729
+ decl('radius-card', `${RADIUS.card}px`),
730
+ decl('radius-ctrl', `${RADIUS.ctrl}px`),
731
+ // The two offset tiers (SegmentedControl indicator/Kbd/Code, Progress/scale-sm) — added so a
732
+ // component's inline `styles`/`style` can read the SAME source as `RADIUS_STEP.tight`/`.fine`
733
+ // instead of a hand-typed literal (see `theme/index.ts`'s component sweep).
734
+ decl('radius-tight', `${RADIUS_STEP.tight}px`),
735
+ decl('radius-fine', `${RADIUS_STEP.fine}px`),
736
+ // The floating tier (Modal/Tooltip/Popover/Notification, content code/mermaid blocks).
737
+ decl('radius-floating', `${RADIUS_STEP.floating}px`),
738
+ // The pill affordance (scroll-to-bottom button, chips) — fixed, level-invariant, so it lives
739
+ // only in this static block, never in `buildRadiusCss`'s dynamic override (see
740
+ // `RADIUS_STEP.pill`'s doc).
741
+ decl('radius-pill', `${RADIUS_STEP.pill}px`),
742
+ // The pointer tier's fine default — density-EXEMPT, so it lives only in this static block
743
+ // (same law as `radius-pill` above). `coarseHitBlock` re-points it to 44px.
744
+ decl('hit', `${HIT_FINE}px`),
745
+ ...(only === 'core' ? space.filter(isCoreSpaceDecl) : space),
746
+ // Article-density Prose measure (docs/CONTENT-SPEC.md §5) — theme-independent, like the radii.
747
+ decl('prose-measure', '72ch'),
748
+ // The fill band (see FILL in palette.ts). Emitted on `:root`, NOT per scheme — a filled surface
749
+ // is the same hex in both, which is the whole point: it is squeezed between its white label and
750
+ // the page behind it, and only one luminance band satisfies both on both pages.
751
+ ...Object.entries(data.FILL).map(([name, hex]) => decl(`fill-${name}`, hex)),
752
+ // Hover is DERIVED from the fill, so retuning a fill (in the theme lab, or here) carries its
753
+ // hover along instead of leaving a stale pair. 88% of the fill over black lands ~0.128 luminance:
754
+ // a visible press-darkening that still clears AA for the white label (~5.9:1).
755
+ ...Object.keys(data.FILL).map((name) =>
756
+ decl(`fill-hover-${name}`, `color-mix(in srgb, var(--vx-fill-${name}) 88%, #000)`),
757
+ ),
758
+ // The type scale, emitted from the SAME `TEXT` object `VX.text` reads — CSS modules and the
759
+ // Mantine theme consume these; inline styles / visx read the numbers. One ladder, two forms.
760
+ ...Object.entries(TEXT).map(([step, px]) => decl(`text-${step}`, `${px}px`)),
761
+ decl('area-top', '22%'),
762
+ decl('area-bottom', '1%'),
763
+ decl('good', 'color-mix(in srgb, var(--vx-good-solid) 18%, transparent)'),
764
+ decl('good-soft', 'color-mix(in srgb, var(--vx-good-solid) 8%, transparent)'),
765
+ decl('bad', 'color-mix(in srgb, var(--vx-bad-solid) 18%, transparent)'),
766
+ decl('warn', 'color-mix(in srgb, var(--vx-warn-solid) 8%, transparent)'),
767
+ decl('good-ref', 'color-mix(in srgb, var(--vx-good-solid) 30%, transparent)'),
768
+ decl('bad-ref', 'color-mix(in srgb, var(--vx-bad-solid) 30%, transparent)'),
769
+ decl('warn-ref', 'color-mix(in srgb, var(--vx-warn-solid) 20%, transparent)'),
770
+ ].join('\n')
771
+ return legacyAliases ? `${canonical}\n${frameworkDerivedAliases(data)}` : canonical
772
+ }
773
+
774
+ /**
775
+ * Throw on a consumer series/group name inside a basalt-owned `--vx-*` family (`surface`, `space`,
776
+ * `ink`, …): `check-theme`'s `unknown-vx-token` judges every name there against what basalt emits,
777
+ * which is sound only while no consumer REF points there. Dev builds only (the call sits behind a
778
+ * literal NODE_ENV read); `unknown-vx-token` left the grace ledger at 1.36.0, which is when this
779
+ * went from a once-per-name warning to a throw. The families are the guard's own generated
780
+ * `BASALT_VX_FAMILIES`. Checked on the refs, not in `buildPaletteCss`, which runs on every first
781
+ * paint.
782
+ */
783
+ function assertConsumerVxName(name: string): void {
784
+ const family = vxFamilyOf(name)
785
+ if (!BASALT_VX_FAMILIES.includes(family)) return
786
+ throw new Error(
787
+ `[basalt] ${name} is inside basalt's own --vx-${family} family — name your series group something basalt does not own (groupTokens('app', …)).`,
788
+ )
789
+ }
790
+
791
+ /**
792
+ * Wrap a map of `{ name: ColorPair }` into `var(--vx-<prefix><name>)` token refs — the runtime
793
+ * companion to `buildPaletteCss`. Lets a consumer define its own series and read stable refs.
794
+ *
795
+ * Returns an exact-keyed object: the return type mirrors the keys of the input map, so a renamed
796
+ * or removed key becomes a tsc error rather than silently widening to `Record<string, string>`.
797
+ */
798
+ export function seriesTokens<const T extends SeriesMap>(
799
+ map: T,
800
+ prefix = '',
801
+ ): { [K in keyof T]: string } {
802
+ const out = {} as { [K in keyof T]: string }
803
+ for (const key of Object.keys(map) as (keyof T)[]) {
804
+ // Literal read at the branch, so a production bundle drops the check (common/dev-gate.test.ts).
805
+ if (process.env.NODE_ENV !== 'production') assertConsumerVxName(`--vx-${prefix}${String(key)}`)
806
+ out[key] = `var(--vx-${prefix}${String(key)})`
807
+ }
808
+ return out
809
+ }
810
+
811
+ /**
812
+ * Define a named series map. Identity passthrough — gives consumers a typed authoring entry
813
+ * point and preserves the exact literal keys of the input (const-generic so callers get back T,
814
+ * not the widened SeriesMap, enabling downstream exact-key checks in groupTokens / seriesTokens).
815
+ */
816
+ export function defineSeries<const T extends SeriesMap>(map: T): T {
817
+ return map
818
+ }
819
+
820
+ /** Runtime key of the {@link SeriesGroup} brand. */
821
+ const SERIES_GROUP: unique symbol = Symbol('basalt.series-group')
822
+
823
+ /**
824
+ * The brand `groupTokens` puts on its return value. `BuildPaletteOpts.groups` takes these, so the
825
+ * `${name}-` key that emits the variables and the one that references them come from one call and
826
+ * cannot be spelled two ways.
827
+ */
828
+ export type SeriesGroup = {
829
+ readonly [SERIES_GROUP]: { readonly prefix: string; readonly map: SeriesMap }
830
+ }
831
+
832
+ /**
833
+ * Build a single namespaced group's token refs (`var(--vx-<name>-<key>)`).
834
+ *
835
+ * Returns an exact-keyed object matching the keys of `map` — a stale or renamed key is a tsc
836
+ * error at the call site rather than a silent `Record<string, string>` widening.
837
+ */
838
+ export function groupTokens<const T extends SeriesMap>(
839
+ name: string,
840
+ map: T,
841
+ ): { [K in keyof T]: string } & SeriesGroup {
842
+ const refs = seriesTokens(map, `${name}-`)
843
+ // Non-enumerable, symbol-keyed: a series key can never collide with the brand, and
844
+ // `Object.keys(refs)` still lists exactly the series.
845
+ Object.defineProperty(refs, SERIES_GROUP, { value: { prefix: `${name}-`, map } })
846
+ return refs as { [K in keyof T]: string } & SeriesGroup
847
+ }
848
+
849
+ /**
850
+ * The attribute + values the per-scheme blocks key off. Defaults reproduce Mantine's own toggle
851
+ * (`data-mantine-color-scheme` = `dark` / `light`), so a consumer only names the axis it differs
852
+ * on. Supplying this object at all switches the emitter onto the framework-neutral selector shape
853
+ * — see {@link BuildPaletteOpts.scheme}.
854
+ */
855
+ export type ColorSchemeSelector = {
856
+ attribute?: string
857
+ darkValue?: string
858
+ lightValue?: string
859
+ }
860
+
861
+ /** Options for {@link buildPaletteCss}. */
862
+ export type BuildPaletteOpts = {
863
+ /** Extra named series maps to emit under the given prefixes (each one is the return value of `groupTokens`). */
864
+ groups?: readonly SeriesGroup[]
865
+ /** Theme-independent derived declarations (gradients, semantic fills) emitted once on `:root`. */
866
+ derived?: string[]
867
+ /**
868
+ * Attribute + values the per-scheme blocks key off — for a consumer whose scheme toggle is not
869
+ * Mantine's (a static site on `data-theme`, say). Default: Mantine's own
870
+ * `data-mantine-color-scheme` = `dark` / `light`.
871
+ *
872
+ * Setting ANY of `scheme` / `defaultScheme` / `mediaFallback` moves the emitter off the legacy
873
+ * `html[…]` selector onto `:root[…]`. That is deliberate and load-bearing, NOT cosmetic:
874
+ *
875
+ * `:root[data-theme='dark']` 0-2-0 ← what this path emits
876
+ * `html[data-theme='dark']` 0-1-1
877
+ * `[data-theme='dark']` 0-1-0 ← same as a bare `:root`, so source order decides
878
+ *
879
+ * A light-default site whose own `:root` block happens to come last would beat the third form
880
+ * outright — dark mode silently does nothing. `:root[…]` sits above both a bare `:root` and
881
+ * `html[…]`, and carries no assumption about the element the attribute lands on.
882
+ *
883
+ * The no-options path keeps emitting `html[data-mantine-color-scheme='…']` verbatim, because a
884
+ * basalt consumer may already override `--vx-*` under that exact selector — raising basalt's own
885
+ * specificity would silently win over their override.
886
+ */
887
+ scheme?: ColorSchemeSelector
888
+ /**
889
+ * Which scheme rides along on the bare `:root` block — the one that applies when the attribute
890
+ * is absent. Default: `'dark'` (the framework's own default scheme).
891
+ *
892
+ * `'none'` emits no bare-`:root` primitives at all: each scheme gets only its attribute block,
893
+ * and an unattributed document falls back to the theme-independent scalars alone. Pair it with
894
+ * `mediaFallback` unless the consumer always sets the attribute.
895
+ */
896
+ defaultScheme?: 'dark' | 'light' | 'none'
897
+ /**
898
+ * Also emit an `@media (prefers-color-scheme: …)` block for every scheme that is not the bare
899
+ * `:root` default, so a document with no attribute set follows the OS. Default: `false`.
900
+ *
901
+ * The block is a bare `:root` (0-1-0), so an explicit attribute block (0-2-0) always wins over
902
+ * it regardless of source order — the OS preference is a fallback, never an override.
903
+ */
904
+ mediaFallback?: boolean
905
+ /**
906
+ * How much of the SPACING set to emit. Default: `'all'` (every one of the 108 `--vx-space-*`
907
+ * declarations the framework's own components read).
908
+ *
909
+ * `'core'` keeps the 9 generic anchors — the stack rhythm, the two control heights, the row
910
+ * insets — and drops the 99 named for a specific basalt component
911
+ * (`--vx-space-agent-transcript-inset`, `--vx-space-toc-sub-indent`, …), taking the emitted set
912
+ * from 204 variables to 105. Nothing outside basalt's own React components reads those, so a
913
+ * framework-free consumer is carrying them for nothing.
914
+ *
915
+ * This is a SPACING filter only: color, radius, shadow, type and status variables are identical
916
+ * in both modes. The partition is derived from the `SPACE` key set rather than a maintained list
917
+ * — see {@link CORE_SPACE_VARS}.
918
+ */
919
+ only?: 'core' | 'all'
920
+ /**
921
+ * Whether to also emit the 32 deprecated camelCase aliases (`--vx-accentFill`, `--vx-tooltipBg`,
922
+ * `--vx-fillHover-gray`, …) alongside the canonical kebab-case names the 1.4.0 normalization
923
+ * introduced. Default: `true`, so an existing consumer's hand-written CSS referencing a
924
+ * camelCase name keeps resolving. Each alias is defined as `var(--vx-<canonical>)` — it can
925
+ * never drift from the value it aliases. Pass `false` once every reference in your own code has
926
+ * migrated to the kebab names to drop the extra 32 lines.
927
+ */
928
+ legacyAliases?: boolean
929
+ }
930
+
931
+ /** Mantine's own color-scheme attribute — the default {@link ColorSchemeSelector.attribute}. */
932
+ const MANTINE_SCHEME_ATTRIBUTE = 'data-mantine-color-scheme'
933
+
934
+ /** Indent an already-2-space-indented declaration block one level deeper (for `@media` nesting). */
935
+ const indent = (block: string): string =>
936
+ block
937
+ .split('\n')
938
+ .map((line) => (line === '' ? line : ` ${line}`))
939
+ .join('\n')
940
+
941
+ /**
942
+ * The `< sm` breakpoint, written the way a stylesheet has to write it — the SAME literal every
943
+ * basalt CSS module spells (`shell/app-header.module.css`, `dashboard/section.module.css`, …), and
944
+ * for the same reason `BREAKPOINTS` above exists: a `@media` condition is resolved before custom
945
+ * properties exist, so the number cannot be a `var()`. Held here as a constant rather than inlined
946
+ * so the emitter and the modules can be grepped as one set.
947
+ */
948
+ const MOBILE_MEDIA = `@media (max-width: ${sizeClassMaxEm(SIZE_CLASSES.medium)})`
949
+
950
+ const coarseHitBlock = `@media (pointer: coarse) {\n :root {\n --vx-hit: ${HIT_COARSE}px;\n }\n}`
951
+
952
+ /**
953
+ * The MOBILE INSET LADDER, and this function is its ONE home — framework-wide, not per module.
954
+ *
955
+ * The problem it answers: the desktop-shell pass took the page gutter from 20 to 8 below `sm`
956
+ * (`appShellInsetMobile`), and the card ladder INSIDE that gutter never moved — card insets stayed
957
+ * `xs`/`sm` and a `Section`'s gaps stayed `stackMd` at every width — so a 390px phone still read
958
+ * "content spacing too big" after the gutter fix. Two rungs close it: the card's horizontal inset
959
+ * steps `sm` -> `xs`, and the Section's gap steps `stackMd` -> `stackSm`.
960
+ *
961
+ * Why it lives HERE and not in the three CSS files that consume it: two of those three sites are
962
+ * INLINE styles in TSX (`dashboard/stat-card.tsx`, `charts/primitives/ChartCard.tsx`), and an
963
+ * inline style cannot carry a media query at all. The only surface that reaches all three is a
964
+ * custom property whose VALUE moves at the breakpoint, and the only stylesheet guaranteed present
965
+ * wherever those components render is this one — `BasaltProvider` injects `buildPaletteCss()`, and
966
+ * a component's own CSS module is loaded only if a bundler kept that module. So the law is one
967
+ * `@media` block emitted beside the base declarations it overrides, and every consuming site is a
968
+ * plain `var()` read with no breakpoint knowledge. A media query copied into three files (or into
969
+ * one component stylesheet reaching for `:global(:root)`) was the alternative, and it fails the
970
+ * same way twice: three copies drift, and one copy in a tree-shakeable module is a law that
971
+ * sometimes does not ship.
972
+ *
973
+ * This does NOT make the token layer responsive in general. It stays what `BREAKPOINTS`'s doc says
974
+ * it is — the layer that owns the NUMBER a media query has to write — and this is the one law whose
975
+ * consuming sites cannot write that query themselves. A dimension whose consumer is a CSS module
976
+ * keeps making the choice in that module (`appShellInset`/`appShellInsetMobile` still do).
977
+ */
978
+ function spaceMobileDecls(space: SpaceValues): string[] {
979
+ return [
980
+ decl('space-card-inset-x', `${space.scale.xs}px`),
981
+ decl('space-section-gap', `${space.anchors.stackSm}px`),
982
+ ]
983
+ }
984
+
985
+ /** {@link spaceMobileDecls} as the `@media` block that overrides the base `:root` declarations —
986
+ * same specificity, later in source order, so it wins below `sm` and nowhere else. */
987
+ function mobileLadderBlock(space: SpaceValues): string {
988
+ return `${MOBILE_MEDIA} {\n :root {\n${indent(spaceMobileDecls(space).join('\n'))}\n }\n}`
989
+ }
990
+
991
+ /**
992
+ * Emit the `--vx-*` stylesheet: a `:root` block of theme-independent scalars, then the
993
+ * dark (default) and light primitive blocks under `html[data-mantine-color-scheme='…']`.
994
+ *
995
+ * The framework chrome (STATUS/SEMANTIC/SURFACE pairs + the framework DERIVED defaults) is
996
+ * built in, so an empty `buildPaletteCss()` already emits every framework var. Consumer
997
+ * `opts.groups` / `opts.derived` append on top (e.g. argo's domain SERIES/ACTIVITY/USAGE).
998
+ *
999
+ * `data` defaults to the static, shipped palette (`buildPaletteData()` at `DEFAULT_DERIVE_CONFIG`)
1000
+ * — pass the result of `buildPaletteData(config)` to emit CSS for a retuned derive config instead
1001
+ * (this is how `BasaltProvider` follows `createBasaltTheme`'s `{ derive }` option; see `../theme`
1002
+ * and `../provider`).
1003
+ *
1004
+ * Dark is the default (`:root`) since the framework defaults to dark; the
1005
+ * `[data-mantine-color-scheme]` selectors track Mantine's toggle on `<html>`.
1006
+ *
1007
+ * A non-Mantine consumer retargets all three of those choices — the attribute, which scheme rides
1008
+ * the bare `:root`, and whether the OS preference is honoured — via `opts.scheme` /
1009
+ * `opts.defaultScheme` / `opts.mediaFallback`. Setting any of them also moves the per-scheme
1010
+ * selector from `html[…]` to the framework-neutral, higher-specificity `:root[…]`; see
1011
+ * `BuildPaletteOpts.scheme` for why that is not cosmetic. With none of them set the output is
1012
+ * byte-identical to what the framework has always emitted (pinned by
1013
+ * `tests/palette-css.test.ts`).
1014
+ */
1015
+ export function buildPaletteCss(
1016
+ opts: BuildPaletteOpts = {},
1017
+ data: PaletteData = buildPaletteData(),
1018
+ ): string {
1019
+ const groups = opts.groups ?? []
1020
+ const legacyAliases = opts.legacyAliases ?? true
1021
+ const only = opts.only ?? 'all'
1022
+ const extraDerived = (opts.derived ?? []).map((d) => ` ${d}`).join('\n')
1023
+ const derivedBlock = frameworkDerived(data, only, legacyAliases)
1024
+ const derived = extraDerived ? `${derivedBlock}\n${extraDerived}` : derivedBlock
1025
+ const side = (s: Side): string => {
1026
+ const extra = groups
1027
+ .map((group) => groupSide(group[SERIES_GROUP].prefix, group[SERIES_GROUP].map, s))
1028
+ .join('\n')
1029
+ const framework = frameworkPrimitives(s, data, legacyAliases)
1030
+ return extra ? `${framework}\n${extra}` : framework
1031
+ }
1032
+ // The scalars, then the ONE `@media` block that re-points two of them below `sm` (see
1033
+ // `spaceMobileDecls`). Dropped under `only: 'core'` for the same reason the declarations it
1034
+ // overrides are: both vars are component-named one-offs, not `SPACE` anchors, so a core-only
1035
+ // consumer never receives the base pair the block exists to override.
1036
+ const rootBlock =
1037
+ only === 'core'
1038
+ ? `:root {\n${derived}\n}\n${coarseHitBlock}`
1039
+ : `:root {\n${derived}\n}\n${mobileLadderBlock(DEFAULT_SPACE_VALUES)}\n${coarseHitBlock}`
1040
+
1041
+ // The legacy shape — kept verbatim, and only reachable when the caller asked for nothing.
1042
+ if (opts.scheme === undefined && opts.defaultScheme === undefined && !opts.mediaFallback) {
1043
+ return `${rootBlock}
1044
+ :root,
1045
+ html[data-mantine-color-scheme='dark'] {
1046
+ ${side('dark')}
1047
+ }
1048
+ html[data-mantine-color-scheme='light'] {
1049
+ ${side('light')}
1050
+ }`
1051
+ }
1052
+
1053
+ const attribute = opts.scheme?.attribute ?? MANTINE_SCHEME_ATTRIBUTE
1054
+ const value: Record<Side, string> = {
1055
+ dark: opts.scheme?.darkValue ?? 'dark',
1056
+ light: opts.scheme?.lightValue ?? 'light',
1057
+ }
1058
+ const defaultScheme = opts.defaultScheme ?? 'dark'
1059
+ // Default scheme first, so its attribute block can absorb the bare `:root` instead of repeating
1060
+ // ~40 declarations; `'none'` keeps the framework's dark-then-light order.
1061
+ const order: Side[] = defaultScheme === 'light' ? ['light', 'dark'] : ['dark', 'light']
1062
+
1063
+ const blocks = [rootBlock]
1064
+ for (const s of order) {
1065
+ const selector =
1066
+ s === defaultScheme
1067
+ ? `:root,\n:root[${attribute}='${value[s]}']`
1068
+ : `:root[${attribute}='${value[s]}']`
1069
+ // The OS fallback for a scheme goes immediately before that scheme's own attribute block. It is
1070
+ // a bare `:root` (0-1-0), so the attribute block (0-2-0) outranks it on specificity rather than
1071
+ // on order — an explicit toggle always beats the OS preference, and the OS preference always
1072
+ // beats the hardcoded default that a bare `:root` above it carries.
1073
+ if (opts.mediaFallback === true && s !== defaultScheme) {
1074
+ blocks.push(`@media (prefers-color-scheme: ${s}) {\n :root {\n${indent(side(s))}\n }\n}`)
1075
+ }
1076
+ blocks.push(`${selector} {\n${side(s)}\n}`)
1077
+ }
1078
+ return blocks.join('\n')
1079
+ }
1080
+
1081
+ /**
1082
+ * The `fonts` option's resolved shape — a full CSS font-family stack string per slot (with its own
1083
+ * fallback chain), riding the `--basalt-font-sans/head/mono` override seam `styles.css` ships
1084
+ * fallbacks for. Omitted keys keep the shipped fallback untouched. Lives here (not in `../theme`,
1085
+ * which only re-exports the type) because its builder `buildFontsCss` below is a pure, Mantine-free
1086
+ * string builder that belongs in the tokens layer — see `../theme`'s
1087
+ * `CreateBasaltThemeOptions.fonts` for the production entry point that sets it.
1088
+ */
1089
+ export type BasaltFontsConfig = {
1090
+ sans?: string
1091
+ head?: string
1092
+ mono?: string
1093
+ }
1094
+
1095
+ /** `{ basaltFonts key → --basalt-font-* var name }` — the one place the mapping is spelled out. */
1096
+ const FONT_VAR_NAMES = {
1097
+ sans: '--basalt-font-sans',
1098
+ head: '--basalt-font-head',
1099
+ mono: '--basalt-font-mono',
1100
+ } as const satisfies Record<keyof BasaltFontsConfig, string>
1101
+
1102
+ /**
1103
+ * Build the `:root` block of `--basalt-font-*` declarations for a resolved `basaltFonts` config —
1104
+ * only for the keys the consumer actually set (`createBasaltTheme(overrides, { fonts })`); an
1105
+ * omitted key keeps the shipped `styles.css` fallback chain untouched. Empty string when `fonts` is
1106
+ * absent or every key is omitted, so callers can unconditionally concatenate the result. Internal
1107
+ * (not on the `basalt-ui/tokens` barrel) — `BasaltProvider`'s bridge (`../provider`) calls this
1108
+ * directly to append declarations to its injected `<style>`.
1109
+ */
1110
+ export function buildFontsCss(fonts: BasaltFontsConfig | undefined): string {
1111
+ if (!fonts) return ''
1112
+ const decls = (Object.keys(FONT_VAR_NAMES) as (keyof BasaltFontsConfig)[])
1113
+ .filter((key) => fonts[key] !== undefined)
1114
+ .map((key) => {
1115
+ const value = fonts[key] as string
1116
+ // A font stack is a plain declaration value — `{`/`}`/`;`/comment openers would let a
1117
+ // config string break out of the emitted `<style>` declaration. Throw, don't sanitize,
1118
+ // per the repo's validation convention (see `deriveTokens`'s accent-hex assert).
1119
+ if (/[{};]|\/\*/.test(value)) {
1120
+ throw new Error(
1121
+ `Invalid fonts.${key}: a font-family stack must not contain "{", "}", ";" or "/*" — got ${JSON.stringify(value)}`,
1122
+ )
1123
+ }
1124
+ return ` ${FONT_VAR_NAMES[key]}: ${value};`
1125
+ })
1126
+ if (decls.length === 0) return ''
1127
+ return `:root {\n${decls.join('\n')}\n}`
1128
+ }
1129
+
1130
+ /**
1131
+ * Build the `:root` block of overriding `--vx-radius-*` declarations for a resolved
1132
+ * `basaltRadius` value (`createBasaltTheme(overrides, { radius })`) — analog of {@link buildFontsCss}.
1133
+ * Empty string when `radius` is absent (the default level-0 path never sets `theme.other.basaltRadius`
1134
+ * in the first place), so callers can unconditionally concatenate the result. Internal, like
1135
+ * `buildFontsCss` above.
1136
+ */
1137
+ export function buildRadiusCss(radius: RadiusValues | undefined): string {
1138
+ if (!radius) return ''
1139
+ return `:root {
1140
+ --vx-radius-card: ${radius.card}px;
1141
+ --vx-radius-ctrl: ${radius.ctrl}px;
1142
+ --vx-radius-tight: ${radius.tight}px;
1143
+ --vx-radius-fine: ${radius.fine}px;
1144
+ --vx-radius-floating: ${radius.floating}px;
1145
+ }`
1146
+ }
1147
+
1148
+ /**
1149
+ * Build the `:root` block of overriding `--vx-space-*` declarations for a resolved `basaltDensity`
1150
+ * value (`createBasaltTheme(overrides, { density })`) — analog of {@link buildRadiusCss}, reusing
1151
+ * the SAME `spaceDecls` list `frameworkDerived` emits the shipped defaults from (so the two can
1152
+ * never drift). Empty string when `space` is absent (the default level-0 path never sets
1153
+ * `theme.other.basaltDensity` in the first place), so callers can unconditionally concatenate the
1154
+ * result. Importable from `basalt-ui/tokens`, same SSR/custom-injection use as `buildRadiusCss`
1155
+ * above.
1156
+ */
1157
+ export function buildDensityCss(space: SpaceValues | undefined): string {
1158
+ if (!space) return ''
1159
+ // The `< sm` ladder rides along, at the retuned level — without it a phone at `density: 2` would
1160
+ // take the level-0 mobile inset from the static stylesheet and the level-2 base from this one,
1161
+ // which is the only way the two rungs of one ladder could ever come from different levels.
1162
+ return `:root {\n${spaceDecls(space).join('\n')}\n}\n${mobileLadderBlock(space)}`
1163
+ }