@nocoo/basalt 2.1.8 → 3.0.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 (352) hide show
  1. package/README.md +2 -0
  2. package/ai/COMPATIBILITY.md +11 -1
  3. package/ai/DESIGN.md +342 -0
  4. package/ai/INTEGRATION.md +239 -152
  5. package/ai/RECIPES.md +96 -26
  6. package/ai/USAGE.md +5 -5
  7. package/ai/registry.json +2883 -275
  8. package/dist/assets/brand-mark.js.map +1 -1
  9. package/dist/charts/area.js.map +1 -1
  10. package/dist/charts/bar.js.map +1 -1
  11. package/dist/charts/bullet.js.map +1 -1
  12. package/dist/charts/charts.js.map +1 -1
  13. package/dist/charts/config.d.ts +11 -11
  14. package/dist/charts/config.js +4 -4
  15. package/dist/charts/config.js.map +1 -1
  16. package/dist/charts/custom-chart.js.map +1 -1
  17. package/dist/charts/date-navigation.js +10 -10
  18. package/dist/charts/date-navigation.js.map +1 -1
  19. package/dist/charts/donut.js +1 -1
  20. package/dist/charts/donut.js.map +1 -1
  21. package/dist/charts/frame.d.ts +1 -1
  22. package/dist/charts/frame.js +4 -4
  23. package/dist/charts/frame.js.map +1 -1
  24. package/dist/charts/funnel.js.map +1 -1
  25. package/dist/charts/gauge.js +5 -5
  26. package/dist/charts/gauge.js.map +1 -1
  27. package/dist/charts/grouped-bar.js.map +1 -1
  28. package/dist/charts/heatmap-calendar.js +11 -11
  29. package/dist/charts/heatmap-calendar.js.map +1 -1
  30. package/dist/charts/heatmap-matrix.js +8 -8
  31. package/dist/charts/heatmap-matrix.js.map +1 -1
  32. package/dist/charts/item-list.js +1 -1
  33. package/dist/charts/item-list.js.map +1 -1
  34. package/dist/charts/legend.js +19 -19
  35. package/dist/charts/legend.js.map +1 -1
  36. package/dist/charts/line.js.map +1 -1
  37. package/dist/charts/palette.js +2 -2
  38. package/dist/charts/palette.js.map +1 -1
  39. package/dist/charts/radar.js.map +1 -1
  40. package/dist/charts/sample.js.map +1 -1
  41. package/dist/charts/sankey.js.map +1 -1
  42. package/dist/charts/series.js.map +1 -1
  43. package/dist/charts/slot-bar.js +2 -2
  44. package/dist/charts/slot-bar.js.map +1 -1
  45. package/dist/charts/sparkline.js +38 -23
  46. package/dist/charts/sparkline.js.map +1 -1
  47. package/dist/charts/stacked-bar.js.map +1 -1
  48. package/dist/charts/stat-card.d.ts +2 -2
  49. package/dist/charts/stat-card.js +61 -58
  50. package/dist/charts/stat-card.js.map +1 -1
  51. package/dist/charts/timeline.js +18 -9
  52. package/dist/charts/timeline.js.map +1 -1
  53. package/dist/charts/timeseries.js.map +1 -1
  54. package/dist/charts/tooltip.js +16 -16
  55. package/dist/charts/tooltip.js.map +1 -1
  56. package/dist/components/accordion.js +1 -1
  57. package/dist/components/accordion.js.map +1 -1
  58. package/dist/components/alert-dialog.js +4 -4
  59. package/dist/components/alert-dialog.js.map +1 -1
  60. package/dist/components/app-header.d.ts +5 -1
  61. package/dist/components/app-header.js +19 -11
  62. package/dist/components/app-header.js.map +1 -1
  63. package/dist/components/app-shell.d.ts +4 -1
  64. package/dist/components/app-shell.js +8 -5
  65. package/dist/components/app-shell.js.map +1 -1
  66. package/dist/components/approval-card.d.ts +13 -0
  67. package/dist/components/approval-card.js +161 -0
  68. package/dist/components/approval-card.js.map +1 -0
  69. package/dist/components/autocomplete.js.map +1 -1
  70. package/dist/components/avatar.d.ts +12 -0
  71. package/dist/components/avatar.js +39 -18
  72. package/dist/components/avatar.js.map +1 -1
  73. package/dist/components/badge.js +8 -8
  74. package/dist/components/badge.js.map +1 -1
  75. package/dist/components/banner.d.ts +2 -2
  76. package/dist/components/banner.js +9 -8
  77. package/dist/components/banner.js.map +1 -1
  78. package/dist/components/basalt-mark.js +1 -1
  79. package/dist/components/basalt-mark.js.map +1 -1
  80. package/dist/components/battery-meter.js +5 -5
  81. package/dist/components/battery-meter.js.map +1 -1
  82. package/dist/components/breadcrumbs.js +6 -6
  83. package/dist/components/breadcrumbs.js.map +1 -1
  84. package/dist/components/button.js +10 -10
  85. package/dist/components/button.js.map +1 -1
  86. package/dist/components/chat-bubble.js +4 -3
  87. package/dist/components/chat-bubble.js.map +1 -1
  88. package/dist/components/chat-composer.d.ts +21 -3
  89. package/dist/components/chat-composer.js +126 -60
  90. package/dist/components/chat-composer.js.map +1 -1
  91. package/dist/components/chat-header.js +6 -6
  92. package/dist/components/chat-header.js.map +1 -1
  93. package/dist/components/chat-inbox.js +34 -30
  94. package/dist/components/chat-inbox.js.map +1 -1
  95. package/dist/components/chat-markdown.d.ts +8 -0
  96. package/dist/components/chat-markdown.js +122 -0
  97. package/dist/components/chat-markdown.js.map +1 -0
  98. package/dist/components/chat-message.d.ts +26 -0
  99. package/dist/components/chat-message.js +145 -0
  100. package/dist/components/chat-message.js.map +1 -0
  101. package/dist/components/checkbox.js +11 -11
  102. package/dist/components/checkbox.js.map +1 -1
  103. package/dist/components/clipboard-text.js +3 -3
  104. package/dist/components/clipboard-text.js.map +1 -1
  105. package/dist/components/code.d.ts +22 -20
  106. package/dist/components/code.js +94 -32
  107. package/dist/components/code.js.map +1 -1
  108. package/dist/components/collapsible.js +8 -4
  109. package/dist/components/collapsible.js.map +1 -1
  110. package/dist/components/combobox.js.map +1 -1
  111. package/dist/components/command-palette.js +73 -47
  112. package/dist/components/command-palette.js.map +1 -1
  113. package/dist/components/confirm-dialog.d.ts +4 -1
  114. package/dist/components/confirm-dialog.js +20 -18
  115. package/dist/components/confirm-dialog.js.map +1 -1
  116. package/dist/components/context-cards.d.ts +15 -0
  117. package/dist/components/context-cards.js +148 -0
  118. package/dist/components/context-cards.js.map +1 -0
  119. package/dist/components/context-menu.d.ts +1 -1
  120. package/dist/components/context-menu.js +18 -14
  121. package/dist/components/context-menu.js.map +1 -1
  122. package/dist/components/data-table.d.ts +8 -2
  123. package/dist/components/data-table.js +161 -106
  124. package/dist/components/data-table.js.map +1 -1
  125. package/dist/components/date-picker.js +13 -13
  126. package/dist/components/date-picker.js.map +1 -1
  127. package/dist/components/delete-resource.js +1 -1
  128. package/dist/components/delete-resource.js.map +1 -1
  129. package/dist/components/description-list.js +3 -3
  130. package/dist/components/description-list.js.map +1 -1
  131. package/dist/components/dialog.d.ts +2 -2
  132. package/dist/components/dialog.js +8 -8
  133. package/dist/components/dialog.js.map +1 -1
  134. package/dist/components/diff-table.d.ts +15 -0
  135. package/dist/components/diff-table.js +146 -0
  136. package/dist/components/diff-table.js.map +1 -0
  137. package/dist/components/dock.js +2 -2
  138. package/dist/components/dock.js.map +1 -1
  139. package/dist/components/dropdown-menu.js +23 -18
  140. package/dist/components/dropdown-menu.js.map +1 -1
  141. package/dist/components/editable-nav-item.js +63 -58
  142. package/dist/components/editable-nav-item.js.map +1 -1
  143. package/dist/components/empty.js +6 -6
  144. package/dist/components/empty.js.map +1 -1
  145. package/dist/components/fab.js +1 -1
  146. package/dist/components/fab.js.map +1 -1
  147. package/dist/components/field.js +3 -3
  148. package/dist/components/field.js.map +1 -1
  149. package/dist/components/file-dropzone.js +7 -7
  150. package/dist/components/file-dropzone.js.map +1 -1
  151. package/dist/components/filter-bar.js +6 -6
  152. package/dist/components/filter-bar.js.map +1 -1
  153. package/dist/components/flow.js +4 -3
  154. package/dist/components/flow.js.map +1 -1
  155. package/dist/components/grid.d.ts +3 -1
  156. package/dist/components/grid.js +15 -8
  157. package/dist/components/grid.js.map +1 -1
  158. package/dist/components/hover-card.js +1 -1
  159. package/dist/components/hover-card.js.map +1 -1
  160. package/dist/components/icon-picker.d.ts +8 -2
  161. package/dist/components/icon-picker.js +75 -47
  162. package/dist/components/icon-picker.js.map +1 -1
  163. package/dist/components/inline-editable.js +6 -6
  164. package/dist/components/inline-editable.js.map +1 -1
  165. package/dist/components/input-area.js +3 -3
  166. package/dist/components/input-area.js.map +1 -1
  167. package/dist/components/input-group.js +5 -5
  168. package/dist/components/input-group.js.map +1 -1
  169. package/dist/components/input.js +4 -4
  170. package/dist/components/input.js.map +1 -1
  171. package/dist/components/label.js +3 -3
  172. package/dist/components/label.js.map +1 -1
  173. package/dist/components/layer-card.d.ts +9 -4
  174. package/dist/components/layer-card.js +72 -67
  175. package/dist/components/layer-card.js.map +1 -1
  176. package/dist/components/link.js.map +1 -1
  177. package/dist/components/loader.d.ts +16 -10
  178. package/dist/components/loader.js +46 -53
  179. package/dist/components/loader.js.map +1 -1
  180. package/dist/components/loading-screen.js +3 -3
  181. package/dist/components/loading-screen.js.map +1 -1
  182. package/dist/components/menu-bar.js +32 -27
  183. package/dist/components/menu-bar.js.map +1 -1
  184. package/dist/components/meter.d.ts +5 -5
  185. package/dist/components/meter.js +25 -12
  186. package/dist/components/meter.js.map +1 -1
  187. package/dist/components/multi-select.js +66 -62
  188. package/dist/components/multi-select.js.map +1 -1
  189. package/dist/components/navigation-menu.d.ts +2 -2
  190. package/dist/components/navigation-menu.js +22 -8
  191. package/dist/components/navigation-menu.js.map +1 -1
  192. package/dist/components/overlay.js +2 -2
  193. package/dist/components/overlay.js.map +1 -1
  194. package/dist/components/page-header.d.ts +3 -1
  195. package/dist/components/page-header.js +12 -12
  196. package/dist/components/page-header.js.map +1 -1
  197. package/dist/components/pagination.js +3 -3
  198. package/dist/components/pagination.js.map +1 -1
  199. package/dist/components/popover.js +18 -23
  200. package/dist/components/popover.js.map +1 -1
  201. package/dist/components/prompt-bar.d.ts +16 -0
  202. package/dist/components/prompt-bar.js +49 -0
  203. package/dist/components/prompt-bar.js.map +1 -0
  204. package/dist/components/radio.js +9 -9
  205. package/dist/components/radio.js.map +1 -1
  206. package/dist/components/recommendation-card.d.ts +13 -0
  207. package/dist/components/recommendation-card.js +123 -0
  208. package/dist/components/recommendation-card.js.map +1 -0
  209. package/dist/components/resource-list.js +1 -1
  210. package/dist/components/resource-list.js.map +1 -1
  211. package/dist/components/responsive-master-detail.js +7 -7
  212. package/dist/components/responsive-master-detail.js.map +1 -1
  213. package/dist/components/scroll-area.js +3 -3
  214. package/dist/components/scroll-area.js.map +1 -1
  215. package/dist/components/section-rule.d.ts +5 -3
  216. package/dist/components/section-rule.js +12 -12
  217. package/dist/components/section-rule.js.map +1 -1
  218. package/dist/components/segment-control.js +3 -3
  219. package/dist/components/segment-control.js.map +1 -1
  220. package/dist/components/select.js +47 -32
  221. package/dist/components/select.js.map +1 -1
  222. package/dist/components/sensitive-input.js +17 -19
  223. package/dist/components/sensitive-input.js.map +1 -1
  224. package/dist/components/separator.js.map +1 -1
  225. package/dist/components/sheet.d.ts +4 -4
  226. package/dist/components/sheet.js +12 -10
  227. package/dist/components/sheet.js.map +1 -1
  228. package/dist/components/sidebar.d.ts +3 -1
  229. package/dist/components/sidebar.js +150 -138
  230. package/dist/components/sidebar.js.map +1 -1
  231. package/dist/components/skeleton-line.d.ts +1 -1
  232. package/dist/components/skeleton-line.js +2 -2
  233. package/dist/components/skeleton-line.js.map +1 -1
  234. package/dist/components/slider.js +2 -2
  235. package/dist/components/slider.js.map +1 -1
  236. package/dist/components/stat-strip.js +5 -4
  237. package/dist/components/stat-strip.js.map +1 -1
  238. package/dist/components/switch.js +6 -6
  239. package/dist/components/switch.js.map +1 -1
  240. package/dist/components/table-of-contents.js +5 -5
  241. package/dist/components/table-of-contents.js.map +1 -1
  242. package/dist/components/table-pager.js +2 -2
  243. package/dist/components/table-pager.js.map +1 -1
  244. package/dist/components/table.js +4 -4
  245. package/dist/components/table.js.map +1 -1
  246. package/dist/components/tabs.js +7 -6
  247. package/dist/components/tabs.js.map +1 -1
  248. package/dist/components/tag-badge.d.ts +10 -10
  249. package/dist/components/tag-badge.js +11 -11
  250. package/dist/components/tag-badge.js.map +1 -1
  251. package/dist/components/tag-color-picker.d.ts +8 -1
  252. package/dist/components/tag-color-picker.js +33 -25
  253. package/dist/components/tag-color-picker.js.map +1 -1
  254. package/dist/components/text.js +5 -5
  255. package/dist/components/text.js.map +1 -1
  256. package/dist/components/theme-toggle.js +1 -1
  257. package/dist/components/theme-toggle.js.map +1 -1
  258. package/dist/components/thinking.d.ts +16 -0
  259. package/dist/components/thinking.js +85 -0
  260. package/dist/components/thinking.js.map +1 -0
  261. package/dist/components/toast.js +25 -20
  262. package/dist/components/toast.js.map +1 -1
  263. package/dist/components/toggle-group.js +6 -5
  264. package/dist/components/toggle-group.js.map +1 -1
  265. package/dist/components/toggle.js +6 -6
  266. package/dist/components/toggle.js.map +1 -1
  267. package/dist/components/tool-chips.d.ts +12 -0
  268. package/dist/components/tool-chips.js +138 -0
  269. package/dist/components/tool-chips.js.map +1 -0
  270. package/dist/components/toolbar.js +3 -3
  271. package/dist/components/toolbar.js.map +1 -1
  272. package/dist/components/tooltip.js +1 -1
  273. package/dist/components/tooltip.js.map +1 -1
  274. package/dist/components/typeahead-field.js +59 -56
  275. package/dist/components/typeahead-field.js.map +1 -1
  276. package/dist/components/upload-queue.js +15 -14
  277. package/dist/components/upload-queue.js.map +1 -1
  278. package/dist/models/agent-feedback.d.ts +44 -0
  279. package/dist/models/agent-feedback.js +28 -0
  280. package/dist/models/agent-feedback.js.map +1 -0
  281. package/dist/models/avatar.d.ts +2 -0
  282. package/dist/models/avatar.js +15 -0
  283. package/dist/models/avatar.js.map +1 -0
  284. package/dist/models/code.d.ts +6 -0
  285. package/dist/models/code.js +24 -0
  286. package/dist/models/code.js.map +1 -0
  287. package/dist/models/context-cards.d.ts +13 -0
  288. package/dist/models/context-cards.js +17 -0
  289. package/dist/models/context-cards.js.map +1 -0
  290. package/dist/models/diff-table.d.ts +12 -0
  291. package/dist/models/diff-table.js +9 -0
  292. package/dist/models/diff-table.js.map +1 -0
  293. package/dist/models/recommendation.d.ts +26 -0
  294. package/dist/models/recommendation.js +23 -0
  295. package/dist/models/recommendation.js.map +1 -0
  296. package/dist/providers/accent.js +1 -1
  297. package/dist/providers/accent.js.map +1 -1
  298. package/dist/providers/link.js.map +1 -1
  299. package/dist/providers/theme.js +1 -1
  300. package/dist/providers/theme.js.map +1 -1
  301. package/dist/styles/base.css +128 -0
  302. package/dist/styles/standalone.css +1994 -937
  303. package/dist/styles/tailwind.css +179 -13
  304. package/dist/styles/tokens.css +679 -20
  305. package/dist/utils/cn.js +25 -4
  306. package/dist/utils/cn.js.map +1 -1
  307. package/dist/utils/control-surface.d.ts +1 -1
  308. package/dist/utils/control-surface.js +1 -1
  309. package/dist/utils/control-surface.js.map +1 -1
  310. package/dist/utils/navigation.d.ts +3 -0
  311. package/dist/utils/navigation.js +8 -0
  312. package/dist/utils/navigation.js.map +1 -0
  313. package/dist/utils/preference-store.js +1 -1
  314. package/dist/utils/preference-store.js.map +1 -1
  315. package/dist/utils/selection-indicator.d.ts +1 -1
  316. package/dist/utils/selection-indicator.js +49 -72
  317. package/dist/utils/selection-indicator.js.map +1 -1
  318. package/dist/utils/use-checkable-group.js.map +1 -1
  319. package/dist/utils/use-hover-highlight.d.ts +4 -0
  320. package/dist/utils/use-hover-highlight.js +80 -0
  321. package/dist/utils/use-hover-highlight.js.map +1 -0
  322. package/dist/viewmodels/use-approval-card.d.ts +20 -0
  323. package/dist/viewmodels/use-approval-card.js +83 -0
  324. package/dist/viewmodels/use-approval-card.js.map +1 -0
  325. package/dist/viewmodels/use-chat-composer.d.ts +16 -0
  326. package/dist/viewmodels/use-chat-composer.js +34 -0
  327. package/dist/viewmodels/use-chat-composer.js.map +1 -0
  328. package/dist/viewmodels/use-chat-message.d.ts +16 -0
  329. package/dist/viewmodels/use-chat-message.js +26 -0
  330. package/dist/viewmodels/use-chat-message.js.map +1 -0
  331. package/dist/viewmodels/use-code-block.d.ts +8 -0
  332. package/dist/viewmodels/use-code-block.js +38 -0
  333. package/dist/viewmodels/use-code-block.js.map +1 -0
  334. package/dist/viewmodels/use-context-cards.d.ts +17 -0
  335. package/dist/viewmodels/use-context-cards.js +18 -0
  336. package/dist/viewmodels/use-context-cards.js.map +1 -0
  337. package/dist/viewmodels/use-diff-table.d.ts +16 -0
  338. package/dist/viewmodels/use-diff-table.js +42 -0
  339. package/dist/viewmodels/use-diff-table.js.map +1 -0
  340. package/dist/viewmodels/use-loader.d.ts +4 -0
  341. package/dist/viewmodels/use-loader.js +22 -0
  342. package/dist/viewmodels/use-loader.js.map +1 -0
  343. package/dist/viewmodels/use-recommendation-card.d.ts +17 -0
  344. package/dist/viewmodels/use-recommendation-card.js +41 -0
  345. package/dist/viewmodels/use-recommendation-card.js.map +1 -0
  346. package/dist/viewmodels/use-thinking.d.ts +13 -0
  347. package/dist/viewmodels/use-thinking.js +18 -0
  348. package/dist/viewmodels/use-thinking.js.map +1 -0
  349. package/dist/viewmodels/use-tool-chips.d.ts +10 -0
  350. package/dist/viewmodels/use-tool-chips.js +20 -0
  351. package/dist/viewmodels/use-tool-chips.js.map +1 -0
  352. package/package.json +3 -1
package/README.md CHANGED
@@ -158,3 +158,5 @@ For comprehensive architectural recipes, form adapters, and framework migration
158
158
  ## License
159
159
 
160
160
  [MIT](https://opensource.org/licenses/MIT)
161
+
162
+ Design contract: [ai/DESIGN.md](ai/DESIGN.md) (spacing, component categories, intrinsic sizes and motion).
@@ -1,5 +1,15 @@
1
1
  # Basalt Public API and Compatibility Policy
2
2
 
3
+ ## 3.0 migration
4
+
5
+ Loader now renders an inline span with an eight-cell perimeter grid, an optional shimmering label, and an elapsed timer shown after 5000ms. Use `showLabel={false} showElapsed={false}` for icon-only slots. `size` controls grid dimensions, not the entire row. SVG-specific attributes no longer apply; span attributes do. `animate`, `shimmer`, `showElapsed`, and `elapsedDelayMs` control presentation. Reduced motion stops both animations. The timer measures real elapsed time from mount, including time before it becomes visible, and is hidden from live announcements.
6
+
7
+ Component geometry and DOM styling are intentional major-version changes. Recheck custom selectors and visual snapshots against the current components rather than relying on former utility classes. Action rows are 34px; banners are 46px with 12px vertical and 16px horizontal insets. Compact control spacing remains separate from the 12/16/24/32px card and layout scale. ContentIsland owns page insets and card slots own card padding; remove duplicated wrapper padding rather than overriding shared control tokens.
8
+
9
+ IconPicker trigger names include the current value (`Folder icon: Folder`, then `Folder icon: Book`). Exact-name automation must follow that value; Field labels, validation and descriptions remain attached to the trigger. Standalone application recipes scope native-element resets with `basalt-ui` and explicitly focus mobile drawer openers before opening them. The catalog's visitor-badge login is an intentional layout exception, not a general application-shell template.
10
+
11
+ The historical export baseline below stays frozen. This major release documents actual DOM, geometry and presentation changes; it does not imply removal of those entrypoints or require a React major upgrade.
12
+
3
13
  This document defines the stability contracts, export architecture, and semantic versioning guarantees for `@nocoo/basalt`.
4
14
 
5
15
  ---
@@ -144,7 +154,7 @@ These notes cover the components, documentation, examples and palette changes in
144
154
 
145
155
  Chart gray retains its prior light/dark values. Marks use solid candy colors without contrasting outlines or shadows; their fills are not darkened. Raw candy fills can fall below 3:1 on light surfaces; use descriptive labels and `summary` / `dataAlternative` alongside them. Custom control palettes remain independent.
146
156
 
147
- Named Teal and Purple `Badge` variants use the Bondi and Grape control swatches. Solid color badges (`success`, `red`, `orange`, `teal`, `blue`, `purple`) keep white label text in both themes. Their backgrounds use `--basalt-badge-teal` and `--basalt-badge-purple`, keeping named badge hues independent of the numbered chart aliases.
157
+ Named Teal and Purple `Badge` variants retain the Bondi and Grape identities with dedicated light/dark background tokens. Solid color badges (`success`, `red`, `orange`, `teal`, `blue`, `purple`) now use paired semantic foreground tokens, including dark text on brighter dark-theme fills. Do not force white text or substitute raw chart colors for those background/foreground pairs.
148
158
 
149
159
  v2.1.0 includes the palette count and value changes above as an explicit exception to the usual minor-release compatibility policy. Consumers that index colors or persist palette choices must follow these migration notes. The unchanged export baseline alone does not prove runtime-value compatibility.
150
160
 
package/ai/DESIGN.md ADDED
@@ -0,0 +1,342 @@
1
+ # Basalt Design Contract
2
+
3
+ This is the authoritative design contract for the library, catalog and copyable
4
+ examples. [INTEGRATION.md](INTEGRATION.md) describes composition and public APIs.
5
+ All dimensions below are reference CSS pixels at a 16px root font, not device
6
+ pixels or fixed-height limits. Respect browser font preferences and zoom.
7
+
8
+ ## Visual direction
9
+
10
+ Simple, generous and worldly. Use precise alignment, confident type hierarchy,
11
+ matte material and readable proportions rather than scenic dashboard backgrounds,
12
+ decorative frames or empty hero heights. Reading pages and task interfaces have
13
+ different density; opening the page layout must not enlarge compact controls.
14
+
15
+ ## Ownership and units
16
+
17
+ - Shared design values belong in `packages/basalt/src/styles/tokens.css` and are
18
+ exposed identically by the Tailwind and standalone entrypoints. Components
19
+ consume tokens; examples must not repair a component through private selectors.
20
+ - Use `rem` for lengths, unitless line-height for text, `em`/`lh` for text-relative
21
+ geometry, and percentages, fractions and intrinsic sizing for layout. Do not
22
+ set the root font-size to a pixel value.
23
+ - DOM measurements, SVG coordinates, data/chart dimensions, aspect ratios and
24
+ caller-owned viewport constraints are geometry, not spacing. Measured CSS pixel
25
+ coordinates are allowed for overlays; never round them to the spacing scale.
26
+ - Model/ViewModel code owns data, state and transitions. Geometry measurement is
27
+ View-only; ViewModels do not import React Views or DOM APIs.
28
+
29
+ ## Spacing
30
+
31
+ | Step | Token | Value | Reference |
32
+ | --- | --- | --- | --- |
33
+ | Extra small | `--basalt-space-xs` | `.125rem` | 2px |
34
+ | Small | `--basalt-space-sm` | `.25rem` | 4px |
35
+ | Medium | `--basalt-space-md` | `.375rem` | 6px |
36
+ | Large / default | `--basalt-space-lg`, `--basalt-space-default` | `.5rem` | 8px |
37
+
38
+ These four steps belong to control content: labels, icons, fields, actions,
39
+ selectable rows, menus and banner content. They are not the maximum spacing for a page
40
+ or content surface. Card and layout boundaries use their own four-step roles:
41
+
42
+ | Step | Card token | Layout token | Value | Reference |
43
+ | --- | --- | --- | --- | --- |
44
+ | Small | `--basalt-space-card-sm` | `--basalt-space-layout-sm` | `.75rem` | 12px |
45
+ | Medium | `--basalt-space-card` | `--basalt-space-layout` | `1rem` | 16px |
46
+ | Large | `--basalt-space-card-lg` | `--basalt-space-layout-lg` | `1.5rem` | 24px |
47
+ | Extra large | `--basalt-space-card-xl` | `--basalt-space-layout-xl` | `2rem` | 32px |
48
+
49
+ Card padding defaults to medium. LayerCard header/footer insets are 16px horizontal
50
+ and 12px vertical; Body/Well use 16px. `padding="sm|md|lg|xl"` selects unstructured
51
+ card padding; structured cards keep their slot-owned insets. Compact composite
52
+ card panels use the same 16/12px panel aliases. Their internal buttons and rows
53
+ keep control spacing; do not stretch action rows to the card scale.
54
+
55
+ Layout grids default to medium (16px). ContentIsland owns 16px insets on mobile,
56
+ 24px on tablets and 24px vertical / 32px horizontal from 1024px. The catalog shell
57
+ keeps 8px outside the island on mobile and 12px from 768px. Ordinary application
58
+ sections use 24px gaps, including catalog documentation. SectionRule
59
+ uses 12px content separation for compact labels and 16px for reading headings. `Grid gap="sm|md|lg|xl"`
60
+ and semantic utilities such as `gap-basalt-layout-xl` select these tiers.
61
+ There is no universal height for either category.
62
+
63
+ Dialog and AlertDialog panels use 16px outer insets on mobile and 24px from
64
+ 640px. Their buttons and fields retain compact control spacing. Sheets and
65
+ small floating menus have separate inset contracts.
66
+
67
+ Metric cards use the shared `StatCard`, not a hand-built Header/Body split. A single
68
+ 16px inset owns the title, value, comparison and supporting chart; 12px separates
69
+ the heading, metric and content groups, and 4px separates value/supporting text.
70
+ Keep the comparison next to the metric in reading order, before the chart. Icons
71
+ are small unboxed heading cues, not a second painted surface. Values use tabular
72
+ display numerals and wrap naturally. Compact trend plots use 4rem geometry, not
73
+ `flex-1` to fill the card; analytical charts with axes remain separate surfaces.
74
+ Show the time window and disclose a nonzero line-chart scale. Bar charts keep
75
+ their zero baseline. Metric cards have no internal divider or decorative border.
76
+
77
+ All nonzero padding, margin and gaps use the appropriate category scale or aliases.
78
+ Zero, auto margins and hairline seams are structural exceptions, not new density
79
+ levels. Negative margins may cancel an owned boundary, not invent a new gap.
80
+ Use `p-basalt-card`, `gap-basalt-layout`, `gap-basalt-space-sm`, etc. Default nav
81
+ insets and field gaps remain 8px. Selectable rows use 8px horizontal / 6px vertical
82
+ insets and an 8px icon gap. One component owns each boundary: nested sections must
83
+ not accidentally double outer padding. Indented detail content may derive its
84
+ alignment from the row inset + icon width + gap; this is not another density tier.
85
+
86
+ Existing numbered `--basalt-space-*` tokens are a geometry scale for icons,
87
+ media and shell widths. They are not an alternative margin/padding scale.
88
+
89
+ ## Component categories and natural size
90
+
91
+ Choose a category before implementing a component. A composite's root and its
92
+ children can have different categories. Root module inventory:
93
+
94
+ | Category | Reference | Component modules |
95
+ | --- | --- | --- |
96
+ | Inline | 22px single line | `badge`, `tag-badge` |
97
+ | Action | 34px default | `button`, `input`, `select`, `toggle`, `toggle-group`, `segment-control`, `autocomplete`, `combobox`, `typeahead-field`, `sensitive-input`, `input-group`, `input-area`, `clipboard-text`, `inline-editable`, `multi-select`, `date-picker`, `icon-picker`, `tag-color-picker`, `theme-toggle`, `pagination`, `table-pager` |
98
+ | Banner | 38px single line | `banner`, `toast` |
99
+ | Card / content surface | Natural | `layer-card`, `approval-card`, `context-cards`, `recommendation-card`, `chat-bubble`, `code`, `empty`, `file-dropzone`, `resource-list`, `upload-queue` |
100
+ | Layout / composite | Natural | `accordion`, `collapsible`, `app-header`, `app-shell`, `sidebar`, `navigation-menu`, `editable-nav-item`, `breadcrumbs`, `page-header`, `section-rule`, `filter-bar`, `toolbar`, `grid`, `flow`, `field`, `description-list`, `table`, `data-table`, `diff-table`, `tabs`, `table-of-contents`, `responsive-master-detail`, `scroll-area`, `separator`, `stat-strip`, `chat-header`, `chat-inbox`, `chat-message`, `chat-markdown`, `chat-composer`, `prompt-bar`, `thinking`, `tool-chips`, `loading-screen` |
101
+ | Overlay composite | Natural; triggers/actions follow Action | `dialog`, `alert-dialog`, `confirm-dialog`, `delete-resource`, `sheet`, `dock`, `popover`, `hover-card`, `tooltip`, `dropdown-menu`, `context-menu`, `menu-bar`, `command-palette` |
102
+ | Graphic / text primitive | Content-specific | `avatar`, `basalt-mark`, `battery-meter`, `meter`, `loader`, `skeleton-line`, `text`, `label`, `link`, `checkbox`, `radio`, `switch`, `slider`, `fab` |
103
+
104
+ Graphic primitives preserve their intrinsic shape: a checkbox mark is not a
105
+ 34px square and an avatar is not a text input. Their containing action rows use
106
+ the Action standard. Inline links retain surrounding typography. Floating and
107
+ mobile shell actions may use the shared 44px touch minimum for accessibility.
108
+ Charts use data geometry; their toolbars still follow Action and spacing rules.
109
+
110
+ The live catalog splits Components into Inline, Actions, Banners, Cards, Layouts,
111
+ Overlays and Primitives. Each begins with an Overview covering design reasoning,
112
+ geometry, interaction and best practices at `/ui/overview/<category>` (singular
113
+ category IDs). Nonvisual theme/link providers live with Layouts. Charts and Blocks
114
+ retain separate navigation groups and their own overviews; blocks still inherit
115
+ the root categories in the inventory above. Existing `/ui/<component>` URLs do
116
+ not change. Catalog navigation, filters and discovery share
117
+ `src/pages/ui/catalog-categories.ts`; guide content is application documentation,
118
+ not a separate set of component tokens or APIs.
119
+
120
+ The shared classes calculate natural size as follows:
121
+
122
+ - `basalt-inline`: 12px font, 16px line box + 2px padding each side + 1px border
123
+ each side = 22px. Text wraps/grows rather than being clipped.
124
+ - `basalt-action`: 14px font, 24px line box + 4px vertical padding each side +
125
+ 1px border each side = 34px. Explicit `sm` (28px) and `lg` (40px) are shared
126
+ size variants, not ad-hoc height utilities. Icon-only actions use a minimum
127
+ derived from `1lh`, not a fixed height; icons are normally 14px.
128
+ - `basalt-banner`: 14px font, 22px line box + 12px vertical padding = 46px.
129
+ The outer boundary uses 16px horizontal / 12px vertical panel insets; internal
130
+ icons and actions retain compact control spacing.
131
+ Descriptions, wrapping and actions can make a banner taller.
132
+ - Action rows: 22px text line (`--basalt-line-row`) + 6px vertical padding each
133
+ side = 34px. Body prose/code retain the 20px line box. Compound inputs own one
134
+ outer border; inset children do not add another. Segmented tracks keep 12px text
135
+ on a 20px line box: 30px items + 2px track inset per side = 34px.
136
+ - Cards, dialogs and layout containers have no mandatory height. Textareas and
137
+ chat inputs grow with content (chat: up to five lines, then scroll).
138
+
139
+ Do not use `height`, `max-height`, truncation or overflow clipping to force text
140
+ controls to a target. Use a line box, shared padding and optional minimums.
141
+ Constrain scroll regions, not their individual text rows. Long labels, loading
142
+ indicators, validation text and 200% text zoom must remain usable.
143
+
144
+ Reading surfaces such as code panels contain horizontal overflow without trapping
145
+ vertical page scrolling. Use axis-specific overscroll: contain X, allow Y to chain.
146
+ Natural-height code leaves vertical scrolling to its ancestor; height-constrained
147
+ code scrolls internally and hands off at its top/bottom boundary. Keep native wheel
148
+ and touch behavior instead of intercepting events or converting vertical deltas
149
+ into horizontal movement. Modal scroll locks and editable text inputs have separate
150
+ interaction contracts; do not change their containment as a global workaround.
151
+
152
+ ## Radius, typography and color
153
+
154
+ - Radius choices: `--basalt-radius-sm` (.25rem), `-md` (.5rem), `-lg` (.75rem)
155
+ and `-full` (pill/circle); `none` and `inherit` are structural choices. Widget,
156
+ card and island aliases reference those choices; no per-component radii.
157
+ - Typography uses `--basalt-text-*` and `--basalt-leading-*`. Ordinary text is
158
+ at least 11px reference, body/action 14px, code content 12px (`text-basalt-sm`),
159
+ code panel titles 13px (`text-basalt-code`). Display headings use
160
+ the shared larger scale. Do not shrink text to fit a fixed box.
161
+ - Surfaces preserve the matte L0/L1/L2/L3 hierarchy. Controls use the
162
+ surface-relative control fill. Hover uses `--basalt-hover`,
163
+ `--basalt-primary-hover` or `--basalt-destructive-hover`, not caller opacity.
164
+ - Semantic states, tags, data series and brand imagery may choose semantic
165
+ palettes; keep foreground/background pairs and verify both themes. Centralize
166
+ shared values rather than declaring hex colors in component classes. Site-only
167
+ scenery tokens live in `src/styles/site-tokens.css`, not in reusable controls.
168
+
169
+ ## Selection and navigation hierarchy
170
+
171
+ - Neutral selection uses `--basalt-selected` and `--basalt-selected-foreground`.
172
+ Selected surfaces are brighter than their container (white in light mode, 22%
173
+ lightness in dark mode), never the darker decorative `accent` or `muted` fill.
174
+ Do not add decorative side bars, selection outlines, shadows or pseudo-element
175
+ frames. Navigation uses foreground contrast and medium weight alongside fill;
176
+ existing semantic checkmarks and tab underlines remain. Both primary and
177
+ secondary text retain at least 4.5:1 contrast.
178
+ - Hover is transient and follows the configured primary. `--basalt-hover` and
179
+ `--basalt-hover-end` are complete CSS colors, mixed against the theme popover
180
+ surface at 9.6%/4.8% in light mode and 16%/9.6% in dark mode. Moving menu and command
181
+ highlights use a subtle gradient between them, without decorative borders.
182
+ SegmentControl and ToggleGroup selected items use `--basalt-primary` and its
183
+ contrast-corrected foreground; they are accent controls, not neutral navigation.
184
+ Neutral row selection retains its independent selected tokens.
185
+ Selection remains painted on its own row when the shared hover layer moves
186
+ elsewhere. The layer animates transforms, not a second React state per item.
187
+ Multi-selection paints every selected row, not only the current hover target.
188
+ Typeahead and command active descendants are transient candidates, not committed
189
+ choices; retain their moving layer instead of painting an instant second fill.
190
+ - Use the component's `active`, `selected`, `value` or `pressed` API. Neutral
191
+ Button variants honor `aria-pressed` / `aria-current`; application controls must
192
+ expose that state instead of switching to a gray secondary variant alone.
193
+ Checkbox/radio/switch glyphs, primary/destructive actions and semantic diff
194
+ colors keep their semantic fills. Color swatches retain their actual palette.
195
+ - Keyboard focus is separate from selection. Navigation uses an inset focus ring
196
+ so overflow and collapsed groups cannot clip it. Disabled controls retain
197
+ subdued opacity and cannot become moving-hover targets; selection never lowers
198
+ opacity or mutes its primary label.
199
+ - SidebarPartition is a major section label (12px, semibold, uppercase, 16px top
200
+ spacing). SidebarGroup is a compact disclosure label (11px, semibold, uppercase,
201
+ 8px inset, 12px group separation), not a destination or hover-highlight target.
202
+ Ordinary destinations use 14px text and the shared 8px/6px row insets. Groups
203
+ preserve keyboard expansion and chevron motion; collapsed rails omit labels.
204
+ - Content wells use the actual surface hierarchy, not selected tokens or muted
205
+ fills. Skeletons, progress tracks and decorative code marks may remain subdued.
206
+
207
+ ## Motion and performance
208
+
209
+ Motion is on by default and owned by the component, never by a required example
210
+ prop. Use shared durations (120 / 180 / 280ms) and nonlinear
211
+ `cubic-bezier(.22, 1, .36, 1)` for interaction. Portal enter/exit and sheet motion
212
+ have dedicated shared duration/easing tokens. Continuous progress spinners may
213
+ use linear motion; decorative motion pauses off-screen/in background tabs.
214
+
215
+ - Prefer transform and opacity. Rotate disclosure chevrons and smoothly reveal
216
+ content with the shared Radix measured-height animations. Avoid transition-all,
217
+ animated blur on every hovered row, permanent will-change, or a timer per row.
218
+ - One moving hover background per list. Coalesce reads into one animation frame,
219
+ do not remeasure the same row for pointer movement, and write geometry without
220
+ React rerenders. Nested lists are isolated. Disabled and hidden rows cannot
221
+ become targets; keyboard selection/focus works independently of pointer hover.
222
+ - Selection indicators snap to the first valid measured selection before paint.
223
+ Never animate from `(0, 0)`, including controlled initial values, non-first
224
+ segments, remounts or initially hidden panels. Animate only between established
225
+ user selections. Resize/font/layout changes snap; redundant observer delivery
226
+ must not interrupt an in-flight selection animation.
227
+ - Do not observe the moving highlight as content or respond to its own transition
228
+ completion. Disconnect observers/listeners and cancel frames on unmount.
229
+ - `prefers-reduced-motion: reduce` disables spatial motion, entrance effects and
230
+ shimmer without hiding content or delaying access. Keep exit nodes mounted
231
+ through the primitive's closing state, and preserve focus restoration.
232
+
233
+ ## Page composition
234
+
235
+ Application pages, including every catalog category overview, use the same
236
+ composition: `ContentIsland` owns the outside inset, `PageHeader` owns the page
237
+ title/actions/filters, `SectionRule` owns region headings, and `LayerCard` owns
238
+ content surfaces. The catalog's app-local `ShowcasePage` composes the header and
239
+ section gap. All routes under `DashboardLayout` use it, including missing/planned
240
+ catalog states and source viewers. Do not add a second `main`, viewport height,
241
+ page background, wrapper card or page padding inside the island. A bounded chat
242
+ workspace can use `h-full min-h-0`; it must not create a nested viewport.
243
+
244
+ Every catalog route uses the same ShowcasePage header: 30px display titles,
245
+ 16px descriptions on a 24px line box, 8px title separation and a 65ch maximum
246
+ reading width. Page sections use 24px separation regardless of route category.
247
+ The standalone PageHeader API retains its size choices for consumers, but catalog
248
+ pages do not override the shared scale. ShowcasePage marks `variant="document"`
249
+ for the library index, category guides, component documentation and source viewers.
250
+ The shell owns the complete breadcrumb trail from Dashboard through the library,
251
+ category and component to the current route. Ancestors are links; the current
252
+ route appears once as nonlinked text with aria-current. Source views include their
253
+ component parent. Narrow headers keep every segment reachable by scrolling the
254
+ breadcrumb region without overflowing the page.
255
+ Document sections use `SectionRule variant="heading"`: 20px sentence-case
256
+ headings without the compact dashed rule. Component previews retain their own
257
+ scope; document typography must not cascade into copied controls. Headers have
258
+ natural height, no scenic wrappers, negative insets or fixed empty openings.
259
+
260
+ Scrollable gallery previews reserve 8px of internal space on every edge for
261
+ control rings, focus outlines and shadows. Padding outside the scroll viewport
262
+ does not protect that paint. Center previews safely so oversized content keeps
263
+ its leading edge reachable.
264
+ The component library uses one LayerCard per category, separated by 24px. Each
265
+ category uses LayerCard.Header above its grid, not individual cards per component.
266
+ Its light surface is white-toned; dark mode uses the same surface hierarchy rather
267
+ than a fixed white fill.
268
+
269
+ Cards with headings use `LayerCard.Header` and `LayerCard.Body`; use one Body for
270
+ related content with a shared internal gap, not a padded Body for every line.
271
+ Structured roots and slots must not receive extra outer padding or margins.
272
+ Unstructured cards select `padding` rather than overriding it with utilities.
273
+ Do not paint a fixed surface onto LayerCard or recreate cards with rounded,
274
+ painted divs. Use library controls and tables instead of rebuilding their spacing,
275
+ focus, selection and motion in page CSS. Semantic color overrides and purposeful
276
+ chart/canvas geometry remain separate from shared control geometry.
277
+
278
+ ### Container boundary ownership
279
+
280
+ Choose the boundary before choosing a spacing token. A valid token on the wrong
281
+ owner is still a design error:
282
+
283
+ | Boundary | Owner | Composition |
284
+ | --- | --- | --- |
285
+ | Page/region separation | `ShowcasePage` / `SectionRule` | Layout gaps, no second page inset |
286
+ | Painted content or navigation panel | `LayerCard` | Header and Body slots, never an unpadded painted div |
287
+ | Full-width disclosure heading | `LayerCard.Header asChild` | Slot onto `CollapsibleTrigger`; one hit target, 16/12px insets |
288
+ | Expanded prose or fields | `LayerCard.Body` | Inside `CollapsibleContent unstyled`; one 16px inset |
289
+ | Attached code | `CodeBlock attached` / `CodeHighlighted attached` | Direct child of unstyled disclosure; code owns its 16/12px insets |
290
+ | Edge-to-edge table/list | Its row/cell components | Direct card child; no Body with `p-0` or compensating margins |
291
+ | Compact status/action strip | `Banner` | Control spacing, not a hand-drawn card |
292
+
293
+ Collapsible owns visibility, focus semantics and motion; it does not add another
294
+ surface. Its default inset is for plain standalone text. Use `unstyled` when a
295
+ child already owns spacing. Compose a full collapsible card as `Collapsible asChild`
296
+ around `LayerCard`, keeping Header directly under the card so slot detection works.
297
+ Never bury all card slots inside an opaque wrapper and rely on inferred padding.
298
+
299
+ Do not use native details/summary for dashboard or catalog disclosures; they bypass
300
+ the shared inset and motion contracts. The independent public landing FAQ remains
301
+ a deliberate no-JavaScript exception. Example preview, disclosure label, code title
302
+ and source text must align to the same horizontal inset. Attached code selects a
303
+ public prop, not caller border/radius/padding overrides. Sticky placement wrappers
304
+ own only position; their panel owns its background and inset.
305
+
306
+ Landing, login, loading, error, static documents and full-page layout recipes keep
307
+ their independent root contracts; do not apply dashboard chrome to them. The landing
308
+ route additionally owns its marketing spacing, radius and display type scale, which
309
+ the compact global tiers cannot express; its CSS is exempt from those token rules and
310
+ keeps motion tokens only. Explicit
311
+ specialist controls remain in the palette demos: color swatches and native color
312
+ pickers display the colors being edited. Hidden file inputs and theme-preview
313
+ radio inputs preserve native form behavior. The catalog Login visitor badge keeps
314
+ its accepted independent 54:86 artwork composition, bespoke spacing/type/radii,
315
+ provider-button padding and reserved footer clearance. Do not normalize it to
316
+ dashboard/control tiers. The exception is scoped to `src/pages/LoginPage.tsx`;
317
+ motion, intrinsic action sizing and library-control requirements still apply.
318
+ These exceptions are not permission
319
+ to recreate ordinary actions, selects or inputs.
320
+
321
+ ## Enforcement and acceptance
322
+
323
+ `bun run design:check` scans library components/helpers and catalog examples,
324
+ including CSS, for raw spacing/type/radius/motion values, fixed action overrides
325
+ and inline design repairs. The landing route's own CSS is exempt from the spacing,
326
+ radius and type rules and still keeps motion tokens. It derives dashboard page requirements from App routes
327
+ and checks template ownership, duplicate slot spacing, recreated card surfaces,
328
+ ordinary native controls in pages and page-level control padding overrides.
329
+ It rejects compact control padding on content panels, raw application disclosures,
330
+ duplicate disclosure/body insets, zero-padding card-slot repairs and code-frame CSS
331
+ overrides. Navigation rows, tooltips and decorative marks retain their own roles.
332
+ Structural checks inspect JSX, not illustrative source strings. It also checks
333
+ this category inventory. It runs in
334
+ `.husky/pre-push`, **not** in dev, build or pre-commit. Unit tests exercise isolated
335
+ rule fixtures only; they do not run the full repository scan.
336
+
337
+ Static checking is a guard, not proof of appearance. Acceptance also requires
338
+ Tailwind and standalone builds, strict types/lint, unit coverage, keyboard and
339
+ reduced-motion behavior, both themes, narrow screens, wrapped text and enlarged
340
+ root fonts. Browser tests measure actual geometry and animation state; changing
341
+ class-name assertions alone does not certify visual correctness. Do not claim
342
+ browser acceptance when the environment cannot run it.