banhaten 0.1.2 → 0.1.3

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 (231) hide show
  1. package/README.md +93 -328
  2. package/banhaten.config.example.json +1 -1
  3. package/docs/design-system/README.md +11 -0
  4. package/docs/design-system/appearance-presets.md +184 -0
  5. package/docs/design-system/appearances/default.md +94 -0
  6. package/docs/design-system/appearances/rounded.md +95 -0
  7. package/docs/design-system/appearances/sharp.md +95 -0
  8. package/docs/design-system/component-showcase-consistency-report.md +217 -0
  9. package/docs/design-system/component-token-consistency-audit.md +163 -0
  10. package/docs/design-system/components/README.md +74 -0
  11. package/docs/design-system/components/accordion.md +51 -0
  12. package/docs/design-system/components/activity-feed.md +92 -0
  13. package/docs/design-system/components/alert-dialog.md +70 -0
  14. package/docs/design-system/components/alert.md +79 -0
  15. package/docs/design-system/components/aspect-ratio.md +44 -0
  16. package/docs/design-system/components/attribute.md +87 -0
  17. package/docs/design-system/components/autocomplete.md +74 -0
  18. package/docs/design-system/components/avatar.md +52 -0
  19. package/docs/design-system/components/badge.md +53 -0
  20. package/docs/design-system/components/banner.md +85 -0
  21. package/docs/design-system/components/breadcrumbs.md +174 -0
  22. package/docs/design-system/components/button-group.md +83 -0
  23. package/docs/design-system/components/button.md +77 -0
  24. package/docs/design-system/components/card.md +78 -0
  25. package/docs/design-system/components/carousel.md +44 -0
  26. package/docs/design-system/components/catalog-components.md +45 -0
  27. package/docs/design-system/components/chart.md +43 -0
  28. package/docs/design-system/components/checkbox.md +52 -0
  29. package/docs/design-system/components/collapsible.md +48 -0
  30. package/docs/design-system/components/command-bar.md +57 -0
  31. package/docs/design-system/components/command.md +60 -0
  32. package/docs/design-system/components/context-menu.md +44 -0
  33. package/docs/design-system/components/date-picker.md +77 -0
  34. package/docs/design-system/components/divider.md +101 -0
  35. package/docs/design-system/components/empty-state.md +55 -0
  36. package/docs/design-system/components/field.md +69 -0
  37. package/docs/design-system/components/file-upload.md +185 -0
  38. package/docs/design-system/components/hover-card.md +46 -0
  39. package/docs/design-system/components/icons.md +48 -0
  40. package/docs/design-system/components/input-group.md +56 -0
  41. package/docs/design-system/components/input-otp.md +55 -0
  42. package/docs/design-system/components/input.md +48 -0
  43. package/docs/design-system/components/kbd.md +44 -0
  44. package/docs/design-system/components/label.md +48 -0
  45. package/docs/design-system/components/menu.md +59 -0
  46. package/docs/design-system/components/menubar.md +45 -0
  47. package/docs/design-system/components/modal.md +98 -0
  48. package/docs/design-system/components/native-select.md +52 -0
  49. package/docs/design-system/components/navigation-menu.md +48 -0
  50. package/docs/design-system/components/onboarding-step-list-item.md +80 -0
  51. package/docs/design-system/components/page-header.md +84 -0
  52. package/docs/design-system/components/pagination.md +49 -0
  53. package/docs/design-system/components/popover.md +58 -0
  54. package/docs/design-system/components/progress-slider.md +48 -0
  55. package/docs/design-system/components/progress.md +75 -0
  56. package/docs/design-system/components/radio-card.md +49 -0
  57. package/docs/design-system/components/radio-group.md +55 -0
  58. package/docs/design-system/components/resizable.md +42 -0
  59. package/docs/design-system/components/scroll-area.md +45 -0
  60. package/docs/design-system/components/select.md +50 -0
  61. package/docs/design-system/components/sheet.md +65 -0
  62. package/docs/design-system/components/sidebar.md +68 -0
  63. package/docs/design-system/components/skeleton.md +73 -0
  64. package/docs/design-system/components/slideout.md +63 -0
  65. package/docs/design-system/components/slider.md +61 -0
  66. package/docs/design-system/components/social-button.md +47 -0
  67. package/docs/design-system/components/spinner.md +61 -0
  68. package/docs/design-system/components/steps.md +63 -0
  69. package/docs/design-system/components/table.md +397 -0
  70. package/docs/design-system/components/tabs.md +52 -0
  71. package/docs/design-system/components/tag.md +78 -0
  72. package/docs/design-system/components/textarea.md +48 -0
  73. package/docs/design-system/components/timeline.md +81 -0
  74. package/docs/design-system/components/toast.md +56 -0
  75. package/docs/design-system/components/toggle.md +79 -0
  76. package/docs/design-system/components/toolbar.md +85 -0
  77. package/docs/design-system/components/tooltip.md +90 -0
  78. package/docs/design-system/components/typography.md +18 -0
  79. package/docs/design-system/design-system-test-missing-items.md +368 -0
  80. package/docs/design-system/icons.md +69 -0
  81. package/docs/design-system/registry-and-cli.md +41 -0
  82. package/docs/design-system/tabs.md +53 -0
  83. package/docs/design-system/token-governance.md +38 -0
  84. package/package.json +83 -65
  85. package/registry/components/alert-dialog.tsx +297 -0
  86. package/registry/components/aspect-ratio.tsx +30 -0
  87. package/registry/components/carousel.tsx +234 -0
  88. package/registry/components/chart.tsx +170 -0
  89. package/registry/components/collapsible.tsx +69 -0
  90. package/registry/components/command.tsx +174 -0
  91. package/registry/components/context-menu.tsx +236 -0
  92. package/registry/components/date-picker.tsx +1 -1
  93. package/registry/components/expanded/PageHeader.tsx +1 -1
  94. package/registry/components/expanded/breadcrumbs.css +139 -139
  95. package/registry/components/expanded/catalogComponentsShowcase.css +83 -83
  96. package/registry/components/expanded/steps.css +274 -274
  97. package/registry/components/expanded/timeline.css +264 -264
  98. package/registry/components/field.tsx +230 -0
  99. package/registry/components/hover-card.tsx +48 -0
  100. package/registry/components/input-group.tsx +130 -0
  101. package/registry/components/input.tsx +2 -2
  102. package/registry/components/kbd.tsx +44 -0
  103. package/registry/components/label.tsx +78 -0
  104. package/registry/components/menu.tsx +3 -1
  105. package/registry/components/menubar.tsx +226 -0
  106. package/registry/components/modal.tsx +109 -76
  107. package/registry/components/native-select.tsx +205 -0
  108. package/registry/components/navigation-menu.tsx +171 -0
  109. package/registry/components/radio-group.tsx +1 -1
  110. package/registry/components/resizable.tsx +74 -0
  111. package/registry/components/scroll-area.tsx +67 -0
  112. package/registry/components/select.tsx +2 -4
  113. package/registry/components/sheet.tsx +305 -0
  114. package/registry/components/sidebar.tsx +352 -0
  115. package/registry/components/social-button.tsx +74 -10
  116. package/registry/components/{expanded/tabs.css → tabs.css} +127 -106
  117. package/registry/components/tabs.tsx +242 -0
  118. package/registry/components/textarea.tsx +1 -1
  119. package/registry/components/toast.tsx +131 -0
  120. package/registry/examples/alert-dialog-demo.tsx +42 -0
  121. package/registry/examples/aspect-ratio-demo.tsx +11 -0
  122. package/registry/examples/carousel-demo.tsx +25 -0
  123. package/registry/examples/chart-demo.tsx +33 -0
  124. package/registry/examples/collapsible-demo.tsx +16 -0
  125. package/registry/examples/command-demo.tsx +42 -0
  126. package/registry/examples/context-menu-demo.tsx +29 -0
  127. package/registry/examples/expanded/tabs-demo.tsx +1 -1
  128. package/registry/examples/field-demo.tsx +51 -0
  129. package/registry/examples/hover-card-demo.tsx +23 -0
  130. package/registry/examples/input-group-demo.tsx +16 -0
  131. package/registry/examples/kbd-demo.tsx +11 -0
  132. package/registry/examples/label-demo.tsx +20 -0
  133. package/registry/examples/menubar-demo.tsx +34 -0
  134. package/registry/examples/native-select-demo.tsx +16 -0
  135. package/registry/examples/navigation-menu-demo.tsx +29 -0
  136. package/registry/examples/resizable-demo.tsx +22 -0
  137. package/registry/examples/scroll-area-demo.tsx +15 -0
  138. package/registry/examples/sheet-demo.tsx +47 -0
  139. package/registry/examples/sidebar-demo.tsx +55 -0
  140. package/registry/examples/tabs-demo.tsx +13 -0
  141. package/registry/examples/toast-demo.tsx +35 -0
  142. package/registry/index.json +655 -11
  143. package/registry/styles/globals.css +4733 -4690
  144. package/registry.json +1612 -0
  145. package/schema/config.schema.json +48 -0
  146. package/schema/registry.schema.json +85 -0
  147. package/schema/tokens.schema.json +63 -0
  148. package/src/cli/index.js +312 -18
  149. package/tokens/banhaten.tokens.json +1 -1
  150. package/registry/assets/avatars/avatar-02.jpg +0 -0
  151. package/registry/assets/avatars/avatar-03.jpg +0 -0
  152. package/registry/assets/avatars/avatar-04.jpg +0 -0
  153. package/registry/assets/avatars/avatar-05.jpg +0 -0
  154. package/registry/assets/avatars/avatar-06.jpg +0 -0
  155. package/registry/assets/avatars/avatar-07.jpg +0 -0
  156. package/registry/assets/avatars/avatar-08.jpg +0 -0
  157. package/registry/assets/avatars/avatar-09.jpg +0 -0
  158. package/registry/assets/avatars/avatar-10.jpg +0 -0
  159. package/registry/assets/avatars/avatar-11.jpg +0 -0
  160. package/registry/assets/avatars/avatar-12.jpg +0 -0
  161. package/registry/assets/avatars/avatar-13.jpg +0 -0
  162. package/registry/assets/avatars/avatar-14.jpg +0 -0
  163. package/registry/assets/avatars/avatar-15.jpg +0 -0
  164. package/registry/assets/avatars/avatar-16.jpg +0 -0
  165. package/registry/assets/avatars/avatar-17.jpg +0 -0
  166. package/registry/assets/avatars/avatar-18.jpg +0 -0
  167. package/registry/assets/avatars/avatar-19.jpg +0 -0
  168. package/registry/assets/avatars/avatar-20.jpg +0 -0
  169. package/registry/assets/avatars/avatar-21.jpg +0 -0
  170. package/registry/assets/avatars/avatar-22.jpg +0 -0
  171. package/registry/assets/avatars/avatar-23.jpg +0 -0
  172. package/registry/assets/avatars/avatar-24.jpg +0 -0
  173. package/registry/assets/avatars/avatar-25.jpg +0 -0
  174. package/registry/assets/avatars/avatar-26.jpg +0 -0
  175. package/registry/assets/avatars/avatar-27.jpg +0 -0
  176. package/registry/assets/avatars/avatar-28.jpg +0 -0
  177. package/registry/assets/avatars/avatar-29.jpg +0 -0
  178. package/registry/assets/avatars/avatar-30.jpg +0 -0
  179. package/registry/assets/avatars/avatar-31.jpg +0 -0
  180. package/registry/assets/avatars/avatar-32.jpg +0 -0
  181. package/registry/assets/avatars/avatar-33.jpg +0 -0
  182. package/registry/assets/avatars/avatar-34.jpg +0 -0
  183. package/registry/assets/avatars/avatar-35.jpg +0 -0
  184. package/registry/assets/image-assets.json +0 -744
  185. package/registry/assets/images/art-02.jpg +0 -0
  186. package/registry/assets/images/art-03.jpg +0 -0
  187. package/registry/assets/images/art-04.jpg +0 -0
  188. package/registry/assets/images/art-05.jpg +0 -0
  189. package/registry/assets/images/art-06.jpg +0 -0
  190. package/registry/assets/images/art-07.jpg +0 -0
  191. package/registry/assets/images/art-08.jpg +0 -0
  192. package/registry/assets/images/art-09.jpg +0 -0
  193. package/registry/assets/images/art-10.jpg +0 -0
  194. package/registry/assets/images/art-11.jpg +0 -0
  195. package/registry/assets/images/art-12.jpg +0 -0
  196. package/registry/assets/images/art-13.jpg +0 -0
  197. package/registry/assets/images/art-14.jpg +0 -0
  198. package/registry/assets/images/art-15.jpg +0 -0
  199. package/registry/assets/images/art-16.jpg +0 -0
  200. package/registry/assets/images/art-17.jpg +0 -0
  201. package/registry/assets/images/art-18.jpg +0 -0
  202. package/registry/assets/images/art-19.jpg +0 -0
  203. package/registry/assets/images/art-20.jpg +0 -0
  204. package/registry/assets/images/art-21.jpg +0 -0
  205. package/registry/assets/images/art-22.jpg +0 -0
  206. package/registry/assets/images/art-23.jpg +0 -0
  207. package/registry/assets/images/art-24.jpg +0 -0
  208. package/registry/assets/images/art-25.jpg +0 -0
  209. package/registry/assets/images/art-26.jpg +0 -0
  210. package/registry/assets/images/art-27.jpg +0 -0
  211. package/registry/assets/images/nature-01.jpg +0 -0
  212. package/registry/assets/images/nature-02.jpg +0 -0
  213. package/registry/assets/images/nature-03.jpg +0 -0
  214. package/registry/assets/images/nature-04.jpg +0 -0
  215. package/registry/assets/images/nature-05.jpg +0 -0
  216. package/registry/assets/images/nature-06.jpg +0 -0
  217. package/registry/assets/images/nature-07.jpg +0 -0
  218. package/registry/assets/images/nature-08.jpg +0 -0
  219. package/registry/assets/images/nature-09.jpg +0 -0
  220. package/registry/assets/images/nature-10.jpg +0 -0
  221. package/registry/assets/images/nature-11.jpg +0 -0
  222. package/registry/assets/images/nature-12.jpg +0 -0
  223. package/registry/assets/images/nature-13.jpg +0 -0
  224. package/registry/assets/images/nature-14.jpg +0 -0
  225. package/registry/assets/images/nature-15.jpg +0 -0
  226. package/registry/assets/images/nature-16.jpg +0 -0
  227. package/registry/assets/images/nature-17.jpg +0 -0
  228. package/registry/assets/images/nature-18.jpg +0 -0
  229. package/registry/assets/images/nature-19.jpg +0 -0
  230. package/registry/assets/images/nature-20.jpg +0 -0
  231. package/registry/components/expanded/Tabs.tsx +0 -86
@@ -0,0 +1,230 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Slot } from "@radix-ui/react-slot"
5
+
6
+ import { Label, type LabelProps } from "@/components/ui/label"
7
+ import { cn } from "@/lib/utils"
8
+
9
+ type FieldOrientation = "vertical" | "horizontal" | "responsive"
10
+
11
+ type FieldContextValue = {
12
+ controlId: string
13
+ descriptionId: string
14
+ disabled?: boolean
15
+ errorId: string
16
+ invalid?: boolean
17
+ }
18
+
19
+ type FieldProps = React.ComponentProps<"div"> & {
20
+ controlId?: string
21
+ disabled?: boolean
22
+ invalid?: boolean
23
+ orientation?: FieldOrientation
24
+ }
25
+
26
+ type FieldControlProps = React.ComponentProps<typeof Slot> & {
27
+ "aria-describedby"?: string
28
+ }
29
+
30
+ type FieldLabelProps = LabelProps
31
+ type FieldDescriptionProps = React.ComponentProps<"p">
32
+ type FieldErrorProps = React.ComponentProps<"p">
33
+ type FieldGroupProps = React.ComponentProps<"div">
34
+ type FieldSetProps = React.ComponentProps<"fieldset">
35
+ type FieldLegendProps = React.ComponentProps<"legend">
36
+ type FieldContentProps = React.ComponentProps<"div">
37
+
38
+ const FieldContext = React.createContext<FieldContextValue | null>(null)
39
+
40
+ function Field({
41
+ children,
42
+ className,
43
+ controlId,
44
+ disabled = false,
45
+ invalid = false,
46
+ orientation = "vertical",
47
+ ...props
48
+ }: FieldProps) {
49
+ const generatedId = React.useId()
50
+ const resolvedControlId = controlId ?? `${generatedId}-control`
51
+ const context = React.useMemo<FieldContextValue>(
52
+ () => ({
53
+ controlId: resolvedControlId,
54
+ descriptionId: `${generatedId}-description`,
55
+ disabled,
56
+ errorId: `${generatedId}-error`,
57
+ invalid,
58
+ }),
59
+ [disabled, generatedId, invalid, resolvedControlId]
60
+ )
61
+
62
+ return (
63
+ <FieldContext.Provider value={context}>
64
+ <div
65
+ data-disabled={disabled ? "true" : undefined}
66
+ data-invalid={invalid ? "true" : undefined}
67
+ data-orientation={orientation}
68
+ data-slot="field"
69
+ className={cn(
70
+ "grid min-w-0 gap-[var(--bh-space-xs-4)] text-start",
71
+ orientation === "horizontal" &&
72
+ "items-start gap-x-[var(--bh-space-3xl-16)] sm:grid-cols-[minmax(0,var(--bh-space-17xl-288))_minmax(0,1fr)]",
73
+ orientation === "responsive" &&
74
+ "gap-y-[var(--bh-space-md-8)] sm:grid-cols-[minmax(0,var(--bh-space-17xl-288))_minmax(0,1fr)] sm:items-start sm:gap-x-[var(--bh-space-3xl-16)]",
75
+ className
76
+ )}
77
+ {...props}
78
+ >
79
+ {children}
80
+ </div>
81
+ </FieldContext.Provider>
82
+ )
83
+ }
84
+
85
+ function FieldControl({ className, ...props }: FieldControlProps) {
86
+ const context = useFieldContext("FieldControl")
87
+ const describedBy =
88
+ props["aria-describedby"] ??
89
+ [context.descriptionId, context.invalid ? context.errorId : undefined]
90
+ .filter(Boolean)
91
+ .join(" ")
92
+
93
+ return (
94
+ <Slot
95
+ aria-describedby={describedBy || undefined}
96
+ aria-disabled={context.disabled || undefined}
97
+ aria-invalid={context.invalid || undefined}
98
+ data-slot="field-control"
99
+ id={props.id ?? context.controlId}
100
+ className={cn("min-w-0", className)}
101
+ {...props}
102
+ />
103
+ )
104
+ }
105
+
106
+ function FieldLabel({ htmlFor, ...props }: FieldLabelProps) {
107
+ const context = useFieldContext("FieldLabel")
108
+
109
+ return (
110
+ <Label
111
+ data-slot="field-label"
112
+ htmlFor={htmlFor ?? context.controlId}
113
+ {...props}
114
+ />
115
+ )
116
+ }
117
+
118
+ function FieldDescription({ className, id, ...props }: FieldDescriptionProps) {
119
+ const context = useFieldContext("FieldDescription")
120
+
121
+ return (
122
+ <p
123
+ data-slot="field-description"
124
+ dir="auto"
125
+ id={id ?? context.descriptionId}
126
+ className={cn(
127
+ "m-0 text-[length:var(--bh-text-body-xs-regular-font-size)] font-[var(--bh-text-body-xs-regular-font-weight)] leading-[var(--bh-text-body-xs-regular-line-height)] tracking-[var(--bh-text-body-xs-regular-letter-spacing)] text-[var(--bh-content-subtle)]",
128
+ className
129
+ )}
130
+ {...props}
131
+ />
132
+ )
133
+ }
134
+
135
+ function FieldError({ className, id, role = "alert", ...props }: FieldErrorProps) {
136
+ const context = useFieldContext("FieldError")
137
+
138
+ return (
139
+ <p
140
+ data-slot="field-error"
141
+ dir="auto"
142
+ id={id ?? context.errorId}
143
+ role={role}
144
+ className={cn(
145
+ "m-0 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-danger-default)]",
146
+ className
147
+ )}
148
+ {...props}
149
+ />
150
+ )
151
+ }
152
+
153
+ function FieldGroup({ className, ...props }: FieldGroupProps) {
154
+ return (
155
+ <div
156
+ data-slot="field-group"
157
+ className={cn("grid min-w-0 gap-[var(--bh-space-3xl-16)]", className)}
158
+ {...props}
159
+ />
160
+ )
161
+ }
162
+
163
+ function FieldSet({ className, ...props }: FieldSetProps) {
164
+ return (
165
+ <fieldset
166
+ data-slot="field-set"
167
+ className={cn(
168
+ "m-0 grid min-w-0 gap-[var(--bh-space-3xl-16)] border-0 p-0 [min-inline-size:0]",
169
+ className
170
+ )}
171
+ {...props}
172
+ />
173
+ )
174
+ }
175
+
176
+ function FieldLegend({ className, ...props }: FieldLegendProps) {
177
+ return (
178
+ <legend
179
+ data-slot="field-legend"
180
+ dir="auto"
181
+ className={cn(
182
+ "m-0 p-0 text-[length:var(--bh-text-body-md-semibold-font-size)] font-[var(--bh-text-body-md-semibold-font-weight)] leading-[var(--bh-text-body-md-semibold-line-height)] tracking-[var(--bh-text-body-md-semibold-letter-spacing)] text-[var(--bh-content-default)]",
183
+ className
184
+ )}
185
+ {...props}
186
+ />
187
+ )
188
+ }
189
+
190
+ function FieldContent({ className, ...props }: FieldContentProps) {
191
+ return (
192
+ <div
193
+ data-slot="field-content"
194
+ className={cn("grid min-w-0 gap-[var(--bh-space-xs-4)]", className)}
195
+ {...props}
196
+ />
197
+ )
198
+ }
199
+
200
+ function useFieldContext(component: string) {
201
+ const context = React.useContext(FieldContext)
202
+ if (!context) {
203
+ throw new Error(`${component} must be used within Field`)
204
+ }
205
+ return context
206
+ }
207
+
208
+ export {
209
+ Field,
210
+ FieldContent,
211
+ FieldControl,
212
+ FieldDescription,
213
+ FieldError,
214
+ FieldGroup,
215
+ FieldLabel,
216
+ FieldLegend,
217
+ FieldSet,
218
+ }
219
+ export type {
220
+ FieldContentProps,
221
+ FieldControlProps,
222
+ FieldDescriptionProps,
223
+ FieldErrorProps,
224
+ FieldGroupProps,
225
+ FieldLabelProps,
226
+ FieldLegendProps,
227
+ FieldOrientation,
228
+ FieldProps,
229
+ FieldSetProps,
230
+ }
@@ -0,0 +1,48 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ type HoverCardProps = React.ComponentProps<typeof HoverCardPrimitive.Root>
9
+ type HoverCardTriggerProps = React.ComponentPropsWithoutRef<
10
+ typeof HoverCardPrimitive.Trigger
11
+ >
12
+ type HoverCardContentProps = React.ComponentPropsWithoutRef<
13
+ typeof HoverCardPrimitive.Content
14
+ >
15
+
16
+ const HoverCard = HoverCardPrimitive.Root
17
+ const HoverCardTrigger = HoverCardPrimitive.Trigger
18
+ const HoverCardPortal = HoverCardPrimitive.Portal
19
+
20
+ // Radix sideOffset is numeric; this mirrors --bh-space-md-8.
21
+ const HOVER_CARD_SIDE_OFFSET_PX = 8
22
+
23
+ const HoverCardContent = React.forwardRef<
24
+ React.ElementRef<typeof HoverCardPrimitive.Content>,
25
+ HoverCardContentProps
26
+ >(function HoverCardContent(
27
+ { align = "center", className, sideOffset = HOVER_CARD_SIDE_OFFSET_PX, ...props },
28
+ ref
29
+ ) {
30
+ return (
31
+ <HoverCardPortal>
32
+ <HoverCardPrimitive.Content
33
+ align={align}
34
+ data-slot="hover-card-content"
35
+ ref={ref}
36
+ sideOffset={sideOffset}
37
+ className={cn(
38
+ "z-[var(--bh-z-popover)] w-[var(--bh-space-18xl-320)] rounded-[var(--bh-radius-2xl-12)] bg-[var(--bh-bg-raised)] p-[var(--bh-space-3xl-16)] text-start text-[var(--bh-content-default)] shadow-[var(--shadow-popover)] outline-none",
39
+ className
40
+ )}
41
+ {...props}
42
+ />
43
+ </HoverCardPortal>
44
+ )
45
+ })
46
+
47
+ export { HoverCard, HoverCardContent, HoverCardPortal, HoverCardTrigger }
48
+ export type { HoverCardContentProps, HoverCardProps, HoverCardTriggerProps }
@@ -0,0 +1,130 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Slot } from "@radix-ui/react-slot"
5
+ import { cva, type VariantProps } from "class-variance-authority"
6
+
7
+ import { Button, type ButtonProps } from "@/components/ui/button"
8
+ import { cn } from "@/lib/utils"
9
+
10
+ const inputGroupVariants = cva(
11
+ [
12
+ "group/input-group relative flex w-[var(--bh-input-width)] max-w-full items-stretch overflow-hidden rounded-[var(--bh-input-radius)]",
13
+ "[--bh-input-border:var(--bh-border-input)] [--shadow-input:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-input-border,var(--bh-border-input)),var(--shadow-component-default)]",
14
+ "bg-[var(--bh-interactive-input-default)] shadow-[var(--shadow-input)] transition-[background-color,box-shadow]",
15
+ "focus-within:shadow-[var(--shadow-input-focus-ring)] data-[disabled=true]:bg-[var(--bh-interactive-input-disabled)] data-[disabled=true]:shadow-[inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-border-disabled)]",
16
+ ],
17
+ {
18
+ variants: {
19
+ size: {
20
+ md: "min-h-[var(--bh-input-md-height)]",
21
+ lg: "min-h-[var(--bh-input-lg-height)]",
22
+ },
23
+ state: {
24
+ default: "",
25
+ error:
26
+ "[--bh-input-border:var(--bh-border-danger-strong)] focus-within:shadow-[var(--shadow-input)]",
27
+ },
28
+ variant: {
29
+ default: "",
30
+ soft: "bg-[var(--bh-interactive-input-soft)] shadow-none",
31
+ },
32
+ },
33
+ defaultVariants: {
34
+ size: "lg",
35
+ state: "default",
36
+ variant: "default",
37
+ },
38
+ }
39
+ )
40
+
41
+ type InputGroupProps = React.ComponentProps<"div"> &
42
+ VariantProps<typeof inputGroupVariants> & {
43
+ disabled?: boolean
44
+ }
45
+ type InputGroupInputProps = React.ComponentProps<"input">
46
+ type InputGroupAddonProps = React.ComponentProps<"span"> & {
47
+ asChild?: boolean
48
+ }
49
+ type InputGroupButtonProps = ButtonProps
50
+
51
+ function InputGroup({
52
+ className,
53
+ disabled = false,
54
+ size,
55
+ state,
56
+ variant,
57
+ ...props
58
+ }: InputGroupProps) {
59
+ return (
60
+ <div
61
+ data-disabled={disabled ? "true" : undefined}
62
+ data-slot="input-group"
63
+ className={cn(inputGroupVariants({ size, state, variant, className }))}
64
+ {...props}
65
+ />
66
+ )
67
+ }
68
+
69
+ const InputGroupInput = React.forwardRef<HTMLInputElement, InputGroupInputProps>(
70
+ function InputGroupInput({ className, ...props }, ref) {
71
+ return (
72
+ <input
73
+ data-slot="input-group-input"
74
+ ref={ref}
75
+ className={cn(
76
+ "min-w-0 flex-1 border-0 bg-transparent px-[var(--bh-input-lg-padding-x)] py-[var(--bh-space-sm-6)] text-start text-[length:var(--bh-text-body-md-regular-font-size)] font-[var(--bh-text-body-md-regular-font-weight)] leading-[var(--bh-text-body-md-regular-line-height)] tracking-[var(--bh-text-body-md-regular-letter-spacing)] text-[var(--bh-content-default)] outline-none placeholder:text-[var(--bh-content-muted)] disabled:cursor-not-allowed disabled:text-[var(--bh-content-disabled)]",
77
+ className
78
+ )}
79
+ {...props}
80
+ />
81
+ )
82
+ }
83
+ )
84
+
85
+ const InputGroupAddon = React.forwardRef<HTMLSpanElement, InputGroupAddonProps>(
86
+ function InputGroupAddon({ asChild = false, className, ...props }, ref) {
87
+ const Comp = asChild ? Slot : "span"
88
+
89
+ return (
90
+ <Comp
91
+ data-slot="input-group-addon"
92
+ ref={ref}
93
+ className={cn(
94
+ "inline-flex shrink-0 items-center gap-[var(--bh-space-xs-4)] border-e border-[var(--bh-border-input)] px-[var(--bh-space-xl-12)] text-[length:var(--bh-text-body-sm-medium-font-size)] font-[var(--bh-text-body-sm-medium-font-weight)] leading-[var(--bh-text-body-sm-medium-line-height)] tracking-[var(--bh-text-body-sm-medium-letter-spacing)] text-[var(--bh-content-subtle)]",
95
+ className
96
+ )}
97
+ {...props}
98
+ />
99
+ )
100
+ }
101
+ )
102
+
103
+ const InputGroupButton = React.forwardRef<HTMLButtonElement, InputGroupButtonProps>(
104
+ function InputGroupButton({ className, size = "sm", variant = "ghost", ...props }, ref) {
105
+ return (
106
+ <Button
107
+ data-slot="input-group-button"
108
+ ref={ref}
109
+ size={size}
110
+ variant={variant}
111
+ className={cn("m-[var(--bh-space-xs-4)] shrink-0", className)}
112
+ {...props}
113
+ />
114
+ )
115
+ }
116
+ )
117
+
118
+ export {
119
+ InputGroup,
120
+ InputGroupAddon,
121
+ InputGroupButton,
122
+ InputGroupInput,
123
+ inputGroupVariants,
124
+ }
125
+ export type {
126
+ InputGroupAddonProps,
127
+ InputGroupButtonProps,
128
+ InputGroupInputProps,
129
+ InputGroupProps,
130
+ }
@@ -94,8 +94,8 @@ const inputSurface = cva(
94
94
  "[--bh-input-border:var(--bh-border-input)] [--shadow-input:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-input-border,var(--bh-border-input)),var(--shadow-component-default)]",
95
95
  "[--shadow-input-surface:var(--shadow-component-default)]",
96
96
  "[--shadow-input-overlay:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-input-border,var(--bh-border-input))]",
97
- "[--shadow-input-focus-surface:0px_0px_0px_var(--bh-focus-ring-width)_color-mix(in_srgb,var(--bh-border-focus)_30%,transparent)]",
98
- "[--shadow-input-focus-overlay:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-border-focus)]",
97
+ "[--shadow-input-focus-surface:var(--shadow-input-focus-ring)]",
98
+ "[--shadow-input-focus-overlay:none]",
99
99
  "[--shadow-input-disabled-overlay:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-border-disabled)]",
100
100
  "after:pointer-events-none after:absolute after:inset-0 after:z-[var(--bh-z-raised)] after:rounded-[inherit] after:[box-shadow:var(--shadow-input-overlay)] after:content-['']",
101
101
  "focus-within:shadow-[var(--shadow-input-focus-surface)] focus-within:after:[box-shadow:var(--shadow-input-focus-overlay)]",
@@ -0,0 +1,44 @@
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ const kbdVariants = cva(
7
+ [
8
+ "inline-flex min-w-[var(--bh-space-5xl-24)] items-center justify-center whitespace-nowrap rounded-[var(--bh-radius-md-6)]",
9
+ "border border-[var(--bh-border-default)] bg-[var(--bh-interactive-soft-default)] text-[var(--bh-content-subtle)]",
10
+ "font-[var(--bh-font-family)] text-[length:var(--bh-text-body-xs-medium-font-size)] font-[var(--bh-text-body-xs-medium-font-weight)]",
11
+ "leading-[var(--bh-text-body-xs-medium-line-height)] tracking-[var(--bh-text-body-xs-medium-letter-spacing)] shadow-[var(--shadow-button-soft)]",
12
+ ],
13
+ {
14
+ variants: {
15
+ size: {
16
+ sm: "h-[var(--bh-space-5xl-24)] gap-[var(--bh-space-xxs-2)] px-[var(--bh-space-sm-6)]",
17
+ md: "h-[var(--bh-space-6xl-32)] gap-[var(--bh-space-xs-4)] px-[var(--bh-space-md-8)]",
18
+ },
19
+ },
20
+ defaultVariants: {
21
+ size: "sm",
22
+ },
23
+ }
24
+ )
25
+
26
+ type KbdProps = React.ComponentProps<"kbd"> &
27
+ VariantProps<typeof kbdVariants>
28
+
29
+ const Kbd = React.forwardRef<HTMLElement, KbdProps>(function Kbd(
30
+ { className, size, ...props },
31
+ ref
32
+ ) {
33
+ return (
34
+ <kbd
35
+ data-slot="kbd"
36
+ ref={ref}
37
+ className={cn(kbdVariants({ size, className }))}
38
+ {...props}
39
+ />
40
+ )
41
+ })
42
+
43
+ export { Kbd, kbdVariants }
44
+ export type { KbdProps }
@@ -0,0 +1,78 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as LabelPrimitive from "@radix-ui/react-label"
5
+ import { cva, type VariantProps } from "class-variance-authority"
6
+
7
+ import { cn } from "@/lib/utils"
8
+
9
+ const labelVariants = cva(
10
+ [
11
+ "inline-flex max-w-full items-center gap-[var(--bh-space-xs-4)] text-start",
12
+ "text-[length:var(--bh-text-body-sm-medium-font-size)] font-[var(--bh-text-body-sm-medium-font-weight)]",
13
+ "leading-[var(--bh-text-body-sm-medium-line-height)] tracking-[var(--bh-text-body-sm-medium-letter-spacing)]",
14
+ "text-[var(--bh-content-default)]",
15
+ "has-[[aria-disabled=true]]:text-[var(--bh-content-disabled)]",
16
+ "peer-disabled:cursor-not-allowed peer-disabled:text-[var(--bh-content-disabled)]",
17
+ ],
18
+ {
19
+ variants: {
20
+ tone: {
21
+ default: "",
22
+ subtle: "text-[var(--bh-content-subtle)]",
23
+ danger: "text-[var(--bh-content-danger-default)]",
24
+ },
25
+ },
26
+ defaultVariants: {
27
+ tone: "default",
28
+ },
29
+ }
30
+ )
31
+
32
+ type LabelProps = React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
33
+ VariantProps<typeof labelVariants> & {
34
+ optionalText?: React.ReactNode
35
+ required?: boolean
36
+ }
37
+
38
+ const Label = React.forwardRef<
39
+ React.ElementRef<typeof LabelPrimitive.Root>,
40
+ LabelProps
41
+ >(function Label(
42
+ { children, className, optionalText, required = false, tone, ...props },
43
+ ref
44
+ ) {
45
+ return (
46
+ <LabelPrimitive.Root
47
+ data-slot="label"
48
+ ref={ref}
49
+ className={cn(labelVariants({ tone, className }))}
50
+ {...props}
51
+ >
52
+ <span data-slot="label-text" dir="auto" className="min-w-0">
53
+ {children}
54
+ </span>
55
+ {required ? (
56
+ <span
57
+ aria-hidden="true"
58
+ data-slot="label-required"
59
+ className="shrink-0 text-[var(--bh-content-danger-default)]"
60
+ >
61
+ *
62
+ </span>
63
+ ) : null}
64
+ {optionalText ? (
65
+ <span
66
+ data-slot="label-optional"
67
+ dir="auto"
68
+ className="min-w-0 text-[length:var(--bh-text-body-xs-regular-font-size)] font-[var(--bh-text-body-xs-regular-font-weight)] leading-[var(--bh-text-body-xs-regular-line-height)] tracking-[var(--bh-text-body-xs-regular-letter-spacing)] text-[var(--bh-content-subtle)]"
69
+ >
70
+ {optionalText}
71
+ </span>
72
+ ) : null}
73
+ </LabelPrimitive.Root>
74
+ )
75
+ })
76
+
77
+ export { Label, labelVariants }
78
+ export type { LabelProps }
@@ -32,6 +32,8 @@ type MenuResizeObserverConstructor = new (
32
32
 
33
33
  // Default Radix collisionPadding mirrors --bh-space-md-8.
34
34
  const MENU_COLLISION_PADDING_PX = 8
35
+ // Radix sideOffset is numeric; this mirrors --bh-space-xs-4.
36
+ const MENU_SUBCONTENT_SIDE_OFFSET_PX = 4
35
37
  const zeroMenuCollisionShift: MenuCollisionShift = { x: 0, y: 0 }
36
38
  const useSafeLayoutEffect =
37
39
  typeof window === "undefined" ? React.useEffect : React.useLayoutEffect
@@ -327,7 +329,7 @@ const MenuSubContent = React.forwardRef<HTMLDivElement, MenuSubContentProps>(fun
327
329
  children,
328
330
  className,
329
331
  collisionPadding = MENU_COLLISION_PADDING_PX,
330
- sideOffset = 4,
332
+ sideOffset = MENU_SUBCONTENT_SIDE_OFFSET_PX,
331
333
  width,
332
334
  asChild = false,
333
335
  role = "menu",