@hexdspace/react 0.1.7 → 0.1.8

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.
@@ -95,11 +95,6 @@
95
95
  --color-warning: var(--warning);
96
96
  --color-danger: var(--danger);
97
97
 
98
- --color-info-soft: var(--info-soft);
99
- --color-success-soft: var(--success-soft);
100
- --color-warning-soft: var(--warning-soft);
101
- --color-danger-soft: var(--danger-soft);
102
-
103
98
  --color-on-info: var(--on-info);
104
99
  --color-on-success: var(--on-success);
105
100
  --color-on-warning: var(--on-warning);
@@ -1,2 +1,3 @@
1
1
  @import "./base-theme.css";
2
2
  @import "tailwindcss";
3
+ @config "../../../../tailwind.config.mjs";
@@ -19,6 +19,11 @@
19
19
  --shape-radius-50: 1.5rem; /* 24px */
20
20
  --shape-radius-pill: 999px;
21
21
 
22
+ /* Motion */
23
+ --motion-fast: 0.1s;
24
+ --motion-med: 0.16s;
25
+ --motion-slow: 0.24s;
26
+
22
27
  /* Neutral ramp */
23
28
  --ink-10: oklch(0.92 0.043 258);
24
29
  --ink-20: oklch(0.84 0.043 258);
@@ -34,15 +39,15 @@
34
39
  /* Accent */
35
40
  /* Accent (cool mint green) */
36
41
  --accent-10: oklch(0.93 0.028 160);
37
- --accent-20: oklch(0.90 0.038 160);
38
- --accent-30: oklch(0.87 0.052 160);
39
- --accent-40: oklch(0.84 0.068 160);
40
- --accent-50: oklch(0.80 0.085 160);
41
- --accent-60: oklch(0.74 0.105 160);
42
- --accent-70: oklch(0.66 0.125 160);
43
- --accent-80: oklch(0.58 0.135 160);
44
- --accent-90: oklch(0.50 0.125 160);
45
- --accent-100: oklch(0.42 0.105 160);
42
+ --accent-20: oklch(0.87 0.052 160);
43
+ --accent-30: oklch(0.84 0.068 160);
44
+ --accent-40: oklch(0.80 0.085 160);
45
+ --accent-50: oklch(0.74 0.105 160);
46
+ --accent-60: oklch(0.66 0.125 160);
47
+ --accent-70: oklch(0.58 0.135 160);
48
+ --accent-80: oklch(0.48 0.125 160);
49
+ --accent-90: oklch(0.42 0.105 160);
50
+ --accent-100: oklch(0.36 0.105 160);
46
51
 
47
52
  /* Brand ramp */
48
53
  --brand-10: oklch(0.97 0.020 250);
@@ -19,14 +19,16 @@
19
19
  --divider: color-mix(in oklab, var(--border), transparent 55%);
20
20
 
21
21
  /* Brand + accent roles */
22
- --brand: var(--brand-70);
23
- --brand-strong: var(--brand-90);
22
+ --brand: var(--brand-60);
23
+ --brand-strong: var(--brand-80);
24
+ --brand-contrast: var(--brand-90);
24
25
 
25
- --accent: var(--accent-70);
26
- --accent-strong: var(--accent-80);
26
+ --accent: var(--accent-50);
27
+ --accent-strong: var(--accent-70);
28
+ --accent-contrast: var(--accent-80);
27
29
 
28
30
  /* UI roles */
29
- --link: var(--accent);
31
+ --link: var(--accent-contrast);
30
32
  --link-hover: color-mix(in oklab, var(--link), white 14%);
31
33
  --focus: var(--brand);
32
34
  --focus-ring: 0 0 0 3px color-mix(in oklab, var(--focus), transparent 60%);
@@ -37,10 +39,10 @@
37
39
  --warning: var(--warning-60);
38
40
  --danger: var(--danger-60);
39
41
 
40
- --info-soft: color-mix(in oklab, var(--info), var(--bg) 85%);
41
- --success-soft: color-mix(in oklab, var(--success), var(--bg) 85%);
42
- --warning-soft: color-mix(in oklab, var(--warning), var(--bg) 85%);
43
- --danger-soft: color-mix(in oklab, var(--danger), var(--bg) 85%);
42
+ --info-contrast: var(--info-90);
43
+ --success-contrast: var(--success-80);
44
+ --warning-contrast: var(--warning-90);
45
+ --danger-contrast: var(--danger-80);
44
46
 
45
47
  /* On-colors */
46
48
  --on-info: oklch(0.16 0 0);
@@ -49,8 +51,8 @@
49
51
  --on-danger: oklch(0.16 0 0);
50
52
 
51
53
  /* Interaction tokens */
52
- --surface-hover: color-mix(in oklab, var(--surface-1), black 10%);
53
- --surface-active: color-mix(in oklab, var(--surface-1), black 12%);
54
+ --surface-hover: color-mix(in oklab, var(--surface-1), black 2%);
55
+ --surface-active: color-mix(in oklab, var(--surface-1), black 4%);
54
56
 
55
57
  /* Overlay */
56
58
  --overlay: oklch(0.16 0 0 / 0.40);
@@ -88,13 +90,15 @@
88
90
  --border: var(--ink-40);
89
91
  --divider: color-mix(in oklab, var(--border), transparent 40%);
90
92
 
91
- --brand: var(--brand-50);
92
- --brand-strong: var(--brand-40);
93
+ --brand: var(--brand-80);
94
+ --brand-strong: var(--brand-100);
95
+ --brand-contrast: var(--brand-50);
93
96
 
94
- --accent: var(--accent-70);
95
- --accent-strong: var(--accent-60);
97
+ --accent: var(--accent-80);
98
+ --accent-strong: var(--accent-100);
99
+ --accent-contrast: var(--accent-50);
96
100
 
97
- --link: var(--accent);
101
+ --link: var(--accent-contrast);
98
102
  --link-hover: color-mix(in oklab, var(--link), white 18%);
99
103
  --focus: var(--brand);
100
104
  --focus-ring: 0 0 0 3px color-mix(in oklab, var(--focus), transparent 60%);
@@ -104,6 +108,11 @@
104
108
  --warning: var(--warning-60);
105
109
  --danger: var(--danger-60);
106
110
 
111
+ --info-contrast: var(--info-50);
112
+ --success-contrast: var(--success-50);
113
+ --warning-contrast: var(--warning-50);
114
+ --danger-contrast: var(--danger-50);
115
+
107
116
  --surface-hover: color-mix(in oklab, var(--surface-1), white 6%);
108
117
  --surface-active: color-mix(in oklab, var(--surface-1), white 8%);
109
118
 
@@ -118,3 +127,7 @@
118
127
  --disabled-bg: color-mix(in oklab, var(--surface-2), transparent 35%);
119
128
  --disabled-border: color-mix(in oklab, var(--border), transparent 55%);
120
129
  }
130
+
131
+ @media (prefers-reduced-motion: reduce) {
132
+ transition-duration: 1ms; animation-duration: 1ms;
133
+ }
package/dist/index.d.ts CHANGED
@@ -352,17 +352,6 @@ type AuthControllerProviderProps = {
352
352
  };
353
353
  declare function AuthControllerProvider({ children, controller }: AuthControllerProviderProps): react_jsx_runtime.JSX.Element;
354
354
 
355
- interface InputProps {
356
- id: string;
357
- label: string;
358
- type: string;
359
- value: string;
360
- onChange: (e: React__default.ChangeEvent<HTMLInputElement>) => void;
361
- placeholder: string;
362
- className?: string;
363
- }
364
- declare const AuthFormInputField: React__default.FC<InputProps>;
365
-
366
355
  type RequireAuthProps = {
367
356
  guarded?: ReactNode;
368
357
  loadingPlaceholder?: ReactNode;
@@ -411,7 +400,54 @@ interface ButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>
411
400
  loading?: boolean;
412
401
  leftIcon?: React.ReactNode;
413
402
  rightIcon?: React.ReactNode;
403
+ reserveLeftIcon?: boolean;
404
+ reserveRightIcon?: boolean;
414
405
  }
415
406
  declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
416
407
 
417
- export { AuthController, AuthControllerCtx, type AuthControllerDeps, AuthControllerProvider, AuthDispatchCtx, AuthFormInputField, AuthProvider, type AuthState, AuthStateCtx, Button, type ButtonProps, type CacheInstruction, type CustomInstruction, DEFAULT_NOTIFICATION_CHANNEL, type ErrorResponse, type GenericResponse, type HttpClient, HttpError, type HttpMethod, type HttpResponse, type Instruction, type InstructionContext, MockAuthHttpClient, MockHttpClient, type MutationFn, type Notification, type NotificationAction, NotificationHost, type NotificationInstruction, type NotificationVariant, NotifierController, type OnSuccessFn, type OptimisticSnapshot, type OptimisticUpdateFn, RedirectIfAuthed, type RedirectIfAuthedProps, type RequestConfig, RequireAuth, type RequireAuthProps, type ResolvedToastTheme, type ResponsiveMutation, type ToastActionTheme, type ToastTheme, type ToastTransition, type ToastifyCSSVars, type UIFail, type UIOk, type UIResult, type User, authController, controllerFactory, createAuthController, httpClient as fetchHttpClient, notifierController, resolveToastTheme, ui, useAuth, useAuthActions, useAuthController, useAuthDispatch, useAuthedUser, useResponsiveMutation };
408
+ type ControlLikeProps = {
409
+ id?: string;
410
+ disabled?: boolean;
411
+ required?: boolean;
412
+ invalid?: boolean;
413
+ "aria-describedby"?: string;
414
+ };
415
+ interface FieldProps extends React.HTMLAttributes<HTMLDivElement> {
416
+ label?: React.ReactNode;
417
+ hint?: React.ReactNode;
418
+ error?: React.ReactNode;
419
+ required?: boolean;
420
+ disabled?: boolean;
421
+ id?: string;
422
+ children: React.ReactElement<ControlLikeProps>;
423
+ }
424
+ declare function Field({ label, hint, error, required, disabled, id, children, className, ...props }: FieldProps): react_jsx_runtime.JSX.Element;
425
+
426
+ declare const controlShellVariants: (props?: ({
427
+ variant?: "outline" | "ghost" | "surface" | null | undefined;
428
+ size?: "sm" | "md" | "lg" | null | undefined;
429
+ fullWidth?: boolean | null | undefined;
430
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
431
+ interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">, VariantProps<typeof controlShellVariants> {
432
+ invalid?: boolean;
433
+ leftSlot?: React.ReactNode;
434
+ rightSlot?: React.ReactNode;
435
+ reserveLeftSlot?: boolean;
436
+ reserveRightSlot?: boolean;
437
+ inputClassName?: string;
438
+ }
439
+ declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
440
+
441
+ declare const textareaShellVariants: (props?: ({
442
+ variant?: "outline" | "ghost" | "surface" | null | undefined;
443
+ size?: "sm" | "md" | "lg" | null | undefined;
444
+ fullWidth?: boolean | null | undefined;
445
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
446
+ interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement>, VariantProps<typeof textareaShellVariants> {
447
+ invalid?: boolean;
448
+ textareaClassName?: string;
449
+ autoResize?: boolean;
450
+ }
451
+ declare const Textarea: React.ForwardRefExoticComponent<TextareaProps & React.RefAttributes<HTMLTextAreaElement>>;
452
+
453
+ export { AuthController, AuthControllerCtx, type AuthControllerDeps, AuthControllerProvider, AuthDispatchCtx, AuthProvider, type AuthState, AuthStateCtx, Button, type ButtonProps, type CacheInstruction, type CustomInstruction, DEFAULT_NOTIFICATION_CHANNEL, type ErrorResponse, Field, type FieldProps, type GenericResponse, type HttpClient, HttpError, type HttpMethod, type HttpResponse, Input, type InputProps, type Instruction, type InstructionContext, MockAuthHttpClient, MockHttpClient, type MutationFn, type Notification, type NotificationAction, NotificationHost, type NotificationInstruction, type NotificationVariant, NotifierController, type OnSuccessFn, type OptimisticSnapshot, type OptimisticUpdateFn, RedirectIfAuthed, type RedirectIfAuthedProps, type RequestConfig, RequireAuth, type RequireAuthProps, type ResolvedToastTheme, type ResponsiveMutation, Textarea, type TextareaProps, type ToastActionTheme, type ToastTheme, type ToastTransition, type ToastifyCSSVars, type UIFail, type UIOk, type UIResult, type User, authController, controllerFactory, createAuthController, httpClient as fetchHttpClient, notifierController, resolveToastTheme, ui, useAuth, useAuthActions, useAuthController, useAuthDispatch, useAuthedUser, useResponsiveMutation };
package/dist/index.js CHANGED
@@ -897,42 +897,9 @@ function useAuthedUser() {
897
897
  return auth.user;
898
898
  }
899
899
 
900
- // src/feature/auth/interface/web/react/AuthFormInputField.tsx
901
- import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
902
- var inputLabelStyles = {
903
- display: "block",
904
- fontSize: "small",
905
- fontWeight: "bolder",
906
- marginBottom: "4px"
907
- };
908
- var inputFieldStyles = {
909
- display: "block",
910
- width: "100%",
911
- padding: "0.5rem 0.75rem"
912
- };
913
- var AuthFormInputField = ({ id, label, type, value, onChange, placeholder, className }) => {
914
- return /* @__PURE__ */ jsxs2("div", { children: [
915
- /* @__PURE__ */ jsx5("label", { htmlFor: id, style: inputLabelStyles, children: label }),
916
- /* @__PURE__ */ jsx5(
917
- "input",
918
- {
919
- id,
920
- name: id,
921
- type,
922
- required: true,
923
- value,
924
- onChange,
925
- className,
926
- placeholder,
927
- style: inputFieldStyles
928
- }
929
- )
930
- ] });
931
- };
932
-
933
900
  // src/feature/auth/interface/web/react/auth-route-guards.tsx
934
901
  import { Navigate, Outlet, useLocation } from "react-router-dom";
935
- import { Fragment, jsx as jsx6 } from "react/jsx-runtime";
902
+ import { Fragment, jsx as jsx5 } from "react/jsx-runtime";
936
903
  function RequireAuth({
937
904
  guarded,
938
905
  loadingPlaceholder,
@@ -943,18 +910,18 @@ function RequireAuth({
943
910
  const auth = useAuth();
944
911
  const location = useLocation();
945
912
  if (auth.status === "loading") {
946
- return /* @__PURE__ */ jsx6(Fragment, { children: loadingPlaceholder ?? null });
913
+ return /* @__PURE__ */ jsx5(Fragment, { children: loadingPlaceholder ?? null });
947
914
  }
948
915
  if (auth.status === "error") {
949
- return /* @__PURE__ */ jsx6(Fragment, { children: errorPlaceholder ?? null });
916
+ return /* @__PURE__ */ jsx5(Fragment, { children: errorPlaceholder ?? null });
950
917
  }
951
918
  if (auth.status === "unauthenticated") {
952
919
  if (unauthenticatedRedirectTo) {
953
- return /* @__PURE__ */ jsx6(Navigate, { to: unauthenticatedRedirectTo, replace: true, state: { from: location } });
920
+ return /* @__PURE__ */ jsx5(Navigate, { to: unauthenticatedRedirectTo, replace: true, state: { from: location } });
954
921
  }
955
- return /* @__PURE__ */ jsx6(Fragment, { children: unauthenticatedPlaceholder ?? null });
922
+ return /* @__PURE__ */ jsx5(Fragment, { children: unauthenticatedPlaceholder ?? null });
956
923
  }
957
- return guarded ? /* @__PURE__ */ jsx6(Fragment, { children: guarded }) : /* @__PURE__ */ jsx6(Outlet, {});
924
+ return guarded ? /* @__PURE__ */ jsx5(Fragment, { children: guarded }) : /* @__PURE__ */ jsx5(Outlet, {});
958
925
  }
959
926
  function RedirectIfAuthed({
960
927
  redirectTo,
@@ -965,18 +932,18 @@ function RedirectIfAuthed({
965
932
  }) {
966
933
  const auth = useAuth();
967
934
  if (auth.status === "loading") {
968
- return /* @__PURE__ */ jsx6(Fragment, { children: loadingPlaceholder ?? null });
935
+ return /* @__PURE__ */ jsx5(Fragment, { children: loadingPlaceholder ?? null });
969
936
  }
970
937
  if (auth.status === "error") {
971
- return /* @__PURE__ */ jsx6(Fragment, { children: errorPlaceholder ?? loadingPlaceholder ?? null });
938
+ return /* @__PURE__ */ jsx5(Fragment, { children: errorPlaceholder ?? loadingPlaceholder ?? null });
972
939
  }
973
940
  if (auth.status === "authenticated") {
974
941
  if (redirectTo) {
975
- return /* @__PURE__ */ jsx6(Navigate, { to: redirectTo, replace: true });
942
+ return /* @__PURE__ */ jsx5(Navigate, { to: redirectTo, replace: true });
976
943
  }
977
- return /* @__PURE__ */ jsx6(Fragment, { children: authenticatedPlaceholder ?? null });
944
+ return /* @__PURE__ */ jsx5(Fragment, { children: authenticatedPlaceholder ?? null });
978
945
  }
979
- return nonAuthedPlaceholder ? /* @__PURE__ */ jsx6(Fragment, { children: nonAuthedPlaceholder }) : /* @__PURE__ */ jsx6(Outlet, {});
946
+ return nonAuthedPlaceholder ? /* @__PURE__ */ jsx5(Fragment, { children: nonAuthedPlaceholder }) : /* @__PURE__ */ jsx5(Outlet, {});
980
947
  }
981
948
 
982
949
  // src/feature/auth/infra/mock-auth-http-client.ts
@@ -1064,23 +1031,22 @@ function cn(...inputs) {
1064
1031
  }
1065
1032
 
1066
1033
  // src/ui/components/Button.tsx
1067
- import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
1034
+ import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
1068
1035
  var buttonBase = cn(
1069
1036
  "relative isolate inline-flex items-center justify-center gap-2 leading-none text-sm font-medium select-none cursor-pointer",
1070
1037
  "rounded-[var(--radius-btn)]",
1071
- "transition-[background-color,color,box-shadow,transform] duration-150 ease",
1038
+ "transition-[background-color,color,box-shadow,transform] duration-[var(--motion-med)] ease",
1072
1039
  "focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)]",
1073
1040
  "[--state-hover:color-mix(in_oklab,currentColor,transparent_92%)]",
1074
1041
  "[--state-active:color-mix(in_oklab,currentColor,transparent_86%)]",
1075
- "after:content-[''] after:absolute after:inset-0 after:rounded-[inherit] after:bg-[color:var(--state-hover)] after:opacity-0 after:pointer-events-none after:transition-opacity after:duration-100 after:ease",
1076
- "hover:after:opacity-100 active:after:bg-[color:var(--state-active)] active:after:opacity-100",
1077
- "disabled:opacity-50 disabled:pointer-events-none disabled:cursor-not-allowed"
1042
+ "after:content-[''] after:absolute after:inset-0 after:rounded-[inherit] after:bg-[color:var(--state-hover)] after:opacity-0 after:pointer-events-none after:transition-opacity after:duration-[var(--motion-fast)] after:ease",
1043
+ "hover:after:opacity-100 active:after:bg-[color:var(--state-active)] active:after:opacity-100"
1078
1044
  );
1079
1045
  var flatChrome = cn();
1080
1046
  var pushChrome = cn(
1081
1047
  "border-b-[3px]",
1082
1048
  "transition-all",
1083
- "duration-100",
1049
+ "duration-[var(--motion-fast)]",
1084
1050
  "ease-out",
1085
1051
  "shadow-[0_2px_4px_rgba(0,0,0,0.28),0_8px_16px_rgba(0,0,0,0.14)]",
1086
1052
  "hover:shadow-[0_3px_6px_rgba(0,0,0,0.30),0_12px_22px_rgba(0,0,0,0.18)]",
@@ -1091,7 +1057,7 @@ var softChrome = cn(
1091
1057
  "font-thin",
1092
1058
  "border",
1093
1059
  "border-[color:color-mix(in_oklab,var(--border),transparent_80%)]",
1094
- "shadow-[0_0_8px_2px_rgba(255,255,255,0.35)_inset,0_3px_8px_rgba(0,0,0,0.14)]"
1060
+ "shadow-[0_0_2px_1px_rgba(255,255,255,0.35)_inset,0_2px_4px_rgba(0,0,0,0.14)]"
1095
1061
  );
1096
1062
  var insetChrome = cn(
1097
1063
  "border",
@@ -1099,7 +1065,7 @@ var insetChrome = cn(
1099
1065
  "shadow-[inset_0_2px_6px_rgba(0,0,0,0.18)]"
1100
1066
  );
1101
1067
  var glassChrome = cn(
1102
- "backdrop-blur-[6px]",
1068
+ "backdrop-blur-[2px]",
1103
1069
  "border",
1104
1070
  "shadow-[0_8px_18px_rgba(0,0,0,0.12)]"
1105
1071
  );
@@ -1118,8 +1084,8 @@ var hairlineChrome = cn(
1118
1084
  var buttonVariants = cva(buttonBase, {
1119
1085
  variants: {
1120
1086
  variant: {
1121
- primary: "bg-[color:var(--brand)] text-[color:var(--text-inverted)]",
1122
- accent: "bg-[color:var(--accent)] text-[color:var(--text-inverted)]",
1087
+ primary: "bg-[color:var(--brand)]",
1088
+ accent: "bg-[color:var(--accent)]",
1123
1089
  secondary: "border border-[color:var(--border)] bg-[color:var(--surface-2)]",
1124
1090
  outline: "bg-transparent border border-[color:var(--border)] shadow-none",
1125
1091
  ghost: "bg-transparent shadow-none",
@@ -1151,26 +1117,26 @@ var buttonVariants = cva(buttonBase, {
1151
1117
  { variant: "link", className: "border-0" },
1152
1118
  { chrome: "soft", className: "text-[color:var(--text-1)]" },
1153
1119
  { chrome: "glass", className: "text-[color:var(--text-1)]" },
1154
- { chrome: "push", variant: "primary", className: "border-b-[color:var(--brand-90)]" },
1155
- { chrome: "push", variant: "accent", className: "border-b-[color:var(--accent-90)]" },
1156
- { chrome: "push", variant: "secondary", className: "border-b-[color:color-mix(in oklab, var(--border), transparent 50%)]" },
1157
- { chrome: "push", variant: "outline", className: "border-b-[color:color-mix(in oklab, var(--border), transparent 50%)]" },
1120
+ { chrome: "push", variant: "primary", className: "border-b-[color:var(--brand-strong)]" },
1121
+ { chrome: "push", variant: "accent", className: "border-b-[color:var(--accent-strong)]" },
1122
+ { chrome: "push", variant: "secondary", className: "border-b-[color:color-mix(in_oklab,var(--border),transparent 50%)]" },
1123
+ { chrome: "push", variant: "outline", className: "border-b-[color:color-mix(in_oklab,var(--border),transparent 50%)]" },
1158
1124
  { chrome: "push", variant: "ghost", className: "border-b-transparent shadow-none" },
1159
1125
  { chrome: "push", variant: "success", className: "border-b-[color:var(--success-80)]" },
1160
1126
  { chrome: "push", variant: "info", className: "border-b-[color:var(--info-80)]" },
1161
1127
  { chrome: "push", variant: "warning", className: "border-b-[color:var(--warning-80)]" },
1162
1128
  { chrome: "push", variant: "danger", className: "border-b-[color:var(--danger-80)]" },
1163
1129
  { chrome: "push", variant: "link", className: "border-b-0 shadow-none" },
1164
- { chrome: "soft", variant: "primary", className: cn("bg-[color:color-mix(in_oklab,var(--brand),transparent_70%)]", "border-[color:color-mix(in_oklab,var(--brand),transparent_85%)]", "shadow-[0_0_8px_2px_rgba(255,255,255,0.2)_inset,0_3px_8px_color-mix(in_oklab,var(--brand),transparent_70%)]") },
1165
- { chrome: "soft", variant: "accent", className: cn("bg-[color:color-mix(in_oklab,var(--accent),transparent_70%)]", "border-[color:color-mix(in_oklab,var(--accent),transparent_85%)]", "shadow-[0_0_8px_2px_rgba(255,255,255,0.2)_inset,0_3px_8px_color-mix(in_oklab,var(--accent),transparent_70%)]") },
1166
- { chrome: "soft", variant: "secondary", className: cn("bg-[color:color-mix(in_oklab,var(--surface-2),transparent_25%)]", "border-[color:color-mix(in_oklab,var(--border),transparent_45%)]", "shadow-[0_0_8px_2px_rgba(255,255,255,0.1)_inset,0_3px_8px_color-mix(in_oklab,var(--border),transparent_70%)]") },
1167
- { chrome: "soft", variant: "outline", className: cn("border-[color:color-mix(in_oklab,var(--border),transparent_45%)]", "shadow-[0_0_8px_2px_rgba(255,255,255,0.1)_inset,0_3px_8px_color-mix(in_oklab,var(--border),transparent_70%)]") },
1168
- { chrome: "soft", variant: "ghost", className: "shadow-[0_3px_8px_color-mix(in_oklab,var(--border),transparent_75%)]" },
1169
- { chrome: "soft", variant: "success", className: cn("bg-[color:color-mix(in_oklab,var(--success),transparent_70%)]", "border-[color:color-mix(in_oklab,var(--success),transparent_75%)]", "shadow-[0_0_8px_2px_rgba(255,255,255,0.2)_inset,0_3px_8px_color-mix(in_oklab,var(--success),transparent_70%)]") },
1170
- { chrome: "soft", variant: "info", className: cn("bg-[color:color-mix(in_oklab,var(--info),transparent_70%)]", "border-[color:color-mix(in_oklab,var(--info),transparent_75%)]", "shadow-[0_0_8px_2px_rgba(255,255,255,0.2)_inset,0_3px_8px_color-mix(in_oklab,var(--info),transparent_70%)]") },
1171
- { chrome: "soft", variant: "warning", className: cn("bg-[color:color-mix(in_oklab,var(--warning),transparent_70%)]", "border-[color:color-mix(in_oklab,var(--warning),transparent_75%)]", "shadow-[0_0_8px_2px_rgba(255,255,255,0.2)_inset,0_3px_8px_color-mix(in_oklab,var(--warning),transparent_70%)]") },
1172
- { chrome: "soft", variant: "danger", className: cn("bg-[color:color-mix(in_oklab,var(--danger),transparent_70%)]", "border-[color:color-mix(in_oklab,var(--danger),transparent_75%)]", "shadow-[0_0_8px_2px_rgba(255,255,255,0.2)_inset,0_3px_8px_color-mix(in_oklab,var(--danger),transparent_70%)]") },
1173
- { chrome: "soft", variant: "link", className: cn("text-[color:var(--link)]", "shadow-[0_3px_8px_color-mix(in_oklab,var(--link),transparent_75%)]") },
1130
+ { chrome: "soft", variant: "primary", className: cn("bg-[color:color-mix(in_oklab,var(--brand),transparent_70%)]", "border-[color:color-mix(in_oklab,var(--brand),transparent_85%)]", "shadow-[0_0_2px_1px_rgba(255,255,255,0.2)_inset,0_2px_4px_color-mix(in_oklab,var(--brand),transparent_70%)]") },
1131
+ { chrome: "soft", variant: "accent", className: cn("bg-[color:color-mix(in_oklab,var(--accent),transparent_70%)]", "border-[color:color-mix(in_oklab,var(--accent),transparent_85%)]", "shadow-[0_0_2px_1px_rgba(255,255,255,0.2)_inset,0_2px_4px_color-mix(in_oklab,var(--accent),transparent_70%)]") },
1132
+ { chrome: "soft", variant: "secondary", className: cn("bg-[color:color-mix(in_oklab,var(--surface-2),transparent_25%)]", "border-[color:color-mix(in_oklab,var(--border),transparent_45%)]", "shadow-[0_0_2px_1px_rgba(255,255,255,0.1)_inset,0_2px_4px_color-mix(in_oklab,var(--border),transparent_70%)]") },
1133
+ { chrome: "soft", variant: "outline", className: cn("border-[color:color-mix(in_oklab,var(--border),transparent_45%)]", "shadow-[0_0_2px_1px_rgba(255,255,255,0.1)_inset,0_2px_4px_color-mix(in_oklab,var(--border),transparent_70%)]") },
1134
+ { chrome: "soft", variant: "ghost", className: "shadow-[0_2px_4px_color-mix(in_oklab,var(--border),transparent_75%)]" },
1135
+ { chrome: "soft", variant: "success", className: cn("bg-[color:color-mix(in_oklab,var(--success),transparent_70%)]", "border-[color:color-mix(in_oklab,var(--success),transparent_75%)]", "shadow-[0_0_2px_1px_rgba(255,255,255,0.2)_inset,0_2px_4px_color-mix(in_oklab,var(--success),transparent_70%)]") },
1136
+ { chrome: "soft", variant: "info", className: cn("bg-[color:color-mix(in_oklab,var(--info),transparent_70%)]", "border-[color:color-mix(in_oklab,var(--info),transparent_75%)]", "shadow-[0_0_2px_1px_rgba(255,255,255,0.2)_inset,0_2px_4px_color-mix(in_oklab,var(--info),transparent_70%)]") },
1137
+ { chrome: "soft", variant: "warning", className: cn("bg-[color:color-mix(in_oklab,var(--warning),transparent_70%)]", "border-[color:color-mix(in_oklab,var(--warning),transparent_75%)]", "shadow-[0_0_2px_1px_rgba(255,255,255,0.2)_inset,0_2px_4px_color-mix(in_oklab,var(--warning),transparent_70%)]") },
1138
+ { chrome: "soft", variant: "danger", className: cn("bg-[color:color-mix(in_oklab,var(--danger),transparent_70%)]", "border-[color:color-mix(in_oklab,var(--danger),transparent_75%)]", "shadow-[0_0_2px_1px_rgba(255,255,255,0.2)_inset,0_2px_4px_color-mix(in_oklab,var(--danger),transparent_70%)]") },
1139
+ { chrome: "soft", variant: "link", className: cn("text-[color:var(--link)]", "shadow-[0_2px_4px_color-mix(in_oklab,var(--link),transparent_75%)]") },
1174
1140
  { chrome: "glass", variant: "primary", className: cn("bg-[color:color-mix(in_oklab,var(--brand),transparent_65%)]", "border-[color:var(--brand)]") },
1175
1141
  { chrome: "glass", variant: "accent", className: cn("bg-[color:color-mix(in_oklab,var(--accent),transparent_65%)]", "border-[color:var(--accent)]") },
1176
1142
  { chrome: "glass", variant: "secondary", className: cn("bg-[color:color-mix(in_oklab,var(--surface-2),transparent_35%)]", "border-[color:color-mix(in_oklab,var(--border),transparent_70%)]") },
@@ -1191,19 +1157,21 @@ var buttonVariants = cva(buttonBase, {
1191
1157
  { chrome: "glow", variant: "warning", className: cn("border-[color:var(--warning)]", "shadow-[0_0_0_1px_color-mix(in_oklab,var(--warning),transparent_20%),0_10px_26px_color-mix(in_oklab,var(--warning),transparent_55%)]", "hover:shadow-[0_0_0_1px_color-mix(in_oklab,var(--warning),transparent_10%),0_14px_34px_color-mix(in_oklab,var(--warning),transparent_40%)]", "dark:shadow-[0_0_0_1px_color-mix(in_oklab,var(--warning),transparent_30%),0_8px_22px_color-mix(in_oklab,var(--warning),transparent_70%)]", "dark:hover:shadow-[0_0_0_1px_color-mix(in_oklab,var(--warning),transparent_15%),0_12px_30px_color-mix(in_oklab,var(--warning),transparent_55%)]") },
1192
1158
  { chrome: "glow", variant: "danger", className: cn("border-[color:var(--danger)]", "shadow-[0_0_0_1px_color-mix(in_oklab,var(--danger),transparent_20%),0_10px_26px_color-mix(in_oklab,var(--danger),transparent_55%)]", "hover:shadow-[0_0_0_1px_color-mix(in_oklab,var(--danger),transparent_10%),0_14px_34px_color-mix(in_oklab,var(--danger),transparent_40%)]", "dark:shadow-[0_0_0_1px_color-mix(in_oklab,var(--danger),transparent_30%),0_8px_22px_color-mix(in_oklab,var(--danger),transparent_70%)]", "dark:hover:shadow-[0_0_0_1px_color-mix(in_oklab,var(--danger),transparent_15%),0_12px_30px_color-mix(in_oklab,var(--danger),transparent_55%)]") },
1193
1159
  { chrome: "glow", variant: "link", className: "text-[color:var(--link)] shadow-none border-transparent" },
1194
- { chrome: "hairline", variant: "primary", className: "border-[color:color-mix(in_oklab,var(--brand),transparent_20%)] text-[color:var(--brand)]" },
1195
- { chrome: "hairline", variant: "accent", className: "border-[color:color-mix(in_oklab,var(--accent),transparent_20%)] text-[color:var(--accent)]" },
1160
+ { chrome: "hairline", variant: "primary", className: "border-[color:color-mix(in_oklab,var(--brand-contrast),transparent_20%)] text-[color:var(--brand-contrast)]" },
1161
+ { chrome: "hairline", variant: "accent", className: "border-[color:color-mix(in_oklab,var(--accent-contrast),transparent_20%)] text-[color:var(--accent-contrast)]" },
1196
1162
  { chrome: "hairline", variant: "secondary", className: "border-[color:color-mix(in_oklab,var(--border),transparent_20%)] text-[color:var(--text-1)]" },
1197
1163
  { chrome: "hairline", variant: "outline", className: "border-[color:color-mix(in_oklab,var(--border),transparent_20%)] text-[color:var(--text-1)]" },
1198
1164
  { chrome: "hairline", variant: "ghost", className: "border-transparent text-[color:var(--text-1)]" },
1199
- { chrome: "hairline", variant: "success", className: "border-[color:color-mix(in_oklab,var(--success),transparent_20%)] text-[color:var(--success)]" },
1200
- { chrome: "hairline", variant: "info", className: "border-[color:color-mix(in_oklab,var(--info),transparent_20%)] text-[color:var(--info)]" },
1201
- { chrome: "hairline", variant: "warning", className: "border-[color:color-mix(in_oklab,var(--warning),transparent_20%)] text-[color:var(--warning)]" },
1202
- { chrome: "hairline", variant: "danger", className: "border-[color:color-mix(in_oklab,var(--danger),transparent_20%)] text-[color:var(--danger)]" },
1165
+ { chrome: "hairline", variant: "success", className: "border-[color:color-mix(in_oklab,var(--success),transparent_20%)] text-[color:var(--success-contrast)]" },
1166
+ { chrome: "hairline", variant: "info", className: "border-[color:color-mix(in_oklab,var(--info),transparent_20%)] text-[color:var(--info-contrast)]" },
1167
+ { chrome: "hairline", variant: "warning", className: "border-[color:color-mix(in_oklab,var(--warning),transparent_20%)] text-[color:var(--warning-contrast)]" },
1168
+ { chrome: "hairline", variant: "danger", className: "border-[color:color-mix(in_oklab,var(--danger),transparent_20%)] text-[color:var(--danger-contrast)]" },
1203
1169
  { chrome: "hairline", variant: "link", className: "text-[color:var(--link)] border-transparent" }
1204
1170
  ],
1205
1171
  defaultVariants: { variant: "primary", size: "md", chrome: "flat", fullWidth: false }
1206
1172
  });
1173
+ var iconSlotBase = cn("inline-flex items-center justify-center", "shrink-0");
1174
+ var iconSlotReserve = "min-w-4 min-h-4";
1207
1175
  var Button = React3.forwardRef(
1208
1176
  ({
1209
1177
  asChild = false,
@@ -1216,6 +1184,8 @@ var Button = React3.forwardRef(
1216
1184
  disabled,
1217
1185
  leftIcon,
1218
1186
  rightIcon,
1187
+ reserveLeftIcon,
1188
+ reserveRightIcon,
1219
1189
  children,
1220
1190
  onClick,
1221
1191
  type,
@@ -1227,19 +1197,19 @@ var Button = React3.forwardRef(
1227
1197
  isDisabled && "opacity-50 pointer-events-none cursor-not-allowed",
1228
1198
  className
1229
1199
  );
1230
- const content = /* @__PURE__ */ jsxs3(Fragment2, { children: [
1231
- loading ? /* @__PURE__ */ jsx7(
1232
- "span",
1233
- {
1234
- "aria-hidden": "true",
1235
- className: "inline-block h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent"
1236
- }
1237
- ) : leftIcon,
1238
- children,
1239
- rightIcon
1240
- ] });
1200
+ const showLeftSlot = Boolean(loading || leftIcon || reserveLeftIcon);
1201
+ const showRightSlot = Boolean(rightIcon || reserveRightIcon);
1202
+ const spinner = /* @__PURE__ */ jsx6(
1203
+ "span",
1204
+ {
1205
+ "aria-hidden": "true",
1206
+ className: "inline-block h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent"
1207
+ }
1208
+ );
1209
+ const leftSlot = showLeftSlot ? /* @__PURE__ */ jsx6("span", { className: cn(iconSlotBase, reserveLeftIcon && iconSlotReserve), children: loading ? spinner : leftIcon }) : null;
1210
+ const rightSlot = showRightSlot ? /* @__PURE__ */ jsx6("span", { className: cn(iconSlotBase, reserveRightIcon && iconSlotReserve), children: rightIcon }) : null;
1241
1211
  if (asChild) {
1242
- return /* @__PURE__ */ jsx7(
1212
+ return /* @__PURE__ */ jsxs2(
1243
1213
  Slot.Root,
1244
1214
  {
1245
1215
  ref,
@@ -1257,11 +1227,15 @@ var Button = React3.forwardRef(
1257
1227
  onClick?.(e);
1258
1228
  },
1259
1229
  ...props,
1260
- children: content
1230
+ children: [
1231
+ leftSlot,
1232
+ /* @__PURE__ */ jsx6(Slot.Slottable, { children }),
1233
+ rightSlot
1234
+ ]
1261
1235
  }
1262
1236
  );
1263
1237
  }
1264
- return /* @__PURE__ */ jsx7(
1238
+ return /* @__PURE__ */ jsxs2(
1265
1239
  "button",
1266
1240
  {
1267
1241
  ref,
@@ -1272,29 +1246,318 @@ var Button = React3.forwardRef(
1272
1246
  "data-loading": loading ? "" : void 0,
1273
1247
  onClick,
1274
1248
  ...props,
1275
- children: content
1249
+ children: [
1250
+ leftSlot,
1251
+ children,
1252
+ rightSlot
1253
+ ]
1276
1254
  }
1277
1255
  );
1278
1256
  }
1279
1257
  );
1280
1258
  Button.displayName = "Button";
1259
+
1260
+ // src/ui/components/Field.tsx
1261
+ import * as React5 from "react";
1262
+
1263
+ // src/ui/components/Label.tsx
1264
+ import * as React4 from "react";
1265
+ import { jsx as jsx7 } from "react/jsx-runtime";
1266
+ var Label = React4.forwardRef(
1267
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx7(
1268
+ "label",
1269
+ {
1270
+ ref,
1271
+ className: cn(
1272
+ "inline-flex items-center gap-2 text-sm font-medium text-[color:var(--text-1)]",
1273
+ className
1274
+ ),
1275
+ ...props
1276
+ }
1277
+ )
1278
+ );
1279
+ Label.displayName = "Label";
1280
+
1281
+ // src/ui/components/Field.tsx
1282
+ import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
1283
+ function Field({ label, hint, error, required, disabled, id, children, className, ...props }) {
1284
+ const reactId = React5.useId();
1285
+ const controlId = id ?? `field-${reactId}`;
1286
+ const hintId = hint ? `${controlId}-hint` : void 0;
1287
+ const errorId = error ? `${controlId}-error` : void 0;
1288
+ const childInvalid = Boolean(children.props.invalid);
1289
+ const invalid = Boolean(error) || childInvalid;
1290
+ const describedByParts = [
1291
+ children.props["aria-describedby"],
1292
+ hintId,
1293
+ errorId
1294
+ ].filter(Boolean);
1295
+ const ariaDescribedBy = describedByParts.length ? describedByParts.join(" ") : void 0;
1296
+ const control = React5.cloneElement(children, {
1297
+ id: controlId,
1298
+ disabled: disabled ?? children.props.disabled,
1299
+ required: required ?? children.props.required,
1300
+ invalid,
1301
+ "aria-describedby": ariaDescribedBy
1302
+ });
1303
+ return /* @__PURE__ */ jsxs3("div", { className: cn("grid gap-1.5", className), ...props, children: [
1304
+ label ? /* @__PURE__ */ jsx8("div", { className: "flex items-center justify-between gap-3", children: /* @__PURE__ */ jsxs3(Label, { htmlFor: controlId, children: [
1305
+ label,
1306
+ required ? /* @__PURE__ */ jsx8("span", { "aria-hidden": "true", className: "text-(--muted)", children: " * " }) : null
1307
+ ] }) }) : null,
1308
+ control,
1309
+ error ? /* @__PURE__ */ jsx8(
1310
+ "p",
1311
+ {
1312
+ id: errorId,
1313
+ className: "text-xs leading-5 text-(--danger-contrast)",
1314
+ role: "alert",
1315
+ children: error
1316
+ }
1317
+ ) : hint ? /* @__PURE__ */ jsx8("p", { id: hintId, className: "text-xs leading-5 text-(--muted)", children: hint }) : null
1318
+ ] });
1319
+ }
1320
+
1321
+ // src/ui/components/Input.tsx
1322
+ import * as React6 from "react";
1323
+ import { cva as cva2 } from "class-variance-authority";
1324
+ import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
1325
+ var controlShellBase = cn(
1326
+ "relative inline-flex items-center gap-2",
1327
+ "rounded-[var(--radius-input)]",
1328
+ "border border-[color:color-mix(in_oklab,var(--border),transparent_55%)]",
1329
+ "text-[color:var(--text-1)]",
1330
+ "transition-[background-color,border-color,box-shadow] duration-[var(--motion-med)] ease",
1331
+ "[--control-focus:var(--focus)]",
1332
+ "focus-within:shadow-[0_0_0_3px_color-mix(in_oklab,var(--control-focus),transparent_60%)]",
1333
+ "focus-within:border-[color:var(--control-focus)]",
1334
+ "data-[invalid]:[--control-focus:var(--danger)]",
1335
+ "data-[invalid]:border-[color:color-mix(in_oklab,var(--danger),transparent_20%)]",
1336
+ "data-[disabled]:bg-[color:var(--disabled-bg)]",
1337
+ "data-[disabled]:text-[color:var(--disabled-fg)]",
1338
+ "data-[disabled]:border-[color:var(--disabled-border)]",
1339
+ "data-[disabled]:cursor-not-allowed"
1340
+ );
1341
+ var controlShellVariants = cva2(controlShellBase, {
1342
+ variants: {
1343
+ variant: {
1344
+ surface: cn(
1345
+ "bg-[color:var(--surface-2)]",
1346
+ "hover:bg-[color:var(--surface-hover)]"
1347
+ ),
1348
+ outline: cn(
1349
+ "bg-transparent",
1350
+ "hover:bg-[color:color-mix(in_oklab,var(--surface-2),transparent_40%)]",
1351
+ "border-[color:color-mix(in_oklab,var(--border),transparent_35%)]"
1352
+ ),
1353
+ ghost: cn(
1354
+ "bg-transparent",
1355
+ "border-[color:transparent]",
1356
+ "hover:bg-[color:color-mix(in_oklab,var(--surface-2),transparent_55%)]",
1357
+ "focus-within:border-[color:color-mix(in_oklab,var(--border),transparent_35%)]"
1358
+ )
1359
+ },
1360
+ size: {
1361
+ sm: "h-8 px-3 text-sm [--input-slot-size:2rem]",
1362
+ md: "h-10 px-3.5 text-sm [--input-slot-size:2.5rem]",
1363
+ lg: "h-11 px-4 text-base [--input-slot-size:2.75rem]"
1364
+ },
1365
+ fullWidth: { true: "w-full", false: "w-auto" }
1366
+ },
1367
+ defaultVariants: { variant: "surface", size: "md", fullWidth: false }
1368
+ });
1369
+ var inputBase = cn(
1370
+ "min-w-0 flex-1 bg-transparent",
1371
+ "outline-none border-0 ring-0",
1372
+ "placeholder:text-[color:color-mix(in_oklab,var(--muted),transparent_20%)]",
1373
+ "disabled:cursor-not-allowed",
1374
+ "read-only:cursor-default"
1375
+ );
1376
+ var slotBase = cn(
1377
+ "inline-flex items-center justify-center",
1378
+ "text-[color:var(--muted)]",
1379
+ "data-[disabled]:text-[color:var(--disabled-fg)]"
1380
+ );
1381
+ var slotReserve = "min-w-[var(--input-slot-size)]";
1382
+ var Input = React6.forwardRef(
1383
+ ({
1384
+ variant,
1385
+ size,
1386
+ fullWidth,
1387
+ invalid,
1388
+ leftSlot,
1389
+ rightSlot,
1390
+ reserveLeftSlot,
1391
+ reserveRightSlot,
1392
+ className,
1393
+ inputClassName,
1394
+ disabled,
1395
+ ...props
1396
+ }, ref) => {
1397
+ const isDisabled = Boolean(disabled);
1398
+ const showLeftSlot = Boolean(leftSlot || reserveLeftSlot);
1399
+ const showRightSlot = Boolean(rightSlot || reserveRightSlot);
1400
+ return /* @__PURE__ */ jsxs4(
1401
+ "div",
1402
+ {
1403
+ className: cn(controlShellVariants({ variant, size, fullWidth }), className),
1404
+ "data-invalid": invalid ? "" : void 0,
1405
+ "data-disabled": isDisabled ? "" : void 0,
1406
+ children: [
1407
+ showLeftSlot ? /* @__PURE__ */ jsx9(
1408
+ "span",
1409
+ {
1410
+ className: cn(slotBase, reserveLeftSlot && slotReserve),
1411
+ "data-disabled": isDisabled ? "" : void 0,
1412
+ children: leftSlot
1413
+ }
1414
+ ) : null,
1415
+ /* @__PURE__ */ jsx9(
1416
+ "input",
1417
+ {
1418
+ ref,
1419
+ disabled: isDisabled,
1420
+ "aria-invalid": invalid || void 0,
1421
+ className: cn(inputBase, inputClassName),
1422
+ ...props
1423
+ }
1424
+ ),
1425
+ showRightSlot ? /* @__PURE__ */ jsx9(
1426
+ "span",
1427
+ {
1428
+ className: cn(slotBase, reserveRightSlot && slotReserve),
1429
+ "data-disabled": isDisabled ? "" : void 0,
1430
+ children: rightSlot
1431
+ }
1432
+ ) : null
1433
+ ]
1434
+ }
1435
+ );
1436
+ }
1437
+ );
1438
+ Input.displayName = "Input";
1439
+
1440
+ // src/ui/components/Textarea.tsx
1441
+ import * as React7 from "react";
1442
+ import { cva as cva3 } from "class-variance-authority";
1443
+ import { jsx as jsx10 } from "react/jsx-runtime";
1444
+ var controlShellBase2 = cn(
1445
+ "relative inline-flex w-full",
1446
+ "rounded-[var(--radius-input)]",
1447
+ "border border-[color:color-mix(in_oklab,var(--border),transparent_55%)]",
1448
+ "text-[color:var(--text-1)]",
1449
+ "transition-[background-color,border-color,box-shadow] duration-[var(--motion-med)] ease",
1450
+ "[--control-focus:var(--focus)]",
1451
+ "focus-within:shadow-[0_0_0_3px_color-mix(in_oklab,var(--control-focus),transparent_60%)]",
1452
+ "focus-within:border-[color:var(--control-focus)]",
1453
+ "data-[invalid]:[--control-focus:var(--danger)]",
1454
+ "data-[invalid]:border-[color:color-mix(in_oklab,var(--danger),transparent_20%)]",
1455
+ "data-[disabled]:bg-[color:var(--disabled-bg)]",
1456
+ "data-[disabled]:text-[color:var(--disabled-fg)]",
1457
+ "data-[disabled]:border-[color:var(--disabled-border)]",
1458
+ "data-[disabled]:cursor-not-allowed"
1459
+ );
1460
+ var textareaShellVariants = cva3(controlShellBase2, {
1461
+ variants: {
1462
+ variant: {
1463
+ surface: cn(
1464
+ "bg-[color:var(--surface-2)]",
1465
+ "hover:bg-[color:var(--surface-hover)]"
1466
+ ),
1467
+ outline: cn(
1468
+ "bg-transparent",
1469
+ "hover:bg-[color:color-mix(in_oklab,var(--surface-2),transparent_40%)]",
1470
+ "border-[color:color-mix(in_oklab,var(--border),transparent_35%)]"
1471
+ ),
1472
+ ghost: cn(
1473
+ "bg-transparent",
1474
+ "border-[color:transparent]",
1475
+ "hover:bg-[color:color-mix(in_oklab,var(--surface-2),transparent_55%)]",
1476
+ "focus-within:border-[color:color-mix(in_oklab,var(--border),transparent_35%)]"
1477
+ )
1478
+ },
1479
+ size: {
1480
+ sm: "p-3 text-sm",
1481
+ md: "p-3.5 text-sm",
1482
+ lg: "p-4 text-base"
1483
+ },
1484
+ fullWidth: { true: "w-full", false: "w-auto" }
1485
+ },
1486
+ defaultVariants: { variant: "surface", size: "md", fullWidth: true }
1487
+ });
1488
+ var textareaBase = cn(
1489
+ "min-w-0 w-full bg-transparent",
1490
+ "outline-none border-0 ring-0",
1491
+ "resize-y",
1492
+ "placeholder:text-[color:color-mix(in_oklab,var(--muted),transparent_20%)]",
1493
+ "disabled:cursor-not-allowed",
1494
+ "read-only:cursor-default"
1495
+ );
1496
+ var Textarea = React7.forwardRef(
1497
+ ({
1498
+ variant,
1499
+ size,
1500
+ fullWidth,
1501
+ invalid,
1502
+ className,
1503
+ textareaClassName,
1504
+ disabled,
1505
+ autoResize,
1506
+ onInput,
1507
+ rows = 4,
1508
+ ...props
1509
+ }, ref) => {
1510
+ const isDisabled = Boolean(disabled);
1511
+ const handleInput = (e) => {
1512
+ if (autoResize) {
1513
+ const el = e.currentTarget;
1514
+ el.style.height = "auto";
1515
+ el.style.height = `${el.scrollHeight}px`;
1516
+ }
1517
+ onInput?.(e);
1518
+ };
1519
+ return /* @__PURE__ */ jsx10(
1520
+ "div",
1521
+ {
1522
+ className: cn(textareaShellVariants({ variant, size, fullWidth }), className),
1523
+ "data-invalid": invalid ? "" : void 0,
1524
+ "data-disabled": isDisabled ? "" : void 0,
1525
+ children: /* @__PURE__ */ jsx10(
1526
+ "textarea",
1527
+ {
1528
+ ref,
1529
+ disabled: isDisabled,
1530
+ "aria-invalid": invalid || void 0,
1531
+ className: cn(textareaBase, textareaClassName),
1532
+ rows,
1533
+ onInput: handleInput,
1534
+ ...props
1535
+ }
1536
+ )
1537
+ }
1538
+ );
1539
+ }
1540
+ );
1541
+ Textarea.displayName = "Textarea";
1281
1542
  export {
1282
1543
  AuthController,
1283
1544
  AuthControllerCtx,
1284
1545
  AuthControllerProvider,
1285
1546
  AuthDispatchCtx,
1286
- AuthFormInputField,
1287
1547
  AuthProvider,
1288
1548
  AuthStateCtx,
1289
1549
  Button,
1290
1550
  DEFAULT_NOTIFICATION_CHANNEL,
1551
+ Field,
1291
1552
  HttpError,
1553
+ Input,
1292
1554
  MockAuthHttpClient,
1293
1555
  MockHttpClient,
1294
1556
  NotificationHost,
1295
1557
  NotifierController,
1296
1558
  RedirectIfAuthed,
1297
1559
  RequireAuth,
1560
+ Textarea,
1298
1561
  authController,
1299
1562
  controllerFactory,
1300
1563
  createAuthController,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexdspace/react",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -52,8 +52,7 @@
52
52
  "@tailwindcss/vite": "^4.1.18",
53
53
  "@tanstack/react-query": "^5.90.11",
54
54
  "@types/react": "^19.2.7",
55
- "@vitest/browser": "^3.2.4",
56
- "@vitest/coverage-v8": "^3.2.4",
55
+ "@vitest/browser-playwright": "^4.0.16",
57
56
  "autoprefixer": "^10.4.22",
58
57
  "lucide-react": "^0.555.0",
59
58
  "playwright": "^1.57.0",