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
@@ -18,10 +18,25 @@ import { Button, type ButtonProps } from "@/components/ui/button"
18
18
  import { Checkbox, type CheckboxProps } from "@/components/ui/checkbox"
19
19
  import { Spinner } from "@/components/ui/spinner"
20
20
  import { cn } from "@/lib/utils"
21
+ import { useResolvedDirection } from "@/hooks/use-direction"
21
22
 
22
23
  type ModalSize = "sm" | "default" | "lg"
23
24
  type ModalIntent = "default" | "warning" | "danger"
24
25
 
26
+ type ModalMessages = {
27
+ checkboxLabel: React.ReactNode
28
+ closeLabel: string
29
+ primaryActionLabel: React.ReactNode
30
+ secondaryActionLabel: React.ReactNode
31
+ }
32
+
33
+ type ConfirmModalMessages = {
34
+ cancelLabel: React.ReactNode
35
+ closeLabel: string
36
+ confirmLabel: React.ReactNode
37
+ confirmLoadingLabel: React.ReactNode
38
+ }
39
+
25
40
  const modalSurfaceBaseClassName = [
26
41
  "flex max-h-[calc(100vh-(var(--bh-space-8xl-48)*2))] w-[var(--bh-modal-width)] max-w-[calc(100vw-(var(--bh-space-4xl-20)*2))] flex-col overflow-hidden rounded-[var(--bh-radius-2xl-12)] bg-[var(--bh-bg-raised)] text-[var(--bh-content-default)] shadow-[var(--shadow-modal)] outline-none",
27
42
  "[--bh-modal-border:var(--bh-border-subtle)] [--shadow-modal:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-modal-border),var(--shadow-component-default)]",
@@ -109,9 +124,11 @@ type ModalProps = Omit<ModalRootProps, "children"> &
109
124
  checkboxLabel?: React.ReactNode
110
125
  children?: React.ReactNode
111
126
  className?: string
127
+ closeLabel?: string
112
128
  description?: React.ReactNode
113
129
  dir?: "ltr" | "rtl"
114
130
  footer?: React.ReactNode
131
+ messages?: Partial<ModalMessages>
115
132
  overlayClassName?: string
116
133
  primaryActionLabel?: React.ReactNode
117
134
  secondaryActionLabel?: React.ReactNode
@@ -120,13 +137,14 @@ type ModalProps = Omit<ModalRootProps, "children"> &
120
137
  showDivider?: boolean
121
138
  showFooter?: boolean
122
139
  showIcon?: boolean
123
- title?: React.ReactNode
140
+ title: React.ReactNode
124
141
  }
125
142
 
126
143
  type ConfirmModalProps = Omit<
127
144
  ModalProps,
128
145
  | "checkboxLabel"
129
146
  | "footer"
147
+ | "messages"
130
148
  | "primaryActionLabel"
131
149
  | "secondaryActionLabel"
132
150
  | "showCheckbox"
@@ -142,38 +160,42 @@ type ConfirmModalProps = Omit<
142
160
  confirmLoadingLabel?: React.ReactNode
143
161
  confirmVariant?: ModalActionButtonProps["variant"]
144
162
  intent?: ModalIntent
163
+ messages?: Partial<ConfirmModalMessages>
145
164
  onCancel?: React.MouseEventHandler<HTMLButtonElement>
146
165
  onConfirm?: React.MouseEventHandler<HTMLButtonElement>
147
166
  onEscapeKeyDown?: ModalContentProps["onEscapeKeyDown"]
148
- onInteractOutside?: ModalContentProps["onInteractOutside"]
149
167
  onOpenAutoFocus?: ModalContentProps["onOpenAutoFocus"]
150
- onPointerDownOutside?: ModalContentProps["onPointerDownOutside"]
151
168
  preventEscapeDismiss?: boolean
152
- preventOutsideDismiss?: boolean
153
169
  showCancelButton?: boolean
154
170
  }
155
171
 
156
- const defaultModalTitle = "Modal Title"
157
- const defaultModalDescription =
158
- "Are you sure you want to proceed with this action? Please review your choices before confirming."
159
- const defaultModalCheckboxLabel = "Don't show me again"
160
- const defaultConfirmTitle = "Confirm action"
161
- const defaultConfirmDescription =
162
- "This action may affect saved data. Review the details before confirming."
163
- const defaultConfirmCancelLabel = "Cancel"
164
- const defaultConfirmActionLabel = "Confirm"
165
- const defaultConfirmLoadingLabel = "Confirming"
166
- const defaultRtlModalTitle = "\u0639\u0646\u0648\u0627\u0646 \u0627\u0644\u0646\u0645\u0648\u0630\u062c"
167
- const defaultRtlModalDescription =
168
- "\u0647\u0644 \u0623\u0646\u062a \u0645\u062a\u0623\u0643\u062f \u0623\u0646\u0643 \u062a\u0631\u064a\u062f \u0627\u0644\u0645\u062a\u0627\u0628\u0639\u0629 \u0645\u0639 \u0647\u0630\u0627 \u0627\u0644\u0625\u062c\u0631\u0627\u0621\u061f \u064a\u0631\u062c\u0649 \u0645\u0631\u0627\u062c\u0639\u0629 \u062e\u064a\u0627\u0631\u0627\u062a\u0643 \u0642\u0628\u0644 \u0627\u0644\u062a\u0623\u0643\u064a\u062f."
169
- const defaultRtlModalCheckboxLabel = "\u0627\u0644\u062a\u0633\u0645\u064a\u0629"
170
- const defaultRtlActionLabel = "\u0627\u0646\u0642\u0631 \u0647\u0646\u0627"
171
- const defaultRtlConfirmTitle = "\u062a\u0623\u0643\u064a\u062f \u0627\u0644\u0625\u062c\u0631\u0627\u0621"
172
- const defaultRtlConfirmDescription =
173
- "\u0642\u062f \u064a\u0624\u062b\u0631 \u0647\u0630\u0627 \u0627\u0644\u0625\u062c\u0631\u0627\u0621 \u0639\u0644\u0649 \u0627\u0644\u0628\u064a\u0627\u0646\u0627\u062a \u0627\u0644\u0645\u062d\u0641\u0648\u0638\u0629. \u0631\u0627\u062c\u0639 \u0627\u0644\u062a\u0641\u0627\u0635\u064a\u0644 \u0642\u0628\u0644 \u0627\u0644\u062a\u0623\u0643\u064a\u062f."
174
- const defaultRtlConfirmCancelLabel = "\u0625\u0644\u063a\u0627\u0621"
175
- const defaultRtlConfirmActionLabel = "\u062a\u0623\u0643\u064a\u062f"
176
- const defaultRtlConfirmLoadingLabel = "\u062c\u0627\u0631\u064a \u0627\u0644\u062a\u0623\u0643\u064a\u062f"
172
+ const defaultModalMessages: ModalMessages = {
173
+ checkboxLabel: "Don't show me again",
174
+ closeLabel: "Close modal",
175
+ primaryActionLabel: "Action",
176
+ secondaryActionLabel: "Action",
177
+ }
178
+
179
+ const arabicModalMessages: ModalMessages = {
180
+ checkboxLabel: "\u0627\u0644\u062a\u0633\u0645\u064a\u0629",
181
+ closeLabel: "\u0625\u063a\u0644\u0627\u0642 \u0627\u0644\u0646\u0645\u0648\u0630\u062c",
182
+ primaryActionLabel: "\u0627\u0646\u0642\u0631 \u0647\u0646\u0627",
183
+ secondaryActionLabel: "\u0627\u0646\u0642\u0631 \u0647\u0646\u0627",
184
+ }
185
+
186
+ const defaultConfirmModalMessages: ConfirmModalMessages = {
187
+ cancelLabel: "Cancel",
188
+ closeLabel: "Close modal",
189
+ confirmLabel: "Confirm",
190
+ confirmLoadingLabel: "Confirming",
191
+ }
192
+
193
+ const arabicConfirmModalMessages: ConfirmModalMessages = {
194
+ cancelLabel: "\u0625\u0644\u063a\u0627\u0621",
195
+ closeLabel: "\u0625\u063a\u0644\u0627\u0642 \u0627\u0644\u0646\u0645\u0648\u0630\u062c",
196
+ confirmLabel: "\u062a\u0623\u0643\u064a\u062f",
197
+ confirmLoadingLabel: "\u062c\u0627\u0631\u064a \u0627\u0644\u062a\u0623\u0643\u064a\u062f",
198
+ }
177
199
 
178
200
  const ModalRoot = DialogPrimitive.Root
179
201
  const ModalTrigger = DialogPrimitive.Trigger
@@ -198,16 +220,33 @@ const ModalContent = React.forwardRef<
198
220
  React.ElementRef<typeof DialogPrimitive.Content>,
199
221
  ModalContentProps
200
222
  >(function ModalContent(
201
- { children, className, overlayClassName, size, ...props },
223
+ { children, className, dir, overlayClassName, size, ...props },
202
224
  ref
203
225
  ) {
226
+ const contentRef = React.useRef<
227
+ React.ElementRef<typeof DialogPrimitive.Content> | null
228
+ >(null)
229
+ const resolvedDirection = useResolvedDirection(
230
+ dir === "ltr" || dir === "rtl" ? dir : undefined,
231
+ contentRef
232
+ )
233
+ const setContentRef = React.useCallback(
234
+ (node: React.ElementRef<typeof DialogPrimitive.Content> | null) => {
235
+ contentRef.current = node
236
+ if (typeof ref === "function") ref(node)
237
+ else if (ref) ref.current = node
238
+ },
239
+ [ref]
240
+ )
241
+
204
242
  return (
205
243
  <ModalPortal>
206
244
  <ModalOverlay className={overlayClassName} />
207
245
  <DialogPrimitive.Content
208
246
  data-size={size ?? "sm"}
209
247
  data-slot="modal-content"
210
- ref={ref}
248
+ dir={resolvedDirection}
249
+ ref={setContentRef}
211
250
  className={cn(modalContentVariants({ size, className }))}
212
251
  {...props}
213
252
  >
@@ -278,34 +317,46 @@ const ModalDescription = React.forwardRef<
278
317
  )
279
318
  })
280
319
 
281
- const ConfirmModalCloseButton = React.forwardRef<
320
+ // One close affordance, two dismissal wrappers. Dialog uses ModalClose and
321
+ // AlertDialog uses AlertDialogCancel, but the button itself is identical — so
322
+ // an a11y or focus-ring fix here reaches both instead of one.
323
+ const ModalCloseButtonSurface = React.forwardRef<
282
324
  HTMLButtonElement,
283
325
  ModalCloseButtonProps
284
- >(function ConfirmModalCloseButton(
326
+ >(function ModalCloseButtonSurface(
285
327
  { children, className, label = "Close modal", type = "button", ...props },
286
328
  ref
287
329
  ) {
330
+ return (
331
+ <button
332
+ aria-label={label}
333
+ data-slot="modal-close"
334
+ ref={ref}
335
+ type={type}
336
+ className={cn(
337
+ "absolute end-[var(--bh-space-3xl-16)] top-1/2 inline-flex size-[var(--bh-space-6xl-32)] -translate-y-1/2 items-center justify-center rounded-[var(--bh-radius-full)] border-0 bg-transparent p-0 text-[var(--bh-content-subtle)] outline-none transition-[background-color,box-shadow,color] hover:bg-[var(--bh-interactive-ghost-hover)] hover:text-[var(--bh-content-default)] focus-visible:shadow-[var(--shadow-button-focus)]",
338
+ className
339
+ )}
340
+ {...props}
341
+ >
342
+ {children ?? (
343
+ <XIcon
344
+ aria-hidden="true"
345
+ className="size-[var(--bh-space-5xl-24)]"
346
+ strokeWidth="var(--bh-icon-stroke-200)"
347
+ />
348
+ )}
349
+ </button>
350
+ )
351
+ })
352
+
353
+ const ConfirmModalCloseButton = React.forwardRef<
354
+ HTMLButtonElement,
355
+ ModalCloseButtonProps
356
+ >(function ConfirmModalCloseButton(props, ref) {
288
357
  return (
289
358
  <AlertDialogCancel asChild>
290
- <button
291
- aria-label={label}
292
- data-slot="modal-close"
293
- ref={ref}
294
- type={type}
295
- className={cn(
296
- "absolute end-[var(--bh-space-3xl-16)] top-1/2 inline-flex size-[var(--bh-space-6xl-32)] -translate-y-1/2 items-center justify-center rounded-[var(--bh-radius-full)] border-0 bg-transparent p-0 text-[var(--bh-content-subtle)] outline-none transition-[background-color,box-shadow,color] hover:bg-[var(--bh-interactive-ghost-hover)] hover:text-[var(--bh-content-default)] focus-visible:shadow-[var(--shadow-button-focus)]",
297
- className
298
- )}
299
- {...props}
300
- >
301
- {children ?? (
302
- <XIcon
303
- aria-hidden="true"
304
- className="size-[var(--bh-space-5xl-24)]"
305
- strokeWidth={2}
306
- />
307
- )}
308
- </button>
359
+ <ModalCloseButtonSurface ref={ref} {...props} />
309
360
  </AlertDialogCancel>
310
361
  )
311
362
  })
@@ -377,38 +428,17 @@ function ModalHeaderIcon({
377
428
  {...props}
378
429
  >
379
430
  {children ?? (
380
- <InfoIcon className="size-[var(--bh-space-3xl-16)]" strokeWidth={2} />
431
+ <InfoIcon className="size-[var(--bh-space-3xl-16)]" strokeWidth="var(--bh-icon-stroke-200)" />
381
432
  )}
382
433
  </span>
383
434
  )
384
435
  }
385
436
 
386
437
  const ModalCloseButton = React.forwardRef<HTMLButtonElement, ModalCloseButtonProps>(
387
- function ModalCloseButton(
388
- { children, className, label = "Close modal", type = "button", ...props },
389
- ref
390
- ) {
438
+ function ModalCloseButton(props, ref) {
391
439
  return (
392
440
  <ModalClose asChild>
393
- <button
394
- aria-label={label}
395
- data-slot="modal-close"
396
- ref={ref}
397
- type={type}
398
- className={cn(
399
- "absolute end-[var(--bh-space-3xl-16)] top-1/2 inline-flex size-[var(--bh-space-6xl-32)] -translate-y-1/2 items-center justify-center rounded-[var(--bh-radius-full)] border-0 bg-transparent p-0 text-[var(--bh-content-subtle)] outline-none transition-[background-color,box-shadow,color] hover:bg-[var(--bh-interactive-ghost-hover)] hover:text-[var(--bh-content-default)] focus-visible:shadow-[var(--shadow-button-focus)]",
400
- className
401
- )}
402
- {...props}
403
- >
404
- {children ?? (
405
- <XIcon
406
- aria-hidden="true"
407
- className="size-[var(--bh-space-5xl-24)]"
408
- strokeWidth={2}
409
- />
410
- )}
411
- </button>
441
+ <ModalCloseButtonSurface ref={ref} {...props} />
412
442
  </ModalClose>
413
443
  )
414
444
  }
@@ -419,7 +449,7 @@ function ModalFooterCheckbox({
419
449
  className,
420
450
  defaultChecked,
421
451
  disabled,
422
- label = defaultModalCheckboxLabel,
452
+ label = defaultModalMessages.checkboxLabel,
423
453
  onCheckedChange,
424
454
  ...props
425
455
  }: ModalFooterCheckboxProps) {
@@ -491,10 +521,12 @@ function Modal({
491
521
  checkboxLabel,
492
522
  children,
493
523
  className,
494
- defaultOpen = true,
524
+ closeLabel,
525
+ defaultOpen = false,
495
526
  description,
496
527
  dir,
497
528
  footer,
529
+ messages: messageOverrides,
498
530
  modal = true,
499
531
  onOpenChange,
500
532
  open,
@@ -510,20 +542,20 @@ function Modal({
510
542
  title,
511
543
  ...props
512
544
  }: ModalProps) {
513
- const isRtl = dir === "rtl"
514
545
  const rootProps =
515
546
  open === undefined
516
547
  ? { defaultOpen, modal, onOpenChange }
517
548
  : { modal, onOpenChange, open }
518
- const resolvedTitle = title ?? (isRtl ? defaultRtlModalTitle : defaultModalTitle)
519
- const resolvedDescription =
520
- description ?? (isRtl ? defaultRtlModalDescription : defaultModalDescription)
549
+ const messages = { ...defaultModalMessages, ...messageOverrides }
550
+ const resolvedTitle = title
551
+ const resolvedDescription = description
521
552
  const resolvedCheckboxLabel =
522
- checkboxLabel ?? (isRtl ? defaultRtlModalCheckboxLabel : defaultModalCheckboxLabel)
553
+ checkboxLabel ?? messages.checkboxLabel
523
554
  const resolvedPrimaryAction =
524
- primaryActionLabel ?? (isRtl ? defaultRtlActionLabel : "Action")
555
+ primaryActionLabel ?? messages.primaryActionLabel
525
556
  const resolvedSecondaryAction =
526
- secondaryActionLabel ?? (isRtl ? defaultRtlActionLabel : "Action")
557
+ secondaryActionLabel ?? messages.secondaryActionLabel
558
+ const resolvedCloseLabel = closeLabel ?? messages.closeLabel
527
559
 
528
560
  return (
529
561
  <ModalRoot {...rootProps}>
@@ -538,7 +570,7 @@ function Modal({
538
570
  <ModalHeader>
539
571
  {showIcon ? <ModalHeaderIcon /> : null}
540
572
  <ModalTitle>{resolvedTitle}</ModalTitle>
541
- {showCloseButton ? <ModalCloseButton /> : null}
573
+ {showCloseButton ? <ModalCloseButton label={resolvedCloseLabel} /> : null}
542
574
  </ModalHeader>
543
575
  <ModalBody>
544
576
  {children ?? <ModalDescription>{resolvedDescription}</ModalDescription>}
@@ -569,6 +601,7 @@ function ConfirmModal({
569
601
  cancelLabel,
570
602
  children,
571
603
  className,
604
+ closeLabel,
572
605
  closeOnCancel = true,
573
606
  closeOnConfirm = true,
574
607
  confirmButtonProps,
@@ -577,22 +610,20 @@ function ConfirmModal({
577
610
  confirmLoading = false,
578
611
  confirmLoadingLabel,
579
612
  confirmVariant,
580
- defaultOpen = true,
613
+ defaultOpen = false,
581
614
  description,
582
615
  dir,
583
616
  intent = "danger",
584
- modal: _modal = true,
617
+ messages: messageOverrides,
618
+ modal = true,
585
619
  onCancel,
586
620
  onConfirm,
587
621
  onEscapeKeyDown,
588
622
  onOpenAutoFocus,
589
623
  onOpenChange,
590
- onInteractOutside: _onInteractOutside,
591
- onPointerDownOutside: _onPointerDownOutside,
592
624
  open,
593
625
  overlayClassName,
594
626
  preventEscapeDismiss = false,
595
- preventOutsideDismiss: _preventOutsideDismiss = true,
596
627
  showCancelButton = true,
597
628
  showCloseButton = false,
598
629
  showDivider = true,
@@ -604,21 +635,24 @@ function ConfirmModal({
604
635
  }: ConfirmModalProps) {
605
636
  const cancelButtonRef = React.useRef<HTMLButtonElement>(null)
606
637
  const confirmButtonRef = React.useRef<HTMLButtonElement>(null)
607
- const isRtl = dir === "rtl"
638
+ const directionBoundaryRef = React.useRef<
639
+ React.ElementRef<typeof AlertDialogContent> | null
640
+ >(null)
641
+ const resolvedDirection = useResolvedDirection(dir, directionBoundaryRef)
608
642
  const rootProps =
609
643
  open === undefined
610
- ? { defaultOpen, onOpenChange }
611
- : { onOpenChange, open }
612
- const resolvedTitle = title ?? (isRtl ? defaultRtlConfirmTitle : defaultConfirmTitle)
613
- const resolvedDescription =
614
- description ?? (isRtl ? defaultRtlConfirmDescription : defaultConfirmDescription)
644
+ ? { defaultOpen, modal, onOpenChange }
645
+ : { modal, onOpenChange, open }
646
+ const messages = { ...defaultConfirmModalMessages, ...messageOverrides }
647
+ const resolvedTitle = title
648
+ const resolvedDescription = description
615
649
  const resolvedCancelLabel =
616
- cancelLabel ?? (isRtl ? defaultRtlConfirmCancelLabel : defaultConfirmCancelLabel)
650
+ cancelLabel ?? messages.cancelLabel
617
651
  const resolvedConfirmLabel =
618
- confirmLabel ?? (isRtl ? defaultRtlConfirmActionLabel : defaultConfirmActionLabel)
652
+ confirmLabel ?? messages.confirmLabel
619
653
  const resolvedConfirmLoadingLabel =
620
- confirmLoadingLabel ??
621
- (isRtl ? defaultRtlConfirmLoadingLabel : defaultConfirmLoadingLabel)
654
+ confirmLoadingLabel ?? messages.confirmLoadingLabel
655
+ const resolvedCloseLabel = closeLabel ?? messages.closeLabel
622
656
  const {
623
657
  children: cancelButtonChildren,
624
658
  onClick: cancelButtonOnClick,
@@ -663,7 +697,6 @@ function ConfirmModal({
663
697
  },
664
698
  [onEscapeKeyDown, preventEscapeDismiss]
665
699
  )
666
-
667
700
  const handleCancel: React.MouseEventHandler<HTMLButtonElement> = (event) => {
668
701
  cancelButtonOnClick?.(event)
669
702
  if (!event.defaultPrevented) {
@@ -717,7 +750,8 @@ function ConfirmModal({
717
750
  data-intent={intent}
718
751
  data-size={size}
719
752
  data-slot="confirm-modal"
720
- dir={dir}
753
+ dir={resolvedDirection}
754
+ ref={directionBoundaryRef}
721
755
  className={cn(modalContentVariants({ size, className }))}
722
756
  overlayClassName={overlayClassName}
723
757
  onEscapeKeyDown={handleEscapeKeyDown}
@@ -730,12 +764,12 @@ function ConfirmModal({
730
764
  {intent === "default" ? (
731
765
  <InfoIcon
732
766
  className="size-[var(--bh-space-3xl-16)]"
733
- strokeWidth={2}
767
+ strokeWidth="var(--bh-icon-stroke-200)"
734
768
  />
735
769
  ) : (
736
770
  <TriangleAlertIcon
737
771
  className="size-[var(--bh-space-3xl-16)]"
738
- strokeWidth={2}
772
+ strokeWidth="var(--bh-icon-stroke-200)"
739
773
  />
740
774
  )}
741
775
  </ModalHeaderIcon>
@@ -746,7 +780,9 @@ function ConfirmModal({
746
780
  >
747
781
  {resolvedTitle}
748
782
  </AlertDialogTitle>
749
- {showCloseButton ? <ConfirmModalCloseButton /> : null}
783
+ {showCloseButton ? (
784
+ <ConfirmModalCloseButton label={resolvedCloseLabel} />
785
+ ) : null}
750
786
  </ModalHeader>
751
787
  <ModalBody>
752
788
  {children ?? (
@@ -789,6 +825,8 @@ function getConfirmActionVariant(
789
825
  }
790
826
 
791
827
  export {
828
+ arabicConfirmModalMessages,
829
+ arabicModalMessages,
792
830
  ConfirmModal,
793
831
  Modal,
794
832
  ModalActionButton,
@@ -813,6 +851,7 @@ export {
813
851
  modalSurfaceVariants,
814
852
  }
815
853
  export type {
854
+ ConfirmModalMessages,
816
855
  ConfirmModalProps,
817
856
  ModalActionButtonProps,
818
857
  ModalActionsProps,
@@ -824,6 +863,7 @@ export type {
824
863
  ModalHeaderIconProps,
825
864
  ModalHeaderProps,
826
865
  ModalIntent,
866
+ ModalMessages,
827
867
  ModalOverlayProps,
828
868
  ModalProps,
829
869
  ModalRootProps,
@@ -163,7 +163,7 @@ const NativeSelect = React.forwardRef<HTMLSelectElement, NativeSelectProps>(
163
163
  <ChevronDownIcon
164
164
  aria-hidden="true"
165
165
  className="pointer-events-none absolute end-[var(--bh-select-trigger-padding-x)] top-1/2 size-[var(--bh-space-3xl-16)] -translate-y-1/2 text-[var(--bh-select-trigger-icon-color)]"
166
- strokeWidth={2}
166
+ strokeWidth="var(--bh-icon-stroke-200)"
167
167
  />
168
168
  </div>
169
169
  {shouldRenderHelper ? (
@@ -181,7 +181,7 @@ const NativeSelect = React.forwardRef<HTMLSelectElement, NativeSelectProps>(
181
181
  <InfoIcon
182
182
  aria-hidden="true"
183
183
  className="size-[var(--bh-space-3xl-16)] shrink-0"
184
- strokeWidth={2}
184
+ strokeWidth="var(--bh-icon-stroke-200)"
185
185
  />
186
186
  {helper}
187
187
  </p>