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
@@ -3,11 +3,16 @@ import { createContext, useCallback, useContext, useEffect, useRef, useState } f
3
3
  import { createPortal } from "react-dom";
4
4
  import { cx } from "../common/props.js";
5
5
  import { CtlSlot } from "../theme/index.js";
6
- import { BarActionRow, BarExtrasProvider, globalActionAsBarAction } from "../controls/actions.js";
6
+ import {
7
+ BarActionRow,
8
+ BarExtrasProvider,
9
+ PAGE_BAR_END_ATTR,
10
+ globalActionAsBarAction
11
+ } from "../controls/actions.js";
7
12
  import { FilterPill } from "../controls/filter-pill.js";
8
13
  import { FilterSheet } from "../controls/filter-sheet.js";
9
14
  import { SyncButton } from "../controls/sync-button.js";
10
- import { useIsomorphicLayoutEffect } from "./isomorphic-layout-effect.js";
15
+ import { useIsomorphicLayoutEffect } from "../common/isomorphic-layout-effect.js";
11
16
  import classes from "./page-bar.module.css";
12
17
  import asideClasses from "./page-aside.module.css";
13
18
  const PAGE_BAR_HEIGHT_VAR = "--basalt-page-bar-h";
@@ -162,12 +167,12 @@ function PageBar({
162
167
  if (!inShell || !hasRow2) return;
163
168
  return claimPanelHost();
164
169
  }, [inShell, hasRow2, claimPanelHost]);
165
- const row1 = hasRow1 ? /* @__PURE__ */ jsxs("div", { className: cx(classes.row1, classNames?.row1), children: [
170
+ const row1 = hasRow1 ? /* @__PURE__ */ jsxs("div", { className: cx(classes.row1, classNames?.row1), "data-basalt-hit-scope": true, children: [
166
171
  hasLead && /* @__PURE__ */ jsxs("div", { className: classes.lead, children: [
167
172
  icon !== void 0 && /* @__PURE__ */ jsx("span", { className: classes.icon, "aria-hidden": true, children: icon }),
168
173
  title !== void 0 && /* @__PURE__ */ jsx("h1", { className: classes.title, children: title })
169
174
  ] }),
170
- /* @__PURE__ */ jsx("div", { className: classes.row1End, children: (actions !== void 0 || filtersEndActions.length > 0 || sync !== void 0) && /* @__PURE__ */ jsx(CtlSlot, { children: /* @__PURE__ */ jsx(
175
+ /* @__PURE__ */ jsx("div", { className: classes.row1End, ...{ [PAGE_BAR_END_ATTR]: "" }, children: (actions !== void 0 || filtersEndActions.length > 0 || sync !== void 0) && /* @__PURE__ */ jsx(CtlSlot, { children: /* @__PURE__ */ jsx(
171
176
  BarActionRow,
172
177
  {
173
178
  ...actions,
@@ -178,7 +183,7 @@ function PageBar({
178
183
  ) }) })
179
184
  ] }) : null;
180
185
  const hasPills = filters !== void 0 || panel !== null || filtersEndActions.length > 0;
181
- const row2 = hasRow2 ? /* @__PURE__ */ jsxs("div", { className: cx(classes.row2, classNames?.row2), children: [
186
+ const row2 = hasRow2 ? /* @__PURE__ */ jsxs("div", { className: cx(classes.row2, classNames?.row2), "data-basalt-hit-scope": true, children: [
182
187
  tabs !== void 0 && /* @__PURE__ */ jsx("div", { className: classes.tabs, "data-basalt-page-bar-line": "tabs", children: /* @__PURE__ */ jsx(CtlSlot, { children: tabs }) }),
183
188
  hasPills && /* @__PURE__ */ jsxs("div", { className: classes.pills, "data-basalt-page-bar-line": "pills", children: [
184
189
  filters !== void 0 && /* @__PURE__ */ jsx("div", { className: classes.filters, children: /* @__PURE__ */ jsx(CtlSlot, { children: filters }) }),
@@ -46,6 +46,8 @@
46
46
  }
47
47
 
48
48
  .row1 {
49
+ /* Fills the header's elastic outlet, so `.row1End` below is the ROOM the action fold measures. */
50
+ width: 100%;
49
51
  display: flex;
50
52
  flex-wrap: nowrap;
51
53
  align-items: center;
@@ -188,42 +190,16 @@
188
190
  }
189
191
 
190
192
  /*
191
- * Law C15 — every touch target inside a home is at least `touchControlHeight` (36px at level 0)
192
- * below `sm`, while the VISIBLE box stays the 30px `ctl` tier. `::after` and not `::before`:
193
- * Mantine's Button owns `::before` for its loading overlay (`@mantine/core/styles/Button.css`),
194
- * so a hit area drawn there would fight it. `pointer-events` stays default so the overlay is the
195
- * one receiving the tap.
196
- *
197
- * `overflow: visible` is LOAD-BEARING, not tidying. Mantine's Button and ActionIcon roots both set
198
- * `overflow: hidden` (`Button.css`, `ActionIcon.css`), which clips an overlay that by definition
199
- * extends past the 30px box — so without this the 36px target simply does not exist and a tap 3px
200
- * above the control misses. What `overflow: hidden` buys those roots is clipping the loading
201
- * overlay's blur, and that overlay is only painted under `[data-loading]`, which is excluded below.
193
+ * Law C15 — controls in a home adopt the shared `[data-basalt-hit]` overlay (`styles.css`) through
194
+ * `data-basalt-hit-scope` on `.row1`/`.row2`; `--vx-hit-gap` caps the expansion at the real gap so
195
+ * neighbours' hit boxes never overlap. The VISIBLE box stays the 30px `ctl` tier.
202
196
  */
203
- @media (max-width: 47.99375em) {
204
- .row1 :is(button, a),
205
- .row2 :is(button, a) {
206
- position: relative;
207
- }
208
-
209
- .row1 :is(button, a):not([data-loading]),
210
- .row2 :is(button, a):not([data-loading]) {
211
- overflow: visible;
212
- }
213
-
214
- .row1 :is(button, a)::after,
215
- .row2 :is(button, a)::after {
216
- content: '';
217
- position: absolute;
218
- top: 50%;
219
- left: 50%;
220
- translate: -50% -50%;
221
- width: 100%;
222
- height: 100%;
223
- min-width: var(--vx-space-touch-control-height);
224
- min-height: var(--vx-space-touch-control-height);
225
- }
197
+ .row1,
198
+ .row2 {
199
+ --vx-hit-gap: var(--vx-space-control-gap);
200
+ }
226
201
 
202
+ @media (max-width: 52.49375em) {
227
203
  /*
228
204
  * Row 2's phone form: two declared lines, tabs then pills — not a wrap (law C7). `.pills` folds
229
205
  * every overflow into `Filters (n)` at any width, EXCEPT when it holds nothing visible: below `sm`
@@ -1,10 +1,9 @@
1
1
  export type BreakpointName = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
2
2
  /**
3
- * Internal — the raw media-query read. Exported (not `default`) so `page-aside.tsx` can compose
4
- * it directly for its `(min-width: theme.breakpoints.sm)` desktop check without re-deriving the
5
- * query string `useBreakpoint` builds for the fixed `BreakpointName` union.
3
+ * @deprecated Use `useSizeClass` — removed next minor (see `MIGRATING.md`). `theme.breakpoints` is
4
+ * now derived: `sm` moved 48em → 52.5em and `md`/`lg`/`xl` all sit at 75em (`xs`/`sm` collapse,
5
+ * `md`/`lg`/`xl` collapse).
6
6
  */
7
- export declare function useMediaQueryMatches(query: string, fallback: boolean): boolean;
8
7
  export declare function useBreakpoint(name: BreakpointName, edge?: 'min' | 'max', options?: {
9
8
  fallback?: boolean;
10
9
  }): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"use-breakpoint.d.ts","sourceRoot":"","sources":["../../src/shell/use-breakpoint.ts"],"names":[],"mappings":"AA6BA,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAQ7D;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAmB9E;AAED,wBAAgB,aAAa,CAC3B,IAAI,EAAE,cAAc,EACpB,IAAI,GAAE,KAAK,GAAG,KAAa,EAC3B,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/B,OAAO,CAKT"}
1
+ {"version":3,"file":"use-breakpoint.d.ts","sourceRoot":"","sources":["../../src/shell/use-breakpoint.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAE7D;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,cAAc,EACpB,IAAI,GAAE,KAAK,GAAG,KAAa,EAC3B,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/B,OAAO,CAKT"}
@@ -1,33 +1,11 @@
1
- import { useCallback, useMemo, useSyncExternalStore } from "react";
2
1
  import { useMantineTheme } from "@mantine/core";
3
- function mediaQueryList(query) {
4
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") return null;
5
- return window.matchMedia(query);
6
- }
7
- function useMediaQueryMatches(query, fallback) {
8
- const list = useMemo(() => mediaQueryList(query), [query]);
9
- const subscribe = useCallback(
10
- (onStoreChange) => {
11
- if (list === null) return () => {
12
- };
13
- list.addEventListener("change", onStoreChange);
14
- return () => {
15
- list.removeEventListener("change", onStoreChange);
16
- };
17
- },
18
- [list]
19
- );
20
- const getSnapshot = useCallback(() => list?.matches ?? fallback, [list, fallback]);
21
- const getServerSnapshot = useCallback(() => fallback, [fallback]);
22
- return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
23
- }
2
+ import { useMediaQuery } from "../common/use-media-query.js";
24
3
  function useBreakpoint(name, edge = "min", options) {
25
4
  const theme = useMantineTheme();
26
5
  const bp = theme.breakpoints[name];
27
6
  const fallback = options?.fallback ?? edge === "max";
28
- return useMediaQueryMatches(`(${edge}-width: ${bp})`, fallback);
7
+ return useMediaQuery(`(${edge}-width: ${bp})`, fallback);
29
8
  }
30
9
  export {
31
- useBreakpoint,
32
- useMediaQueryMatches
10
+ useBreakpoint
33
11
  };
@@ -0,0 +1,6 @@
1
+ import { SIZE_CLASSES } from '../tokens/size-classes.js';
2
+ export type SizeClass = keyof typeof SIZE_CLASSES;
3
+ /** Provided by `BasaltProvider`'s `sizeClassHint`; the server snapshot. `undefined` = no provider. */
4
+ export declare const SizeClassHintContext: import("react").Context<"compact" | "medium" | "expanded" | undefined>;
5
+ export declare function useSizeClass(): SizeClass;
6
+ //# sourceMappingURL=use-size-class.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-size-class.d.ts","sourceRoot":"","sources":["../../src/shell/use-size-class.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAAQ,MAAM,wBAAwB,CAAA;AAE3D,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,YAAY,CAAA;AAEjD,sGAAsG;AACtG,eAAO,MAAM,oBAAoB,wEAAkD,CAAA;AAEnF,wBAAgB,YAAY,IAAI,SAAS,CAUxC"}
@@ -0,0 +1,19 @@
1
+ import { createContext, useContext } from "react";
2
+ import { useMediaQuery } from "../common/use-media-query.js";
3
+ import { SIZE_CLASSES, toEm } from "../tokens/size-classes.js";
4
+ const SizeClassHintContext = createContext(void 0);
5
+ function useSizeClass() {
6
+ const hint = useContext(SizeClassHintContext) ?? "compact";
7
+ const isMedium = useMediaQuery(`(min-width: ${toEm(SIZE_CLASSES.medium)})`, hint !== "compact");
8
+ const isExpanded = useMediaQuery(
9
+ `(min-width: ${toEm(SIZE_CLASSES.expanded)})`,
10
+ hint === "expanded"
11
+ );
12
+ if (isExpanded) return "expanded";
13
+ if (isMedium) return "medium";
14
+ return "compact";
15
+ }
16
+ export {
17
+ SizeClassHintContext,
18
+ useSizeClass
19
+ };
@@ -3,7 +3,10 @@ export declare function usePersistedOrLocal<T>({ scope, persistKey, initial, }:
3
3
  scope: string;
4
4
  /** Persist under `basalt:<scope>:<persistKey>`. Omitted → local state, nothing written. */
5
5
  persistKey: string | undefined;
6
- /** First-render value, respected only while nothing is persisted. */
6
+ /**
7
+ * The value while the user has not chosen one — unset, NOT "written as this". It may change
8
+ * between renders (a size-class default) and the flag follows until a set call pins it.
9
+ */
7
10
  initial: T;
8
11
  }): readonly [T, (next: T) => void];
9
12
  //# sourceMappingURL=persisted-or-local.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"persisted-or-local.d.ts","sourceRoot":"","sources":["../../src/state/persisted-or-local.ts"],"names":[],"mappings":"AAqBA,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,EACrC,KAAK,EACL,UAAU,EACV,OAAO,GACR,EAAE;IACD,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAA;IACb,2FAA2F;IAC3F,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9B,qEAAqE;IACrE,OAAO,EAAE,CAAC,CAAA;CACX,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,CAiBlC"}
1
+ {"version":3,"file":"persisted-or-local.d.ts","sourceRoot":"","sources":["../../src/state/persisted-or-local.ts"],"names":[],"mappings":"AAqBA,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,EACrC,KAAK,EACL,UAAU,EACV,OAAO,GACR,EAAE;IACD,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAA;IACb,2FAA2F;IAC3F,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9B;;;OAGG;IACH,OAAO,EAAE,CAAC,CAAA;CACX,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,CAoBlC"}
@@ -1,4 +1,4 @@
1
- import { useMemo, useState } from "react";
1
+ import { useCallback, useMemo, useState } from "react";
2
2
  import { createPersistedState } from "./persisted.js";
3
3
  const UNPERSISTED_KEY = "__local__";
4
4
  function usePersistedOrLocal({
@@ -6,7 +6,7 @@ function usePersistedOrLocal({
6
6
  persistKey,
7
7
  initial
8
8
  }) {
9
- const [local, setLocal] = useState(initial);
9
+ const [local, setLocal] = useState(null);
10
10
  const usePersisted = useMemo(
11
11
  () => createPersistedState({
12
12
  key: `${scope}:${persistKey ?? UNPERSISTED_KEY}`,
@@ -16,8 +16,9 @@ function usePersistedOrLocal({
16
16
  [scope, persistKey, initial]
17
17
  );
18
18
  const [persisted, setPersisted] = usePersisted();
19
+ const setLocalValue = useCallback((next) => setLocal({ value: next }), []);
19
20
  if (persistKey !== void 0) return [persisted, setPersisted];
20
- return [local, setLocal];
21
+ return [local === null ? initial : local.value, setLocalValue];
21
22
  }
22
23
  export {
23
24
  usePersistedOrLocal
@@ -27,6 +27,16 @@ export type PersistedStateOptions<T> = {
27
27
  * const [draft, setDraft] = useFilterDraft()
28
28
  */
29
29
  export declare function createPersistedState<T>(opts: PersistedStateOptions<T>): () => readonly [T, (next: T) => void];
30
+ /**
31
+ * `createPersistedState` plus a third tuple element: `isSet`, false until a value is written or a
32
+ * stored one is accepted (a stale-version or schema-invalid envelope is NOT set).
33
+ * `value` alone cannot say — an unset key and an explicit write of `initial` both read as `initial`,
34
+ * and a default that depends on context (the shell's size-class collapse default) may apply to the
35
+ * first only.
36
+ *
37
+ * @internal Not exported from the `basalt-ui/state` barrel.
38
+ */
39
+ export declare function createPersistedStore<T>(opts: PersistedStateOptions<T>): () => readonly [T, (next: T) => void, boolean];
30
40
  /**
31
41
  * Read a value previously written by `createPersistedState` under the namespaced key
32
42
  * `basalt:<key>`. Parses the versioned envelope shape `{ v, value }`. Returns `null` on
@@ -1 +1 @@
1
- {"version":3,"file":"persisted.d.ts","sourceRoot":"","sources":["../../src/state/persisted.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAOnD,MAAM,MAAM,qBAAqB,CAAC,CAAC,IAAI;IACrC,+DAA+D;IAC/D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;IACnB,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC,CAAA;IACjE;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;CAC/C,CAAA;AA4FD;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAC7B,MAAM,SAAS,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,CAwEvC;AAID;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAYhF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAItF"}
1
+ {"version":3,"file":"persisted.d.ts","sourceRoot":"","sources":["../../src/state/persisted.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAUnD,MAAM,MAAM,qBAAqB,CAAC,CAAC,IAAI;IACrC,+DAA+D;IAC/D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;IACnB,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC,CAAA;IACjE;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;CAC/C,CAAA;AA+GD;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAC7B,MAAM,SAAS,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,CAMvC;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAC7B,MAAM,SAAS,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,EAAE,OAAO,CAAC,CA0FhD;AAID;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAYhF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAItF"}
@@ -1,6 +1,7 @@
1
1
  import { useSyncExternalStore } from "react";
2
2
  const noopSubscribe = (_cb) => () => {
3
3
  };
4
+ const serverIsSet = () => false;
4
5
  const channels = /* @__PURE__ */ new Map();
5
6
  function notify(storageKey) {
6
7
  const channel = channels.get(storageKey);
@@ -18,43 +19,57 @@ function isEnvelope(raw) {
18
19
  return typeof raw === "object" && raw !== null && "v" in raw && typeof raw["v"] === "number" && "value" in raw;
19
20
  }
20
21
  function parseStorage(raw, opts) {
21
- if (raw === null) return opts.initial;
22
+ const miss = { value: opts.initial, accepted: false };
23
+ if (raw === null) return miss;
22
24
  try {
23
25
  const parsed = JSON.parse(raw);
24
- if (!isEnvelope(parsed)) return opts.initial;
26
+ if (!isEnvelope(parsed)) return miss;
25
27
  let value = parsed.value;
26
28
  if (parsed.v !== opts.version) {
27
- value = opts.migrate ? opts.migrate(parsed.value, parsed.v) : opts.initial;
29
+ if (!opts.migrate) return miss;
30
+ value = opts.migrate(parsed.value, parsed.v);
28
31
  }
29
32
  if (opts.schema) {
30
33
  const result = opts.schema["~standard"].validate(value);
31
- if (result instanceof Promise) return opts.initial;
32
- if (result.issues !== void 0) return opts.initial;
33
- return result.value;
34
+ if (result instanceof Promise) return miss;
35
+ if (result.issues !== void 0) return miss;
36
+ return { value: result.value, accepted: true };
34
37
  }
35
- return value;
38
+ return { value, accepted: true };
36
39
  } catch {
37
- return opts.initial;
40
+ return miss;
38
41
  }
39
42
  }
43
+ const unpersisted = /* @__PURE__ */ new Map();
40
44
  function writeEnvelope(storageKey, version, value) {
41
45
  try {
42
46
  const envelope = { v: version, value };
43
47
  window.localStorage.setItem(storageKey, JSON.stringify(envelope));
48
+ unpersisted.delete(storageKey);
44
49
  } catch {
50
+ unpersisted.set(storageKey, { value });
45
51
  }
46
52
  }
47
53
  function writeStorage(opts, next) {
48
54
  writeEnvelope(`basalt:${opts.key}`, opts.version, next);
49
55
  }
50
56
  function createPersistedState(opts) {
57
+ const useStore = createPersistedStore(opts);
58
+ return function usePersistedState() {
59
+ const [value, setValue] = useStore();
60
+ return [value, setValue];
61
+ };
62
+ }
63
+ function createPersistedStore(opts) {
51
64
  const storageKey = `basalt:${opts.key}`;
52
65
  const subscribe = (cb) => {
53
66
  const channel = channelFor(storageKey);
54
67
  channel.listeners.add(cb);
55
68
  if (channel.handler === null) {
56
69
  channel.handler = (e) => {
57
- if (e.key === storageKey) notify(storageKey);
70
+ if (e.key !== storageKey) return;
71
+ unpersisted.delete(storageKey);
72
+ notify(storageKey);
58
73
  };
59
74
  window.addEventListener("storage", channel.handler);
60
75
  }
@@ -68,8 +83,11 @@ function createPersistedState(opts) {
68
83
  };
69
84
  let cachedRaw = null;
70
85
  let cachedValue = opts.initial;
86
+ let cachedIsSet = false;
71
87
  let primed = false;
72
88
  const getSnapshot = () => {
89
+ const override = unpersisted.get(storageKey);
90
+ if (override !== void 0) return override.value;
73
91
  let raw;
74
92
  try {
75
93
  raw = window.localStorage.getItem(storageKey);
@@ -77,25 +95,36 @@ function createPersistedState(opts) {
77
95
  raw = null;
78
96
  }
79
97
  if (primed && raw === cachedRaw) return cachedValue;
98
+ const parsed = parseStorage(raw, opts);
80
99
  cachedRaw = raw;
81
- cachedValue = parseStorage(raw, opts);
100
+ cachedValue = parsed.value;
101
+ cachedIsSet = parsed.accepted;
82
102
  primed = true;
83
103
  return cachedValue;
84
104
  };
105
+ const getIsSet = () => {
106
+ getSnapshot();
107
+ return unpersisted.has(storageKey) || cachedIsSet;
108
+ };
85
109
  const getServerSnapshot = () => opts.initial;
86
110
  const setState = (next) => {
87
111
  writeStorage(opts, next);
88
112
  notify(storageKey);
89
113
  };
90
114
  const isServer = typeof window === "undefined";
91
- return function usePersistedState() {
115
+ return function usePersistedStore() {
116
+ const listen = isServer ? noopSubscribe : subscribe;
92
117
  const value = useSyncExternalStore(
93
- // SSR guard — subscribe must not reference `window` on the server.
94
- isServer ? noopSubscribe : subscribe,
118
+ listen,
95
119
  isServer ? getServerSnapshot : getSnapshot,
96
120
  getServerSnapshot
97
121
  );
98
- return [value, setState];
122
+ const isSet = useSyncExternalStore(
123
+ listen,
124
+ isServer ? serverIsSet : getIsSet,
125
+ serverIsSet
126
+ );
127
+ return [value, setState, isSet];
99
128
  };
100
129
  }
101
130
  function readPersistedValue(key, version) {
@@ -118,6 +147,7 @@ function writePersistedValue(key, version, value) {
118
147
  }
119
148
  export {
120
149
  createPersistedState,
150
+ createPersistedStore,
121
151
  readPersistedValue,
122
152
  writePersistedValue
123
153
  };
package/dist/styles.css CHANGED
@@ -210,6 +210,47 @@
210
210
  background-color: var(--basalt-scrollbar-thumb-hover);
211
211
  }
212
212
 
213
+ /* Bottom-anchored toasts clear the shell's bottom bar + safe area (docs/CONTROLS-SPEC.md §2). */
214
+ .mantine-Notifications-root[data-position^='bottom'] {
215
+ bottom: calc(
216
+ var(--app-shell-footer-offset, 0px) + env(safe-area-inset-bottom, 0px) +
217
+ var(--mantine-spacing-md)
218
+ );
219
+ }
220
+
221
+ /* Pointer tier: an invisible hit-area expansion to at least `--vx-hit` (44px coarse, 24px fine),
222
+ no layout change. `::after`, not `::before` — Mantine's Button owns `::before` for its loading
223
+ overlay. `overflow: visible` is load-bearing: Button/ActionIcon roots clip to their box, which
224
+ would clip the overlay; the clip only serves their `[data-loading]` overlay, so it stays then.
225
+ Adopters: `data-basalt-hit` on the control, or `data-basalt-hit-scope` on a home whose
226
+ `button`/`a` descendants (consumer-supplied) adopt it.
227
+ DECISION: the axis is `pointer: coarse` (the PRIMARY input, via `--vx-hit`), never
228
+ `any-pointer` — a touch laptop's mouse-first session must not inflate every hit area.
229
+ OVERLAP CAP: a host with neighbours sets `--vx-hit-gap` (its real gap between controls); the
230
+ expansion is then limited to that gap in total (half per side), so adjacent hit boxes never
231
+ overlap and a later sibling never steals a tap. Unset = uncapped (an isolated control). The
232
+ `+ 2px` is the control's own 1px border (Mantine buttons carry one, transparent or not): `100%`
233
+ is the PADDING box, the gap is measured between border boxes.
234
+ `position: relative` is the ::after's containing block; an adopter that must be
235
+ absolute/sticky sets its own `position` in an unlayered rule, which outranks this layer. */
236
+ :is([data-basalt-hit], [data-basalt-hit-scope] :is(button, a)) {
237
+ position: relative;
238
+ }
239
+
240
+ :is([data-basalt-hit], [data-basalt-hit-scope] :is(button, a)):not([data-loading]) {
241
+ overflow: visible;
242
+ }
243
+
244
+ :is([data-basalt-hit], [data-basalt-hit-scope] :is(button, a))::after {
245
+ content: '';
246
+ position: absolute;
247
+ top: 50%;
248
+ left: 50%;
249
+ translate: -50% -50%;
250
+ width: min(max(100%, var(--vx-hit)), calc(100% + 2px + var(--vx-hit-gap, 100vmax)));
251
+ height: min(max(100%, var(--vx-hit)), calc(100% + 2px + var(--vx-hit-gap, 100vmax)));
252
+ }
253
+
213
254
  /* Print: hide the BasaltShell chrome so only page content prints.
214
255
  Scoped to AppShell's own class hooks, NOT to bare nav/header/footer. The element form matched
215
256
  any landmark on the page — a consumer with a real <nav> and no BasaltShell silently printed a
@@ -32,7 +32,7 @@ export type RuleName = 'tokens' | 'charts' | 'mantine' | 'state' | 'controls' |
32
32
  * const id: PluginRuleId = 'hand-rolled-filter' // ok
33
33
  * // const bad: PluginRuleId = 'hand-rolled-filters' // tsc error
34
34
  */
35
- export declare const PLUGIN_RULE_ID_LIST: readonly ["no-raw-font-size", "raw-size-literal", "card-inset", "chart-in-raw-surface", "hand-rolled-plot", "chart-legend-literal", "shadow-basalt-export", "hand-rolled-shell", "raw-scroll-container", "hand-rolled-filter", "control-outside-home", "bound-control-outside-home", "control-size-literal", "page-bar-budget", "in-body-page-title", "responsive-twin", "search-literal-link", "use-search-from-literal", "provider-above-router", "query-fn-unwrap", "deprecated-export", "forms-field-key", "visx-boundary", "visx-tooltip", "token-layer-boundary", "agent-resume-guard", "agent-no-raw-usechat", "ai-sdk-major", "no-import-meta-env"];
35
+ export declare const PLUGIN_RULE_ID_LIST: readonly ["no-raw-font-size", "raw-size-literal", "card-inset", "chart-in-raw-surface", "hand-rolled-plot", "chart-legend-literal", "shadow-basalt-export", "hand-rolled-shell", "raw-scroll-container", "hand-rolled-filter", "control-outside-home", "bound-control-outside-home", "control-size-literal", "page-bar-budget", "in-body-page-title", "responsive-twin", "search-literal-link", "use-search-from-literal", "provider-above-router", "query-fn-unwrap", "deprecated-export", "forms-field-key", "visx-boundary", "visx-tooltip", "token-layer-boundary", "agent-resume-guard", "agent-no-raw-usechat", "ai-sdk-major", "no-import-meta-env", "raw-breakpoint"];
36
36
  /** One registered oxlint plugin rule id — the literal union of {@link PLUGIN_RULE_ID_LIST}. */
37
37
  export type PluginRuleId = (typeof PLUGIN_RULE_ID_LIST)[number];
38
38
  /** The 3 shipped skill names (agent/skills/basalt-{name}/SKILL.md, placed into a consumer's
@@ -172,10 +172,10 @@ export declare const SURFACES: {
172
172
  readonly layer: "mantine-coupled";
173
173
  readonly rule: "mantine";
174
174
  readonly skill: readonly ["basalt-app", "basalt-design"];
175
- readonly guardKinds: readonly ["raw-motion-value", "card-with-border", "raw-form-control", "sub-16-input-font", "mantine-shade-index", "in-body-page-title", "raw-spacing", "inline-spacing", "inline-display", "raw-html-layout", "hidden-inline-style"];
176
- readonly pluginRules: readonly ["hand-rolled-shell", "card-inset", "in-body-page-title", "raw-scroll-container", "page-bar-budget", "shadow-basalt-export", "provider-above-router", "deprecated-export", "no-import-meta-env"];
175
+ readonly guardKinds: readonly ["raw-motion-value", "card-with-border", "raw-form-control", "sub-16-input-font", "mantine-shade-index", "in-body-page-title", "raw-spacing", "inline-spacing", "inline-display", "raw-html-layout", "hidden-inline-style", "raw-media-query"];
176
+ readonly pluginRules: readonly ["hand-rolled-shell", "raw-breakpoint", "card-inset", "in-body-page-title", "raw-scroll-container", "page-bar-budget", "shadow-basalt-export", "provider-above-router", "deprecated-export", "no-import-meta-env"];
177
177
  readonly advisoryLaws: readonly ["no second cssVariablesResolver — don't hand-build createTheme or re-add the resolver basalt already installs"];
178
- readonly description: "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)";
178
+ readonly description: "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)";
179
179
  readonly optionalPeers: readonly ["react-markdown", "remark-gfm", "rehype-sanitize", "remend", "shiki", "beautiful-mermaid", "@tanstack/react-router-devtools"];
180
180
  readonly forbiddenImports: readonly [];
181
181
  };
@@ -1 +1 @@
1
- {"version":3,"file":"surfaces.d.ts","sourceRoot":"","sources":["../src/surfaces.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"surfaces.d.ts","sourceRoot":"","sources":["../src/surfaces.ts"],"names":[],"mappings":"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"}
package/dist/surfaces.js CHANGED
@@ -27,7 +27,8 @@ const PLUGIN_RULE_ID_LIST = [
27
27
  "agent-resume-guard",
28
28
  "agent-no-raw-usechat",
29
29
  "ai-sdk-major",
30
- "no-import-meta-env"
30
+ "no-import-meta-env",
31
+ "raw-breakpoint"
31
32
  ];
32
33
  const v = (spec, message, extra) => ({
33
34
  spec,
@@ -67,13 +68,20 @@ const SURFACES = {
67
68
  "inline-spacing",
68
69
  "inline-display",
69
70
  "raw-html-layout",
70
- "hidden-inline-style"
71
+ "hidden-inline-style",
72
+ // The CSS-text twin of `raw-breakpoint` (below) — a width @media, an undeclared @container
73
+ // name/literal, or a non-coarse pointer query, the shape a JSX-only AST rule cannot see.
74
+ "raw-media-query"
71
75
  ],
72
76
  // The chrome half of the plugin: the shell, the card idiom, the page title and the scroll
73
77
  // doctrine. `hand-rolled-shell` sits here rather than on a shell-shaped surface because
74
78
  // `BasaltShell` IS the root barrel's promise (docs/CONTROLS-SPEC.md §6 wave 6).
75
79
  pluginRules: [
76
80
  "hand-rolled-shell",
81
+ // The size-class axis's one JSX escape hatch (docs/CONTROLS-SPEC.md §6) is reserved
82
+ // for BasaltShell/AppSidebar/MobileNav/AppBrand (`SHELL_HOME_NAMES`, configs/oxlint-plugin.js)
83
+ // — the same shell promise `hand-rolled-shell` polices, one axis over.
84
+ "raw-breakpoint",
77
85
  "card-inset",
78
86
  "in-body-page-title",
79
87
  "raw-scroll-container",
@@ -96,7 +104,7 @@ const SURFACES = {
96
104
  advisoryLaws: [
97
105
  "no second cssVariablesResolver \u2014 don't hand-build createTheme or re-add the resolver basalt already installs"
98
106
  ],
99
- description: "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)",
107
+ description: "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)",
100
108
  optionalPeers: [
101
109
  "react-markdown",
102
110
  "remark-gfm",
@@ -61,6 +61,13 @@ html[data-mantine-color-scheme] .inputWrapper[data-variant='default']:not([data-
61
61
  box-shadow: var(--field-depth);
62
62
  }
63
63
 
64
+ /* An input cannot host the `[data-basalt-hit]` ::after overlay, so its touch floor is real height. */
65
+ @media (pointer: coarse) {
66
+ .input {
67
+ min-height: var(--vx-hit);
68
+ }
69
+ }
70
+
64
71
  .input[data-variant='default'] {
65
72
  --field-depth: var(--vx-shadow-raised);
66
73
  }
@@ -1,5 +1,6 @@
1
1
  import type { MantineThemeOverride } from '@mantine/core';
2
2
  import type { ReactNode } from 'react';
3
+ export declare const HIT_ATTR: Record<string, true>;
3
4
  /** The `MantineThemeOverride` a `<CtlSlot>` provides — see this module's doc for the merge/
4
5
  * precedence contract. Exported so a consumer composing its own slot (a bespoke home not covered
5
6
  * by `PageBar`/`Section`/the sidebar blocks) can reach the SAME tier without re-declaring it. */
@@ -1 +1 @@
1
- {"version":3,"file":"ctl-theme.d.ts","sourceRoot":"","sources":["../../src/theme/ctl-theme.tsx"],"names":[],"mappings":"AA+CA,OAAO,KAAK,EAAe,oBAAoB,EAAE,MAAM,eAAe,CAAA;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC;;iGAEiG;AACjG,eAAO,MAAM,SAAS,EAAE,oBAyBvB,CAAA;AAoCD,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,EAAE,SAAS,CAAA;IACnB;;;;;;;;;;;;OAYG;IACH,IAAI,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAA;CACxB,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,EAAE,QAAQ,EAAE,IAAY,EAAE,EAAE,YAAY,GAAG,SAAS,CAQ3E"}
1
+ {"version":3,"file":"ctl-theme.d.ts","sourceRoot":"","sources":["../../src/theme/ctl-theme.tsx"],"names":[],"mappings":"AA+CA,OAAO,KAAK,EAAe,oBAAoB,EAAE,MAAM,eAAe,CAAA;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAMtC,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAA+B,CAAA;AAEzE;;iGAEiG;AACjG,eAAO,MAAM,SAAS,EAAE,oBA+BvB,CAAA;AAoCD,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,EAAE,SAAS,CAAA;IACnB;;;;;;;;;;;;OAYG;IACH,IAAI,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAA;CACxB,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,EAAE,QAAQ,EAAE,IAAY,EAAE,EAAE,YAAY,GAAG,SAAS,CAQ3E"}
@@ -14,6 +14,7 @@ import {
14
14
  Switch,
15
15
  TextInput
16
16
  } from "@mantine/core";
17
+ const HIT_ATTR = { "data-basalt-hit": true };
17
18
  const CTL_THEME = {
18
19
  components: {
19
20
  Button: Button.extend({ defaultProps: { size: "ctl" } }),
@@ -26,7 +27,9 @@ const CTL_THEME = {
26
27
  NativeSelect: NativeSelect.extend({ defaultProps: { size: "ctl" } }),
27
28
  // The theme keys are the component names Mantine's own `useProps` looks up — `RadioGroup`, not
28
29
  // `Radio.Group` (`RadioGroup.mjs`: `useProps("RadioGroup", …)`).
29
- Radio: Radio.extend({ defaultProps: { size: "ctl" } }),
30
+ Radio: Radio.extend({
31
+ defaultProps: { size: "ctl", attributes: { label: HIT_ATTR } }
32
+ }),
30
33
  // Mantine types `RadioGroup.size` as a CLOSED `MantineSize`, while `Radio`, `Checkbox`,
31
34
  // `Checkbox.Group`, `Switch` and `Switch.Group` all type theirs OPEN
32
35
  // (`MantineSize | (string & {})`) — verified in the installed 9.3.0 `.d.ts` files, so this is an
@@ -34,9 +37,13 @@ const CTL_THEME = {
34
37
  // runtime `getSize` interpolates the string identically for all six. The cast restores what
35
38
  // every sibling type already allows; it widens nothing.
36
39
  RadioGroup: Radio.Group.extend({ defaultProps: { size: "ctl" } }),
37
- Checkbox: Checkbox.extend({ defaultProps: { size: "ctl" } }),
40
+ Checkbox: Checkbox.extend({
41
+ defaultProps: { size: "ctl", attributes: { label: HIT_ATTR } }
42
+ }),
38
43
  CheckboxGroup: Checkbox.Group.extend({ defaultProps: { size: "ctl" } }),
39
- Switch: Switch.extend({ defaultProps: { size: "ctl" } }),
44
+ Switch: Switch.extend({
45
+ defaultProps: { size: "ctl", attributes: { track: HIT_ATTR } }
46
+ }),
40
47
  SwitchGroup: Switch.Group.extend({ defaultProps: { size: "ctl" } })
41
48
  }
42
49
  };
@@ -50,5 +57,6 @@ function CtlSlot({ children, tier = "ctl" }) {
50
57
  }
51
58
  export {
52
59
  CTL_THEME,
53
- CtlSlot
60
+ CtlSlot,
61
+ HIT_ATTR
54
62
  };