banhaten 0.1.3 → 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 (258) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/CONTRIBUTING.md +44 -0
  3. package/README.md +65 -13
  4. package/SECURITY.md +15 -0
  5. package/banhaten.config.example.json +0 -2
  6. package/docs/design-system/README.md +9 -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 +6 -1
  10. package/docs/design-system/component-docs-pattern.md +81 -0
  11. package/docs/design-system/component-token-consistency-audit.md +13 -3
  12. package/docs/design-system/components/README.md +3 -2
  13. package/docs/design-system/components/accordion.md +16 -0
  14. package/docs/design-system/components/activity-feed.md +15 -1
  15. package/docs/design-system/components/alert-dialog.md +17 -0
  16. package/docs/design-system/components/alert.md +46 -14
  17. package/docs/design-system/components/aspect-ratio.md +15 -0
  18. package/docs/design-system/components/attribute.md +16 -0
  19. package/docs/design-system/components/autocomplete.md +19 -0
  20. package/docs/design-system/components/avatar.md +18 -0
  21. package/docs/design-system/components/badge.md +19 -0
  22. package/docs/design-system/components/banner.md +15 -1
  23. package/docs/design-system/components/breadcrumbs.md +14 -0
  24. package/docs/design-system/components/button-group.md +34 -0
  25. package/docs/design-system/components/button.md +18 -0
  26. package/docs/design-system/components/card.md +18 -1
  27. package/docs/design-system/components/carousel.md +20 -3
  28. package/docs/design-system/components/catalog-components.md +15 -1
  29. package/docs/design-system/components/chart.md +15 -0
  30. package/docs/design-system/components/checkbox.md +16 -0
  31. package/docs/design-system/components/collapsible.md +15 -0
  32. package/docs/design-system/components/command.md +15 -0
  33. package/docs/design-system/components/context-menu.md +15 -0
  34. package/docs/design-system/components/date-picker.md +21 -1
  35. package/docs/design-system/components/divider.md +16 -0
  36. package/docs/design-system/components/empty-state.md +17 -0
  37. package/docs/design-system/components/field.md +17 -0
  38. package/docs/design-system/components/file-upload.md +14 -0
  39. package/docs/design-system/components/hover-card.md +15 -0
  40. package/docs/design-system/components/input-group.md +17 -0
  41. package/docs/design-system/components/input-otp.md +18 -0
  42. package/docs/design-system/components/input.md +43 -7
  43. package/docs/design-system/components/item.md +72 -0
  44. package/docs/design-system/components/kbd.md +18 -0
  45. package/docs/design-system/components/label.md +16 -0
  46. package/docs/design-system/components/menu.md +17 -0
  47. package/docs/design-system/components/menubar.md +15 -0
  48. package/docs/design-system/components/modal.md +25 -4
  49. package/docs/design-system/components/native-select.md +18 -0
  50. package/docs/design-system/components/navigation-menu.md +15 -0
  51. package/docs/design-system/components/onboarding-step-list-item.md +15 -1
  52. package/docs/design-system/components/page-header.md +15 -1
  53. package/docs/design-system/components/pagination.md +30 -8
  54. package/docs/design-system/components/popover.md +19 -0
  55. package/docs/design-system/components/progress-slider.md +18 -0
  56. package/docs/design-system/components/progress.md +17 -0
  57. package/docs/design-system/components/radio-card.md +16 -0
  58. package/docs/design-system/components/radio-group.md +17 -0
  59. package/docs/design-system/components/resizable.md +15 -0
  60. package/docs/design-system/components/scroll-area.md +15 -0
  61. package/docs/design-system/components/segmented-control.md +71 -0
  62. package/docs/design-system/components/select.md +44 -7
  63. package/docs/design-system/components/sheet.md +17 -0
  64. package/docs/design-system/components/sidebar.md +15 -0
  65. package/docs/design-system/components/skeleton.md +18 -0
  66. package/docs/design-system/components/slideout.md +14 -0
  67. package/docs/design-system/components/slider.md +20 -0
  68. package/docs/design-system/components/social-button.md +21 -3
  69. package/docs/design-system/components/spinner.md +16 -0
  70. package/docs/design-system/components/steps.md +14 -0
  71. package/docs/design-system/components/table.md +15 -1
  72. package/docs/design-system/components/tabs.md +17 -0
  73. package/docs/design-system/components/tag.md +17 -0
  74. package/docs/design-system/components/textarea.md +16 -0
  75. package/docs/design-system/components/timeline.md +14 -0
  76. package/docs/design-system/components/toast.md +17 -0
  77. package/docs/design-system/components/toggle.md +17 -0
  78. package/docs/design-system/components/toolbar.md +21 -1
  79. package/docs/design-system/components/tooltip.md +18 -1
  80. package/docs/design-system/density-and-elevation.md +44 -0
  81. package/docs/design-system/direction-and-localization.md +23 -0
  82. package/docs/design-system/form-recipes.md +73 -0
  83. package/docs/design-system/gap-promotion-policy.md +21 -0
  84. package/docs/design-system/gap-promotion-report.md +166 -0
  85. package/docs/design-system/installation.md +68 -0
  86. package/docs/design-system/launch-comparison-shadcn.md +98 -0
  87. package/docs/design-system/registry-and-cli.md +30 -7
  88. package/docs/design-system/release-policy.md +30 -0
  89. package/docs/design-system/roadmap.md +14 -0
  90. package/docs/design-system/support-matrix.md +23 -0
  91. package/docs/design-system/tabs.md +1 -1
  92. package/docs/design-system/token-governance.md +23 -1
  93. package/package.json +29 -15
  94. package/registry/component-contract.json +40 -0
  95. package/registry/components/alert-dialog.tsx +1 -1
  96. package/registry/components/alert.tsx +86 -65
  97. package/registry/components/autocomplete.tsx +23 -9
  98. package/registry/components/avatar.tsx +7 -7
  99. package/registry/components/badge.tsx +1 -1
  100. package/registry/components/button-group.tsx +321 -4
  101. package/registry/components/button.tsx +38 -17
  102. package/registry/components/card.tsx +68 -59
  103. package/registry/components/carousel.tsx +53 -5
  104. package/registry/components/chart.tsx +36 -3
  105. package/registry/components/checkbox.tsx +3 -3
  106. package/registry/components/collapsible.tsx +1 -1
  107. package/registry/components/command.tsx +1 -1
  108. package/registry/components/context-menu.tsx +1 -1
  109. package/registry/components/date-picker.tsx +245 -104
  110. package/registry/components/expanded/ActivityFeed.tsx +6 -15
  111. package/registry/components/expanded/Banner.tsx +3 -3
  112. package/registry/components/expanded/Breadcrumbs.tsx +5 -5
  113. package/registry/components/expanded/CatalogComponentsShowcase.tsx +13 -13
  114. package/registry/components/expanded/CatalogDivider.tsx +1 -1
  115. package/registry/components/expanded/EmptyState.tsx +1 -1
  116. package/registry/components/expanded/FileUpload.tsx +9 -2
  117. package/registry/components/expanded/OnboardingStepListItem.tsx +2 -4
  118. package/registry/components/expanded/PageHeader.tsx +13 -13
  119. package/registry/components/expanded/Slideout.tsx +15 -415
  120. package/registry/components/expanded/Steps.tsx +4 -4
  121. package/registry/components/expanded/Table.tsx +116 -69
  122. package/registry/components/expanded/Timeline.tsx +3 -3
  123. package/registry/components/expanded/activityFeed.css +1 -1
  124. package/registry/components/expanded/breadcrumbs.css +1 -1
  125. package/registry/components/expanded/divider.css +1 -1
  126. package/registry/components/expanded/emptyState.css +1 -1
  127. package/registry/components/expanded/fileUpload.css +6 -6
  128. package/registry/components/expanded/slideout.css +9 -685
  129. package/registry/components/expanded/steps.css +3 -3
  130. package/registry/components/expanded/table.css +2 -2
  131. package/registry/components/expanded/timeline.css +2 -2
  132. package/registry/components/input-content.ts +1 -0
  133. package/registry/components/input-otp.tsx +13 -3
  134. package/registry/components/input.tsx +68 -43
  135. package/registry/components/item.tsx +189 -0
  136. package/registry/components/kbd.tsx +111 -4
  137. package/registry/components/menu.tsx +31 -245
  138. package/registry/components/menubar.tsx +1 -1
  139. package/registry/components/modal.tsx +142 -102
  140. package/registry/components/native-select.tsx +2 -2
  141. package/registry/components/pagination.tsx +80 -58
  142. package/registry/components/popover.tsx +162 -664
  143. package/registry/components/radio-card.tsx +116 -87
  144. package/registry/components/segmented-control.tsx +87 -0
  145. package/registry/components/select-content.tsx +2 -2
  146. package/registry/components/select.tsx +52 -16
  147. package/registry/components/sheet.tsx +1 -1
  148. package/registry/components/sidebar.tsx +1 -1
  149. package/registry/components/skeleton.css +0 -3
  150. package/registry/components/social-button.tsx +47 -25
  151. package/registry/components/spinner.tsx +18 -55
  152. package/registry/components/tabs.css +1 -1
  153. package/registry/components/tabs.tsx +8 -4
  154. package/registry/components/tag.tsx +2 -2
  155. package/registry/components/textarea.tsx +3 -3
  156. package/registry/components/toast.tsx +16 -11
  157. package/registry/components/toggle.tsx +2 -2
  158. package/registry/components/toolbar.tsx +89 -17
  159. package/registry/components/tooltip.tsx +1 -1
  160. package/registry/examples/alert-demo.tsx +25 -5
  161. package/registry/examples/attribute-demo.tsx +1 -1
  162. package/registry/examples/autocomplete-demo.tsx +3 -3
  163. package/registry/examples/avatar-demo.tsx +7 -7
  164. package/registry/examples/badge-demo.tsx +1 -1
  165. package/registry/examples/button-demo.tsx +6 -6
  166. package/registry/examples/button-group-demo.tsx +23 -2
  167. package/registry/examples/card-demo.tsx +46 -8
  168. package/registry/examples/carousel-demo.tsx +20 -2
  169. package/registry/examples/chart-demo.tsx +1 -1
  170. package/registry/examples/checkbox-demo.tsx +5 -5
  171. package/registry/examples/collection-filter-bar-demo.tsx +135 -0
  172. package/registry/examples/command-demo.tsx +3 -3
  173. package/registry/examples/{expanded/command-bar-demo.tsx → command-palette-demo.tsx} +5 -15
  174. package/registry/examples/context-menu-demo.tsx +1 -1
  175. package/registry/examples/control-density-contract-demo.tsx +61 -0
  176. package/registry/examples/date-picker-demo.tsx +12 -12
  177. package/registry/examples/divider-demo.tsx +3 -3
  178. package/registry/examples/expanded/banner-demo.tsx +1 -1
  179. package/registry/examples/expanded/breadcrumbs-demo.tsx +1 -1
  180. package/registry/examples/expanded/empty-state-demo.tsx +1 -1
  181. package/registry/examples/expanded/file-upload-demo.tsx +1 -1
  182. package/registry/examples/expanded/onboarding-step-demo.tsx +1 -1
  183. package/registry/examples/expanded/slideout-demo.tsx +2 -2
  184. package/registry/examples/expanded/steps-demo.tsx +1 -1
  185. package/registry/examples/expanded/tabs-demo.tsx +1 -1
  186. package/registry/examples/expanded/timeline-demo.tsx +1 -1
  187. package/registry/examples/hover-card-demo.tsx +2 -2
  188. package/registry/examples/input-demo.tsx +6 -6
  189. package/registry/examples/input-otp-demo.tsx +4 -4
  190. package/registry/examples/item-demo.tsx +46 -0
  191. package/registry/examples/kbd-demo.tsx +1 -1
  192. package/registry/examples/label-demo.tsx +1 -1
  193. package/registry/examples/menu-demo.tsx +1 -1
  194. package/registry/examples/modal-demo.tsx +67 -9
  195. package/registry/examples/navigation-menu-demo.tsx +1 -1
  196. package/registry/examples/pagination-demo.tsx +6 -3
  197. package/registry/examples/popover-demo.tsx +19 -19
  198. package/registry/examples/progress-demo.tsx +1 -1
  199. package/registry/examples/progress-slider-demo.tsx +1 -1
  200. package/registry/examples/radio-card-demo.tsx +4 -4
  201. package/registry/examples/radio-group-demo.tsx +2 -2
  202. package/registry/examples/resizable-demo.tsx +1 -1
  203. package/registry/examples/results-toolbar-demo.tsx +44 -0
  204. package/registry/examples/scroll-area-demo.tsx +3 -3
  205. package/registry/examples/segmented-control-demo.tsx +39 -0
  206. package/registry/examples/select-demo.tsx +1 -1
  207. package/registry/examples/sidebar-demo.tsx +1 -1
  208. package/registry/examples/slider-demo.tsx +3 -3
  209. package/registry/examples/social-button-demo.tsx +18 -8
  210. package/registry/examples/spinner-demo.tsx +13 -13
  211. package/registry/examples/tabs-demo.tsx +1 -1
  212. package/registry/examples/tag-demo.tsx +1 -1
  213. package/registry/examples/textarea-demo.tsx +4 -4
  214. package/registry/examples/toast-demo.tsx +1 -1
  215. package/registry/examples/toggle-demo.tsx +6 -6
  216. package/registry/examples/tooltip-demo.tsx +4 -4
  217. package/registry/guidance/USAGE.md +27 -0
  218. package/registry/hooks/use-direction.ts +193 -13
  219. package/registry/index.json +5311 -377
  220. package/registry/styles/globals.css +2440 -4061
  221. package/registry.json +119 -149
  222. package/schema/config.schema.json +11 -8
  223. package/schema/registry.schema.json +150 -45
  224. package/schema/tokens.schema.json +134 -34
  225. package/src/cli/command-line.js +194 -0
  226. package/src/cli/config-editors.js +9 -0
  227. package/src/cli/doctor.js +399 -0
  228. package/src/cli/file-transaction.js +295 -0
  229. package/src/cli/global-css.js +153 -0
  230. package/src/cli/index.js +567 -1613
  231. package/src/cli/install-plan.js +592 -0
  232. package/src/cli/install-receipt.js +171 -0
  233. package/src/cli/jsonc.js +203 -0
  234. package/src/cli/package-install-state.js +254 -0
  235. package/src/cli/package-manager.js +238 -0
  236. package/src/cli/package-plan.js +185 -0
  237. package/src/cli/package-version.js +97 -0
  238. package/src/cli/registry-query.js +207 -0
  239. package/src/cli/reporting.js +55 -0
  240. package/src/cli/schema-validation.js +19 -0
  241. package/src/cli/source-scanner.js +686 -0
  242. package/src/cli/vite-config.js +478 -0
  243. package/tokens/banhaten.tokens.json +4312 -309
  244. package/docs/design-system/components/command-bar.md +0 -57
  245. package/registry/components/expanded/BannerBoard.tsx +0 -225
  246. package/registry/components/expanded/CatalogTag.tsx +0 -85
  247. package/registry/components/expanded/CommandBar.tsx +0 -386
  248. package/registry/components/expanded/IconExplorer.tsx +0 -612
  249. package/registry/components/expanded/TimelineShowcase.tsx +0 -158
  250. package/registry/components/expanded/commandBar.css +0 -470
  251. package/registry/components/expanded/iconExplorer.css +0 -764
  252. package/registry/components/expanded/iconPacks.ts +0 -866
  253. package/registry/components/expanded/tag.css +0 -148
  254. package/registry/examples/expanded/icons-demo.tsx +0 -5
  255. package/tokens/Color mode.zip +0 -0
  256. package/tokens/Numbers.zip +0 -0
  257. package/tokens/Radius.zip +0 -0
  258. package/tokens/Theme.zip +0 -0
@@ -121,15 +121,15 @@ const Card = React.forwardRef<HTMLElement, CardProps>(function Card({
121
121
  moreButtonLabel = "Open card menu",
122
122
  onMoreClick,
123
123
  orientation = "vertical",
124
- primaryActionLabel = "Button",
124
+ primaryActionLabel,
125
125
  primaryActionProps,
126
- secondaryActionLabel = "Button",
126
+ secondaryActionLabel,
127
127
  secondaryActionProps,
128
- showDescription = true,
129
- showFooter = true,
130
- showImage = true,
131
- showMoreButton = true,
132
- showSubtitle = true,
128
+ showDescription,
129
+ showFooter,
130
+ showImage,
131
+ showMoreButton,
132
+ showSubtitle,
133
133
  subtitle,
134
134
  title,
135
135
  type = "leading-image",
@@ -142,13 +142,15 @@ const Card = React.forwardRef<HTMLElement, CardProps>(function Card({
142
142
  ? "leading-image"
143
143
  : type ?? "leading-image"
144
144
  const fullImage = cardOrientation === "vertical" && cardType === "full-image"
145
- const renderedTitle = title ?? "Card Title"
146
- const renderedSubtitle = subtitle ?? "Card Subtitle"
147
- const renderedDescription =
148
- description ??
149
- "Journey through time as we explore the mysteries of the Great Pyramids and the Sphinx."
145
+ const shouldShowDescription = showDescription ?? description !== undefined
146
+ const shouldShowFooter =
147
+ showFooter ??
148
+ Boolean(footer || actions || primaryActionLabel || secondaryActionLabel)
149
+ const shouldShowImage = showImage ?? Boolean(imageSrc)
150
+ const shouldShowMoreButton = showMoreButton ?? Boolean(onMoreClick)
151
+ const shouldShowSubtitle = showSubtitle ?? subtitle !== undefined
150
152
 
151
- const media = showImage ? (
153
+ const media = shouldShowImage ? (
152
154
  <CardMedia
153
155
  imageAlt={imageAlt}
154
156
  imageSrc={imageSrc}
@@ -160,7 +162,7 @@ const Card = React.forwardRef<HTMLElement, CardProps>(function Card({
160
162
  <CardBody
161
163
  actions={actions}
162
164
  breakpoint={cardBreakpoint}
163
- description={renderedDescription}
165
+ description={description}
164
166
  footer={footer}
165
167
  moreButtonLabel={moreButtonLabel}
166
168
  onMoreClick={onMoreClick}
@@ -169,12 +171,12 @@ const Card = React.forwardRef<HTMLElement, CardProps>(function Card({
169
171
  primaryActionProps={primaryActionProps}
170
172
  secondaryActionLabel={secondaryActionLabel}
171
173
  secondaryActionProps={secondaryActionProps}
172
- showDescription={showDescription}
173
- showFooter={showFooter && !fullImage}
174
- showMoreButton={showMoreButton}
175
- showSubtitle={showSubtitle}
176
- subtitle={renderedSubtitle}
177
- title={renderedTitle}
174
+ showDescription={shouldShowDescription}
175
+ showFooter={shouldShowFooter && !fullImage}
176
+ showMoreButton={shouldShowMoreButton}
177
+ showSubtitle={shouldShowSubtitle}
178
+ subtitle={subtitle}
179
+ title={title}
178
180
  />
179
181
  )
180
182
 
@@ -206,10 +208,10 @@ const Card = React.forwardRef<HTMLElement, CardProps>(function Card({
206
208
  breakpoint={cardBreakpoint}
207
209
  moreButtonLabel={moreButtonLabel}
208
210
  onMoreClick={onMoreClick}
209
- showMoreButton={showMoreButton}
210
- showSubtitle={showSubtitle}
211
- subtitle={renderedSubtitle}
212
- title={renderedTitle}
211
+ showMoreButton={shouldShowMoreButton}
212
+ showSubtitle={shouldShowSubtitle}
213
+ subtitle={subtitle}
214
+ title={title}
213
215
  />
214
216
  </>
215
217
  ) : cardOrientation === "horizontal" &&
@@ -275,9 +277,9 @@ const CardActions = React.forwardRef<HTMLDivElement, CardActionsProps>(
275
277
  function CardActions({
276
278
  children,
277
279
  className,
278
- primaryLabel = "Button",
280
+ primaryLabel,
279
281
  primaryProps,
280
- secondaryLabel = "Button",
282
+ secondaryLabel,
281
283
  secondaryProps,
282
284
  ...props
283
285
  }, ref) {
@@ -286,23 +288,28 @@ const CardActions = React.forwardRef<HTMLDivElement, CardActionsProps>(
286
288
  data-slot="card-actions"
287
289
  ref={ref}
288
290
  className={cn(
289
- "grid w-full max-w-[var(--bh-card-actions-max-width)] grid-cols-2 gap-[var(--bh-space-xl-12)]",
291
+ "grid w-full max-w-[var(--bh-card-actions-max-width)] gap-[var(--bh-space-xl-12)]",
292
+ primaryLabel && secondaryLabel ? "grid-cols-2" : "grid-cols-1",
290
293
  className
291
294
  )}
292
295
  {...props}
293
296
  >
294
297
  {children ?? (
295
298
  <>
296
- <Button {...primaryProps} className={cn("min-w-0", primaryProps?.className)}>
297
- {primaryLabel}
298
- </Button>
299
- <Button
300
- variant="soft"
301
- {...secondaryProps}
302
- className={cn("min-w-0", secondaryProps?.className)}
303
- >
304
- {secondaryLabel}
305
- </Button>
299
+ {primaryLabel ? (
300
+ <Button {...primaryProps} className={cn("min-w-0", primaryProps?.className)}>
301
+ {primaryLabel}
302
+ </Button>
303
+ ) : null}
304
+ {secondaryLabel ? (
305
+ <Button
306
+ variant="soft"
307
+ {...secondaryProps}
308
+ className={cn("min-w-0", secondaryProps?.className)}
309
+ >
310
+ {secondaryLabel}
311
+ </Button>
312
+ ) : null}
306
313
  </>
307
314
  )}
308
315
  </div>
@@ -331,21 +338,21 @@ function CardBody({
331
338
  }: {
332
339
  actions?: React.ReactNode
333
340
  breakpoint: CardBreakpoint
334
- description: React.ReactNode
341
+ description?: React.ReactNode
335
342
  footer?: React.ReactNode
336
343
  moreButtonLabel: string
337
344
  onMoreClick?: React.MouseEventHandler<HTMLButtonElement>
338
345
  orientation: CardOrientation
339
- primaryActionLabel: React.ReactNode
346
+ primaryActionLabel?: React.ReactNode
340
347
  primaryActionProps?: ButtonProps
341
- secondaryActionLabel: React.ReactNode
348
+ secondaryActionLabel?: React.ReactNode
342
349
  secondaryActionProps?: ButtonProps
343
350
  showDescription: boolean
344
351
  showFooter: boolean
345
352
  showMoreButton: boolean
346
353
  showSubtitle: boolean
347
- subtitle: React.ReactNode
348
- title: React.ReactNode
354
+ subtitle?: React.ReactNode
355
+ title?: React.ReactNode
349
356
  }) {
350
357
  const horizontal = orientation === "horizontal"
351
358
 
@@ -378,7 +385,7 @@ function CardBody({
378
385
  title={title}
379
386
  subtitleClassName={getSubtitleClassName(breakpoint)}
380
387
  />
381
- {showDescription ? (
388
+ {showDescription && description !== undefined ? (
382
389
  <p
383
390
  data-slot="card-description"
384
391
  dir="auto"
@@ -423,8 +430,8 @@ function CardFullImageContent({
423
430
  onMoreClick?: React.MouseEventHandler<HTMLButtonElement>
424
431
  showMoreButton: boolean
425
432
  showSubtitle: boolean
426
- subtitle: React.ReactNode
427
- title: React.ReactNode
433
+ subtitle?: React.ReactNode
434
+ title?: React.ReactNode
428
435
  }) {
429
436
  return (
430
437
  <div
@@ -475,20 +482,22 @@ function CardHeader({
475
482
  className="flex min-w-0 items-start gap-[var(--bh-space-3xl-16)]"
476
483
  >
477
484
  <div className="flex min-w-0 flex-1 flex-col gap-[var(--bh-space-xxs-2)]">
478
- <h3
479
- data-slot="card-title"
480
- dir="auto"
481
- className={cn(
482
- "min-w-0 break-words text-[length:var(--bh-text-heading-sm-semibold-font-size)]",
483
- "font-[var(--bh-text-heading-sm-semibold-font-weight)]",
484
- "leading-[var(--bh-text-heading-sm-semibold-line-height)]",
485
- "tracking-[var(--bh-text-heading-sm-semibold-letter-spacing)]",
486
- onImage ? "text-[var(--bh-content-on-color)]" : "text-[var(--bh-content-default)]"
487
- )}
488
- >
489
- {title}
490
- </h3>
491
- {showSubtitle ? (
485
+ {title !== undefined ? (
486
+ <h3
487
+ data-slot="card-title"
488
+ dir="auto"
489
+ className={cn(
490
+ "min-w-0 break-words text-[length:var(--bh-text-heading-sm-semibold-font-size)]",
491
+ "font-[var(--bh-text-heading-sm-semibold-font-weight)]",
492
+ "leading-[var(--bh-text-heading-sm-semibold-line-height)]",
493
+ "tracking-[var(--bh-text-heading-sm-semibold-letter-spacing)]",
494
+ onImage ? "text-[var(--bh-content-on-color)]" : "text-[var(--bh-content-default)]"
495
+ )}
496
+ >
497
+ {title}
498
+ </h3>
499
+ ) : null}
500
+ {showSubtitle && subtitle !== undefined ? (
492
501
  <p
493
502
  data-slot="card-subtitle"
494
503
  dir="auto"
@@ -8,6 +8,7 @@ import { ArrowLeftIcon, ArrowRightIcon } from "lucide-react"
8
8
 
9
9
  import { Button } from "@/components/ui/button"
10
10
  import { cn } from "@/lib/utils"
11
+ import { useResolvedDirection, type Direction } from "@/hooks/use-direction"
11
12
 
12
13
  type CarouselApi = UseEmblaCarouselType[1]
13
14
  type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
@@ -15,7 +16,23 @@ type CarouselOptions = UseCarouselParameters[0]
15
16
  type CarouselPlugin = UseCarouselParameters[1]
16
17
  type CarouselOrientation = "horizontal" | "vertical"
17
18
 
19
+ type CarouselMessages = {
20
+ nextSlide: string
21
+ previousSlide: string
22
+ }
23
+
24
+ const defaultCarouselMessages: CarouselMessages = {
25
+ nextSlide: "Next slide",
26
+ previousSlide: "Previous slide",
27
+ }
28
+
29
+ const arabicCarouselMessages: CarouselMessages = {
30
+ nextSlide: "\u0627\u0644\u0634\u0631\u064a\u062d\u0629 \u0627\u0644\u062a\u0627\u0644\u064a\u0629",
31
+ previousSlide: "\u0627\u0644\u0634\u0631\u064a\u062d\u0629 \u0627\u0644\u0633\u0627\u0628\u0642\u0629",
32
+ }
33
+
18
34
  type CarouselProps = React.ComponentProps<"div"> & {
35
+ messages?: Partial<CarouselMessages>
19
36
  opts?: CarouselOptions
20
37
  orientation?: CarouselOrientation
21
38
  plugins?: CarouselPlugin
@@ -29,6 +46,8 @@ type CarouselContextValue = {
29
46
  api: CarouselApi
30
47
  canScrollNext: boolean
31
48
  canScrollPrev: boolean
49
+ direction: Direction
50
+ messages: CarouselMessages
32
51
  orientation: CarouselOrientation
33
52
  scrollNext: () => void
34
53
  scrollPrev: () => void
@@ -41,6 +60,8 @@ const Carousel = React.forwardRef<HTMLDivElement, CarouselProps>(
41
60
  {
42
61
  children,
43
62
  className,
63
+ dir,
64
+ messages: messageOverrides,
44
65
  opts,
45
66
  orientation = "horizontal",
46
67
  plugins,
@@ -49,15 +70,33 @@ const Carousel = React.forwardRef<HTMLDivElement, CarouselProps>(
49
70
  },
50
71
  ref
51
72
  ) {
73
+ const rootRef = React.useRef<HTMLDivElement | null>(null)
74
+ const direction = useResolvedDirection(
75
+ dir === "ltr" || dir === "rtl" ? dir : undefined,
76
+ rootRef
77
+ )
78
+ const setRootRef = React.useCallback(
79
+ (node: HTMLDivElement | null) => {
80
+ rootRef.current = node
81
+ if (typeof ref === "function") ref(node)
82
+ else if (ref) ref.current = node
83
+ },
84
+ [ref]
85
+ )
52
86
  const [carouselRef, api] = useEmblaCarousel(
53
87
  {
54
88
  ...opts,
55
89
  axis: orientation === "horizontal" ? "x" : "y",
90
+ direction,
56
91
  },
57
92
  plugins
58
93
  )
59
94
  const [canScrollPrev, setCanScrollPrev] = React.useState(false)
60
95
  const [canScrollNext, setCanScrollNext] = React.useState(false)
96
+ const messages = React.useMemo(
97
+ () => ({ ...defaultCarouselMessages, ...messageOverrides }),
98
+ [messageOverrides]
99
+ )
61
100
 
62
101
  const onSelect = React.useCallback((nextApi: CarouselApi) => {
63
102
  if (!nextApi) return
@@ -86,6 +125,8 @@ const Carousel = React.forwardRef<HTMLDivElement, CarouselProps>(
86
125
  api,
87
126
  canScrollNext,
88
127
  canScrollPrev,
128
+ direction,
129
+ messages,
89
130
  orientation,
90
131
  scrollNext,
91
132
  scrollPrev,
@@ -94,7 +135,8 @@ const Carousel = React.forwardRef<HTMLDivElement, CarouselProps>(
94
135
  <div
95
136
  data-orientation={orientation}
96
137
  data-slot="carousel"
97
- ref={ref}
138
+ dir={direction}
139
+ ref={setRootRef}
98
140
  role="region"
99
141
  className={cn("relative", className)}
100
142
  {...props}
@@ -153,11 +195,13 @@ const CarouselItem = React.forwardRef<HTMLDivElement, CarouselItemProps>(
153
195
 
154
196
  const CarouselPrevious = React.forwardRef<HTMLButtonElement, CarouselButtonProps>(
155
197
  function CarouselPrevious({ className, variant = "outline", size = "icon", ...props }, ref) {
156
- const { canScrollPrev, orientation, scrollPrev } = useCarousel()
198
+ const { canScrollPrev, messages, orientation, scrollPrev } = useCarousel()
157
199
 
158
200
  return (
159
201
  <Button
160
- aria-label="Previous slide"
202
+ aria-label={
203
+ props["aria-label"] ?? messages.previousSlide
204
+ }
161
205
  className={cn(
162
206
  "absolute size-[var(--bh-space-7xl-40)] rounded-[var(--bh-radius-full)]",
163
207
  orientation === "horizontal"
@@ -181,11 +225,13 @@ const CarouselPrevious = React.forwardRef<HTMLButtonElement, CarouselButtonProps
181
225
 
182
226
  const CarouselNext = React.forwardRef<HTMLButtonElement, CarouselButtonProps>(
183
227
  function CarouselNext({ className, variant = "outline", size = "icon", ...props }, ref) {
184
- const { canScrollNext, orientation, scrollNext } = useCarousel()
228
+ const { canScrollNext, messages, orientation, scrollNext } = useCarousel()
185
229
 
186
230
  return (
187
231
  <Button
188
- aria-label="Next slide"
232
+ aria-label={
233
+ props["aria-label"] ?? messages.nextSlide
234
+ }
189
235
  className={cn(
190
236
  "absolute size-[var(--bh-space-7xl-40)] rounded-[var(--bh-radius-full)]",
191
237
  orientation === "horizontal"
@@ -216,6 +262,7 @@ function useCarousel() {
216
262
  }
217
263
 
218
264
  export {
265
+ arabicCarouselMessages,
219
266
  Carousel,
220
267
  CarouselContent,
221
268
  CarouselItem,
@@ -227,6 +274,7 @@ export type {
227
274
  CarouselButtonProps,
228
275
  CarouselContentProps,
229
276
  CarouselItemProps,
277
+ CarouselMessages,
230
278
  CarouselOptions,
231
279
  CarouselOrientation,
232
280
  CarouselPlugin,
@@ -37,6 +37,17 @@ type ChartLegendContentProps = React.ComponentProps<"div"> & {
37
37
  payload?: ChartPayloadItem[]
38
38
  }
39
39
 
40
+ const chartCategoricalTokens = [
41
+ "--bh-chart-categorical-brand",
42
+ "--bh-chart-categorical-success",
43
+ "--bh-chart-categorical-warning",
44
+ "--bh-chart-categorical-danger",
45
+ "--bh-chart-categorical-information",
46
+ "--bh-chart-categorical-purple",
47
+ "--bh-chart-categorical-aqua",
48
+ "--bh-chart-categorical-fuchsia",
49
+ ] as const
50
+
40
51
  function ChartContainer({
41
52
  children,
42
53
  className,
@@ -62,6 +73,26 @@ function ChartContainer({
62
73
  const ChartTooltip = RechartsTooltip
63
74
  const ChartLegend = RechartsLegend
64
75
 
76
+ /**
77
+ * Recharts clones the element passed as `content` and injects its own internal
78
+ * props (`iconSize`, `verticalAlign`, `chartWidth`, `itemSorter`, …). Those are
79
+ * not valid DOM attributes, so forward only what a presentational element can
80
+ * legitimately receive.
81
+ */
82
+ function forwardableDomProps(props: Record<string, unknown>) {
83
+ return Object.fromEntries(
84
+ Object.entries(props).filter(
85
+ ([key]) =>
86
+ key === "id" ||
87
+ key === "role" ||
88
+ key === "style" ||
89
+ key === "tabIndex" ||
90
+ key.startsWith("aria-") ||
91
+ key.startsWith("data-")
92
+ )
93
+ )
94
+ }
95
+
65
96
  function ChartTooltipContent({
66
97
  active,
67
98
  className,
@@ -79,7 +110,7 @@ function ChartTooltipContent({
79
110
  "grid min-w-[var(--bh-space-17xl-288)] gap-[var(--bh-space-sm-6)] rounded-[var(--bh-radius-lg-8)] bg-[var(--bh-bg-raised)] p-[var(--bh-space-xl-12)] text-[length:var(--bh-text-body-xs-regular-font-size)] font-[var(--bh-text-body-xs-regular-font-weight)] leading-[var(--bh-text-body-xs-regular-line-height)] tracking-[var(--bh-text-body-xs-regular-letter-spacing)] text-[var(--bh-content-default)] shadow-[var(--shadow-popover)]",
80
111
  className
81
112
  )}
82
- {...props}
113
+ {...forwardableDomProps(props)}
83
114
  >
84
115
  {!hideLabel && label ? (
85
116
  <div className="font-[var(--bh-text-body-xs-medium-font-weight)]">
@@ -121,7 +152,7 @@ function ChartLegendContent({
121
152
  <div
122
153
  data-slot="chart-legend"
123
154
  className={cn("flex flex-wrap items-center justify-center gap-[var(--bh-space-3xl-16)]", className)}
124
- {...props}
155
+ {...forwardableDomProps(props)}
125
156
  >
126
157
  {payload.map((item, index) => (
127
158
  <div
@@ -146,7 +177,9 @@ function getChartStyle(config?: ChartConfig): React.CSSProperties {
146
177
  return Object.entries(config).reduce<React.CSSProperties>(
147
178
  (style, [key, value], index) => ({
148
179
  ...style,
149
- [`--color-${key}`]: value.color ?? `var(--bh-chart-${(index % 5) + 1})`,
180
+ [`--color-${key}`]:
181
+ value.color ??
182
+ `var(${chartCategoricalTokens[index % chartCategoricalTokens.length]})`,
150
183
  }),
151
184
  {}
152
185
  )
@@ -63,13 +63,13 @@ const Checkbox = React.forwardRef<
63
63
  aria-hidden="true"
64
64
  className="size-[var(--bh-checkbox-icon-size)] group-data-[state=indeterminate]/checkbox:hidden"
65
65
  focusable="false"
66
- strokeWidth={3}
66
+ strokeWidth="var(--bh-icon-stroke-300)"
67
67
  />
68
68
  <MinusIcon
69
69
  aria-hidden="true"
70
70
  className="hidden size-[var(--bh-checkbox-icon-size)] group-data-[state=indeterminate]/checkbox:block"
71
71
  focusable="false"
72
- strokeWidth={3}
72
+ strokeWidth="var(--bh-icon-stroke-300)"
73
73
  />
74
74
  </CheckboxPrimitive.Indicator>
75
75
  </CheckboxPrimitive.Root>
@@ -231,7 +231,7 @@ function CheckboxCardIndicator({
231
231
  aria-hidden="true"
232
232
  className="absolute top-1/2 left-1/2 size-[var(--bh-checkbox-icon-size)] -translate-x-1/2 -translate-y-1/2 text-[var(--bh-content-on-color)]"
233
233
  focusable="false"
234
- strokeWidth={3}
234
+ strokeWidth="var(--bh-icon-stroke-300)"
235
235
  />
236
236
  ) : null}
237
237
  </span>
@@ -38,7 +38,7 @@ const CollapsibleTrigger = React.forwardRef<
38
38
  <ChevronDownIcon
39
39
  aria-hidden="true"
40
40
  className="size-[var(--bh-space-3xl-16)] shrink-0 text-[var(--bh-content-subtle)] transition-transform group-data-[state=open]/collapsible-trigger:rotate-180"
41
- strokeWidth={2}
41
+ strokeWidth="var(--bh-icon-stroke-200)"
42
42
  />
43
43
  </CollapsiblePrimitive.Trigger>
44
44
  )
@@ -44,7 +44,7 @@ const CommandInput = React.forwardRef<HTMLInputElement, CommandInputProps>(
44
44
  <SearchIcon
45
45
  aria-hidden="true"
46
46
  className="size-[var(--bh-space-5xl-24)] shrink-0 text-[var(--bh-content-muted)]"
47
- strokeWidth={2}
47
+ strokeWidth="var(--bh-icon-stroke-200)"
48
48
  />
49
49
  <CommandPrimitive.Input
50
50
  data-slot="command-input"
@@ -53,7 +53,7 @@ const menuContentClassName =
53
53
  "z-[var(--bh-z-popover)] min-w-[var(--bh-menu-width)] overflow-hidden rounded-[var(--bh-radius-xl-10)] bg-[var(--bh-bg-raised)] p-[var(--bh-space-xs-4)] text-[var(--bh-content-default)] shadow-[var(--shadow-menu)]"
54
54
 
55
55
  const menuItemClassName =
56
- "relative flex min-h-[var(--bh-menu-item-min-height)] cursor-default select-none items-center gap-[var(--bh-space-md-8)] rounded-[var(--bh-radius-md-6)] px-[var(--bh-space-md-8)] py-[var(--bh-space-sm-6)] text-start text-[length:var(--bh-text-body-sm-regular-font-size)] font-[var(--bh-text-body-sm-regular-font-weight)] leading-[var(--bh-text-body-sm-regular-line-height)] tracking-[var(--bh-text-body-sm-regular-letter-spacing)] text-[var(--bh-content-default)] outline-none data-[disabled]:pointer-events-none data-[disabled]:text-[var(--bh-content-disabled)] data-[highlighted]:bg-[var(--bh-menu-item-hover-bg)]"
56
+ "relative flex min-h-[var(--bh-menu-item-default-height)] cursor-default select-none items-center gap-[var(--bh-space-md-8)] rounded-[var(--bh-radius-md-6)] px-[var(--bh-space-md-8)] py-[var(--bh-space-sm-6)] text-start text-[length:var(--bh-text-body-sm-regular-font-size)] font-[var(--bh-text-body-sm-regular-font-weight)] leading-[var(--bh-text-body-sm-regular-line-height)] tracking-[var(--bh-text-body-sm-regular-letter-spacing)] text-[var(--bh-content-default)] outline-none data-[disabled]:pointer-events-none data-[disabled]:text-[var(--bh-content-disabled)] data-[highlighted]:bg-[var(--bh-menu-item-hover-bg)]"
57
57
 
58
58
  const ContextMenuContent = React.forwardRef<
59
59
  React.ElementRef<typeof ContextMenuPrimitive.Content>,