basalt-ui 1.25.0 → 1.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (462) hide show
  1. package/AGENTS.md +8 -6
  2. package/CHANGELOG.md +18 -0
  3. package/MIGRATING.md +546 -20
  4. package/README.md +45 -32
  5. package/agent/rules/basalt-batteries.md +205 -0
  6. package/agent/rules/basalt-charts.md +125 -555
  7. package/agent/rules/basalt-controls.md +155 -0
  8. package/agent/rules/basalt-mantine.md +146 -439
  9. package/agent/rules/basalt-state.md +113 -96
  10. package/agent/rules/basalt-tokens.md +126 -288
  11. package/agent/skills/basalt-app/SKILL.md +70 -195
  12. package/agent/skills/basalt-charts/SKILL.md +54 -234
  13. package/agent/skills/basalt-design/SKILL.md +56 -248
  14. package/agent/templates/CLAUDE-block.md.tpl +31 -40
  15. package/agent/templates/DESIGN.md.tpl +22 -51
  16. package/configs/oxlint-plugin.js +1275 -31
  17. package/configs/oxlint-plugin.test.ts +947 -11
  18. package/configs/oxlint.json +11 -3
  19. package/dist/charts/cursor/resolve.d.ts +18 -0
  20. package/dist/charts/cursor/resolve.d.ts.map +1 -1
  21. package/dist/charts/cursor/resolve.js +15 -6
  22. package/dist/charts/cursor/scope.d.ts +6 -3
  23. package/dist/charts/cursor/scope.d.ts.map +1 -1
  24. package/dist/charts/cursor/store.d.ts +5 -1
  25. package/dist/charts/cursor/store.d.ts.map +1 -1
  26. package/dist/charts/cursor/store.js +4 -4
  27. package/dist/charts/hooks/useChartCursor.d.ts.map +1 -1
  28. package/dist/charts/hooks/useChartCursor.js +9 -8
  29. package/dist/charts/index.d.ts +2 -2
  30. package/dist/charts/index.d.ts.map +1 -1
  31. package/dist/charts/primitives/ChartCard.d.ts +34 -9
  32. package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
  33. package/dist/charts/primitives/ChartCard.js +29 -114
  34. package/dist/charts/primitives/ChartCard.test.d.ts +2 -0
  35. package/dist/charts/primitives/ChartCard.test.d.ts.map +1 -0
  36. package/dist/charts/sparklines/BarSparkline.d.ts +26 -2
  37. package/dist/charts/sparklines/BarSparkline.d.ts.map +1 -1
  38. package/dist/charts/sparklines/BarSparkline.js +18 -4
  39. package/dist/cli/check-coverage.test.d.ts +2 -0
  40. package/dist/cli/check-coverage.test.d.ts.map +1 -0
  41. package/dist/cli/index.d.ts +67 -4
  42. package/dist/cli/index.d.ts.map +1 -1
  43. package/dist/cli/index.js +225 -7
  44. package/dist/connectivity/connectivity-indicator.d.ts +4 -1
  45. package/dist/connectivity/connectivity-indicator.d.ts.map +1 -1
  46. package/dist/connectivity/connectivity-indicator.js +1 -1
  47. package/dist/content/index.d.ts +5 -3
  48. package/dist/content/index.d.ts.map +1 -1
  49. package/dist/content/index.js +4 -2
  50. package/dist/content/prose.module.css +7 -28
  51. package/dist/controls/actions.d.ts +169 -0
  52. package/dist/controls/actions.d.ts.map +1 -0
  53. package/dist/controls/actions.js +287 -0
  54. package/dist/controls/actions.module.css +62 -0
  55. package/dist/controls/actions.module.css.d.ts +13 -0
  56. package/dist/controls/actions.test.d.ts +2 -0
  57. package/dist/controls/actions.test.d.ts.map +1 -0
  58. package/dist/controls/actions.type-guard.test.d.ts +2 -0
  59. package/dist/controls/actions.type-guard.test.d.ts.map +1 -0
  60. package/dist/controls/compare-filter.d.ts +41 -0
  61. package/dist/controls/compare-filter.d.ts.map +1 -0
  62. package/dist/controls/compare-filter.js +23 -0
  63. package/dist/controls/control-group.d.ts +19 -0
  64. package/dist/controls/control-group.d.ts.map +1 -0
  65. package/dist/controls/control-group.js +20 -0
  66. package/dist/controls/control-group.test.d.ts +2 -0
  67. package/dist/controls/control-group.test.d.ts.map +1 -0
  68. package/dist/controls/controls.module.css +542 -0
  69. package/dist/controls/controls.module.css.d.ts +35 -0
  70. package/dist/controls/controls.router.test.d.ts +2 -0
  71. package/dist/controls/controls.router.test.d.ts.map +1 -0
  72. package/dist/controls/enum-filter.d.ts +30 -0
  73. package/dist/controls/enum-filter.d.ts.map +1 -0
  74. package/dist/controls/enum-filter.js +91 -0
  75. package/dist/controls/filter-context.d.ts +55 -0
  76. package/dist/controls/filter-context.d.ts.map +1 -0
  77. package/dist/controls/filter-context.js +69 -0
  78. package/dist/controls/filter-pill.d.ts +28 -0
  79. package/dist/controls/filter-pill.d.ts.map +1 -0
  80. package/dist/controls/filter-pill.js +70 -0
  81. package/dist/controls/filter-set.d.ts +13 -0
  82. package/dist/controls/filter-set.d.ts.map +1 -0
  83. package/dist/controls/filter-set.js +125 -0
  84. package/dist/controls/filter-set.test.d.ts +2 -0
  85. package/dist/controls/filter-set.test.d.ts.map +1 -0
  86. package/dist/controls/filter-sheet.d.ts +117 -0
  87. package/dist/controls/filter-sheet.d.ts.map +1 -0
  88. package/dist/controls/filter-sheet.js +146 -0
  89. package/dist/controls/glyphs.d.ts +27 -0
  90. package/dist/controls/glyphs.d.ts.map +1 -0
  91. package/dist/controls/glyphs.js +49 -0
  92. package/dist/controls/index.d.ts +19 -0
  93. package/dist/controls/index.d.ts.map +1 -0
  94. package/dist/controls/index.js +44 -0
  95. package/dist/controls/multi-select-filter.d.ts +23 -0
  96. package/dist/controls/multi-select-filter.d.ts.map +1 -0
  97. package/dist/controls/multi-select-filter.js +89 -0
  98. package/dist/controls/peer-boundary.test.d.ts +2 -0
  99. package/dist/controls/peer-boundary.test.d.ts.map +1 -0
  100. package/dist/controls/props-contract.test.d.ts +2 -0
  101. package/dist/controls/props-contract.test.d.ts.map +1 -0
  102. package/dist/controls/range-filter.d.ts +29 -0
  103. package/dist/controls/range-filter.d.ts.map +1 -0
  104. package/dist/controls/range-filter.js +120 -0
  105. package/dist/controls/search-filter.d.ts +10 -0
  106. package/dist/controls/search-filter.d.ts.map +1 -0
  107. package/dist/controls/search-filter.js +81 -0
  108. package/dist/controls/select-filter.d.ts +74 -0
  109. package/dist/controls/select-filter.d.ts.map +1 -0
  110. package/dist/controls/select-filter.js +10 -0
  111. package/dist/controls/sync-button.d.ts +38 -0
  112. package/dist/controls/sync-button.d.ts.map +1 -0
  113. package/dist/controls/sync-button.js +119 -0
  114. package/dist/controls/sync-button.module.css +63 -0
  115. package/dist/controls/sync-button.module.css.d.ts +11 -0
  116. package/dist/controls/sync-button.test.d.ts +2 -0
  117. package/dist/controls/sync-button.test.d.ts.map +1 -0
  118. package/dist/controls/toggle-filter.d.ts +9 -0
  119. package/dist/controls/toggle-filter.d.ts.map +1 -0
  120. package/dist/controls/toggle-filter.js +42 -0
  121. package/dist/controls/view-tabs.d.ts +23 -0
  122. package/dist/controls/view-tabs.d.ts.map +1 -0
  123. package/dist/controls/view-tabs.js +59 -0
  124. package/dist/controls-dates/date-range-picker.d.ts +4 -0
  125. package/dist/controls-dates/date-range-picker.d.ts.map +1 -0
  126. package/dist/controls-dates/date-range-picker.js +18 -0
  127. package/dist/controls-dates/index.d.ts +9 -0
  128. package/dist/controls-dates/index.d.ts.map +1 -0
  129. package/dist/controls-dates/index.js +4 -0
  130. package/dist/dashboard/delta-badge.d.ts +6 -12
  131. package/dist/dashboard/delta-badge.d.ts.map +1 -1
  132. package/dist/dashboard/delta-badge.js +1 -50
  133. package/dist/dashboard/settings-section.d.ts +6 -4
  134. package/dist/dashboard/settings-section.d.ts.map +1 -1
  135. package/dist/dashboard/settings-section.js +23 -16
  136. package/dist/dashboard/settings-section.module.css +10 -0
  137. package/dist/dashboard/settings-section.module.css.d.ts +1 -0
  138. package/dist/dashboard/settings-section.test.d.ts +2 -0
  139. package/dist/dashboard/settings-section.test.d.ts.map +1 -0
  140. package/dist/dashboard/stat-card.d.ts +44 -9
  141. package/dist/dashboard/stat-card.d.ts.map +1 -1
  142. package/dist/dashboard/stat-card.js +80 -48
  143. package/dist/dashboard/stat-card.module.css +82 -0
  144. package/dist/dashboard/stat-card.module.css.d.ts +11 -0
  145. package/dist/data/data-table.d.ts +37 -15
  146. package/dist/data/data-table.d.ts.map +1 -1
  147. package/dist/data/data-table.js +88 -58
  148. package/dist/guard/grace.test.d.ts +20 -0
  149. package/dist/guard/grace.test.d.ts.map +1 -0
  150. package/dist/guard/index.d.ts +66 -3
  151. package/dist/guard/index.d.ts.map +1 -1
  152. package/dist/guard/index.js +82 -7
  153. package/dist/guard/types.d.ts +3 -3
  154. package/dist/guard/types.d.ts.map +1 -1
  155. package/dist/index.d.ts +12 -2
  156. package/dist/index.d.ts.map +1 -1
  157. package/dist/index.js +13 -10
  158. package/dist/nav/types.d.ts +78 -2
  159. package/dist/nav/types.d.ts.map +1 -1
  160. package/dist/notifications/bell.d.ts +4 -1
  161. package/dist/notifications/bell.d.ts.map +1 -1
  162. package/dist/notifications/index.d.ts +1 -1
  163. package/dist/router-tanstack/field.d.ts +14 -0
  164. package/dist/router-tanstack/field.d.ts.map +1 -0
  165. package/dist/router-tanstack/field.js +4 -0
  166. package/dist/router-tanstack/index.d.ts +6 -2
  167. package/dist/router-tanstack/index.d.ts.map +1 -1
  168. package/dist/router-tanstack/index.js +4 -0
  169. package/dist/router-tanstack/multi-search-param-store.d.ts +4 -52
  170. package/dist/router-tanstack/multi-search-param-store.d.ts.map +1 -1
  171. package/dist/router-tanstack/multi-search-param-store.js +19 -27
  172. package/dist/router-tanstack/search-param-store.d.ts +3 -87
  173. package/dist/router-tanstack/search-param-store.d.ts.map +1 -1
  174. package/dist/router-tanstack/search-param-store.js +16 -45
  175. package/dist/router-tanstack/search-store.d.ts +81 -0
  176. package/dist/router-tanstack/search-store.d.ts.map +1 -0
  177. package/dist/router-tanstack/search-store.js +187 -0
  178. package/dist/router-tanstack/search-store.router.test.d.ts +2 -0
  179. package/dist/router-tanstack/search-store.router.test.d.ts.map +1 -0
  180. package/dist/router-tanstack/search-store.test.d.ts +2 -0
  181. package/dist/router-tanstack/search-store.test.d.ts.map +1 -0
  182. package/dist/router-tanstack/use-nav.d.ts +0 -2
  183. package/dist/router-tanstack/use-nav.d.ts.map +1 -1
  184. package/dist/router-tanstack/use-nav.js +34 -12
  185. package/dist/router-tanstack/use-nav.test.d.ts +2 -0
  186. package/dist/router-tanstack/use-nav.test.d.ts.map +1 -0
  187. package/dist/section/index.d.ts +2 -0
  188. package/dist/section/index.d.ts.map +1 -0
  189. package/dist/section/index.js +4 -0
  190. package/dist/section/section.d.ts +24 -0
  191. package/dist/section/section.d.ts.map +1 -0
  192. package/dist/section/section.js +126 -0
  193. package/dist/section/section.module.css +104 -0
  194. package/dist/section/section.module.css.d.ts +11 -0
  195. package/dist/section/section.test.d.ts +2 -0
  196. package/dist/section/section.test.d.ts.map +1 -0
  197. package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
  198. package/dist/shell/app-breadcrumbs.js +18 -15
  199. package/dist/shell/app-header.module.css +77 -37
  200. package/dist/shell/app-header.module.css.d.ts +2 -1
  201. package/dist/shell/app-mobile-nav.d.ts +9 -2
  202. package/dist/shell/app-mobile-nav.d.ts.map +1 -1
  203. package/dist/shell/app-mobile-nav.js +98 -11
  204. package/dist/shell/app-mobile-nav.module.css +8 -0
  205. package/dist/shell/app-mobile-nav.module.css.d.ts +1 -0
  206. package/dist/shell/app-sidebar.d.ts +55 -28
  207. package/dist/shell/app-sidebar.d.ts.map +1 -1
  208. package/dist/shell/app-sidebar.js +116 -67
  209. package/dist/shell/app-sidebar.module.css +303 -20
  210. package/dist/shell/app-sidebar.module.css.d.ts +19 -1
  211. package/dist/shell/index.d.ts +55 -21
  212. package/dist/shell/index.d.ts.map +1 -1
  213. package/dist/shell/index.js +40 -18
  214. package/dist/shell/mobile-nav-model.d.ts +13 -2
  215. package/dist/shell/mobile-nav-model.d.ts.map +1 -1
  216. package/dist/shell/mobile-nav-model.js +15 -1
  217. package/dist/shell/nav-count-badge.d.ts +1 -1
  218. package/dist/shell/page-bar.d.ts +51 -0
  219. package/dist/shell/page-bar.d.ts.map +1 -0
  220. package/dist/shell/page-bar.js +154 -0
  221. package/dist/shell/page-bar.module.css +178 -0
  222. package/dist/shell/page-bar.module.css.d.ts +17 -0
  223. package/dist/shell/page-bar.test.d.ts +2 -0
  224. package/dist/shell/page-bar.test.d.ts.map +1 -0
  225. package/dist/shell/sidebar-block-model.d.ts +48 -0
  226. package/dist/shell/sidebar-block-model.d.ts.map +1 -0
  227. package/dist/shell/sidebar-block-model.js +39 -0
  228. package/dist/shell/sidebar-blocks.d.ts +32 -0
  229. package/dist/shell/sidebar-blocks.d.ts.map +1 -0
  230. package/dist/shell/sidebar-blocks.js +169 -0
  231. package/dist/shell/sidebar-search.d.ts +13 -1
  232. package/dist/shell/sidebar-search.d.ts.map +1 -1
  233. package/dist/shell/sidebar-search.js +34 -1
  234. package/dist/shell/sidebar-search.module.css +26 -0
  235. package/dist/shell/sidebar-search.module.css.d.ts +2 -0
  236. package/dist/state/fields.d.ts +392 -0
  237. package/dist/state/fields.d.ts.map +1 -0
  238. package/dist/state/fields.js +369 -0
  239. package/dist/state/fields.test.d.ts +2 -0
  240. package/dist/state/fields.test.d.ts.map +1 -0
  241. package/dist/state/persisted.d.ts +48 -0
  242. package/dist/state/persisted.d.ts.map +1 -0
  243. package/dist/state/persisted.js +105 -0
  244. package/dist/state.d.ts +11 -56
  245. package/dist/state.d.ts.map +1 -1
  246. package/dist/state.js +14 -120
  247. package/dist/styles.css +13 -0
  248. package/dist/surfaces.d.ts +106 -24
  249. package/dist/surfaces.d.ts.map +1 -1
  250. package/dist/surfaces.js +155 -19
  251. package/dist/theme/controls.module.css +16 -1
  252. package/dist/theme/ctl-theme.d.ts +32 -0
  253. package/dist/theme/ctl-theme.d.ts.map +1 -0
  254. package/dist/theme/ctl-theme.js +54 -0
  255. package/dist/theme/ctl-theme.test.d.ts +2 -0
  256. package/dist/theme/ctl-theme.test.d.ts.map +1 -0
  257. package/dist/theme/ctl-tier-coverage.test.d.ts +2 -0
  258. package/dist/theme/ctl-tier-coverage.test.d.ts.map +1 -0
  259. package/dist/theme/divider-law.test.d.ts +2 -0
  260. package/dist/theme/divider-law.test.d.ts.map +1 -0
  261. package/dist/theme/icon-slot.d.ts +44 -0
  262. package/dist/theme/icon-slot.d.ts.map +1 -0
  263. package/dist/theme/icon-slot.js +21 -0
  264. package/dist/theme/icon-slot.module.css +47 -0
  265. package/dist/theme/icon-slot.module.css.d.ts +8 -0
  266. package/dist/theme/icon-slot.test.d.ts +2 -0
  267. package/dist/theme/icon-slot.test.d.ts.map +1 -0
  268. package/dist/theme/index.d.ts +1 -0
  269. package/dist/theme/index.d.ts.map +1 -1
  270. package/dist/theme/index.js +82 -5
  271. package/dist/theme/layout-rhythm-css.test.d.ts +2 -0
  272. package/dist/theme/layout-rhythm-css.test.d.ts.map +1 -0
  273. package/dist/theme/segmented-control.module.css +20 -0
  274. package/dist/theme/shadow-surfaces.test.d.ts.map +1 -1
  275. package/dist/tokens/index.d.ts +12 -0
  276. package/dist/tokens/index.d.ts.map +1 -1
  277. package/dist/tokens/index.js +38 -8
  278. package/dist/tokens/palette.d.ts +97 -61
  279. package/dist/tokens/palette.d.ts.map +1 -1
  280. package/dist/tokens/palette.js +76 -38
  281. package/dist/tokens.css +10 -2
  282. package/dist/widget-header/delta-badge.d.ts +13 -0
  283. package/dist/widget-header/delta-badge.d.ts.map +1 -0
  284. package/dist/widget-header/delta-badge.js +29 -0
  285. package/dist/widget-header/delta-badge.test.d.ts +2 -0
  286. package/dist/widget-header/delta-badge.test.d.ts.map +1 -0
  287. package/dist/widget-header/index.d.ts +3 -0
  288. package/dist/widget-header/index.d.ts.map +1 -0
  289. package/dist/widget-header/index.js +6 -0
  290. package/dist/widget-header/no-mantine.test.d.ts +2 -0
  291. package/dist/widget-header/no-mantine.test.d.ts.map +1 -0
  292. package/dist/widget-header/widget-header.d.ts +37 -0
  293. package/dist/widget-header/widget-header.d.ts.map +1 -0
  294. package/dist/widget-header/widget-header.js +77 -0
  295. package/dist/widget-header/widget-header.module.css +335 -0
  296. package/dist/widget-header/widget-header.module.css.d.ts +24 -0
  297. package/dist/widget-header/widget-header.test.d.ts +2 -0
  298. package/dist/widget-header/widget-header.test.d.ts.map +1 -0
  299. package/llms.txt +30 -17
  300. package/package.json +14 -1
  301. package/src/charts/cursor/resolve.test.ts +29 -1
  302. package/src/charts/cursor/resolve.ts +38 -6
  303. package/src/charts/cursor/scope.tsx +6 -3
  304. package/src/charts/cursor/store.test.ts +19 -1
  305. package/src/charts/cursor/store.ts +10 -5
  306. package/src/charts/hooks/useChartCursor.ts +16 -8
  307. package/src/charts/index.ts +2 -2
  308. package/src/charts/primitives/CartesianChart.test.tsx +124 -0
  309. package/src/charts/primitives/ChartCard.test.tsx +66 -0
  310. package/src/charts/primitives/ChartCard.tsx +86 -146
  311. package/src/charts/sparklines/BarSparkline.tsx +43 -5
  312. package/src/cli/check-coverage.test.ts +297 -0
  313. package/src/cli/check-theme.test.ts +38 -14
  314. package/src/cli/index.ts +392 -16
  315. package/src/cli/init-sync.test.ts +97 -0
  316. package/src/connectivity/connectivity-indicator.tsx +16 -9
  317. package/src/content/index.ts +16 -3
  318. package/src/content/prose.module.css +7 -28
  319. package/src/content/prose.module.css.test.ts +8 -24
  320. package/src/controls/actions.module.css +62 -0
  321. package/src/controls/actions.module.css.d.ts +13 -0
  322. package/src/controls/actions.test.tsx +252 -0
  323. package/src/controls/actions.tsx +655 -0
  324. package/src/controls/actions.type-guard.test.ts +63 -0
  325. package/src/controls/compare-filter.tsx +64 -0
  326. package/src/controls/control-group.test.tsx +229 -0
  327. package/src/controls/control-group.tsx +63 -0
  328. package/src/controls/controls.module.css +542 -0
  329. package/src/controls/controls.module.css.d.ts +35 -0
  330. package/src/controls/controls.router.test.tsx +637 -0
  331. package/src/controls/enum-filter.tsx +159 -0
  332. package/src/controls/filter-context.tsx +153 -0
  333. package/src/controls/filter-pill.tsx +120 -0
  334. package/src/controls/filter-set.test.tsx +472 -0
  335. package/src/controls/filter-set.tsx +225 -0
  336. package/src/controls/filter-sheet.tsx +285 -0
  337. package/src/controls/glyphs.tsx +81 -0
  338. package/src/controls/index.ts +48 -0
  339. package/src/controls/multi-select-filter.tsx +140 -0
  340. package/src/controls/peer-boundary.test.ts +85 -0
  341. package/src/controls/props-contract.test.tsx +106 -0
  342. package/src/controls/range-filter.tsx +184 -0
  343. package/src/controls/search-filter.tsx +123 -0
  344. package/src/controls/select-filter.tsx +89 -0
  345. package/src/controls/sync-button.module.css +63 -0
  346. package/src/controls/sync-button.module.css.d.ts +11 -0
  347. package/src/controls/sync-button.test.tsx +183 -0
  348. package/src/controls/sync-button.tsx +197 -0
  349. package/src/controls/toggle-filter.tsx +60 -0
  350. package/src/controls/view-tabs.tsx +110 -0
  351. package/src/controls-dates/date-range-picker.tsx +42 -0
  352. package/src/controls-dates/index.ts +8 -0
  353. package/src/dashboard/delta-badge.tsx +4 -74
  354. package/src/dashboard/settings-section.module.css +10 -0
  355. package/src/dashboard/settings-section.module.css.d.ts +1 -0
  356. package/src/dashboard/settings-section.test.tsx +56 -0
  357. package/src/dashboard/settings-section.tsx +33 -30
  358. package/src/dashboard/stat-card.module.css +82 -0
  359. package/src/dashboard/stat-card.module.css.d.ts +11 -0
  360. package/src/dashboard/stat-card.test.tsx +67 -1
  361. package/src/dashboard/stat-card.tsx +209 -68
  362. package/src/data/data-table.test.tsx +96 -11
  363. package/src/data/data-table.tsx +220 -84
  364. package/src/guard/check-source.test.ts +126 -13
  365. package/src/guard/grace.test.ts +149 -0
  366. package/src/guard/index.ts +172 -29
  367. package/src/guard/types.ts +4 -2
  368. package/src/index.ts +46 -5
  369. package/src/nav/types.ts +84 -2
  370. package/src/notifications/bell.tsx +6 -3
  371. package/src/notifications/index.ts +1 -1
  372. package/src/router-tanstack/field.ts +31 -0
  373. package/src/router-tanstack/index.ts +24 -2
  374. package/src/router-tanstack/multi-search-param-store.test.ts +71 -3
  375. package/src/router-tanstack/multi-search-param-store.ts +46 -77
  376. package/src/router-tanstack/search-param-store.ts +42 -159
  377. package/src/router-tanstack/search-store.router.test.tsx +661 -0
  378. package/src/router-tanstack/search-store.test.ts +418 -0
  379. package/src/router-tanstack/search-store.ts +380 -0
  380. package/src/router-tanstack/use-nav.test.tsx +187 -0
  381. package/src/router-tanstack/use-nav.ts +72 -14
  382. package/src/section/index.ts +1 -0
  383. package/src/section/section.module.css +104 -0
  384. package/src/section/section.module.css.d.ts +11 -0
  385. package/src/section/section.test.tsx +204 -0
  386. package/src/section/section.tsx +220 -0
  387. package/src/shell/app-breadcrumbs.tsx +44 -31
  388. package/src/shell/app-header.module.css +77 -37
  389. package/src/shell/app-header.module.css.d.ts +2 -1
  390. package/src/shell/app-mobile-nav.module.css +8 -0
  391. package/src/shell/app-mobile-nav.module.css.d.ts +1 -0
  392. package/src/shell/app-mobile-nav.tsx +147 -8
  393. package/src/shell/app-sidebar-account.tsx +2 -2
  394. package/src/shell/app-sidebar.module.css +303 -20
  395. package/src/shell/app-sidebar.module.css.d.ts +19 -1
  396. package/src/shell/app-sidebar.test.tsx +456 -41
  397. package/src/shell/app-sidebar.tsx +276 -140
  398. package/src/shell/index.test.tsx +291 -5
  399. package/src/shell/index.tsx +137 -38
  400. package/src/shell/mobile-nav-model.test.ts +228 -7
  401. package/src/shell/mobile-nav-model.ts +53 -3
  402. package/src/shell/nav-count-badge.tsx +1 -1
  403. package/src/shell/page-bar.module.css +178 -0
  404. package/src/shell/page-bar.module.css.d.ts +17 -0
  405. package/src/shell/page-bar.test.tsx +316 -0
  406. package/src/shell/page-bar.tsx +315 -0
  407. package/src/shell/sidebar-block-model.ts +83 -0
  408. package/src/shell/sidebar-blocks.tsx +301 -0
  409. package/src/shell/sidebar-search.module.css +26 -0
  410. package/src/shell/sidebar-search.module.css.d.ts +2 -0
  411. package/src/shell/sidebar-search.tsx +71 -2
  412. package/src/state/fields.test.tsx +258 -0
  413. package/src/state/fields.ts +892 -0
  414. package/src/state/persisted.ts +211 -0
  415. package/src/state.ts +46 -240
  416. package/src/styles.css +13 -0
  417. package/src/surfaces.ts +203 -36
  418. package/src/theme/controls.module.css +16 -1
  419. package/src/theme/ctl-theme.test.tsx +275 -0
  420. package/src/theme/ctl-theme.tsx +138 -0
  421. package/src/theme/ctl-tier-coverage.test.ts +170 -0
  422. package/src/theme/density-relations.test.ts +106 -51
  423. package/src/theme/divider-law.test.ts +259 -0
  424. package/src/theme/icon-slot.module.css +47 -0
  425. package/src/theme/icon-slot.module.css.d.ts +8 -0
  426. package/src/theme/icon-slot.test.tsx +275 -0
  427. package/src/theme/icon-slot.tsx +59 -0
  428. package/src/theme/index.ts +165 -3
  429. package/src/theme/layout-rhythm-css.test.ts +72 -0
  430. package/src/theme/radius.test.ts +11 -1
  431. package/src/theme/segmented-control.module.css +20 -0
  432. package/src/theme/shadow-surfaces.test.ts +20 -7
  433. package/src/theme/spacing.test.ts +75 -27
  434. package/src/theme/use-basalt-spacing.ts +2 -2
  435. package/src/tokens/density.test.ts +7 -25
  436. package/src/tokens/index.ts +48 -8
  437. package/src/tokens/palette.ts +107 -73
  438. package/src/widget-header/delta-badge.test.tsx +60 -0
  439. package/src/widget-header/delta-badge.tsx +57 -0
  440. package/src/widget-header/index.ts +2 -0
  441. package/src/widget-header/no-mantine.test.ts +33 -0
  442. package/src/widget-header/widget-header.module.css +335 -0
  443. package/src/widget-header/widget-header.module.css.d.ts +24 -0
  444. package/src/widget-header/widget-header.test.tsx +187 -0
  445. package/src/widget-header/widget-header.tsx +168 -0
  446. package/agent/rules/basalt-agent.md +0 -452
  447. package/agent/rules/basalt-app.md +0 -246
  448. package/agent/rules/basalt-commands.md +0 -320
  449. package/agent/rules/basalt-content.md +0 -409
  450. package/agent/rules/basalt-data.md +0 -329
  451. package/agent/rules/basalt-forms.md +0 -186
  452. package/agent/rules/basalt-notifications.md +0 -264
  453. package/agent/rules/basalt-query.md +0 -187
  454. package/agent/rules/basalt-router.md +0 -336
  455. package/dist/content/article-filter-bar.d.ts +0 -40
  456. package/dist/content/article-filter-bar.d.ts.map +0 -1
  457. package/dist/content/article-filter-bar.js +0 -56
  458. package/dist/shell/page-header.d.ts +0 -13
  459. package/dist/shell/page-header.d.ts.map +0 -1
  460. package/dist/shell/page-header.js +0 -23
  461. package/src/content/article-filter-bar.tsx +0 -88
  462. package/src/shell/page-header.tsx +0 -35
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Section — the tier-2 heading composer (docs/CONTROLS-SPEC.md §2.2, law C8): a page-level content
3
+ * grouping under `WidgetHeader tier="section"`. Named exactly `Section` — not `PageSection` — so
4
+ * the existing `shadow-basalt-export` guard fires on a consumer's own hand-rolled `Section` /
5
+ * `PageSection` / `SectionTitle` / `SectionHeading` copy with zero new rule code.
6
+ *
7
+ * No `variant`, no border/background of its own — one shaded container level per page belongs to
8
+ * the cards inside a Section (`ChartCard`/`StatCard`), not to the Section itself.
9
+ *
10
+ * `actions` and `tabs` are two separate `CtlSlot`-wrapped fragments rendered in the header row
11
+ * (C1/C5) via `WidgetHeader`'s single `actions` slot; `tabs` hides while the section is collapsed.
12
+ * `Section` holds ≤3 actions (C6) — past that, a dev-only console warning fires (the AST guard
13
+ * lands in wave 6).
14
+ *
15
+ * `collapsible` state persists via `createPersistedState('basalt:section:<persistKey>')` when
16
+ * `persistKey` is given, else it is local `useState` — the header always stays drawn, only the body
17
+ * unmounts. `defaultOpen` picks the state a first visit lands on; a persisted value outranks it.
18
+ *
19
+ * `summary` renders under the header and survives a collapse. That is the whole reason it is a
20
+ * separate slot rather than the first child: a section a reader folded away should still state its
21
+ * headline figures, or folding it costs them the numbers they were watching.
22
+ *
23
+ * `id` turns the root into a scroll anchor, offset by both the shell header height and the
24
+ * `PageBar`'s measured height (`--basalt-page-bar-h`, wave 4) so an anchor-jumped heading isn't
25
+ * hidden under either sticky bar.
26
+ *
27
+ * @example
28
+ * import { Section, StatCard } from 'basalt-ui'
29
+ *
30
+ * <Section title="Revenue" icon={<IconChartBar size={16} />} count={rows.length}>
31
+ * <StatCard title="MRR" value="$48,204" delta={3.1} />
32
+ * </Section>
33
+ *
34
+ * @example
35
+ * // Collapsible, with tabs and a persisted fold state.
36
+ * <Section
37
+ * id="usage"
38
+ * title="Usage"
39
+ * collapsible
40
+ * persistKey="usage"
41
+ * tabs={<ViewTabs field={viewField} />}
42
+ * >
43
+ * <UsageChart />
44
+ * </Section>
45
+ */
46
+ import { Children, useId, useMemo, useState } from 'react'
47
+ import type { ReactNode } from 'react'
48
+ import { CtlSlot } from '../theme'
49
+ import { createPersistedState } from '../state'
50
+ import { WidgetHeader } from '../widget-header'
51
+ import type { WidgetHeaderProps } from '../widget-header'
52
+ import classes from './section.module.css'
53
+
54
+ export type SectionProps = Omit<WidgetHeaderProps, 'tier'> & {
55
+ /** Rendered in the header row, in its own `CtlSlot` — hidden while the section is collapsed. */
56
+ tabs?: ReactNode
57
+ /** Renders a chevron toggle in the header; the header stays drawn when closed, only `children`
58
+ * (and `tabs`) unmount. @default false */
59
+ collapsible?: boolean
60
+ /** Persists the fold state at `basalt:section:<persistKey>`. Omit for a local, unpersisted fold
61
+ * (`useState`). */
62
+ persistKey?: string
63
+ /** The fold state a section opens on, respected only while no persisted value exists — a section
64
+ * a reader has closed stays closed. `false` is what a long secondary block wants. @default true */
65
+ defaultOpen?: boolean
66
+ /** Rendered directly under the header and ALWAYS visible, collapsed or not: a collapsed section
67
+ * still states its headline figures, which is what makes collapsing it a real option. Keep it to
68
+ * one row — the body is what `children` is for. */
69
+ summary?: ReactNode
70
+ /** Turns the root into a scroll anchor (`id` + `scroll-margin-top` cleared for both sticky bars). */
71
+ id?: string
72
+ children: ReactNode
73
+ }
74
+
75
+ /** The house dev gate (`data/data-table.tsx`, `provider`, `charts/kinds/BandStrip` use the same
76
+ * expression): `basaltViteConfig` defines `process.env.NODE_ENV`, so a production bundle
77
+ * constant-folds this to `false` and drops the warning. Read per call, never hoisted. */
78
+ function isDev(): boolean {
79
+ return process.env['NODE_ENV'] !== 'production'
80
+ }
81
+
82
+ /** ≤3 actions is C6 (docs/CONTROLS-SPEC.md §1) — the AST guard (`basalt/page-bar-budget`) lands in
83
+ * wave 6; this is the dev-only stopgap so the budget is visible before the lint rule exists. */
84
+ function warnPastActionBudget(title: string, actions: ReactNode): void {
85
+ if (!isDev()) return
86
+ const count = Children.count(actions)
87
+ if (count <= 3) return
88
+ console.warn(
89
+ `Section "${title}": ${count} actions exceeds the ≤3 budget (docs/CONTROLS-SPEC.md C6) — ` +
90
+ 'move the rest behind a menu.',
91
+ )
92
+ }
93
+
94
+ const UNPERSISTED_KEY = '__local__'
95
+
96
+ /** Collapse state — `createPersistedState` when `persistKey` is given, else local `useState`.
97
+ * Both hooks are always called (stable order across renders); only the unpersisted branch's setter
98
+ * is ever invoked when `persistKey` is absent, so no unpersisted Section writes to storage. */
99
+ function useSectionOpen(
100
+ persistKey: string | undefined,
101
+ defaultOpen: boolean,
102
+ ): readonly [boolean, (next: boolean) => void] {
103
+ const [localOpen, setLocalOpen] = useState(defaultOpen)
104
+ const usePersistedOpen = useMemo(
105
+ () =>
106
+ createPersistedState<boolean>({
107
+ key: `section:${persistKey ?? UNPERSISTED_KEY}`,
108
+ version: 1,
109
+ initial: defaultOpen,
110
+ }),
111
+ [persistKey, defaultOpen],
112
+ )
113
+ const [persistedOpen, setPersistedOpen] = usePersistedOpen()
114
+
115
+ if (persistKey !== undefined) return [persistedOpen, setPersistedOpen] as const
116
+ return [localOpen, setLocalOpen] as const
117
+ }
118
+
119
+ function ChevronToggle({
120
+ open,
121
+ onToggle,
122
+ controls,
123
+ }: {
124
+ open: boolean
125
+ onToggle: () => void
126
+ controls: string
127
+ }) {
128
+ return (
129
+ <button
130
+ type="button"
131
+ className={classes.chevron}
132
+ data-open={open}
133
+ aria-expanded={open}
134
+ aria-controls={controls}
135
+ aria-label={open ? 'Collapse section' : 'Expand section'}
136
+ onClick={onToggle}
137
+ >
138
+ <svg
139
+ width={12}
140
+ height={12}
141
+ viewBox="0 0 24 24"
142
+ fill="none"
143
+ stroke="currentColor"
144
+ strokeWidth={2.5}
145
+ aria-hidden="true"
146
+ >
147
+ <path d="M6 9l6 6 6-6" strokeLinecap="round" strokeLinejoin="round" />
148
+ </svg>
149
+ </button>
150
+ )
151
+ }
152
+
153
+ export function Section({
154
+ title,
155
+ icon,
156
+ subtitle,
157
+ info,
158
+ value,
159
+ delta,
160
+ deltaPeriod,
161
+ sparkline,
162
+ count,
163
+ actions,
164
+ tabs,
165
+ collapsible = false,
166
+ persistKey,
167
+ defaultOpen = true,
168
+ summary,
169
+ id,
170
+ children,
171
+ }: SectionProps) {
172
+ const [open, setOpen] = useSectionOpen(persistKey, defaultOpen)
173
+ const bodyId = useId()
174
+
175
+ if (actions !== undefined) warnPastActionBudget(title, actions)
176
+
177
+ const headerActions =
178
+ tabs !== undefined || actions !== undefined || collapsible ? (
179
+ <>
180
+ {tabs !== undefined && open && <CtlSlot>{tabs}</CtlSlot>}
181
+ {actions !== undefined && <CtlSlot>{actions}</CtlSlot>}
182
+ {collapsible && (
183
+ <ChevronToggle open={open} onToggle={() => setOpen(!open)} controls={bodyId} />
184
+ )}
185
+ </>
186
+ ) : undefined
187
+
188
+ return (
189
+ <div
190
+ className={classes.root}
191
+ {...(id !== undefined && { id })}
192
+ {...(id !== undefined && {
193
+ style: {
194
+ scrollMarginTop:
195
+ 'calc(var(--app-shell-header-height, 0px) + var(--basalt-page-bar-h, 0px))',
196
+ },
197
+ })}
198
+ >
199
+ <WidgetHeader
200
+ tier="section"
201
+ title={title}
202
+ {...(icon !== undefined && { icon })}
203
+ {...(subtitle !== undefined && { subtitle })}
204
+ {...(info !== undefined && { info })}
205
+ {...(value !== undefined && { value })}
206
+ {...(delta !== undefined && { delta })}
207
+ {...(deltaPeriod !== undefined && { deltaPeriod })}
208
+ {...(sparkline !== undefined && { sparkline })}
209
+ {...(count !== undefined && { count })}
210
+ {...(headerActions !== undefined && { actions: headerActions })}
211
+ />
212
+ {summary !== undefined && <div className={classes.summary}>{summary}</div>}
213
+ {open && (
214
+ <div id={bodyId} className={classes.body}>
215
+ {children}
216
+ </div>
217
+ )}
218
+ </div>
219
+ )
220
+ }
@@ -14,6 +14,7 @@ import { Anchor, Group, Text } from '@mantine/core'
14
14
  import type { CSSProperties } from 'react'
15
15
  import type { NavAnchor } from '../nav/types'
16
16
  import { VX } from '../tokens'
17
+ import classes from './app-header.module.css'
17
18
 
18
19
  /** Parent/section crumbs — faint. */
19
20
  const crumbStyle: CSSProperties = { fontSize: VX.text.md, color: 'var(--vx-faint)' }
@@ -52,37 +53,49 @@ export function AppBreadcrumbs({
52
53
  if (!page) return null
53
54
  return (
54
55
  <Group gap={6} wrap="nowrap" style={{ minWidth: 0 }}>
55
- {section && (
56
- <>
57
- <Text style={crumbStyle} truncate>
58
- {section}
59
- </Text>
60
- <Text style={separatorStyle}>/</Text>
61
- </>
62
- )}
63
- {parent && parentAnchor && (
64
- <>
65
- <Anchor style={crumbStyle} underline="never" component={parentAnchor} truncate>
66
- {parent}
67
- </Anchor>
68
- <Text style={separatorStyle}>/</Text>
69
- </>
70
- )}
71
- {parent && !parentAnchor && parentHref && (
72
- <>
73
- <Anchor style={crumbStyle} underline="never" href={parentHref} truncate>
74
- {parent}
75
- </Anchor>
76
- <Text style={separatorStyle}>/</Text>
77
- </>
78
- )}
79
- {parent && !parentAnchor && !parentHref && (
80
- <>
81
- <Text style={crumbStyle} truncate>
82
- {parent}
83
- </Text>
84
- <Text style={separatorStyle}>/</Text>
85
- </>
56
+ {/*
57
+ The ANCESTOR crumbs, in one box so CSS can drop them below `sm` — law C9's swap, owned by the
58
+ component, no JS media query and no first-paint flash. On a phone the header row is the
59
+ scarcest space in the app and the breadcrumb is its one elastic side, so `Overview / Dashboard`
60
+ truncated to `O… / D…` — three crumbs' worth of separators and ellipses saying nothing. The
61
+ PAGE is the crumb that names where you are; the ancestors are navigation context, and the
62
+ sidebar (a tap away on the bottom bar) is where a phone reader looks for that.
63
+ */}
64
+ {(section !== undefined || parent !== undefined) && (
65
+ <Group gap={6} wrap="nowrap" className={classes.crumbAncestors} style={{ minWidth: 0 }}>
66
+ {section && (
67
+ <>
68
+ <Text style={crumbStyle} truncate>
69
+ {section}
70
+ </Text>
71
+ <Text style={separatorStyle}>/</Text>
72
+ </>
73
+ )}
74
+ {parent && parentAnchor && (
75
+ <>
76
+ <Anchor style={crumbStyle} underline="never" component={parentAnchor} truncate>
77
+ {parent}
78
+ </Anchor>
79
+ <Text style={separatorStyle}>/</Text>
80
+ </>
81
+ )}
82
+ {parent && !parentAnchor && parentHref && (
83
+ <>
84
+ <Anchor style={crumbStyle} underline="never" href={parentHref} truncate>
85
+ {parent}
86
+ </Anchor>
87
+ <Text style={separatorStyle}>/</Text>
88
+ </>
89
+ )}
90
+ {parent && !parentAnchor && !parentHref && (
91
+ <>
92
+ <Text style={crumbStyle} truncate>
93
+ {parent}
94
+ </Text>
95
+ <Text style={separatorStyle}>/</Text>
96
+ </>
97
+ )}
98
+ </Group>
86
99
  )}
87
100
  <Text style={currentStyle} truncate>
88
101
  {page}
@@ -1,7 +1,10 @@
1
- /* App-shell top bar layout. Desktop: a single 56px row —
2
- * [burger? · breadcrumb] ········ [page actions] | [global actions].
3
- * Mobile (< sm): wraps to two rows — lead + global on row 1, the page action row
4
- * (horizontally scrollable) on row 2. Header height is responsive to match. */
1
+ /* App-shell top bar layout. ONE 48px row on every viewport —
2
+ * [breadcrumb] ········ [page bar row 1] | [global actions].
3
+ *
4
+ * The pre-1.26.0 mobile layout wrapped to a second, always-reserved row for the page actions and
5
+ * scrolled it sideways. Both are gone (law C7 and law C14): the header height is now the
6
+ * `appShellHeaderHeight` token at every width, an empty page bar reserves nothing, and overflow
7
+ * folds into `ActionGroup`'s kebab instead of scrolling out of view. */
5
8
 
6
9
  /*
7
10
  * Transparent header (docs/DESIGN-SPEC.md §5): NO bottom rule at all. The header and the page body
@@ -16,58 +19,95 @@
16
19
  height: 100%;
17
20
  }
18
21
 
22
+ /*
23
+ * **Exactly ONE side of the header row is elastic, and it is the side whose content can ellipsize.**
24
+ * That is the whole rule, and both collisions this header has produced were the same violation of
25
+ * it from opposite directions.
26
+ *
27
+ * First the breadcrumb was `flex-shrink: 0`, so it won the whole width and the primary action
28
+ * rendered UNDERNEATH it. Then BOTH sides were made shrinkable (`.lead` at `0 1 auto`, `.pageBar` at
29
+ * `1 1 auto` with `min-width: 0` pushed onto its children) — and at 390px the controls, which cannot
30
+ * clip, shrank below their content and spilled: a flex container with `justify-content: flex-end`
31
+ * overflows towards its START, so the sync button painted 30px INTO the page title. Measured in
32
+ * Chrome: the title spanned 119.7–197.1 and the sync button 158.3–188.3.
33
+ *
34
+ * So the lead is now the elastic side (`flex: 1 1 auto`, `min-width: 0`, each crumb truncating via
35
+ * its own `truncate` prop) and the control side is rigid. A row of buttons that genuinely does not
36
+ * fit is not a shrinking problem — it is the OVERFLOW FOLD's problem, and basalt already owns that
37
+ * (law C7: below `sm` row 1 is the primary plus ONE kebab).
38
+ */
19
39
  .lead {
20
40
  display: flex;
21
41
  align-items: center;
22
42
  gap: var(--mantine-spacing-sm);
23
- flex-shrink: 0;
43
+ flex: 1 1 auto;
24
44
  min-width: 0;
25
45
  }
26
46
 
27
- .pageActions {
28
- flex: 1 1 auto;
47
+ /* No `overflow: hidden` here or on `.lead` above: Mantine's focus ring is `outline: 2px solid;
48
+ * outline-offset: 2px` — OUTSIDE the anchor box — and a wrapper clipping this tightly to the
49
+ * breadcrumb anchors clipped the ring on every edge, leaving the primary in-shell nav link with no
50
+ * visible focus indicator. Truncation is already owned by each crumb's own `truncate` prop
51
+ * (`app-breadcrumbs.tsx`, Mantine's `[data-truncate]` rule sets its own `overflow: hidden;
52
+ * text-overflow: ellipsis; white-space: nowrap` on the element that actually holds the text) — this
53
+ * wrapper only needs to let its children shrink. */
54
+ .lead * {
29
55
  min-width: 0;
30
- display: flex;
31
- align-items: center;
32
- justify-content: flex-end;
33
- gap: var(--mantine-spacing-xs);
34
- overflow-x: auto;
35
- scrollbar-width: thin;
36
56
  }
37
57
 
38
- /* Keep the whole control row on one horizontally-scrollable line. Mantine Group
39
- * defaults to flex-wrap:wrap, which otherwise stacks the nested control groups and
40
- * clips them against the slim bar's fixed height. */
41
- .pageActions :global(.mantine-Group-root) {
58
+ /*
59
+ * The `PageBar` row-1 portal target — the RIGID side of the row (see `.lead` above for the rule and
60
+ * for the two collisions that established it). Never a scroll container — see the header note above.
61
+ *
62
+ * `flex: 0 0 auto` and no `min-width: 0` anywhere below it, deliberately: a button cannot ellipsize,
63
+ * so letting this side shrink does not make it narrower, it makes it OVERFLOW — leftwards, into the
64
+ * breadcrumb, because of the `justify-content: flex-end` two lines down. The previous version pushed
65
+ * `min-width: 0` onto every child for exactly the reason that comment gave (a `wrap="nowrap"` group's
66
+ * automatic minimum size is its min-content width and flex will not go below it) — the reasoning was
67
+ * right and the conclusion was backwards: that floor is the row's protection, not its bug.
68
+ */
69
+ .pageBar {
70
+ flex: 0 0 auto;
71
+ display: flex;
42
72
  flex-wrap: nowrap;
73
+ align-items: center;
74
+ justify-content: flex-end;
75
+ gap: var(--vx-space-control-gap);
43
76
  }
44
77
 
78
+ /*
79
+ * The shell globals sit at the SAME 30px tier as everything else in the row, and they get there the
80
+ * same way a home's slot does — `HeaderGlobalActions` wraps this region in a `CtlSlot`, so a
81
+ * consumer's bare `<ActionIcon>` in `globalActions` defaults to `size="ctl"` with no prop at the call
82
+ * site (law C5). They rendered at Mantine's `md` 28px before, which is 2px under the page group
83
+ * beside them: close enough to look like a mistake rather than a tier.
84
+ *
85
+ * The gap to the page group is `.bar`'s own `sm`, so the globals read as a separate cluster; between
86
+ * themselves they use the control gap, like every other adjacent-control pair.
87
+ */
45
88
  .global {
46
89
  display: flex;
90
+ flex-wrap: nowrap;
47
91
  align-items: center;
48
- gap: var(--mantine-spacing-xs);
92
+ gap: var(--vx-space-control-gap);
49
93
  flex-shrink: 0;
50
94
  }
51
95
 
52
- @media (max-width: 47.99375em) {
53
- .bar {
54
- flex-wrap: wrap;
55
- align-content: center;
56
- }
57
-
58
- .lead {
59
- order: 1;
60
- flex: 1 1 auto;
61
- }
62
-
63
- .global {
64
- order: 2;
65
- }
96
+ .global :global(.mantine-ActionIcon-root) > svg {
97
+ width: 1rem;
98
+ height: 1rem;
99
+ }
66
100
 
67
- .pageActions {
68
- order: 3;
69
- flex-basis: 100%;
70
- height: var(--vx-space-app-header-mobile-actions-height);
71
- justify-content: flex-start;
101
+ /*
102
+ * The breadcrumb's ANCESTOR crumbs — hidden below `sm`. See `app-breadcrumbs.tsx` for why: at 390px
103
+ * the row's elastic side truncated `Overview / Dashboard` to `O… / D…`, which is two ellipses and a
104
+ * separator in place of the one word that says where the reader is.
105
+ *
106
+ * `47.99375em` is the `sm` breakpoint in the exact literal form every other basalt CSS module uses —
107
+ * a media query cannot resolve a custom property.
108
+ */
109
+ @media (max-width: 47.99375em) {
110
+ .crumbAncestors {
111
+ display: none;
72
112
  }
73
113
  }
@@ -5,7 +5,8 @@
5
5
  declare const classes: {
6
6
  readonly bar: string
7
7
  readonly lead: string
8
- readonly pageActions: string
8
+ readonly pageBar: string
9
9
  readonly global: string
10
+ readonly crumbAncestors: string
10
11
  }
11
12
  export default classes
@@ -252,6 +252,14 @@
252
252
  background-color: color-mix(in srgb, var(--vx-ink) 10%, transparent);
253
253
  }
254
254
 
255
+ /* Trailing meta on a block-item row (a timestamp, an owner) — the mobile twin of the sidebar's
256
+ * `.blockRowMeta`, so the same `SidebarBlockItem.meta` reads the same on both surfaces. */
257
+ .rowMeta {
258
+ flex: none;
259
+ font-size: var(--vx-text-micro);
260
+ color: var(--vx-faint);
261
+ }
262
+
255
263
  /*
256
264
  * Active = neutral surface fill + plain text color. Never the identity blue (DESIGN.md). --nl-color
257
265
  * forces NavLink's active text/icon color (otherwise the accent) to the plain text color. The
@@ -19,5 +19,6 @@ declare const classes: {
19
19
  readonly sheetHeader: string
20
20
  readonly row: string
21
21
  readonly rowNested: string
22
+ readonly rowMeta: string
22
23
  }
23
24
  export default classes