banhaten 0.1.2 → 0.2.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 (365) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/CONTRIBUTING.md +44 -0
  3. package/README.md +145 -328
  4. package/SECURITY.md +15 -0
  5. package/banhaten.config.example.json +1 -3
  6. package/docs/design-system/README.md +20 -0
  7. package/docs/design-system/adoption-quality-remediation.md +87 -0
  8. package/docs/design-system/agent-usage.md +13 -0
  9. package/docs/design-system/appearance-presets.md +189 -0
  10. package/docs/design-system/appearances/default.md +94 -0
  11. package/docs/design-system/appearances/rounded.md +95 -0
  12. package/docs/design-system/appearances/sharp.md +95 -0
  13. package/docs/design-system/component-docs-pattern.md +81 -0
  14. package/docs/design-system/component-showcase-consistency-report.md +217 -0
  15. package/docs/design-system/component-token-consistency-audit.md +173 -0
  16. package/docs/design-system/components/README.md +75 -0
  17. package/docs/design-system/components/accordion.md +67 -0
  18. package/docs/design-system/components/activity-feed.md +106 -0
  19. package/docs/design-system/components/alert-dialog.md +87 -0
  20. package/docs/design-system/components/alert.md +111 -0
  21. package/docs/design-system/components/aspect-ratio.md +59 -0
  22. package/docs/design-system/components/attribute.md +103 -0
  23. package/docs/design-system/components/autocomplete.md +93 -0
  24. package/docs/design-system/components/avatar.md +70 -0
  25. package/docs/design-system/components/badge.md +72 -0
  26. package/docs/design-system/components/banner.md +99 -0
  27. package/docs/design-system/components/breadcrumbs.md +188 -0
  28. package/docs/design-system/components/button-group.md +117 -0
  29. package/docs/design-system/components/button.md +95 -0
  30. package/docs/design-system/components/card.md +95 -0
  31. package/docs/design-system/components/carousel.md +61 -0
  32. package/docs/design-system/components/catalog-components.md +59 -0
  33. package/docs/design-system/components/chart.md +58 -0
  34. package/docs/design-system/components/checkbox.md +68 -0
  35. package/docs/design-system/components/collapsible.md +63 -0
  36. package/docs/design-system/components/command.md +75 -0
  37. package/docs/design-system/components/context-menu.md +59 -0
  38. package/docs/design-system/components/date-picker.md +97 -0
  39. package/docs/design-system/components/divider.md +117 -0
  40. package/docs/design-system/components/empty-state.md +72 -0
  41. package/docs/design-system/components/field.md +86 -0
  42. package/docs/design-system/components/file-upload.md +199 -0
  43. package/docs/design-system/components/hover-card.md +61 -0
  44. package/docs/design-system/components/icons.md +48 -0
  45. package/docs/design-system/components/input-group.md +73 -0
  46. package/docs/design-system/components/input-otp.md +73 -0
  47. package/docs/design-system/components/input.md +84 -0
  48. package/docs/design-system/components/item.md +72 -0
  49. package/docs/design-system/components/kbd.md +62 -0
  50. package/docs/design-system/components/label.md +64 -0
  51. package/docs/design-system/components/menu.md +76 -0
  52. package/docs/design-system/components/menubar.md +60 -0
  53. package/docs/design-system/components/modal.md +119 -0
  54. package/docs/design-system/components/native-select.md +70 -0
  55. package/docs/design-system/components/navigation-menu.md +63 -0
  56. package/docs/design-system/components/onboarding-step-list-item.md +94 -0
  57. package/docs/design-system/components/page-header.md +98 -0
  58. package/docs/design-system/components/pagination.md +71 -0
  59. package/docs/design-system/components/popover.md +77 -0
  60. package/docs/design-system/components/progress-slider.md +66 -0
  61. package/docs/design-system/components/progress.md +92 -0
  62. package/docs/design-system/components/radio-card.md +65 -0
  63. package/docs/design-system/components/radio-group.md +72 -0
  64. package/docs/design-system/components/resizable.md +57 -0
  65. package/docs/design-system/components/scroll-area.md +60 -0
  66. package/docs/design-system/components/segmented-control.md +71 -0
  67. package/docs/design-system/components/select.md +87 -0
  68. package/docs/design-system/components/sheet.md +82 -0
  69. package/docs/design-system/components/sidebar.md +83 -0
  70. package/docs/design-system/components/skeleton.md +91 -0
  71. package/docs/design-system/components/slideout.md +77 -0
  72. package/docs/design-system/components/slider.md +81 -0
  73. package/docs/design-system/components/social-button.md +65 -0
  74. package/docs/design-system/components/spinner.md +77 -0
  75. package/docs/design-system/components/steps.md +77 -0
  76. package/docs/design-system/components/table.md +411 -0
  77. package/docs/design-system/components/tabs.md +69 -0
  78. package/docs/design-system/components/tag.md +95 -0
  79. package/docs/design-system/components/textarea.md +64 -0
  80. package/docs/design-system/components/timeline.md +95 -0
  81. package/docs/design-system/components/toast.md +73 -0
  82. package/docs/design-system/components/toggle.md +96 -0
  83. package/docs/design-system/components/toolbar.md +105 -0
  84. package/docs/design-system/components/tooltip.md +107 -0
  85. package/docs/design-system/components/typography.md +18 -0
  86. package/docs/design-system/density-and-elevation.md +44 -0
  87. package/docs/design-system/design-system-test-missing-items.md +368 -0
  88. package/docs/design-system/direction-and-localization.md +23 -0
  89. package/docs/design-system/form-recipes.md +73 -0
  90. package/docs/design-system/gap-promotion-policy.md +21 -0
  91. package/docs/design-system/gap-promotion-report.md +166 -0
  92. package/docs/design-system/icons.md +69 -0
  93. package/docs/design-system/installation.md +68 -0
  94. package/docs/design-system/launch-comparison-shadcn.md +98 -0
  95. package/docs/design-system/registry-and-cli.md +64 -0
  96. package/docs/design-system/release-policy.md +30 -0
  97. package/docs/design-system/roadmap.md +14 -0
  98. package/docs/design-system/support-matrix.md +23 -0
  99. package/docs/design-system/tabs.md +53 -0
  100. package/docs/design-system/token-governance.md +60 -0
  101. package/package.json +97 -65
  102. package/registry/component-contract.json +40 -0
  103. package/registry/components/alert-dialog.tsx +297 -0
  104. package/registry/components/alert.tsx +86 -65
  105. package/registry/components/aspect-ratio.tsx +30 -0
  106. package/registry/components/autocomplete.tsx +23 -9
  107. package/registry/components/avatar.tsx +7 -7
  108. package/registry/components/badge.tsx +1 -1
  109. package/registry/components/button-group.tsx +321 -4
  110. package/registry/components/button.tsx +38 -17
  111. package/registry/components/card.tsx +68 -59
  112. package/registry/components/carousel.tsx +282 -0
  113. package/registry/components/chart.tsx +203 -0
  114. package/registry/components/checkbox.tsx +3 -3
  115. package/registry/components/collapsible.tsx +69 -0
  116. package/registry/components/command.tsx +174 -0
  117. package/registry/components/context-menu.tsx +236 -0
  118. package/registry/components/date-picker.tsx +246 -105
  119. package/registry/components/expanded/ActivityFeed.tsx +6 -15
  120. package/registry/components/expanded/Banner.tsx +3 -3
  121. package/registry/components/expanded/Breadcrumbs.tsx +5 -5
  122. package/registry/components/expanded/CatalogComponentsShowcase.tsx +13 -13
  123. package/registry/components/expanded/CatalogDivider.tsx +1 -1
  124. package/registry/components/expanded/EmptyState.tsx +1 -1
  125. package/registry/components/expanded/FileUpload.tsx +9 -2
  126. package/registry/components/expanded/OnboardingStepListItem.tsx +2 -4
  127. package/registry/components/expanded/PageHeader.tsx +14 -14
  128. package/registry/components/expanded/Slideout.tsx +15 -415
  129. package/registry/components/expanded/Steps.tsx +4 -4
  130. package/registry/components/expanded/Table.tsx +116 -69
  131. package/registry/components/expanded/Timeline.tsx +3 -3
  132. package/registry/components/expanded/activityFeed.css +1 -1
  133. package/registry/components/expanded/breadcrumbs.css +139 -139
  134. package/registry/components/expanded/catalogComponentsShowcase.css +83 -83
  135. package/registry/components/expanded/divider.css +1 -1
  136. package/registry/components/expanded/emptyState.css +1 -1
  137. package/registry/components/expanded/fileUpload.css +6 -6
  138. package/registry/components/expanded/slideout.css +9 -685
  139. package/registry/components/expanded/steps.css +274 -274
  140. package/registry/components/expanded/table.css +2 -2
  141. package/registry/components/expanded/timeline.css +264 -264
  142. package/registry/components/field.tsx +230 -0
  143. package/registry/components/hover-card.tsx +48 -0
  144. package/registry/components/input-content.ts +1 -0
  145. package/registry/components/input-group.tsx +130 -0
  146. package/registry/components/input-otp.tsx +13 -3
  147. package/registry/components/input.tsx +70 -45
  148. package/registry/components/item.tsx +189 -0
  149. package/registry/components/kbd.tsx +151 -0
  150. package/registry/components/label.tsx +78 -0
  151. package/registry/components/menu.tsx +34 -246
  152. package/registry/components/menubar.tsx +226 -0
  153. package/registry/components/modal.tsx +216 -143
  154. package/registry/components/native-select.tsx +205 -0
  155. package/registry/components/navigation-menu.tsx +171 -0
  156. package/registry/components/pagination.tsx +80 -58
  157. package/registry/components/popover.tsx +162 -664
  158. package/registry/components/radio-card.tsx +116 -87
  159. package/registry/components/radio-group.tsx +1 -1
  160. package/registry/components/resizable.tsx +74 -0
  161. package/registry/components/scroll-area.tsx +67 -0
  162. package/registry/components/segmented-control.tsx +87 -0
  163. package/registry/components/select-content.tsx +2 -2
  164. package/registry/components/select.tsx +54 -20
  165. package/registry/components/sheet.tsx +305 -0
  166. package/registry/components/sidebar.tsx +352 -0
  167. package/registry/components/skeleton.css +0 -3
  168. package/registry/components/social-button.tsx +116 -30
  169. package/registry/components/spinner.tsx +18 -55
  170. package/registry/components/{expanded/tabs.css → tabs.css} +127 -106
  171. package/registry/components/tabs.tsx +246 -0
  172. package/registry/components/tag.tsx +2 -2
  173. package/registry/components/textarea.tsx +4 -4
  174. package/registry/components/toast.tsx +136 -0
  175. package/registry/components/toggle.tsx +2 -2
  176. package/registry/components/toolbar.tsx +89 -17
  177. package/registry/components/tooltip.tsx +1 -1
  178. package/registry/examples/alert-demo.tsx +25 -5
  179. package/registry/examples/alert-dialog-demo.tsx +42 -0
  180. package/registry/examples/aspect-ratio-demo.tsx +11 -0
  181. package/registry/examples/attribute-demo.tsx +1 -1
  182. package/registry/examples/autocomplete-demo.tsx +3 -3
  183. package/registry/examples/avatar-demo.tsx +7 -7
  184. package/registry/examples/badge-demo.tsx +1 -1
  185. package/registry/examples/button-demo.tsx +6 -6
  186. package/registry/examples/button-group-demo.tsx +23 -2
  187. package/registry/examples/card-demo.tsx +46 -8
  188. package/registry/examples/carousel-demo.tsx +43 -0
  189. package/registry/examples/chart-demo.tsx +33 -0
  190. package/registry/examples/checkbox-demo.tsx +5 -5
  191. package/registry/examples/collapsible-demo.tsx +16 -0
  192. package/registry/examples/collection-filter-bar-demo.tsx +135 -0
  193. package/registry/examples/command-demo.tsx +42 -0
  194. package/registry/examples/{expanded/command-bar-demo.tsx → command-palette-demo.tsx} +5 -15
  195. package/registry/examples/context-menu-demo.tsx +29 -0
  196. package/registry/examples/control-density-contract-demo.tsx +61 -0
  197. package/registry/examples/date-picker-demo.tsx +12 -12
  198. package/registry/examples/divider-demo.tsx +3 -3
  199. package/registry/examples/expanded/banner-demo.tsx +1 -1
  200. package/registry/examples/expanded/breadcrumbs-demo.tsx +1 -1
  201. package/registry/examples/expanded/empty-state-demo.tsx +1 -1
  202. package/registry/examples/expanded/file-upload-demo.tsx +1 -1
  203. package/registry/examples/expanded/onboarding-step-demo.tsx +1 -1
  204. package/registry/examples/expanded/slideout-demo.tsx +2 -2
  205. package/registry/examples/expanded/steps-demo.tsx +1 -1
  206. package/registry/examples/expanded/tabs-demo.tsx +2 -2
  207. package/registry/examples/expanded/timeline-demo.tsx +1 -1
  208. package/registry/examples/field-demo.tsx +51 -0
  209. package/registry/examples/hover-card-demo.tsx +23 -0
  210. package/registry/examples/input-demo.tsx +6 -6
  211. package/registry/examples/input-group-demo.tsx +16 -0
  212. package/registry/examples/input-otp-demo.tsx +4 -4
  213. package/registry/examples/item-demo.tsx +46 -0
  214. package/registry/examples/kbd-demo.tsx +11 -0
  215. package/registry/examples/label-demo.tsx +20 -0
  216. package/registry/examples/menu-demo.tsx +1 -1
  217. package/registry/examples/menubar-demo.tsx +34 -0
  218. package/registry/examples/modal-demo.tsx +67 -9
  219. package/registry/examples/native-select-demo.tsx +16 -0
  220. package/registry/examples/navigation-menu-demo.tsx +29 -0
  221. package/registry/examples/pagination-demo.tsx +6 -3
  222. package/registry/examples/popover-demo.tsx +19 -19
  223. package/registry/examples/progress-demo.tsx +1 -1
  224. package/registry/examples/progress-slider-demo.tsx +1 -1
  225. package/registry/examples/radio-card-demo.tsx +4 -4
  226. package/registry/examples/radio-group-demo.tsx +2 -2
  227. package/registry/examples/resizable-demo.tsx +22 -0
  228. package/registry/examples/results-toolbar-demo.tsx +44 -0
  229. package/registry/examples/scroll-area-demo.tsx +15 -0
  230. package/registry/examples/segmented-control-demo.tsx +39 -0
  231. package/registry/examples/select-demo.tsx +1 -1
  232. package/registry/examples/sheet-demo.tsx +47 -0
  233. package/registry/examples/sidebar-demo.tsx +55 -0
  234. package/registry/examples/slider-demo.tsx +3 -3
  235. package/registry/examples/social-button-demo.tsx +18 -8
  236. package/registry/examples/spinner-demo.tsx +13 -13
  237. package/registry/examples/tabs-demo.tsx +13 -0
  238. package/registry/examples/tag-demo.tsx +1 -1
  239. package/registry/examples/textarea-demo.tsx +4 -4
  240. package/registry/examples/toast-demo.tsx +35 -0
  241. package/registry/examples/toggle-demo.tsx +6 -6
  242. package/registry/examples/tooltip-demo.tsx +4 -4
  243. package/registry/guidance/USAGE.md +27 -0
  244. package/registry/hooks/use-direction.ts +193 -13
  245. package/registry/index.json +6010 -432
  246. package/registry/styles/globals.css +3112 -4690
  247. package/registry.json +1582 -0
  248. package/schema/config.schema.json +51 -0
  249. package/schema/registry.schema.json +190 -0
  250. package/schema/tokens.schema.json +163 -0
  251. package/src/cli/command-line.js +194 -0
  252. package/src/cli/config-editors.js +9 -0
  253. package/src/cli/doctor.js +399 -0
  254. package/src/cli/file-transaction.js +295 -0
  255. package/src/cli/global-css.js +153 -0
  256. package/src/cli/index.js +639 -1391
  257. package/src/cli/install-plan.js +592 -0
  258. package/src/cli/install-receipt.js +171 -0
  259. package/src/cli/jsonc.js +203 -0
  260. package/src/cli/package-install-state.js +254 -0
  261. package/src/cli/package-manager.js +238 -0
  262. package/src/cli/package-plan.js +185 -0
  263. package/src/cli/package-version.js +97 -0
  264. package/src/cli/registry-query.js +207 -0
  265. package/src/cli/reporting.js +55 -0
  266. package/src/cli/schema-validation.js +19 -0
  267. package/src/cli/source-scanner.js +686 -0
  268. package/src/cli/vite-config.js +478 -0
  269. package/tokens/banhaten.tokens.json +4313 -310
  270. package/registry/assets/avatars/avatar-02.jpg +0 -0
  271. package/registry/assets/avatars/avatar-03.jpg +0 -0
  272. package/registry/assets/avatars/avatar-04.jpg +0 -0
  273. package/registry/assets/avatars/avatar-05.jpg +0 -0
  274. package/registry/assets/avatars/avatar-06.jpg +0 -0
  275. package/registry/assets/avatars/avatar-07.jpg +0 -0
  276. package/registry/assets/avatars/avatar-08.jpg +0 -0
  277. package/registry/assets/avatars/avatar-09.jpg +0 -0
  278. package/registry/assets/avatars/avatar-10.jpg +0 -0
  279. package/registry/assets/avatars/avatar-11.jpg +0 -0
  280. package/registry/assets/avatars/avatar-12.jpg +0 -0
  281. package/registry/assets/avatars/avatar-13.jpg +0 -0
  282. package/registry/assets/avatars/avatar-14.jpg +0 -0
  283. package/registry/assets/avatars/avatar-15.jpg +0 -0
  284. package/registry/assets/avatars/avatar-16.jpg +0 -0
  285. package/registry/assets/avatars/avatar-17.jpg +0 -0
  286. package/registry/assets/avatars/avatar-18.jpg +0 -0
  287. package/registry/assets/avatars/avatar-19.jpg +0 -0
  288. package/registry/assets/avatars/avatar-20.jpg +0 -0
  289. package/registry/assets/avatars/avatar-21.jpg +0 -0
  290. package/registry/assets/avatars/avatar-22.jpg +0 -0
  291. package/registry/assets/avatars/avatar-23.jpg +0 -0
  292. package/registry/assets/avatars/avatar-24.jpg +0 -0
  293. package/registry/assets/avatars/avatar-25.jpg +0 -0
  294. package/registry/assets/avatars/avatar-26.jpg +0 -0
  295. package/registry/assets/avatars/avatar-27.jpg +0 -0
  296. package/registry/assets/avatars/avatar-28.jpg +0 -0
  297. package/registry/assets/avatars/avatar-29.jpg +0 -0
  298. package/registry/assets/avatars/avatar-30.jpg +0 -0
  299. package/registry/assets/avatars/avatar-31.jpg +0 -0
  300. package/registry/assets/avatars/avatar-32.jpg +0 -0
  301. package/registry/assets/avatars/avatar-33.jpg +0 -0
  302. package/registry/assets/avatars/avatar-34.jpg +0 -0
  303. package/registry/assets/avatars/avatar-35.jpg +0 -0
  304. package/registry/assets/image-assets.json +0 -744
  305. package/registry/assets/images/art-02.jpg +0 -0
  306. package/registry/assets/images/art-03.jpg +0 -0
  307. package/registry/assets/images/art-04.jpg +0 -0
  308. package/registry/assets/images/art-05.jpg +0 -0
  309. package/registry/assets/images/art-06.jpg +0 -0
  310. package/registry/assets/images/art-07.jpg +0 -0
  311. package/registry/assets/images/art-08.jpg +0 -0
  312. package/registry/assets/images/art-09.jpg +0 -0
  313. package/registry/assets/images/art-10.jpg +0 -0
  314. package/registry/assets/images/art-11.jpg +0 -0
  315. package/registry/assets/images/art-12.jpg +0 -0
  316. package/registry/assets/images/art-13.jpg +0 -0
  317. package/registry/assets/images/art-14.jpg +0 -0
  318. package/registry/assets/images/art-15.jpg +0 -0
  319. package/registry/assets/images/art-16.jpg +0 -0
  320. package/registry/assets/images/art-17.jpg +0 -0
  321. package/registry/assets/images/art-18.jpg +0 -0
  322. package/registry/assets/images/art-19.jpg +0 -0
  323. package/registry/assets/images/art-20.jpg +0 -0
  324. package/registry/assets/images/art-21.jpg +0 -0
  325. package/registry/assets/images/art-22.jpg +0 -0
  326. package/registry/assets/images/art-23.jpg +0 -0
  327. package/registry/assets/images/art-24.jpg +0 -0
  328. package/registry/assets/images/art-25.jpg +0 -0
  329. package/registry/assets/images/art-26.jpg +0 -0
  330. package/registry/assets/images/art-27.jpg +0 -0
  331. package/registry/assets/images/nature-01.jpg +0 -0
  332. package/registry/assets/images/nature-02.jpg +0 -0
  333. package/registry/assets/images/nature-03.jpg +0 -0
  334. package/registry/assets/images/nature-04.jpg +0 -0
  335. package/registry/assets/images/nature-05.jpg +0 -0
  336. package/registry/assets/images/nature-06.jpg +0 -0
  337. package/registry/assets/images/nature-07.jpg +0 -0
  338. package/registry/assets/images/nature-08.jpg +0 -0
  339. package/registry/assets/images/nature-09.jpg +0 -0
  340. package/registry/assets/images/nature-10.jpg +0 -0
  341. package/registry/assets/images/nature-11.jpg +0 -0
  342. package/registry/assets/images/nature-12.jpg +0 -0
  343. package/registry/assets/images/nature-13.jpg +0 -0
  344. package/registry/assets/images/nature-14.jpg +0 -0
  345. package/registry/assets/images/nature-15.jpg +0 -0
  346. package/registry/assets/images/nature-16.jpg +0 -0
  347. package/registry/assets/images/nature-17.jpg +0 -0
  348. package/registry/assets/images/nature-18.jpg +0 -0
  349. package/registry/assets/images/nature-19.jpg +0 -0
  350. package/registry/assets/images/nature-20.jpg +0 -0
  351. package/registry/components/expanded/BannerBoard.tsx +0 -225
  352. package/registry/components/expanded/CatalogTag.tsx +0 -85
  353. package/registry/components/expanded/CommandBar.tsx +0 -386
  354. package/registry/components/expanded/IconExplorer.tsx +0 -612
  355. package/registry/components/expanded/Tabs.tsx +0 -86
  356. package/registry/components/expanded/TimelineShowcase.tsx +0 -158
  357. package/registry/components/expanded/commandBar.css +0 -470
  358. package/registry/components/expanded/iconExplorer.css +0 -764
  359. package/registry/components/expanded/iconPacks.ts +0 -866
  360. package/registry/components/expanded/tag.css +0 -148
  361. package/registry/examples/expanded/icons-demo.tsx +0 -5
  362. package/tokens/Color mode.zip +0 -0
  363. package/tokens/Numbers.zip +0 -0
  364. package/tokens/Radius.zip +0 -0
  365. package/tokens/Theme.zip +0 -0
@@ -0,0 +1,136 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import {
5
+ Toaster as SonnerToaster,
6
+ toast as sonnerToast,
7
+ type ExternalToast,
8
+ type ToasterProps as SonnerToasterProps,
9
+ } from "sonner"
10
+
11
+ import { cn } from "@/lib/utils"
12
+ import { useResolvedDirection } from "@/hooks/use-direction"
13
+
14
+ type ToastIntent = "default" | "success" | "danger" | "warning" | "info"
15
+ type ToastSide = "start" | "end"
16
+ type ToastOptions = ExternalToast & {
17
+ intent?: ToastIntent
18
+ }
19
+ type ToasterProps = Omit<SonnerToasterProps, "position"> & {
20
+ dir?: "ltr" | "rtl"
21
+ position?: SonnerToasterProps["position"]
22
+ side?: ToastSide
23
+ }
24
+
25
+ const intentClassNames: Record<ToastIntent, string> = {
26
+ danger:
27
+ "[--bh-toast-accent:var(--bh-content-danger-default)] [--bh-toast-accent-bg:var(--bh-bg-danger-subtle)]",
28
+ default:
29
+ "[--bh-toast-accent:var(--bh-content-brand-default)] [--bh-toast-accent-bg:var(--bh-bg-brand-subtle)]",
30
+ info:
31
+ "[--bh-toast-accent:var(--bh-content-brand-default)] [--bh-toast-accent-bg:var(--bh-bg-info-subtle)]",
32
+ success:
33
+ "[--bh-toast-accent:var(--bh-content-success-default)] [--bh-toast-accent-bg:var(--bh-bg-success-subtle)]",
34
+ warning:
35
+ "[--bh-toast-accent:var(--bh-content-warning-default)] [--bh-toast-accent-bg:var(--bh-bg-warning-subtle)]",
36
+ }
37
+
38
+ const toastClassName = [
39
+ "border border-[var(--bh-border-subtle)] bg-[var(--bh-bg-raised)] text-[var(--bh-content-default)] shadow-[var(--shadow-component-default)]",
40
+ "rounded-[var(--bh-radius-xl-10)] font-[var(--bh-font-family)]",
41
+ "[--normal-bg:var(--bh-bg-raised)] [--normal-border:var(--bh-border-subtle)] [--normal-text:var(--bh-content-default)]",
42
+ "[--success-bg:var(--bh-bg-raised)] [--success-border:var(--bh-border-success-subtle)] [--success-text:var(--bh-content-default)]",
43
+ "[--error-bg:var(--bh-bg-raised)] [--error-border:var(--bh-border-danger-subtle)] [--error-text:var(--bh-content-default)]",
44
+ "[--warning-bg:var(--bh-bg-raised)] [--warning-border:var(--bh-border-warning-subtle)] [--warning-text:var(--bh-content-default)]",
45
+ "[--info-bg:var(--bh-bg-raised)] [--info-border:var(--bh-border-info-subtle)] [--info-text:var(--bh-content-default)]",
46
+ ].join(" ")
47
+
48
+ function Toaster({
49
+ className,
50
+ closeButton = true,
51
+ dir,
52
+ position,
53
+ richColors = false,
54
+ side = "end",
55
+ toastOptions,
56
+ ...props
57
+ }: ToasterProps) {
58
+ const directionBoundaryRef = React.useRef<HTMLSpanElement | null>(null)
59
+ const resolvedDirection = useResolvedDirection(dir, directionBoundaryRef)
60
+ const resolvedPosition = position ?? getToastPosition(resolvedDirection, side)
61
+ const mergedToastOptions: ToasterProps["toastOptions"] = {
62
+ ...toastOptions,
63
+ classNames: {
64
+ ...toastOptions?.classNames,
65
+ toast: cn(toastClassName, toastOptions?.classNames?.toast),
66
+ title: cn(
67
+ "text-[length:var(--bh-text-body-sm-semibold-font-size)] font-[var(--bh-text-body-sm-semibold-font-weight)] leading-[var(--bh-text-body-sm-semibold-line-height)] tracking-[var(--bh-text-body-sm-semibold-letter-spacing)]",
68
+ toastOptions?.classNames?.title
69
+ ),
70
+ description: cn(
71
+ "text-[length:var(--bh-text-body-xs-regular-font-size)] font-[var(--bh-text-body-xs-regular-font-weight)] leading-[var(--bh-text-body-xs-regular-line-height)] tracking-[var(--bh-text-body-xs-regular-letter-spacing)] text-[var(--bh-content-subtle)]",
72
+ toastOptions?.classNames?.description
73
+ ),
74
+ actionButton: cn(
75
+ "rounded-[var(--bh-control-default)] bg-[var(--bh-interactive-brand-default)] px-[var(--bh-space-lg-10)] py-[var(--bh-space-xs-4)] text-[var(--bh-content-on-brand)]",
76
+ toastOptions?.classNames?.actionButton
77
+ ),
78
+ cancelButton: cn(
79
+ "rounded-[var(--bh-control-default)] bg-[var(--bh-bg-neutral-soft)] px-[var(--bh-space-lg-10)] py-[var(--bh-space-xs-4)] text-[var(--bh-content-default)]",
80
+ toastOptions?.classNames?.cancelButton
81
+ ),
82
+ },
83
+ }
84
+
85
+ return (
86
+ <span className="contents" data-slot="toaster-direction-boundary" ref={directionBoundaryRef}>
87
+ <SonnerToaster
88
+ className={cn("toaster group", className)}
89
+ closeButton={closeButton}
90
+ dir={resolvedDirection}
91
+ position={resolvedPosition}
92
+ richColors={richColors}
93
+ toastOptions={mergedToastOptions}
94
+ {...props}
95
+ />
96
+ </span>
97
+ )
98
+ }
99
+
100
+ function showToast(message: React.ReactNode, options: ToastOptions = {}) {
101
+ const { intent = "default", className, ...rest } = options
102
+ return sonnerToast(message, {
103
+ className: cn(intentClassNames[intent], className),
104
+ ...rest,
105
+ })
106
+ }
107
+
108
+ const toast = Object.assign(showToast, {
109
+ danger: (message: React.ReactNode, options?: Omit<ToastOptions, "intent">) =>
110
+ showToast(message, { ...options, intent: "danger" }),
111
+ dismiss: sonnerToast.dismiss,
112
+ error: (message: React.ReactNode, options?: Omit<ToastOptions, "intent">) =>
113
+ showToast(message, { ...options, intent: "danger" }),
114
+ info: (message: React.ReactNode, options?: Omit<ToastOptions, "intent">) =>
115
+ showToast(message, { ...options, intent: "info" }),
116
+ loading: sonnerToast.loading,
117
+ promise: sonnerToast.promise,
118
+ success: (message: React.ReactNode, options?: Omit<ToastOptions, "intent">) =>
119
+ showToast(message, { ...options, intent: "success" }),
120
+ warning: (message: React.ReactNode, options?: Omit<ToastOptions, "intent">) =>
121
+ showToast(message, { ...options, intent: "warning" }),
122
+ })
123
+
124
+ function getToastPosition(
125
+ dir: ToasterProps["dir"],
126
+ side: ToastSide
127
+ ): SonnerToasterProps["position"] {
128
+ if (side === "start") {
129
+ return dir === "rtl" ? "top-right" : "top-left"
130
+ }
131
+
132
+ return dir === "rtl" ? "top-left" : "top-right"
133
+ }
134
+
135
+ export { Toaster, toast }
136
+ export type { ToastIntent, ToastOptions, ToasterProps }
@@ -231,7 +231,7 @@ function ToggleCheckIcon({ className }: { className?: string }) {
231
231
  aria-hidden="true"
232
232
  className={className}
233
233
  focusable="false"
234
- strokeWidth={2.5}
234
+ strokeWidth="var(--bh-icon-stroke-250)"
235
235
  />
236
236
  )
237
237
  }
@@ -242,7 +242,7 @@ function ToggleCloseIcon({ className }: { className?: string }) {
242
242
  aria-hidden="true"
243
243
  className={className}
244
244
  focusable="false"
245
- strokeWidth={2.5}
245
+ strokeWidth="var(--bh-icon-stroke-250)"
246
246
  />
247
247
  )
248
248
  }
@@ -9,6 +9,12 @@ import {
9
9
  } from "lucide-react"
10
10
 
11
11
  import { cn } from "@/lib/utils"
12
+ import {
13
+ KbdShortcut,
14
+ type ShortcutPlatform,
15
+ } from "@/components/ui/kbd"
16
+
17
+ type ControlDensity = "compact" | "default" | "comfortable"
12
18
 
13
19
  const toolbarVariants = cva(
14
20
  "flex w-full max-w-full min-w-0 gap-[var(--bh-space-3xl-16)] text-[var(--bh-content-default)]",
@@ -65,37 +71,44 @@ const toolbarSectionVariants = cva(
65
71
  )
66
72
 
67
73
  const toolbarButtonVariants = cva(
68
- "group/toolbar-button inline-flex min-w-0 items-center justify-center whitespace-nowrap rounded-[var(--bh-control-default)] text-[length:var(--bh-text-button-font-size)] font-[var(--bh-text-button-font-weight)] leading-[var(--bh-text-button-line-height)] tracking-[var(--bh-text-button-letter-spacing)] outline-none transition-[background-color,border-color,color,box-shadow] focus-visible:shadow-[var(--shadow-button-focus)] disabled:pointer-events-none disabled:shadow-none aria-invalid:border aria-invalid:border-destructive aria-invalid:shadow-[var(--shadow-button-danger-focus)] [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[var(--bh-button-icon-size)] [&_svg[data-icon='inline-start']]:ms-[var(--bh-button-icon-offset)] [&_svg[data-icon='inline-end']]:me-[var(--bh-button-icon-offset)] rtl:[&_svg[data-rtl-flip='true']]:-scale-x-100",
74
+ "group/toolbar-button inline-flex min-w-0 items-center justify-center whitespace-nowrap rounded-[var(--bh-control-default)] text-[length:var(--bh-text-button-font-size)] font-[var(--bh-text-button-font-weight)] leading-[var(--bh-text-button-line-height)] tracking-[var(--bh-text-button-letter-spacing)] outline-none transition-[background-color,border-color,color,box-shadow] focus-visible:shadow-[var(--shadow-button-focus)] disabled:pointer-events-none disabled:shadow-none aria-invalid:border-[length:var(--bh-border-width-default)] aria-invalid:border-[var(--bh-border-danger-strong)] aria-invalid:shadow-[var(--shadow-button-danger-focus)] [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[var(--bh-button-icon-size)] [&_svg[data-icon='inline-start']]:ms-[var(--bh-button-icon-offset)] [&_svg[data-icon='inline-end']]:me-[var(--bh-button-icon-offset)] rtl:[&_svg[data-rtl-flip='true']]:-scale-x-100",
69
75
  {
70
76
  variants: {
71
77
  variant: {
72
78
  default:
73
- "bg-primary text-primary-foreground shadow-[var(--shadow-button-raised)] hover:bg-[var(--primary-hover)] active:bg-[var(--primary-pressed)] disabled:bg-[var(--bh-interactive-brand-disabled)] disabled:text-[var(--bh-content-disabled)]",
74
- soft: "border border-transparent bg-[var(--soft)] text-foreground shadow-[var(--shadow-button-soft)] hover:bg-[var(--soft-hover)] active:bg-[var(--soft-pressed)] focus-visible:border-[var(--bh-border-brand-strong)] disabled:bg-[var(--bh-interactive-soft-disabled)] disabled:text-[var(--bh-content-disabled)]",
79
+ "bg-[var(--bh-interactive-brand-default)] text-[var(--bh-content-on-brand)] shadow-[var(--shadow-button-raised)] hover:bg-[var(--bh-interactive-brand-hover)] active:bg-[var(--bh-interactive-brand-pressed)] disabled:bg-[var(--bh-interactive-brand-disabled)] disabled:text-[var(--bh-content-disabled)]",
80
+ soft: "border-[length:var(--bh-border-width-default)] border-transparent bg-[var(--bh-interactive-soft-default)] text-[var(--bh-content-default)] shadow-[var(--shadow-button-soft)] hover:bg-[var(--bh-interactive-soft-hover)] active:bg-[var(--bh-interactive-soft-pressed)] focus-visible:border-[var(--bh-border-brand-strong)] disabled:bg-[var(--bh-interactive-soft-disabled)] disabled:text-[var(--bh-content-disabled)]",
75
81
  outline:
76
- "border border-[var(--bh-border-default)] bg-[var(--outline)] text-foreground hover:bg-[var(--outline-hover)] active:bg-[var(--outline-pressed)] focus-visible:border-[var(--bh-border-brand-strong)] disabled:border-[var(--bh-border-disabled)] disabled:bg-[var(--bh-interactive-outlined-disabled)] disabled:text-[var(--bh-content-disabled)]",
82
+ "border-[length:var(--bh-border-width-default)] border-[var(--bh-border-default)] bg-[var(--bh-interactive-outlined-default)] text-[var(--bh-content-default)] hover:bg-[var(--bh-interactive-outlined-hover)] active:bg-[var(--bh-interactive-outlined-pressed)] focus-visible:border-[var(--bh-border-brand-strong)] disabled:border-[var(--bh-border-disabled)] disabled:bg-[var(--bh-interactive-outlined-disabled)] disabled:text-[var(--bh-content-disabled)]",
77
83
  ghost:
78
- "border border-transparent bg-[var(--ghost)] text-foreground hover:bg-[var(--ghost-hover)] active:bg-[var(--ghost-pressed)] focus-visible:border-[var(--bh-border-brand-strong)] disabled:bg-[var(--bh-interactive-ghost-disabled)] disabled:text-[var(--bh-content-disabled)]",
79
- link: "border border-transparent bg-[var(--ghost)] text-[var(--bh-content-brand-default)] hover:bg-[var(--ghost-hover)] active:bg-[var(--ghost-pressed)] focus-visible:border-[var(--bh-border-brand-strong)] disabled:bg-[var(--bh-interactive-ghost-disabled)] disabled:text-[var(--bh-content-disabled)]",
84
+ "border-[length:var(--bh-border-width-default)] border-transparent bg-[var(--bh-interactive-ghost-default)] text-[var(--bh-content-default)] hover:bg-[var(--bh-interactive-ghost-hover)] active:bg-[var(--bh-interactive-ghost-pressed)] focus-visible:border-[var(--bh-border-brand-strong)] disabled:bg-[var(--bh-interactive-ghost-disabled)] disabled:text-[var(--bh-content-disabled)]",
85
+ link: "border-[length:var(--bh-border-width-default)] border-transparent bg-[var(--bh-interactive-ghost-default)] text-[var(--bh-content-brand-default)] hover:bg-[var(--bh-interactive-ghost-hover)] active:bg-[var(--bh-interactive-ghost-pressed)] focus-visible:border-[var(--bh-border-brand-strong)] disabled:bg-[var(--bh-interactive-ghost-disabled)] disabled:text-[var(--bh-content-disabled)]",
80
86
  },
81
87
  size: {
88
+ xs: "h-[var(--bh-button-xs-height)] gap-[var(--bh-button-gap-xs)] px-[var(--bh-button-xs-padding-x)] py-[var(--bh-button-sm-padding-y)] has-[>svg]:px-[var(--bh-button-xs-padding-x)]",
82
89
  sm: "h-[var(--bh-button-sm-height)] gap-[var(--bh-button-gap)] px-[var(--bh-button-sm-padding-x)] py-[var(--bh-button-sm-padding-y)] has-[>svg]:px-[var(--bh-button-sm-padding-x)]",
83
90
  default: "h-[var(--bh-button-md-height)] gap-[var(--bh-button-gap)] px-[var(--bh-button-md-padding-x)] py-[var(--bh-button-sm-padding-y)] has-[>svg]:px-[var(--bh-button-md-padding-x)]",
91
+ "icon-xs": "size-[var(--bh-button-xs-height)] gap-[var(--bh-space-none)] p-[var(--bh-space-none)]",
84
92
  "icon-sm": "size-[var(--bh-button-sm-height)] gap-[var(--bh-space-none)] p-[var(--bh-space-none)]",
85
93
  icon: "size-[var(--bh-button-md-height)] gap-[var(--bh-space-none)] p-[var(--bh-space-none)]",
86
94
  },
87
95
  },
88
96
  defaultVariants: {
89
97
  variant: "soft",
90
- size: "default",
98
+ size: "sm",
91
99
  },
92
100
  }
93
101
  )
94
102
 
95
103
  const toolbarSearchVariants = cva(
96
- "flex h-[var(--bh-input-lg-height)] max-w-full min-w-0 items-center gap-[var(--bh-input-content-gap)] rounded-[var(--bh-input-radius)] bg-[var(--bh-interactive-input-default)] px-[var(--bh-input-lg-padding-x)] text-[var(--bh-content-default)] shadow-[var(--shadow-input)] transition-[background-color,box-shadow] focus-within:shadow-[var(--shadow-input-focus-ring)] data-[disabled=true]:pointer-events-none data-[disabled=true]:bg-[var(--bh-interactive-input-disabled)] data-[disabled=true]:text-[var(--bh-content-disabled)]",
104
+ "flex max-w-full min-w-0 items-center gap-[var(--bh-input-content-gap)] rounded-[var(--bh-input-radius)] bg-[var(--bh-interactive-input-default)] text-[var(--bh-content-default)] shadow-[var(--shadow-input)] transition-[background-color,box-shadow] focus-within:shadow-[var(--shadow-input-focus-ring)] data-[disabled=true]:pointer-events-none data-[disabled=true]:bg-[var(--bh-interactive-input-disabled)] data-[disabled=true]:text-[var(--bh-content-disabled)]",
97
105
  {
98
106
  variants: {
107
+ density: {
108
+ compact: "h-[var(--bh-input-md-height)] px-[var(--bh-input-md-padding-x)]",
109
+ default: "h-[var(--bh-input-lg-height)] px-[var(--bh-input-lg-padding-x)]",
110
+ comfortable: "h-[var(--bh-input-comfortable-height)] px-[var(--bh-input-comfortable-padding-x)]",
111
+ },
99
112
  width: {
100
113
  compact: "w-[calc(var(--bh-input-width)/2)]",
101
114
  default: "w-[var(--bh-input-width)]",
@@ -104,15 +117,21 @@ const toolbarSearchVariants = cva(
104
117
  },
105
118
  },
106
119
  defaultVariants: {
120
+ density: "default",
107
121
  width: "default",
108
122
  },
109
123
  }
110
124
  )
111
125
 
112
126
  const toolbarSelectVariants = cva(
113
- "inline-flex h-[var(--bh-input-lg-height)] max-w-full min-w-0 items-center justify-between gap-[var(--bh-space-md-8)] rounded-[var(--bh-input-radius)] bg-[var(--bh-interactive-input-default)] px-[var(--bh-select-trigger-padding-x)] text-start text-[length:var(--bh-text-body-md-regular-font-size)] font-[var(--bh-text-body-md-regular-font-weight)] leading-[var(--bh-text-body-md-regular-line-height)] tracking-[var(--bh-text-body-md-regular-letter-spacing)] text-[var(--bh-content-default)] shadow-[var(--shadow-select-trigger)] outline-none transition-[background-color,box-shadow] hover:bg-[var(--bh-interactive-input-hover)] focus-visible:shadow-[var(--shadow-input-focus-ring)] disabled:pointer-events-none disabled:bg-[var(--bh-interactive-input-disabled)] disabled:text-[var(--bh-content-disabled)]",
127
+ "inline-flex max-w-full min-w-0 items-center justify-between gap-[var(--bh-space-md-8)] rounded-[var(--bh-input-radius)] bg-[var(--bh-interactive-input-default)] px-[var(--bh-select-trigger-padding-x)] text-start text-[length:var(--bh-text-body-md-regular-font-size)] font-[var(--bh-text-body-md-regular-font-weight)] leading-[var(--bh-text-body-md-regular-line-height)] tracking-[var(--bh-text-body-md-regular-letter-spacing)] text-[var(--bh-content-default)] shadow-[var(--shadow-select-trigger)] outline-none transition-[background-color,box-shadow] hover:bg-[var(--bh-bg-default-hover)] focus-visible:shadow-[var(--shadow-input-focus-ring)] disabled:pointer-events-none disabled:bg-[var(--bh-interactive-input-disabled)] disabled:text-[var(--bh-content-disabled)]",
114
128
  {
115
129
  variants: {
130
+ density: {
131
+ compact: "h-[var(--bh-input-md-height)]",
132
+ default: "h-[var(--bh-input-lg-height)]",
133
+ comfortable: "h-[var(--bh-input-comfortable-height)]",
134
+ },
116
135
  width: {
117
136
  compact: "w-[calc(var(--bh-select-width)/2)]",
118
137
  default: "w-[var(--bh-select-width)]",
@@ -121,6 +140,7 @@ const toolbarSelectVariants = cva(
121
140
  },
122
141
  },
123
142
  defaultVariants: {
143
+ density: "default",
124
144
  width: "compact",
125
145
  },
126
146
  }
@@ -201,20 +221,39 @@ const ToolbarSpacer = React.forwardRef<HTMLDivElement, ToolbarSpacerProps>(
201
221
  type ToolbarButtonProps = React.ComponentProps<"button"> &
202
222
  VariantProps<typeof toolbarButtonVariants> & {
203
223
  asChild?: boolean
224
+ density?: ControlDensity
204
225
  }
205
226
 
227
+ type ToolbarButtonSize = NonNullable<
228
+ VariantProps<typeof toolbarButtonVariants>["size"]
229
+ >
230
+
206
231
  const ToolbarButton = React.forwardRef<HTMLButtonElement, ToolbarButtonProps>(
207
232
  function ToolbarButton(
208
- { className, variant, size, asChild = false, children, type = "button", ...props },
233
+ {
234
+ asChild = false,
235
+ children,
236
+ className,
237
+ density,
238
+ size,
239
+ type = "button",
240
+ variant,
241
+ ...props
242
+ },
209
243
  ref
210
244
  ) {
211
245
  const Comp = asChild ? Slot : "button"
246
+ const resolvedSize = resolveToolbarButtonSize(density, size)
212
247
 
213
248
  return (
214
249
  <Comp
250
+ data-density={density}
251
+ data-size={resolvedSize}
215
252
  data-slot="toolbar-button"
216
253
  ref={ref}
217
- className={cn(toolbarButtonVariants({ variant, size, className }))}
254
+ className={cn(
255
+ toolbarButtonVariants({ variant, size: resolvedSize, className })
256
+ )}
218
257
  {...(!asChild ? { type } : {})}
219
258
  {...props}
220
259
  >
@@ -271,7 +310,7 @@ const ToolbarMoreButton = React.forwardRef<
271
310
  HTMLButtonElement,
272
311
  ToolbarMoreButtonProps
273
312
  >(function ToolbarMoreButton(
274
- { label = "More actions", size = "icon", ...props },
313
+ { label = "More actions", size = "icon-sm", ...props },
275
314
  ref
276
315
  ) {
277
316
  return (
@@ -289,6 +328,8 @@ type ToolbarSearchProps = Omit<
289
328
  className?: string
290
329
  inputClassName?: string
291
330
  icon?: React.ReactNode
331
+ shortcutKeys?: readonly string[]
332
+ shortcutPlatform?: ShortcutPlatform
292
333
  }
293
334
 
294
335
  const ToolbarSearch = React.forwardRef<HTMLInputElement, ToolbarSearchProps>(
@@ -299,7 +340,10 @@ const ToolbarSearch = React.forwardRef<HTMLInputElement, ToolbarSearchProps>(
299
340
  icon,
300
341
  inputClassName,
301
342
  placeholder = "Search...",
343
+ shortcutKeys,
344
+ shortcutPlatform,
302
345
  type = "search",
346
+ density,
303
347
  width,
304
348
  ...props
305
349
  },
@@ -308,8 +352,9 @@ const ToolbarSearch = React.forwardRef<HTMLInputElement, ToolbarSearchProps>(
308
352
  return (
309
353
  <div
310
354
  data-disabled={disabled ? "true" : undefined}
355
+ data-density={density ?? "default"}
311
356
  data-slot="toolbar-search"
312
- className={cn(toolbarSearchVariants({ width, className }))}
357
+ className={cn(toolbarSearchVariants({ density, width, className }))}
313
358
  >
314
359
  <span
315
360
  aria-hidden="true"
@@ -329,6 +374,13 @@ const ToolbarSearch = React.forwardRef<HTMLInputElement, ToolbarSearchProps>(
329
374
  type={type}
330
375
  {...props}
331
376
  />
377
+ {shortcutKeys?.length ? (
378
+ <KbdShortcut
379
+ disabled={disabled}
380
+ keys={shortcutKeys}
381
+ platform={shortcutPlatform}
382
+ />
383
+ ) : null}
332
384
  </div>
333
385
  )
334
386
  }
@@ -349,6 +401,7 @@ const ToolbarSelect = React.forwardRef<HTMLButtonElement, ToolbarSelectProps>(
349
401
  placeholder = "Select",
350
402
  type = "button",
351
403
  value,
404
+ density,
352
405
  width,
353
406
  ...props
354
407
  },
@@ -357,10 +410,11 @@ const ToolbarSelect = React.forwardRef<HTMLButtonElement, ToolbarSelectProps>(
357
410
  return (
358
411
  <button
359
412
  aria-haspopup="listbox"
413
+ data-density={density ?? "default"}
360
414
  data-slot="toolbar-select"
361
415
  ref={ref}
362
416
  type={type}
363
- className={cn(toolbarSelectVariants({ width, className }))}
417
+ className={cn(toolbarSelectVariants({ density, width, className }))}
364
418
  {...props}
365
419
  >
366
420
  <span
@@ -415,16 +469,22 @@ const ToolbarBadge = React.forwardRef<HTMLSpanElement, ToolbarBadgeProps>(
415
469
  }
416
470
  )
417
471
 
418
- type ToolbarTextProps = React.ComponentProps<"span">
472
+ type ToolbarTextProps = React.ComponentProps<"span"> & {
473
+ density?: ControlDensity
474
+ }
419
475
 
420
476
  const ToolbarText = React.forwardRef<HTMLSpanElement, ToolbarTextProps>(
421
- function ToolbarText({ className, ...props }, ref) {
477
+ function ToolbarText({ className, density = "default", ...props }, ref) {
422
478
  return (
423
479
  <span
424
480
  data-slot="toolbar-text"
481
+ data-density={density}
425
482
  ref={ref}
426
483
  className={cn(
427
- "inline-flex h-[var(--bh-input-lg-height)] min-w-0 items-center text-[length:var(--bh-text-body-md-regular-font-size)] font-[var(--bh-text-body-md-regular-font-weight)] leading-[var(--bh-text-body-md-regular-line-height)] tracking-[var(--bh-text-body-md-regular-letter-spacing)] text-[var(--bh-content-subtle)]",
484
+ "inline-flex min-w-0 items-center text-[length:var(--bh-text-body-md-regular-font-size)] font-[var(--bh-text-body-md-regular-font-weight)] leading-[var(--bh-text-body-md-regular-line-height)] tracking-[var(--bh-text-body-md-regular-letter-spacing)] text-[var(--bh-content-subtle)]",
485
+ density === "compact" && "h-[var(--bh-input-md-height)]",
486
+ density === "default" && "h-[var(--bh-input-lg-height)]",
487
+ density === "comfortable" && "h-[var(--bh-input-comfortable-height)]",
428
488
  className
429
489
  )}
430
490
  {...props}
@@ -433,6 +493,17 @@ const ToolbarText = React.forwardRef<HTMLSpanElement, ToolbarTextProps>(
433
493
  }
434
494
  )
435
495
 
496
+ function resolveToolbarButtonSize(
497
+ density: ControlDensity | undefined,
498
+ size: ToolbarButtonSize | null | undefined
499
+ ): ToolbarButtonSize | null | undefined {
500
+ if (!density) return size
501
+ const iconOnly = size?.startsWith("icon") ?? false
502
+ if (density === "compact") return iconOnly ? "icon-xs" : "xs"
503
+ if (density === "default") return iconOnly ? "icon-sm" : "sm"
504
+ return iconOnly ? "icon" : "default"
505
+ }
506
+
436
507
  export {
437
508
  Toolbar,
438
509
  ToolbarBadge,
@@ -453,6 +524,7 @@ export {
453
524
  toolbarVariants,
454
525
  }
455
526
  export type {
527
+ ControlDensity,
456
528
  ToolbarBadgeProps,
457
529
  ToolbarButtonProps,
458
530
  ToolbarFilterButtonProps,
@@ -280,7 +280,7 @@ function TooltipCloseButton({
280
280
  <XIcon
281
281
  aria-hidden="true"
282
282
  className="size-[var(--bh-tooltip-close-icon-size)]"
283
- strokeWidth={2}
283
+ strokeWidth="var(--bh-icon-stroke-200)"
284
284
  />
285
285
  </button>
286
286
  )
@@ -2,13 +2,33 @@ import { Alert } from "@/components/ui/alert"
2
2
 
3
3
  export function AlertDemo() {
4
4
  return (
5
- <div className="grid gap-4">
6
- <Alert status="danger" />
7
- <Alert status="success" />
8
- <Alert isExpandable status="warning" />
5
+ <div className="grid gap-[var(--bh-space-3xl-16)]">
6
+ <Alert status="danger" title="Payment failed" />
7
+ <Alert status="success" title="Changes saved" />
8
+ <Alert
9
+ actions={[
10
+ { label: "Review details", onClick: noop },
11
+ { label: "Dismiss", onClick: noop },
12
+ ]}
13
+ description="Update the payment method before the next billing attempt."
14
+ isExpandable
15
+ status="warning"
16
+ title="Payment method expires soon"
17
+ />
9
18
  <div dir="rtl">
10
- <Alert dir="rtl" isExpandable status="info" />
19
+ <Alert
20
+ actions={[
21
+ { label: "عرض التفاصيل", onClick: noop },
22
+ { label: "متابعة", onClick: noop },
23
+ ]}
24
+ description="يمكنك مراجعة التفاصيل أو المتابعة من لوحة التحكم."
25
+ isExpandable
26
+ status="info"
27
+ title="تم تحديث حسابك"
28
+ />
11
29
  </div>
12
30
  </div>
13
31
  )
14
32
  }
33
+
34
+ function noop() {}
@@ -0,0 +1,42 @@
1
+ import { Button } from "@/components/ui/button"
2
+ import {
3
+ AlertDialog,
4
+ AlertDialogAction,
5
+ AlertDialogBody,
6
+ AlertDialogCancel,
7
+ AlertDialogContent,
8
+ AlertDialogDescription,
9
+ AlertDialogFooter,
10
+ AlertDialogHeader,
11
+ AlertDialogIcon,
12
+ AlertDialogTitle,
13
+ AlertDialogTrigger,
14
+ } from "@/components/ui/alert-dialog"
15
+
16
+ export function AlertDialogDemo() {
17
+ return (
18
+ <AlertDialog>
19
+ <AlertDialogTrigger asChild>
20
+ <Button size="sm" variant="danger">
21
+ Delete project
22
+ </Button>
23
+ </AlertDialogTrigger>
24
+ <AlertDialogContent>
25
+ <AlertDialogHeader>
26
+ <AlertDialogIcon intent="danger" />
27
+ <AlertDialogTitle>Delete project?</AlertDialogTitle>
28
+ </AlertDialogHeader>
29
+ <AlertDialogBody>
30
+ <AlertDialogDescription>
31
+ This action cannot be undone. All environments, deployments, and
32
+ saved settings will be permanently removed.
33
+ </AlertDialogDescription>
34
+ </AlertDialogBody>
35
+ <AlertDialogFooter>
36
+ <AlertDialogCancel>Cancel</AlertDialogCancel>
37
+ <AlertDialogAction>Delete project</AlertDialogAction>
38
+ </AlertDialogFooter>
39
+ </AlertDialogContent>
40
+ </AlertDialog>
41
+ )
42
+ }
@@ -0,0 +1,11 @@
1
+ import { AspectRatio } from "@/components/ui/aspect-ratio"
2
+
3
+ export function AspectRatioDemo() {
4
+ return (
5
+ <AspectRatio ratio={16 / 9} className="max-w-xl">
6
+ <div className="flex size-full items-center justify-center bg-[var(--bh-bg-neutral-subtle)] text-[var(--bh-content-subtle)]">
7
+ 16:9
8
+ </div>
9
+ </AspectRatio>
10
+ )
11
+ }
@@ -11,7 +11,7 @@ import { Button } from "@/components/ui/button"
11
11
 
12
12
  export function AttributeDemo() {
13
13
  return (
14
- <div className="grid max-w-5xl gap-4 lg:grid-cols-2">
14
+ <div className="grid max-w-5xl gap-[var(--bh-space-3xl-16)] lg:grid-cols-2">
15
15
  <AttributeCard>
16
16
  <AttributeHeader
17
17
  title="Company profile"
@@ -61,8 +61,8 @@ const rtlOptions = [
61
61
 
62
62
  export function AutocompleteDemo() {
63
63
  return (
64
- <div className="grid w-full gap-5 lg:grid-cols-2">
65
- <div className="grid min-w-0 content-start gap-5">
64
+ <div className="grid w-full gap-[var(--bh-space-4xl-20)] lg:grid-cols-2">
65
+ <div className="grid min-w-0 content-start gap-[var(--bh-space-4xl-20)]">
66
66
  <Autocomplete
67
67
  className="mb-[var(--bh-space-13xl-160)]"
68
68
  defaultOpen
@@ -83,7 +83,7 @@ export function AutocompleteDemo() {
83
83
  />
84
84
  </div>
85
85
 
86
- <div className="grid min-w-0 content-start gap-5">
86
+ <div className="grid min-w-0 content-start gap-[var(--bh-space-4xl-20)]">
87
87
  <Autocomplete
88
88
  label="Project"
89
89
  options={projectOptions}
@@ -30,8 +30,8 @@ const secondaryStatusColumns = [
30
30
 
31
31
  export function AvatarDemo() {
32
32
  return (
33
- <div className="flex flex-col gap-7">
34
- <div className="grid w-fit grid-cols-5 gap-x-4 gap-y-4">
33
+ <div className="flex flex-col gap-[calc(var(--bh-space-5xl-24)_+_var(--bh-space-xs-4))]">
34
+ <div className="grid w-fit grid-cols-5 gap-x-[var(--bh-space-3xl-16)] gap-y-[var(--bh-space-3xl-16)]">
35
35
  {statusColumns.map((status, index) => (
36
36
  <Avatar key={`icon-${status}-${index}`} size="2xl">
37
37
  <AvatarIcon />
@@ -56,7 +56,7 @@ export function AvatarDemo() {
56
56
  ))}
57
57
  </div>
58
58
 
59
- <div className="flex flex-wrap items-center gap-5 ps-7">
59
+ <div className="flex flex-wrap items-center gap-[var(--bh-space-4xl-20)] ps-[calc(var(--bh-space-5xl-24)_+_var(--bh-space-xs-4))]">
60
60
  <Avatar size="2xl">
61
61
  <AvatarImage src={avatarUrl} alt="Profile portrait" />
62
62
  <AvatarFallback>AG</AvatarFallback>
@@ -74,7 +74,7 @@ export function AvatarDemo() {
74
74
  </Avatar>
75
75
  </div>
76
76
 
77
- <div className="flex items-center gap-3">
77
+ <div className="flex items-center gap-[var(--bh-space-xl-12)]">
78
78
  <AvatarGroup>
79
79
  {Array.from({ length: 7 }).map((_, index) => (
80
80
  <Avatar key={index} size="md">
@@ -89,11 +89,11 @@ export function AvatarDemo() {
89
89
  className="border border-dashed border-[var(--bh-border-subtle)] bg-[var(--bh-bg-default)] text-[var(--bh-content-subtle)]"
90
90
  size="md"
91
91
  >
92
- <PlusIcon aria-hidden="true" className="size-4" />
92
+ <PlusIcon aria-hidden="true" className="size-[var(--bh-space-3xl-16)]" />
93
93
  </Avatar>
94
94
  </div>
95
95
 
96
- <div className="flex items-center gap-3">
96
+ <div className="flex items-center gap-[var(--bh-space-xl-12)]">
97
97
  <Avatar size="xl">
98
98
  <AvatarImage src={avatarUrl} alt="John Doe" />
99
99
  <AvatarFallback>JD</AvatarFallback>
@@ -109,7 +109,7 @@ export function AvatarDemo() {
109
109
  </div>
110
110
  </div>
111
111
 
112
- <div className="flex items-center gap-3">
112
+ <div className="flex items-center gap-[var(--bh-space-xl-12)]">
113
113
  <Avatar size="md">
114
114
  <AvatarImage src={avatarUrl} alt="John Doe" />
115
115
  <AvatarFallback>JD</AvatarFallback>
@@ -10,7 +10,7 @@ import { Spinner } from "@/components/ui/spinner"
10
10
 
11
11
  export function BadgeDemo() {
12
12
  return (
13
- <div className="flex flex-wrap items-center gap-2">
13
+ <div className="flex flex-wrap items-center gap-[var(--bh-space-md-8)]">
14
14
  <Badge>Badge</Badge>
15
15
  <Badge badgeStyle="outline">Outline</Badge>
16
16
  <Badge badgeStyle="solid" color="blue">