feat: Added shadcn-ui b014c7ff
Steve · 2025-05-03 15:16 10 file(s) · +250 −6
bun.lock +20 −0
15 15
      "name": "client",
16 16
      "version": "0.0.1",
17 17
      "dependencies": {
18 +
        "@radix-ui/react-slot": "^1.2.0",
18 19
        "@tailwindcss/vite": "^4.1.5",
20 +
        "class-variance-authority": "^0.7.1",
21 +
        "clsx": "^2.1.1",
22 +
        "lucide-react": "^0.507.0",
19 23
        "react": "^19.0.0",
20 24
        "react-dom": "^19.0.0",
21 25
        "shared": "workspace:*",
26 +
        "tailwind-merge": "^3.2.0",
22 27
        "tailwindcss": "^4.1.5",
23 28
      },
24 29
      "devDependencies": {
31 36
        "eslint-plugin-react-hooks": "^5.2.0",
32 37
        "eslint-plugin-react-refresh": "^0.4.19",
33 38
        "globals": "^16.0.0",
39 +
        "tw-animate-css": "^1.2.9",
34 40
        "typescript": "~5.7.2",
35 41
        "typescript-eslint": "^8.26.1",
36 42
        "vite": "^6.3.1",
187 193
    "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
188 194
189 195
    "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
196 +
197 +
    "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
198 +
199 +
    "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w=="],
190 200
191 201
    "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.40.1", "", { "os": "android", "cpu": "arm" }, "sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw=="],
192 202
334 344
335 345
    "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
336 346
347 +
    "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
348 +
337 349
    "client": ["client@workspace:client"],
338 350
339 351
    "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
352 +
353 +
    "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
340 354
341 355
    "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
342 356
560 574
561 575
    "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
562 576
577 +
    "lucide-react": ["lucide-react@0.507.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-XfgE6gvAHwAtnbUvWiTTHx4S3VGR+cUJHEc0vrh9Ogu672I1Tue2+Cp/8JJqpytgcBHAB1FVI297W4XGNwc2dQ=="],
578 +
563 579
    "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
564 580
565 581
    "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
696 712
697 713
    "supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
698 714
715 +
    "tailwind-merge": ["tailwind-merge@3.2.0", "", {}, "sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA=="],
716 +
699 717
    "tailwindcss": ["tailwindcss@4.1.5", "", {}, "sha512-nYtSPfWGDiWgCkwQG/m+aX83XCwf62sBgg3bIlNiiOcggnS1x3uVRDAuyelBFL+vJdOPPCGElxv9DjHJjRHiVA=="],
700 718
701 719
    "tapable": ["tapable@2.2.1", "", {}, "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="],
711 729
    "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
712 730
713 731
    "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
732 +
733 +
    "tw-animate-css": ["tw-animate-css@1.2.9", "", {}, "sha512-9O4k1at9pMQff9EAcCEuy1UNO43JmaPQvq+0lwza9Y0BQ6LB38NiMj+qHqjoQf40355MX+gs6wtlR6H9WsSXFg=="],
714 734
715 735
    "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
716 736
client/components.json (added) +21 −0
1 +
{
2 +
  "$schema": "https://ui.shadcn.com/schema.json",
3 +
  "style": "new-york",
4 +
  "rsc": false,
5 +
  "tsx": true,
6 +
  "tailwind": {
7 +
    "config": "",
8 +
    "css": "src/index.css",
9 +
    "baseColor": "neutral",
10 +
    "cssVariables": true,
11 +
    "prefix": ""
12 +
  },
13 +
  "aliases": {
14 +
    "components": "@/components",
15 +
    "utils": "@/lib/utils",
16 +
    "ui": "@/components/ui",
17 +
    "lib": "@/lib",
18 +
    "hooks": "@/hooks"
19 +
  },
20 +
  "iconLibrary": "lucide"
21 +
}
client/package.json +6 −0
10 10
    "preview": "vite preview"
11 11
  },
12 12
  "dependencies": {
13 +
    "@radix-ui/react-slot": "^1.2.0",
13 14
    "@tailwindcss/vite": "^4.1.5",
15 +
    "class-variance-authority": "^0.7.1",
16 +
    "clsx": "^2.1.1",
17 +
    "lucide-react": "^0.507.0",
14 18
    "react": "^19.0.0",
15 19
    "react-dom": "^19.0.0",
16 20
    "shared": "workspace:*",
21 +
    "tailwind-merge": "^3.2.0",
17 22
    "tailwindcss": "^4.1.5"
18 23
  },
19 24
  "devDependencies": {
26 31
    "eslint-plugin-react-hooks": "^5.2.0",
27 32
    "eslint-plugin-react-refresh": "^0.4.19",
28 33
    "globals": "^16.0.0",
34 +
    "tw-animate-css": "^1.2.9",
29 35
    "typescript": "~5.7.2",
30 36
    "typescript-eslint": "^8.26.1",
31 37
    "vite": "^6.3.1"
client/src/App.tsx +3 −3
1 1
import { useState } from 'react'
2 2
import beaver from './assets/beaver.svg'
3 3
import { ApiResponse } from 'shared'
4 +
import { Button } from './components/ui/button'
4 5
5 6
const SERVER_URL = import.meta.env.VITE_SERVER_URL || "http://localhost:3000"
6 7
29 30
      <h1 className="text-5xl font-black">bhvr</h1>
30 31
      <h2 className="text-2xl font-bold">Bun + Hono + Vite + React</h2>
31 32
      <p>A typesafe fullstack monorepo</p>
32 -
        <button
33 +
        <Button
33 34
          onClick={sendRequest}
34 -
          className="bg-black text-white px-2.5 py-1.5 rounded-md"
35 35
        >
36 36
          Call API
37 -
        </button>
37 +
        </Button>
38 38
        {data && (
39 39
          <pre className="bg-gray-100 p-4 rounded-md">
40 40
            <code>
client/src/components/ui/button.tsx (added) +59 −0
1 +
import * as React from "react"
2 +
import { Slot } from "@radix-ui/react-slot"
3 +
import { cva, type VariantProps } from "class-variance-authority"
4 +
5 +
import { cn } from "@/lib/utils"
6 +
7 +
const buttonVariants = cva(
8 +
  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
9 +
  {
10 +
    variants: {
11 +
      variant: {
12 +
        default:
13 +
          "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
14 +
        destructive:
15 +
          "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
16 +
        outline:
17 +
          "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
18 +
        secondary:
19 +
          "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
20 +
        ghost:
21 +
          "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
22 +
        link: "text-primary underline-offset-4 hover:underline",
23 +
      },
24 +
      size: {
25 +
        default: "h-9 px-4 py-2 has-[>svg]:px-3",
26 +
        sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
27 +
        lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
28 +
        icon: "size-9",
29 +
      },
30 +
    },
31 +
    defaultVariants: {
32 +
      variant: "default",
33 +
      size: "default",
34 +
    },
35 +
  }
36 +
)
37 +
38 +
function Button({
39 +
  className,
40 +
  variant,
41 +
  size,
42 +
  asChild = false,
43 +
  ...props
44 +
}: React.ComponentProps<"button"> &
45 +
  VariantProps<typeof buttonVariants> & {
46 +
    asChild?: boolean
47 +
  }) {
48 +
  const Comp = asChild ? Slot : "button"
49 +
50 +
  return (
51 +
    <Comp
52 +
      data-slot="button"
53 +
      className={cn(buttonVariants({ variant, size, className }))}
54 +
      {...props}
55 +
    />
56 +
  )
57 +
}
58 +
59 +
export { Button, buttonVariants }
client/src/index.css +119 −0
1 1
@import "tailwindcss";
2 +
@import "tw-animate-css";
3 +
4 +
@custom-variant dark (&:is(.dark *));
5 +
6 +
@theme inline {
7 +
  --radius-sm: calc(var(--radius) - 4px);
8 +
  --radius-md: calc(var(--radius) - 2px);
9 +
  --radius-lg: var(--radius);
10 +
  --radius-xl: calc(var(--radius) + 4px);
11 +
  --color-background: var(--background);
12 +
  --color-foreground: var(--foreground);
13 +
  --color-card: var(--card);
14 +
  --color-card-foreground: var(--card-foreground);
15 +
  --color-popover: var(--popover);
16 +
  --color-popover-foreground: var(--popover-foreground);
17 +
  --color-primary: var(--primary);
18 +
  --color-primary-foreground: var(--primary-foreground);
19 +
  --color-secondary: var(--secondary);
20 +
  --color-secondary-foreground: var(--secondary-foreground);
21 +
  --color-muted: var(--muted);
22 +
  --color-muted-foreground: var(--muted-foreground);
23 +
  --color-accent: var(--accent);
24 +
  --color-accent-foreground: var(--accent-foreground);
25 +
  --color-destructive: var(--destructive);
26 +
  --color-border: var(--border);
27 +
  --color-input: var(--input);
28 +
  --color-ring: var(--ring);
29 +
  --color-chart-1: var(--chart-1);
30 +
  --color-chart-2: var(--chart-2);
31 +
  --color-chart-3: var(--chart-3);
32 +
  --color-chart-4: var(--chart-4);
33 +
  --color-chart-5: var(--chart-5);
34 +
  --color-sidebar: var(--sidebar);
35 +
  --color-sidebar-foreground: var(--sidebar-foreground);
36 +
  --color-sidebar-primary: var(--sidebar-primary);
37 +
  --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
38 +
  --color-sidebar-accent: var(--sidebar-accent);
39 +
  --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
40 +
  --color-sidebar-border: var(--sidebar-border);
41 +
  --color-sidebar-ring: var(--sidebar-ring);
42 +
}
43 +
44 +
:root {
45 +
  --radius: 0.625rem;
46 +
  --background: oklch(1 0 0);
47 +
  --foreground: oklch(0.145 0 0);
48 +
  --card: oklch(1 0 0);
49 +
  --card-foreground: oklch(0.145 0 0);
50 +
  --popover: oklch(1 0 0);
51 +
  --popover-foreground: oklch(0.145 0 0);
52 +
  --primary: oklch(0.205 0 0);
53 +
  --primary-foreground: oklch(0.985 0 0);
54 +
  --secondary: oklch(0.97 0 0);
55 +
  --secondary-foreground: oklch(0.205 0 0);
56 +
  --muted: oklch(0.97 0 0);
57 +
  --muted-foreground: oklch(0.556 0 0);
58 +
  --accent: oklch(0.97 0 0);
59 +
  --accent-foreground: oklch(0.205 0 0);
60 +
  --destructive: oklch(0.577 0.245 27.325);
61 +
  --border: oklch(0.922 0 0);
62 +
  --input: oklch(0.922 0 0);
63 +
  --ring: oklch(0.708 0 0);
64 +
  --chart-1: oklch(0.646 0.222 41.116);
65 +
  --chart-2: oklch(0.6 0.118 184.704);
66 +
  --chart-3: oklch(0.398 0.07 227.392);
67 +
  --chart-4: oklch(0.828 0.189 84.429);
68 +
  --chart-5: oklch(0.769 0.188 70.08);
69 +
  --sidebar: oklch(0.985 0 0);
70 +
  --sidebar-foreground: oklch(0.145 0 0);
71 +
  --sidebar-primary: oklch(0.205 0 0);
72 +
  --sidebar-primary-foreground: oklch(0.985 0 0);
73 +
  --sidebar-accent: oklch(0.97 0 0);
74 +
  --sidebar-accent-foreground: oklch(0.205 0 0);
75 +
  --sidebar-border: oklch(0.922 0 0);
76 +
  --sidebar-ring: oklch(0.708 0 0);
77 +
}
78 +
79 +
.dark {
80 +
  --background: oklch(0.145 0 0);
81 +
  --foreground: oklch(0.985 0 0);
82 +
  --card: oklch(0.205 0 0);
83 +
  --card-foreground: oklch(0.985 0 0);
84 +
  --popover: oklch(0.205 0 0);
85 +
  --popover-foreground: oklch(0.985 0 0);
86 +
  --primary: oklch(0.922 0 0);
87 +
  --primary-foreground: oklch(0.205 0 0);
88 +
  --secondary: oklch(0.269 0 0);
89 +
  --secondary-foreground: oklch(0.985 0 0);
90 +
  --muted: oklch(0.269 0 0);
91 +
  --muted-foreground: oklch(0.708 0 0);
92 +
  --accent: oklch(0.269 0 0);
93 +
  --accent-foreground: oklch(0.985 0 0);
94 +
  --destructive: oklch(0.704 0.191 22.216);
95 +
  --border: oklch(1 0 0 / 10%);
96 +
  --input: oklch(1 0 0 / 15%);
97 +
  --ring: oklch(0.556 0 0);
98 +
  --chart-1: oklch(0.488 0.243 264.376);
99 +
  --chart-2: oklch(0.696 0.17 162.48);
100 +
  --chart-3: oklch(0.769 0.188 70.08);
101 +
  --chart-4: oklch(0.627 0.265 303.9);
102 +
  --chart-5: oklch(0.645 0.246 16.439);
103 +
  --sidebar: oklch(0.205 0 0);
104 +
  --sidebar-foreground: oklch(0.985 0 0);
105 +
  --sidebar-primary: oklch(0.488 0.243 264.376);
106 +
  --sidebar-primary-foreground: oklch(0.985 0 0);
107 +
  --sidebar-accent: oklch(0.269 0 0);
108 +
  --sidebar-accent-foreground: oklch(0.985 0 0);
109 +
  --sidebar-border: oklch(1 0 0 / 10%);
110 +
  --sidebar-ring: oklch(0.556 0 0);
111 +
}
112 +
113 +
@layer base {
114 +
  * {
115 +
    @apply border-border outline-ring/50;
116 +
  }
117 +
  body {
118 +
    @apply bg-background text-foreground;
119 +
  }
120 +
}
client/src/lib/utils.ts (added) +6 −0
1 +
import { clsx, type ClassValue } from "clsx"
2 +
import { twMerge } from "tailwind-merge"
3 +
4 +
export function cn(...inputs: ClassValue[]) {
5 +
  return twMerge(clsx(inputs))
6 +
}
client/tsconfig.app.json +7 −1
16 16
    "noUnusedLocals": true,
17 17
    "noUnusedParameters": true,
18 18
    "noFallthroughCasesInSwitch": true,
19 -
    "noUncheckedSideEffectImports": true
19 +
    "noUncheckedSideEffectImports": true,
20 +
    "baseUrl": ".",
21 +
       "paths": {
22 +
         "@/*": [
23 +
           "./src/*"
24 +
         ]
25 +
       }
20 26
  },
21 27
  "include": ["src"]
22 28
}
client/tsconfig.json +7 −1
1 1
{
2 2
  "extends": "../tsconfig.json",
3 3
  "files": [],
4 -
  "references": [{ "path": "./tsconfig.node.json" }, { "path": "./tsconfig.app.json"}]
4 +
  "references": [{ "path": "./tsconfig.node.json" }, { "path": "./tsconfig.app.json"}],
5 +
  "compilerOptions": {
6 +
      "baseUrl": ".",
7 +
      "paths": {
8 +
        "@/*": ["./src/*"]
9 +
      }
10 +
    }
5 11
}
client/vite.config.ts +2 −1
9 9
    alias: {
10 10
      "@client": path.resolve(__dirname, "./src"),
11 11
      "@server": path.resolve(__dirname, "../server/src"),
12 -
      "@shared": path.resolve(__dirname, "../shared/src")
12 +
      "@shared": path.resolve(__dirname, "../shared/src"),
13 +
      "@": path.resolve(__dirname, "./src")
13 14
    }
14 15
  }
15 16
})