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
@@ -40,7 +40,7 @@ function SpinnerBase({ className, motion, ...props }: SpinnerBaseProps) {
40
40
  r="10"
41
41
  fill="none"
42
42
  stroke="currentColor"
43
- strokeWidth="4"
43
+ strokeWidth="var(--bh-spinner-stroke-width)"
44
44
  opacity="var(--bh-opacity-25)"
45
45
  />
46
46
  <circle
@@ -50,64 +50,27 @@ function SpinnerBase({ className, motion, ...props }: SpinnerBaseProps) {
50
50
  r="10"
51
51
  fill="none"
52
52
  stroke="currentColor"
53
- strokeWidth="4"
53
+ strokeWidth="var(--bh-spinner-stroke-width)"
54
54
  strokeLinecap="round"
55
55
  pathLength="100"
56
56
  strokeDasharray="30 70"
57
- >
58
- {motion === "dynamic" ? (
59
- <DynamicSpinnerMotion />
60
- ) : (
61
- <SteadySpinnerMotion />
62
- )}
63
- </circle>
64
- </svg>
65
- )
66
- }
67
-
68
- function DynamicSpinnerMotion() {
69
- return (
70
- <>
71
- <animate
72
- attributeName="stroke-dasharray"
73
- calcMode="spline"
74
- dur="6s"
75
- keySplines="0.45 0 0.55 1; 0.45 0 0.55 1; 0.45 0 0.55 1; 0.45 0 0.55 1"
76
- keyTimes="0; 0.25; 0.5; 0.75; 1"
77
- repeatCount="indefinite"
78
- values="22 78; 38 62; 30 70; 42 58; 22 78"
79
- />
80
- <animate
81
- attributeName="stroke-dashoffset"
82
- calcMode="spline"
83
- dur="6s"
84
- keySplines="0.45 0 0.55 1; 0.45 0 0.55 1; 0.45 0 0.55 1; 0.45 0 0.55 1"
85
- keyTimes="0; 0.25; 0.5; 0.75; 1"
86
- repeatCount="indefinite"
87
- values="0; -16; -44; -72; -100"
57
+ style={{
58
+ animationDuration:
59
+ motion === "dynamic"
60
+ ? "var(--bh-spinner-duration-dynamic)"
61
+ : "var(--bh-spinner-duration-steady)",
62
+ animationIterationCount: "infinite",
63
+ animationName:
64
+ motion === "dynamic"
65
+ ? "bh-spinner-dynamic"
66
+ : "bh-spinner-steady",
67
+ animationTimingFunction:
68
+ motion === "dynamic" ? "var(--bh-spinner-easing)" : "linear",
69
+ transformBox: "fill-box",
70
+ transformOrigin: "center",
71
+ }}
88
72
  />
89
- <animateTransform
90
- attributeName="transform"
91
- dur="6s"
92
- from="0 12 12"
93
- repeatCount="indefinite"
94
- to="1080 12 12"
95
- type="rotate"
96
- />
97
- </>
98
- )
99
- }
100
-
101
- function SteadySpinnerMotion() {
102
- return (
103
- <animateTransform
104
- attributeName="transform"
105
- dur="1.5s"
106
- from="0 12 12"
107
- repeatCount="indefinite"
108
- to="360 12 12"
109
- type="rotate"
110
- />
73
+ </svg>
111
74
  )
112
75
  }
113
76
 
@@ -25,11 +25,11 @@
25
25
  --tabs-shadow-selected: var(--shadow-button-soft);
26
26
 
27
27
  box-sizing: border-box;
28
- display: flex;
29
- position: relative;
30
- max-width: 100%;
31
- min-width: 0;
32
- overflow-x: auto;
28
+ display: flex;
29
+ position: relative;
30
+ max-width: 100%;
31
+ min-width: 0;
32
+ overflow-x: auto;
33
33
  overflow-y: visible;
34
34
  color: var(--tabs-content-subtle);
35
35
  font-family: var(--bh-font-family);
@@ -39,100 +39,109 @@
39
39
  line-height: var(--bh-text-body-md-regular-line-height);
40
40
  letter-spacing: var(--bh-text-body-md-regular-letter-spacing);
41
41
  }
42
-
43
- .ds-tabs *,
44
- .ds-tabs *::before,
45
- .ds-tabs *::after {
46
- box-sizing: border-box;
47
- }
48
-
49
- .ds-tabs--full {
50
- width: 100%;
51
- }
52
-
53
- .ds-tabs--segment,
54
- .ds-tabs--rounded {
55
- align-items: center;
56
- gap: var(--tabs-space-xxs);
57
- padding: var(--tabs-space-xxs);
58
- background: var(--tabs-bg-neutral-subtle);
59
- }
60
-
61
- .ds-tabs--segment {
62
- border-radius: var(--tabs-radius-full);
63
- }
64
-
65
- .ds-tabs--rounded {
66
- border-radius: var(--tabs-radius-lg);
67
- }
68
-
42
+
43
+ .ds-tabs *,
44
+ .ds-tabs *::before,
45
+ .ds-tabs *::after {
46
+ box-sizing: border-box;
47
+ }
48
+
49
+ .ds-tabs--full {
50
+ width: 100%;
51
+ }
52
+
53
+ .ds-tabs--segment,
54
+ .ds-tabs--rounded {
55
+ align-items: center;
56
+ gap: var(--tabs-space-xxs);
57
+ padding: var(--tabs-space-xxs);
58
+ background: var(--tabs-bg-neutral-subtle);
59
+ }
60
+
61
+ .ds-tabs--segment {
62
+ border-radius: var(--tabs-radius-full);
63
+ }
64
+
65
+ .ds-tabs--rounded {
66
+ border-radius: var(--tabs-radius-lg);
67
+ }
68
+
69
69
  .ds-tabs--underline {
70
70
  align-items: center;
71
71
  gap: var(--tabs-space-3xl);
72
72
  border-bottom: var(--bh-space-xxxs-1) solid var(--tabs-border-default);
73
73
  }
74
-
75
- .ds-tabs__tab {
76
- appearance: none;
77
- border: 0;
78
- margin: 0;
79
- padding: 0;
80
- position: relative;
81
- display: flex;
82
- align-items: center;
83
- justify-content: center;
84
- min-width: 0;
85
- color: var(--tabs-content-subtle);
86
- background: transparent;
87
- font: inherit;
88
- text-align: start;
89
- white-space: nowrap;
90
- cursor: pointer;
91
- }
92
-
74
+
75
+ .ds-tabs__tab {
76
+ appearance: none;
77
+ border: 0;
78
+ margin: 0;
79
+ padding: 0;
80
+ position: relative;
81
+ display: flex;
82
+ align-items: center;
83
+ justify-content: center;
84
+ min-width: 0;
85
+ color: var(--tabs-content-subtle);
86
+ background: transparent;
87
+ font: inherit;
88
+ text-align: start;
89
+ white-space: nowrap;
90
+ cursor: pointer;
91
+ }
92
+
93
+ .ds-tabs__tab:disabled,
94
+ .ds-tabs__tab[data-disabled] {
95
+ cursor: not-allowed;
96
+ opacity: var(--bh-opacity-60);
97
+ }
98
+
93
99
  .ds-tabs__tab:focus-visible {
94
100
  outline: var(--tabs-space-xxs) solid var(--tabs-content-brand);
95
101
  outline-offset: var(--tabs-space-xxs);
96
102
  }
97
-
98
- .ds-tabs__content {
99
- display: flex;
100
- align-items: center;
101
- justify-content: center;
102
- gap: var(--tabs-space-xs);
103
- min-width: 0;
104
- }
105
-
106
- .ds-tabs__label {
107
- display: block;
108
- flex: 0 0 auto;
109
- padding-inline: var(--tabs-label-padding-x);
110
- color: currentColor;
111
- font: inherit;
112
- white-space: nowrap;
113
- word-break: break-word;
114
- }
115
-
116
- .ds-tabs__tab[aria-selected="true"] {
103
+
104
+ .ds-tabs__content {
105
+ display: flex;
106
+ align-items: center;
107
+ justify-content: center;
108
+ gap: var(--tabs-space-xs);
109
+ min-width: 0;
110
+ }
111
+
112
+ .ds-tabs__label {
113
+ display: block;
114
+ flex: 0 0 auto;
115
+ padding-inline: var(--tabs-label-padding-x);
116
+ color: currentColor;
117
+ font: inherit;
118
+ white-space: nowrap;
119
+ word-break: break-word;
120
+ }
121
+
122
+ .ds-tabs__tab[aria-selected="true"],
123
+ .ds-tabs__tab[data-state="active"] {
117
124
  color: var(--tabs-content-default);
118
125
  font-weight: var(--bh-font-weight-medium);
119
126
  }
120
-
121
- .ds-tabs--segment .ds-tabs__tab,
122
- .ds-tabs--rounded .ds-tabs__tab {
123
- gap: var(--tabs-space-xs);
124
- }
125
-
126
- .ds-tabs--segment .ds-tabs__tab {
127
- border-radius: var(--tabs-radius-full);
128
- }
129
-
130
- .ds-tabs--rounded .ds-tabs__tab {
131
- border-radius: var(--tabs-radius-md);
132
- }
133
-
127
+
128
+ .ds-tabs--segment .ds-tabs__tab,
129
+ .ds-tabs--rounded .ds-tabs__tab {
130
+ gap: var(--tabs-space-xs);
131
+ }
132
+
133
+ .ds-tabs--segment .ds-tabs__tab {
134
+ border-radius: var(--tabs-radius-full);
135
+ }
136
+
137
+ .ds-tabs--rounded .ds-tabs__tab {
138
+ border-radius: var(--tabs-radius-md);
139
+ }
140
+
134
141
  .ds-tabs--segment .ds-tabs__tab[aria-selected="true"],
135
- .ds-tabs--rounded .ds-tabs__tab[aria-selected="true"] {
142
+ .ds-tabs--segment .ds-tabs__tab[data-state="active"],
143
+ .ds-tabs--rounded .ds-tabs__tab[aria-selected="true"],
144
+ .ds-tabs--rounded .ds-tabs__tab[data-state="active"] {
136
145
  background: var(--tabs-bg-default);
137
146
  box-shadow: var(--tabs-shadow-selected);
138
147
  }
@@ -154,20 +163,21 @@
154
163
  min-height: var(--tabs-height-segment-xs);
155
164
  padding: var(--tabs-space-xs) var(--tabs-space-lg);
156
165
  }
157
-
158
- .ds-tabs--segment.ds-tabs--full .ds-tabs__tab,
159
- .ds-tabs--rounded.ds-tabs--full .ds-tabs__tab {
160
- flex: 1 0 0;
161
- }
162
-
166
+
167
+ .ds-tabs--segment.ds-tabs--full .ds-tabs__tab,
168
+ .ds-tabs--rounded.ds-tabs--full .ds-tabs__tab {
169
+ flex: 1 0 0;
170
+ }
171
+
163
172
  .ds-tabs--underline .ds-tabs__tab {
164
173
  border-bottom: var(--tabs-space-xxs) solid transparent;
165
174
  }
166
-
167
- .ds-tabs--underline .ds-tabs__tab[aria-selected="true"] {
168
- border-bottom-color: var(--tabs-content-brand);
169
- }
170
-
175
+
176
+ .ds-tabs--underline .ds-tabs__tab[aria-selected="true"],
177
+ .ds-tabs--underline .ds-tabs__tab[data-state="active"] {
178
+ border-bottom-color: var(--tabs-content-brand);
179
+ }
180
+
171
181
  .ds-tabs--underline.ds-tabs--md .ds-tabs__tab {
172
182
  min-height: var(--tabs-height-underline-md);
173
183
  padding-block: var(--tabs-space-md);
@@ -182,16 +192,27 @@
182
192
  .ds-tabs--underline.ds-tabs--xs .ds-tabs__content {
183
193
  height: var(--tabs-height-underline-content);
184
194
  }
185
-
186
- .ds-tabs--underline.ds-tabs--full .ds-tabs__tab {
187
- flex: 1 0 0;
188
- }
189
-
190
- .ds-tabs--rtl .ds-tabs__tab {
191
- text-align: right;
192
- }
193
-
195
+
196
+ .ds-tabs--underline.ds-tabs--full .ds-tabs__tab {
197
+ flex: 1 0 0;
198
+ }
199
+
200
+ .ds-tabs--rtl .ds-tabs__tab {
201
+ text-align: start;
202
+ }
203
+
194
204
  .ds-tabs--rtl {
195
205
  --tabs-label-padding-x: var(--tabs-space-xxs);
196
206
  font-family: var(--bh-font-family);
197
207
  }
208
+
209
+ .ds-tabs__panel {
210
+ margin-top: var(--tabs-space-3xl);
211
+ color: var(--tabs-content-default);
212
+ outline: none;
213
+ }
214
+
215
+ .ds-tabs__panel:focus-visible {
216
+ outline: var(--tabs-space-xxs) solid var(--tabs-content-brand);
217
+ outline-offset: var(--tabs-space-xxs);
218
+ }
@@ -0,0 +1,246 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as TabsPrimitive from "@radix-ui/react-tabs"
5
+
6
+ import { cn } from "@/lib/utils"
7
+ import { useResolvedDirection } from "@/hooks/use-direction"
8
+
9
+ import "./tabs.css"
10
+
11
+ export type TabsVariant = "underline" | "segment" | "rounded"
12
+ export type TabsSize = "md" | "sm" | "xs"
13
+
14
+ export type TabsItem = {
15
+ content?: React.ReactNode
16
+ disabled?: boolean
17
+ label: React.ReactNode
18
+ value?: string
19
+ }
20
+
21
+ type RadixTabsRootProps = React.ComponentPropsWithoutRef<
22
+ typeof TabsPrimitive.Root
23
+ >
24
+
25
+ type TabsRootProps = React.ComponentProps<typeof TabsPrimitive.Root>
26
+ type TabsListProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> & {
27
+ fullWidth?: boolean
28
+ size?: TabsSize
29
+ variant?: TabsVariant
30
+ }
31
+ type TabsTriggerProps = React.ComponentPropsWithoutRef<
32
+ typeof TabsPrimitive.Trigger
33
+ >
34
+ type TabsContentProps = React.ComponentPropsWithoutRef<
35
+ typeof TabsPrimitive.Content
36
+ >
37
+
38
+ type TabsProps = Omit<
39
+ RadixTabsRootProps,
40
+ "defaultValue" | "dir" | "onValueChange" | "value"
41
+ > & {
42
+ activeIndex?: number
43
+ ariaLabel?: string
44
+ className?: string
45
+ defaultActiveIndex?: number
46
+ defaultValue?: string
47
+ dir?: "ltr" | "rtl"
48
+ fullWidth?: boolean
49
+ items?: Array<string | TabsItem>
50
+ onActiveIndexChange?: (index: number) => void
51
+ onValueChange?: (value: string) => void
52
+ rootClassName?: string
53
+ size?: TabsSize
54
+ value?: string
55
+ variant?: TabsVariant
56
+ }
57
+
58
+ const TabsRoot = TabsPrimitive.Root
59
+
60
+ const TabsList = React.forwardRef<
61
+ React.ElementRef<typeof TabsPrimitive.List>,
62
+ TabsListProps
63
+ >(function TabsList(
64
+ {
65
+ className,
66
+ dir,
67
+ fullWidth = false,
68
+ size = "md",
69
+ variant = "underline",
70
+ ...props
71
+ },
72
+ ref
73
+ ) {
74
+ const isRtl = dir === "rtl"
75
+
76
+ return (
77
+ <TabsPrimitive.List
78
+ data-size={size}
79
+ data-slot="tabs-list"
80
+ data-variant={variant}
81
+ dir={dir}
82
+ ref={ref}
83
+ className={cn(
84
+ "ds-tabs",
85
+ `ds-tabs--${variant}`,
86
+ `ds-tabs--${size}`,
87
+ fullWidth ? "ds-tabs--full" : "ds-tabs--hug",
88
+ isRtl ? "ds-tabs--rtl" : dir === "ltr" ? "ds-tabs--ltr" : undefined,
89
+ className
90
+ )}
91
+ {...props}
92
+ />
93
+ )
94
+ })
95
+
96
+ const TabsTrigger = React.forwardRef<
97
+ React.ElementRef<typeof TabsPrimitive.Trigger>,
98
+ TabsTriggerProps
99
+ >(function TabsTrigger({ children, className, dir = "auto", ...props }, ref) {
100
+ return (
101
+ <TabsPrimitive.Trigger
102
+ data-slot="tabs-trigger"
103
+ ref={ref}
104
+ className={cn("ds-tabs__tab", className)}
105
+ {...props}
106
+ >
107
+ <span className="ds-tabs__content">
108
+ <span className="ds-tabs__label" dir={dir}>
109
+ {children}
110
+ </span>
111
+ </span>
112
+ </TabsPrimitive.Trigger>
113
+ )
114
+ })
115
+
116
+ const TabsContent = React.forwardRef<
117
+ React.ElementRef<typeof TabsPrimitive.Content>,
118
+ TabsContentProps
119
+ >(function TabsContent({ className, ...props }, ref) {
120
+ return (
121
+ <TabsPrimitive.Content
122
+ data-slot="tabs-content"
123
+ ref={ref}
124
+ className={cn("ds-tabs__panel", className)}
125
+ {...props}
126
+ />
127
+ )
128
+ })
129
+
130
+ function Tabs({
131
+ activeIndex,
132
+ ariaLabel,
133
+ children,
134
+ className = "",
135
+ defaultActiveIndex = 0,
136
+ defaultValue,
137
+ dir,
138
+ fullWidth = false,
139
+ items = [],
140
+ onActiveIndexChange,
141
+ onValueChange,
142
+ rootClassName,
143
+ size = "md",
144
+ value,
145
+ variant = "underline",
146
+ ...props
147
+ }: TabsProps) {
148
+ const rootRef = React.useRef<React.ElementRef<typeof TabsPrimitive.Root>>(null)
149
+ const resolvedDirection = useResolvedDirection(dir, rootRef)
150
+ const normalizedItems = items.map(normalizeTabsItem)
151
+ const activeValue =
152
+ value ?? (activeIndex === undefined ? undefined : normalizedItems[activeIndex]?.value)
153
+ const uncontrolledDefaultValue =
154
+ defaultValue ??
155
+ normalizedItems[defaultActiveIndex]?.value ??
156
+ normalizedItems[0]?.value
157
+ const hasPanels = normalizedItems.some((item) => item.content !== undefined)
158
+
159
+ function handleValueChange(nextValue: string) {
160
+ onValueChange?.(nextValue)
161
+
162
+ const nextIndex = normalizedItems.findIndex((item) => item.value === nextValue)
163
+ if (nextIndex >= 0) {
164
+ onActiveIndexChange?.(nextIndex)
165
+ }
166
+ }
167
+
168
+ return (
169
+ <TabsRoot
170
+ data-slot="tabs-root"
171
+ defaultValue={activeValue === undefined ? uncontrolledDefaultValue : undefined}
172
+ dir={resolvedDirection}
173
+ ref={rootRef}
174
+ value={activeValue}
175
+ onValueChange={handleValueChange}
176
+ className={rootClassName}
177
+ {...props}
178
+ >
179
+ <TabsList
180
+ aria-label={ariaLabel}
181
+ className={className}
182
+ dir={resolvedDirection}
183
+ fullWidth={fullWidth}
184
+ size={size}
185
+ variant={variant}
186
+ >
187
+ {normalizedItems.map((item) => (
188
+ <TabsTrigger
189
+ disabled={item.disabled}
190
+ key={item.value}
191
+ value={item.value}
192
+ >
193
+ {item.label}
194
+ </TabsTrigger>
195
+ ))}
196
+ </TabsList>
197
+ {hasPanels
198
+ ? normalizedItems.map((item) =>
199
+ item.content === undefined ? null : (
200
+ <TabsContent key={`${item.value}-panel`} value={item.value}>
201
+ {item.content}
202
+ </TabsContent>
203
+ )
204
+ )
205
+ : null}
206
+ {children}
207
+ </TabsRoot>
208
+ )
209
+ }
210
+
211
+ function normalizeTabsItem(item: string | TabsItem, index: number): TabsItem & {
212
+ value: string
213
+ } {
214
+ if (typeof item === "string") {
215
+ return {
216
+ label: item,
217
+ value: getTabsItemValue(item, index),
218
+ }
219
+ }
220
+
221
+ return {
222
+ ...item,
223
+ value: item.value ?? getTabsItemValue(item.label, index),
224
+ }
225
+ }
226
+
227
+ function getTabsItemValue(label: React.ReactNode, index: number) {
228
+ if (typeof label !== "string") return `tab-${index}`
229
+
230
+ const slug = label
231
+ .trim()
232
+ .toLowerCase()
233
+ .replace(/[^a-z0-9]+/g, "-")
234
+ .replace(/(^-|-$)/g, "")
235
+
236
+ return slug || `tab-${index}`
237
+ }
238
+
239
+ export { Tabs, TabsContent, TabsList, TabsRoot, TabsTrigger }
240
+ export type {
241
+ TabsContentProps,
242
+ TabsListProps,
243
+ TabsProps,
244
+ TabsRootProps,
245
+ TabsTriggerProps,
246
+ }
@@ -372,7 +372,7 @@ function TagIcon({
372
372
  )}
373
373
  {...props}
374
374
  >
375
- {children ?? <PlusIcon aria-hidden="true" strokeWidth={2.25} />}
375
+ {children ?? <PlusIcon aria-hidden="true" strokeWidth="var(--bh-icon-stroke-225)" />}
376
376
  </span>
377
377
  )
378
378
  }
@@ -405,7 +405,7 @@ function TagCloseButton({
405
405
  <XIcon
406
406
  aria-hidden="true"
407
407
  className="size-[var(--bh-space-xl-12)]"
408
- strokeWidth={2}
408
+ strokeWidth="var(--bh-icon-stroke-200)"
409
409
  />
410
410
  </button>
411
411
  )
@@ -54,7 +54,7 @@ const textareaSurface = cva(
54
54
  "min-h-[var(--bh-textarea-min-height)] p-[var(--bh-textarea-padding)]",
55
55
  "transition-[background-color,box-shadow]",
56
56
  "[--bh-textarea-border:var(--bh-border-input)] [--shadow-textarea:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-textarea-border,var(--bh-border-input)),var(--shadow-component-default)]",
57
- "after:pointer-events-none after:absolute after:inset-[var(--bh-textarea-focus-ring-inset)] after:rounded-[var(--bh-textarea-radius)] after:border-[length:var(--bh-textarea-focus-ring-width)] after:border-[var(--bh-border-focus)] after:opacity-0 after:content-['']",
57
+ "after:pointer-events-none after:absolute after:inset-[var(--bh-textarea-focus-ring-inset)] after:rounded-[var(--bh-textarea-radius)] after:border-[length:var(--bh-textarea-focus-ring-width)] after:border-[var(--bh-border-focus)] after:opacity-[var(--bh-opacity-0)] after:content-['']",
58
58
  "focus-within:after:opacity-[var(--bh-opacity-30)]",
59
59
  ],
60
60
  {
@@ -539,14 +539,14 @@ function TextareaIcon({
539
539
  aria-hidden="true"
540
540
  className="size-[var(--bh-textarea-helper-icon-size)]"
541
541
  focusable="false"
542
- strokeWidth={2.25}
542
+ strokeWidth="var(--bh-icon-stroke-225)"
543
543
  />
544
544
  ) : (
545
545
  <InfoIcon
546
546
  aria-hidden="true"
547
547
  className="size-[var(--bh-textarea-helper-icon-size)]"
548
548
  focusable="false"
549
- strokeWidth={2.25}
549
+ strokeWidth="var(--bh-icon-stroke-225)"
550
550
  />
551
551
  )}
552
552
  </span>
@@ -563,7 +563,7 @@ function CloseIcon() {
563
563
  aria-hidden="true"
564
564
  className="size-[var(--bh-textarea-close-icon-size)]"
565
565
  focusable="false"
566
- strokeWidth={2.5}
566
+ strokeWidth="var(--bh-icon-stroke-250)"
567
567
  />
568
568
  </span>
569
569
  )