create-better-t-stack 3.8.3 → 3.9.0-pr730.0ee9844

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 (63) hide show
  1. package/README.md +17 -16
  2. package/dist/cli.mjs +1 -1
  3. package/dist/index.mjs +1 -1
  4. package/dist/{src-BCBL3cO2.mjs → src-DW15ZRe9.mjs} +80 -108
  5. package/package.json +44 -44
  6. package/templates/auth/better-auth/convex/backend/convex/auth.config.ts.hbs +5 -7
  7. package/templates/auth/better-auth/convex/backend/convex/auth.ts.hbs +21 -19
  8. package/templates/auth/better-auth/convex/backend/convex/http.ts.hbs +6 -2
  9. package/templates/auth/better-auth/convex/native/base/lib/auth-client.ts.hbs +11 -6
  10. package/templates/auth/better-auth/convex/web/react/next/src/app/api/auth/[...all]/route.ts.hbs +2 -2
  11. package/templates/auth/better-auth/convex/web/react/next/src/components/user-menu.tsx.hbs +23 -20
  12. package/templates/auth/better-auth/convex/web/react/next/src/lib/auth-server.ts.hbs +13 -5
  13. package/templates/auth/better-auth/convex/web/react/tanstack-router/src/components/user-menu.tsx.hbs +25 -22
  14. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/user-menu.tsx.hbs +23 -24
  15. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/lib/auth-server.ts.hbs +11 -5
  16. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/routes/api/auth/$.ts.hbs +4 -4
  17. package/templates/auth/better-auth/fullstack/tanstack-start/src/routes/api/auth/$.ts.hbs +6 -10
  18. package/templates/auth/better-auth/web/react/base/src/lib/auth-client.ts.hbs +0 -1
  19. package/templates/auth/better-auth/web/react/next/src/components/user-menu.tsx.hbs +24 -21
  20. package/templates/auth/better-auth/web/react/react-router/src/components/user-menu.tsx.hbs +24 -21
  21. package/templates/auth/better-auth/web/react/{tanstack-start/src/components/user-menu.tsx → tanstack-router/src/components/user-menu.tsx.hbs} +26 -23
  22. package/templates/auth/better-auth/web/react/{tanstack-router/src/components/user-menu.tsx → tanstack-start/src/components/user-menu.tsx.hbs} +26 -23
  23. package/templates/frontend/react/next/package.json.hbs +8 -7
  24. package/templates/frontend/react/next/src/app/layout.tsx.hbs +28 -1
  25. package/templates/frontend/react/next/src/components/providers.tsx.hbs +14 -4
  26. package/templates/frontend/react/react-router/package.json.hbs +2 -1
  27. package/templates/frontend/react/{tanstack-router/src/components/mode-toggle.tsx → react-router/src/components/mode-toggle.tsx.hbs} +1 -1
  28. package/templates/frontend/react/tanstack-router/package.json.hbs +2 -1
  29. package/templates/frontend/react/{react-router/src/components/mode-toggle.tsx → tanstack-router/src/components/mode-toggle.tsx.hbs} +1 -1
  30. package/templates/frontend/react/tanstack-start/package.json.hbs +2 -1
  31. package/templates/frontend/react/tanstack-start/src/router.tsx.hbs +6 -0
  32. package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +13 -14
  33. package/templates/frontend/react/tanstack-start/vite.config.ts.hbs +5 -0
  34. package/templates/frontend/react/web-base/components.json +5 -2
  35. package/templates/frontend/react/web-base/src/components/ui/button.tsx.hbs +57 -0
  36. package/templates/frontend/react/web-base/src/components/ui/card.tsx.hbs +103 -0
  37. package/templates/frontend/react/web-base/src/components/ui/checkbox.tsx.hbs +26 -0
  38. package/templates/frontend/react/web-base/src/components/ui/dropdown-menu.tsx.hbs +262 -0
  39. package/templates/frontend/react/web-base/src/components/ui/input.tsx.hbs +20 -0
  40. package/templates/frontend/react/web-base/src/components/ui/label.tsx.hbs +20 -0
  41. package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx.hbs +13 -0
  42. package/templates/frontend/react/web-base/src/components/ui/sonner.tsx.hbs +44 -0
  43. package/templates/frontend/react/web-base/src/index.css.hbs +57 -63
  44. package/templates/frontend/react/web-base/src/components/ui/button.tsx +0 -56
  45. package/templates/frontend/react/web-base/src/components/ui/card.tsx +0 -75
  46. package/templates/frontend/react/web-base/src/components/ui/checkbox.tsx +0 -27
  47. package/templates/frontend/react/web-base/src/components/ui/dropdown-menu.tsx +0 -228
  48. package/templates/frontend/react/web-base/src/components/ui/input.tsx +0 -21
  49. package/templates/frontend/react/web-base/src/components/ui/label.tsx +0 -19
  50. package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx +0 -13
  51. package/templates/frontend/react/web-base/src/components/ui/sonner.tsx +0 -25
  52. /package/templates/auth/better-auth/web/react/tanstack-router/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
  53. /package/templates/auth/better-auth/web/react/tanstack-router/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
  54. /package/templates/auth/better-auth/web/react/tanstack-router/src/routes/{login.tsx → login.tsx.hbs} +0 -0
  55. /package/templates/auth/better-auth/web/react/tanstack-start/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
  56. /package/templates/auth/better-auth/web/react/tanstack-start/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
  57. /package/templates/auth/better-auth/web/react/tanstack-start/src/routes/{login.tsx → login.tsx.hbs} +0 -0
  58. /package/templates/auth/better-auth/web/solid/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
  59. /package/templates/auth/better-auth/web/solid/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
  60. /package/templates/auth/better-auth/web/solid/src/routes/{login.tsx → login.tsx.hbs} +0 -0
  61. /package/templates/frontend/react/react-router/src/components/{theme-provider.tsx → theme-provider.tsx.hbs} +0 -0
  62. /package/templates/frontend/react/tanstack-router/src/components/{theme-provider.tsx → theme-provider.tsx.hbs} +0 -0
  63. /package/templates/frontend/react/web-base/src/lib/{utils.ts → utils.ts.hbs} +0 -0
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  DropdownMenu,
3
3
  DropdownMenuContent,
4
+ DropdownMenuGroup,
4
5
  DropdownMenuItem,
5
6
  DropdownMenuLabel,
6
7
  DropdownMenuSeparator,
@@ -22,7 +23,7 @@ export default function UserMenu() {
22
23
 
23
24
  if (!session) {
24
25
  return (
25
- <Button variant="outline" asChild>
26
+ <Button variant="outline">
26
27
  <Link to="/login">Sign In</Link>
27
28
  </Button>
28
29
  );
@@ -30,32 +31,34 @@ export default function UserMenu() {
30
31
 
31
32
  return (
32
33
  <DropdownMenu>
33
- <DropdownMenuTrigger asChild>
34
+ <DropdownMenuTrigger>
34
35
  <Button variant="outline">{session.user.name}</Button>
35
36
  </DropdownMenuTrigger>
36
37
  <DropdownMenuContent className="bg-card">
37
- <DropdownMenuLabel>My Account</DropdownMenuLabel>
38
- <DropdownMenuSeparator />
39
- <DropdownMenuItem>{session.user.email}</DropdownMenuItem>
40
- <DropdownMenuItem asChild>
41
- <Button
42
- variant="destructive"
43
- className="w-full"
44
- onClick={() => {
45
- authClient.signOut({
46
- fetchOptions: {
47
- onSuccess: () => {
48
- navigate({
49
- to: "/",
50
- });
38
+ <DropdownMenuGroup>
39
+ <DropdownMenuLabel>My Account</DropdownMenuLabel>
40
+ <DropdownMenuSeparator />
41
+ <DropdownMenuItem>{session.user.email}</DropdownMenuItem>
42
+ <DropdownMenuItem>
43
+ <Button
44
+ variant="destructive"
45
+ className="w-full"
46
+ onClick={() => {
47
+ authClient.signOut({
48
+ fetchOptions: {
49
+ onSuccess: () => {
50
+ navigate({
51
+ to: "/",
52
+ });
53
+ },
51
54
  },
52
- },
53
- });
54
- }}
55
- >
56
- Sign Out
57
- </Button>
58
- </DropdownMenuItem>
55
+ });
56
+ }}
57
+ >
58
+ Sign Out
59
+ </Button>
60
+ </DropdownMenuItem>
61
+ </DropdownMenuGroup>
59
62
  </DropdownMenuContent>
60
63
  </DropdownMenu>
61
64
  );
@@ -3,20 +3,21 @@
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
5
  "scripts": {
6
- "dev": "next dev",
6
+ "dev": "next dev --port 3001",
7
7
  "build": "next build",
8
8
  "start": "next start"
9
9
  },
10
10
  "dependencies": {
11
- "radix-ui": "^1.4.2",
11
+ "@base-ui/react": "^1.0.0",
12
+ "shadcn": "^3.6.2",
12
13
  "@tanstack/react-form": "^1.27.3",
13
14
  "class-variance-authority": "^0.7.1",
14
15
  "clsx": "^2.1.1",
15
16
  "lucide-react": "^0.546.0",
16
- "next": "^16.0.10",
17
+ "next": "^16.1.0",
17
18
  "next-themes": "^0.4.6",
18
- "react": "19.2.3",
19
- "react-dom": "19.2.3",
19
+ "react": "^19.2.3",
20
+ "react-dom": "^19.2.3",
20
21
  "sonner": "^2.0.5",
21
22
  "tailwind-merge": "^3.3.1",
22
23
  "tw-animate-css": "^1.3.4",
@@ -25,8 +26,8 @@
25
26
  "devDependencies": {
26
27
  "@tailwindcss/postcss": "^4.1.10",
27
28
  "@types/node": "^20",
28
- "@types/react": "19.2.7",
29
- "@types/react-dom": "19.2.3",
29
+ "@types/react": "^19.2.7",
30
+ "@types/react-dom": "^19.2.3",
30
31
  "tailwindcss": "^4.1.10",
31
32
  "typescript": "^5"
32
33
  }
@@ -2,7 +2,10 @@ import type { Metadata } from "next";
2
2
  import { Geist, Geist_Mono } from "next/font/google";
3
3
  import "../index.css";
4
4
  {{#if (eq auth "clerk")}}{{#if (eq backend "convex")}}import { ClerkProvider } from "@clerk/nextjs";
5
- {{/if}}{{/if}}import Providers from "@/components/providers";
5
+ {{/if}}{{/if}}{{#if (and (eq backend "convex") (eq auth "better-auth"))}}
6
+ import { getToken } from "@/lib/auth-server";
7
+ {{/if}}
8
+ import Providers from "@/components/providers";
6
9
  import Header from "@/components/header";
7
10
 
8
11
  const geistSans = Geist({
@@ -20,6 +23,29 @@ export const metadata: Metadata = {
20
23
  description: "{{projectName}}",
21
24
  };
22
25
 
26
+ {{#if (and (eq backend "convex") (eq auth "better-auth"))}}
27
+ export default async function RootLayout({
28
+ children,
29
+ }: Readonly<{
30
+ children: React.ReactNode;
31
+ }>) {
32
+ const token = await getToken();
33
+ return (
34
+ <html lang="en" suppressHydrationWarning>
35
+ <body
36
+ className={`${geistSans.variable} ${geistMono.variable} antialiased`}
37
+ >
38
+ <Providers initialToken={token}>
39
+ <div className="grid grid-rows-[auto_1fr] h-svh">
40
+ <Header />
41
+ {children}
42
+ </div>
43
+ </Providers>
44
+ </body>
45
+ </html>
46
+ );
47
+ }
48
+ {{else}}
23
49
  export default function RootLayout({
24
50
  children,
25
51
  }: Readonly<{
@@ -47,3 +73,4 @@ export default function RootLayout({
47
73
  </html>
48
74
  );
49
75
  }
76
+ {{/if}}
@@ -6,7 +6,7 @@ import { useAuth } from "@clerk/nextjs";
6
6
  import { ConvexReactClient } from "convex/react";
7
7
  import { ConvexProviderWithClerk } from "convex/react-clerk";
8
8
  {{else if (eq auth "better-auth")}}
9
- import { ConvexProvider, ConvexReactClient } from "convex/react";
9
+ import { ConvexReactClient } from "convex/react";
10
10
  import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react";
11
11
  import { authClient } from "@/lib/auth-client";
12
12
  {{else}}
@@ -32,9 +32,15 @@ const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);
32
32
  {{/if}}
33
33
 
34
34
  export default function Providers({
35
- children
35
+ children,
36
+ {{#if (and (eq backend "convex") (eq auth "better-auth"))}}
37
+ initialToken,
38
+ {{/if}}
36
39
  }: {
37
- children: React.ReactNode
40
+ children: React.ReactNode;
41
+ {{#if (and (eq backend "convex") (eq auth "better-auth"))}}
42
+ initialToken?: string | null;
43
+ {{/if}}
38
44
  }) {
39
45
  return (
40
46
  <ThemeProvider
@@ -49,7 +55,11 @@ export default function Providers({
49
55
  {children}
50
56
  </ConvexProviderWithClerk>
51
57
  {{else if (eq auth "better-auth")}}
52
- <ConvexBetterAuthProvider client={convex} authClient={authClient}>
58
+ <ConvexBetterAuthProvider
59
+ client={convex}
60
+ authClient={authClient}
61
+ initialToken={initialToken}
62
+ >
53
63
  {children}
54
64
  </ConvexBetterAuthProvider>
55
65
  {{else}}
@@ -9,7 +9,8 @@
9
9
  "typecheck": "react-router typegen && tsc"
10
10
  },
11
11
  "dependencies": {
12
- "radix-ui": "^1.4.2",
12
+ "@base-ui/react": "^1.0.0",
13
+ "shadcn": "^3.6.2",
13
14
  "@react-router/fs-routes": "^7.10.1",
14
15
  "@react-router/node": "^7.10.1",
15
16
  "@react-router/serve": "^7.10.1",
@@ -14,7 +14,7 @@ export function ModeToggle() {
14
14
 
15
15
  return (
16
16
  <DropdownMenu>
17
- <DropdownMenuTrigger asChild>
17
+ <DropdownMenuTrigger>
18
18
  <Button variant="outline" size="icon">
19
19
  <Sun className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
20
20
  <Moon className="absolute h-[1.2rem] w-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
@@ -12,7 +12,8 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@hookform/resolvers": "^5.1.1",
15
- "radix-ui": "^1.4.2",
15
+ "@base-ui/react": "^1.0.0",
16
+ "shadcn": "^3.6.2",
16
17
  "@tanstack/react-form": "^1.12.3",
17
18
  "@tailwindcss/vite": "^4.0.15",
18
19
  "@tanstack/react-router": "^1.141.1",
@@ -14,7 +14,7 @@ export function ModeToggle() {
14
14
 
15
15
  return (
16
16
  <DropdownMenu>
17
- <DropdownMenuTrigger asChild>
17
+ <DropdownMenuTrigger>
18
18
  <Button variant="outline" size="icon">
19
19
  <Sun className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
20
20
  <Moon className="absolute h-[1.2rem] w-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
@@ -8,7 +8,8 @@
8
8
  "dev": "vite dev"
9
9
  },
10
10
  "dependencies": {
11
- "radix-ui": "^1.4.2",
11
+ "@base-ui/react": "^1.0.0",
12
+ "shadcn": "^3.6.2",
12
13
  "@tanstack/react-form": "^1.23.5",
13
14
  "@tailwindcss/vite": "^4.1.8",
14
15
  "@tanstack/react-query": "^5.80.6",
@@ -35,7 +35,13 @@ export function getRouter() {
35
35
  unsavedChangesWarning: false,
36
36
  });
37
37
 
38
+ {{#if (eq auth "better-auth")}}
39
+ const convexQueryClient = new ConvexQueryClient(convex, {
40
+ expectAuth: true,
41
+ });
42
+ {{else}}
38
43
  const convexQueryClient = new ConvexQueryClient(convex);
44
+ {{/if}}
39
45
 
40
46
  const queryClient: QueryClient = new QueryClient({
41
47
  defaultOptions: {
@@ -37,20 +37,12 @@ const fetchClerkAuth = createServerFn({ method: "GET" }).handler(async () => {
37
37
  });
38
38
  {{else if (and (eq backend "convex") (eq auth "better-auth"))}}
39
39
  import { createServerFn } from "@tanstack/react-start";
40
- import { getRequest, getCookie } from "@tanstack/react-start/server";
41
40
  import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react";
42
- import { fetchSession, getCookieName } from "@convex-dev/better-auth/react-start";
43
41
  import { authClient } from "@/lib/auth-client";
44
- import { createAuth } from "@{{projectName}}/backend/convex/auth";
42
+ import { getToken } from "@/lib/auth-server";
45
43
 
46
- const fetchAuth = createServerFn({ method: "GET" }).handler(async () => {
47
- const { session } = await fetchSession(getRequest());
48
- const sessionCookieName = getCookieName(createAuth);
49
- const token = getCookie(sessionCookieName);
50
- return {
51
- userId: session?.user.id,
52
- token,
53
- };
44
+ const getAuth = createServerFn({ method: "GET" }).handler(async () => {
45
+ return await getToken();
54
46
  });
55
47
  {{/if}}
56
48
 
@@ -113,11 +105,14 @@ export const Route = createRootRouteWithContext<RouterAppContext>()({
113
105
  },
114
106
  {{else if (and (eq backend "convex") (eq auth "better-auth"))}}
115
107
  beforeLoad: async (ctx) => {
116
- const { userId, token } = await fetchAuth();
108
+ const token = await getAuth();
117
109
  if (token) {
118
110
  ctx.context.convexQueryClient.serverHttpClient?.setAuth(token);
119
111
  }
120
- return { userId, token };
112
+ return {
113
+ isAuthenticated: !!token,
114
+ token,
115
+ };
121
116
  },
122
117
  {{/if}}
123
118
  });
@@ -148,7 +143,11 @@ function RootDocument() {
148
143
  {{else if (and (eq backend "convex") (eq auth "better-auth"))}}
149
144
  const context = useRouteContext({ from: Route.id });
150
145
  return (
151
- <ConvexBetterAuthProvider client={context.convexClient} authClient={authClient}>
146
+ <ConvexBetterAuthProvider
147
+ client={context.convexClient}
148
+ authClient={authClient}
149
+ initialToken={context.token}
150
+ >
152
151
  <html lang="en" className="dark">
153
152
  <head>
154
153
  <HeadContent />
@@ -14,4 +14,9 @@ export default defineConfig({
14
14
  server: {
15
15
  port: 3001,
16
16
  },
17
+ {{#if (and (eq backend "convex") (eq auth "better-auth"))}}
18
+ ssr: {
19
+ noExternal: ["@convex-dev/better-auth"],
20
+ },
21
+ {{/if}}
17
22
  });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://ui.shadcn.com/schema.json",
3
- "style": "new-york",
3
+ "style": "base-lyra",
4
4
  "rsc": false,
5
5
  "tsx": true,
6
6
  "tailwind": {
@@ -10,6 +10,7 @@
10
10
  "cssVariables": true,
11
11
  "prefix": ""
12
12
  },
13
+ "iconLibrary": "lucide",
13
14
  "aliases": {
14
15
  "components": "@/components",
15
16
  "utils": "@/lib/utils",
@@ -17,5 +18,7 @@
17
18
  "lib": "@/lib",
18
19
  "hooks": "@/hooks"
19
20
  },
20
- "iconLibrary": "lucide"
21
+ "menuColor": "default",
22
+ "menuAccent": "subtle",
23
+ "registries": {}
21
24
  }
@@ -0,0 +1,57 @@
1
+ import { Button as ButtonPrimitive } from '@base-ui/react/button'
2
+ import { cva } from 'class-variance-authority'
3
+ import type {VariantProps} from 'class-variance-authority';
4
+
5
+ import { cn } from '@/lib/utils'
6
+
7
+ const buttonVariants = cva(
8
+ "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-none border border-transparent bg-clip-padding text-xs font-medium focus-visible:ring-1 aria-invalid:ring-1 [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
13
+ outline:
14
+ 'border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground',
15
+ secondary:
16
+ 'bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground',
17
+ ghost:
18
+ 'hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground',
19
+ destructive:
20
+ 'bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30',
21
+ link: 'text-primary underline-offset-4 hover:underline',
22
+ },
23
+ size: {
24
+ default:
25
+ 'h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
26
+ xs: "h-6 gap-1 rounded-none px-2 text-xs has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
27
+ sm: "h-7 gap-1 rounded-none px-2.5 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
28
+ lg: 'h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3',
29
+ icon: 'size-8',
30
+ 'icon-xs': "size-6 rounded-none [&_svg:not([class*='size-'])]:size-3",
31
+ 'icon-sm': 'size-7 rounded-none',
32
+ 'icon-lg': 'size-9',
33
+ },
34
+ },
35
+ defaultVariants: {
36
+ variant: 'default',
37
+ size: 'default',
38
+ },
39
+ },
40
+ )
41
+
42
+ function Button({
43
+ className,
44
+ variant = 'default',
45
+ size = 'default',
46
+ ...props
47
+ }: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
48
+ return (
49
+ <ButtonPrimitive
50
+ data-slot="button"
51
+ className={cn(buttonVariants({ variant, size, className }))}
52
+ {...props}
53
+ />
54
+ )
55
+ }
56
+
57
+ export { Button, buttonVariants }
@@ -0,0 +1,103 @@
1
+ import * as React from 'react'
2
+
3
+ import { cn } from '@/lib/utils'
4
+
5
+ function Card({
6
+ className,
7
+ size = 'default',
8
+ ...props
9
+ }: React.ComponentProps<'div'> & { size?: 'default' | 'sm' }) {
10
+ return (
11
+ <div
12
+ data-slot="card"
13
+ data-size={size}
14
+ className={cn(
15
+ 'ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-none py-4 text-xs/relaxed ring-1 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-2 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-none *:[img:last-child]:rounded-none group/card flex flex-col',
16
+ className,
17
+ )}
18
+ {...props}
19
+ />
20
+ )
21
+ }
22
+
23
+ function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
24
+ return (
25
+ <div
26
+ data-slot="card-header"
27
+ className={cn(
28
+ 'gap-1 rounded-none px-4 group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]',
29
+ className,
30
+ )}
31
+ {...props}
32
+ />
33
+ )
34
+ }
35
+
36
+ function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
37
+ return (
38
+ <div
39
+ data-slot="card-title"
40
+ className={cn(
41
+ 'text-sm font-medium group-data-[size=sm]/card:text-sm',
42
+ className,
43
+ )}
44
+ {...props}
45
+ />
46
+ )
47
+ }
48
+
49
+ function CardDescription({ className, ...props }: React.ComponentProps<'div'>) {
50
+ return (
51
+ <div
52
+ data-slot="card-description"
53
+ className={cn('text-muted-foreground text-xs/relaxed', className)}
54
+ {...props}
55
+ />
56
+ )
57
+ }
58
+
59
+ function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
60
+ return (
61
+ <div
62
+ data-slot="card-action"
63
+ className={cn(
64
+ 'col-start-2 row-span-2 row-start-1 self-start justify-self-end',
65
+ className,
66
+ )}
67
+ {...props}
68
+ />
69
+ )
70
+ }
71
+
72
+ function CardContent({ className, ...props }: React.ComponentProps<'div'>) {
73
+ return (
74
+ <div
75
+ data-slot="card-content"
76
+ className={cn('px-4 group-data-[size=sm]/card:px-3', className)}
77
+ {...props}
78
+ />
79
+ )
80
+ }
81
+
82
+ function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {
83
+ return (
84
+ <div
85
+ data-slot="card-footer"
86
+ className={cn(
87
+ 'rounded-none border-t p-4 group-data-[size=sm]/card:p-3 flex items-center',
88
+ className,
89
+ )}
90
+ {...props}
91
+ />
92
+ )
93
+ }
94
+
95
+ export {
96
+ Card,
97
+ CardHeader,
98
+ CardFooter,
99
+ CardTitle,
100
+ CardAction,
101
+ CardDescription,
102
+ CardContent,
103
+ }
@@ -0,0 +1,26 @@
1
+ import { Checkbox as CheckboxPrimitive } from '@base-ui/react/checkbox'
2
+
3
+ import { CheckIcon } from 'lucide-react'
4
+ import { cn } from '@/lib/utils'
5
+
6
+ function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
7
+ return (
8
+ <CheckboxPrimitive.Root
9
+ data-slot="checkbox"
10
+ className={cn(
11
+ 'border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 flex size-4 items-center justify-center rounded-none border transition-colors group-has-disabled/field:opacity-50 focus-visible:ring-1 aria-invalid:ring-1 peer relative shrink-0 outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50',
12
+ className,
13
+ )}
14
+ {...props}
15
+ >
16
+ <CheckboxPrimitive.Indicator
17
+ data-slot="checkbox-indicator"
18
+ className="[&>svg]:size-3.5 grid place-content-center text-current transition-none"
19
+ >
20
+ <CheckIcon />
21
+ </CheckboxPrimitive.Indicator>
22
+ </CheckboxPrimitive.Root>
23
+ )
24
+ }
25
+
26
+ export { Checkbox }