@nationaldesignstudio/react 0.6.0 → 0.7.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 (106) hide show
  1. package/dist/accordion/index.d.ts +95 -0
  2. package/dist/accordion/index.js +143 -0
  3. package/dist/accordion/index.js.map +1 -0
  4. package/dist/background/index.d.ts +149 -0
  5. package/dist/background/index.js +200 -0
  6. package/dist/background/index.js.map +1 -0
  7. package/dist/banner/index.d.ts +101 -0
  8. package/dist/banner/index.js +81 -0
  9. package/dist/banner/index.js.map +1 -0
  10. package/dist/blurred-video-backdrop/index.d.ts +233 -0
  11. package/dist/blurred-video-backdrop/index.js +266 -0
  12. package/dist/blurred-video-backdrop/index.js.map +1 -0
  13. package/dist/button/index.d.ts +180 -0
  14. package/dist/button/index.js +169 -0
  15. package/dist/button/index.js.map +1 -0
  16. package/dist/button-B2g5fH9b.d.ts +152 -0
  17. package/dist/card/index.d.ts +406 -0
  18. package/dist/card/index.js +219 -0
  19. package/dist/card/index.js.map +1 -0
  20. package/dist/card-grid/index.d.ts +90 -0
  21. package/dist/card-grid/index.js +74 -0
  22. package/dist/card-grid/index.js.map +1 -0
  23. package/dist/component-registry.md +136 -2
  24. package/dist/dev-toolbar/index.d.ts +8 -0
  25. package/dist/dev-toolbar/index.js +206 -0
  26. package/dist/dev-toolbar/index.js.map +1 -0
  27. package/dist/dialog/index.d.ts +268 -0
  28. package/dist/dialog/index.js +288 -0
  29. package/dist/dialog/index.js.map +1 -0
  30. package/dist/faq-section/index.d.ts +47 -0
  31. package/dist/faq-section/index.js +152 -0
  32. package/dist/faq-section/index.js.map +1 -0
  33. package/dist/grid-overlay/index.d.ts +10 -0
  34. package/dist/grid-overlay/index.js +38 -0
  35. package/dist/grid-overlay/index.js.map +1 -0
  36. package/dist/hero/index.d.ts +462 -0
  37. package/dist/hero/index.js +494 -0
  38. package/dist/hero/index.js.map +1 -0
  39. package/dist/hooks/index.d.ts +150 -0
  40. package/dist/hooks/index.js +339 -0
  41. package/dist/hooks/index.js.map +1 -0
  42. package/dist/index.d.ts +46 -5339
  43. package/dist/index.js +157 -4080
  44. package/dist/index.js.map +1 -1
  45. package/dist/input/index.d.ts +404 -0
  46. package/dist/input/index.js +393 -0
  47. package/dist/input/index.js.map +1 -0
  48. package/dist/navbar/index.d.ts +68 -0
  49. package/dist/navbar/index.js +227 -0
  50. package/dist/navbar/index.js.map +1 -0
  51. package/dist/ndstudio-footer/index.d.ts +32 -0
  52. package/dist/ndstudio-footer/index.js +35 -0
  53. package/dist/ndstudio-footer/index.js.map +1 -0
  54. package/dist/pager-control/index.d.ts +173 -0
  55. package/dist/pager-control/index.js +267 -0
  56. package/dist/pager-control/index.js.map +1 -0
  57. package/dist/popover/index.d.ts +200 -0
  58. package/dist/popover/index.js +290 -0
  59. package/dist/popover/index.js.map +1 -0
  60. package/dist/prose/index.d.ts +39 -0
  61. package/dist/prose/index.js +56 -0
  62. package/dist/prose/index.js.map +1 -0
  63. package/dist/quote-block/index.d.ts +156 -0
  64. package/dist/quote-block/index.js +321 -0
  65. package/dist/quote-block/index.js.map +1 -0
  66. package/dist/river/index.d.ts +100 -0
  67. package/dist/river/index.js +107 -0
  68. package/dist/river/index.js.map +1 -0
  69. package/dist/select/index.d.ts +188 -0
  70. package/dist/select/index.js +295 -0
  71. package/dist/select/index.js.map +1 -0
  72. package/dist/theme/index.d.ts +149 -0
  73. package/dist/theme/index.js +211 -0
  74. package/dist/theme/index.js.map +1 -0
  75. package/dist/theme-CzBPUlh_.d.ts +332 -0
  76. package/dist/tooltip/index.d.ts +166 -0
  77. package/dist/tooltip/index.js +200 -0
  78. package/dist/tooltip/index.js.map +1 -0
  79. package/dist/tout/index.d.ts +157 -0
  80. package/dist/tout/index.js +315 -0
  81. package/dist/tout/index.js.map +1 -0
  82. package/dist/two-column-section/index.d.ts +122 -0
  83. package/dist/two-column-section/index.js +121 -0
  84. package/dist/two-column-section/index.js.map +1 -0
  85. package/dist/us-gov-banner/index.d.ts +141 -0
  86. package/dist/us-gov-banner/index.js +74 -0
  87. package/dist/us-gov-banner/index.js.map +1 -0
  88. package/dist/use-captions-AkKlJhov.d.ts +71 -0
  89. package/dist/utils/index.d.ts +7 -0
  90. package/dist/utils/index.js +12 -0
  91. package/dist/utils/index.js.map +1 -0
  92. package/dist/video-dialog/index.d.ts +106 -0
  93. package/dist/video-dialog/index.js +1305 -0
  94. package/dist/video-dialog/index.js.map +1 -0
  95. package/dist/video-player/index.d.ts +115 -0
  96. package/dist/video-player/index.js +879 -0
  97. package/dist/video-player/index.js.map +1 -0
  98. package/dist/video-player-qxf-BURH.d.ts +236 -0
  99. package/dist/video-with-backdrop/index.d.ts +267 -0
  100. package/dist/video-with-backdrop/index.js +1284 -0
  101. package/dist/video-with-backdrop/index.js.map +1 -0
  102. package/package.json +13 -2
  103. package/src/components/organisms/us-gov-banner/us-gov-banner.tsx +5 -27
  104. package/src/theme/hooks.ts +2 -0
  105. package/src/theme/index.ts +2 -0
  106. package/src/theme/theme-provider.tsx +2 -0
@@ -0,0 +1,393 @@
1
+ "use client";
2
+ import * as React from 'react';
3
+ import { tv, cnBase } from 'tailwind-variants';
4
+ import { clsx } from 'clsx';
5
+ import { jsx } from 'react/jsx-runtime';
6
+ import { Button as Button$1 } from '@base-ui-components/react/button';
7
+
8
+ // src/components/atoms/input/input.tsx
9
+ var formControlBase = [
10
+ // Layout
11
+ "flex w-full items-center",
12
+ // Typography
13
+ "text-16 font-medium leading-14",
14
+ // Border and radius - uses surface ui radius for theming support
15
+ "border border-solid border-ui-color-border rounded-surface-ui-medium",
16
+ // Background
17
+ "bg-ui-control-background",
18
+ // Transitions
19
+ "transition-[background-color,border-color,box-shadow] duration-150",
20
+ // Focus state
21
+ "outline-none focus-visible:border-border-focus focus-visible:ring-4 focus-visible:ring-ui-color-focus",
22
+ // Hover state (when not focused or disabled)
23
+ "hover:bg-ui-control-background-hover hover:focus-visible:bg-ui-control-background",
24
+ // Disabled state
25
+ "disabled:bg-ui-control-background-disabled disabled:cursor-not-allowed disabled:opacity-50"
26
+ ];
27
+ var formControlSizes = {
28
+ sm: "h-spatial-ui-control-height-small px-spatial-ui-control-padding-x-small py-spatial-ui-control-padding-y-small text-14",
29
+ default: "h-spatial-ui-control-height-medium px-spatial-ui-control-padding-x-medium py-spatial-ui-control-padding-y-medium",
30
+ lg: "h-spatial-ui-control-height-large px-spatial-ui-control-padding-x-large py-spatial-ui-control-padding-y-large text-18"
31
+ };
32
+ var formControlError = {
33
+ true: "border-ui-error-color focus-visible:border-ui-error-color focus-visible:ring-ui-error-color/20 text-ui-error-color",
34
+ false: ""
35
+ };
36
+ tv({
37
+ base: formControlBase,
38
+ variants: {
39
+ size: formControlSizes,
40
+ error: formControlError
41
+ },
42
+ defaultVariants: {
43
+ size: "default",
44
+ error: false
45
+ }
46
+ });
47
+ function cn(...inputs) {
48
+ return cnBase(clsx(inputs));
49
+ }
50
+ var inputSizes = {
51
+ sm: "h-spatial-ui-input-height-small px-spatial-ui-input-padding-x-small py-spatial-ui-input-padding-y-small typography-body-sm-md",
52
+ default: "h-spatial-ui-input-height-medium px-spatial-ui-input-padding-x-medium py-spatial-ui-input-padding-y-medium typography-body-md-md",
53
+ lg: "h-spatial-ui-input-height-large px-spatial-ui-input-padding-x-large py-spatial-ui-input-padding-y-large typography-body-md-lg"
54
+ };
55
+ var inputVariants = tv({
56
+ base: [
57
+ ...formControlBase,
58
+ // Input-specific: Placeholder styling
59
+ "placeholder:text-text-muted"
60
+ ],
61
+ variants: {
62
+ size: inputSizes,
63
+ error: {
64
+ ...formControlError,
65
+ true: `${formControlError.true} placeholder:text-ui-error-color/60`
66
+ }
67
+ },
68
+ defaultVariants: {
69
+ size: "default",
70
+ error: false
71
+ }
72
+ });
73
+ var Input = React.forwardRef(
74
+ ({ className, size, error, type = "text", ...props }, ref) => {
75
+ return /* @__PURE__ */ jsx(
76
+ "input",
77
+ {
78
+ ref,
79
+ type,
80
+ "aria-invalid": error || void 0,
81
+ className: cn(inputVariants({ size, error, class: className })),
82
+ "data-size": size ?? "default",
83
+ "data-error": error ?? false,
84
+ ...props
85
+ }
86
+ );
87
+ }
88
+ );
89
+ Input.displayName = "Input";
90
+
91
+ // src/lib/theme.ts
92
+ function colorToVar(token) {
93
+ return `var(--color-${token})`;
94
+ }
95
+ function radiusToVar(token) {
96
+ return `var(--${token})`;
97
+ }
98
+ function buttonThemeToStyleVars(theme) {
99
+ if (!theme) return {};
100
+ const vars = {};
101
+ if (theme.bg) vars["--btn-bg"] = colorToVar(theme.bg);
102
+ if (theme.bgHover) vars["--btn-bg-hover"] = colorToVar(theme.bgHover);
103
+ if (theme.bgActive) vars["--btn-bg-active"] = colorToVar(theme.bgActive);
104
+ if (theme.text) vars["--btn-text"] = colorToVar(theme.text);
105
+ if (theme.borderColor)
106
+ vars["--btn-border-color"] = colorToVar(theme.borderColor);
107
+ if (theme.borderWidth !== void 0)
108
+ vars["--btn-border-width"] = `${theme.borderWidth}px`;
109
+ if (theme.radius) vars["--btn-radius"] = radiusToVar(theme.radius);
110
+ return vars;
111
+ }
112
+ var buttonVariants = tv({
113
+ base: "inline-flex items-center justify-center gap-spatial-ui-button-gap-icon-text whitespace-nowrap transition-colors duration-150 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-1 disabled:pointer-events-none disabled:opacity-50",
114
+ variants: {
115
+ variant: {
116
+ // Primary - blue filled button
117
+ primary: "bg-button-primary-bg text-button-primary-text hover:bg-button-primary-bg-hover active:bg-button-primary-bg-active border-transparent",
118
+ // Default - dark filled button
119
+ default: "bg-button-default-bg text-button-default-text hover:bg-button-default-bg-hover active:bg-button-default-bg-active border-transparent",
120
+ // Secondary - light gray filled with subtle border
121
+ secondary: "bg-button-secondary-bg text-button-secondary-text hover:bg-button-secondary-bg-hover active:bg-button-secondary-bg-active border border-button-secondary-border",
122
+ // Destructive - red filled button
123
+ destructive: "bg-button-destructive-bg text-button-destructive-text hover:bg-button-destructive-bg-hover active:bg-button-destructive-bg-active border-transparent",
124
+ // Outline - bordered with transparent background
125
+ outline: "bg-button-outline-bg text-button-outline-text hover:bg-button-outline-bg-hover active:bg-button-outline-bg-active border border-button-outline-border hover:border-button-outline-border-hover",
126
+ // Ghost - transparent with subtle hover
127
+ ghost: "bg-button-ghost-bg text-button-ghost-text hover:bg-button-ghost-bg-hover active:bg-button-ghost-bg-active border-transparent",
128
+ // Link - text only with underline on hover
129
+ link: "bg-transparent text-button-link-text hover:text-button-link-text-hover hover:underline active:text-button-link-text-hover border-transparent underline-offset-4",
130
+ // Themed - uses CSS custom properties for styling
131
+ themed: "[background:var(--btn-bg)] [color:var(--btn-text)] [border-color:var(--btn-border-color,transparent)] hover:[background:var(--btn-bg-hover,var(--btn-bg))] active:[background:var(--btn-bg-active,var(--btn-bg-hover,var(--btn-bg)))]"
132
+ },
133
+ size: {
134
+ sm: "typography-ui-text-xs h-spatial-ui-button-height-small px-spatial-ui-button-padding-x-small py-spatial-ui-button-padding-y-small rounded-surface-button-small",
135
+ default: "typography-ui-text-sm h-spatial-ui-button-height-medium px-spatial-ui-button-padding-x-medium py-spatial-ui-button-padding-y-medium rounded-surface-button-medium",
136
+ lg: "typography-ui-text-md h-spatial-ui-button-height-large px-spatial-ui-button-padding-x-large py-spatial-ui-button-padding-y-large rounded-surface-button-large"
137
+ }
138
+ },
139
+ defaultVariants: {
140
+ variant: "default",
141
+ size: "default"
142
+ }
143
+ });
144
+ function hasThemeValues(theme) {
145
+ if (!theme) return false;
146
+ return Object.values(theme).some((v) => v !== void 0 && v !== null);
147
+ }
148
+ var Button = React.forwardRef(
149
+ ({ className, variant, size, render, nativeButton, theme, style, ...props }, ref) => {
150
+ const isNativeButton = nativeButton ?? render === void 0;
151
+ const hasTheme = hasThemeValues(theme);
152
+ const effectiveVariant = hasTheme ? "themed" : variant;
153
+ const themeStyles = buttonThemeToStyleVars(theme);
154
+ const combinedStyles = hasTheme ? { ...themeStyles, ...style } : style;
155
+ const resolvedVariant = effectiveVariant ?? "default";
156
+ const resolvedSize = size ?? "default";
157
+ return /* @__PURE__ */ jsx(
158
+ Button$1,
159
+ {
160
+ className: buttonVariants({
161
+ variant: effectiveVariant,
162
+ size,
163
+ class: className
164
+ }),
165
+ ref,
166
+ render,
167
+ nativeButton: isNativeButton,
168
+ style: combinedStyles,
169
+ "data-variant": resolvedVariant,
170
+ "data-size": resolvedSize,
171
+ ...props
172
+ }
173
+ );
174
+ }
175
+ );
176
+ Button.displayName = "Button";
177
+ var inputGroupVariants = tv({
178
+ base: [
179
+ ...formControlBase,
180
+ // InputGroup-specific: Container layout and styling
181
+ "group/input-group relative flex w-full items-center justify-start",
182
+ "shadow-xs",
183
+ // Override focus state to work with child focus detection
184
+ "focus-visible:border-ui-color-border focus-visible:ring-0",
185
+ // Override hover to work with InputGroup structure
186
+ "hover:bg-ui-control-background",
187
+ // Height and layout (overridden by size variants)
188
+ "min-w-0",
189
+ "has-[>textarea]:h-auto",
190
+ "has-[>[data-align=inline-start]]:[&>input]:pl-6",
191
+ "has-[>[data-align=inline-end]]:[&>input]:pr-6",
192
+ "has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pt-spatial-ui-input-group-gap-block has-[>[data-align=block-start]]:[&>input]:pb-spatial-ui-input-group-padding-y-medium",
193
+ "has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pb-spatial-ui-input-group-gap-block",
194
+ // Focus state detection on child input (overrides formControlBase focus)
195
+ "has-[[data-slot=input-group-control]:focus-visible]:border-ui-accent-base has-[[data-slot=input-group-control]:focus-visible]:ring-4 has-[[data-slot=input-group-control]:focus-visible]:ring-ui-color-focus",
196
+ // Error state detection on child elements
197
+ "has-[[data-slot][aria-invalid=true]]:border-ui-error-color has-[[data-slot][aria-invalid=true]]:ring-ui-error-color/20",
198
+ // Disabled state (overrides formControlBase disabled)
199
+ "data-[disabled=true]:bg-ui-control-background-disabled data-[disabled=true]:opacity-50 data-[disabled=true]:cursor-not-allowed"
200
+ ],
201
+ variants: {
202
+ size: {
203
+ sm: "h-spatial-ui-input-group-height-small",
204
+ default: "h-spatial-ui-input-group-height-medium",
205
+ lg: "h-spatial-ui-input-group-height-large"
206
+ }
207
+ },
208
+ defaultVariants: {
209
+ size: "default"
210
+ }
211
+ });
212
+ var inputGroupAddonVariants = tv({
213
+ base: [
214
+ "flex items-center justify-center gap-6",
215
+ "typography-ui-text-sm text-text-muted",
216
+ "select-none cursor-text",
217
+ "[&>svg:not([class*='size-'])]:size-16",
218
+ "[&_button]:text-[unset] [&_button]:cursor-pointer",
219
+ "group-data-[disabled=true]/input-group:opacity-50"
220
+ ],
221
+ variants: {
222
+ align: {
223
+ "inline-start": [
224
+ "order-first h-full",
225
+ "pl-spatial-ui-input-group-padding-x-medium",
226
+ "has-[>button]:ml-[-6px]"
227
+ ],
228
+ "inline-end": [
229
+ "order-last h-full",
230
+ "pr-spatial-ui-input-group-padding-x-medium",
231
+ "has-[>button]:mr-[-6px]"
232
+ ],
233
+ "block-start": [
234
+ "order-first h-auto w-full justify-start",
235
+ "px-spatial-ui-input-group-padding-x-medium pt-spatial-ui-input-group-padding-y-medium",
236
+ "[.border-b]:pb-spatial-ui-input-group-padding-y-medium",
237
+ // Use shared text style for block addons (12px, Regular weight per Figma)
238
+ "typography-ui-text-xs"
239
+ ],
240
+ "block-end": [
241
+ "order-last h-auto w-full justify-start",
242
+ "px-spatial-ui-input-group-padding-x-medium pb-spatial-ui-input-group-padding-y-medium",
243
+ "[.border-t]:pt-spatial-ui-input-group-padding-y-medium",
244
+ // Use shared text style for block addons (12px, Regular weight per Figma)
245
+ "typography-ui-text-xs"
246
+ ]
247
+ }
248
+ },
249
+ defaultVariants: {
250
+ align: "inline-start"
251
+ }
252
+ });
253
+ function InputGroup({ className, size, disabled, ...props }) {
254
+ return /* @__PURE__ */ jsx(
255
+ "fieldset",
256
+ {
257
+ "data-slot": "input-group",
258
+ "data-disabled": disabled || void 0,
259
+ disabled,
260
+ className: cn(
261
+ "p-0 m-0 min-w-0",
262
+ inputGroupVariants({ size, class: className })
263
+ ),
264
+ ...props
265
+ }
266
+ );
267
+ }
268
+ function InputGroupAddon({
269
+ className,
270
+ align = "inline-start",
271
+ onClick,
272
+ onKeyDown,
273
+ ...props
274
+ }) {
275
+ const focusInput = (element) => {
276
+ element.parentElement?.querySelector("input")?.focus();
277
+ };
278
+ const handleClick = (e) => {
279
+ if (e.target.closest("button")) {
280
+ onClick?.(e);
281
+ return;
282
+ }
283
+ focusInput(e.currentTarget);
284
+ onClick?.(e);
285
+ };
286
+ const handleKeyDown = (e) => {
287
+ if ((e.key === "Enter" || e.key === " ") && !e.target.closest("button")) {
288
+ focusInput(e.currentTarget);
289
+ }
290
+ onKeyDown?.(e);
291
+ };
292
+ return (
293
+ // biome-ignore lint/a11y/noStaticElementInteractions: Click-to-focus is a convenience UX pattern; primary interaction is via the input itself
294
+ /* @__PURE__ */ jsx(
295
+ "div",
296
+ {
297
+ "data-slot": "input-group-addon",
298
+ "data-align": align,
299
+ className: cn(inputGroupAddonVariants({ align, class: className })),
300
+ onClick: handleClick,
301
+ onKeyDown: handleKeyDown,
302
+ ...props
303
+ }
304
+ )
305
+ );
306
+ }
307
+ var inputGroupButtonVariants = tv({
308
+ base: [
309
+ "typography-ui-button-small shadow-none flex gap-6 items-center",
310
+ "focus-visible:ring-1 focus-visible:ring-offset-0 focus-visible:ring-ui-color-border",
311
+ "transition-opacity duration-150"
312
+ ],
313
+ variants: {
314
+ size: {
315
+ xs: "!h-24 gap-4 px-8 rounded-surface-ui-small [&>svg:not([class*='size-'])]:size-16 has-[>svg]:px-6",
316
+ sm: "!h-28 px-10 gap-6 rounded-surface-ui-medium has-[>svg]:px-8",
317
+ "icon-xs": "!size-24 rounded-surface-ui-small p-0 [&>svg:not([class*='size-'])]:size-16 has-[>svg]:p-0",
318
+ "icon-sm": "!size-28 rounded-surface-ui-medium p-0 [&>svg:not([class*='size-'])]:size-16 has-[>svg]:p-0"
319
+ }
320
+ },
321
+ defaultVariants: {
322
+ size: "xs"
323
+ }
324
+ });
325
+ function InputGroupButton({
326
+ className,
327
+ type = "button",
328
+ variant = "ghost",
329
+ size = "xs",
330
+ ...props
331
+ }) {
332
+ return /* @__PURE__ */ jsx(
333
+ Button,
334
+ {
335
+ type,
336
+ "data-size": size,
337
+ variant,
338
+ className: cn(inputGroupButtonVariants({ size, class: className })),
339
+ ...props
340
+ }
341
+ );
342
+ }
343
+ function InputGroupText({ className, ...props }) {
344
+ return /* @__PURE__ */ jsx(
345
+ "span",
346
+ {
347
+ className: cn(
348
+ "flex items-center gap-6 typography-ui-text-sm text-text-muted",
349
+ "[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-16",
350
+ className
351
+ ),
352
+ ...props
353
+ }
354
+ );
355
+ }
356
+ function InputGroupInput({ className, ...props }) {
357
+ return /* @__PURE__ */ jsx(
358
+ "input",
359
+ {
360
+ "data-slot": "input-group-control",
361
+ className: cn(
362
+ "flex-1 min-w-0 h-full w-full",
363
+ "border-0 bg-transparent shadow-none outline-none",
364
+ "typography-ui-text-sm text-left placeholder:text-text-muted",
365
+ "px-spatial-ui-input-group-padding-x-medium",
366
+ "focus-visible:ring-0",
367
+ className
368
+ ),
369
+ ...props
370
+ }
371
+ );
372
+ }
373
+ function InputGroupTextarea({ className, ...props }) {
374
+ return /* @__PURE__ */ jsx(
375
+ "textarea",
376
+ {
377
+ "data-slot": "input-group-control",
378
+ className: cn(
379
+ "flex-1 min-w-0 w-full resize-none",
380
+ "border-0 bg-transparent shadow-none outline-none",
381
+ "typography-ui-text-sm text-left placeholder:text-text-muted",
382
+ "px-spatial-ui-input-group-padding-x-medium py-spatial-ui-input-group-padding-y-medium",
383
+ "focus-visible:ring-0",
384
+ className
385
+ ),
386
+ ...props
387
+ }
388
+ );
389
+ }
390
+
391
+ export { Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, inputGroupAddonVariants, inputGroupVariants, inputVariants };
392
+ //# sourceMappingURL=index.js.map
393
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/form-control.ts","../../src/lib/utils.ts","../../src/components/atoms/input/input.tsx","../../src/lib/theme.ts","../../src/components/atoms/button/button.tsx","../../src/components/atoms/input/input-group.tsx"],"names":["twMerge","tv","React2","jsx","BaseButton"],"mappings":"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","file":"index.js","sourcesContent":["import { tv } from \"tailwind-variants\";\n\n/**\n * Shared form control styles for Input, Select, and similar components.\n *\n * These base styles ensure consistent appearance across all form controls:\n * - Consistent height and padding\n * - Unified focus ring and border treatment\n * - Shared hover/disabled states\n *\n * Based on Figma BaseKit / Interface / Input & Dropdown designs.\n */\n\n/**\n * Base styles shared by all form controls (input, select, etc.)\n */\nexport const formControlBase = [\n\t// Layout\n\t\"flex w-full items-center\",\n\t// Typography\n\t\"text-16 font-medium leading-14\",\n\t// Border and radius - uses surface ui radius for theming support\n\t\"border border-solid border-ui-color-border rounded-surface-ui-medium\",\n\t// Background\n\t\"bg-ui-control-background\",\n\t// Transitions\n\t\"transition-[background-color,border-color,box-shadow] duration-150\",\n\t// Focus state\n\t\"outline-none focus-visible:border-border-focus focus-visible:ring-4 focus-visible:ring-ui-color-focus\",\n\t// Hover state (when not focused or disabled)\n\t\"hover:bg-ui-control-background-hover hover:focus-visible:bg-ui-control-background\",\n\t// Disabled state\n\t\"disabled:bg-ui-control-background-disabled disabled:cursor-not-allowed disabled:opacity-50\",\n];\n\n/**\n * Size variants shared by form controls\n * Uses spatial tokens for consistent sizing across form controls\n */\nexport const formControlSizes = {\n\tsm: \"h-spatial-ui-control-height-small px-spatial-ui-control-padding-x-small py-spatial-ui-control-padding-y-small text-14\",\n\tdefault:\n\t\t\"h-spatial-ui-control-height-medium px-spatial-ui-control-padding-x-medium py-spatial-ui-control-padding-y-medium\",\n\tlg: \"h-spatial-ui-control-height-large px-spatial-ui-control-padding-x-large py-spatial-ui-control-padding-y-large text-18\",\n} as const;\n\n/**\n * Error state styles shared by form controls\n */\nexport const formControlError = {\n\ttrue: \"border-ui-error-color focus-visible:border-ui-error-color focus-visible:ring-ui-error-color/20 text-ui-error-color\",\n\tfalse: \"\",\n} as const;\n\n/**\n * Form control variants using tailwind-variants\n * Can be composed with other variants for specific components\n */\nexport const formControlVariants = tv({\n\tbase: formControlBase,\n\tvariants: {\n\t\tsize: formControlSizes,\n\t\terror: formControlError,\n\t},\n\tdefaultVariants: {\n\t\tsize: \"default\",\n\t\terror: false,\n\t},\n});\n\nexport type FormControlSize = keyof typeof formControlSizes;\n","import { type ClassValue, clsx } from \"clsx\";\nimport { cnBase as twMerge } from \"tailwind-variants\";\n\nexport { twMerge };\n\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\nimport { formControlBase, formControlError } from \"@/lib/form-control\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * Input-specific size variants using semantic input tokens.\n * These tokens allow input sizing to be customized independently from other controls.\n */\nconst inputSizes = {\n\tsm: \"h-spatial-ui-input-height-small px-spatial-ui-input-padding-x-small py-spatial-ui-input-padding-y-small typography-body-sm-md\",\n\tdefault:\n\t\t\"h-spatial-ui-input-height-medium px-spatial-ui-input-padding-x-medium py-spatial-ui-input-padding-y-medium typography-body-md-md\",\n\tlg: \"h-spatial-ui-input-height-large px-spatial-ui-input-padding-x-large py-spatial-ui-input-padding-y-large typography-body-md-lg\",\n} as const;\n\n/**\n * Input variants for styling based on Figma BaseKit / Interface / Input\n *\n * States (handled via CSS pseudo-classes and props):\n * - Default: White background, subtle border\n * - Hover: Light gray background (via :hover)\n * - Focus: Accent border with focus ring (via :focus-visible)\n * - Error: Error border color (via error prop)\n * - Disabled: Disabled background (via :disabled)\n *\n * Sizes:\n * - sm: Smaller height and padding (36px)\n * - default: Standard height (48px)\n * - lg: Larger height and padding (56px)\n */\nconst inputVariants = tv({\n\tbase: [\n\t\t...formControlBase,\n\t\t// Input-specific: Placeholder styling\n\t\t\"placeholder:text-text-muted\",\n\t],\n\tvariants: {\n\t\tsize: inputSizes,\n\t\terror: {\n\t\t\t...formControlError,\n\t\t\ttrue: `${formControlError.true} placeholder:text-ui-error-color/60`,\n\t\t},\n\t},\n\tdefaultVariants: {\n\t\tsize: \"default\",\n\t\terror: false,\n\t},\n});\n\nexport interface InputProps\n\textends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"size\">,\n\t\tVariantProps<typeof inputVariants> {\n\t/**\n\t * Whether the input is in an error state\n\t */\n\terror?: boolean;\n}\n\n/**\n * Input component based on Figma BaseKit / Interface / Input\n *\n * A styled text input with support for various states:\n * - Default, hover, focus, error, and disabled states\n * - Three size variants: sm, default, lg\n *\n * Uses semantic UI tokens for theming support.\n *\n * @example\n * ```tsx\n * // Basic usage\n * <Input placeholder=\"Enter your email\" />\n *\n * // With error state\n * <Input error placeholder=\"Invalid input\" />\n *\n * // Different sizes\n * <Input size=\"sm\" placeholder=\"Small\" />\n * <Input size=\"lg\" placeholder=\"Large\" />\n *\n * // Disabled\n * <Input disabled placeholder=\"Disabled input\" />\n * ```\n */\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\n\t({ className, size, error, type = \"text\", ...props }, ref) => {\n\t\treturn (\n\t\t\t<input\n\t\t\t\tref={ref}\n\t\t\t\ttype={type}\n\t\t\t\taria-invalid={error || undefined}\n\t\t\t\tclassName={cn(inputVariants({ size, error, class: className }))}\n\t\t\t\tdata-size={size ?? \"default\"}\n\t\t\t\tdata-error={error ?? false}\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t},\n);\nInput.displayName = \"Input\";\n\nexport { Input, inputVariants };\n","/**\n * Component-level theming interface\n *\n * This interface defines all the customizable design tokens that can be\n * overridden at the component level. Components accepting a `theme` prop\n * will apply these values as CSS custom properties, allowing fine-grained\n * control over appearance without creating new variants.\n *\n * All values use primitive token names (e.g., \"gray-100\", \"spacing-40\")\n * which are converted to CSS variable references internally.\n */\n\n// =============================================================================\n// Token Name Types\n// =============================================================================\n\n/**\n * Color token names - maps to `--color-{name}` CSS variables\n * @example \"gray-100\", \"ember-v300\", \"alpha-black-10\"\n */\nexport type ColorToken =\n\t// Grayscale\n\t| \"gray-50\"\n\t| \"gray-100\"\n\t| \"gray-200\"\n\t| \"gray-300\"\n\t| \"gray-400\"\n\t| \"gray-500\"\n\t| \"gray-600\"\n\t| \"gray-700\"\n\t| \"gray-800\"\n\t| \"gray-900\"\n\t| \"gray-1000\"\n\t| \"gray-1100\"\n\t| \"gray-1200\"\n\t// Steel\n\t| \"steel-50\"\n\t| \"steel-100\"\n\t| \"steel-200\"\n\t| \"steel-300\"\n\t| \"steel-400\"\n\t| \"steel-500\"\n\t| \"steel-600\"\n\t| \"steel-700\"\n\t| \"steel-800\"\n\t| \"steel-900\"\n\t| \"steel-1000\"\n\t| \"steel-1100\"\n\t| \"steel-1200\"\n\t// Brown\n\t| \"brown-50\"\n\t| \"brown-100\"\n\t| \"brown-200\"\n\t| \"brown-300\"\n\t| \"brown-400\"\n\t| \"brown-500\"\n\t| \"brown-600\"\n\t| \"brown-700\"\n\t| \"brown-800\"\n\t| \"brown-900\"\n\t| \"brown-1000\"\n\t| \"brown-1100\"\n\t| \"brown-1200\"\n\t// Ember\n\t| \"ember-50\"\n\t| \"ember-100\"\n\t| \"ember-200\"\n\t| \"ember-300\"\n\t| \"ember-400\"\n\t| \"ember-500\"\n\t| \"ember-600\"\n\t| \"ember-700\"\n\t| \"ember-800\"\n\t| \"ember-900\"\n\t| \"ember-v100\"\n\t| \"ember-v200\"\n\t| \"ember-v300\"\n\t| \"ember-v400\"\n\t// Orange\n\t| \"orange-50\"\n\t| \"orange-100\"\n\t| \"orange-200\"\n\t| \"orange-300\"\n\t| \"orange-400\"\n\t| \"orange-500\"\n\t| \"orange-600\"\n\t| \"orange-700\"\n\t| \"orange-800\"\n\t| \"orange-900\"\n\t| \"orange-v100\"\n\t| \"orange-v200\"\n\t| \"orange-v300\"\n\t| \"orange-v400\"\n\t// Amber\n\t| \"amber-50\"\n\t| \"amber-100\"\n\t| \"amber-200\"\n\t| \"amber-300\"\n\t| \"amber-400\"\n\t| \"amber-500\"\n\t| \"amber-600\"\n\t| \"amber-700\"\n\t| \"amber-800\"\n\t| \"amber-900\"\n\t| \"amber-v100\"\n\t| \"amber-v200\"\n\t| \"amber-v300\"\n\t| \"amber-v400\"\n\t// Yellow\n\t| \"yellow-50\"\n\t| \"yellow-100\"\n\t| \"yellow-200\"\n\t| \"yellow-300\"\n\t| \"yellow-400\"\n\t| \"yellow-500\"\n\t| \"yellow-600\"\n\t| \"yellow-700\"\n\t| \"yellow-800\"\n\t| \"yellow-900\"\n\t| \"yellow-v100\"\n\t| \"yellow-v200\"\n\t| \"yellow-v300\"\n\t| \"yellow-v400\"\n\t// Lime\n\t| \"lime-50\"\n\t| \"lime-100\"\n\t| \"lime-200\"\n\t| \"lime-300\"\n\t| \"lime-400\"\n\t| \"lime-500\"\n\t| \"lime-600\"\n\t| \"lime-700\"\n\t| \"lime-800\"\n\t| \"lime-900\"\n\t| \"lime-v100\"\n\t| \"lime-v200\"\n\t| \"lime-v300\"\n\t| \"lime-v400\"\n\t// Green\n\t| \"green-50\"\n\t| \"green-100\"\n\t| \"green-200\"\n\t| \"green-300\"\n\t| \"green-400\"\n\t| \"green-500\"\n\t| \"green-600\"\n\t| \"green-700\"\n\t| \"green-800\"\n\t| \"green-900\"\n\t| \"green-v100\"\n\t| \"green-v200\"\n\t| \"green-v300\"\n\t| \"green-v400\"\n\t// Sage\n\t| \"sage-50\"\n\t| \"sage-100\"\n\t| \"sage-200\"\n\t| \"sage-300\"\n\t| \"sage-400\"\n\t| \"sage-500\"\n\t| \"sage-600\"\n\t| \"sage-700\"\n\t| \"sage-800\"\n\t| \"sage-900\"\n\t| \"sage-v100\"\n\t| \"sage-v200\"\n\t| \"sage-v300\"\n\t| \"sage-v400\"\n\t// Teal\n\t| \"teal-50\"\n\t| \"teal-100\"\n\t| \"teal-200\"\n\t| \"teal-300\"\n\t| \"teal-400\"\n\t| \"teal-500\"\n\t| \"teal-600\"\n\t| \"teal-700\"\n\t| \"teal-800\"\n\t| \"teal-900\"\n\t| \"teal-v100\"\n\t| \"teal-v200\"\n\t| \"teal-v300\"\n\t| \"teal-v400\"\n\t// Cyan\n\t| \"cyan-50\"\n\t| \"cyan-100\"\n\t| \"cyan-200\"\n\t| \"cyan-300\"\n\t| \"cyan-400\"\n\t| \"cyan-500\"\n\t| \"cyan-600\"\n\t| \"cyan-700\"\n\t| \"cyan-800\"\n\t| \"cyan-900\"\n\t| \"cyan-v100\"\n\t| \"cyan-v200\"\n\t| \"cyan-v300\"\n\t| \"cyan-v400\"\n\t// Ice\n\t| \"ice-50\"\n\t| \"ice-100\"\n\t| \"ice-200\"\n\t| \"ice-300\"\n\t| \"ice-400\"\n\t| \"ice-500\"\n\t| \"ice-600\"\n\t| \"ice-700\"\n\t| \"ice-800\"\n\t| \"ice-900\"\n\t| \"ice-v100\"\n\t| \"ice-v200\"\n\t| \"ice-v300\"\n\t| \"ice-v400\"\n\t// Blue\n\t| \"blue-50\"\n\t| \"blue-100\"\n\t| \"blue-200\"\n\t| \"blue-300\"\n\t| \"blue-400\"\n\t| \"blue-500\"\n\t| \"blue-600\"\n\t| \"blue-700\"\n\t| \"blue-800\"\n\t| \"blue-900\"\n\t| \"blue-v100\"\n\t| \"blue-v200\"\n\t| \"blue-v300\"\n\t| \"blue-v400\"\n\t// Indigo\n\t| \"indigo-50\"\n\t| \"indigo-100\"\n\t| \"indigo-200\"\n\t| \"indigo-300\"\n\t| \"indigo-400\"\n\t| \"indigo-500\"\n\t| \"indigo-600\"\n\t| \"indigo-700\"\n\t| \"indigo-800\"\n\t| \"indigo-900\"\n\t| \"indigo-v100\"\n\t| \"indigo-v200\"\n\t| \"indigo-v300\"\n\t| \"indigo-v400\"\n\t// Iris\n\t| \"iris-50\"\n\t| \"iris-100\"\n\t| \"iris-200\"\n\t| \"iris-300\"\n\t| \"iris-400\"\n\t| \"iris-500\"\n\t| \"iris-600\"\n\t| \"iris-700\"\n\t| \"iris-800\"\n\t| \"iris-900\"\n\t| \"iris-v100\"\n\t| \"iris-v200\"\n\t| \"iris-v300\"\n\t| \"iris-v400\"\n\t// Purple\n\t| \"purple-50\"\n\t| \"purple-100\"\n\t| \"purple-200\"\n\t| \"purple-300\"\n\t| \"purple-400\"\n\t| \"purple-500\"\n\t| \"purple-600\"\n\t| \"purple-700\"\n\t| \"purple-800\"\n\t| \"purple-900\"\n\t| \"purple-v100\"\n\t| \"purple-v200\"\n\t| \"purple-v300\"\n\t| \"purple-v400\"\n\t// Pink\n\t| \"pink-50\"\n\t| \"pink-100\"\n\t| \"pink-200\"\n\t| \"pink-300\"\n\t| \"pink-400\"\n\t| \"pink-500\"\n\t| \"pink-600\"\n\t| \"pink-700\"\n\t| \"pink-800\"\n\t| \"pink-900\"\n\t| \"pink-v100\"\n\t| \"pink-v200\"\n\t| \"pink-v300\"\n\t| \"pink-v400\"\n\t// Red\n\t| \"red-50\"\n\t| \"red-100\"\n\t| \"red-200\"\n\t| \"red-300\"\n\t| \"red-400\"\n\t| \"red-500\"\n\t| \"red-600\"\n\t| \"red-700\"\n\t| \"red-800\"\n\t| \"red-900\"\n\t| \"red-v100\"\n\t| \"red-v200\"\n\t| \"red-v300\"\n\t| \"red-v400\"\n\t// Alpha\n\t| \"alpha-black-5\"\n\t| \"alpha-black-10\"\n\t| \"alpha-black-20\"\n\t| \"alpha-black-30\"\n\t| \"alpha-black-40\"\n\t| \"alpha-black-50\"\n\t| \"alpha-black-60\"\n\t| \"alpha-black-70\"\n\t| \"alpha-black-80\"\n\t| \"alpha-black-90\"\n\t| \"alpha-black-95\"\n\t| \"alpha-white-5\"\n\t| \"alpha-white-10\"\n\t| \"alpha-white-20\"\n\t| \"alpha-white-30\"\n\t| \"alpha-white-40\"\n\t| \"alpha-white-50\"\n\t| \"alpha-white-60\"\n\t| \"alpha-white-70\"\n\t| \"alpha-white-80\"\n\t| \"alpha-white-90\"\n\t| \"alpha-white-95\"\n\t// Special\n\t| \"white\"\n\t| \"black\";\n\n/**\n * Spacing token names - maps to `--spacing-{name}` CSS variables\n * @example \"spacing-40\", \"spacing-72\"\n */\nexport type SpacingToken =\n\t| \"spacing-0\"\n\t| \"spacing-2\"\n\t| \"spacing-4\"\n\t| \"spacing-6\"\n\t| \"spacing-8\"\n\t| \"spacing-10\"\n\t| \"spacing-11\"\n\t| \"spacing-12\"\n\t| \"spacing-16\"\n\t| \"spacing-20\"\n\t| \"spacing-24\"\n\t| \"spacing-28\"\n\t| \"spacing-32\"\n\t| \"spacing-36\"\n\t| \"spacing-40\"\n\t| \"spacing-48\"\n\t| \"spacing-56\"\n\t| \"spacing-64\"\n\t| \"spacing-72\"\n\t| \"spacing-80\"\n\t| \"spacing-96\"\n\t| \"spacing-112\"\n\t| \"spacing-128\"\n\t| \"spacing-144\"\n\t| \"spacing-160\"\n\t| \"spacing-176\"\n\t| \"spacing-192\"\n\t| \"spacing-208\"\n\t| \"spacing-224\"\n\t| \"spacing-240\"\n\t| \"spacing-256\"\n\t| \"spacing-288\"\n\t| \"spacing-320\"\n\t| \"spacing-352\"\n\t| \"spacing-384\"\n\t| \"spacing-400\";\n\n/**\n * Radius token names - maps to `--radii-{name}` CSS variables\n * @example \"radii-4\", \"radii-6\"\n */\nexport type RadiusToken =\n\t| \"radii-0\"\n\t| \"radii-2\"\n\t| \"radii-4\"\n\t| \"radii-6\"\n\t| \"radii-8\"\n\t| \"radii-10\"\n\t| \"radii-11\"\n\t| \"radii-12\"\n\t| \"radii-16\"\n\t| \"radii-20\"\n\t| \"radii-24\"\n\t| \"radii-28\"\n\t| \"radii-32\"\n\t| \"radii-36\"\n\t| \"radii-40\"\n\t| \"radii-48\"\n\t| \"radii-56\"\n\t| \"radii-64\"\n\t| \"radii-72\"\n\t| \"radii-80\"\n\t| \"radii-96\"\n\t| \"radii-112\"\n\t| \"radii-128\"\n\t| \"radii-144\"\n\t| \"radii-160\"\n\t| \"radii-176\"\n\t| \"radii-192\"\n\t| \"radii-208\"\n\t| \"radii-224\"\n\t| \"radii-240\"\n\t| \"radii-256\"\n\t| \"radii-288\"\n\t| \"radii-320\"\n\t| \"radii-352\"\n\t| \"radii-384\"\n\t| \"radii-400\";\n\n/**\n * Font size token values - primitive font sizes available in the design system\n * These correspond to Tailwind classes like `text-64`, `text-128`, etc.\n * @example 64, 128, 192\n */\nexport type FontSizeToken =\n\t| 9\n\t| 11\n\t| 12\n\t| 14\n\t| 16\n\t| 18\n\t| 21\n\t| 24\n\t| 28\n\t| 32\n\t| 36\n\t| 42\n\t| 48\n\t| 56\n\t| 64\n\t| 72\n\t| 84\n\t| 88\n\t| 96\n\t| 112\n\t| 128\n\t| 148\n\t| 168\n\t| 192\n\t| 224\n\t| 256\n\t| 280;\n\n/**\n * Array of all available font sizes for use in UI selectors/dropdowns\n */\nexport const FONT_SIZES: FontSizeToken[] = [\n\t9, 11, 12, 14, 16, 18, 21, 24, 28, 32, 36, 42, 48, 56, 64, 72, 84, 88, 96,\n\t112, 128, 148, 168, 192, 224, 256, 280,\n];\n\n/**\n * Helper to generate typography class string from font size\n * @example fontSizeToClass(128) => \"text-128 leading-128 tracking-128\"\n */\nexport function fontSizeToClass(size: FontSizeToken): string {\n\treturn `text-${size} leading-${size} tracking-${size}`;\n}\n\n/**\n * Helper to generate responsive typography class string\n * @example responsiveTypographyClass(64, 128, 192) => \"text-64 leading-64 tracking-64 md:text-128 md:leading-128 md:tracking-128 xl:text-192 xl:leading-192 xl:tracking-192\"\n */\nexport function responsiveTypographyClass(\n\tmobile: FontSizeToken,\n\ttablet: FontSizeToken,\n\tdesktop: FontSizeToken,\n): string {\n\treturn [\n\t\tfontSizeToClass(mobile),\n\t\t`md:${fontSizeToClass(tablet).split(\" \").join(\" md:\")}`,\n\t\t`xl:${fontSizeToClass(desktop).split(\" \").join(\" xl:\")}`,\n\t].join(\" \");\n}\n\n// =============================================================================\n// Color Tokens\n// =============================================================================\n\nexport interface ComponentThemeColors {\n\t/**\n\t * Background color for sections\n\t * @example \"gray-100\"\n\t */\n\tbgSection?: ColorToken;\n\n\t/**\n\t * Background color for cards\n\t * @example \"white\"\n\t */\n\tcardBackground?: ColorToken;\n\n\t/**\n\t * Muted background color\n\t * @example \"gray-50\"\n\t */\n\tbgMuted?: ColorToken;\n\n\t/**\n\t * Primary text color\n\t * @example \"gray-1100\"\n\t */\n\ttextPrimary?: ColorToken;\n\n\t/**\n\t * Secondary text color\n\t * @example \"gray-800\"\n\t */\n\ttextSecondary?: ColorToken;\n\n\t/**\n\t * Muted text color\n\t * @example \"gray-600\"\n\t */\n\ttextMuted?: ColorToken;\n\n\t/**\n\t * Inverted text color (for dark backgrounds)\n\t * @example \"gray-100\"\n\t */\n\ttextInverted?: ColorToken;\n\n\t/**\n\t * Link text color\n\t * @example \"gray-1100\"\n\t */\n\ttextLink?: ColorToken;\n\n\t/**\n\t * Link hover text color\n\t * @example \"gray-700\"\n\t */\n\ttextLinkHover?: ColorToken;\n\n\t/**\n\t * Brand accent color\n\t * @example \"ember-v300\"\n\t */\n\taccentBrand?: ColorToken;\n\n\t/**\n\t * Soft brand accent color\n\t * @example \"ember-100\"\n\t */\n\taccentBrandSoft?: ColorToken;\n\n\t/**\n\t * Subtle border color\n\t * @example \"alpha-black-10\"\n\t */\n\tborderSubtle?: ColorToken;\n\n\t/**\n\t * Strong border color\n\t * @example \"alpha-black-20\"\n\t */\n\tborderStrong?: ColorToken;\n\n\t/**\n\t * Focus border color (uses accentBrand by default)\n\t * @example \"ember-v300\"\n\t */\n\tborderFocus?: ColorToken;\n\n\t/**\n\t * Divider border color\n\t * @example \"alpha-black-10\"\n\t */\n\tborderDivider?: ColorToken;\n\n\t/**\n\t * Primary button background color\n\t * @example \"gray-1100\"\n\t */\n\tbuttonPrimaryBg?: ColorToken;\n\n\t/**\n\t * Primary button hover background color\n\t * @example \"gray-600\"\n\t */\n\tbuttonPrimaryBgHover?: ColorToken;\n\n\t/**\n\t * Secondary button background color\n\t * @example \"white\"\n\t */\n\tbuttonSecondaryBg?: ColorToken;\n\n\t/**\n\t * Secondary button hover background color\n\t * @example \"gray-100\"\n\t */\n\tbuttonSecondaryBgHover?: ColorToken;\n}\n\n// =============================================================================\n// Spatial Tokens\n// =============================================================================\n\nexport interface ComponentThemeSpatial {\n\t/**\n\t * Grid margin for large breakpoint\n\t * @example \"spacing-72\"\n\t */\n\tgridLargeMargin?: SpacingToken;\n\n\t/**\n\t * Grid gutter for large breakpoint\n\t * @example \"spacing-24\"\n\t */\n\tgridLargeGutter?: SpacingToken;\n\n\t/**\n\t * Number of grid columns for large breakpoint\n\t */\n\tgridLargeColumns?: number;\n\n\t/**\n\t * Grid margin for medium breakpoint\n\t * @example \"spacing-56\"\n\t */\n\tgridMediumMargin?: SpacingToken;\n\n\t/**\n\t * Grid gutter for medium breakpoint\n\t * @example \"spacing-20\"\n\t */\n\tgridMediumGutter?: SpacingToken;\n\n\t/**\n\t * Number of grid columns for medium breakpoint\n\t */\n\tgridMediumColumns?: number;\n\n\t/**\n\t * Grid margin for small breakpoint\n\t * @example \"spacing-24\"\n\t */\n\tgridSmallMargin?: SpacingToken;\n\n\t/**\n\t * Grid gutter for small breakpoint\n\t * @example \"spacing-12\"\n\t */\n\tgridSmallGutter?: SpacingToken;\n\n\t/**\n\t * Number of grid columns for small breakpoint\n\t */\n\tgridSmallColumns?: number;\n\n\t/**\n\t * Section gap for large breakpoint\n\t * @example \"spacing-64\"\n\t */\n\tsectionLargeGap?: SpacingToken;\n\n\t/**\n\t * Section padding for large breakpoint\n\t * @example \"spacing-128\"\n\t */\n\tsectionLargePadding?: SpacingToken;\n\n\t/**\n\t * Section gap for medium breakpoint\n\t * @example \"spacing-56\"\n\t */\n\tsectionMediumGap?: SpacingToken;\n\n\t/**\n\t * Section padding for medium breakpoint\n\t * @example \"spacing-96\"\n\t */\n\tsectionMediumPadding?: SpacingToken;\n\n\t/**\n\t * Section gap for small breakpoint\n\t * @example \"spacing-32\"\n\t */\n\tsectionSmallGap?: SpacingToken;\n\n\t/**\n\t * Section padding for small breakpoint\n\t * @example \"spacing-64\"\n\t */\n\tsectionSmallPadding?: SpacingToken;\n\n\t/**\n\t * Card gap for large size\n\t * @example \"spacing-10\"\n\t */\n\tcardLargeGap?: SpacingToken;\n\n\t/**\n\t * Card padding for large size\n\t * @example \"spacing-24\"\n\t */\n\tcardLargePadding?: SpacingToken;\n\n\t/**\n\t * Card gap for small size\n\t * @example \"spacing-12\"\n\t */\n\tcardSmallGap?: SpacingToken;\n\n\t/**\n\t * Card padding for small size\n\t * @example \"spacing-16\"\n\t */\n\tcardSmallPadding?: SpacingToken;\n}\n\n// =============================================================================\n// Surface Tokens\n// =============================================================================\n\nexport interface ComponentThemeSurface {\n\t/**\n\t * Card border radius\n\t * @example \"radii-4\"\n\t */\n\tcardRadius?: RadiusToken;\n\n\t/**\n\t * Card stroke/border width in pixels\n\t * @example 1\n\t */\n\tcardStroke?: number;\n\n\t/**\n\t * Button border radius\n\t * @example \"radii-6\"\n\t */\n\tbuttonRadius?: RadiusToken;\n\n\t/**\n\t * Button stroke/border weight in pixels\n\t * @example 1\n\t */\n\tbuttonStrokeWeight?: number;\n}\n\n// =============================================================================\n// Combined Theme Interface\n// =============================================================================\n\n/**\n * Complete component theme interface combining colors, spatial, and surface tokens.\n *\n * @example\n * ```tsx\n * const customTheme: ComponentTheme = {\n * colors: {\n * textPrimary: \"gray-100\",\n * accentBrand: \"ember-500\",\n * },\n * spatial: {\n * sectionLargePadding: \"spacing-96\",\n * },\n * surface: {\n * cardRadius: \"radii-8\",\n * },\n * };\n *\n * <Tout theme={customTheme} ... />\n * ```\n */\nexport interface ComponentTheme {\n\tcolors?: ComponentThemeColors;\n\tspatial?: ComponentThemeSpatial;\n\tsurface?: ComponentThemeSurface;\n}\n\n// =============================================================================\n// Button Theme Interface\n// =============================================================================\n\n/**\n * Button-specific theme interface for customizing individual button appearance.\n *\n * @example\n * ```tsx\n * const buttonTheme: ButtonTheme = {\n * bg: \"ember-500\",\n * bgHover: \"ember-600\",\n * text: \"white\",\n * radius: \"radii-8\",\n * };\n *\n * <Button theme={buttonTheme}>Themed Button</Button>\n * ```\n */\nexport interface ButtonTheme {\n\t/** Background color */\n\tbg?: ColorToken;\n\t/** Background color on hover */\n\tbgHover?: ColorToken;\n\t/** Background color on active/press */\n\tbgActive?: ColorToken;\n\t/** Text color */\n\ttext?: ColorToken;\n\t/** Border color (if using border) */\n\tborderColor?: ColorToken;\n\t/** Border width in pixels (0 for no border) */\n\tborderWidth?: number;\n\t/** Border radius */\n\tradius?: RadiusToken;\n}\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\n/**\n * Converts a color token name to a CSS variable reference\n */\nfunction colorToVar(token: ColorToken): string {\n\treturn `var(--color-${token})`;\n}\n\n/**\n * Converts a spacing token name to a CSS variable reference\n */\nfunction spacingToVar(token: SpacingToken): string {\n\treturn `var(--${token})`;\n}\n\n/**\n * Converts a radius token name to a CSS variable reference\n */\nfunction radiusToVar(token: RadiusToken): string {\n\treturn `var(--${token})`;\n}\n\n/**\n * Converts a ComponentTheme object to CSS custom properties (inline style object)\n *\n * @param theme - The theme object to convert\n * @returns An object suitable for use as React inline styles\n */\nexport function themeToStyleVars(\n\ttheme: ComponentTheme | undefined,\n): React.CSSProperties {\n\tif (!theme) return {};\n\n\tconst vars: Record<string, string> = {};\n\n\t// Colors\n\tif (theme.colors) {\n\t\tconst { colors } = theme;\n\t\tif (colors.bgSection)\n\t\t\tvars[\"--theme-bg-section\"] = colorToVar(colors.bgSection);\n\t\tif (colors.cardBackground)\n\t\t\tvars[\"--theme-card-background\"] = colorToVar(colors.cardBackground);\n\t\tif (colors.bgMuted) vars[\"--theme-bg-muted\"] = colorToVar(colors.bgMuted);\n\t\tif (colors.textPrimary)\n\t\t\tvars[\"--theme-text-primary\"] = colorToVar(colors.textPrimary);\n\t\tif (colors.textSecondary)\n\t\t\tvars[\"--theme-text-secondary\"] = colorToVar(colors.textSecondary);\n\t\tif (colors.textMuted)\n\t\t\tvars[\"--theme-text-muted\"] = colorToVar(colors.textMuted);\n\t\tif (colors.textInverted)\n\t\t\tvars[\"--theme-text-inverted\"] = colorToVar(colors.textInverted);\n\t\tif (colors.textLink)\n\t\t\tvars[\"--theme-text-link\"] = colorToVar(colors.textLink);\n\t\tif (colors.textLinkHover)\n\t\t\tvars[\"--theme-text-link-hover\"] = colorToVar(colors.textLinkHover);\n\t\tif (colors.accentBrand)\n\t\t\tvars[\"--theme-accent-brand\"] = colorToVar(colors.accentBrand);\n\t\tif (colors.accentBrandSoft)\n\t\t\tvars[\"--theme-accent-brand-soft\"] = colorToVar(colors.accentBrandSoft);\n\t\tif (colors.borderSubtle)\n\t\t\tvars[\"--theme-border-subtle\"] = colorToVar(colors.borderSubtle);\n\t\tif (colors.borderStrong)\n\t\t\tvars[\"--theme-border-strong\"] = colorToVar(colors.borderStrong);\n\t\tif (colors.borderFocus)\n\t\t\tvars[\"--theme-border-focus\"] = colorToVar(colors.borderFocus);\n\t\tif (colors.borderDivider)\n\t\t\tvars[\"--theme-border-divider\"] = colorToVar(colors.borderDivider);\n\t\tif (colors.buttonPrimaryBg)\n\t\t\tvars[\"--theme-button-primary-bg\"] = colorToVar(colors.buttonPrimaryBg);\n\t\tif (colors.buttonPrimaryBgHover)\n\t\t\tvars[\"--theme-button-primary-bg-hover\"] = colorToVar(\n\t\t\t\tcolors.buttonPrimaryBgHover,\n\t\t\t);\n\t\tif (colors.buttonSecondaryBg)\n\t\t\tvars[\"--theme-button-secondary-bg\"] = colorToVar(\n\t\t\t\tcolors.buttonSecondaryBg,\n\t\t\t);\n\t\tif (colors.buttonSecondaryBgHover)\n\t\t\tvars[\"--theme-button-secondary-bg-hover\"] = colorToVar(\n\t\t\t\tcolors.buttonSecondaryBgHover,\n\t\t\t);\n\t}\n\n\t// Spatial\n\tif (theme.spatial) {\n\t\tconst { spatial } = theme;\n\t\tif (spatial.gridLargeMargin)\n\t\t\tvars[\"--theme-grid-large-margin\"] = spacingToVar(spatial.gridLargeMargin);\n\t\tif (spatial.gridLargeGutter)\n\t\t\tvars[\"--theme-grid-large-gutter\"] = spacingToVar(spatial.gridLargeGutter);\n\t\tif (spatial.gridLargeColumns)\n\t\t\tvars[\"--theme-grid-large-columns\"] = String(spatial.gridLargeColumns);\n\t\tif (spatial.gridMediumMargin)\n\t\t\tvars[\"--theme-grid-medium-margin\"] = spacingToVar(\n\t\t\t\tspatial.gridMediumMargin,\n\t\t\t);\n\t\tif (spatial.gridMediumGutter)\n\t\t\tvars[\"--theme-grid-medium-gutter\"] = spacingToVar(\n\t\t\t\tspatial.gridMediumGutter,\n\t\t\t);\n\t\tif (spatial.gridMediumColumns)\n\t\t\tvars[\"--theme-grid-medium-columns\"] = String(spatial.gridMediumColumns);\n\t\tif (spatial.gridSmallMargin)\n\t\t\tvars[\"--theme-grid-small-margin\"] = spacingToVar(spatial.gridSmallMargin);\n\t\tif (spatial.gridSmallGutter)\n\t\t\tvars[\"--theme-grid-small-gutter\"] = spacingToVar(spatial.gridSmallGutter);\n\t\tif (spatial.gridSmallColumns)\n\t\t\tvars[\"--theme-grid-small-columns\"] = String(spatial.gridSmallColumns);\n\t\tif (spatial.sectionLargeGap)\n\t\t\tvars[\"--theme-section-large-gap\"] = spacingToVar(spatial.sectionLargeGap);\n\t\tif (spatial.sectionLargePadding)\n\t\t\tvars[\"--theme-section-large-padding\"] = spacingToVar(\n\t\t\t\tspatial.sectionLargePadding,\n\t\t\t);\n\t\tif (spatial.sectionMediumGap)\n\t\t\tvars[\"--theme-section-medium-gap\"] = spacingToVar(\n\t\t\t\tspatial.sectionMediumGap,\n\t\t\t);\n\t\tif (spatial.sectionMediumPadding)\n\t\t\tvars[\"--theme-section-medium-padding\"] = spacingToVar(\n\t\t\t\tspatial.sectionMediumPadding,\n\t\t\t);\n\t\tif (spatial.sectionSmallGap)\n\t\t\tvars[\"--theme-section-small-gap\"] = spacingToVar(spatial.sectionSmallGap);\n\t\tif (spatial.sectionSmallPadding)\n\t\t\tvars[\"--theme-section-small-padding\"] = spacingToVar(\n\t\t\t\tspatial.sectionSmallPadding,\n\t\t\t);\n\t\tif (spatial.cardLargeGap)\n\t\t\tvars[\"--theme-card-large-gap\"] = spacingToVar(spatial.cardLargeGap);\n\t\tif (spatial.cardLargePadding)\n\t\t\tvars[\"--theme-card-large-padding\"] = spacingToVar(\n\t\t\t\tspatial.cardLargePadding,\n\t\t\t);\n\t\tif (spatial.cardSmallGap)\n\t\t\tvars[\"--theme-card-small-gap\"] = spacingToVar(spatial.cardSmallGap);\n\t\tif (spatial.cardSmallPadding)\n\t\t\tvars[\"--theme-card-small-padding\"] = spacingToVar(\n\t\t\t\tspatial.cardSmallPadding,\n\t\t\t);\n\t}\n\n\t// Surface\n\tif (theme.surface) {\n\t\tconst { surface } = theme;\n\t\tif (surface.cardRadius)\n\t\t\tvars[\"--theme-card-radius\"] = radiusToVar(surface.cardRadius);\n\t\tif (surface.cardStroke)\n\t\t\tvars[\"--theme-card-stroke\"] = `${surface.cardStroke}px`;\n\t\tif (surface.buttonRadius)\n\t\t\tvars[\"--theme-button-radius\"] = radiusToVar(surface.buttonRadius);\n\t\tif (surface.buttonStrokeWeight)\n\t\t\tvars[\"--theme-button-stroke-weight\"] = `${surface.buttonStrokeWeight}px`;\n\t}\n\n\treturn vars as React.CSSProperties;\n}\n\n/**\n * Converts a ButtonTheme object to CSS custom properties (inline style object)\n *\n * @param theme - The button theme object to convert\n * @returns An object suitable for use as React inline styles\n */\nexport function buttonThemeToStyleVars(\n\ttheme: ButtonTheme | undefined,\n): React.CSSProperties {\n\tif (!theme) return {};\n\n\tconst vars: Record<string, string> = {};\n\n\tif (theme.bg) vars[\"--btn-bg\"] = colorToVar(theme.bg);\n\tif (theme.bgHover) vars[\"--btn-bg-hover\"] = colorToVar(theme.bgHover);\n\tif (theme.bgActive) vars[\"--btn-bg-active\"] = colorToVar(theme.bgActive);\n\tif (theme.text) vars[\"--btn-text\"] = colorToVar(theme.text);\n\tif (theme.borderColor)\n\t\tvars[\"--btn-border-color\"] = colorToVar(theme.borderColor);\n\tif (theme.borderWidth !== undefined)\n\t\tvars[\"--btn-border-width\"] = `${theme.borderWidth}px`;\n\tif (theme.radius) vars[\"--btn-radius\"] = radiusToVar(theme.radius);\n\n\treturn vars as React.CSSProperties;\n}\n","\"use client\";\n\nimport {\n\tButton as BaseButton,\n\ttype ButtonProps as BaseButtonProps,\n} from \"@base-ui-components/react/button\";\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\nimport { type ButtonTheme, buttonThemeToStyleVars } from \"../../../lib/theme\";\n\n/**\n * Button component based on Figma Button component\n *\n * Variants (matches Figma):\n * - primary: Blue filled button for primary actions\n * - default: Dark filled button for secondary prominence\n * - secondary: Light gray filled button with subtle border\n * - destructive: Red filled button for destructive actions\n * - outline: Bordered button with transparent background\n * - ghost: Transparent button with subtle hover\n * - link: Text-only button with underline on hover\n *\n * Sizes (matches Figma):\n * - sm: Small buttons (32px height)\n * - default: Default buttons (36px height)\n * - lg: Large buttons (40px height)\n *\n * For icon-only buttons, use the IconButton component instead.\n *\n * Theme Support:\n * Pass a `theme` prop to override default colors via CSS custom properties.\n */\nconst buttonVariants = tv({\n\tbase: \"inline-flex items-center justify-center gap-spatial-ui-button-gap-icon-text whitespace-nowrap transition-colors duration-150 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-1 disabled:pointer-events-none disabled:opacity-50\",\n\tvariants: {\n\t\tvariant: {\n\t\t\t// Primary - blue filled button\n\t\t\tprimary:\n\t\t\t\t\"bg-button-primary-bg text-button-primary-text hover:bg-button-primary-bg-hover active:bg-button-primary-bg-active border-transparent\",\n\t\t\t// Default - dark filled button\n\t\t\tdefault:\n\t\t\t\t\"bg-button-default-bg text-button-default-text hover:bg-button-default-bg-hover active:bg-button-default-bg-active border-transparent\",\n\t\t\t// Secondary - light gray filled with subtle border\n\t\t\tsecondary:\n\t\t\t\t\"bg-button-secondary-bg text-button-secondary-text hover:bg-button-secondary-bg-hover active:bg-button-secondary-bg-active border border-button-secondary-border\",\n\t\t\t// Destructive - red filled button\n\t\t\tdestructive:\n\t\t\t\t\"bg-button-destructive-bg text-button-destructive-text hover:bg-button-destructive-bg-hover active:bg-button-destructive-bg-active border-transparent\",\n\t\t\t// Outline - bordered with transparent background\n\t\t\toutline:\n\t\t\t\t\"bg-button-outline-bg text-button-outline-text hover:bg-button-outline-bg-hover active:bg-button-outline-bg-active border border-button-outline-border hover:border-button-outline-border-hover\",\n\t\t\t// Ghost - transparent with subtle hover\n\t\t\tghost:\n\t\t\t\t\"bg-button-ghost-bg text-button-ghost-text hover:bg-button-ghost-bg-hover active:bg-button-ghost-bg-active border-transparent\",\n\t\t\t// Link - text only with underline on hover\n\t\t\tlink: \"bg-transparent text-button-link-text hover:text-button-link-text-hover hover:underline active:text-button-link-text-hover border-transparent underline-offset-4\",\n\t\t\t// Themed - uses CSS custom properties for styling\n\t\t\tthemed:\n\t\t\t\t\"[background:var(--btn-bg)] [color:var(--btn-text)] [border-color:var(--btn-border-color,transparent)] hover:[background:var(--btn-bg-hover,var(--btn-bg))] active:[background:var(--btn-bg-active,var(--btn-bg-hover,var(--btn-bg)))]\",\n\t\t},\n\t\tsize: {\n\t\t\tsm: \"typography-ui-text-xs h-spatial-ui-button-height-small px-spatial-ui-button-padding-x-small py-spatial-ui-button-padding-y-small rounded-surface-button-small\",\n\t\t\tdefault:\n\t\t\t\t\"typography-ui-text-sm h-spatial-ui-button-height-medium px-spatial-ui-button-padding-x-medium py-spatial-ui-button-padding-y-medium rounded-surface-button-medium\",\n\t\t\tlg: \"typography-ui-text-md h-spatial-ui-button-height-large px-spatial-ui-button-padding-x-large py-spatial-ui-button-padding-y-large rounded-surface-button-large\",\n\t\t},\n\t},\n\tdefaultVariants: {\n\t\tvariant: \"default\",\n\t\tsize: \"default\",\n\t},\n});\n\nexport type HTMLButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement>;\nexport type ButtonProps = BaseButtonProps &\n\tVariantProps<typeof buttonVariants> &\n\tHTMLButtonProps & {\n\t\t/**\n\t\t * Theme overrides for button styling via CSS custom properties\n\t\t */\n\t\ttheme?: ButtonTheme;\n\t};\n\n/**\n * Check if a ButtonTheme has any actual values set\n */\nfunction hasThemeValues(theme: ButtonTheme | undefined): boolean {\n\tif (!theme) return false;\n\treturn Object.values(theme).some((v) => v !== undefined && v !== null);\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n\t(\n\t\t{ className, variant, size, render, nativeButton, theme, style, ...props },\n\t\tref,\n\t) => {\n\t\t// When render prop is provided, default nativeButton to false to suppress warnings\n\t\tconst isNativeButton = nativeButton ?? render === undefined;\n\n\t\t// If theme has actual values, use \"themed\" variant to enable CSS custom property styling\n\t\tconst hasTheme = hasThemeValues(theme);\n\t\tconst effectiveVariant = hasTheme ? \"themed\" : variant;\n\t\tconst themeStyles = buttonThemeToStyleVars(theme);\n\t\tconst combinedStyles = hasTheme ? { ...themeStyles, ...style } : style;\n\n\t\t// Resolve actual values for data attributes\n\t\tconst resolvedVariant = effectiveVariant ?? \"default\";\n\t\tconst resolvedSize = size ?? \"default\";\n\n\t\treturn (\n\t\t\t<BaseButton\n\t\t\t\tclassName={buttonVariants({\n\t\t\t\t\tvariant: effectiveVariant,\n\t\t\t\t\tsize,\n\t\t\t\t\tclass: className,\n\t\t\t\t})}\n\t\t\t\tref={ref}\n\t\t\t\trender={render}\n\t\t\t\tnativeButton={isNativeButton}\n\t\t\t\tstyle={combinedStyles}\n\t\t\t\tdata-variant={resolvedVariant}\n\t\t\t\tdata-size={resolvedSize}\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t},\n);\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants };\n","\"use client\";\n\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\nimport { formControlBase } from \"@/lib/form-control\";\nimport { cn } from \"@/lib/utils\";\nimport { Button, type ButtonProps } from \"../button\";\n\n// =============================================================================\n// InputGroup Variants\n// =============================================================================\n\nconst inputGroupVariants = tv({\n\tbase: [\n\t\t...formControlBase,\n\t\t// InputGroup-specific: Container layout and styling\n\t\t\"group/input-group relative flex w-full items-center justify-start\",\n\t\t\"shadow-xs\",\n\t\t// Override focus state to work with child focus detection\n\t\t\"focus-visible:border-ui-color-border focus-visible:ring-0\",\n\t\t// Override hover to work with InputGroup structure\n\t\t\"hover:bg-ui-control-background\",\n\t\t// Height and layout (overridden by size variants)\n\t\t\"min-w-0\",\n\t\t\"has-[>textarea]:h-auto\",\n\t\t\"has-[>[data-align=inline-start]]:[&>input]:pl-6\",\n\t\t\"has-[>[data-align=inline-end]]:[&>input]:pr-6\",\n\t\t\"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pt-spatial-ui-input-group-gap-block has-[>[data-align=block-start]]:[&>input]:pb-spatial-ui-input-group-padding-y-medium\",\n\t\t\"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pb-spatial-ui-input-group-gap-block\",\n\t\t// Focus state detection on child input (overrides formControlBase focus)\n\t\t\"has-[[data-slot=input-group-control]:focus-visible]:border-ui-accent-base has-[[data-slot=input-group-control]:focus-visible]:ring-4 has-[[data-slot=input-group-control]:focus-visible]:ring-ui-color-focus\",\n\t\t// Error state detection on child elements\n\t\t\"has-[[data-slot][aria-invalid=true]]:border-ui-error-color has-[[data-slot][aria-invalid=true]]:ring-ui-error-color/20\",\n\t\t// Disabled state (overrides formControlBase disabled)\n\t\t\"data-[disabled=true]:bg-ui-control-background-disabled data-[disabled=true]:opacity-50 data-[disabled=true]:cursor-not-allowed\",\n\t],\n\tvariants: {\n\t\tsize: {\n\t\t\tsm: \"h-spatial-ui-input-group-height-small\",\n\t\t\tdefault: \"h-spatial-ui-input-group-height-medium\",\n\t\t\tlg: \"h-spatial-ui-input-group-height-large\",\n\t\t},\n\t},\n\tdefaultVariants: {\n\t\tsize: \"default\",\n\t},\n});\n\n// =============================================================================\n// InputGroupAddon Variants\n// =============================================================================\n\nconst inputGroupAddonVariants = tv({\n\tbase: [\n\t\t\"flex items-center justify-center gap-6\",\n\t\t\"typography-ui-text-sm text-text-muted\",\n\t\t\"select-none cursor-text\",\n\t\t\"[&>svg:not([class*='size-'])]:size-16\",\n\t\t\"[&_button]:text-[unset] [&_button]:cursor-pointer\",\n\t\t\"group-data-[disabled=true]/input-group:opacity-50\",\n\t],\n\tvariants: {\n\t\talign: {\n\t\t\t\"inline-start\": [\n\t\t\t\t\"order-first h-full\",\n\t\t\t\t\"pl-spatial-ui-input-group-padding-x-medium\",\n\t\t\t\t\"has-[>button]:ml-[-6px]\",\n\t\t\t],\n\t\t\t\"inline-end\": [\n\t\t\t\t\"order-last h-full\",\n\t\t\t\t\"pr-spatial-ui-input-group-padding-x-medium\",\n\t\t\t\t\"has-[>button]:mr-[-6px]\",\n\t\t\t],\n\t\t\t\"block-start\": [\n\t\t\t\t\"order-first h-auto w-full justify-start\",\n\t\t\t\t\"px-spatial-ui-input-group-padding-x-medium pt-spatial-ui-input-group-padding-y-medium\",\n\t\t\t\t\"[.border-b]:pb-spatial-ui-input-group-padding-y-medium\",\n\t\t\t\t// Use shared text style for block addons (12px, Regular weight per Figma)\n\t\t\t\t\"typography-ui-text-xs\",\n\t\t\t],\n\t\t\t\"block-end\": [\n\t\t\t\t\"order-last h-auto w-full justify-start\",\n\t\t\t\t\"px-spatial-ui-input-group-padding-x-medium pb-spatial-ui-input-group-padding-y-medium\",\n\t\t\t\t\"[.border-t]:pt-spatial-ui-input-group-padding-y-medium\",\n\t\t\t\t// Use shared text style for block addons (12px, Regular weight per Figma)\n\t\t\t\t\"typography-ui-text-xs\",\n\t\t\t],\n\t\t},\n\t},\n\tdefaultVariants: {\n\t\talign: \"inline-start\",\n\t},\n});\n\n// =============================================================================\n// InputGroup Component\n// =============================================================================\n\nexport interface InputGroupProps\n\textends React.FieldsetHTMLAttributes<HTMLFieldSetElement>,\n\t\tVariantProps<typeof inputGroupVariants> {}\n\n/**\n * InputGroup component for combining inputs with addons, buttons, and text.\n *\n * A container that groups an input with prefix/suffix addons, icons, or buttons.\n * Supports inline (left/right) and block (top/bottom) addon positioning.\n *\n * Uses semantic UI tokens for theming support.\n *\n * @example\n * ```tsx\n * // With prefix icon\n * <InputGroup>\n * <InputGroupAddon>\n * <SearchIcon />\n * </InputGroupAddon>\n * <InputGroupInput placeholder=\"Search...\" />\n * </InputGroup>\n *\n * // With suffix button\n * <InputGroup>\n * <InputGroupInput placeholder=\"Enter email\" />\n * <InputGroupAddon align=\"inline-end\">\n * <InputGroupButton>Subscribe</InputGroupButton>\n * </InputGroupAddon>\n * </InputGroup>\n *\n * // With text prefix\n * <InputGroup>\n * <InputGroupAddon>\n * <InputGroupText>https://</InputGroupText>\n * </InputGroupAddon>\n * <InputGroupInput placeholder=\"example.com\" />\n * </InputGroup>\n * ```\n */\nfunction InputGroup({ className, size, disabled, ...props }: InputGroupProps) {\n\treturn (\n\t\t<fieldset\n\t\t\tdata-slot=\"input-group\"\n\t\t\tdata-disabled={disabled || undefined}\n\t\t\tdisabled={disabled}\n\t\t\tclassName={cn(\n\t\t\t\t\"p-0 m-0 min-w-0\",\n\t\t\t\tinputGroupVariants({ size, class: className }),\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\n// =============================================================================\n// InputGroupAddon Component\n// =============================================================================\n\nexport interface InputGroupAddonProps\n\textends React.HTMLAttributes<HTMLDivElement>,\n\t\tVariantProps<typeof inputGroupAddonVariants> {}\n\n/**\n * InputGroupAddon component for positioning addons within an InputGroup.\n *\n * Can contain icons, text, or buttons. Clicking the addon focuses the input.\n *\n * @example\n * ```tsx\n * // Inline start (default - left side)\n * <InputGroupAddon>\n * <SearchIcon />\n * </InputGroupAddon>\n *\n * // Inline end (right side)\n * <InputGroupAddon align=\"inline-end\">\n * <InputGroupButton>Submit</InputGroupButton>\n * </InputGroupAddon>\n *\n * // Block positions (top/bottom)\n * <InputGroupAddon align=\"block-start\">\n * <label>Email Address</label>\n * </InputGroupAddon>\n * ```\n */\nfunction InputGroupAddon({\n\tclassName,\n\talign = \"inline-start\",\n\tonClick,\n\tonKeyDown,\n\t...props\n}: InputGroupAddonProps) {\n\tconst focusInput = (element: HTMLElement) => {\n\t\telement.parentElement?.querySelector(\"input\")?.focus();\n\t};\n\n\tconst handleClick = (e: React.MouseEvent<HTMLDivElement>) => {\n\t\t// Don't focus input if clicking a button inside the addon\n\t\tif ((e.target as HTMLElement).closest(\"button\")) {\n\t\t\tonClick?.(e);\n\t\t\treturn;\n\t\t}\n\t\t// Focus the input when clicking the addon\n\t\tfocusInput(e.currentTarget);\n\t\tonClick?.(e);\n\t};\n\n\tconst handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n\t\t// Focus input on Enter or Space (unless inside a button)\n\t\tif (\n\t\t\t(e.key === \"Enter\" || e.key === \" \") &&\n\t\t\t!(e.target as HTMLElement).closest(\"button\")\n\t\t) {\n\t\t\tfocusInput(e.currentTarget);\n\t\t}\n\t\tonKeyDown?.(e);\n\t};\n\n\treturn (\n\t\t// biome-ignore lint/a11y/noStaticElementInteractions: Click-to-focus is a convenience UX pattern; primary interaction is via the input itself\n\t\t<div\n\t\t\tdata-slot=\"input-group-addon\"\n\t\t\tdata-align={align}\n\t\t\tclassName={cn(inputGroupAddonVariants({ align, class: className }))}\n\t\t\tonClick={handleClick}\n\t\t\tonKeyDown={handleKeyDown}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\n// =============================================================================\n// InputGroupButton Component\n// =============================================================================\n\nconst inputGroupButtonVariants = tv({\n\tbase: [\n\t\t\"typography-ui-button-small shadow-none flex gap-6 items-center\",\n\t\t\"focus-visible:ring-1 focus-visible:ring-offset-0 focus-visible:ring-ui-color-border\",\n\t\t\"transition-opacity duration-150\",\n\t],\n\tvariants: {\n\t\tsize: {\n\t\t\txs: \"!h-24 gap-4 px-8 rounded-surface-ui-small [&>svg:not([class*='size-'])]:size-16 has-[>svg]:px-6\",\n\t\t\tsm: \"!h-28 px-10 gap-6 rounded-surface-ui-medium has-[>svg]:px-8\",\n\t\t\t\"icon-xs\":\n\t\t\t\t\"!size-24 rounded-surface-ui-small p-0 [&>svg:not([class*='size-'])]:size-16 has-[>svg]:p-0\",\n\t\t\t\"icon-sm\":\n\t\t\t\t\"!size-28 rounded-surface-ui-medium p-0 [&>svg:not([class*='size-'])]:size-16 has-[>svg]:p-0\",\n\t\t},\n\t},\n\tdefaultVariants: {\n\t\tsize: \"xs\",\n\t},\n});\n\nexport interface InputGroupButtonProps\n\textends Omit<ButtonProps, \"size\">,\n\t\tVariantProps<typeof inputGroupButtonVariants> {}\n\n/**\n * InputGroupButton component for inline buttons within an InputGroup.\n *\n * A small button variant designed to fit inside input groups.\n *\n * @example\n * ```tsx\n * <InputGroupAddon align=\"inline-end\">\n * <InputGroupButton>Submit</InputGroupButton>\n * </InputGroupAddon>\n *\n * // Icon button\n * <InputGroupAddon align=\"inline-end\">\n * <InputGroupButton size=\"icon-xs\">\n * <ClearIcon />\n * </InputGroupButton>\n * </InputGroupAddon>\n * ```\n */\nfunction InputGroupButton({\n\tclassName,\n\ttype = \"button\",\n\tvariant = \"ghost\",\n\tsize = \"xs\",\n\t...props\n}: InputGroupButtonProps) {\n\treturn (\n\t\t<Button\n\t\t\ttype={type}\n\t\t\tdata-size={size}\n\t\t\tvariant={variant}\n\t\t\tclassName={cn(inputGroupButtonVariants({ size, class: className }))}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\n// =============================================================================\n// InputGroupText Component\n// =============================================================================\n\nexport interface InputGroupTextProps\n\textends React.HTMLAttributes<HTMLSpanElement> {}\n\n/**\n * InputGroupText component for static text within an InputGroup.\n *\n * Use for prefixes like \"https://\" or suffixes like \".com\"\n *\n * @example\n * ```tsx\n * <InputGroupAddon>\n * <InputGroupText>https://</InputGroupText>\n * </InputGroupAddon>\n * ```\n */\nfunction InputGroupText({ className, ...props }: InputGroupTextProps) {\n\treturn (\n\t\t<span\n\t\t\tclassName={cn(\n\t\t\t\t\"flex items-center gap-6 typography-ui-text-sm text-text-muted\",\n\t\t\t\t\"[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-16\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\n// =============================================================================\n// InputGroupInput Component\n// =============================================================================\n\nexport interface InputGroupInputProps\n\textends React.InputHTMLAttributes<HTMLInputElement> {}\n\n/**\n * InputGroupInput component - the input element within an InputGroup.\n *\n * Styled to integrate seamlessly with the InputGroup container.\n *\n * @example\n * ```tsx\n * <InputGroup>\n * <InputGroupInput placeholder=\"Enter text...\" />\n * </InputGroup>\n * ```\n */\nfunction InputGroupInput({ className, ...props }: InputGroupInputProps) {\n\treturn (\n\t\t<input\n\t\t\tdata-slot=\"input-group-control\"\n\t\t\tclassName={cn(\n\t\t\t\t\"flex-1 min-w-0 h-full w-full\",\n\t\t\t\t\"border-0 bg-transparent shadow-none outline-none\",\n\t\t\t\t\"typography-ui-text-sm text-left placeholder:text-text-muted\",\n\t\t\t\t\"px-spatial-ui-input-group-padding-x-medium\",\n\t\t\t\t\"focus-visible:ring-0\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\n// =============================================================================\n// InputGroupTextarea Component\n// =============================================================================\n\nexport interface InputGroupTextareaProps\n\textends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}\n\n/**\n * InputGroupTextarea component - a textarea element within an InputGroup.\n *\n * Styled to integrate seamlessly with the InputGroup container.\n *\n * @example\n * ```tsx\n * <InputGroup>\n * <InputGroupTextarea placeholder=\"Enter long text...\" rows={4} />\n * </InputGroup>\n * ```\n */\nfunction InputGroupTextarea({ className, ...props }: InputGroupTextareaProps) {\n\treturn (\n\t\t<textarea\n\t\t\tdata-slot=\"input-group-control\"\n\t\t\tclassName={cn(\n\t\t\t\t\"flex-1 min-w-0 w-full resize-none\",\n\t\t\t\t\"border-0 bg-transparent shadow-none outline-none\",\n\t\t\t\t\"typography-ui-text-sm text-left placeholder:text-text-muted\",\n\t\t\t\t\"px-spatial-ui-input-group-padding-x-medium py-spatial-ui-input-group-padding-y-medium\",\n\t\t\t\t\"focus-visible:ring-0\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\nexport {\n\tInputGroup,\n\tInputGroupAddon,\n\tInputGroupButton,\n\tInputGroupText,\n\tInputGroupInput,\n\tInputGroupTextarea,\n\tinputGroupVariants,\n\tinputGroupAddonVariants,\n};\n"]}
@@ -0,0 +1,68 @@
1
+ import * as React from 'react';
2
+
3
+ interface NavbarProps extends React.HTMLAttributes<HTMLElement> {
4
+ }
5
+ /**
6
+ * Main navigation bar container.
7
+ * Provides responsive layout for brand, links, and actions.
8
+ */
9
+ declare const Navbar: React.ForwardRefExoticComponent<NavbarProps & React.RefAttributes<HTMLElement>>;
10
+ interface NavbarBrandProps extends React.HTMLAttributes<HTMLDivElement> {
11
+ asChild?: boolean;
12
+ }
13
+ /**
14
+ * Brand/logo area of the navbar.
15
+ * Use asChild to render as a link.
16
+ */
17
+ declare const NavbarBrand: React.ForwardRefExoticComponent<NavbarBrandProps & React.RefAttributes<HTMLDivElement>>;
18
+ interface NavbarLinksProps extends React.HTMLAttributes<HTMLDivElement> {
19
+ }
20
+ /**
21
+ * Container for navigation links.
22
+ * Centers links on desktop, hidden on mobile (use NavbarMobileMenu instead).
23
+ */
24
+ declare const NavbarLinks: React.ForwardRefExoticComponent<NavbarLinksProps & React.RefAttributes<HTMLDivElement>>;
25
+ interface NavbarLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
26
+ asChild?: boolean;
27
+ active?: boolean;
28
+ }
29
+ /**
30
+ * Individual navigation link.
31
+ * Use asChild to render with a router Link component.
32
+ */
33
+ declare const NavbarLink: React.ForwardRefExoticComponent<NavbarLinkProps & React.RefAttributes<HTMLAnchorElement>>;
34
+ interface NavbarActionsProps extends React.HTMLAttributes<HTMLDivElement> {
35
+ }
36
+ /**
37
+ * Container for navbar action items (search, menu button, etc).
38
+ */
39
+ declare const NavbarActions: React.ForwardRefExoticComponent<NavbarActionsProps & React.RefAttributes<HTMLDivElement>>;
40
+ interface NavbarMobileMenuProps extends React.HTMLAttributes<HTMLDivElement> {
41
+ }
42
+ /**
43
+ * Mobile menu container that displays navigation links on mobile devices.
44
+ * Hidden on desktop (md and above). Should be used with NavbarMobileMenuButton.
45
+ * Built on Base UI Dialog for accessibility (focus trap, escape key, click-outside).
46
+ */
47
+ declare const NavbarMobileMenu: React.ForwardRefExoticComponent<NavbarMobileMenuProps & React.RefAttributes<HTMLDivElement>>;
48
+ interface NavbarMobileMenuButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
49
+ asChild?: boolean;
50
+ }
51
+ /**
52
+ * Button to toggle the mobile menu.
53
+ * Should be placed in NavbarActions on mobile.
54
+ * Use asChild to render as a custom button component (e.g., IconButton).
55
+ */
56
+ declare const NavbarMobileMenuButton: React.ForwardRefExoticComponent<NavbarMobileMenuButtonProps & React.RefAttributes<HTMLButtonElement>>;
57
+ interface NavbarMobileMenuLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
58
+ asChild?: boolean;
59
+ active?: boolean;
60
+ }
61
+ /**
62
+ * Navigation link for the mobile menu.
63
+ * Automatically closes the mobile menu when clicked.
64
+ * Use asChild to render with a router Link component.
65
+ */
66
+ declare const NavbarMobileMenuLink: React.ForwardRefExoticComponent<NavbarMobileMenuLinkProps & React.RefAttributes<HTMLAnchorElement>>;
67
+
68
+ export { Navbar, NavbarActions, type NavbarActionsProps, NavbarBrand, type NavbarBrandProps, NavbarLink, type NavbarLinkProps, NavbarLinks, type NavbarLinksProps, NavbarMobileMenu, NavbarMobileMenuButton, type NavbarMobileMenuButtonProps, NavbarMobileMenuLink, type NavbarMobileMenuLinkProps, type NavbarMobileMenuProps, type NavbarProps };