banhaten 0.1.1 → 0.1.2

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 (37) hide show
  1. package/README.md +20 -8
  2. package/package.json +8 -2
  3. package/registry/components/autocomplete.tsx +637 -0
  4. package/registry/components/avatar.tsx +258 -22
  5. package/registry/components/badge.tsx +97 -35
  6. package/registry/components/date-picker-state.ts +253 -0
  7. package/registry/components/date-picker.tsx +115 -158
  8. package/registry/components/expanded/EmptyState.tsx +155 -0
  9. package/registry/components/expanded/emptyState.css +111 -0
  10. package/registry/components/expanded/slideout.css +1 -0
  11. package/registry/components/expanded/table.css +1 -0
  12. package/registry/components/input-otp.tsx +574 -0
  13. package/registry/components/input.tsx +21 -11
  14. package/registry/components/menu.tsx +371 -8
  15. package/registry/components/popover.tsx +840 -0
  16. package/registry/components/select.tsx +4 -0
  17. package/registry/components/skeleton.css +57 -0
  18. package/registry/components/skeleton.tsx +482 -0
  19. package/registry/components/spinner.tsx +79 -11
  20. package/registry/components/textarea.tsx +1 -1
  21. package/registry/components/tooltip.tsx +4 -0
  22. package/registry/examples/autocomplete-demo.tsx +109 -0
  23. package/registry/examples/avatar-demo.tsx +102 -47
  24. package/registry/examples/badge-demo.tsx +16 -0
  25. package/registry/examples/expanded/command-bar-demo.tsx +236 -0
  26. package/registry/examples/expanded/empty-state-demo.tsx +39 -0
  27. package/registry/examples/input-demo.tsx +1 -1
  28. package/registry/examples/input-otp-demo.tsx +72 -0
  29. package/registry/examples/menu-demo.tsx +101 -88
  30. package/registry/examples/popover-demo.tsx +546 -0
  31. package/registry/examples/select-demo.tsx +1 -1
  32. package/registry/examples/skeleton-demo.tsx +56 -0
  33. package/registry/examples/spinner-demo.tsx +23 -1
  34. package/registry/examples/textarea-demo.tsx +1 -1
  35. package/registry/index.json +240 -8
  36. package/registry/styles/globals.css +88 -0
  37. package/src/cli/index.js +997 -62
@@ -0,0 +1,574 @@
1
+ import * as React from "react"
2
+ import { cva } from "class-variance-authority"
3
+
4
+ import { cn } from "@/lib/utils"
5
+ import {
6
+ inputControl,
7
+ inputRoot,
8
+ inputSurface,
9
+ type InputSize,
10
+ type InputVariant,
11
+ type InputVisualState,
12
+ } from "./input"
13
+
14
+ type InputOTPState = InputVisualState
15
+
16
+ type InputOTPNativeProps = Omit<
17
+ React.ComponentProps<"input">,
18
+ | "children"
19
+ | "className"
20
+ | "defaultValue"
21
+ | "dir"
22
+ | "disabled"
23
+ | "maxLength"
24
+ | "name"
25
+ | "onChange"
26
+ | "readOnly"
27
+ | "size"
28
+ | "type"
29
+ | "value"
30
+ >
31
+
32
+ type InputOTPProps = InputOTPNativeProps & {
33
+ className?: string
34
+ controlClassName?: string
35
+ defaultValue?: string
36
+ dir?: "ltr" | "rtl" | "auto"
37
+ disabled?: boolean
38
+ errorMessage?: React.ReactNode
39
+ groupClassName?: string
40
+ groupSize?: number
41
+ hasHelperText?: boolean
42
+ hasLabel?: boolean
43
+ isOptional?: boolean
44
+ isRequired?: boolean
45
+ label?: React.ReactNode
46
+ labelClassName?: string
47
+ length?: number
48
+ mask?: boolean
49
+ message?: React.ReactNode
50
+ name?: string
51
+ onComplete?: (value: string) => void
52
+ onValueChange?: (value: string) => void
53
+ optionalText?: React.ReactNode
54
+ readOnly?: boolean
55
+ separator?: React.ReactNode
56
+ size?: InputSize
57
+ slotClassName?: string
58
+ state?: InputOTPState
59
+ value?: string
60
+ variant?: InputVariant
61
+ }
62
+
63
+ const inputOtpGroup = cva(
64
+ "flex w-[var(--bh-input-otp-width)] max-w-full flex-wrap items-center gap-[var(--bh-input-otp-group-gap)]"
65
+ )
66
+
67
+ const inputOtpSlotGroup = cva(
68
+ "flex min-w-0 items-center gap-[var(--bh-input-otp-slot-gap)]"
69
+ )
70
+
71
+ const inputOtpSlot = cva("shrink-0", {
72
+ variants: {
73
+ size: {
74
+ md: "w-[var(--bh-input-otp-slot-md-width)]",
75
+ lg: "w-[var(--bh-input-otp-slot-lg-width)]",
76
+ },
77
+ },
78
+ defaultVariants: {
79
+ size: "lg",
80
+ },
81
+ })
82
+
83
+ const inputOtpControl = cva(
84
+ [
85
+ "h-full w-full flex-none min-w-[var(--bh-space-none)] p-[var(--bh-space-none)] text-center",
86
+ "font-[var(--bh-text-body-md-medium-font-weight)] caret-[var(--bh-content-default)]",
87
+ "selection:bg-[var(--bh-bg-brand-soft)]",
88
+ ]
89
+ )
90
+
91
+ const inputOtpSeparator = cva(
92
+ "flex h-[var(--bh-input-lg-height)] w-[var(--bh-input-otp-separator-width)] shrink-0 items-center justify-center text-[var(--bh-content-muted)]"
93
+ )
94
+
95
+ const inputOtpSeparatorMark = cva(
96
+ "block h-[var(--bh-input-otp-separator-height)] w-full rounded-[var(--bh-radius-full)] bg-[var(--bh-border-default)]"
97
+ )
98
+
99
+ const InputOTP = React.forwardRef<HTMLInputElement, InputOTPProps>(
100
+ (
101
+ {
102
+ "aria-describedby": ariaDescribedBy,
103
+ "aria-label": ariaLabel,
104
+ autoComplete = "one-time-code",
105
+ className,
106
+ controlClassName,
107
+ defaultValue,
108
+ dir,
109
+ disabled,
110
+ errorMessage,
111
+ groupClassName,
112
+ groupSize = 3,
113
+ hasHelperText,
114
+ hasLabel,
115
+ id,
116
+ inputMode = "numeric",
117
+ isOptional = false,
118
+ isRequired = false,
119
+ label,
120
+ labelClassName,
121
+ length = 6,
122
+ mask = false,
123
+ message,
124
+ name,
125
+ onBlur,
126
+ onComplete,
127
+ onFocus,
128
+ onKeyDown,
129
+ onPaste,
130
+ onValueChange,
131
+ optionalText,
132
+ pattern = "[0-9]*",
133
+ placeholder,
134
+ readOnly,
135
+ required,
136
+ separator = <InputOTPSeparator />,
137
+ size = "lg",
138
+ slotClassName,
139
+ state = "default",
140
+ value,
141
+ variant = "default",
142
+ ...slotProps
143
+ },
144
+ ref
145
+ ) => {
146
+ const generatedId = React.useId()
147
+ const inputId = id || generatedId
148
+ const labelId = `${inputId}-label`
149
+ const helperId = `${inputId}-helper`
150
+ const slotCount = normalizeCount(length, 6)
151
+ const normalizedGroupSize = normalizeCount(groupSize, slotCount)
152
+ const isControlled = value !== undefined
153
+ const [internalValue, setInternalValue] = React.useState(
154
+ normalizeValue(defaultValue, slotCount)
155
+ )
156
+ const slotRefs = React.useRef<Array<HTMLInputElement | null>>([])
157
+ const currentValue = normalizeValue(
158
+ isControlled ? value : internalValue,
159
+ slotCount
160
+ )
161
+ const valueChars = valueToSlots(currentValue, slotCount)
162
+ const visualState = disabled ? "disabled" : state
163
+ const isDisabled = visualState === "disabled"
164
+ const isInvalid = visualState === "error" && !isDisabled
165
+ const isComplete = valueChars.every(Boolean)
166
+ const shouldRenderLabel = hasLabel ?? hasRenderableContent(label)
167
+ const helperText = isInvalid ? errorMessage : message
168
+ const shouldRenderHelperText =
169
+ hasHelperText ?? hasRenderableContent(helperText)
170
+ const describedBy = [ariaDescribedBy, shouldRenderHelperText ? helperId : ""]
171
+ .filter(Boolean)
172
+ .join(" ") || undefined
173
+ const groups = groupSlots(slotCount, normalizedGroupSize)
174
+ const slotPlaceholder = getFirstCharacter(placeholder)
175
+
176
+ function commitSlots(nextSlots: string[], focusIndex?: number) {
177
+ const nextValue = slotsToValue(nextSlots, slotCount)
178
+
179
+ if (!isControlled) {
180
+ setInternalValue(nextValue)
181
+ }
182
+
183
+ onValueChange?.(nextValue)
184
+
185
+ if (nextValue.length === slotCount) {
186
+ onComplete?.(nextValue)
187
+ }
188
+
189
+ if (focusIndex !== undefined) {
190
+ focusSlot(focusIndex)
191
+ }
192
+ }
193
+
194
+ function focusSlot(index: number) {
195
+ const nextIndex = Math.max(0, Math.min(slotCount - 1, index))
196
+
197
+ window.requestAnimationFrame(() => {
198
+ const slot = slotRefs.current[nextIndex]
199
+ slot?.focus()
200
+ slot?.select()
201
+ })
202
+ }
203
+
204
+ function setSlotRef(index: number, node: HTMLInputElement | null) {
205
+ slotRefs.current[index] = node
206
+
207
+ if (index === 0) {
208
+ assignRef(ref, node)
209
+ }
210
+ }
211
+
212
+ function updateSlot(index: number, nextInputValue: string) {
213
+ if (isDisabled || readOnly) return
214
+
215
+ const nextCharacters = toCharacters(nextInputValue, slotCount - index)
216
+ const nextSlots = [...valueChars]
217
+
218
+ if (nextCharacters.length === 0) {
219
+ nextSlots[index] = ""
220
+ commitSlots(nextSlots, index)
221
+ return
222
+ }
223
+
224
+ for (let offset = 0; offset < nextCharacters.length; offset += 1) {
225
+ nextSlots[index + offset] = nextCharacters[offset]
226
+ }
227
+
228
+ commitSlots(nextSlots, index + nextCharacters.length)
229
+ }
230
+
231
+ function handleSlotKeyDown(
232
+ event: React.KeyboardEvent<HTMLInputElement>,
233
+ index: number
234
+ ) {
235
+ onKeyDown?.(event)
236
+ if (event.defaultPrevented) return
237
+
238
+ const previousKey = dir === "rtl" ? "ArrowRight" : "ArrowLeft"
239
+ const nextKey = dir === "rtl" ? "ArrowLeft" : "ArrowRight"
240
+
241
+ if (event.key === previousKey) {
242
+ event.preventDefault()
243
+ focusSlot(index - 1)
244
+ return
245
+ }
246
+
247
+ if (event.key === nextKey) {
248
+ event.preventDefault()
249
+ focusSlot(index + 1)
250
+ return
251
+ }
252
+
253
+ if (event.key === "Home") {
254
+ event.preventDefault()
255
+ focusSlot(0)
256
+ return
257
+ }
258
+
259
+ if (event.key === "End") {
260
+ event.preventDefault()
261
+ focusSlot(slotCount - 1)
262
+ return
263
+ }
264
+
265
+ if (event.key !== "Backspace" || readOnly || isDisabled) {
266
+ return
267
+ }
268
+
269
+ event.preventDefault()
270
+
271
+ if (valueChars[index]) {
272
+ const nextSlots = [...valueChars]
273
+ nextSlots[index] = ""
274
+ commitSlots(nextSlots, index)
275
+ return
276
+ }
277
+
278
+ if (index > 0) {
279
+ const nextSlots = [...valueChars]
280
+ nextSlots[index - 1] = ""
281
+ commitSlots(nextSlots, index - 1)
282
+ }
283
+ }
284
+
285
+ function handleSlotPaste(
286
+ event: React.ClipboardEvent<HTMLInputElement>,
287
+ index: number
288
+ ) {
289
+ onPaste?.(event)
290
+ if (event.defaultPrevented || readOnly || isDisabled) return
291
+
292
+ event.preventDefault()
293
+ updateSlot(index, event.clipboardData.getData("text"))
294
+ }
295
+
296
+ return (
297
+ <div
298
+ aria-label={shouldRenderLabel ? undefined : ariaLabel}
299
+ aria-labelledby={shouldRenderLabel ? labelId : undefined}
300
+ data-complete={isComplete ? "true" : undefined}
301
+ data-size={size}
302
+ data-slot="input-otp-root"
303
+ data-state={visualState}
304
+ data-variant={variant}
305
+ dir={dir}
306
+ role="group"
307
+ className={cn(
308
+ inputRoot(),
309
+ "[--bh-input-width:var(--bh-input-otp-width)]",
310
+ className
311
+ )}
312
+ >
313
+ {shouldRenderLabel ? (
314
+ <InputOTPLabel
315
+ className={labelClassName}
316
+ htmlFor={`${inputId}-0`}
317
+ id={labelId}
318
+ isOptional={isOptional}
319
+ isRequired={isRequired || Boolean(required)}
320
+ label={label}
321
+ optionalText={optionalText}
322
+ />
323
+ ) : null}
324
+
325
+ <div
326
+ data-slot="input-otp-body"
327
+ className={cn(
328
+ "grid",
329
+ shouldRenderLabel && "mt-[var(--bh-input-label-gap)]",
330
+ "gap-[var(--bh-input-helper-gap)]"
331
+ )}
332
+ >
333
+ <div data-slot="input-otp-group" className={cn(inputOtpGroup(), groupClassName)}>
334
+ {groups.map((group, groupIndex) => (
335
+ <React.Fragment key={`group-${group[0]}`}>
336
+ {groupIndex > 0 && separator ? separator : null}
337
+ <div data-slot="input-otp-slot-group" className={cn(inputOtpSlotGroup())}>
338
+ {group.map((slotIndex) => (
339
+ <span
340
+ aria-disabled={isDisabled || undefined}
341
+ data-filled={valueChars[slotIndex] ? "true" : undefined}
342
+ data-slot="input-otp-slot"
343
+ data-state={visualState}
344
+ key={slotIndex}
345
+ className={cn(
346
+ inputSurface({ size, state: visualState, variant }),
347
+ inputOtpSlot({ size }),
348
+ slotClassName
349
+ )}
350
+ onClick={() => focusSlot(slotIndex)}
351
+ >
352
+ <input
353
+ aria-describedby={describedBy}
354
+ aria-invalid={isInvalid || undefined}
355
+ aria-label={getSlotAriaLabel(slotIndex, slotCount)}
356
+ autoComplete={slotIndex === 0 ? autoComplete : "off"}
357
+ data-slot="input-otp-control"
358
+ data-state={visualState}
359
+ disabled={isDisabled}
360
+ id={`${inputId}-${slotIndex}`}
361
+ inputMode={inputMode}
362
+ maxLength={1}
363
+ pattern={pattern}
364
+ placeholder={slotPlaceholder}
365
+ readOnly={readOnly}
366
+ ref={(node) => setSlotRef(slotIndex, node)}
367
+ required={required}
368
+ type={mask ? "password" : "text"}
369
+ value={valueChars[slotIndex] || ""}
370
+ className={cn(inputControl(), inputOtpControl(), controlClassName)}
371
+ onBlur={onBlur}
372
+ onChange={(event) => updateSlot(slotIndex, event.currentTarget.value)}
373
+ onFocus={(event) => {
374
+ onFocus?.(event)
375
+ event.currentTarget.select()
376
+ }}
377
+ onKeyDown={(event) => handleSlotKeyDown(event, slotIndex)}
378
+ onPaste={(event) => handleSlotPaste(event, slotIndex)}
379
+ {...slotProps}
380
+ />
381
+ </span>
382
+ ))}
383
+ </div>
384
+ </React.Fragment>
385
+ ))}
386
+ </div>
387
+
388
+ {name ? (
389
+ <input
390
+ data-slot="input-otp-hidden-control"
391
+ disabled={isDisabled}
392
+ name={name}
393
+ type="hidden"
394
+ value={currentValue}
395
+ />
396
+ ) : null}
397
+
398
+ {shouldRenderHelperText ? (
399
+ <InputOTPHelperText
400
+ id={helperId}
401
+ invalid={isInvalid}
402
+ >
403
+ {helperText}
404
+ </InputOTPHelperText>
405
+ ) : null}
406
+ </div>
407
+ </div>
408
+ )
409
+ }
410
+ )
411
+ InputOTP.displayName = "InputOTP"
412
+
413
+ function InputOTPSeparator({ className }: { className?: string }) {
414
+ return (
415
+ <span
416
+ aria-hidden="true"
417
+ data-slot="input-otp-separator"
418
+ className={cn(inputOtpSeparator(), className)}
419
+ >
420
+ <span data-slot="input-otp-separator-mark" className={cn(inputOtpSeparatorMark())} />
421
+ </span>
422
+ )
423
+ }
424
+
425
+ function InputOTPLabel({
426
+ className,
427
+ htmlFor,
428
+ id,
429
+ isOptional,
430
+ isRequired,
431
+ label,
432
+ optionalText,
433
+ }: {
434
+ className?: string
435
+ htmlFor: string
436
+ id: string
437
+ isOptional: boolean
438
+ isRequired: boolean
439
+ label: React.ReactNode
440
+ optionalText: React.ReactNode
441
+ }) {
442
+ return (
443
+ <label
444
+ data-slot="input-otp-label"
445
+ htmlFor={htmlFor}
446
+ id={id}
447
+ className={cn(
448
+ "flex w-full items-center gap-[var(--bh-input-inline-gap)] text-start",
449
+ className
450
+ )}
451
+ >
452
+ <span
453
+ data-slot="input-otp-label-text"
454
+ dir="auto"
455
+ className="min-w-0 shrink-0 whitespace-nowrap 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-default)]"
456
+ >
457
+ {label}
458
+ </span>
459
+ {isRequired ? (
460
+ <span
461
+ aria-hidden="true"
462
+ data-slot="input-otp-label-required"
463
+ className="shrink-0 whitespace-nowrap 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)]"
464
+ >
465
+ *
466
+ </span>
467
+ ) : null}
468
+ {isOptional && hasRenderableContent(optionalText) ? (
469
+ <span
470
+ data-slot="input-otp-label-optional"
471
+ dir="auto"
472
+ className="min-w-0 shrink-0 whitespace-nowrap 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)]"
473
+ >
474
+ {optionalText}
475
+ </span>
476
+ ) : null}
477
+ </label>
478
+ )
479
+ }
480
+
481
+ function InputOTPHelperText({
482
+ children,
483
+ id,
484
+ invalid,
485
+ }: {
486
+ children: React.ReactNode
487
+ id: string
488
+ invalid: boolean
489
+ }) {
490
+ return (
491
+ <p
492
+ data-slot="input-otp-helper-text"
493
+ id={id}
494
+ className={cn(
495
+ "m-0 w-full text-start text-[length:var(--bh-text-body-xs-regular-font-size)]",
496
+ "font-[var(--bh-text-body-xs-regular-font-weight)]",
497
+ "leading-[var(--bh-text-body-xs-regular-line-height)]",
498
+ "tracking-[var(--bh-text-body-xs-regular-letter-spacing)]",
499
+ invalid ? "text-[var(--bh-content-danger-default)]" : "text-[var(--bh-content-subtle)]"
500
+ )}
501
+ >
502
+ <span data-slot="input-otp-helper-label" dir="auto" className="min-w-0">
503
+ {children}
504
+ </span>
505
+ </p>
506
+ )
507
+ }
508
+
509
+ function assignRef<T>(ref: React.ForwardedRef<T>, value: T | null) {
510
+ if (typeof ref === "function") {
511
+ ref(value)
512
+ } else if (ref) {
513
+ ref.current = value
514
+ }
515
+ }
516
+
517
+ function getFirstCharacter(value?: string) {
518
+ return value ? Array.from(value)[0] : undefined
519
+ }
520
+
521
+ function getSlotAriaLabel(index: number, total: number) {
522
+ return `One-time code character ${index + 1} of ${total}`
523
+ }
524
+
525
+ function groupSlots(slotCount: number, groupSize: number) {
526
+ const groups: number[][] = []
527
+
528
+ for (let index = 0; index < slotCount; index += groupSize) {
529
+ groups.push(
530
+ Array.from(
531
+ { length: Math.min(groupSize, slotCount - index) },
532
+ (_, offset) => index + offset
533
+ )
534
+ )
535
+ }
536
+
537
+ return groups
538
+ }
539
+
540
+ function hasRenderableContent(content: React.ReactNode) {
541
+ return (
542
+ content !== undefined &&
543
+ content !== null &&
544
+ content !== false &&
545
+ content !== ""
546
+ )
547
+ }
548
+
549
+ function normalizeCount(value: number | undefined, fallback: number) {
550
+ if (!Number.isFinite(value)) return fallback
551
+
552
+ return Math.max(1, Math.floor(value || fallback))
553
+ }
554
+
555
+ function normalizeValue(value: string | undefined, maxLength: number) {
556
+ return toCharacters(value || "", maxLength).join("")
557
+ }
558
+
559
+ function slotsToValue(slots: string[], maxLength: number) {
560
+ return slots.slice(0, maxLength).join("")
561
+ }
562
+
563
+ function toCharacters(value: string, maxLength: number) {
564
+ return Array.from(value.replace(/\s/g, "")).slice(0, maxLength)
565
+ }
566
+
567
+ function valueToSlots(value: string, slotCount: number) {
568
+ const characters = toCharacters(value, slotCount)
569
+
570
+ return Array.from({ length: slotCount }, (_, index) => characters[index] || "")
571
+ }
572
+
573
+ export { InputOTP, InputOTPSeparator }
574
+ export type { InputOTPProps, InputOTPState }
@@ -1,6 +1,7 @@
1
1
  import * as React from "react"
2
2
  import { cva } from "class-variance-authority"
3
3
  import {
4
+ AtSignIcon,
4
5
  ChevronDownIcon,
5
6
  InfoIcon,
6
7
  MinusIcon as LucideMinusIcon,
@@ -91,11 +92,13 @@ const inputSurface = cva(
91
92
  "group/input-surface relative flex w-full overflow-hidden rounded-[var(--bh-input-radius)]",
92
93
  "transition-[background-color,box-shadow]",
93
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)]",
94
- "[--shadow-input-overlay:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-input-border,var(--bh-border-input)),inset_0px_-1px_0px_0px_rgb(0_0_0_/_0.08)]",
95
+ "[--shadow-input-surface:var(--shadow-component-default)]",
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)]",
95
98
  "[--shadow-input-focus-overlay:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-border-focus)]",
96
99
  "[--shadow-input-disabled-overlay:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-border-disabled)]",
97
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-['']",
98
- "focus-within:shadow-[var(--shadow-input-focus-ring)] focus-within:after:[box-shadow:var(--shadow-input-focus-overlay)]",
101
+ "focus-within:shadow-[var(--shadow-input-focus-surface)] focus-within:after:[box-shadow:var(--shadow-input-focus-overlay)]",
99
102
  ],
100
103
  {
101
104
  variants: {
@@ -109,13 +112,13 @@ const inputSurface = cva(
109
112
  },
110
113
  state: {
111
114
  default:
112
- "shadow-[var(--shadow-input)]",
115
+ "shadow-[var(--shadow-input-surface)]",
113
116
  filled:
114
- "shadow-[var(--shadow-input)]",
117
+ "shadow-[var(--shadow-input-surface)]",
115
118
  error:
116
- "[--bh-input-border:var(--bh-border-danger-strong)] shadow-[var(--shadow-input)] focus-within:shadow-[var(--shadow-input)] focus-within:after:[box-shadow:var(--shadow-input-overlay)]",
119
+ "[--bh-input-border:var(--bh-border-danger-strong)] shadow-[var(--shadow-input-surface)] focus-within:shadow-[var(--shadow-input-surface)] focus-within:after:[box-shadow:var(--shadow-input-overlay)]",
117
120
  disabled:
118
- "[--bh-input-border:var(--bh-border-disabled)] bg-[var(--bh-interactive-input-disabled)] shadow-[inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-border-disabled)] after:[box-shadow:var(--shadow-input-disabled-overlay)]",
121
+ "[--bh-input-border:var(--bh-border-disabled)] bg-[var(--bh-interactive-input-disabled)] shadow-none after:[box-shadow:var(--shadow-input-disabled-overlay)]",
119
122
  },
120
123
  },
121
124
  compoundVariants: [
@@ -188,7 +191,7 @@ const inputTag = cva(
188
191
  const inputSegment = cva(
189
192
  [
190
193
  "inline-flex h-full shrink-0 items-center justify-center gap-[var(--bh-input-segment-gap)]",
191
- "bg-[var(--bh-interactive-secondary-default)] px-[var(--bh-input-segment-padding-x)]",
194
+ "bg-transparent px-[var(--bh-input-segment-padding-x)]",
192
195
  "text-[length:var(--bh-text-body-md-regular-font-size)]",
193
196
  "font-[var(--bh-text-body-md-regular-font-weight)]",
194
197
  "leading-[var(--bh-text-body-md-regular-line-height)]",
@@ -209,7 +212,7 @@ const inputSegment = cva(
209
212
  },
210
213
  disabled: {
211
214
  true:
212
- "border-[var(--bh-border-disabled)] bg-[var(--bh-interactive-secondary-disabled)] text-[var(--bh-content-disabled)]",
215
+ "border-[var(--bh-border-disabled)] text-[var(--bh-content-disabled)]",
213
216
  false: "",
214
217
  },
215
218
  },
@@ -224,10 +227,10 @@ const inputSegment = cva(
224
227
  const inputStepperButton = cva(
225
228
  [
226
229
  "inline-flex h-full w-[var(--bh-input-stepper-width,var(--bh-input-lg-height))] shrink-0 items-center justify-center",
227
- "bg-[var(--bh-interactive-secondary-default)] text-[var(--bh-content-subtle)]",
230
+ "bg-transparent text-[var(--bh-content-subtle)]",
228
231
  "outline-none transition-[background-color,color]",
229
232
  "hover:bg-[var(--bh-interactive-secondary-hover)] focus-visible:bg-[var(--bh-interactive-secondary-hover)]",
230
- "disabled:bg-[var(--bh-interactive-secondary-disabled)] disabled:text-[var(--bh-content-disabled)]",
233
+ "disabled:border-[var(--bh-border-disabled)] disabled:bg-transparent disabled:text-[var(--bh-content-disabled)]",
231
234
  ],
232
235
  {
233
236
  variants: {
@@ -585,7 +588,14 @@ function InputLeadingIcon({
585
588
  disabled && "text-[var(--bh-content-disabled)]"
586
589
  )}
587
590
  >
588
- {icon || <span className="text-[length:var(--bh-input-leading-symbol-font-size)] leading-none">@</span>}
591
+ {icon || (
592
+ <AtSignIcon
593
+ aria-hidden="true"
594
+ className="size-[var(--bh-input-icon-size)]"
595
+ focusable="false"
596
+ strokeWidth={2.25}
597
+ />
598
+ )}
589
599
  </span>
590
600
  )
591
601
  }