basalt-ui 1.30.2 → 1.31.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 (334) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +31 -0
  3. package/MIGRATING.md +172 -12
  4. package/agent/rules/basalt-controls.md +19 -19
  5. package/agent/rules/basalt-mantine.md +1 -1
  6. package/configs/oxlint-plugin.js +254 -4
  7. package/configs/oxlint-plugin.test.ts +145 -2
  8. package/configs/oxlint.json +2 -1
  9. package/dist/agent-chat/composer.d.ts.map +1 -1
  10. package/dist/agent-chat/composer.js +41 -43
  11. package/dist/agent-chat/thread-workspace.d.ts +2 -2
  12. package/dist/agent-chat/thread-workspace.d.ts.map +1 -1
  13. package/dist/agent-chat/thread-workspace.js +3 -4
  14. package/dist/agent-chat/use-keyboard-inset.d.ts +2 -0
  15. package/dist/agent-chat/use-keyboard-inset.d.ts.map +1 -0
  16. package/dist/agent-chat/use-keyboard-inset.js +34 -0
  17. package/dist/charts/cursor/touch-pin.d.ts +45 -0
  18. package/dist/charts/cursor/touch-pin.d.ts.map +1 -0
  19. package/dist/charts/cursor/touch-pin.js +79 -0
  20. package/dist/charts/hooks/use-discrete-cursor.d.ts +66 -0
  21. package/dist/charts/hooks/use-discrete-cursor.d.ts.map +1 -0
  22. package/dist/charts/hooks/use-discrete-cursor.js +198 -0
  23. package/dist/charts/hooks/use-hysteresis.d.ts +18 -0
  24. package/dist/charts/hooks/use-hysteresis.d.ts.map +1 -0
  25. package/dist/charts/hooks/use-hysteresis.js +12 -0
  26. package/dist/charts/hooks/useBandPlot.d.ts +7 -0
  27. package/dist/charts/hooks/useBandPlot.d.ts.map +1 -1
  28. package/dist/charts/hooks/useBandPlot.js +62 -18
  29. package/dist/charts/hooks/useChartCursor.d.ts +40 -11
  30. package/dist/charts/hooks/useChartCursor.d.ts.map +1 -1
  31. package/dist/charts/hooks/useChartCursor.js +102 -11
  32. package/dist/charts/index.d.ts +0 -1
  33. package/dist/charts/index.d.ts.map +1 -1
  34. package/dist/charts/index.js +0 -10
  35. package/dist/charts/kinds/BandStrip.d.ts.map +1 -1
  36. package/dist/charts/kinds/BandStrip.js +54 -41
  37. package/dist/charts/kinds/Donut.d.ts +9 -3
  38. package/dist/charts/kinds/Donut.d.ts.map +1 -1
  39. package/dist/charts/kinds/Donut.js +110 -48
  40. package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
  41. package/dist/charts/kinds/DualPanel.js +145 -131
  42. package/dist/charts/kinds/Heatmap.d.ts +7 -0
  43. package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
  44. package/dist/charts/kinds/Heatmap.js +37 -16
  45. package/dist/charts/kinds/MirroredBars.d.ts.map +1 -1
  46. package/dist/charts/kinds/MirroredBars.js +83 -68
  47. package/dist/charts/kinds/donut-layout.d.ts +47 -0
  48. package/dist/charts/kinds/donut-layout.d.ts.map +1 -0
  49. package/dist/charts/kinds/donut-layout.js +52 -0
  50. package/dist/charts/layout/auto-margin.d.ts +7 -2
  51. package/dist/charts/layout/auto-margin.d.ts.map +1 -1
  52. package/dist/charts/layout/auto-margin.js +12 -3
  53. package/dist/charts/layout/end-labels.d.ts +64 -0
  54. package/dist/charts/layout/end-labels.d.ts.map +1 -0
  55. package/dist/charts/layout/end-labels.js +76 -0
  56. package/dist/charts/primitives/Axes.d.ts +32 -4
  57. package/dist/charts/primitives/Axes.d.ts.map +1 -1
  58. package/dist/charts/primitives/Axes.js +62 -16
  59. package/dist/charts/primitives/CartesianChart.d.ts +7 -6
  60. package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
  61. package/dist/charts/primitives/CartesianChart.js +271 -125
  62. package/dist/charts/primitives/ChartCard.d.ts +0 -14
  63. package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
  64. package/dist/charts/primitives/ChartCard.js +74 -21
  65. package/dist/charts/primitives/ChartFrame.d.ts +28 -22
  66. package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
  67. package/dist/charts/primitives/ChartFrame.js +133 -34
  68. package/dist/charts/primitives/ChartLegend.d.ts +19 -15
  69. package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
  70. package/dist/charts/primitives/ChartLegend.js +212 -71
  71. package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
  72. package/dist/charts/primitives/ChartTooltip.js +4 -3
  73. package/dist/charts/primitives/Crosshair.js +2 -2
  74. package/dist/charts/primitives/HoverOverlay.d.ts +6 -1
  75. package/dist/charts/primitives/HoverOverlay.d.ts.map +1 -1
  76. package/dist/charts/primitives/HoverOverlay.js +37 -26
  77. package/dist/charts/primitives/HoverOverlay.module.css +13 -0
  78. package/dist/charts/primitives/HoverOverlay.module.css.d.ts +8 -0
  79. package/dist/charts/primitives/chart-card-context.d.ts +29 -0
  80. package/dist/charts/primitives/chart-card-context.d.ts.map +1 -0
  81. package/dist/charts/primitives/chart-card-context.js +13 -0
  82. package/dist/charts/primitives/chart-frame-layout.d.ts +93 -113
  83. package/dist/charts/primitives/chart-frame-layout.d.ts.map +1 -1
  84. package/dist/charts/primitives/chart-frame-layout.js +76 -29
  85. package/dist/charts/primitives/chart-layout.d.ts +112 -0
  86. package/dist/charts/primitives/chart-layout.d.ts.map +1 -0
  87. package/dist/charts/primitives/chart-layout.js +175 -0
  88. package/dist/charts/primitives/chart-tier.d.ts +13 -11
  89. package/dist/charts/primitives/chart-tier.d.ts.map +1 -1
  90. package/dist/charts/primitives/chart-tier.js +42 -13
  91. package/dist/charts/series.d.ts +4 -1
  92. package/dist/charts/series.d.ts.map +1 -1
  93. package/dist/charts/utils/ticks.d.ts +27 -8
  94. package/dist/charts/utils/ticks.d.ts.map +1 -1
  95. package/dist/charts/utils/ticks.js +35 -11
  96. package/dist/common/errors.d.ts +11 -1
  97. package/dist/common/errors.d.ts.map +1 -1
  98. package/dist/common/errors.js +16 -2
  99. package/dist/common/hit-floor.d.ts +10 -0
  100. package/dist/common/hit-floor.d.ts.map +1 -0
  101. package/dist/common/hit-floor.js +6 -0
  102. package/dist/{shell → common}/isomorphic-layout-effect.d.ts +4 -3
  103. package/dist/common/isomorphic-layout-effect.d.ts.map +1 -0
  104. package/dist/common/use-measured-widths.d.ts +37 -0
  105. package/dist/common/use-measured-widths.d.ts.map +1 -0
  106. package/dist/common/use-measured-widths.js +51 -0
  107. package/dist/common/use-media-query.d.ts +6 -0
  108. package/dist/common/use-media-query.d.ts.map +1 -0
  109. package/dist/common/use-media-query.js +40 -0
  110. package/dist/common/validate.d.ts +9 -0
  111. package/dist/common/validate.d.ts.map +1 -1
  112. package/dist/common/validate.js +9 -4
  113. package/dist/content/article-layout.d.ts +4 -2
  114. package/dist/content/article-layout.d.ts.map +1 -1
  115. package/dist/content/article-layout.js +43 -3
  116. package/dist/content/article-layout.module.css +45 -4
  117. package/dist/content/article-layout.module.css.d.ts +3 -0
  118. package/dist/content/code-block.d.ts.map +1 -1
  119. package/dist/content/code-block.js +40 -2
  120. package/dist/content/code-block.module.css +32 -2
  121. package/dist/content/prose.module.css +13 -8
  122. package/dist/controls/actions.d.ts +29 -6
  123. package/dist/controls/actions.d.ts.map +1 -1
  124. package/dist/controls/actions.js +158 -27
  125. package/dist/controls/actions.module.css +14 -1
  126. package/dist/controls/actions.module.css.d.ts +1 -0
  127. package/dist/controls/controls.module.css +8 -31
  128. package/dist/controls/enum-filter.js +2 -2
  129. package/dist/controls/filter-context.d.ts +10 -0
  130. package/dist/controls/filter-context.d.ts.map +1 -1
  131. package/dist/controls/filter-context.js +2 -0
  132. package/dist/controls/filter-pill.d.ts.map +1 -1
  133. package/dist/controls/filter-pill.js +1 -0
  134. package/dist/controls/index.d.ts +1 -1
  135. package/dist/controls/index.d.ts.map +1 -1
  136. package/dist/controls/index.js +0 -2
  137. package/dist/controls/multi-select-filter.js +2 -2
  138. package/dist/controls/sync-button.module.css +1 -1
  139. package/dist/dashboard/section.d.ts.map +1 -1
  140. package/dist/dashboard/section.js +1 -0
  141. package/dist/dashboard/section.module.css +1 -26
  142. package/dist/dashboard/stat-card.d.ts +1 -1
  143. package/dist/dashboard/stat-card.d.ts.map +1 -1
  144. package/dist/dashboard/stat-card.js +4 -1
  145. package/dist/dashboard/stat-card.module.css +6 -9
  146. package/dist/dashboard/stat-group.module.css +16 -4
  147. package/dist/dashboard/widget-grid.d.ts.map +1 -1
  148. package/dist/dashboard/widget-header.d.ts +14 -4
  149. package/dist/dashboard/widget-header.d.ts.map +1 -1
  150. package/dist/dashboard/widget-header.js +23 -6
  151. package/dist/dashboard/widget-header.module.css +145 -133
  152. package/dist/dashboard/widget-header.module.css.d.ts +2 -0
  153. package/dist/data/data-table.d.ts +23 -0
  154. package/dist/data/data-table.d.ts.map +1 -1
  155. package/dist/data/data-table.js +339 -141
  156. package/dist/data/data-table.module.css +94 -0
  157. package/dist/data/data-table.module.css.d.ts +11 -0
  158. package/dist/data/virtual-list.d.ts.map +1 -1
  159. package/dist/data/virtual-list.js +10 -0
  160. package/dist/forms/form-layout.d.ts.map +1 -1
  161. package/dist/forms/form-layout.js +3 -3
  162. package/dist/forms/form-layout.module.css +30 -5
  163. package/dist/forms/form-layout.module.css.d.ts +1 -0
  164. package/dist/guard/index.d.ts +8 -2
  165. package/dist/guard/index.d.ts.map +1 -1
  166. package/dist/guard/index.js +67 -2
  167. package/dist/guard/types.d.ts +3 -3
  168. package/dist/guard/types.d.ts.map +1 -1
  169. package/dist/index.d.ts +1 -1
  170. package/dist/index.d.ts.map +1 -1
  171. package/dist/index.js +3 -1
  172. package/dist/provider/index.d.ts +20 -1
  173. package/dist/provider/index.d.ts.map +1 -1
  174. package/dist/provider/index.js +22 -2
  175. package/dist/shell/app-brand.d.ts.map +1 -1
  176. package/dist/shell/app-brand.js +1 -0
  177. package/dist/shell/app-brand.module.css +2 -2
  178. package/dist/shell/app-header.module.css +18 -8
  179. package/dist/shell/app-main.module.css +54 -3
  180. package/dist/shell/app-main.module.css.d.ts +1 -0
  181. package/dist/shell/app-mobile-nav.module.css +16 -4
  182. package/dist/shell/app-sidebar.module.css +4 -6
  183. package/dist/shell/index.d.ts +12 -0
  184. package/dist/shell/index.d.ts.map +1 -1
  185. package/dist/shell/index.js +57 -16
  186. package/dist/shell/mobile-nav-model.d.ts +1 -1
  187. package/dist/shell/page-aside.d.ts +22 -3
  188. package/dist/shell/page-aside.d.ts.map +1 -1
  189. package/dist/shell/page-aside.js +27 -13
  190. package/dist/shell/page-bar.d.ts.map +1 -1
  191. package/dist/shell/page-bar.js +10 -5
  192. package/dist/shell/page-bar.module.css +10 -34
  193. package/dist/shell/use-breakpoint.d.ts +3 -4
  194. package/dist/shell/use-breakpoint.d.ts.map +1 -1
  195. package/dist/shell/use-breakpoint.js +3 -25
  196. package/dist/shell/use-size-class.d.ts +6 -0
  197. package/dist/shell/use-size-class.d.ts.map +1 -0
  198. package/dist/shell/use-size-class.js +19 -0
  199. package/dist/state/persisted-or-local.d.ts +4 -1
  200. package/dist/state/persisted-or-local.d.ts.map +1 -1
  201. package/dist/state/persisted-or-local.js +4 -3
  202. package/dist/state/persisted.d.ts +10 -0
  203. package/dist/state/persisted.d.ts.map +1 -1
  204. package/dist/state/persisted.js +44 -14
  205. package/dist/styles.css +41 -0
  206. package/dist/surfaces.d.ts +4 -4
  207. package/dist/surfaces.d.ts.map +1 -1
  208. package/dist/surfaces.js +11 -3
  209. package/dist/theme/controls.module.css +7 -0
  210. package/dist/theme/ctl-theme.d.ts +1 -0
  211. package/dist/theme/ctl-theme.d.ts.map +1 -1
  212. package/dist/theme/ctl-theme.js +12 -4
  213. package/dist/theme/floating.module.css +36 -0
  214. package/dist/theme/floating.module.css.d.ts +3 -0
  215. package/dist/theme/index.d.ts +2 -2
  216. package/dist/theme/index.d.ts.map +1 -1
  217. package/dist/theme/index.js +34 -5
  218. package/dist/theme/nav-link.module.css +7 -0
  219. package/dist/tokens/index.d.ts +7 -16
  220. package/dist/tokens/index.d.ts.map +1 -1
  221. package/dist/tokens/index.js +23 -28
  222. package/dist/tokens/palette.d.ts +5 -27
  223. package/dist/tokens/palette.d.ts.map +1 -1
  224. package/dist/tokens/palette.js +8 -30
  225. package/dist/tokens/size-classes.d.ts +40 -0
  226. package/dist/tokens/size-classes.d.ts.map +1 -0
  227. package/dist/tokens/size-classes.js +29 -0
  228. package/dist/tokens.css +8 -4
  229. package/llms.txt +3 -3
  230. package/package.json +17 -17
  231. package/src/agent-chat/composer.tsx +74 -47
  232. package/src/agent-chat/thread-workspace.tsx +8 -11
  233. package/src/agent-chat/use-keyboard-inset.ts +68 -0
  234. package/src/charts/cursor/touch-pin.ts +138 -0
  235. package/src/charts/hooks/use-discrete-cursor.ts +335 -0
  236. package/src/charts/hooks/use-hysteresis.ts +26 -0
  237. package/src/charts/hooks/useBandPlot.ts +90 -27
  238. package/src/charts/hooks/useChartCursor.ts +211 -26
  239. package/src/charts/index.ts +0 -8
  240. package/src/charts/kinds/BandStrip.tsx +11 -1
  241. package/src/charts/kinds/Donut.tsx +153 -62
  242. package/src/charts/kinds/DualPanel.tsx +19 -4
  243. package/src/charts/kinds/Heatmap.tsx +61 -22
  244. package/src/charts/kinds/MirroredBars.tsx +15 -3
  245. package/src/charts/kinds/donut-layout.ts +82 -0
  246. package/src/charts/layout/auto-margin.ts +24 -5
  247. package/src/charts/layout/end-labels.ts +135 -0
  248. package/src/charts/primitives/Axes.tsx +112 -21
  249. package/src/charts/primitives/CartesianChart.tsx +282 -68
  250. package/src/charts/primitives/ChartCard.tsx +141 -48
  251. package/src/charts/primitives/ChartFrame.tsx +243 -60
  252. package/src/charts/primitives/ChartLegend.tsx +257 -73
  253. package/src/charts/primitives/ChartTooltip.tsx +8 -3
  254. package/src/charts/primitives/Crosshair.tsx +2 -2
  255. package/src/charts/primitives/HoverOverlay.module.css +13 -0
  256. package/src/charts/primitives/HoverOverlay.module.css.d.ts +8 -0
  257. package/src/charts/primitives/HoverOverlay.tsx +15 -4
  258. package/src/charts/primitives/chart-card-context.ts +37 -0
  259. package/src/charts/primitives/chart-frame-layout.ts +214 -177
  260. package/src/charts/primitives/chart-layout.ts +318 -0
  261. package/src/charts/primitives/chart-tier.tsx +66 -21
  262. package/src/charts/series.ts +4 -1
  263. package/src/charts/utils/ticks.ts +95 -19
  264. package/src/common/errors.ts +25 -1
  265. package/src/common/hit-floor.ts +9 -0
  266. package/src/{shell → common}/isomorphic-layout-effect.ts +4 -3
  267. package/src/common/use-measured-widths.ts +88 -0
  268. package/src/common/use-media-query.ts +56 -0
  269. package/src/common/validate.ts +17 -4
  270. package/src/content/article-layout.module.css +45 -4
  271. package/src/content/article-layout.module.css.d.ts +3 -0
  272. package/src/content/article-layout.tsx +60 -5
  273. package/src/content/code-block.module.css +32 -2
  274. package/src/content/code-block.tsx +47 -3
  275. package/src/content/prose.module.css +13 -8
  276. package/src/controls/actions.module.css +14 -1
  277. package/src/controls/actions.module.css.d.ts +1 -0
  278. package/src/controls/actions.tsx +223 -75
  279. package/src/controls/controls.module.css +8 -31
  280. package/src/controls/enum-filter.tsx +2 -2
  281. package/src/controls/filter-context.tsx +11 -0
  282. package/src/controls/filter-pill.tsx +1 -0
  283. package/src/controls/index.ts +0 -1
  284. package/src/controls/multi-select-filter.tsx +2 -2
  285. package/src/controls/sync-button.module.css +1 -1
  286. package/src/dashboard/section.module.css +1 -26
  287. package/src/dashboard/section.tsx +1 -0
  288. package/src/dashboard/stat-card.module.css +6 -9
  289. package/src/dashboard/stat-card.tsx +6 -3
  290. package/src/dashboard/stat-group.module.css +16 -4
  291. package/src/dashboard/widget-grid.tsx +7 -6
  292. package/src/dashboard/widget-header.module.css +145 -133
  293. package/src/dashboard/widget-header.module.css.d.ts +2 -0
  294. package/src/dashboard/widget-header.tsx +45 -11
  295. package/src/data/data-table.module.css +94 -0
  296. package/src/data/data-table.module.css.d.ts +11 -0
  297. package/src/data/data-table.tsx +503 -164
  298. package/src/data/virtual-list.tsx +31 -0
  299. package/src/forms/form-layout.module.css +30 -5
  300. package/src/forms/form-layout.module.css.d.ts +1 -0
  301. package/src/forms/form-layout.tsx +12 -10
  302. package/src/guard/index.ts +183 -3
  303. package/src/guard/types.ts +3 -2
  304. package/src/index.ts +2 -0
  305. package/src/provider/index.tsx +53 -1
  306. package/src/shell/app-brand.module.css +2 -2
  307. package/src/shell/app-brand.tsx +1 -0
  308. package/src/shell/app-header.module.css +18 -8
  309. package/src/shell/app-main.module.css +54 -3
  310. package/src/shell/app-main.module.css.d.ts +1 -0
  311. package/src/shell/app-mobile-nav.module.css +16 -4
  312. package/src/shell/app-sidebar.module.css +4 -6
  313. package/src/shell/index.tsx +200 -129
  314. package/src/shell/mobile-nav-model.ts +1 -1
  315. package/src/shell/page-aside.tsx +70 -21
  316. package/src/shell/page-bar.module.css +10 -34
  317. package/src/shell/page-bar.tsx +10 -5
  318. package/src/shell/use-breakpoint.ts +9 -45
  319. package/src/shell/use-size-class.ts +31 -0
  320. package/src/state/persisted-or-local.ts +10 -4
  321. package/src/state/persisted.ts +76 -17
  322. package/src/styles.css +41 -0
  323. package/src/surfaces.ts +9 -1
  324. package/src/theme/controls.module.css +7 -0
  325. package/src/theme/ctl-theme.tsx +15 -3
  326. package/src/theme/floating.module.css +36 -0
  327. package/src/theme/floating.module.css.d.ts +3 -0
  328. package/src/theme/index.ts +46 -8
  329. package/src/theme/nav-link.module.css +7 -0
  330. package/src/tokens/index.ts +20 -30
  331. package/src/tokens/palette.ts +6 -28
  332. package/src/tokens/size-classes.ts +46 -0
  333. package/dist/shell/isomorphic-layout-effect.d.ts.map +0 -1
  334. /package/dist/{shell → common}/isomorphic-layout-effect.js +0 -0
package/AGENTS.md CHANGED
@@ -8,7 +8,7 @@ Toolchain: Bun runtime, oxlint + oxfmt, conventional commits (empty scope), `mas
8
8
 
9
9
  | Subpath | Layer | Purpose |
10
10
  | --------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
11
- | `basalt-ui` | mantine-coupled | BasaltProvider, createBasaltTheme, BasaltShell + sidebar/mobile-nav/breadcrumbs, PageBar, PageAside, useBreakpoint, NavCountBadge, ThemeToggle, ThreadWorkspace + thread-chat components, WidgetHeader, dashboard composites (DeltaBadge, StatCard with threshold tone, EmptyState, QueryState/LoadingState/ErrorState, SettingsSection/SettingsRow/DangerZone), ConnectivityProvider/useConnectivity/ConnectivityIndicator (auto-mounted, C1: absorbed from the dropped ./connectivity), createBasaltQueryClient/unwrap/BasaltQueryDevtools (C1: absorbed from the dropped ./query), BasaltDevDock (C5: lazy Router/Query devtools + theme lab in one dock) |
11
+ | `basalt-ui` | mantine-coupled | BasaltProvider, createBasaltTheme, BasaltShell + sidebar/mobile-nav/breadcrumbs, PageBar, PageAside, useSizeClass (useBreakpoint on notice), NavCountBadge, ThemeToggle, ThreadWorkspace + thread-chat components, WidgetHeader, dashboard composites (DeltaBadge, StatCard with threshold tone, EmptyState, QueryState/LoadingState/ErrorState, SettingsSection/SettingsRow/DangerZone), ConnectivityProvider/useConnectivity/ConnectivityIndicator (auto-mounted, C1: absorbed from the dropped ./connectivity), createBasaltQueryClient/unwrap/BasaltQueryDevtools (C1: absorbed from the dropped ./query), BasaltDevDock (C5: lazy Router/Query devtools + theme lab in one dock) |
12
12
  | `basalt-ui/charts` | headless | CartesianChart + visx chart primitives, kinds, sparklines, hooks, and token re-exports (Mantine-free) |
13
13
  | `basalt-ui/tokens` | headless | VX token refs, buildPaletteCss, defineSeries, seriesTokens, groupTokens, alpha (Mantine-free) |
14
14
  | `basalt-ui/theme-lab` | mantine-coupled | ThemeLabControls, applyOverrides, COLOR_GROUPS — a low-level inspector for the non-derived structural tokens; identity/color tuning lives in DeriveControls |
package/CHANGELOG.md CHANGED
@@ -1,3 +1,34 @@
1
+ # [1.31.0](https://github.com/jkrumm/basalt-ui/compare/v1.30.2...v1.31.0) (2026-09-28)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * card and legend correctness — hysteresis, dots fit, slot ownership ([890658f](https://github.com/jkrumm/basalt-ui/commit/890658fc6ac5d2ffac1e04b79f6ec8d33f73e583))
7
+ * close responsive-guard gaps and dedupe measured-fold/hit-floor internals ([d37d9c2](https://github.com/jkrumm/basalt-ui/commit/d37d9c29151a65ffc83a9cce9344e602b223bfce))
8
+ * economise chart axis space with a shared y-placement ladder ([05562c7](https://github.com/jkrumm/basalt-ui/commit/05562c748bff5b6f2bf62b020e6d112463e615d1))
9
+ * give the plot its space — inside-y floor, wrap law, height clamp ([16e3e18](https://github.com/jkrumm/basalt-ui/commit/16e3e189c62170151d72014000140726c94a2204))
10
+ * keep a committed chart pin through a cancelled second press ([53e0264](https://github.com/jkrumm/basalt-ui/commit/53e0264343ffd2c06fe666bc916d6159c28ab16d))
11
+ * make chart touch presses provisional and dismiss on scroll ([c0ee0ae](https://github.com/jkrumm/basalt-ui/commit/c0ee0ae17cb6ecaf91183107b26c7f35a026b83f))
12
+ * resolve the pre-release review findings ([a33b6dd](https://github.com/jkrumm/basalt-ui/commit/a33b6ddd58519912698f3dc5b7a6a2c7ff5adf92))
13
+ * thin rotated x labels by projected width, default charts to regular ([105b12d](https://github.com/jkrumm/basalt-ui/commit/105b12d36900846212c7cf91eb6361fe4a554ae5))
14
+ * touch floors, filter hit gap, form-row container width, composer padding ([a12f230](https://github.com/jkrumm/basalt-ui/commit/a12f230963270a8785070b6711768204548fe02d))
15
+
16
+
17
+ ### Features
18
+
19
+ * adopt the pointer-tier hit area, gate hover reveals, sheet the modal ([3a63850](https://github.com/jkrumm/basalt-ui/commit/3a63850b489190927d221eaea62d67dd1a8d1f62))
20
+ * drop the dead chart-tier API and consolidate media-query hooks ([b255dcf](https://github.com/jkrumm/basalt-ui/commit/b255dcf5d408b34bec76a4b6a9e5da712e27fa52))
21
+ * fit chart legends by measured width, fold donut and label line ends ([5f0c5bd](https://github.com/jkrumm/basalt-ui/commit/5f0c5bd16cd3abd0b7772da991321ceaff8a1477))
22
+ * fold chart-card headers by height, not width alone ([9d7fc88](https://github.com/jkrumm/basalt-ui/commit/9d7fc889c5981b08db8bd47908a6598c94a55999))
23
+ * fold DataTable columns, warn short rows, sheet the TOC, pin composer keys ([f3bd775](https://github.com/jkrumm/basalt-ui/commit/f3bd775a57411bcb42a0ff49cfc3ae3a80d5a162))
24
+ * fold short-card chart legends to dots, ship the raw-breakpoint guards ([2557d9d](https://github.com/jkrumm/basalt-ui/commit/2557d9dc88438cbb88e0b7a90c572c69dd79c827))
25
+ * key card chrome on the card's own width ([aa407b8](https://github.com/jkrumm/basalt-ui/commit/aa407b866a0d9b343ce5b48f2b3c8f2b60341888))
26
+ * pin chart tooltips on tap, add keyboard parity to donut and heatmap ([a9cc50a](https://github.com/jkrumm/basalt-ui/commit/a9cc50aa6dd0626e3d312eba068cf95bbf50817b))
27
+ * resolve chart layout from the container class, height from width ([ae9632c](https://github.com/jkrumm/basalt-ui/commit/ae9632c010f58c02f49659cec1343bb8c1e23540))
28
+ * size-class shell docking, measured page-bar fold and toast bottom inset ([1fd619d](https://github.com/jkrumm/basalt-ui/commit/1fd619db7f58d81b60b20832b6353968b957c905))
29
+ * spend compact-width axes on the plot, not the gutters ([b0d5b49](https://github.com/jkrumm/basalt-ui/commit/b0d5b49f442a471f1a53b2fb57c90144efb66095))
30
+ * three-axis responsive foundations and --vx-hit ([6609ec1](https://github.com/jkrumm/basalt-ui/commit/6609ec1d62d189184dd512c12334963fb8fe0b84))
31
+
1
32
  ## [1.30.2](https://github.com/jkrumm/basalt-ui/compare/v1.30.1...v1.30.2) (2026-09-09)
2
33
 
3
34
 
package/MIGRATING.md CHANGED
@@ -66,18 +66,18 @@ There is one row per SURFACE, not per export, and a surface earns a row once any
66
66
  shipped lefthook preset both changed in 1.30.0 with no row that owned them — which is how a
67
67
  consumer read this index, found nothing, and diffed a KPI row that had silently gone 4-up → 2-up.
68
68
 
69
- | Surface | 1.27.0 | 1.28.0 | 1.29.0 | 1.30.0 | 1.30.1 | 1.30.2 | Unreleased |
70
- | ----------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------ | ---------- |
71
- | **tokens** (`./tokens`) | — | `--vx-divider` → `rgba()` on both schemes (§ Shell) | `--vx-space-touch-target` added (§ Additions) | eight spacing numbers (§ Chrome) | — | — | — |
72
- | **charts** (`./charts`) | — | phone tier, `curve`, formatters, `state`, log axis (§ preamble) | `ChartState.empty: string`, `AxisBottomNumeric` (§ Additions) | responsive `height`, `legend.maxRows` (§ Charts) | `height` reaches the kinds; `maxRows` beats the `fill` fit (§ Charts) | — | — |
73
- | **shell** (`.`) | — | scrollport, region seams, **the brand left the sidebar** (§§ scrollport, brand, Shell) | `PageTitle`, `BasaltDevDock`, `useBreakpoint` (§ Additions) | More popover, aside pill, the responsive gutter, `useBasaltSpacing` (§ Chrome) | — | — | — |
74
- | **controls** (`./controls`) | `NumberFilter`, `field.number` (§ Controls) | sheet renders panel rows; `PanelRow`, `SliderControl` (§ `basalt-ui/controls`) | — | — | — | — | — |
75
- | **forms** (`./forms`) | — | `field` → `inputProps`, **`inputProps` drops `key`**, the layout tier (§ `inputProps`) | `field` DELETED — and the `--fix` recipe no longer works (§ Consolidation) | — | — | — | — |
76
- | **data** (`./data/table`) | — | root is a `<div>`, row selection, `getItemKey` required (§ `basalt-ui/data`) | `./data` + `./query` + `./connectivity` dropped (§ Consolidation) | — | — | — | — |
77
- | **dashboard** (`.`) | `StatCard`: `unit`, `breakdown`, delta format (§ `StatCard`) | `StatGroup`, `WidgetGrid`, query-aware `StatCard` — additive | — | **the column law keys on the CONTAINER; `cols={4}` is 2-up at `sm`** (§ Dashboard) | — | — | — |
78
- | **toolchain** (`./configs/*`) | — | — | the shipped lefthook preset's `check-theme` BROKE — the CLI resolver stopped ascending (§ CLI) | the preset's default bin, and the `root:` recipe (§ Toolchain) | — | — | — |
79
- | **CLI** (`basalt-ui`) | — | — | **resolver stops relocating; `doctor` loses 6 of 10 checks** (§ CLI) | — | — | — | — |
80
- | **guards** | five rules → `error` (§ Guards) | six new ids at `warn` (§ preamble) | `query-dual-import` retired in 1.29.1 (§ Guards) | four rules → `error` (§ Guards) | — | — | — |
69
+ | Surface | 1.27.0 | 1.28.0 | 1.29.0 | 1.30.0 | 1.30.1 | 1.30.2 | 1.31.0 | Unreleased |
70
+ | ----------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------ | --------------------------------------------------------------------------------------------------------- | ---------- |
71
+ | **tokens** (`./tokens`) | — | `--vx-divider` → `rgba()` on both schemes (§ Shell) | `--vx-space-touch-target` added (§ Additions) | eight spacing numbers (§ Chrome) | — | — | `--vx-hit` replaces the touch vars; `SIZE_CLASSES` (§ Unreleased) | — |
72
+ | **charts** (`./charts`) | — | phone tier, `curve`, formatters, `state`, log axis (§ preamble) | `ChartState.empty: string`, `AxisBottomNumeric` (§ Additions) | responsive `height`, `legend.maxRows` (§ Charts) | `height` reaches the kinds; `maxRows` beats the `fill` fit (§ Charts) | — | container-class tier + `height` keys (§ Unreleased) | — |
73
+ | **shell** (`.`) | — | scrollport, region seams, **the brand left the sidebar** (§§ scrollport, brand, Shell) | `PageTitle`, `BasaltDevDock`, `useBreakpoint` (§ Additions) | More popover, aside pill, the responsive gutter, `useBasaltSpacing` (§ Chrome) | — | — | `theme.breakpoints` derived, `useSizeClass`, `data-basalt-host`, `useBreakpoint` on notice (§ Unreleased) | — |
74
+ | **controls** (`./controls`) | `NumberFilter`, `field.number` (§ Controls) | sheet renders panel rows; `PanelRow`, `SliderControl` (§ `basalt-ui/controls`) | — | — | — | — | — | — |
75
+ | **forms** (`./forms`) | — | `field` → `inputProps`, **`inputProps` drops `key`**, the layout tier (§ `inputProps`) | `field` DELETED — and the `--fix` recipe no longer works (§ Consolidation) | — | — | — | — | — |
76
+ | **data** (`./data/table`) | — | root is a `<div>`, row selection, `getItemKey` required (§ `basalt-ui/data`) | `./data` + `./query` + `./connectivity` dropped (§ Consolidation) | — | — | — | — | — |
77
+ | **dashboard** (`.`) | `StatCard`: `unit`, `breakdown`, delta format (§ `StatCard`) | `StatGroup`, `WidgetGrid`, query-aware `StatCard` — additive | — | **the column law keys on the CONTAINER; `cols={4}` is 2-up at `sm`** (§ Dashboard) | — | — | — | — |
78
+ | **toolchain** (`./configs/*`) | — | — | the shipped lefthook preset's `check-theme` BROKE — the CLI resolver stopped ascending (§ CLI) | the preset's default bin, and the `root:` recipe (§ Toolchain) | — | — | — | — |
79
+ | **CLI** (`basalt-ui`) | — | — | **resolver stops relocating; `doctor` loses 6 of 10 checks** (§ CLI) | — | — | — | — | — |
80
+ | **guards** | five rules → `error` (§ Guards) | six new ids at `warn` (§ preamble) | `query-dual-import` retired in 1.29.1 (§ Guards) | four rules → `error` (§ Guards) | — | — | — | — |
81
81
 
82
82
  ---
83
83
 
@@ -86,6 +86,166 @@ consumer read this index, found nothing, and diffed a KPI row that had silently
86
86
  _Nothing yet. Author the next section under this heading as `## Unreleased — <title>`; the
87
87
  release renames it and opens a new one here._
88
88
 
89
+ ## 1.31.0 — size classes and the pointer tier
90
+
91
+ `theme.breakpoints` is now derived from `SIZE_CLASSES` (compact < 840, medium 840–1199, expanded
92
+ ≥ 1200) and **no longer overridable** — `createBasaltTheme` drops a `breakpoints` override and types
93
+ it out. Mapping: `xs` = `sm` = 52.5em (medium opens), `md` = `lg` = `xl` = 75em (expanded opens) — the
94
+ keys collapse. So `xs` moves 36em → 52.5em, `sm` 48em → 52.5em (an iPad portrait is now bottom-bar chrome), `md`
95
+ 62em → 75em, `lg` stays 75em and `xl` 88em → 75em. Check every `visibleFrom`/`hiddenFrom`/`useBreakpoint`
96
+ on those keys: `xs` no longer differs from `sm`, and `md`/`lg`/`xl` now switch at the same width.
97
+
98
+ | Removed | Replacement |
99
+ | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
100
+ | `VX.spaceTouchTarget`, `--vx-space-touch-target` | `VX.hit`, `--vx-hit` (24px on fine pointers, 44px under `(pointer: coarse)`) |
101
+ | `VX.space.touchControlHeight`, `--vx-space-touch-control-height` | `VX.hit`; density-exempt, so it no longer tracks the knob |
102
+ | `DESKTOP_SECONDARY_MAX` (`./controls`) | none — `PageBar` row 1 folds secondary actions by its measured width (icon-only, then into `More`); removed with no grace window because nothing but the row's own count read it |
103
+ | `resolveChartTier`, `chartTierMetrics`, `useChartTier`, `useChartTierMetrics`, `ChartTier`, `ChartTierMetrics` (`./charts`) | none — removed, no grace: zero consumers. Chart chrome sizes follow the measured container class internally; there is no public tier API to read it from |
104
+ | `VX.phoneChartWidth` | `CONTAINER_CLASSES.regular` (`basalt-ui/tokens`) — removed, no grace: zero consumers, and the constant no longer drove any resolution |
105
+ | `ResponsiveChartHeight.sm/md/lg` | `regular` / `wide` — removed, no grace: zero consumers |
106
+ | `legend.mode` (every kind's `ChartLegendConfig`, `ChartFrameLegend`) | none — the resolver's own 'dots'/'chips' pick is the law; a speculative escape hatch with zero consumers that could silently re-open the plot-eating legend the wave-11 chain removed |
107
+ | `BasaltProvider host` | none — `<html data-basalt-host>` is now always auto-detected (`pwa`/`web`); re-add when a Capacitor/Tauri consumer is named |
108
+
109
+ | Changed | Notes |
110
+ | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
111
+ | Themed `Modal` is a bottom sheet below the `medium` size class; `./content` hover-reveal affordances are visible on touch | Opt out of the sheet by overriding `classNames.inner`/`content`/`body`. A consumer `attributes` (Switch/Checkbox/Radio) or `closeButtonProps` (Modal) replaces the shipped `data-basalt-hit` shallowly — re-add `'data-basalt-hit': true` where needed. |
112
+
113
+ `--vx-space-mobile-nav-row-height` is 44px (was 40). A hit overlay is `[data-basalt-hit]` in
114
+ `styles.css`; the home controls' overlays now key on `(pointer: coarse)` instead of width.
115
+
116
+ **A touch press on a chart is now PROVISIONAL, not pinned on contact** (behaviour change; `useChartCursor`'s exported `ChartCursor<T>` type gains a required `onPointerUp` field and widens `onPointerLeave` to take the pointer event — source-compatible for the normal `{...cursor}`/JSX-prop usage every shipped kind and consumer chart uses, but a manual `cursor.onPointerLeave()` call with no argument now throws). `pointerdown` still resolves and shows immediately; `pointerup` for that pointer commits the pin; a `pointercancel`/`pointerleave` before commit (the browser taking the gesture as a scroll) clears the readout instead of leaving it pinned. A pinned tooltip is now also dismissed by a `document` scroll and by Escape from anywhere in the document, not just an overlay that holds focus. A bespoke chart wired directly to `HoverOverlay` (the five declared non-`CartesianChart` shapes, or a consumer's own) needs to pass through the hook's new `onPointerUp`/`onUp` the same way it already passes `onDown`/`onMove`.
117
+
118
+ **Chart axes economise space at `compact`/`micro` container width** (behaviour change, no API change).
119
+ Y labels draw inside the plot over the grid, on a solid background chip (wave 8: was a thin surface
120
+ halo stroke, which still let the digits visually blend into a bar's fill — a bar/band chart's first
121
+ and last ticks sit directly over drawn ink with no clear region the way a line/area chart's do) and
122
+ the left gutter drops to its floor; `micro` drops the y axis and keeps two x terminals; terminal x
123
+ labels anchor inward; multi-word x labels wrap before they rotate (rotate only past 2x what fits). A
124
+ tick within one line-height of the plot's top edge flips its label below the tick line instead of
125
+ above it, so it no longer clips against the chart's own SVG viewBox (wave 8). At every width y labels
126
+ over 4 chars compact (`12.5k`, `1.2M`) and the y tick count is one per ~44px of plot height (min 2).
127
+ Opt out with explicit props: `margin.left` (keeps y labels outside), `y.format`, `y.ticks`,
128
+ `xLabelRotate`.
129
+
130
+ **`useSizeClass()` (`.`) is added** — `'compact' | 'medium' | 'expanded'`, `useSyncExternalStore`
131
+ over one `MediaQueryList` per boundary. Its server snapshot is the new `<BasaltProvider
132
+ sizeClassHint>` (default `'compact'`). `PageAside` and `ThreadWorkspace` now read it: the first
133
+ server/hydration frame is `compact` unless a hint says otherwise, so an SSR app that used to get the
134
+ desktop `PageAside` projection first should pass `sizeClassHint`. `ThreadWorkspace`'s narrow layout
135
+ follows the compact class (< 840px, was < `sm` 48em).
136
+
137
+ **`<html data-basalt-host="web|pwa">`** is set by `BasaltProvider` (`pwa` when
138
+ `(display-mode: standalone)` matches, else `web` — always auto-detected; the `host="native"` force
139
+ had zero consumers and is removed). The shell's safe-area padding keys on a non-`web` host instead
140
+ of a raw `display-mode` query.
141
+
142
+ **`PageAside`'s `defaultFolded` default now depends on whether the aside can dock.** Where an open
143
+ aside would push main (`expanded`, main keeps its floor width) it starts open; everywhere else it
144
+ would overlay main, so it starts folded on the rail. An explicit choice — a persisted or toggled fold —
145
+ always wins. Opt out with `defaultFolded={false}` (open everywhere) or `defaultFolded` (folded
146
+ everywhere).
147
+
148
+ **The navbar defaults to the rail in the `medium` class** while nothing is persisted under
149
+ `storageKey`. Opt out with a controlled `collapsed` prop; a persisted `false` (the user opened it
150
+ once) pins it open.
151
+
152
+ **On notice — `useBreakpoint` (removed next minor).** `@deprecated`, still works. Replace
153
+ `useBreakpoint('sm')` with `useSizeClass() !== 'compact'`, and `useBreakpoint('lg')` with
154
+ `useSizeClass() === 'expanded'`. A `max`-edge or non-shell width read is a container concern: use
155
+ `@container` in CSS.
156
+
157
+ **Card chrome keys on the card's own width, not the viewport.** `ChartCard`, `StatCard` and `Section`
158
+ are now `container: basalt-card / inline-size` (`container-type: inline-size` — layout and inline-size
159
+ containment). That has two consequences: a card needs a **definite inline size** (a block child, a
160
+ grid `1fr` track or an explicit width — it can no longer shrink-to-fit inside a flex row or an `auto`
161
+ grid track, where it would collapse to 0), and it becomes the containing block for `position: fixed`
162
+ and absolute descendants. What moved onto the card's width (`CONTAINER_CLASSES`: micro < 240,
163
+ compact 240–479, regular 480–799, wide ≥ 800):
164
+
165
+ - **Under 480px, or under 280px of the card's own HEIGHT regardless of width (wave 8), `subtitle`
166
+ folds into the info glyph** and the visible line is hidden. A card with a `subtitle` and no `info`
167
+ now renders a `More information` button (hidden past both floors); with both, the one glyph's
168
+ bubble reads `info`, then `subtitle`. A test asserting the subtitle is visible in a narrow OR short
169
+ card, or that no `More information` button exists without `info`, needs updating. The height half
170
+ is JS-measured (`ChartCard` only, via a `data-basalt-card-short` attribute it sets from its own
171
+ rendered height) rather than a `@container` size query — `container-type: size` collapses an
172
+ auto-height card to 0, so there is no CSS-only way to query a card's own height without an
173
+ explicit size. `StatCard`/`Section` do not (yet) set the attribute, so only `ChartCard` folds by
174
+ height today.
175
+ - **From 480px (was 800px in 1.30.x — wave 8), `value` + `delta` sit on the title row**, before
176
+ `actions`, instead of on their own row. The KPI value never ellipsizes any more: it shrinks with
177
+ the card (`cqi`, `kpi` → 16px) and breaks a pathological string rather than clipping it. A short
178
+ `ChartCard` (see above) also shrinks the value to the `lg` step and trims the header's own top
179
+ inset, so a short-but-wide chart card's header stays at or under a fifth of the card's height.
180
+ - **`StatCard` `sparklinePlacement="right"` collapses to the stacked layout** when the card is under
181
+ 480px wide (it keyed to the viewport's `sm` before). Two-up cards on a
182
+ desktop that kept the sparkline beside the value now stack when the card itself is narrow.
183
+ - **`StatGroup` is one column under 260px** of its own width, with no leading rail hairline.
184
+ - `ChartCard` is a flex column (`display: flex`, body `flex: 1 1 auto`): in a stretched grid row the
185
+ spare height goes to the body.
186
+ - **The chart tier is the measured container class** (`micro`/`compact` < 480px keep the old phone
187
+ metrics; `regular`/`wide` the desktop ones). **Hard removal, no grace window: `resolveChartTier`,
188
+ `chartTierMetrics`, `useChartTier`, `useChartTierMetrics`, `ChartTier`, `ChartTierMetrics` and
189
+ `VX.phoneChartWidth`.** Zero consumers, so these ship straight to removed rather than carrying a
190
+ deprecated forwarder for one more minor — there is no replacement export; chart chrome sizes
191
+ follow the measured container class internally, with no public tier API to read it from.
192
+ `ChartTierMetrics.legendMaxRows` (the field a stated `legend.maxRows` could never actually reach,
193
+ since the tier no longer caps a legend) goes with the type; state a cap with `legend.maxRows`
194
+ directly where one is wanted. **Before the first measurement the class follows the viewport**
195
+ (`<BasaltProvider sizeClassHint>`, default `compact`), so an unmeasured first frame is
196
+ phone-shaped unless a hint says otherwise. With no `BasaltProvider` at all (a charts-only
197
+ consumer) it is `regular`, never phone chrome.
198
+ - **`ResponsiveChartHeight` keys are `base` / `regular` / `wide`** (from 480 / 800px of the frame's
199
+ measured width). **Hard removal, no grace window: the `sm`/`md`/`lg` aliases.** Zero consumers, so
200
+ the old keys are gone outright rather than warning for a minor first — `sm` was `regular` (768 →
201
+ 480px), `md`/`lg` were `wide` (992/1200 → 800px, one step). A `height={{ sm: … }}` object now
202
+ fails `tsc` on the unknown key; rename to `regular`/`wide` and re-check any object that relied on
203
+ the old widths. **On notice (adopt-or-delete): the whole object form.** Nothing under
204
+ `~/SourceRoot` uses `{ base, regular, wide }` over a plain number — the derived height law
205
+ (`ChartFrame` with no `height`, next bullet) already covers the common case a per-step height
206
+ existed for. A future minor may drop it for a plain number plus an explicit override, unless a
207
+ consumer names a need for it first.
208
+ - **`ChartFrame` with no `height` derives one** from its measured width (≈0.62 / 0.5 / 0.4 × width by
209
+ class, clamped 160–420, and to 0.45 × viewport height on a coarse pointer) instead of 240. A
210
+ numeric `height` is an override and warns once in dev when it leaves the plot under
211
+ `VX.minPlotHeight`. Inside a `ChartCard` the height is a minimum: the frame grows into a taller
212
+ card body.
213
+ - **The legend fits by measured width, and `legend.maxRows` is `@deprecated`** (dev-warns once,
214
+ removed in 1.32.0; an explicit value still wins this release, as an entry count). Overflow folds
215
+ into an **`All N`** chip (was `+N more` / `Show less`) that opens every entry, toggles included, in
216
+ a popover on a fine pointer and a bottom sheet on a coarse one — it never grows the band. A legend
217
+ never rolls up fewer than 2 entries and left-aligns (was centred) with a 6px row gap. Inside a
218
+ `ChartCard` with a header the legend now renders in the header slot instead of under the plot; a
219
+ frame under 240px (micro) draws no legend. The phone tier no longer caps a legend at two entries
220
+ by default.
221
+ - **Hard removal, no grace window: `legend.mode`.** A speculative escape hatch back to labels for a
222
+ chart in a short card, added with zero consumers — the resolver's own `'dots'`/`'chips'` pick is
223
+ now the only law, with no override.
224
+ - **Selectors and text matching the old rollup move.** Anything matching `+N more` / `Show less`
225
+ (tests, e2e selectors, screenshots' text) now matches the `All N` chip and the disclosure it opens
226
+ (`role="dialog"`, name `Legend`, `data-basalt-legend-disclosure`); there is no in-place expand.
227
+ - **Standalone `ChartLegend` no longer applies a tier entry cap.** It renders every entry unless
228
+ `maxRows` is passed; only `ChartFrame` derives a cap (from the measured fit).
229
+
230
+ **`BasaltDataTable` folds low-priority columns instead of only scrolling, once the table no
231
+ longer fits its wrapper (behaviour change, no API change beyond the new `meta.priority`).** The
232
+ same measured fit `stickyHeader` already used (`useMeasuredContainment`) now also drives an
233
+ analytic fold: the lowest-priority data columns move into a per-row disclosure (a chevron toggle
234
+ in a new leading cell, revealing the folded columns as label/value pairs directly under the row)
235
+ until the rest fit, rather than forcing `Table.ScrollContainer` into horizontal scroll. **With no
236
+ `meta.priority` set anywhere — every existing `<BasaltDataTable data columns />` — the LAST
237
+ declared column folds first**, so a table that already fit its container renders exactly as
238
+ before; only a table that would otherwise have scrolled changes. Order it explicitly via
239
+ `meta.priority` on a column (a higher number folds before a lower one). Never engages beside an
240
+ explicit `maxHeight`/`minWidth` (a declared scroll box), and never folds the selection column.
241
+
242
+ **`Composer` pins itself above the on-screen keyboard, and its root is now a wrapper (behaviour
243
+ change, no API change).** `className`, `style` and `classNames.root` land on an outer wrapper that
244
+ also holds a keyboard-inset spacer (`--vx-keyboard-inset`, 0px when no keyboard is open), and the
245
+ input row plus actions sit in an inner stack. Padding, margin, border and background on the root
246
+ behave as before; a flex/`gap` override on the root no longer reaches the input row. Style the row
247
+ through `classNames.actions` or wrap the Composer instead.
248
+
89
249
  ## 1.30.2 — the `legendWins` collapse, and two things `ChartFrame` never said out loud
90
250
 
91
251
  **A patch. Nothing is added, renamed or removed.** One rendering regression 1.30.1 shipped is
@@ -23,24 +23,24 @@ size tier and one persistence binding, and basalt owns all three. The API is in
23
23
 
24
24
  ## The laws
25
25
 
26
- | # | Law |
27
- | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
28
- | C1 | A control lives in exactly ONE of three homes — the page bar, a section/widget header, or a form row — entered only through a slot prop. |
29
- | C2 | A basalt filter or tab has no `value`/`onChange`; it takes `field` and owns both the URL write and the localStorage mirror. |
30
- | C3 | Tab and filter state never lives in `useState` — it derives from a store field on the URL lane or the local lane. |
31
- | C4 | Every field declares its lanes once, at definition, and resolves URL ⊳ localStorage ⊳ fallback uniformly for every field kind. |
32
- | C5 | The HOME sets the size tier; an element inside a home slot carries no `size`, `w`, `fullWidth`, `visibleFrom` or `hiddenFrom`. |
33
- | C6 | A page has one `PageBar`; its `actions` hold ≤5 entries and exactly one `primary`; a `Section` holds ≤3. |
34
- | C7 | A home never scrolls horizontally and never wraps — overflow folds into a `More` menu or a `Filters (n)` sheet, computed by basalt from typed data. |
35
- | C8 | Every section, card or table title is a `WidgetHeader`; the page title is the breadcrumb (or `PageBar.title` with no shell). An in-body h1/h2 is an error. |
36
- | C9 | A responsive swap belongs to the control — rendering the same control twice under `visibleFrom`/`hiddenFrom` is an error. |
37
- | C10 | A nav link carrying a store field passes `store.linkSearch` by reference; a `search:` literal in a nav definition, or a literal `useSearch({ from })`, is an error. |
38
- | C11 | Every table or list inside a section states its count in its header. |
39
- | C12 | Refresh/sync has ONE shape, `SyncButton`, whose `scope` picks the home (`global` → the shell header, `page` → `PageBar.sync`). |
40
- | C13 | Sidebar blocks are declared data (`SidebarBlock[]`), never `ReactNode` slots, so rail and More-sheet projection stay basalt's. |
41
- | C14 | An empty home renders nothing, so no route pays for a reserved row. |
42
- | C15 | Every touch target inside a home clears the `touchControlHeight` floor below `sm`. The mobile `Filters (n)` sheet draws no row of its own — it renders the same `PanelRow` the aside's panel surface does. |
43
- | C16 | A new guard lands `warn` with a dated `promote` version, and the build fails once the package version reaches it while the rule is still `warn`. |
26
+ | # | Law |
27
+ | --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
28
+ | C1 | A control lives in exactly ONE of three homes — the page bar, a section/widget header, or a form row — entered only through a slot prop. |
29
+ | C2 | A basalt filter or tab has no `value`/`onChange`; it takes `field` and owns both the URL write and the localStorage mirror. |
30
+ | C3 | Tab and filter state never lives in `useState` — it derives from a store field on the URL lane or the local lane. |
31
+ | C4 | Every field declares its lanes once, at definition, and resolves URL ⊳ localStorage ⊳ fallback uniformly for every field kind. |
32
+ | C5 | The HOME sets the size tier; an element inside a home slot carries no `size`, `w`, `fullWidth`, `visibleFrom` or `hiddenFrom`. |
33
+ | C6 | A page has one `PageBar`; its `actions` hold ≤5 entries and exactly one `primary`; a `Section` holds ≤3. |
34
+ | C7 | A home never scrolls horizontally and never wraps — overflow folds into a `More` menu or a `Filters (n)` sheet, computed by basalt from typed data. |
35
+ | C8 | Every section, card or table title is a `WidgetHeader`; the page title is the breadcrumb (or `PageBar.title` with no shell). An in-body h1/h2 is an error. |
36
+ | C9 | A responsive swap belongs to the control — rendering the same control twice under `visibleFrom`/`hiddenFrom` is an error. |
37
+ | C10 | A nav link carrying a store field passes `store.linkSearch` by reference; a `search:` literal in a nav definition, or a literal `useSearch({ from })`, is an error. |
38
+ | C11 | Every table or list inside a section states its count in its header. |
39
+ | C12 | Refresh/sync has ONE shape, `SyncButton`, whose `scope` picks the home (`global` → the shell header, `page` → `PageBar.sync`). |
40
+ | C13 | Sidebar blocks are declared data (`SidebarBlock[]`), never `ReactNode` slots, so rail and More-sheet projection stay basalt's. |
41
+ | C14 | An empty home renders nothing, so no route pays for a reserved row. |
42
+ | C15 | Every touch target inside a home clears `--vx-hit` (44px under a coarse pointer). The mobile `Filters (n)` sheet draws no row of its own — it renders the same `PanelRow` the aside's panel surface does. |
43
+ | C16 | A new guard lands `warn` with a dated `promote` version, and the build fails once the package version reaches it while the rule is still `warn`. |
44
44
 
45
45
  A green lint run is not evidence the advisory laws the generated header above lists hold.
46
46
 
@@ -70,7 +70,7 @@ A green lint run is not evidence the advisory laws the generated header above li
70
70
  | `controlHeightTag` | count-tag `Badge` | inline chip, table cell |
71
71
  | `controlHeightWidget` | `--ai-size-icon` (explicit `size="icon"`, ActionIcon only) | sidebar search actions — no home defaults to it |
72
72
  | `controlHeightCtl` | `size="ctl"` | `PageBar`, `Section`, table toolbar, sidebar blocks |
73
- | `touchControlHeight` | hit area below `sm` | every home |
73
+ | `--vx-hit` | hit area, 44px under `(pointer: coarse)` | every home |
74
74
  | `controlHeight` | `size="md"` | forms — unchanged |
75
75
 
76
76
  A home sizes its **slot**, never its body: a raw `Button` in `PageBar.actions` is already the right
@@ -8,7 +8,7 @@ paths:
8
8
 
9
9
  <!-- basalt:coverage -->
10
10
  <!-- GENERATED from src/surfaces.ts — `bun scripts/check-coverage.ts --write`. Do not hand-edit. -->
11
- <!-- backed by: guard kinds — card-with-border, hidden-inline-style, in-body-page-title, inline-display, inline-spacing, mantine-shade-index, raw-form-control, raw-html-layout, raw-motion-value, raw-spacing, sub-16-input-font · oxlint rules — basalt/card-inset, basalt/deprecated-export, basalt/hand-rolled-shell, basalt/in-body-page-title, basalt/no-import-meta-env, basalt/page-bar-budget, basalt/provider-above-router, basalt/raw-scroll-container, basalt/shadow-basalt-export -->
11
+ <!-- backed by: guard kinds — card-with-border, hidden-inline-style, in-body-page-title, inline-display, inline-spacing, mantine-shade-index, raw-form-control, raw-html-layout, raw-media-query, raw-motion-value, raw-spacing, sub-16-input-font · oxlint rules — basalt/card-inset, basalt/deprecated-export, basalt/hand-rolled-shell, basalt/in-body-page-title, basalt/no-import-meta-env, basalt/page-bar-budget, basalt/provider-above-router, basalt/raw-breakpoint, basalt/raw-scroll-container, basalt/shadow-basalt-export -->
12
12
  <!-- not guarded: no second cssVariablesResolver — don't hand-build createTheme or re-add the resolver basalt already installs -->
13
13
  <!-- /basalt:coverage -->
14
14