@hexdspace/react 0.0.27 → 0.1.1

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.
package/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # @hexdspace/react
2
+
3
+ React basic features, UI components plus a Tailwind v4-compatible theme CSS.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ pnpm add @hexdspace/react
9
+ ```
10
+
11
+ Peer dependencies (install in your app):
12
+
13
+ ```sh
14
+ pnpm add react react-dom react-router-dom radix-ui lucide-react react-toastify @tanstack/react-query
15
+ ```
16
+
17
+ Tailwind requirements (for the theme + utilities):
18
+
19
+ ```sh
20
+ pnpm add -D tailwindcss @tailwindcss/postcss postcss-import tailwind-scrollbar
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ Import the theme into your Tailwind entry stylesheet (order matters):
26
+
27
+ ```css
28
+ /* src/index.css */
29
+ @import "tailwindcss";
30
+ @import "@hexdspace/react/css";
31
+ @config "../tailwind.config.ts"; /* adjust path if needed */
32
+
33
+ /* Your app styles */
34
+ ```
35
+
36
+ Make sure PostCSS inlines imports before Tailwind:
37
+
38
+ ```js
39
+ // postcss.config.js
40
+ export default {
41
+ plugins: {
42
+ "postcss-import": {},
43
+ "@tailwindcss/postcss": {},
44
+ },
45
+ };
46
+ ```
47
+
48
+ ### Tailwind config
49
+
50
+ Point Tailwind at this package so it can generate utilities used by the components.
51
+
52
+ For a published package:
53
+
54
+ ```js
55
+ // tailwind.config.js (app)
56
+ export default {
57
+ content: [
58
+ "./index.html",
59
+ "./src/**/*.{ts,tsx,js,jsx}",
60
+ "./node_modules/@hexdspace/react/dist/**/*.{js,ts,tsx}",
61
+ ],
62
+ theme: {
63
+ extend: {},
64
+ },
65
+ };
66
+ ```
67
+
68
+ For a monorepo/workspace (use sources instead of dist):
69
+
70
+ ```js
71
+ // tailwind.config.js (app)
72
+ export default {
73
+ content: [
74
+ "./index.html",
75
+ "./src/**/*.{ts,tsx,js,jsx}",
76
+ "../packages/react/src/**/*.{ts,tsx}",
77
+ ],
78
+ };
79
+ ```
80
+
81
+ ### Component usage
82
+
83
+ ```tsx
84
+ import { Button } from "@hexdspace/react";
85
+
86
+ export function Example() {
87
+ return (
88
+ <Button variant="outline" chrome="push">
89
+ Click me
90
+ </Button>
91
+ );
92
+ }
93
+ ```
94
+
95
+ ## Troubleshooting
96
+
97
+ - Theme styles apply but component utilities are missing:
98
+ - Ensure your Tailwind entry includes `@config` so Tailwind loads `tailwind.config.*`.
99
+ - Ensure the `content` array includes this package (dist or source).
100
+ - `tailwind-scrollbar` styles are missing:
101
+ - Install `tailwind-scrollbar` (the theme registers it via `@plugin`).
@@ -2,7 +2,6 @@
2
2
  @import url('https://fonts.googleapis.com/css2?family=Fira+Sans:wght@400;600;700&display=swap');
3
3
  @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap');
4
4
 
5
- @import 'tailwindcss';
6
5
  @import './primitive.css';
7
6
  @import './semantic.css';
8
7
  @import './component.css';
@@ -115,32 +115,60 @@
115
115
 
116
116
  @layer utilities {
117
117
  .brand-gradient {
118
- background: linear-gradient(90deg, var(--accent-strong) 0%, var(--accent) 30%);
118
+ background: linear-gradient(90deg, var(--brand-strong) 0%, var(--brand) 30%);
119
119
  -webkit-background-clip: text;
120
120
  -webkit-text-fill-color: transparent;
121
121
  }
122
122
 
123
123
  .btn {
124
- @apply state-layer inline-flex items-center justify-center gap-2 px-3.5 py-2.5
125
- text-sm font-medium select-none cursor-pointer
126
- transition-[background,color,box-shadow,transform] duration-150;
124
+ @apply inline-flex items-center justify-center gap-2 leading-none
125
+ state-layer text-sm font-medium select-none cursor-pointer;
127
126
  position: relative;
128
127
  border-radius: var(--radius-btn);
129
128
  box-shadow: var(--shadow-10);
129
+ transition-property: background-color, color, box-shadow, transform;
130
+ transition-duration: 150ms;
131
+ transition-timing-function: ease;
132
+ }
133
+
134
+ .btn.shadow-none {
135
+ box-shadow: none;
130
136
  }
131
137
 
132
138
  .btn:disabled, .btn.disabled {
133
139
  opacity: 0.5;
134
140
  pointer-events: none;
141
+ cursor: not-allowed;
135
142
  }
136
143
 
137
144
  .btn-primary {
138
- background: var(--accent);
145
+ background: var(--brand);
139
146
  color: var(--text-inverted);
140
147
  }
141
148
 
142
149
  .btn-secondary {
143
150
  border: 1px solid var(--border);
151
+ background: var(--surface-2);
152
+ }
153
+
154
+ .btn-success {
155
+ background: var(--success);
156
+ color: var(--on-success);
157
+ }
158
+
159
+ .btn-info {
160
+ background: var(--info);
161
+ color: var(--on-info);
162
+ }
163
+
164
+ .btn-warning {
165
+ background: var(--warning);
166
+ color: var(--on-warning);
167
+ }
168
+
169
+ .btn-danger {
170
+ background: var(--danger);
171
+ color: var(--on-danger);
144
172
  }
145
173
 
146
174
  :root .prose code {
@@ -0,0 +1,2 @@
1
+ @import "./base-theme.css";
2
+ @import "tailwindcss";
@@ -4,8 +4,8 @@
4
4
  :root {
5
5
  /* Surfaces */
6
6
  --bg: var(--ink-10);
7
- --surface-1: color-mix(in oklab, var(--ink-10), black 12%);
8
- --surface-2: color-mix(in oklab, var(--ink-10), black 8%);
7
+ --surface-1: color-mix(in oklab, var(--ink-10), black 8%);
8
+ --surface-2: color-mix(in oklab, var(--ink-10), black 6%);
9
9
  --surface-3: color-mix(in oklab, var(--ink-10), black 4%);
10
10
 
11
11
  /* Typography */
package/dist/index.d.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  import * as _tanstack_react_query from '@tanstack/react-query';
2
2
  import { QueryClient, QueryKey } from '@tanstack/react-query';
3
3
  import { ResultOk, ResultError, AsyncResult, Result } from '@hexdspace/util';
4
- import * as react from 'react';
5
- import react__default, { CSSProperties, Dispatch, ReactNode } from 'react';
4
+ import * as React from 'react';
5
+ import React__default, { CSSProperties, Dispatch, ReactNode } from 'react';
6
6
  import * as react_jsx_runtime from 'react/jsx-runtime';
7
+ import * as class_variance_authority_types from 'class-variance-authority/types';
8
+ import { VariantProps } from 'class-variance-authority';
7
9
 
8
10
  type NotificationVariant = 'success' | 'warning' | 'error' | 'info';
9
11
  declare const DEFAULT_NOTIFICATION_CHANNEL = "app.notifications";
@@ -120,7 +122,7 @@ type NotificationHostProps = {
120
122
  isDark?: () => boolean;
121
123
  theme?: ToastTheme;
122
124
  };
123
- declare const NotificationHost: react__default.FC<NotificationHostProps>;
125
+ declare const NotificationHost: React__default.FC<NotificationHostProps>;
124
126
 
125
127
  type InstructionContext = {
126
128
  queryClient: QueryClient;
@@ -337,13 +339,13 @@ type AuthAction = {
337
339
 
338
340
  declare function useAuthDispatch(): Dispatch<AuthAction>;
339
341
 
340
- declare const AuthStateCtx: react.Context<AuthState | null>;
341
- declare const AuthDispatchCtx: react.Context<Dispatch<AuthAction> | null>;
342
+ declare const AuthStateCtx: React.Context<AuthState | null>;
343
+ declare const AuthDispatchCtx: React.Context<Dispatch<AuthAction> | null>;
342
344
  declare function AuthProvider({ children }: {
343
345
  readonly children: ReactNode;
344
346
  }): react_jsx_runtime.JSX.Element;
345
347
 
346
- declare const AuthControllerCtx: react.Context<AuthController | null>;
348
+ declare const AuthControllerCtx: React.Context<AuthController | null>;
347
349
  type AuthControllerProviderProps = {
348
350
  readonly children: ReactNode;
349
351
  readonly controller: AuthController;
@@ -355,11 +357,11 @@ interface InputProps {
355
357
  label: string;
356
358
  type: string;
357
359
  value: string;
358
- onChange: (e: react__default.ChangeEvent<HTMLInputElement>) => void;
360
+ onChange: (e: React__default.ChangeEvent<HTMLInputElement>) => void;
359
361
  placeholder: string;
360
362
  className?: string;
361
363
  }
362
- declare const AuthFormInputField: react__default.FC<InputProps>;
364
+ declare const AuthFormInputField: React__default.FC<InputProps>;
363
365
 
364
366
  type RequireAuthProps = {
365
367
  guarded?: ReactNode;
@@ -397,4 +399,19 @@ declare class MockAuthHttpClient extends MockHttpClient {
397
399
  private registerAuthRoutes;
398
400
  }
399
401
 
400
- export { AuthController, AuthControllerCtx, type AuthControllerDeps, AuthControllerProvider, AuthDispatchCtx, AuthFormInputField, AuthProvider, type AuthState, AuthStateCtx, 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 };
402
+ declare const buttonVariants: (props?: ({
403
+ variant?: "success" | "warning" | "info" | "link" | "primary" | "secondary" | "outline" | "ghost" | "danger" | null | undefined;
404
+ size?: "icon" | "sm" | "md" | "lg" | null | undefined;
405
+ chrome?: "push" | "flat" | "inset" | "soft" | "glass" | "glow" | "hairline" | null | undefined;
406
+ fullWidth?: boolean | null | undefined;
407
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
408
+ interface ButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "children">, VariantProps<typeof buttonVariants> {
409
+ asChild?: boolean;
410
+ children?: React.ReactNode;
411
+ loading?: boolean;
412
+ leftIcon?: React.ReactNode;
413
+ rightIcon?: React.ReactNode;
414
+ }
415
+ declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
416
+
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 };
package/dist/index.js CHANGED
@@ -1050,6 +1050,215 @@ var MockAuthHttpClient = class extends MockHttpClient {
1050
1050
  });
1051
1051
  }
1052
1052
  };
1053
+
1054
+ // src/ui/components/Button.tsx
1055
+ import * as React3 from "react";
1056
+ import { Slot } from "radix-ui";
1057
+ import { cva } from "class-variance-authority";
1058
+
1059
+ // src/ui/utils/cn.ts
1060
+ import { clsx } from "clsx";
1061
+ import { twMerge } from "tailwind-merge";
1062
+ function cn(...inputs) {
1063
+ return twMerge(clsx(inputs));
1064
+ }
1065
+
1066
+ // src/ui/components/Button.tsx
1067
+ import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
1068
+ var pushChrome = cn(
1069
+ "!border-b-[4px]",
1070
+ "!transition-all",
1071
+ "!duration-100",
1072
+ "ease-out",
1073
+ "!shadow-[0_2px_4px_rgba(0,0,0,0.28),0_8px_16px_rgba(0,0,0,0.14)]",
1074
+ "hover:!shadow-[0_3px_6px_rgba(0,0,0,0.30),0_12px_22px_rgba(0,0,0,0.18)]",
1075
+ "hover:-translate-y-[2px]",
1076
+ "active:translate-y-[1px]"
1077
+ );
1078
+ var softChrome = cn(
1079
+ "border",
1080
+ "!border-[color:color-mix(in_oklab,var(--border),transparent_20%)]",
1081
+ "!shadow-[0_1px_0_rgba(255,255,255,0.65)_inset,0_3px_8px_rgba(0,0,0,0.14)]"
1082
+ );
1083
+ var insetChrome = cn(
1084
+ "border",
1085
+ "!border-[color:color-mix(in_oklab,var(--border),transparent_35%)]",
1086
+ "!shadow-[inset_0_2px_6px_rgba(0,0,0,0.18)]"
1087
+ );
1088
+ var glassChrome = cn(
1089
+ "!backdrop-blur-[6px]",
1090
+ "!border",
1091
+ "!shadow-[0_8px_18px_rgba(0,0,0,0.12)]"
1092
+ );
1093
+ var glowChrome = cn(
1094
+ "!border",
1095
+ "!shadow-[0_0_0_1px_color-mix(in_oklab,var(--border),transparent_40%),0_6px_16px_color-mix(in_oklab,var(--border),transparent_70%)]",
1096
+ "hover:!shadow-[0_0_0_1px_color-mix(in_oklab,var(--border),transparent_25%),0_10px_26px_color-mix(in_oklab,var(--border),transparent_60%)]"
1097
+ );
1098
+ var hairlineChrome = cn(
1099
+ "!border",
1100
+ "!border-[color:color-mix(in_oklab,var(--border),transparent_60%)]",
1101
+ "!bg-transparent",
1102
+ "!font-thin",
1103
+ "!shadow-none"
1104
+ );
1105
+ var buttonVariants = cva("btn", {
1106
+ variants: {
1107
+ variant: {
1108
+ primary: "btn-primary",
1109
+ secondary: "btn-secondary text-[var(--text-1)]",
1110
+ outline: "bg-transparent text-[var(--text-1)] border border-[var(--border)] shadow-none",
1111
+ ghost: "bg-transparent text-[var(--text-1)] shadow-none",
1112
+ success: "btn-success",
1113
+ info: "btn-info",
1114
+ warning: "btn-warning",
1115
+ danger: "btn-danger",
1116
+ link: "bg-transparent p-0 h-auto shadow-none text-[var(--link)] hover:underline hover:text-[var(--link-hover)]"
1117
+ },
1118
+ size: {
1119
+ sm: "px-3 py-1.5",
1120
+ md: "px-3.5 py-2.5",
1121
+ lg: "px-4.5 py-3 !text-base",
1122
+ icon: "h-10 w-10 p-0"
1123
+ },
1124
+ chrome: {
1125
+ flat: "",
1126
+ push: pushChrome,
1127
+ soft: softChrome,
1128
+ inset: insetChrome,
1129
+ glass: glassChrome,
1130
+ glow: glowChrome,
1131
+ hairline: hairlineChrome
1132
+ },
1133
+ fullWidth: { true: "w-full", false: "" }
1134
+ },
1135
+ compoundVariants: [
1136
+ { variant: "ghost", className: "!border-0" },
1137
+ { variant: "link", className: "!border-0" },
1138
+ { chrome: "soft", className: "!text-[var(--text-1)]" },
1139
+ { chrome: "glass", className: "!text-[var(--text-1)]" },
1140
+ { chrome: "push", variant: "primary", className: "border-b-[color:var(--brand-90)]" },
1141
+ { chrome: "push", variant: "secondary", className: "border-b-[color:color-mix(in oklab, var(--border), transparent 50%)]" },
1142
+ { chrome: "push", variant: "outline", className: "border-b-[color:color-mix(in oklab, var(--border), transparent 50%)]" },
1143
+ { chrome: "push", variant: "ghost", className: "border-b-transparent !shadow-none" },
1144
+ { chrome: "push", variant: "success", className: "border-b-[color:var(--success-80)]" },
1145
+ { chrome: "push", variant: "info", className: "border-b-[color:var(--info-80)]" },
1146
+ { chrome: "push", variant: "warning", className: "border-b-[color:var(--warning-80)]" },
1147
+ { chrome: "push", variant: "danger", className: "border-b-[color:var(--danger-80)]" },
1148
+ { chrome: "push", variant: "link", className: "border-b-0 !shadow-none" },
1149
+ { 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_55%)]", "!shadow-[0_1px_0_rgba(255,255,255,0.5)_inset,0_3px_8px_color-mix(in_oklab,var(--brand),transparent_70%)]") },
1150
+ { 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_15%)]", "!shadow-[0_1px_0_rgba(255,255,255,0.5)_inset,0_3px_8px_color-mix(in_oklab,var(--border),transparent_70%)]") },
1151
+ { chrome: "soft", variant: "outline", className: cn("!border-[color:color-mix(in_oklab,var(--border),transparent_15%)]", "!shadow-[0_1px_0_rgba(255,255,255,0.5)_inset,0_3px_8px_color-mix(in_oklab,var(--border),transparent_70%)]") },
1152
+ { chrome: "soft", variant: "ghost", className: "!shadow-[0_3px_8px_color-mix(in_oklab,var(--border),transparent_75%)]" },
1153
+ { 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_55%)]", "!shadow-[0_1px_0_rgba(255,255,255,0.5)_inset,0_3px_8px_color-mix(in_oklab,var(--success),transparent_70%)]") },
1154
+ { 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_55%)]", "!shadow-[0_1px_0_rgba(255,255,255,0.5)_inset,0_3px_8px_color-mix(in_oklab,var(--info),transparent_70%)]") },
1155
+ { 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_55%)]", "!shadow-[0_1px_0_rgba(255,255,255,0.5)_inset,0_3px_8px_color-mix(in_oklab,var(--warning),transparent_70%)]") },
1156
+ { 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_55%)]", "!shadow-[0_1px_0_rgba(255,255,255,0.5)_inset,0_3px_8px_color-mix(in_oklab,var(--danger),transparent_70%)]") },
1157
+ { chrome: "soft", variant: "link", className: cn("!text-[var(--link)]", "!shadow-[0_3px_8px_color-mix(in_oklab,var(--link),transparent_75%)]") },
1158
+ { chrome: "glass", variant: "primary", className: cn("!bg-[color:color-mix(in_oklab,var(--brand),transparent_65%)]", "!border-[var(--brand)]") },
1159
+ { 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%)]") },
1160
+ { chrome: "glass", variant: "outline", className: "" },
1161
+ { chrome: "glass", variant: "ghost", className: "" },
1162
+ { chrome: "glass", variant: "success", className: cn("!bg-[color:color-mix(in_oklab,var(--success),transparent_65%)]", "!border-[var(--success)]") },
1163
+ { chrome: "glass", variant: "info", className: cn("!bg-[color:color-mix(in_oklab,var(--info),transparent_65%)]", "!border-[var(--info)]") },
1164
+ { chrome: "glass", variant: "warning", className: cn("!bg-[color:color-mix(in_oklab,var(--warning),transparent_65%)]", "!border-[var(--warning)]") },
1165
+ { chrome: "glass", variant: "danger", className: cn("!bg-[color:color-mix(in_oklab,var(--danger),transparent_65%)]", "!border-[var(--danger)]") },
1166
+ { chrome: "glass", variant: "link", className: "!text-[var(--link)]" },
1167
+ { chrome: "glow", variant: "primary", className: cn("!border-[var(--brand)]", "!shadow-[0_0_0_1px_color-mix(in_oklab,var(--brand),transparent_20%),0_10px_26px_color-mix(in_oklab,var(--brand),transparent_55%)]", "hover:!shadow-[0_0_0_1px_color-mix(in_oklab,var(--brand),transparent_10%),0_14px_34px_color-mix(in_oklab,var(--brand),transparent_40%)]", "dark:!shadow-[0_0_0_1px_color-mix(in_oklab,var(--brand),transparent_30%),0_8px_22px_color-mix(in_oklab,var(--brand),transparent_70%)]", "dark:hover:!shadow-[0_0_0_1px_color-mix(in_oklab,var(--brand),transparent_15%),0_12px_30px_color-mix(in_oklab,var(--brand),transparent_55%)]") },
1168
+ { chrome: "glow", variant: "secondary", className: cn("!border-[color:color-mix(in_oklab,var(--border),transparent_20%)]", "!shadow-[0_0_0_1px_color-mix(in_oklab,var(--border),transparent_20%),0_10px_26px_color-mix(in_oklab,var(--border),transparent_60%)]", "hover:!shadow-[0_0_0_1px_color-mix(in_oklab,var(--border),transparent_15%),0_14px_34px_color-mix(in_oklab,var(--border),transparent_45%)]", "dark:!shadow-[0_0_0_1px_color-mix(in_oklab,var(--border),transparent_30%),0_8px_22px_color-mix(in_oklab,var(--border),transparent_75%)]", "dark:hover:!shadow-[0_0_0_1px_color-mix(in_oklab,var(--border),transparent_20%),0_12px_30px_color-mix(in_oklab,var(--border),transparent_60%)]") },
1169
+ { chrome: "glow", variant: "outline", className: cn("!border-[color:color-mix(in_oklab,var(--border),transparent_20%)]", "!shadow-[0_0_0_1px_color-mix(in_oklab,var(--border),transparent_20%),0_10px_26px_color-mix(in_oklab,var(--border),transparent_60%)]", "hover:!shadow-[0_0_0_1px_color-mix(in_oklab,var(--border),transparent_15%),0_14px_34px_color-mix(in_oklab,var(--border),transparent_45%)]", "dark:!shadow-[0_0_0_1px_color-mix(in_oklab,var(--border),transparent_30%),0_8px_22px_color-mix(in_oklab,var(--border),transparent_75%)]", "dark:hover:!shadow-[0_0_0_1px_color-mix(in_oklab,var(--border),transparent_20%),0_12px_30px_color-mix(in_oklab,var(--border),transparent_60%)]") },
1170
+ { chrome: "glow", variant: "ghost", className: "!shadow-none !border-transparent" },
1171
+ { chrome: "glow", variant: "success", className: cn("!border-[var(--success)]", "!shadow-[0_0_0_1px_color-mix(in_oklab,var(--success),transparent_20%),0_10px_26px_color-mix(in_oklab,var(--success),transparent_55%)]", "hover:!shadow-[0_0_0_1px_color-mix(in_oklab,var(--success),transparent_10%),0_14px_34px_color-mix(in_oklab,var(--success),transparent_40%)]", "dark:!shadow-[0_0_0_1px_color-mix(in_oklab,var(--success),transparent_30%),0_8px_22px_color-mix(in_oklab,var(--success),transparent_70%)]", "dark:hover:!shadow-[0_0_0_1px_color-mix(in_oklab,var(--success),transparent_15%),0_12px_30px_color-mix(in_oklab,var(--success),transparent_55%)]") },
1172
+ { chrome: "glow", variant: "info", className: cn("!border-[var(--info)]", "!shadow-[0_0_0_1px_color-mix(in_oklab,var(--info),transparent_20%),0_10px_26px_color-mix(in_oklab,var(--info),transparent_55%)]", "hover:!shadow-[0_0_0_1px_color-mix(in_oklab,var(--info),transparent_10%),0_14px_34px_color-mix(in_oklab,var(--info),transparent_40%)]", "dark:!shadow-[0_0_0_1px_color-mix(in_oklab,var(--info),transparent_30%),0_8px_22px_color-mix(in_oklab,var(--info),transparent_70%)]", "dark:hover:!shadow-[0_0_0_1px_color-mix(in_oklab,var(--info),transparent_15%),0_12px_30px_color-mix(in_oklab,var(--info),transparent_55%)]") },
1173
+ { chrome: "glow", variant: "warning", className: cn("!border-[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%)]") },
1174
+ { chrome: "glow", variant: "danger", className: cn("!border-[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%)]") },
1175
+ { chrome: "glow", variant: "link", className: "!text-[var(--link)] !shadow-none !border-transparent" },
1176
+ { chrome: "hairline", variant: "primary", className: "!border-[color:color-mix(in_oklab,var(--brand),transparent_20%)] !text-[var(--brand)]" },
1177
+ { chrome: "hairline", variant: "secondary", className: "!border-[color:color-mix(in_oklab,var(--border),transparent_20%)] !text-[var(--text-1)]" },
1178
+ { chrome: "hairline", variant: "outline", className: "!border-[color:color-mix(in_oklab,var(--border),transparent_20%)] !text-[var(--text-1)]" },
1179
+ { chrome: "hairline", variant: "ghost", className: "!border-transparent !text-[var(--text-1)]" },
1180
+ { chrome: "hairline", variant: "success", className: "!border-[color:color-mix(in_oklab,var(--success),transparent_20%)] !text-[var(--success)]" },
1181
+ { chrome: "hairline", variant: "info", className: "!border-[color:color-mix(in_oklab,var(--info),transparent_20%)] !text-[var(--info)]" },
1182
+ { chrome: "hairline", variant: "warning", className: "!border-[color:color-mix(in_oklab,var(--warning),transparent_20%)] !text-[var(--warning)]" },
1183
+ { chrome: "hairline", variant: "danger", className: "!border-[color:color-mix(in_oklab,var(--danger),transparent_20%)] !text-[var(--danger)]" },
1184
+ { chrome: "hairline", variant: "link", className: "!text-[var(--link)] !border-transparent" }
1185
+ ],
1186
+ defaultVariants: { variant: "primary", size: "md", chrome: "flat", fullWidth: false }
1187
+ });
1188
+ var Button = React3.forwardRef(
1189
+ ({
1190
+ asChild = false,
1191
+ variant,
1192
+ size,
1193
+ chrome,
1194
+ fullWidth,
1195
+ className,
1196
+ loading = false,
1197
+ disabled,
1198
+ leftIcon,
1199
+ rightIcon,
1200
+ children,
1201
+ onClick,
1202
+ type,
1203
+ ...props
1204
+ }, ref) => {
1205
+ const isDisabled = disabled || loading;
1206
+ const classes = cn(buttonVariants({ variant, size, chrome, fullWidth }), className);
1207
+ const hasText = React3.Children.count(children) > 0;
1208
+ const content = /* @__PURE__ */ jsxs3(Fragment2, { children: [
1209
+ loading ? /* @__PURE__ */ jsx7(
1210
+ "span",
1211
+ {
1212
+ "aria-hidden": "true",
1213
+ className: [
1214
+ "inline-block h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent",
1215
+ hasText ? "mr-2" : ""
1216
+ ].join(" ")
1217
+ }
1218
+ ) : leftIcon,
1219
+ hasText ? /* @__PURE__ */ jsx7("span", { children }) : null,
1220
+ hasText ? rightIcon : null
1221
+ ] });
1222
+ if (asChild) {
1223
+ return /* @__PURE__ */ jsx7(
1224
+ Slot.Root,
1225
+ {
1226
+ ref,
1227
+ className: classes,
1228
+ "aria-disabled": isDisabled || void 0,
1229
+ "aria-busy": loading || void 0,
1230
+ "data-loading": loading ? "" : void 0,
1231
+ tabIndex: isDisabled ? -1 : void 0,
1232
+ onClick: (e) => {
1233
+ if (isDisabled) {
1234
+ e.preventDefault?.();
1235
+ e.stopPropagation?.();
1236
+ return;
1237
+ }
1238
+ onClick?.(e);
1239
+ },
1240
+ ...props,
1241
+ children: content
1242
+ }
1243
+ );
1244
+ }
1245
+ return /* @__PURE__ */ jsx7(
1246
+ "button",
1247
+ {
1248
+ ref,
1249
+ type: type ?? "button",
1250
+ className: classes,
1251
+ disabled: isDisabled,
1252
+ "aria-busy": loading || void 0,
1253
+ "data-loading": loading ? "" : void 0,
1254
+ onClick,
1255
+ ...props,
1256
+ children: content
1257
+ }
1258
+ );
1259
+ }
1260
+ );
1261
+ Button.displayName = "Button";
1053
1262
  export {
1054
1263
  AuthController,
1055
1264
  AuthControllerCtx,
@@ -1058,6 +1267,7 @@ export {
1058
1267
  AuthFormInputField,
1059
1268
  AuthProvider,
1060
1269
  AuthStateCtx,
1270
+ Button,
1061
1271
  DEFAULT_NOTIFICATION_CHANNEL,
1062
1272
  HttpError,
1063
1273
  MockAuthHttpClient,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexdspace/react",
3
- "version": "0.0.27",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -27,29 +27,52 @@
27
27
  "access": "public"
28
28
  },
29
29
  "dependencies": {
30
- "@tanstack/react-query": "^5.90.11",
31
- "lucide-react": "^0.555.0",
32
- "react-router-dom": "^7.10.1",
33
- "react-toastify": "^11.0.5",
34
- "tailwind-scrollbar": "^4.0.2",
30
+ "class-variance-authority": "^0.7.1",
31
+ "clsx": "^2.1.1",
32
+ "tailwind-merge": "^3.4.0",
35
33
  "uuid": "^13.0.0",
36
34
  "@hexdspace/util": "0.0.25"
37
35
  },
38
36
  "peerDependencies": {
39
37
  "@tanstack/react-query": "^5.90.11",
40
- "react": "^19.2.0"
38
+ "lucide-react": "^0.555.0",
39
+ "radix-ui": "^1.4.3",
40
+ "react": "^19.2.3",
41
+ "react-dom": "^19.2.3",
42
+ "react-router-dom": "^7.10.1",
43
+ "react-toastify": "^11.0.5"
41
44
  },
42
45
  "devDependencies": {
46
+ "@chromatic-com/storybook": "^4.1.3",
47
+ "@storybook/addon-a11y": "^10.1.10",
48
+ "@storybook/addon-docs": "^10.1.10",
49
+ "@storybook/addon-onboarding": "^10.1.10",
50
+ "@storybook/addon-vitest": "^10.1.10",
51
+ "@storybook/react-vite": "^10.1.10",
52
+ "@tailwindcss/vite": "^4.1.18",
43
53
  "@tanstack/react-query": "^5.90.11",
44
54
  "@types/react": "^19.2.7",
55
+ "@vitest/browser": "^3.2.4",
56
+ "@vitest/coverage-v8": "^3.2.4",
45
57
  "autoprefixer": "^10.4.22",
58
+ "lucide-react": "^0.555.0",
59
+ "playwright": "^1.57.0",
46
60
  "postcss": "^8.5.6",
47
- "react": "^19.2.0",
48
- "tailwindcss": "^4.1.17"
61
+ "radix-ui": "^1.4.3",
62
+ "react": "^19.2.3",
63
+ "react-dom": "^19.2.3",
64
+ "react-router-dom": "^7.10.1",
65
+ "react-toastify": "^11.0.5",
66
+ "storybook": "^10.1.10",
67
+ "tailwind-scrollbar": "^4.0.2",
68
+ "tailwindcss": "^4.1.17",
69
+ "vite": "^7.2.4"
49
70
  },
50
71
  "scripts": {
51
72
  "build": "tsup src/index.ts --dts --format esm --clean && pnpm run build:css",
52
- "build:css": "mkdir -p dist/css && cp -R src/feature/theme/css/* dist/css/",
53
- "test": "vitest"
73
+ "build:css": "mkdir -p dist/css && cp -R src/ui/theme/css/* dist/css/",
74
+ "test": "vitest",
75
+ "storybook": "storybook dev -p 6006",
76
+ "build-storybook": "storybook build"
54
77
  }
55
78
  }