banhaten 0.3.0 → 0.5.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 (348) hide show
  1. package/CHANGELOG.md +139 -73
  2. package/CONTRIBUTING.md +46 -44
  3. package/README.md +192 -190
  4. package/SECURITY.md +15 -15
  5. package/banhaten.config.example.json +11 -11
  6. package/docs/design-spacing.md +13 -0
  7. package/docs/design-system/README.md +22 -20
  8. package/docs/design-system/adoption-quality-remediation.md +87 -87
  9. package/docs/design-system/agent-usage.md +13 -13
  10. package/docs/design-system/appearance-presets.md +189 -189
  11. package/docs/design-system/appearances/default.md +94 -94
  12. package/docs/design-system/appearances/rounded.md +95 -95
  13. package/docs/design-system/appearances/sharp.md +95 -95
  14. package/docs/design-system/component-docs-pattern.md +81 -81
  15. package/docs/design-system/component-implementation-ownership-audit.md +245 -244
  16. package/docs/design-system/component-quality-audit-2026-08-20.md +225 -223
  17. package/docs/design-system/component-showcase-consistency-report.md +219 -219
  18. package/docs/design-system/component-token-consistency-audit.md +173 -173
  19. package/docs/design-system/components/README.md +90 -76
  20. package/docs/design-system/components/accordion.md +53 -53
  21. package/docs/design-system/components/activity-feed.md +94 -94
  22. package/docs/design-system/components/activity-heatmap.md +57 -57
  23. package/docs/design-system/components/agent-input.md +85 -0
  24. package/docs/design-system/components/alert-dialog.md +72 -72
  25. package/docs/design-system/components/alert.md +98 -98
  26. package/docs/design-system/components/approval-card.md +134 -0
  27. package/docs/design-system/components/aspect-ratio.md +45 -45
  28. package/docs/design-system/components/attribute.md +89 -89
  29. package/docs/design-system/components/autocomplete.md +75 -75
  30. package/docs/design-system/components/avatar.md +54 -54
  31. package/docs/design-system/components/badge.md +54 -54
  32. package/docs/design-system/components/banner.md +87 -87
  33. package/docs/design-system/components/breadcrumbs.md +177 -176
  34. package/docs/design-system/components/button-group.md +98 -98
  35. package/docs/design-system/components/button.md +83 -80
  36. package/docs/design-system/components/card.md +88 -88
  37. package/docs/design-system/components/carousel.md +46 -46
  38. package/docs/design-system/components/catalog-components.md +47 -47
  39. package/docs/design-system/components/chart.md +95 -24
  40. package/docs/design-system/components/checkbox.md +54 -54
  41. package/docs/design-system/components/collapsible.md +67 -53
  42. package/docs/design-system/components/command.md +62 -62
  43. package/docs/design-system/components/context-menu.md +46 -46
  44. package/docs/design-system/components/date-picker.md +81 -79
  45. package/docs/design-system/components/divider.md +102 -102
  46. package/docs/design-system/components/empty-state.md +56 -56
  47. package/docs/design-system/components/field.md +70 -70
  48. package/docs/design-system/components/file-upload.md +187 -187
  49. package/docs/design-system/components/hover-card.md +48 -48
  50. package/docs/design-system/components/icons.md +48 -48
  51. package/docs/design-system/components/inline-citations.md +93 -0
  52. package/docs/design-system/components/input-group.md +58 -58
  53. package/docs/design-system/components/input-otp.md +56 -56
  54. package/docs/design-system/components/input.md +117 -61
  55. package/docs/design-system/components/item.md +59 -57
  56. package/docs/design-system/components/kbd.md +54 -49
  57. package/docs/design-system/components/label.md +49 -49
  58. package/docs/design-system/components/menu.md +71 -61
  59. package/docs/design-system/components/menubar.md +47 -47
  60. package/docs/design-system/components/modal.md +105 -105
  61. package/docs/design-system/components/native-select.md +54 -54
  62. package/docs/design-system/components/navigation-menu.md +50 -50
  63. package/docs/design-system/components/onboarding-step-list-item.md +82 -82
  64. package/docs/design-system/components/page-header.md +93 -86
  65. package/docs/design-system/components/pagination.md +57 -57
  66. package/docs/design-system/components/popover.md +60 -60
  67. package/docs/design-system/components/progress-slider.md +50 -50
  68. package/docs/design-system/components/progress.md +76 -76
  69. package/docs/design-system/components/radio-card.md +51 -51
  70. package/docs/design-system/components/radio-group.md +57 -57
  71. package/docs/design-system/components/resizable.md +44 -44
  72. package/docs/design-system/components/rolling-count.md +61 -0
  73. package/docs/design-system/components/scroll-area.md +51 -51
  74. package/docs/design-system/components/segmented-control.md +54 -54
  75. package/docs/design-system/components/select.md +68 -62
  76. package/docs/design-system/components/sheet.md +67 -67
  77. package/docs/design-system/components/shimmer-text.md +67 -0
  78. package/docs/design-system/components/sidebar.md +127 -76
  79. package/docs/design-system/components/skeleton.md +75 -75
  80. package/docs/design-system/components/slideout.md +65 -65
  81. package/docs/design-system/components/slider.md +63 -63
  82. package/docs/design-system/components/social-button.md +50 -50
  83. package/docs/design-system/components/spinner.md +62 -62
  84. package/docs/design-system/components/steps.md +65 -65
  85. package/docs/design-system/components/streaming-text.md +82 -0
  86. package/docs/design-system/components/table.md +419 -414
  87. package/docs/design-system/components/tabs.md +59 -55
  88. package/docs/design-system/components/tag.md +80 -80
  89. package/docs/design-system/components/task-list.md +95 -0
  90. package/docs/design-system/components/textarea.md +50 -50
  91. package/docs/design-system/components/thinking-reasoning.md +72 -0
  92. package/docs/design-system/components/thinking-state.md +67 -0
  93. package/docs/design-system/components/timeline.md +83 -83
  94. package/docs/design-system/components/toast.md +58 -58
  95. package/docs/design-system/components/toggle.md +81 -81
  96. package/docs/design-system/components/toolbar.md +92 -87
  97. package/docs/design-system/components/tooltip.md +94 -92
  98. package/docs/design-system/components/typography.md +18 -18
  99. package/docs/design-system/density-and-elevation.md +65 -61
  100. package/docs/design-system/design-system-test-missing-items.md +368 -368
  101. package/docs/design-system/direction-and-localization.md +23 -23
  102. package/docs/design-system/form-recipes.md +73 -73
  103. package/docs/design-system/gap-promotion-policy.md +21 -21
  104. package/docs/design-system/icons.md +74 -69
  105. package/docs/design-system/installation.md +70 -68
  106. package/docs/design-system/launch-comparison-shadcn.md +98 -98
  107. package/docs/design-system/registry-and-cli.md +64 -64
  108. package/docs/design-system/release-policy.md +30 -30
  109. package/docs/design-system/roadmap.md +14 -14
  110. package/docs/design-system/support-matrix.md +23 -23
  111. package/docs/design-system/tabs.md +54 -53
  112. package/docs/design-system/template-gap-audit.md +32 -0
  113. package/docs/design-system/templates.md +59 -0
  114. package/docs/design-system/token-governance.md +72 -60
  115. package/package.json +98 -98
  116. package/registry/component-contract.json +40 -40
  117. package/registry/components/accordion.tsx +155 -155
  118. package/registry/components/agent-input-parts.tsx +552 -0
  119. package/registry/components/agent-input-state.ts +156 -0
  120. package/registry/components/agent-input.css +307 -0
  121. package/registry/components/agent-input.tsx +382 -0
  122. package/registry/components/alert-dialog.tsx +297 -297
  123. package/registry/components/alert.tsx +293 -293
  124. package/registry/components/approval-card.css +409 -0
  125. package/registry/components/approval-card.tsx +401 -0
  126. package/registry/components/approval-frame.tsx +108 -0
  127. package/registry/components/approval-questions.tsx +559 -0
  128. package/registry/components/aspect-ratio.tsx +30 -30
  129. package/registry/components/attribute.tsx +454 -448
  130. package/registry/components/autocomplete.tsx +598 -651
  131. package/registry/components/avatar.tsx +385 -381
  132. package/registry/components/badge.tsx +629 -629
  133. package/registry/components/button-group.tsx +565 -565
  134. package/registry/components/button.tsx +127 -123
  135. package/registry/components/card.tsx +638 -638
  136. package/registry/components/carousel.tsx +296 -296
  137. package/registry/components/chart-block-data.ts +177 -0
  138. package/registry/components/chart-block.css +171 -0
  139. package/registry/components/chart-block.tsx +260 -0
  140. package/registry/components/chart-interactions.ts +123 -0
  141. package/registry/components/chart-options.cartesian.ts +162 -0
  142. package/registry/components/chart-options.paint.ts +99 -0
  143. package/registry/components/chart-options.parts.ts +72 -0
  144. package/registry/components/chart-options.radial.ts +109 -0
  145. package/registry/components/chart-options.ts +44 -0
  146. package/registry/components/chart-options.types.ts +67 -0
  147. package/registry/components/chart.tsx +203 -203
  148. package/registry/components/checkbox.tsx +247 -247
  149. package/registry/components/collapsible.tsx +109 -69
  150. package/registry/components/command.tsx +208 -208
  151. package/registry/components/context-menu.tsx +236 -236
  152. package/registry/components/data-chart.tsx +40 -0
  153. package/registry/components/date-picker.tsx +1289 -1277
  154. package/registry/components/echart-theme.ts +246 -0
  155. package/registry/components/echart.css +49 -0
  156. package/registry/components/echart.tsx +178 -0
  157. package/registry/components/expanded/ActivityFeed.tsx +227 -227
  158. package/registry/components/expanded/ActivityHeatmap.tsx +64 -64
  159. package/registry/components/expanded/Banner.tsx +179 -179
  160. package/registry/components/expanded/Breadcrumbs.tsx +131 -131
  161. package/registry/components/expanded/CatalogComponentsShowcase.tsx +205 -205
  162. package/registry/components/expanded/CatalogDivider.tsx +49 -49
  163. package/registry/components/expanded/EmptyState.tsx +155 -155
  164. package/registry/components/expanded/FileUpload.tsx +543 -543
  165. package/registry/components/expanded/OnboardingStepListItem.tsx +60 -59
  166. package/registry/components/expanded/PageHeader.tsx +179 -166
  167. package/registry/components/expanded/Slideout.tsx +108 -108
  168. package/registry/components/expanded/Steps.tsx +402 -402
  169. package/registry/components/expanded/Table.tsx +1092 -1071
  170. package/registry/components/expanded/Timeline.tsx +203 -203
  171. package/registry/components/expanded/activityFeed.css +248 -248
  172. package/registry/components/expanded/activityHeatmap.css +66 -66
  173. package/registry/components/expanded/banner.css +260 -255
  174. package/registry/components/expanded/breadcrumbs.css +140 -140
  175. package/registry/components/expanded/catalogComponentsShowcase.css +87 -87
  176. package/registry/components/expanded/divider.css +75 -75
  177. package/registry/components/expanded/emptyState.css +111 -111
  178. package/registry/components/expanded/fileUpload.css +457 -457
  179. package/registry/components/expanded/onboardingStepListItem.css +135 -126
  180. package/registry/components/expanded/pageHeader.css +248 -290
  181. package/registry/components/expanded/slideout.css +276 -276
  182. package/registry/components/expanded/steps.css +356 -356
  183. package/registry/components/expanded/table.css +606 -603
  184. package/registry/components/expanded/timeline.css +286 -286
  185. package/registry/components/field.tsx +283 -283
  186. package/registry/components/hover-card.tsx +48 -48
  187. package/registry/components/inline-citations.css +126 -0
  188. package/registry/components/inline-citations.tsx +292 -0
  189. package/registry/components/input-content.ts +107 -107
  190. package/registry/components/input-group.tsx +130 -130
  191. package/registry/components/input-otp.tsx +584 -584
  192. package/registry/components/input.tsx +990 -972
  193. package/registry/components/item.tsx +189 -189
  194. package/registry/components/kbd.tsx +246 -151
  195. package/registry/components/label.tsx +78 -78
  196. package/registry/components/menu.tsx +978 -986
  197. package/registry/components/menubar.tsx +226 -226
  198. package/registry/components/modal.tsx +872 -872
  199. package/registry/components/native-select.tsx +205 -205
  200. package/registry/components/navigation-menu.tsx +171 -171
  201. package/registry/components/pagination.css +36 -36
  202. package/registry/components/pagination.tsx +560 -555
  203. package/registry/components/popover.tsx +338 -338
  204. package/registry/components/progress-slider.tsx +216 -216
  205. package/registry/components/radio-card.tsx +676 -677
  206. package/registry/components/radio-group.tsx +570 -570
  207. package/registry/components/resizable.tsx +108 -108
  208. package/registry/components/rolling-count.css +37 -0
  209. package/registry/components/rolling-count.tsx +110 -0
  210. package/registry/components/scroll-area.tsx +91 -91
  211. package/registry/components/segmented-control.tsx +87 -87
  212. package/registry/components/select-content.tsx +218 -218
  213. package/registry/components/select.tsx +937 -932
  214. package/registry/components/sheet.tsx +305 -305
  215. package/registry/components/shimmer-text.css +69 -0
  216. package/registry/components/shimmer-text.tsx +43 -0
  217. package/registry/components/sidebar.tsx +1433 -358
  218. package/registry/components/skeleton.css +54 -54
  219. package/registry/components/skeleton.tsx +482 -482
  220. package/registry/components/slider.tsx +380 -380
  221. package/registry/components/social-button.tsx +387 -387
  222. package/registry/components/spinner.tsx +78 -78
  223. package/registry/components/streaming-text.css +45 -0
  224. package/registry/components/streaming-text.tsx +165 -0
  225. package/registry/components/table-elements.tsx +128 -128
  226. package/registry/components/tabs.css +222 -218
  227. package/registry/components/tabs.tsx +241 -241
  228. package/registry/components/tag.tsx +423 -423
  229. package/registry/components/task-list.css +328 -0
  230. package/registry/components/task-list.tsx +429 -0
  231. package/registry/components/textarea.tsx +610 -610
  232. package/registry/components/thinking-reasoning.css +178 -0
  233. package/registry/components/thinking-reasoning.tsx +253 -0
  234. package/registry/components/thinking-state.css +8 -0
  235. package/registry/components/thinking-state.tsx +46 -0
  236. package/registry/components/toast.tsx +136 -136
  237. package/registry/components/toggle.tsx +256 -256
  238. package/registry/components/toolbar.tsx +539 -539
  239. package/registry/components/tooltip.tsx +433 -431
  240. package/registry/examples/agent-input-demo.tsx +90 -0
  241. package/registry/examples/alert-demo.tsx +34 -34
  242. package/registry/examples/alert-dialog-demo.tsx +42 -42
  243. package/registry/examples/approval-card-demo.tsx +83 -0
  244. package/registry/examples/aspect-ratio-demo.tsx +11 -11
  245. package/registry/examples/attribute-demo.tsx +65 -65
  246. package/registry/examples/autocomplete-demo.tsx +109 -109
  247. package/registry/examples/avatar-demo.tsx +132 -132
  248. package/registry/examples/badge-demo.tsx +69 -69
  249. package/registry/examples/button-demo.tsx +86 -83
  250. package/registry/examples/button-group-demo.tsx +63 -63
  251. package/registry/examples/card-demo.tsx +86 -86
  252. package/registry/examples/carousel-demo.tsx +43 -43
  253. package/registry/examples/chart-block-demo.tsx +5 -0
  254. package/registry/examples/chart-demo.tsx +18 -33
  255. package/registry/examples/checkbox-demo.tsx +62 -62
  256. package/registry/examples/collapsible-demo.tsx +16 -16
  257. package/registry/examples/collection-filter-bar-demo.tsx +135 -135
  258. package/registry/examples/command-demo.tsx +42 -42
  259. package/registry/examples/command-palette-demo.tsx +235 -236
  260. package/registry/examples/context-menu-demo.tsx +29 -29
  261. package/registry/examples/control-density-contract-demo.tsx +65 -61
  262. package/registry/examples/date-picker-demo.tsx +127 -127
  263. package/registry/examples/divider-demo.tsx +17 -17
  264. package/registry/examples/expanded/activity-heatmap-demo.tsx +29 -29
  265. package/registry/examples/expanded/banner-demo.tsx +48 -48
  266. package/registry/examples/expanded/breadcrumbs-demo.tsx +59 -59
  267. package/registry/examples/expanded/empty-state-demo.tsx +39 -39
  268. package/registry/examples/expanded/file-upload-demo.tsx +60 -60
  269. package/registry/examples/expanded/onboarding-step-demo.tsx +11 -11
  270. package/registry/examples/expanded/slideout-demo.tsx +15 -15
  271. package/registry/examples/expanded/steps-demo.tsx +29 -29
  272. package/registry/examples/expanded/tabs-demo.tsx +13 -13
  273. package/registry/examples/expanded/timeline-demo.tsx +18 -18
  274. package/registry/examples/field-demo.tsx +51 -51
  275. package/registry/examples/hover-card-demo.tsx +23 -23
  276. package/registry/examples/inline-citations-demo.tsx +31 -0
  277. package/registry/examples/input-actions-demo.tsx +36 -0
  278. package/registry/examples/input-demo.tsx +87 -87
  279. package/registry/examples/input-group-demo.tsx +16 -16
  280. package/registry/examples/input-otp-demo.tsx +72 -72
  281. package/registry/examples/item-demo.tsx +46 -46
  282. package/registry/examples/kbd-demo.tsx +12 -11
  283. package/registry/examples/label-demo.tsx +20 -20
  284. package/registry/examples/menu-demo.tsx +122 -122
  285. package/registry/examples/menubar-demo.tsx +34 -34
  286. package/registry/examples/modal-demo.tsx +74 -74
  287. package/registry/examples/native-select-demo.tsx +16 -16
  288. package/registry/examples/navigation-menu-demo.tsx +29 -29
  289. package/registry/examples/pagination-demo.tsx +20 -20
  290. package/registry/examples/popover-demo.tsx +547 -546
  291. package/registry/examples/progress-demo.tsx +37 -37
  292. package/registry/examples/progress-slider-demo.tsx +29 -29
  293. package/registry/examples/radio-card-demo.tsx +51 -51
  294. package/registry/examples/radio-group-demo.tsx +62 -62
  295. package/registry/examples/resizable-demo.tsx +22 -22
  296. package/registry/examples/results-toolbar-demo.tsx +44 -44
  297. package/registry/examples/rolling-count-demo.tsx +39 -0
  298. package/registry/examples/scroll-area-demo.tsx +15 -15
  299. package/registry/examples/segmented-control-demo.tsx +39 -39
  300. package/registry/examples/select-demo.tsx +87 -87
  301. package/registry/examples/sheet-demo.tsx +47 -47
  302. package/registry/examples/shimmer-text-demo.tsx +20 -0
  303. package/registry/examples/sidebar-demo.tsx +101 -57
  304. package/registry/examples/slider-demo.tsx +31 -31
  305. package/registry/examples/social-button-demo.tsx +61 -61
  306. package/registry/examples/spinner-demo.tsx +59 -59
  307. package/registry/examples/streaming-text-demo.tsx +28 -0
  308. package/registry/examples/tabs-demo.tsx +13 -13
  309. package/registry/examples/tag-demo.tsx +29 -29
  310. package/registry/examples/task-list-demo.tsx +74 -0
  311. package/registry/examples/textarea-demo.tsx +79 -79
  312. package/registry/examples/thinking-reasoning-demo.tsx +74 -0
  313. package/registry/examples/thinking-state-demo.tsx +13 -0
  314. package/registry/examples/toast-demo.tsx +35 -35
  315. package/registry/examples/toggle-demo.tsx +59 -59
  316. package/registry/examples/tooltip-demo.tsx +115 -115
  317. package/registry/guidance/USAGE.md +27 -27
  318. package/registry/guidance/charts.LICENSE.txt +24 -0
  319. package/registry/hooks/use-controllable-state.ts +33 -0
  320. package/registry/hooks/use-direction.ts +207 -207
  321. package/registry/hooks/use-listbox-navigation.ts +73 -0
  322. package/registry/hooks/use-motion-end.ts +55 -0
  323. package/registry/hooks/use-reduced-motion.ts +28 -0
  324. package/registry/index.json +1634 -184
  325. package/registry/styles/globals.css +1462 -1570
  326. package/registry.json +364 -10
  327. package/schema/config.schema.json +51 -51
  328. package/schema/registry.schema.json +194 -190
  329. package/schema/tokens.schema.json +167 -163
  330. package/src/cli/command-line.js +194 -194
  331. package/src/cli/config-editors.js +9 -9
  332. package/src/cli/doctor.js +399 -399
  333. package/src/cli/file-transaction.js +302 -295
  334. package/src/cli/global-css.js +153 -153
  335. package/src/cli/index.js +1002 -1009
  336. package/src/cli/install-plan.js +592 -592
  337. package/src/cli/install-receipt.js +171 -171
  338. package/src/cli/jsonc.js +203 -203
  339. package/src/cli/package-install-state.js +254 -254
  340. package/src/cli/package-manager.js +238 -238
  341. package/src/cli/package-plan.js +185 -185
  342. package/src/cli/package-version.js +97 -97
  343. package/src/cli/registry-query.js +207 -207
  344. package/src/cli/reporting.js +55 -55
  345. package/src/cli/schema-validation.js +19 -19
  346. package/src/cli/source-scanner.js +686 -686
  347. package/src/cli/vite-config.js +478 -478
  348. package/tokens/banhaten.tokens.json +458 -224
@@ -1,629 +1,629 @@
1
- import * as React from "react"
2
- import { Slot } from "@radix-ui/react-slot"
3
- import { cva, type VariantProps } from "class-variance-authority"
4
-
5
- import { cn } from "@/lib/utils"
6
-
7
- type BadgeStyle = "light" | "outline" | "solid"
8
- type BadgeColor =
9
- | "neutral"
10
- | "blue"
11
- | "green"
12
- | "amber"
13
- | "danger"
14
- | "purple"
15
- | "fuchsia"
16
- | "rose"
17
- | "sky"
18
- | "golden"
19
- type BadgeType =
20
- | "default"
21
- | "leading-icon"
22
- | "trailing-icon"
23
- | "dot"
24
- | "flag"
25
- type BadgeSize = "xs" | "sm" | "default" | "lg"
26
- type LegacyBadgeVariant =
27
- | "default"
28
- | "secondary"
29
- | "success"
30
- | "warning"
31
- | "destructive"
32
- | "outline"
33
- | "soft"
34
- | "soft-success"
35
- | "soft-warning"
36
- | "soft-destructive"
37
- | "ghost"
38
- | "link"
39
-
40
- const legacyVariantMap: Record<
41
- LegacyBadgeVariant,
42
- { badgeStyle: BadgeStyle; color: BadgeColor; className?: string }
43
- > = {
44
- default: { badgeStyle: "solid", color: "blue" },
45
- secondary: { badgeStyle: "light", color: "neutral" },
46
- success: { badgeStyle: "solid", color: "green" },
47
- warning: { badgeStyle: "solid", color: "amber" },
48
- destructive: { badgeStyle: "solid", color: "danger" },
49
- outline: { badgeStyle: "outline", color: "neutral" },
50
- soft: { badgeStyle: "light", color: "blue" },
51
- "soft-success": { badgeStyle: "light", color: "green" },
52
- "soft-warning": { badgeStyle: "light", color: "amber" },
53
- "soft-destructive": { badgeStyle: "light", color: "danger" },
54
- ghost: {
55
- badgeStyle: "light",
56
- color: "neutral",
57
- className: "bg-[var(--bh-interactive-ghost-default)]",
58
- },
59
- link: {
60
- badgeStyle: "light",
61
- color: "blue",
62
- className:
63
- "bg-[var(--bh-interactive-ghost-default)] px-[var(--bh-space-none)] text-[var(--bh-content-link)] underline-offset-[var(--bh-space-xs-4)] [a&]:hover:underline",
64
- },
65
- }
66
-
67
- const badgeVariants = cva(
68
- [
69
- "inline-flex min-w-0 shrink-0 items-center justify-center whitespace-nowrap",
70
- "rounded-[var(--bh-radius-full)] border border-transparent outline-none",
71
- "transition-[background-color,border-color,color,box-shadow]",
72
- "focus-visible:border-[var(--bh-border-focus)] focus-visible:ring-[length:var(--bh-badge-focus-ring-width)] focus-visible:ring-[var(--ring)]",
73
- "aria-invalid:border-[var(--bh-border-focus-danger)] aria-invalid:ring-[var(--danger-ring)]",
74
- "[&_svg]:pointer-events-none [&_svg]:shrink-0",
75
- "rtl:[&_svg[data-rtl-flip='true']]:-scale-x-100",
76
- ],
77
- {
78
- variants: {
79
- badgeStyle: {
80
- light: "border-transparent",
81
- outline: "border-[length:var(--bh-badge-outline-border-width)] bg-transparent",
82
- solid: "border-transparent",
83
- },
84
- color: {
85
- neutral: "",
86
- blue: "",
87
- green: "",
88
- amber: "",
89
- danger: "",
90
- purple: "",
91
- fuchsia: "",
92
- rose: "",
93
- sky: "",
94
- golden: "",
95
- },
96
- type: {
97
- default: "gap-[var(--bh-space-none)]",
98
- "leading-icon": "gap-[var(--bh-space-none)]",
99
- "trailing-icon": "gap-[var(--bh-space-none)]",
100
- dot: "gap-[var(--bh-space-xs-4)]",
101
- flag: "gap-[var(--bh-space-xs-4)]",
102
- },
103
- size: {
104
- xs: [
105
- "h-[var(--bh-space-5xl-24)]",
106
- "text-[length:var(--bh-text-body-3xs-medium-font-size)]",
107
- "font-[var(--bh-text-body-3xs-medium-font-weight)]",
108
- "leading-[var(--bh-text-body-3xs-medium-line-height)]",
109
- "tracking-[var(--bh-text-body-3xs-medium-letter-spacing)]",
110
- ],
111
- sm: [
112
- "h-[var(--bh-space-5xl-24)]",
113
- "text-[length:var(--bh-text-body-3xs-medium-font-size)]",
114
- "font-[var(--bh-text-body-3xs-medium-font-weight)]",
115
- "leading-[var(--bh-text-body-3xs-medium-line-height)]",
116
- "tracking-[var(--bh-text-body-3xs-medium-letter-spacing)]",
117
- ],
118
- default: [
119
- "h-[var(--bh-space-5xl-24)]",
120
- "text-[length:var(--bh-text-body-3xs-medium-font-size)]",
121
- "font-[var(--bh-text-body-3xs-medium-font-weight)]",
122
- "leading-[var(--bh-text-body-3xs-medium-line-height)]",
123
- "tracking-[var(--bh-text-body-3xs-medium-letter-spacing)]",
124
- ],
125
- lg: [
126
- "h-[calc(var(--bh-space-5xl-24)+var(--bh-space-xs-4))]",
127
- "text-[length:var(--bh-text-body-2xs-medium-font-size)]",
128
- "font-[var(--bh-text-body-2xs-medium-font-weight)]",
129
- "leading-[var(--bh-text-body-2xs-medium-line-height)]",
130
- "tracking-[var(--bh-text-body-2xs-medium-letter-spacing)]",
131
- ],
132
- },
133
- showNumber: {
134
- true: "gap-[var(--bh-space-none)] px-[var(--bh-space-xs-4)] text-center",
135
- false: "",
136
- },
137
- splitAction: {
138
- true:
139
- "overflow-hidden border-[var(--badge-split-border-color,currentColor)] pe-[var(--bh-space-none)]",
140
- false: "[&_svg]:size-[var(--bh-space-3xl-16)]",
141
- },
142
- },
143
- compoundVariants: [
144
- {
145
- type: "default",
146
- size: ["xs", "sm", "default"],
147
- showNumber: false,
148
- class: "px-[var(--bh-space-md-8)]",
149
- },
150
- {
151
- type: "default",
152
- size: "lg",
153
- showNumber: false,
154
- class: "px-[var(--bh-space-xl-12)]",
155
- },
156
- {
157
- type: ["leading-icon", "trailing-icon", "dot"],
158
- showNumber: false,
159
- splitAction: false,
160
- class: "px-[var(--bh-space-md-8)]",
161
- },
162
- {
163
- type: "trailing-icon",
164
- showNumber: false,
165
- splitAction: true,
166
- class: "ps-[var(--bh-space-md-8)]",
167
- },
168
- {
169
- type: "flag",
170
- showNumber: false,
171
- class: "ps-[var(--bh-space-xs-4)] pe-[var(--bh-space-md-8)]",
172
- },
173
- {
174
- size: ["xs", "sm", "default"],
175
- showNumber: true,
176
- class:
177
- "w-[var(--bh-space-5xl-24)] font-[var(--bh-text-body-3xs-semibold-font-weight)]",
178
- },
179
- {
180
- size: "lg",
181
- showNumber: true,
182
- class:
183
- "w-[calc(var(--bh-space-5xl-24)+var(--bh-space-xs-4))] font-[var(--bh-text-body-2xs-semibold-font-weight)]",
184
- },
185
- {
186
- badgeStyle: "light",
187
- color: "neutral",
188
- class:
189
- "bg-[var(--bh-bg-neutral-subtle)] text-[var(--bh-content-default)] [--badge-dot-color:var(--bh-content-muted)] [--badge-split-border-color:var(--bh-border-default)]",
190
- },
191
- {
192
- badgeStyle: "outline",
193
- color: "neutral",
194
- class:
195
- "border-[var(--bh-border-strong)] text-[var(--bh-content-default)] [--badge-dot-color:var(--bh-content-default)] [--badge-split-border-color:var(--bh-border-default)]",
196
- },
197
- {
198
- badgeStyle: "solid",
199
- color: "neutral",
200
- class:
201
- "bg-[var(--bh-bg-neutral-bold)] text-[var(--bh-content-on-neutral)] [--badge-dot-color:var(--bh-content-on-neutral)] [--badge-split-border-color:var(--bh-border-subtle)]",
202
- },
203
- {
204
- badgeStyle: "light",
205
- color: "blue",
206
- class:
207
- "bg-[var(--bh-bg-accent-blue-subtle)] text-[var(--bh-content-accent-blue-strong)] [--badge-dot-color:var(--bh-content-accent-blue-default)] [--badge-split-border-color:var(--bh-border-accent-blue-subtle)]",
208
- },
209
- {
210
- badgeStyle: "outline",
211
- color: "blue",
212
- class:
213
- "border-[var(--bh-border-accent-blue-strong)] text-[var(--bh-content-accent-blue-strong)] [--badge-dot-color:var(--bh-content-accent-blue-strong)] [--badge-split-border-color:var(--bh-border-accent-blue-subtle)]",
214
- },
215
- {
216
- badgeStyle: "solid",
217
- color: "blue",
218
- class:
219
- "bg-[var(--bh-bg-accent-blue-bold)] text-[var(--bh-content-on-color)] [--badge-dot-color:var(--bh-content-on-color)] [--badge-split-border-color:var(--bh-border-accent-blue-subtle)]",
220
- },
221
- {
222
- badgeStyle: "light",
223
- color: "green",
224
- class:
225
- "bg-[var(--bh-bg-accent-green-subtle)] text-[var(--bh-content-accent-green-strong)] [--badge-dot-color:var(--bh-content-accent-green-default)] [--badge-split-border-color:var(--bh-border-accent-green-subtle)]",
226
- },
227
- {
228
- badgeStyle: "outline",
229
- color: "green",
230
- class:
231
- "border-[var(--bh-border-accent-green-strong)] text-[var(--bh-content-accent-green-strong)] [--badge-dot-color:var(--bh-content-accent-green-strong)] [--badge-split-border-color:var(--bh-border-accent-green-subtle)]",
232
- },
233
- {
234
- badgeStyle: "solid",
235
- color: "green",
236
- class:
237
- "bg-[var(--bh-bg-accent-green-default)] text-[var(--bh-content-on-light)] [--badge-dot-color:var(--bh-content-on-light)] [--badge-split-border-color:var(--bh-border-accent-green-subtle)]",
238
- },
239
- {
240
- badgeStyle: "light",
241
- color: "amber",
242
- class:
243
- "bg-[var(--bh-bg-accent-amber-subtle)] text-[var(--bh-content-accent-amber-strong)] [--badge-dot-color:var(--bh-content-accent-amber-default)] [--badge-split-border-color:var(--bh-border-accent-amber-subtle)]",
244
- },
245
- {
246
- badgeStyle: "outline",
247
- color: "amber",
248
- class:
249
- "border-[var(--bh-border-accent-amber-strong)] text-[var(--bh-content-accent-amber-strong)] [--badge-dot-color:var(--bh-content-accent-amber-strong)] [--badge-split-border-color:var(--bh-border-accent-amber-subtle)]",
250
- },
251
- {
252
- badgeStyle: "solid",
253
- color: "amber",
254
- class:
255
- "bg-[var(--bh-bg-accent-amber-default)] text-[var(--bh-content-on-light)] [--badge-dot-color:var(--bh-content-on-light)] [--badge-split-border-color:var(--bh-border-accent-amber-subtle)]",
256
- },
257
- {
258
- badgeStyle: "light",
259
- color: "danger",
260
- class:
261
- "bg-[var(--bh-bg-accent-red-subtle)] text-[var(--bh-content-accent-red-strong)] [--badge-dot-color:var(--bh-content-accent-red-default)] [--badge-split-border-color:var(--bh-border-accent-red-subtle)]",
262
- },
263
- {
264
- badgeStyle: "outline",
265
- color: "danger",
266
- class:
267
- "border-[var(--bh-border-accent-red-strong)] text-[var(--bh-content-accent-red-strong)] [--badge-dot-color:var(--bh-content-accent-red-strong)] [--badge-split-border-color:var(--bh-border-accent-red-subtle)]",
268
- },
269
- {
270
- badgeStyle: "solid",
271
- color: "danger",
272
- class:
273
- "bg-[var(--bh-bg-accent-red-default)] text-[var(--bh-content-on-color)] [--badge-dot-color:var(--bh-content-on-color)] [--badge-split-border-color:var(--bh-border-accent-red-subtle)]",
274
- },
275
- {
276
- badgeStyle: "light",
277
- color: "purple",
278
- class:
279
- "bg-[var(--bh-bg-accent-purple-subtle)] text-[var(--bh-content-accent-purple-strong)] [--badge-dot-color:var(--bh-content-accent-purple-default)] [--badge-split-border-color:var(--bh-border-accent-purple-subtle)]",
280
- },
281
- {
282
- badgeStyle: "outline",
283
- color: "purple",
284
- class:
285
- "border-[var(--bh-border-accent-purple-strong)] text-[var(--bh-content-accent-purple-strong)] [--badge-dot-color:var(--bh-content-accent-purple-strong)] [--badge-split-border-color:var(--bh-border-accent-purple-subtle)]",
286
- },
287
- {
288
- badgeStyle: "solid",
289
- color: "purple",
290
- class:
291
- "bg-[var(--bh-bg-accent-purple-default)] text-[var(--bh-content-on-color)] [--badge-dot-color:var(--bh-content-on-color)] [--badge-split-border-color:var(--bh-border-accent-purple-subtle)]",
292
- },
293
- {
294
- badgeStyle: "light",
295
- color: "fuchsia",
296
- class:
297
- "bg-[var(--bh-bg-accent-fuchsia-subtle)] text-[var(--bh-content-accent-fuchsia-strong)] [--badge-dot-color:var(--bh-content-accent-fuchsia-default)] [--badge-split-border-color:var(--bh-border-accent-fuchsia-subtle)]",
298
- },
299
- {
300
- badgeStyle: "outline",
301
- color: "fuchsia",
302
- class:
303
- "border-[var(--bh-border-accent-fuchsia-strong)] text-[var(--bh-content-accent-fuchsia-strong)] [--badge-dot-color:var(--bh-content-accent-fuchsia-strong)] [--badge-split-border-color:var(--bh-border-accent-fuchsia-subtle)]",
304
- },
305
- {
306
- badgeStyle: "solid",
307
- color: "fuchsia",
308
- class:
309
- "bg-[var(--bh-bg-accent-fuchsia-default)] text-[var(--bh-content-on-light)] [--badge-dot-color:var(--bh-content-on-light)] [--badge-split-border-color:var(--bh-border-accent-fuchsia-subtle)]",
310
- },
311
- {
312
- badgeStyle: "light",
313
- color: "rose",
314
- class:
315
- "bg-[var(--bh-bg-accent-rose-subtle)] text-[var(--bh-content-accent-rose-strong)] [--badge-dot-color:var(--bh-content-accent-rose-default)] [--badge-split-border-color:var(--bh-border-accent-rose-subtle)]",
316
- },
317
- {
318
- badgeStyle: "outline",
319
- color: "rose",
320
- class:
321
- "border-[var(--bh-border-accent-rose-strong)] text-[var(--bh-content-accent-rose-strong)] [--badge-dot-color:var(--bh-content-accent-rose-strong)] [--badge-split-border-color:var(--bh-border-accent-rose-subtle)]",
322
- },
323
- {
324
- badgeStyle: "solid",
325
- color: "rose",
326
- class:
327
- "bg-[var(--bh-bg-accent-rose-bold)] text-[var(--bh-content-on-color)] [--badge-dot-color:var(--bh-content-on-color)] [--badge-split-border-color:var(--bh-border-accent-rose-subtle)]",
328
- },
329
- {
330
- badgeStyle: "light",
331
- color: "sky",
332
- class:
333
- "bg-[var(--bh-bg-accent-sky-subtle)] text-[var(--bh-content-accent-sky-strong)] [--badge-dot-color:var(--bh-content-accent-sky-default)] [--badge-split-border-color:var(--bh-border-accent-sky-subtle)]",
334
- },
335
- {
336
- badgeStyle: "outline",
337
- color: "sky",
338
- class:
339
- "border-[var(--bh-border-accent-sky-strong)] text-[var(--bh-content-accent-sky-strong)] [--badge-dot-color:var(--bh-content-accent-sky-strong)] [--badge-split-border-color:var(--bh-border-accent-sky-subtle)]",
340
- },
341
- {
342
- badgeStyle: "solid",
343
- color: "sky",
344
- class:
345
- "bg-[var(--bh-bg-accent-sky-base)] text-[var(--bh-content-on-light)] [--badge-dot-color:var(--bh-content-on-light)] [--badge-split-border-color:var(--bh-border-accent-sky-subtle)]",
346
- },
347
- {
348
- badgeStyle: "light",
349
- color: "golden",
350
- class:
351
- "bg-[var(--bh-bg-accent-golden-subtle)] text-[var(--bh-content-accent-golden-strong)] [--badge-dot-color:var(--bh-content-accent-golden-default)] [--badge-split-border-color:var(--bh-border-accent-golden-subtle)]",
352
- },
353
- {
354
- badgeStyle: "outline",
355
- color: "golden",
356
- class:
357
- "border-[var(--bh-border-accent-golden-strong)] text-[var(--bh-content-accent-golden-strong)] [--badge-dot-color:var(--bh-content-accent-golden-strong)] [--badge-split-border-color:var(--bh-border-accent-golden-subtle)]",
358
- },
359
- {
360
- badgeStyle: "solid",
361
- color: "golden",
362
- class:
363
- "bg-[var(--bh-bg-accent-golden-default)] text-[var(--bh-content-on-light)] [--badge-dot-color:var(--bh-content-on-light)] [--badge-split-border-color:var(--bh-border-accent-golden-subtle)]",
364
- },
365
- ],
366
- defaultVariants: {
367
- badgeStyle: "light",
368
- color: "neutral",
369
- type: "default",
370
- size: "sm",
371
- showNumber: false,
372
- splitAction: false,
373
- },
374
- }
375
- )
376
-
377
- type BadgeProps = Omit<React.ComponentProps<"span">, "color"> &
378
- VariantProps<typeof badgeVariants> & {
379
- asChild?: boolean
380
- variant?: LegacyBadgeVariant
381
- flag?: React.ReactNode
382
- number?: React.ReactNode
383
- }
384
-
385
- type BadgeContentProps = {
386
- children: React.ReactNode
387
- flag?: React.ReactNode
388
- number?: React.ReactNode
389
- size: BadgeSize
390
- showNumber: boolean
391
- splitAction: boolean
392
- type: BadgeType
393
- }
394
-
395
- const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(function Badge({
396
- className,
397
- variant,
398
- badgeStyle,
399
- color,
400
- type,
401
- size,
402
- showNumber = false,
403
- splitAction = false,
404
- number,
405
- flag,
406
- asChild = false,
407
- dir = "auto",
408
- children,
409
- ...props
410
- }, ref) {
411
- const Comp = asChild ? Slot : "span"
412
- const legacyVariant = variant ? legacyVariantMap[variant] : undefined
413
- const isNumber = Boolean(showNumber)
414
- const resolvedBadgeStyle = badgeStyle ?? legacyVariant?.badgeStyle ?? "light"
415
- const resolvedColor = color ?? legacyVariant?.color ?? "neutral"
416
- const resolvedType = isNumber ? "default" : type ?? "default"
417
- const resolvedSize = (size ?? "sm") as BadgeSize
418
- const resolvedSplitAction =
419
- !isNumber && resolvedType === "trailing-icon" && Boolean(splitAction)
420
- const contentProps = {
421
- flag,
422
- number,
423
- size: resolvedSize,
424
- showNumber: isNumber,
425
- splitAction: resolvedSplitAction,
426
- type: resolvedType,
427
- }
428
-
429
- return (
430
- <Comp
431
- data-slot="badge"
432
- data-variant={
433
- variant ?? `${resolvedBadgeStyle}-${resolvedColor}-${resolvedType}`
434
- }
435
- data-style={resolvedBadgeStyle}
436
- data-color={resolvedColor}
437
- data-type={resolvedType}
438
- data-size={resolvedSize}
439
- data-show-number={isNumber ? "true" : "false"}
440
- data-split-action={resolvedSplitAction ? "true" : "false"}
441
- dir={dir}
442
- ref={ref}
443
- className={cn(
444
- badgeVariants({
445
- badgeStyle: resolvedBadgeStyle,
446
- color: resolvedColor,
447
- type: resolvedType,
448
- size: resolvedSize,
449
- showNumber: isNumber,
450
- splitAction: resolvedSplitAction,
451
- }),
452
- legacyVariant?.className,
453
- className
454
- )}
455
- {...props}
456
- >
457
- {asChild ? (
458
- renderBadgeSlotChild(children, contentProps)
459
- ) : (
460
- <BadgeContent {...contentProps}>
461
- {children}
462
- </BadgeContent>
463
- )}
464
- </Comp>
465
- )
466
- })
467
-
468
- function renderBadgeSlotChild(
469
- children: React.ReactNode,
470
- contentProps: Omit<BadgeContentProps, "children">
471
- ) {
472
- if (!React.isValidElement<{ children?: React.ReactNode }>(children)) {
473
- return children
474
- }
475
-
476
- return React.cloneElement(
477
- children,
478
- undefined,
479
- <BadgeContent {...contentProps}>{children.props.children}</BadgeContent>
480
- )
481
- }
482
-
483
- function BadgeContent({
484
- children,
485
- flag,
486
- number,
487
- size,
488
- showNumber,
489
- splitAction,
490
- type,
491
- }: BadgeContentProps) {
492
- const content = showNumber ? number ?? children : children
493
-
494
- return (
495
- <>
496
- {!showNumber && type === "dot" ? <BadgeDot /> : null}
497
- {!showNumber && type === "flag" ? <BadgeFlag>{flag}</BadgeFlag> : null}
498
- {renderBadgeChildren(content, type, splitAction, size)}
499
- </>
500
- )
501
- }
502
-
503
- function renderBadgeChildren(
504
- children: React.ReactNode,
505
- type: BadgeType,
506
- splitAction: boolean,
507
- size: BadgeSize,
508
- keyPrefix = "badge-label"
509
- ): React.ReactNode[] {
510
- const hasSplitTrailingAction = splitAction && type === "trailing-icon"
511
- const labelClassName = cn(
512
- "min-w-0",
513
- (type === "leading-icon" || type === "trailing-icon") &&
514
- "px-[var(--bh-space-xs-4)]",
515
- hasSplitTrailingAction &&
516
- "ps-[var(--bh-space-xs-4)] pe-[var(--bh-space-md-8)]"
517
- )
518
- const renderedChildren: React.ReactNode[] = []
519
-
520
- React.Children.toArray(children).forEach((child, index) => {
521
- if (
522
- typeof child === "string" ||
523
- typeof child === "number" ||
524
- typeof child === "bigint"
525
- ) {
526
- renderedChildren.push(
527
- <span
528
- key={`${keyPrefix}-${index}`}
529
- data-slot="badge-label"
530
- dir="auto"
531
- className={labelClassName}
532
- >
533
- {child}
534
- </span>
535
- )
536
-
537
- return
538
- }
539
-
540
- if (
541
- React.isValidElement<{ children?: React.ReactNode }>(child) &&
542
- child.type === React.Fragment
543
- ) {
544
- renderedChildren.push(
545
- ...renderBadgeChildren(
546
- child.props.children,
547
- type,
548
- false,
549
- size,
550
- `${keyPrefix}-${index}`
551
- )
552
- )
553
-
554
- return
555
- }
556
-
557
- renderedChildren.push(child)
558
- })
559
-
560
- if (!hasSplitTrailingAction || renderedChildren.length < 2) {
561
- return renderedChildren
562
- }
563
-
564
- return [
565
- ...renderedChildren.slice(0, -1),
566
- <BadgeSplitAction key={`${keyPrefix}-split-action`} size={size}>
567
- {renderedChildren.at(-1)}
568
- </BadgeSplitAction>,
569
- ]
570
- }
571
-
572
- function BadgeSplitAction({
573
- children,
574
- size,
575
- }: {
576
- children: React.ReactNode
577
- size: BadgeSize
578
- }) {
579
- return (
580
- <span
581
- data-slot="badge-split-action"
582
- className={cn(
583
- "flex h-full shrink-0 items-center justify-center border-s border-[var(--badge-split-border-color,currentColor)] text-current",
584
- size === "lg"
585
- ? "w-[calc(var(--bh-space-5xl-24)+var(--bh-space-xs-4))] [&_svg]:size-[var(--bh-space-2xl-14)]"
586
- : "w-[var(--bh-space-5xl-24)] [&_svg]:size-[var(--bh-space-xl-12)]"
587
- )}
588
- >
589
- {children}
590
- </span>
591
- )
592
- }
593
-
594
- function BadgeDot() {
595
- return (
596
- <span
597
- data-slot="badge-dot"
598
- aria-hidden="true"
599
- className="flex size-[var(--bh-space-md-8)] shrink-0 items-center justify-center"
600
- >
601
- <span className="size-[var(--bh-space-sm-6)] rounded-[var(--bh-radius-full)] bg-[var(--badge-dot-color,currentColor)]" />
602
- </span>
603
- )
604
- }
605
-
606
- function BadgeFlag({ children }: { children?: React.ReactNode }) {
607
- return (
608
- <span
609
- data-slot="badge-flag"
610
- aria-hidden={children ? undefined : "true"}
611
- className="flex size-[var(--bh-space-3xl-16)] shrink-0 items-center justify-center overflow-hidden rounded-[var(--bh-radius-xs-2)]"
612
- >
613
- {children ?? (
614
- <span className="size-full rounded-[var(--bh-radius-xs-2)] bg-[var(--badge-dot-color,currentColor)] opacity-[var(--bh-opacity-35)]" />
615
- )}
616
- </span>
617
- )
618
- }
619
-
620
- export {
621
- Badge,
622
- badgeVariants,
623
- type BadgeColor,
624
- type BadgeProps,
625
- type BadgeSize,
626
- type BadgeStyle,
627
- type BadgeType,
628
- type LegacyBadgeVariant,
629
- }
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ type BadgeStyle = "light" | "outline" | "solid"
8
+ type BadgeColor =
9
+ | "neutral"
10
+ | "blue"
11
+ | "green"
12
+ | "amber"
13
+ | "danger"
14
+ | "purple"
15
+ | "fuchsia"
16
+ | "rose"
17
+ | "sky"
18
+ | "golden"
19
+ type BadgeType =
20
+ | "default"
21
+ | "leading-icon"
22
+ | "trailing-icon"
23
+ | "dot"
24
+ | "flag"
25
+ type BadgeSize = "xs" | "sm" | "default" | "lg"
26
+ type LegacyBadgeVariant =
27
+ | "default"
28
+ | "secondary"
29
+ | "success"
30
+ | "warning"
31
+ | "destructive"
32
+ | "outline"
33
+ | "soft"
34
+ | "soft-success"
35
+ | "soft-warning"
36
+ | "soft-destructive"
37
+ | "ghost"
38
+ | "link"
39
+
40
+ const legacyVariantMap: Record<
41
+ LegacyBadgeVariant,
42
+ { badgeStyle: BadgeStyle; color: BadgeColor; className?: string }
43
+ > = {
44
+ default: { badgeStyle: "solid", color: "blue" },
45
+ secondary: { badgeStyle: "light", color: "neutral" },
46
+ success: { badgeStyle: "solid", color: "green" },
47
+ warning: { badgeStyle: "solid", color: "amber" },
48
+ destructive: { badgeStyle: "solid", color: "danger" },
49
+ outline: { badgeStyle: "outline", color: "neutral" },
50
+ soft: { badgeStyle: "light", color: "blue" },
51
+ "soft-success": { badgeStyle: "light", color: "green" },
52
+ "soft-warning": { badgeStyle: "light", color: "amber" },
53
+ "soft-destructive": { badgeStyle: "light", color: "danger" },
54
+ ghost: {
55
+ badgeStyle: "light",
56
+ color: "neutral",
57
+ className: "bg-[var(--bh-interactive-ghost-default)]",
58
+ },
59
+ link: {
60
+ badgeStyle: "light",
61
+ color: "blue",
62
+ className:
63
+ "bg-[var(--bh-interactive-ghost-default)] px-[var(--bh-space-none)] text-[var(--bh-content-link)] underline-offset-[var(--bh-space-xs-4)] [a&]:hover:underline",
64
+ },
65
+ }
66
+
67
+ const badgeVariants = cva(
68
+ [
69
+ "inline-flex min-w-0 shrink-0 items-center justify-center whitespace-nowrap",
70
+ "rounded-[var(--bh-radius-full)] border border-transparent outline-none",
71
+ "transition-[background-color,border-color,color,box-shadow]",
72
+ "focus-visible:border-[var(--bh-border-focus)] focus-visible:ring-[length:var(--bh-badge-focus-ring-width)] focus-visible:ring-[var(--ring)]",
73
+ "aria-invalid:border-[var(--bh-border-focus-danger)] aria-invalid:ring-[var(--danger-ring)]",
74
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0",
75
+ "rtl:[&_svg[data-rtl-flip='true']]:-scale-x-100",
76
+ ],
77
+ {
78
+ variants: {
79
+ badgeStyle: {
80
+ light: "border-transparent",
81
+ outline: "border-[length:var(--bh-badge-outline-border-width)] bg-transparent",
82
+ solid: "border-transparent",
83
+ },
84
+ color: {
85
+ neutral: "",
86
+ blue: "",
87
+ green: "",
88
+ amber: "",
89
+ danger: "",
90
+ purple: "",
91
+ fuchsia: "",
92
+ rose: "",
93
+ sky: "",
94
+ golden: "",
95
+ },
96
+ type: {
97
+ default: "gap-[var(--bh-space-none)]",
98
+ "leading-icon": "gap-[var(--bh-space-none)]",
99
+ "trailing-icon": "gap-[var(--bh-space-none)]",
100
+ dot: "gap-[var(--bh-space-xs-4)]",
101
+ flag: "gap-[var(--bh-space-xs-4)]",
102
+ },
103
+ size: {
104
+ xs: [
105
+ "h-[var(--bh-space-5xl-24)]",
106
+ "text-[length:var(--bh-text-body-3xs-medium-font-size)]",
107
+ "font-[var(--bh-text-body-3xs-medium-font-weight)]",
108
+ "leading-[var(--bh-text-body-3xs-medium-line-height)]",
109
+ "tracking-[var(--bh-text-body-3xs-medium-letter-spacing)]",
110
+ ],
111
+ sm: [
112
+ "h-[var(--bh-space-5xl-24)]",
113
+ "text-[length:var(--bh-text-body-3xs-medium-font-size)]",
114
+ "font-[var(--bh-text-body-3xs-medium-font-weight)]",
115
+ "leading-[var(--bh-text-body-3xs-medium-line-height)]",
116
+ "tracking-[var(--bh-text-body-3xs-medium-letter-spacing)]",
117
+ ],
118
+ default: [
119
+ "h-[var(--bh-space-5xl-24)]",
120
+ "text-[length:var(--bh-text-body-3xs-medium-font-size)]",
121
+ "font-[var(--bh-text-body-3xs-medium-font-weight)]",
122
+ "leading-[var(--bh-text-body-3xs-medium-line-height)]",
123
+ "tracking-[var(--bh-text-body-3xs-medium-letter-spacing)]",
124
+ ],
125
+ lg: [
126
+ "h-[calc(var(--bh-space-5xl-24)+var(--bh-space-xs-4))]",
127
+ "text-[length:var(--bh-text-body-2xs-medium-font-size)]",
128
+ "font-[var(--bh-text-body-2xs-medium-font-weight)]",
129
+ "leading-[var(--bh-text-body-2xs-medium-line-height)]",
130
+ "tracking-[var(--bh-text-body-2xs-medium-letter-spacing)]",
131
+ ],
132
+ },
133
+ showNumber: {
134
+ true: "gap-[var(--bh-space-none)] px-[var(--bh-space-xs-4)] text-center",
135
+ false: "",
136
+ },
137
+ splitAction: {
138
+ true:
139
+ "overflow-hidden border-[var(--badge-split-border-color,currentColor)] pe-[var(--bh-space-none)]",
140
+ false: "[&_svg]:size-[var(--bh-space-3xl-16)]",
141
+ },
142
+ },
143
+ compoundVariants: [
144
+ {
145
+ type: "default",
146
+ size: ["xs", "sm", "default"],
147
+ showNumber: false,
148
+ class: "px-[var(--bh-space-md-8)]",
149
+ },
150
+ {
151
+ type: "default",
152
+ size: "lg",
153
+ showNumber: false,
154
+ class: "px-[var(--bh-space-xl-12)]",
155
+ },
156
+ {
157
+ type: ["leading-icon", "trailing-icon", "dot"],
158
+ showNumber: false,
159
+ splitAction: false,
160
+ class: "px-[var(--bh-space-md-8)]",
161
+ },
162
+ {
163
+ type: "trailing-icon",
164
+ showNumber: false,
165
+ splitAction: true,
166
+ class: "ps-[var(--bh-space-md-8)]",
167
+ },
168
+ {
169
+ type: "flag",
170
+ showNumber: false,
171
+ class: "ps-[var(--bh-space-xs-4)] pe-[var(--bh-space-md-8)]",
172
+ },
173
+ {
174
+ size: ["xs", "sm", "default"],
175
+ showNumber: true,
176
+ class:
177
+ "w-[var(--bh-space-5xl-24)] font-[var(--bh-text-body-3xs-semibold-font-weight)]",
178
+ },
179
+ {
180
+ size: "lg",
181
+ showNumber: true,
182
+ class:
183
+ "w-[calc(var(--bh-space-5xl-24)+var(--bh-space-xs-4))] font-[var(--bh-text-body-2xs-semibold-font-weight)]",
184
+ },
185
+ {
186
+ badgeStyle: "light",
187
+ color: "neutral",
188
+ class:
189
+ "bg-[var(--bh-bg-neutral-subtle)] text-[var(--bh-content-default)] [--badge-dot-color:var(--bh-content-muted)] [--badge-split-border-color:var(--bh-border-default)]",
190
+ },
191
+ {
192
+ badgeStyle: "outline",
193
+ color: "neutral",
194
+ class:
195
+ "border-[var(--bh-border-strong)] text-[var(--bh-content-default)] [--badge-dot-color:var(--bh-content-default)] [--badge-split-border-color:var(--bh-border-default)]",
196
+ },
197
+ {
198
+ badgeStyle: "solid",
199
+ color: "neutral",
200
+ class:
201
+ "bg-[var(--bh-bg-neutral-bold)] text-[var(--bh-content-on-neutral)] [--badge-dot-color:var(--bh-content-on-neutral)] [--badge-split-border-color:var(--bh-border-subtle)]",
202
+ },
203
+ {
204
+ badgeStyle: "light",
205
+ color: "blue",
206
+ class:
207
+ "bg-[var(--bh-bg-accent-blue-subtle)] text-[var(--bh-content-accent-blue-strong)] [--badge-dot-color:var(--bh-content-accent-blue-default)] [--badge-split-border-color:var(--bh-border-accent-blue-subtle)]",
208
+ },
209
+ {
210
+ badgeStyle: "outline",
211
+ color: "blue",
212
+ class:
213
+ "border-[var(--bh-border-accent-blue-strong)] text-[var(--bh-content-accent-blue-strong)] [--badge-dot-color:var(--bh-content-accent-blue-strong)] [--badge-split-border-color:var(--bh-border-accent-blue-subtle)]",
214
+ },
215
+ {
216
+ badgeStyle: "solid",
217
+ color: "blue",
218
+ class:
219
+ "bg-[var(--bh-bg-accent-blue-bold)] text-[var(--bh-content-on-color)] [--badge-dot-color:var(--bh-content-on-color)] [--badge-split-border-color:var(--bh-border-accent-blue-subtle)]",
220
+ },
221
+ {
222
+ badgeStyle: "light",
223
+ color: "green",
224
+ class:
225
+ "bg-[var(--bh-bg-accent-green-subtle)] text-[var(--bh-content-accent-green-strong)] [--badge-dot-color:var(--bh-content-accent-green-default)] [--badge-split-border-color:var(--bh-border-accent-green-subtle)]",
226
+ },
227
+ {
228
+ badgeStyle: "outline",
229
+ color: "green",
230
+ class:
231
+ "border-[var(--bh-border-accent-green-strong)] text-[var(--bh-content-accent-green-strong)] [--badge-dot-color:var(--bh-content-accent-green-strong)] [--badge-split-border-color:var(--bh-border-accent-green-subtle)]",
232
+ },
233
+ {
234
+ badgeStyle: "solid",
235
+ color: "green",
236
+ class:
237
+ "bg-[var(--bh-bg-accent-green-default)] text-[var(--bh-content-on-light)] [--badge-dot-color:var(--bh-content-on-light)] [--badge-split-border-color:var(--bh-border-accent-green-subtle)]",
238
+ },
239
+ {
240
+ badgeStyle: "light",
241
+ color: "amber",
242
+ class:
243
+ "bg-[var(--bh-bg-accent-amber-subtle)] text-[var(--bh-content-accent-amber-strong)] [--badge-dot-color:var(--bh-content-accent-amber-default)] [--badge-split-border-color:var(--bh-border-accent-amber-subtle)]",
244
+ },
245
+ {
246
+ badgeStyle: "outline",
247
+ color: "amber",
248
+ class:
249
+ "border-[var(--bh-border-accent-amber-strong)] text-[var(--bh-content-accent-amber-strong)] [--badge-dot-color:var(--bh-content-accent-amber-strong)] [--badge-split-border-color:var(--bh-border-accent-amber-subtle)]",
250
+ },
251
+ {
252
+ badgeStyle: "solid",
253
+ color: "amber",
254
+ class:
255
+ "bg-[var(--bh-bg-accent-amber-default)] text-[var(--bh-content-on-light)] [--badge-dot-color:var(--bh-content-on-light)] [--badge-split-border-color:var(--bh-border-accent-amber-subtle)]",
256
+ },
257
+ {
258
+ badgeStyle: "light",
259
+ color: "danger",
260
+ class:
261
+ "bg-[var(--bh-bg-accent-red-subtle)] text-[var(--bh-content-accent-red-strong)] [--badge-dot-color:var(--bh-content-accent-red-default)] [--badge-split-border-color:var(--bh-border-accent-red-subtle)]",
262
+ },
263
+ {
264
+ badgeStyle: "outline",
265
+ color: "danger",
266
+ class:
267
+ "border-[var(--bh-border-accent-red-strong)] text-[var(--bh-content-accent-red-strong)] [--badge-dot-color:var(--bh-content-accent-red-strong)] [--badge-split-border-color:var(--bh-border-accent-red-subtle)]",
268
+ },
269
+ {
270
+ badgeStyle: "solid",
271
+ color: "danger",
272
+ class:
273
+ "bg-[var(--bh-bg-accent-red-default)] text-[var(--bh-content-on-color)] [--badge-dot-color:var(--bh-content-on-color)] [--badge-split-border-color:var(--bh-border-accent-red-subtle)]",
274
+ },
275
+ {
276
+ badgeStyle: "light",
277
+ color: "purple",
278
+ class:
279
+ "bg-[var(--bh-bg-accent-purple-subtle)] text-[var(--bh-content-accent-purple-strong)] [--badge-dot-color:var(--bh-content-accent-purple-default)] [--badge-split-border-color:var(--bh-border-accent-purple-subtle)]",
280
+ },
281
+ {
282
+ badgeStyle: "outline",
283
+ color: "purple",
284
+ class:
285
+ "border-[var(--bh-border-accent-purple-strong)] text-[var(--bh-content-accent-purple-strong)] [--badge-dot-color:var(--bh-content-accent-purple-strong)] [--badge-split-border-color:var(--bh-border-accent-purple-subtle)]",
286
+ },
287
+ {
288
+ badgeStyle: "solid",
289
+ color: "purple",
290
+ class:
291
+ "bg-[var(--bh-bg-accent-purple-default)] text-[var(--bh-content-on-color)] [--badge-dot-color:var(--bh-content-on-color)] [--badge-split-border-color:var(--bh-border-accent-purple-subtle)]",
292
+ },
293
+ {
294
+ badgeStyle: "light",
295
+ color: "fuchsia",
296
+ class:
297
+ "bg-[var(--bh-bg-accent-fuchsia-subtle)] text-[var(--bh-content-accent-fuchsia-strong)] [--badge-dot-color:var(--bh-content-accent-fuchsia-default)] [--badge-split-border-color:var(--bh-border-accent-fuchsia-subtle)]",
298
+ },
299
+ {
300
+ badgeStyle: "outline",
301
+ color: "fuchsia",
302
+ class:
303
+ "border-[var(--bh-border-accent-fuchsia-strong)] text-[var(--bh-content-accent-fuchsia-strong)] [--badge-dot-color:var(--bh-content-accent-fuchsia-strong)] [--badge-split-border-color:var(--bh-border-accent-fuchsia-subtle)]",
304
+ },
305
+ {
306
+ badgeStyle: "solid",
307
+ color: "fuchsia",
308
+ class:
309
+ "bg-[var(--bh-bg-accent-fuchsia-default)] text-[var(--bh-content-on-light)] [--badge-dot-color:var(--bh-content-on-light)] [--badge-split-border-color:var(--bh-border-accent-fuchsia-subtle)]",
310
+ },
311
+ {
312
+ badgeStyle: "light",
313
+ color: "rose",
314
+ class:
315
+ "bg-[var(--bh-bg-accent-rose-subtle)] text-[var(--bh-content-accent-rose-strong)] [--badge-dot-color:var(--bh-content-accent-rose-default)] [--badge-split-border-color:var(--bh-border-accent-rose-subtle)]",
316
+ },
317
+ {
318
+ badgeStyle: "outline",
319
+ color: "rose",
320
+ class:
321
+ "border-[var(--bh-border-accent-rose-strong)] text-[var(--bh-content-accent-rose-strong)] [--badge-dot-color:var(--bh-content-accent-rose-strong)] [--badge-split-border-color:var(--bh-border-accent-rose-subtle)]",
322
+ },
323
+ {
324
+ badgeStyle: "solid",
325
+ color: "rose",
326
+ class:
327
+ "bg-[var(--bh-bg-accent-rose-bold)] text-[var(--bh-content-on-color)] [--badge-dot-color:var(--bh-content-on-color)] [--badge-split-border-color:var(--bh-border-accent-rose-subtle)]",
328
+ },
329
+ {
330
+ badgeStyle: "light",
331
+ color: "sky",
332
+ class:
333
+ "bg-[var(--bh-bg-accent-sky-subtle)] text-[var(--bh-content-accent-sky-strong)] [--badge-dot-color:var(--bh-content-accent-sky-default)] [--badge-split-border-color:var(--bh-border-accent-sky-subtle)]",
334
+ },
335
+ {
336
+ badgeStyle: "outline",
337
+ color: "sky",
338
+ class:
339
+ "border-[var(--bh-border-accent-sky-strong)] text-[var(--bh-content-accent-sky-strong)] [--badge-dot-color:var(--bh-content-accent-sky-strong)] [--badge-split-border-color:var(--bh-border-accent-sky-subtle)]",
340
+ },
341
+ {
342
+ badgeStyle: "solid",
343
+ color: "sky",
344
+ class:
345
+ "bg-[var(--bh-bg-accent-sky-base)] text-[var(--bh-content-on-light)] [--badge-dot-color:var(--bh-content-on-light)] [--badge-split-border-color:var(--bh-border-accent-sky-subtle)]",
346
+ },
347
+ {
348
+ badgeStyle: "light",
349
+ color: "golden",
350
+ class:
351
+ "bg-[var(--bh-bg-accent-golden-subtle)] text-[var(--bh-content-accent-golden-strong)] [--badge-dot-color:var(--bh-content-accent-golden-default)] [--badge-split-border-color:var(--bh-border-accent-golden-subtle)]",
352
+ },
353
+ {
354
+ badgeStyle: "outline",
355
+ color: "golden",
356
+ class:
357
+ "border-[var(--bh-border-accent-golden-strong)] text-[var(--bh-content-accent-golden-strong)] [--badge-dot-color:var(--bh-content-accent-golden-strong)] [--badge-split-border-color:var(--bh-border-accent-golden-subtle)]",
358
+ },
359
+ {
360
+ badgeStyle: "solid",
361
+ color: "golden",
362
+ class:
363
+ "bg-[var(--bh-bg-accent-golden-default)] text-[var(--bh-content-on-light)] [--badge-dot-color:var(--bh-content-on-light)] [--badge-split-border-color:var(--bh-border-accent-golden-subtle)]",
364
+ },
365
+ ],
366
+ defaultVariants: {
367
+ badgeStyle: "light",
368
+ color: "neutral",
369
+ type: "default",
370
+ size: "sm",
371
+ showNumber: false,
372
+ splitAction: false,
373
+ },
374
+ }
375
+ )
376
+
377
+ type BadgeProps = Omit<React.ComponentProps<"span">, "color"> &
378
+ VariantProps<typeof badgeVariants> & {
379
+ asChild?: boolean
380
+ variant?: LegacyBadgeVariant
381
+ flag?: React.ReactNode
382
+ number?: React.ReactNode
383
+ }
384
+
385
+ type BadgeContentProps = {
386
+ children: React.ReactNode
387
+ flag?: React.ReactNode
388
+ number?: React.ReactNode
389
+ size: BadgeSize
390
+ showNumber: boolean
391
+ splitAction: boolean
392
+ type: BadgeType
393
+ }
394
+
395
+ const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(function Badge({
396
+ className,
397
+ variant,
398
+ badgeStyle,
399
+ color,
400
+ type,
401
+ size,
402
+ showNumber = false,
403
+ splitAction = false,
404
+ number,
405
+ flag,
406
+ asChild = false,
407
+ dir = "auto",
408
+ children,
409
+ ...props
410
+ }, ref) {
411
+ const Comp = asChild ? Slot : "span"
412
+ const legacyVariant = variant ? legacyVariantMap[variant] : undefined
413
+ const isNumber = Boolean(showNumber)
414
+ const resolvedBadgeStyle = badgeStyle ?? legacyVariant?.badgeStyle ?? "light"
415
+ const resolvedColor = color ?? legacyVariant?.color ?? "neutral"
416
+ const resolvedType = isNumber ? "default" : type ?? "default"
417
+ const resolvedSize = (size ?? "sm") as BadgeSize
418
+ const resolvedSplitAction =
419
+ !isNumber && resolvedType === "trailing-icon" && Boolean(splitAction)
420
+ const contentProps = {
421
+ flag,
422
+ number,
423
+ size: resolvedSize,
424
+ showNumber: isNumber,
425
+ splitAction: resolvedSplitAction,
426
+ type: resolvedType,
427
+ }
428
+
429
+ return (
430
+ <Comp
431
+ data-slot="badge"
432
+ data-variant={
433
+ variant ?? `${resolvedBadgeStyle}-${resolvedColor}-${resolvedType}`
434
+ }
435
+ data-style={resolvedBadgeStyle}
436
+ data-color={resolvedColor}
437
+ data-type={resolvedType}
438
+ data-size={resolvedSize}
439
+ data-show-number={isNumber ? "true" : "false"}
440
+ data-split-action={resolvedSplitAction ? "true" : "false"}
441
+ dir={dir}
442
+ ref={ref}
443
+ className={cn(
444
+ badgeVariants({
445
+ badgeStyle: resolvedBadgeStyle,
446
+ color: resolvedColor,
447
+ type: resolvedType,
448
+ size: resolvedSize,
449
+ showNumber: isNumber,
450
+ splitAction: resolvedSplitAction,
451
+ }),
452
+ legacyVariant?.className,
453
+ className
454
+ )}
455
+ {...props}
456
+ >
457
+ {asChild ? (
458
+ renderBadgeSlotChild(children, contentProps)
459
+ ) : (
460
+ <BadgeContent {...contentProps}>
461
+ {children}
462
+ </BadgeContent>
463
+ )}
464
+ </Comp>
465
+ )
466
+ })
467
+
468
+ function renderBadgeSlotChild(
469
+ children: React.ReactNode,
470
+ contentProps: Omit<BadgeContentProps, "children">
471
+ ) {
472
+ if (!React.isValidElement<{ children?: React.ReactNode }>(children)) {
473
+ return children
474
+ }
475
+
476
+ return React.cloneElement(
477
+ children,
478
+ undefined,
479
+ <BadgeContent {...contentProps}>{children.props.children}</BadgeContent>
480
+ )
481
+ }
482
+
483
+ function BadgeContent({
484
+ children,
485
+ flag,
486
+ number,
487
+ size,
488
+ showNumber,
489
+ splitAction,
490
+ type,
491
+ }: BadgeContentProps) {
492
+ const content = showNumber ? number ?? children : children
493
+
494
+ return (
495
+ <>
496
+ {!showNumber && type === "dot" ? <BadgeDot /> : null}
497
+ {!showNumber && type === "flag" ? <BadgeFlag>{flag}</BadgeFlag> : null}
498
+ {renderBadgeChildren(content, type, splitAction, size)}
499
+ </>
500
+ )
501
+ }
502
+
503
+ function renderBadgeChildren(
504
+ children: React.ReactNode,
505
+ type: BadgeType,
506
+ splitAction: boolean,
507
+ size: BadgeSize,
508
+ keyPrefix = "badge-label"
509
+ ): React.ReactNode[] {
510
+ const hasSplitTrailingAction = splitAction && type === "trailing-icon"
511
+ const labelClassName = cn(
512
+ "min-w-0",
513
+ (type === "leading-icon" || type === "trailing-icon") &&
514
+ "px-[var(--bh-space-xs-4)]",
515
+ hasSplitTrailingAction &&
516
+ "ps-[var(--bh-space-xs-4)] pe-[var(--bh-space-md-8)]"
517
+ )
518
+ const renderedChildren: React.ReactNode[] = []
519
+
520
+ React.Children.toArray(children).forEach((child, index) => {
521
+ if (
522
+ typeof child === "string" ||
523
+ typeof child === "number" ||
524
+ typeof child === "bigint"
525
+ ) {
526
+ renderedChildren.push(
527
+ <span
528
+ key={`${keyPrefix}-${index}`}
529
+ data-slot="badge-label"
530
+ dir="auto"
531
+ className={labelClassName}
532
+ >
533
+ {child}
534
+ </span>
535
+ )
536
+
537
+ return
538
+ }
539
+
540
+ if (
541
+ React.isValidElement<{ children?: React.ReactNode }>(child) &&
542
+ child.type === React.Fragment
543
+ ) {
544
+ renderedChildren.push(
545
+ ...renderBadgeChildren(
546
+ child.props.children,
547
+ type,
548
+ false,
549
+ size,
550
+ `${keyPrefix}-${index}`
551
+ )
552
+ )
553
+
554
+ return
555
+ }
556
+
557
+ renderedChildren.push(child)
558
+ })
559
+
560
+ if (!hasSplitTrailingAction || renderedChildren.length < 2) {
561
+ return renderedChildren
562
+ }
563
+
564
+ return [
565
+ ...renderedChildren.slice(0, -1),
566
+ <BadgeSplitAction key={`${keyPrefix}-split-action`} size={size}>
567
+ {renderedChildren.at(-1)}
568
+ </BadgeSplitAction>,
569
+ ]
570
+ }
571
+
572
+ function BadgeSplitAction({
573
+ children,
574
+ size,
575
+ }: {
576
+ children: React.ReactNode
577
+ size: BadgeSize
578
+ }) {
579
+ return (
580
+ <span
581
+ data-slot="badge-split-action"
582
+ className={cn(
583
+ "flex h-full shrink-0 items-center justify-center border-s border-[var(--badge-split-border-color,currentColor)] text-current",
584
+ size === "lg"
585
+ ? "w-[calc(var(--bh-space-5xl-24)+var(--bh-space-xs-4))] [&_svg]:size-[var(--bh-space-2xl-14)]"
586
+ : "w-[var(--bh-space-5xl-24)] [&_svg]:size-[var(--bh-space-xl-12)]"
587
+ )}
588
+ >
589
+ {children}
590
+ </span>
591
+ )
592
+ }
593
+
594
+ function BadgeDot() {
595
+ return (
596
+ <span
597
+ data-slot="badge-dot"
598
+ aria-hidden="true"
599
+ className="flex size-[var(--bh-space-md-8)] shrink-0 items-center justify-center"
600
+ >
601
+ <span className="size-[var(--bh-space-sm-6)] rounded-[var(--bh-radius-full)] bg-[var(--badge-dot-color,currentColor)]" />
602
+ </span>
603
+ )
604
+ }
605
+
606
+ function BadgeFlag({ children }: { children?: React.ReactNode }) {
607
+ return (
608
+ <span
609
+ data-slot="badge-flag"
610
+ aria-hidden={children ? undefined : "true"}
611
+ className="flex size-[var(--bh-space-3xl-16)] shrink-0 items-center justify-center overflow-hidden rounded-[var(--bh-radius-xs-2)]"
612
+ >
613
+ {children ?? (
614
+ <span className="size-full rounded-[var(--bh-radius-xs-2)] bg-[var(--badge-dot-color,currentColor)] opacity-[var(--bh-opacity-35)]" />
615
+ )}
616
+ </span>
617
+ )
618
+ }
619
+
620
+ export {
621
+ Badge,
622
+ badgeVariants,
623
+ type BadgeColor,
624
+ type BadgeProps,
625
+ type BadgeSize,
626
+ type BadgeStyle,
627
+ type BadgeType,
628
+ type LegacyBadgeVariant,
629
+ }