basalt-ui 1.25.0 → 1.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (462) hide show
  1. package/AGENTS.md +8 -6
  2. package/CHANGELOG.md +18 -0
  3. package/MIGRATING.md +546 -20
  4. package/README.md +45 -32
  5. package/agent/rules/basalt-batteries.md +205 -0
  6. package/agent/rules/basalt-charts.md +125 -555
  7. package/agent/rules/basalt-controls.md +155 -0
  8. package/agent/rules/basalt-mantine.md +146 -439
  9. package/agent/rules/basalt-state.md +113 -96
  10. package/agent/rules/basalt-tokens.md +126 -288
  11. package/agent/skills/basalt-app/SKILL.md +70 -195
  12. package/agent/skills/basalt-charts/SKILL.md +54 -234
  13. package/agent/skills/basalt-design/SKILL.md +56 -248
  14. package/agent/templates/CLAUDE-block.md.tpl +31 -40
  15. package/agent/templates/DESIGN.md.tpl +22 -51
  16. package/configs/oxlint-plugin.js +1275 -31
  17. package/configs/oxlint-plugin.test.ts +947 -11
  18. package/configs/oxlint.json +11 -3
  19. package/dist/charts/cursor/resolve.d.ts +18 -0
  20. package/dist/charts/cursor/resolve.d.ts.map +1 -1
  21. package/dist/charts/cursor/resolve.js +15 -6
  22. package/dist/charts/cursor/scope.d.ts +6 -3
  23. package/dist/charts/cursor/scope.d.ts.map +1 -1
  24. package/dist/charts/cursor/store.d.ts +5 -1
  25. package/dist/charts/cursor/store.d.ts.map +1 -1
  26. package/dist/charts/cursor/store.js +4 -4
  27. package/dist/charts/hooks/useChartCursor.d.ts.map +1 -1
  28. package/dist/charts/hooks/useChartCursor.js +9 -8
  29. package/dist/charts/index.d.ts +2 -2
  30. package/dist/charts/index.d.ts.map +1 -1
  31. package/dist/charts/primitives/ChartCard.d.ts +34 -9
  32. package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
  33. package/dist/charts/primitives/ChartCard.js +29 -114
  34. package/dist/charts/primitives/ChartCard.test.d.ts +2 -0
  35. package/dist/charts/primitives/ChartCard.test.d.ts.map +1 -0
  36. package/dist/charts/sparklines/BarSparkline.d.ts +26 -2
  37. package/dist/charts/sparklines/BarSparkline.d.ts.map +1 -1
  38. package/dist/charts/sparklines/BarSparkline.js +18 -4
  39. package/dist/cli/check-coverage.test.d.ts +2 -0
  40. package/dist/cli/check-coverage.test.d.ts.map +1 -0
  41. package/dist/cli/index.d.ts +67 -4
  42. package/dist/cli/index.d.ts.map +1 -1
  43. package/dist/cli/index.js +225 -7
  44. package/dist/connectivity/connectivity-indicator.d.ts +4 -1
  45. package/dist/connectivity/connectivity-indicator.d.ts.map +1 -1
  46. package/dist/connectivity/connectivity-indicator.js +1 -1
  47. package/dist/content/index.d.ts +5 -3
  48. package/dist/content/index.d.ts.map +1 -1
  49. package/dist/content/index.js +4 -2
  50. package/dist/content/prose.module.css +7 -28
  51. package/dist/controls/actions.d.ts +169 -0
  52. package/dist/controls/actions.d.ts.map +1 -0
  53. package/dist/controls/actions.js +287 -0
  54. package/dist/controls/actions.module.css +62 -0
  55. package/dist/controls/actions.module.css.d.ts +13 -0
  56. package/dist/controls/actions.test.d.ts +2 -0
  57. package/dist/controls/actions.test.d.ts.map +1 -0
  58. package/dist/controls/actions.type-guard.test.d.ts +2 -0
  59. package/dist/controls/actions.type-guard.test.d.ts.map +1 -0
  60. package/dist/controls/compare-filter.d.ts +41 -0
  61. package/dist/controls/compare-filter.d.ts.map +1 -0
  62. package/dist/controls/compare-filter.js +23 -0
  63. package/dist/controls/control-group.d.ts +19 -0
  64. package/dist/controls/control-group.d.ts.map +1 -0
  65. package/dist/controls/control-group.js +20 -0
  66. package/dist/controls/control-group.test.d.ts +2 -0
  67. package/dist/controls/control-group.test.d.ts.map +1 -0
  68. package/dist/controls/controls.module.css +542 -0
  69. package/dist/controls/controls.module.css.d.ts +35 -0
  70. package/dist/controls/controls.router.test.d.ts +2 -0
  71. package/dist/controls/controls.router.test.d.ts.map +1 -0
  72. package/dist/controls/enum-filter.d.ts +30 -0
  73. package/dist/controls/enum-filter.d.ts.map +1 -0
  74. package/dist/controls/enum-filter.js +91 -0
  75. package/dist/controls/filter-context.d.ts +55 -0
  76. package/dist/controls/filter-context.d.ts.map +1 -0
  77. package/dist/controls/filter-context.js +69 -0
  78. package/dist/controls/filter-pill.d.ts +28 -0
  79. package/dist/controls/filter-pill.d.ts.map +1 -0
  80. package/dist/controls/filter-pill.js +70 -0
  81. package/dist/controls/filter-set.d.ts +13 -0
  82. package/dist/controls/filter-set.d.ts.map +1 -0
  83. package/dist/controls/filter-set.js +125 -0
  84. package/dist/controls/filter-set.test.d.ts +2 -0
  85. package/dist/controls/filter-set.test.d.ts.map +1 -0
  86. package/dist/controls/filter-sheet.d.ts +117 -0
  87. package/dist/controls/filter-sheet.d.ts.map +1 -0
  88. package/dist/controls/filter-sheet.js +146 -0
  89. package/dist/controls/glyphs.d.ts +27 -0
  90. package/dist/controls/glyphs.d.ts.map +1 -0
  91. package/dist/controls/glyphs.js +49 -0
  92. package/dist/controls/index.d.ts +19 -0
  93. package/dist/controls/index.d.ts.map +1 -0
  94. package/dist/controls/index.js +44 -0
  95. package/dist/controls/multi-select-filter.d.ts +23 -0
  96. package/dist/controls/multi-select-filter.d.ts.map +1 -0
  97. package/dist/controls/multi-select-filter.js +89 -0
  98. package/dist/controls/peer-boundary.test.d.ts +2 -0
  99. package/dist/controls/peer-boundary.test.d.ts.map +1 -0
  100. package/dist/controls/props-contract.test.d.ts +2 -0
  101. package/dist/controls/props-contract.test.d.ts.map +1 -0
  102. package/dist/controls/range-filter.d.ts +29 -0
  103. package/dist/controls/range-filter.d.ts.map +1 -0
  104. package/dist/controls/range-filter.js +120 -0
  105. package/dist/controls/search-filter.d.ts +10 -0
  106. package/dist/controls/search-filter.d.ts.map +1 -0
  107. package/dist/controls/search-filter.js +81 -0
  108. package/dist/controls/select-filter.d.ts +74 -0
  109. package/dist/controls/select-filter.d.ts.map +1 -0
  110. package/dist/controls/select-filter.js +10 -0
  111. package/dist/controls/sync-button.d.ts +38 -0
  112. package/dist/controls/sync-button.d.ts.map +1 -0
  113. package/dist/controls/sync-button.js +119 -0
  114. package/dist/controls/sync-button.module.css +63 -0
  115. package/dist/controls/sync-button.module.css.d.ts +11 -0
  116. package/dist/controls/sync-button.test.d.ts +2 -0
  117. package/dist/controls/sync-button.test.d.ts.map +1 -0
  118. package/dist/controls/toggle-filter.d.ts +9 -0
  119. package/dist/controls/toggle-filter.d.ts.map +1 -0
  120. package/dist/controls/toggle-filter.js +42 -0
  121. package/dist/controls/view-tabs.d.ts +23 -0
  122. package/dist/controls/view-tabs.d.ts.map +1 -0
  123. package/dist/controls/view-tabs.js +59 -0
  124. package/dist/controls-dates/date-range-picker.d.ts +4 -0
  125. package/dist/controls-dates/date-range-picker.d.ts.map +1 -0
  126. package/dist/controls-dates/date-range-picker.js +18 -0
  127. package/dist/controls-dates/index.d.ts +9 -0
  128. package/dist/controls-dates/index.d.ts.map +1 -0
  129. package/dist/controls-dates/index.js +4 -0
  130. package/dist/dashboard/delta-badge.d.ts +6 -12
  131. package/dist/dashboard/delta-badge.d.ts.map +1 -1
  132. package/dist/dashboard/delta-badge.js +1 -50
  133. package/dist/dashboard/settings-section.d.ts +6 -4
  134. package/dist/dashboard/settings-section.d.ts.map +1 -1
  135. package/dist/dashboard/settings-section.js +23 -16
  136. package/dist/dashboard/settings-section.module.css +10 -0
  137. package/dist/dashboard/settings-section.module.css.d.ts +1 -0
  138. package/dist/dashboard/settings-section.test.d.ts +2 -0
  139. package/dist/dashboard/settings-section.test.d.ts.map +1 -0
  140. package/dist/dashboard/stat-card.d.ts +44 -9
  141. package/dist/dashboard/stat-card.d.ts.map +1 -1
  142. package/dist/dashboard/stat-card.js +80 -48
  143. package/dist/dashboard/stat-card.module.css +82 -0
  144. package/dist/dashboard/stat-card.module.css.d.ts +11 -0
  145. package/dist/data/data-table.d.ts +37 -15
  146. package/dist/data/data-table.d.ts.map +1 -1
  147. package/dist/data/data-table.js +88 -58
  148. package/dist/guard/grace.test.d.ts +20 -0
  149. package/dist/guard/grace.test.d.ts.map +1 -0
  150. package/dist/guard/index.d.ts +66 -3
  151. package/dist/guard/index.d.ts.map +1 -1
  152. package/dist/guard/index.js +82 -7
  153. package/dist/guard/types.d.ts +3 -3
  154. package/dist/guard/types.d.ts.map +1 -1
  155. package/dist/index.d.ts +12 -2
  156. package/dist/index.d.ts.map +1 -1
  157. package/dist/index.js +13 -10
  158. package/dist/nav/types.d.ts +78 -2
  159. package/dist/nav/types.d.ts.map +1 -1
  160. package/dist/notifications/bell.d.ts +4 -1
  161. package/dist/notifications/bell.d.ts.map +1 -1
  162. package/dist/notifications/index.d.ts +1 -1
  163. package/dist/router-tanstack/field.d.ts +14 -0
  164. package/dist/router-tanstack/field.d.ts.map +1 -0
  165. package/dist/router-tanstack/field.js +4 -0
  166. package/dist/router-tanstack/index.d.ts +6 -2
  167. package/dist/router-tanstack/index.d.ts.map +1 -1
  168. package/dist/router-tanstack/index.js +4 -0
  169. package/dist/router-tanstack/multi-search-param-store.d.ts +4 -52
  170. package/dist/router-tanstack/multi-search-param-store.d.ts.map +1 -1
  171. package/dist/router-tanstack/multi-search-param-store.js +19 -27
  172. package/dist/router-tanstack/search-param-store.d.ts +3 -87
  173. package/dist/router-tanstack/search-param-store.d.ts.map +1 -1
  174. package/dist/router-tanstack/search-param-store.js +16 -45
  175. package/dist/router-tanstack/search-store.d.ts +81 -0
  176. package/dist/router-tanstack/search-store.d.ts.map +1 -0
  177. package/dist/router-tanstack/search-store.js +187 -0
  178. package/dist/router-tanstack/search-store.router.test.d.ts +2 -0
  179. package/dist/router-tanstack/search-store.router.test.d.ts.map +1 -0
  180. package/dist/router-tanstack/search-store.test.d.ts +2 -0
  181. package/dist/router-tanstack/search-store.test.d.ts.map +1 -0
  182. package/dist/router-tanstack/use-nav.d.ts +0 -2
  183. package/dist/router-tanstack/use-nav.d.ts.map +1 -1
  184. package/dist/router-tanstack/use-nav.js +34 -12
  185. package/dist/router-tanstack/use-nav.test.d.ts +2 -0
  186. package/dist/router-tanstack/use-nav.test.d.ts.map +1 -0
  187. package/dist/section/index.d.ts +2 -0
  188. package/dist/section/index.d.ts.map +1 -0
  189. package/dist/section/index.js +4 -0
  190. package/dist/section/section.d.ts +24 -0
  191. package/dist/section/section.d.ts.map +1 -0
  192. package/dist/section/section.js +126 -0
  193. package/dist/section/section.module.css +104 -0
  194. package/dist/section/section.module.css.d.ts +11 -0
  195. package/dist/section/section.test.d.ts +2 -0
  196. package/dist/section/section.test.d.ts.map +1 -0
  197. package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
  198. package/dist/shell/app-breadcrumbs.js +18 -15
  199. package/dist/shell/app-header.module.css +77 -37
  200. package/dist/shell/app-header.module.css.d.ts +2 -1
  201. package/dist/shell/app-mobile-nav.d.ts +9 -2
  202. package/dist/shell/app-mobile-nav.d.ts.map +1 -1
  203. package/dist/shell/app-mobile-nav.js +98 -11
  204. package/dist/shell/app-mobile-nav.module.css +8 -0
  205. package/dist/shell/app-mobile-nav.module.css.d.ts +1 -0
  206. package/dist/shell/app-sidebar.d.ts +55 -28
  207. package/dist/shell/app-sidebar.d.ts.map +1 -1
  208. package/dist/shell/app-sidebar.js +116 -67
  209. package/dist/shell/app-sidebar.module.css +303 -20
  210. package/dist/shell/app-sidebar.module.css.d.ts +19 -1
  211. package/dist/shell/index.d.ts +55 -21
  212. package/dist/shell/index.d.ts.map +1 -1
  213. package/dist/shell/index.js +40 -18
  214. package/dist/shell/mobile-nav-model.d.ts +13 -2
  215. package/dist/shell/mobile-nav-model.d.ts.map +1 -1
  216. package/dist/shell/mobile-nav-model.js +15 -1
  217. package/dist/shell/nav-count-badge.d.ts +1 -1
  218. package/dist/shell/page-bar.d.ts +51 -0
  219. package/dist/shell/page-bar.d.ts.map +1 -0
  220. package/dist/shell/page-bar.js +154 -0
  221. package/dist/shell/page-bar.module.css +178 -0
  222. package/dist/shell/page-bar.module.css.d.ts +17 -0
  223. package/dist/shell/page-bar.test.d.ts +2 -0
  224. package/dist/shell/page-bar.test.d.ts.map +1 -0
  225. package/dist/shell/sidebar-block-model.d.ts +48 -0
  226. package/dist/shell/sidebar-block-model.d.ts.map +1 -0
  227. package/dist/shell/sidebar-block-model.js +39 -0
  228. package/dist/shell/sidebar-blocks.d.ts +32 -0
  229. package/dist/shell/sidebar-blocks.d.ts.map +1 -0
  230. package/dist/shell/sidebar-blocks.js +169 -0
  231. package/dist/shell/sidebar-search.d.ts +13 -1
  232. package/dist/shell/sidebar-search.d.ts.map +1 -1
  233. package/dist/shell/sidebar-search.js +34 -1
  234. package/dist/shell/sidebar-search.module.css +26 -0
  235. package/dist/shell/sidebar-search.module.css.d.ts +2 -0
  236. package/dist/state/fields.d.ts +392 -0
  237. package/dist/state/fields.d.ts.map +1 -0
  238. package/dist/state/fields.js +369 -0
  239. package/dist/state/fields.test.d.ts +2 -0
  240. package/dist/state/fields.test.d.ts.map +1 -0
  241. package/dist/state/persisted.d.ts +48 -0
  242. package/dist/state/persisted.d.ts.map +1 -0
  243. package/dist/state/persisted.js +105 -0
  244. package/dist/state.d.ts +11 -56
  245. package/dist/state.d.ts.map +1 -1
  246. package/dist/state.js +14 -120
  247. package/dist/styles.css +13 -0
  248. package/dist/surfaces.d.ts +106 -24
  249. package/dist/surfaces.d.ts.map +1 -1
  250. package/dist/surfaces.js +155 -19
  251. package/dist/theme/controls.module.css +16 -1
  252. package/dist/theme/ctl-theme.d.ts +32 -0
  253. package/dist/theme/ctl-theme.d.ts.map +1 -0
  254. package/dist/theme/ctl-theme.js +54 -0
  255. package/dist/theme/ctl-theme.test.d.ts +2 -0
  256. package/dist/theme/ctl-theme.test.d.ts.map +1 -0
  257. package/dist/theme/ctl-tier-coverage.test.d.ts +2 -0
  258. package/dist/theme/ctl-tier-coverage.test.d.ts.map +1 -0
  259. package/dist/theme/divider-law.test.d.ts +2 -0
  260. package/dist/theme/divider-law.test.d.ts.map +1 -0
  261. package/dist/theme/icon-slot.d.ts +44 -0
  262. package/dist/theme/icon-slot.d.ts.map +1 -0
  263. package/dist/theme/icon-slot.js +21 -0
  264. package/dist/theme/icon-slot.module.css +47 -0
  265. package/dist/theme/icon-slot.module.css.d.ts +8 -0
  266. package/dist/theme/icon-slot.test.d.ts +2 -0
  267. package/dist/theme/icon-slot.test.d.ts.map +1 -0
  268. package/dist/theme/index.d.ts +1 -0
  269. package/dist/theme/index.d.ts.map +1 -1
  270. package/dist/theme/index.js +82 -5
  271. package/dist/theme/layout-rhythm-css.test.d.ts +2 -0
  272. package/dist/theme/layout-rhythm-css.test.d.ts.map +1 -0
  273. package/dist/theme/segmented-control.module.css +20 -0
  274. package/dist/theme/shadow-surfaces.test.d.ts.map +1 -1
  275. package/dist/tokens/index.d.ts +12 -0
  276. package/dist/tokens/index.d.ts.map +1 -1
  277. package/dist/tokens/index.js +38 -8
  278. package/dist/tokens/palette.d.ts +97 -61
  279. package/dist/tokens/palette.d.ts.map +1 -1
  280. package/dist/tokens/palette.js +76 -38
  281. package/dist/tokens.css +10 -2
  282. package/dist/widget-header/delta-badge.d.ts +13 -0
  283. package/dist/widget-header/delta-badge.d.ts.map +1 -0
  284. package/dist/widget-header/delta-badge.js +29 -0
  285. package/dist/widget-header/delta-badge.test.d.ts +2 -0
  286. package/dist/widget-header/delta-badge.test.d.ts.map +1 -0
  287. package/dist/widget-header/index.d.ts +3 -0
  288. package/dist/widget-header/index.d.ts.map +1 -0
  289. package/dist/widget-header/index.js +6 -0
  290. package/dist/widget-header/no-mantine.test.d.ts +2 -0
  291. package/dist/widget-header/no-mantine.test.d.ts.map +1 -0
  292. package/dist/widget-header/widget-header.d.ts +37 -0
  293. package/dist/widget-header/widget-header.d.ts.map +1 -0
  294. package/dist/widget-header/widget-header.js +77 -0
  295. package/dist/widget-header/widget-header.module.css +335 -0
  296. package/dist/widget-header/widget-header.module.css.d.ts +24 -0
  297. package/dist/widget-header/widget-header.test.d.ts +2 -0
  298. package/dist/widget-header/widget-header.test.d.ts.map +1 -0
  299. package/llms.txt +30 -17
  300. package/package.json +14 -1
  301. package/src/charts/cursor/resolve.test.ts +29 -1
  302. package/src/charts/cursor/resolve.ts +38 -6
  303. package/src/charts/cursor/scope.tsx +6 -3
  304. package/src/charts/cursor/store.test.ts +19 -1
  305. package/src/charts/cursor/store.ts +10 -5
  306. package/src/charts/hooks/useChartCursor.ts +16 -8
  307. package/src/charts/index.ts +2 -2
  308. package/src/charts/primitives/CartesianChart.test.tsx +124 -0
  309. package/src/charts/primitives/ChartCard.test.tsx +66 -0
  310. package/src/charts/primitives/ChartCard.tsx +86 -146
  311. package/src/charts/sparklines/BarSparkline.tsx +43 -5
  312. package/src/cli/check-coverage.test.ts +297 -0
  313. package/src/cli/check-theme.test.ts +38 -14
  314. package/src/cli/index.ts +392 -16
  315. package/src/cli/init-sync.test.ts +97 -0
  316. package/src/connectivity/connectivity-indicator.tsx +16 -9
  317. package/src/content/index.ts +16 -3
  318. package/src/content/prose.module.css +7 -28
  319. package/src/content/prose.module.css.test.ts +8 -24
  320. package/src/controls/actions.module.css +62 -0
  321. package/src/controls/actions.module.css.d.ts +13 -0
  322. package/src/controls/actions.test.tsx +252 -0
  323. package/src/controls/actions.tsx +655 -0
  324. package/src/controls/actions.type-guard.test.ts +63 -0
  325. package/src/controls/compare-filter.tsx +64 -0
  326. package/src/controls/control-group.test.tsx +229 -0
  327. package/src/controls/control-group.tsx +63 -0
  328. package/src/controls/controls.module.css +542 -0
  329. package/src/controls/controls.module.css.d.ts +35 -0
  330. package/src/controls/controls.router.test.tsx +637 -0
  331. package/src/controls/enum-filter.tsx +159 -0
  332. package/src/controls/filter-context.tsx +153 -0
  333. package/src/controls/filter-pill.tsx +120 -0
  334. package/src/controls/filter-set.test.tsx +472 -0
  335. package/src/controls/filter-set.tsx +225 -0
  336. package/src/controls/filter-sheet.tsx +285 -0
  337. package/src/controls/glyphs.tsx +81 -0
  338. package/src/controls/index.ts +48 -0
  339. package/src/controls/multi-select-filter.tsx +140 -0
  340. package/src/controls/peer-boundary.test.ts +85 -0
  341. package/src/controls/props-contract.test.tsx +106 -0
  342. package/src/controls/range-filter.tsx +184 -0
  343. package/src/controls/search-filter.tsx +123 -0
  344. package/src/controls/select-filter.tsx +89 -0
  345. package/src/controls/sync-button.module.css +63 -0
  346. package/src/controls/sync-button.module.css.d.ts +11 -0
  347. package/src/controls/sync-button.test.tsx +183 -0
  348. package/src/controls/sync-button.tsx +197 -0
  349. package/src/controls/toggle-filter.tsx +60 -0
  350. package/src/controls/view-tabs.tsx +110 -0
  351. package/src/controls-dates/date-range-picker.tsx +42 -0
  352. package/src/controls-dates/index.ts +8 -0
  353. package/src/dashboard/delta-badge.tsx +4 -74
  354. package/src/dashboard/settings-section.module.css +10 -0
  355. package/src/dashboard/settings-section.module.css.d.ts +1 -0
  356. package/src/dashboard/settings-section.test.tsx +56 -0
  357. package/src/dashboard/settings-section.tsx +33 -30
  358. package/src/dashboard/stat-card.module.css +82 -0
  359. package/src/dashboard/stat-card.module.css.d.ts +11 -0
  360. package/src/dashboard/stat-card.test.tsx +67 -1
  361. package/src/dashboard/stat-card.tsx +209 -68
  362. package/src/data/data-table.test.tsx +96 -11
  363. package/src/data/data-table.tsx +220 -84
  364. package/src/guard/check-source.test.ts +126 -13
  365. package/src/guard/grace.test.ts +149 -0
  366. package/src/guard/index.ts +172 -29
  367. package/src/guard/types.ts +4 -2
  368. package/src/index.ts +46 -5
  369. package/src/nav/types.ts +84 -2
  370. package/src/notifications/bell.tsx +6 -3
  371. package/src/notifications/index.ts +1 -1
  372. package/src/router-tanstack/field.ts +31 -0
  373. package/src/router-tanstack/index.ts +24 -2
  374. package/src/router-tanstack/multi-search-param-store.test.ts +71 -3
  375. package/src/router-tanstack/multi-search-param-store.ts +46 -77
  376. package/src/router-tanstack/search-param-store.ts +42 -159
  377. package/src/router-tanstack/search-store.router.test.tsx +661 -0
  378. package/src/router-tanstack/search-store.test.ts +418 -0
  379. package/src/router-tanstack/search-store.ts +380 -0
  380. package/src/router-tanstack/use-nav.test.tsx +187 -0
  381. package/src/router-tanstack/use-nav.ts +72 -14
  382. package/src/section/index.ts +1 -0
  383. package/src/section/section.module.css +104 -0
  384. package/src/section/section.module.css.d.ts +11 -0
  385. package/src/section/section.test.tsx +204 -0
  386. package/src/section/section.tsx +220 -0
  387. package/src/shell/app-breadcrumbs.tsx +44 -31
  388. package/src/shell/app-header.module.css +77 -37
  389. package/src/shell/app-header.module.css.d.ts +2 -1
  390. package/src/shell/app-mobile-nav.module.css +8 -0
  391. package/src/shell/app-mobile-nav.module.css.d.ts +1 -0
  392. package/src/shell/app-mobile-nav.tsx +147 -8
  393. package/src/shell/app-sidebar-account.tsx +2 -2
  394. package/src/shell/app-sidebar.module.css +303 -20
  395. package/src/shell/app-sidebar.module.css.d.ts +19 -1
  396. package/src/shell/app-sidebar.test.tsx +456 -41
  397. package/src/shell/app-sidebar.tsx +276 -140
  398. package/src/shell/index.test.tsx +291 -5
  399. package/src/shell/index.tsx +137 -38
  400. package/src/shell/mobile-nav-model.test.ts +228 -7
  401. package/src/shell/mobile-nav-model.ts +53 -3
  402. package/src/shell/nav-count-badge.tsx +1 -1
  403. package/src/shell/page-bar.module.css +178 -0
  404. package/src/shell/page-bar.module.css.d.ts +17 -0
  405. package/src/shell/page-bar.test.tsx +316 -0
  406. package/src/shell/page-bar.tsx +315 -0
  407. package/src/shell/sidebar-block-model.ts +83 -0
  408. package/src/shell/sidebar-blocks.tsx +301 -0
  409. package/src/shell/sidebar-search.module.css +26 -0
  410. package/src/shell/sidebar-search.module.css.d.ts +2 -0
  411. package/src/shell/sidebar-search.tsx +71 -2
  412. package/src/state/fields.test.tsx +258 -0
  413. package/src/state/fields.ts +892 -0
  414. package/src/state/persisted.ts +211 -0
  415. package/src/state.ts +46 -240
  416. package/src/styles.css +13 -0
  417. package/src/surfaces.ts +203 -36
  418. package/src/theme/controls.module.css +16 -1
  419. package/src/theme/ctl-theme.test.tsx +275 -0
  420. package/src/theme/ctl-theme.tsx +138 -0
  421. package/src/theme/ctl-tier-coverage.test.ts +170 -0
  422. package/src/theme/density-relations.test.ts +106 -51
  423. package/src/theme/divider-law.test.ts +259 -0
  424. package/src/theme/icon-slot.module.css +47 -0
  425. package/src/theme/icon-slot.module.css.d.ts +8 -0
  426. package/src/theme/icon-slot.test.tsx +275 -0
  427. package/src/theme/icon-slot.tsx +59 -0
  428. package/src/theme/index.ts +165 -3
  429. package/src/theme/layout-rhythm-css.test.ts +72 -0
  430. package/src/theme/radius.test.ts +11 -1
  431. package/src/theme/segmented-control.module.css +20 -0
  432. package/src/theme/shadow-surfaces.test.ts +20 -7
  433. package/src/theme/spacing.test.ts +75 -27
  434. package/src/theme/use-basalt-spacing.ts +2 -2
  435. package/src/tokens/density.test.ts +7 -25
  436. package/src/tokens/index.ts +48 -8
  437. package/src/tokens/palette.ts +107 -73
  438. package/src/widget-header/delta-badge.test.tsx +60 -0
  439. package/src/widget-header/delta-badge.tsx +57 -0
  440. package/src/widget-header/index.ts +2 -0
  441. package/src/widget-header/no-mantine.test.ts +33 -0
  442. package/src/widget-header/widget-header.module.css +335 -0
  443. package/src/widget-header/widget-header.module.css.d.ts +24 -0
  444. package/src/widget-header/widget-header.test.tsx +187 -0
  445. package/src/widget-header/widget-header.tsx +168 -0
  446. package/agent/rules/basalt-agent.md +0 -452
  447. package/agent/rules/basalt-app.md +0 -246
  448. package/agent/rules/basalt-commands.md +0 -320
  449. package/agent/rules/basalt-content.md +0 -409
  450. package/agent/rules/basalt-data.md +0 -329
  451. package/agent/rules/basalt-forms.md +0 -186
  452. package/agent/rules/basalt-notifications.md +0 -264
  453. package/agent/rules/basalt-query.md +0 -187
  454. package/agent/rules/basalt-router.md +0 -336
  455. package/dist/content/article-filter-bar.d.ts +0 -40
  456. package/dist/content/article-filter-bar.d.ts.map +0 -1
  457. package/dist/content/article-filter-bar.js +0 -56
  458. package/dist/shell/page-header.d.ts +0 -13
  459. package/dist/shell/page-header.d.ts.map +0 -1
  460. package/dist/shell/page-header.js +0 -23
  461. package/src/content/article-filter-bar.tsx +0 -88
  462. package/src/shell/page-header.tsx +0 -35
@@ -1,117 +1,134 @@
1
1
  ---
2
2
  source: basalt-ui
3
- description: Client state conventions for basalt-ui apps — where each kind of state lives and the theme-scheme rule (localStorage-theme read is guarded). Mostly advisory.
3
+ description: Where each kind of state lives in a basalt-ui app — createSearchStore over typed fields (URL > localStorage > fallback), the lanes, FieldHandle, linkSearch by reference, defineNav/useNav, createPersistedState. Partly enforced by basalt/search-literal-link, basalt/use-search-from-literal and the localstorage-theme guard kind.
4
4
  paths:
5
5
  - 'src/**'
6
6
  - 'apps/**/src/**'
7
7
  ---
8
8
 
9
- # Basalt State
9
+ <!-- basalt:coverage -->
10
+ <!-- GENERATED from src/surfaces.ts — `basalt-ui check-coverage --write`. Do not hand-edit. -->
11
+ <!-- backed by: guard kinds — localstorage-theme · oxlint rules — basalt/search-literal-link, basalt/use-search-from-literal -->
12
+ <!-- not guarded: — -->
13
+ <!-- /basalt:coverage -->
10
14
 
11
- How to place state in a basalt-ui app. The **theme-scheme rule** below is
12
- guard-enforced (`basalt-ui check-theme` bans `localStorage.getItem('theme')`); the rest is **advisory**.
15
+ # Basalt State — stores, lanes, nav
13
16
 
14
- ## State placement
17
+ A store is defined wherever the feature lives (`src/lib/`, `src/features/…`), not only under
18
+ `src/routes/**` — which is why this rule loads across the whole source tree.
15
19
 
16
- Pick the right home for each kind of state — don't dump everything into one store:
20
+ ## Where state goes
17
21
 
18
- - **Server state** (API data) → TanStack Query (see basalt-query.md).
19
- - **URL state** (filters, active tab, pagination, time window) → `validateSearch` in TanStack Router
20
- (see basalt-router.md). URL state is shareable and survives reload — prefer it for anything a user
21
- might link to.
22
- - **Theme / color scheme** → `useMantineColorScheme()` from Mantine — **never** a client store, and
23
- **never** `localStorage.getItem('theme')`. The scheme persists to Mantine's own key and resolves the
24
- `--vx-*` tokens via CSS; reading it any other way breaks scheme reactivity and trips `basalt-ui check-theme`.
25
- - **UI preferences that must survive navigation but aren't URL-worthy** (sidebar collapsed, panel
26
- layout, draft filters) → `createPersistedState` from `basalt-ui/state` — the framework's own
27
- versioned localStorage primitive (see below). `BasaltShell` persists its own collapse state
28
- through that same primitive since 1.21.0; use `createPersistedState` for app-level preferences.
29
- Reach for a third-party store only when complex cross-component state genuinely warrants it (see
30
- escape hatch below).
22
+ | Kind | Home |
23
+ | ------------------------------------------------------------------- | ------------------------------------------------------------------------ |
24
+ | server data | TanStack Query (basalt-batteries.md) |
25
+ | anything a control reads or writes — filter, tab, range, pagination | a **field** on `createSearchStore` / `createLocalStore` |
26
+ | color scheme | `useMantineColorScheme()` — never a store, never `localStorage['theme']` |
27
+ | a preference that must survive navigation but is not URL-worthy | `createPersistedState` (`basalt-ui/state`) |
28
+ | network status | `useConnectivity()` — auto-mounted by `BasaltProvider` |
31
29
 
32
- ## useOnlineStatus — SSR-safe online/offline hook
30
+ **Never `useState` for a filter, a tab, a range or a view** (law C3). It resets on navigation, it
31
+ cannot be linked, and it is the state a basalt control refuses to take (`value`/`onChange` do not
32
+ exist on one). Reach for a third-party store only for genuinely shared mutable state with no owning
33
+ component; basalt ships and depends on none.
33
34
 
34
- `useOnlineStatus()` returns `true` when the browser reports an active network connection,
35
- `false` when offline. Backed by `useSyncExternalStore` + `window.online`/`offline` events.
36
- SSR-safe — server snapshot is `true` (optimistic). Exported from both `basalt-ui` (root) and
37
- `basalt-ui/state`.
35
+ ## `createSearchStore` — one factory over typed fields
38
36
 
39
37
  ```ts
40
- import { useOnlineStatus } from 'basalt-ui/state'
41
-
42
- const isOnline = useOnlineStatus()
43
- if (!isOnline) return <OfflineBanner />
38
+ import { createSearchStore, field } from 'basalt-ui/router-tanstack'
39
+
40
+ export const analytics = createSearchStore({
41
+ key: 'analytics',
42
+ fields: {
43
+ range: field.range({ presets: ['7d', '30d', '90d'], fallback: '30d', custom: true }),
44
+ channels: field.multi(CHANNELS, []),
45
+ tab: field.enum(['overview', 'detail'], 'overview', { persist: false }),
46
+ compact: field.boolean(false, { url: false }),
47
+ },
48
+ }).labels({ range: { '7d': 'Last 7 days', '30d': 'Last 30 days', '90d': 'Last 90 days' } })
44
49
  ```
45
50
 
46
- No props, no options. Use it in any component; no provider needed.
47
-
48
- ## createPersistedState — the default primitive
49
-
50
- `createPersistedState` is a factory hook: call it once at module scope, use the returned hook in
51
- components. SSR-safe, cross-tab via the `storage` event, versioned to handle shape migrations.
51
+ - **The URL is the truth; the localStorage mirror is a fallback UNDER it.** Every field resolves
52
+ URL ⊳ localStorage ⊳ fallback, identically for every field kind (law C4). A deep link therefore
53
+ wins over the mirror — that is the whole difference from the enum-only pair it replaced, whose
54
+ reader WAS the localStorage state, so a shared link opened on the wrong window.
55
+ - **Lanes are declared once, at definition**: `{ url, persist, history }`. `url: false` is the
56
+ local-only lane (a per-card select, a compact toggle); `persist: false` is the URL-only lane
57
+ (pagination, a tab that should not follow you to another feature). `createLocalStore`
58
+ (`basalt-ui/state`) is the same field vocabulary with no router at all.
59
+ - **Option LABELS come from `.labels()`, once, at definition** — never a per-control prop and never a
60
+ lookup table in the page.
61
+ - **`validateSearch: store.validateSearch`** on the route, in the function form. It returns every
62
+ URL-lane param unconditionally, which is what makes the store's "does this route own the field"
63
+ check exact. A route with a genuinely wider search shape hand-writes `validateSearch` and COMPOSES
64
+ the store into it (`{ ...store.validateSearch(raw), ...rest }`) — there is no schema-backed store
65
+ variant, and none is coming.
66
+ - **A write from outside the owning route persists only**, and `validateSearch` picks it up on the
67
+ next visit. Two different stores using one param name on overlapping routes read as one owner;
68
+ presence in the search is all a store can see without the router naming the declarer.
69
+ - **`field.range` keeps its three URL params** (preset + from/to, renamable) so existing loaders and
70
+ deep links keep their shape; `handle.toWindow(value)` is the projection into query params — never a
71
+ hand-written `presetToParams`.
72
+
73
+ ## Reading and writing
74
+
75
+ - **A page reads `store.useValues()`, or takes the value as a PROP.** Never
76
+ `useSearch({ from: '<literal>' })` — a sibling or child route fails that `from`, and it is
77
+ `basalt/use-search-from-literal`. Basalt's own controls read through the `FieldHandle`, which uses
78
+ the non-strict search internally, so a control renders on any matched route with no `from` at all.
79
+ - **A `FieldHandle` is the whole read/write surface** a control needs: `use()` returns
80
+ `[value, setValue]` and the setter owns the navigate AND the persist. So a control never receives
81
+ `value`/`onChange` (law C2) and a page never writes a navigate for a store field. Where you DO hand-write
82
+ one — a route with its own search shape — use the reducer form, `navigate({ search: (prev) => ({ ...prev, … }) })`,
83
+ so unrelated params survive.
84
+ - **`useActiveCount()` / `useReset()`** are what a `Filters (n)` pill and its `Reset all` derive
85
+ from. Never hand-count either.
86
+
87
+ ## Nav links carry the store BY REFERENCE
52
88
 
53
89
  ```ts
54
- import { createPersistedState } from 'basalt-ui/state'
55
-
56
- // 1. Define the hook once (version: increment when the shape changes):
57
- export const usePanelLayout = createPersistedState({
58
- key: 'panel-layout',
59
- version: 1,
60
- initial: 'split' as 'split' | 'stacked',
61
- })
62
-
63
- // 2. Use in any component — no provider, no context:
64
- const [layout, setLayout] = usePanelLayout()
90
+ link: linkOptions({ to: '/dashboard', search: analytics.linkSearch })
65
91
  ```
66
92
 
67
- Keys are namespaced `basalt:<key>` automatically and never collide with the theme-scheme guard.
68
- Pass a `schema` (Standard Schema) to validate persisted values and fall back to `initial` on mismatch.
69
-
70
- ### Mirroring `BasaltShell`'s collapse (changed key at 1.21.0)
71
-
72
- `BasaltShell` used `@mantine/hooks`' `useLocalStorage` while `createPersistedState` was the
73
- documented house API — so a consumer reading the raw key was complying with the shipped component,
74
- not drifting from it. It now stores through `createPersistedState`, which makes it SSR-safe and
75
- moves the key:
76
-
77
- ```ts
78
- import { readPersistedValue } from 'basalt-ui/state'
79
-
80
- // 1.21.0: localStorage['basalt:<storageKey>'] === '{"v":1,"value":true}'
81
- const collapsed = readPersistedValue('basalt-sidebar-collapsed', 1)
82
- ```
83
-
84
- A one-time migration adopts the raw pre-1.21.0 value, so an upgrade does not silently re-expand
85
- every sidebar. It is a bridge, not a format — do not write against it.
86
-
87
- ## Zustand escape hatch (complex cross-component stores)
88
-
89
- Reach for Zustand only when `createPersistedState` is insufficient — typically when multiple
90
- unrelated components share mutable state that is **not** URL-worthy and has no single owning
91
- component:
92
-
93
- ```ts
94
- import { create } from 'zustand'
95
- import { persist } from 'zustand/middleware'
96
-
97
- type UiState = {
98
- panelLayout: 'split' | 'stacked'
99
- setPanelLayout: (v: UiState['panelLayout']) => void
100
- }
101
-
102
- export const useUiStore = create<UiState>()(
103
- persist(
104
- (set) => ({
105
- panelLayout: 'split',
106
- setPanelLayout: (v) => set({ panelLayout: v }),
107
- }),
108
- { name: 'app-ui' },
109
- ),
110
- )
111
- ```
112
-
113
- basalt-ui does **not** ship or depend on Zustand — it is a consumer choice. Resist putting
114
- query results, derived data, or theme scheme in any store.
115
-
116
- > **Forms** are covered by `basalt-forms.md` — see `./forms` (useBasaltForm, field, FormErrorSummary,
117
- > useFormDraft). `@mantine/form` is an optional peer; install it with `bun add @mantine/form`.
93
+ **Pass the function, never call it, never inline an object.** It is a click-time thunk, so arriving
94
+ from anywhere restores the last selection; a module-scope literal (`search: { range: '30d' }`) pins
95
+ the fallback on every click and is why one consumer's reader had zero call sites while every
96
+ individual piece looked correct. `search: true` is a type error against a store-backed route.
97
+ `basalt/search-literal-link` catches the literal, and in dev `validateSearch` warns once when the URL
98
+ pins the fallback, something else is persisted, and no reader has ever been called.
99
+
100
+ ## The typed nav definition
101
+
102
+ The whole navigation — desktop sidebar and mobile bar — is declared ONCE with `defineNav` /
103
+ `navGroup` in a **leaf** module (`src/lib/nav.tsx`) that never imports `routeTree.gen` or
104
+ `__root.tsx`, so palettes and redirects can import it without closing a cycle. `useNav(NAV)` resolves
105
+ it against the live router and returns `{ sections, mobileNav }` — both `BasaltShell` props, so
106
+ spread it. `navTarget(NAV, id)` is one destination's link options, typed per id, for a `<Link>`, a
107
+ `navigate()` or a `redirect()`. `flattenNav` is the leaf every other surface reads (e.g. projecting
108
+ the nav into Spotlight in one `.map()`).
109
+
110
+ - **Route options live under `link:`, wrapped in TanStack's own `linkOptions`** — never spread flat
111
+ onto the item. A flat shape validates `to` by assignability, which does no excess-property
112
+ checking, so a typo'd metadata key compiles silently. `id`/`label`/`short`/`icon`/`mobile`/
113
+ `disabled`/`exact` ride outside `link`.
114
+ - **Nesting is `children`**; `mobile` places the destination on the bar (basalt-mantine.md).
115
+ - **Without the `Register` module augmentation, `defineNav` validates NOTHING and reports zero
116
+ errors** — `RegisteredRouter` falls back to `AnyRouter`, every `to` widens to `string`, and the API
117
+ looks like it is working while catching nothing. Verify it once by changing a `to` to garbage and
118
+ confirming the compile error lists the real route paths. The router's errors here are large; read
119
+ the union for the intended path and fix the string. **Never reach for `as never`** to quiet one —
120
+ the checking is the entire point.
121
+ - Whether a MISSING `search` is caught depends on the schema's OUTPUT type: with every key required
122
+ (what a store or a defaulted Zod object produces) TanStack raises and the compiler catches it. With
123
+ every key optional, a forgotten `search` compiles — diff the target table by hand there.
124
+ - `staticData` (`title` / `icon` / `navSection`) drives `useRouterBreadcrumbs` and needs the
125
+ generated route tree; everything else in the bridge works against a hand-written router.
126
+
127
+ ## `createPersistedState` — the localStorage primitive
128
+
129
+ Call it once at module scope; use the returned hook anywhere, with no provider. SSR-safe, cross-tab
130
+ through the `storage` event, versioned — bump `version` when the shape changes and stale values fall
131
+ back to `initial`. Keys are namespaced `basalt:<key>` automatically, and `readPersistedValue(key, v)`
132
+ is the non-React read (how you mirror the shell's own collapse state). Pass a Standard Schema to
133
+ validate what comes back. Every basalt module that persists — the shell's collapse, a `Section`
134
+ fold, a sidebar block, a form draft, the notification history — persists through this one primitive.