【React】shadcn/ui やってみたメモ

# shadcn/ui って何？
以下の記事に全てのっています

https://qiita.com/bee-yan/items/33214781b8dd991e2768

- コピペ方式で自由にカスタマイズ可能
- Radix UIベースで堅牢なアクセシビリティを提供
- Tailwindとの相性抜群で開発効率良し

なところが推しです。まだやったことないですが

# やってみる
## Vite + React環境の準備
```
pnpm create vite shadcn-hello-world --template react-ts
```

```
> pnpm create vite shadcn-hello-world --template react-ts
.../193e78272f7-4154                     |   +1 +
.../193e78272f7-4154                     | Progress: resolved 1, reused 0, downloaded 1, added 1, 
done

Scaffolding project in C:\work\repo\shadcn-hello-world...

Done. Now run:

  cd shadcn-hello-world
  pnpm install
  pnpm run dev
```

```
  cd shadcn-hello-world
  pnpm install
  pnpm run dev
```

```
  pnpm run dev
```

いつものやつが表示されたらOK

## TailwindCSSの準備
shadcn/uiはtailwindに依存しているので、tailwindをインストールします。

https://tailwindcss.com/docs/installation/using-postcss

```
pnpm install -D tailwindcss postcss autoprefixer
pnpm exec tailwindcss init
```

```
>pnpm install -D tailwindcss postcss autoprefixer

Progress: resolved 291, reused 243, downloaded 6, added 79, done

devDependencies:
+ autoprefixer 10.4.20
devDependencies:
+ autoprefixer 10.4.20
+ autoprefixer 10.4.20
+ postcss 8.4.49
+ tailwindcss 3.4.17

Done in 16.9s
```

```
>pnpm exec tailwindcss init

Created Tailwind CSS config file: tailwind.config.js
```

設定ファイルが必要なので、作成していく。

存在しないので、以下の内容で新規作成する。
ドキュメントだとmodules.exportとCoomonJsの記載になっているが、
ESModule形式で定義する。

```diff_javascript
// postcss.config.js

+ export default {
+     plugins: {
+         tailwindcss: {},
+         autoprefixer: {},
+     },
+ }
```

```diff_javascript
// tailwind.config.js

/** @type {import('tailwindcss').Config} */
export default {
-  content: [],
+  content: ["./src/**/*.tsx"],
  theme: {
    extend: {},
  },
  plugins: [],
}
```

```diff_javascript
// src/index.css

+ @tailwind base;
+ @tailwind components;
+ @tailwind utilities;
```

tailwindが適応され、画面が崩れたらOK

![cap2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/00c6ebc9-788d-54f5-4675-62b864c30e33.png)


## shadcnのインストール
ここからが本番！！

https://ui.shadcn.com/docs/installation/manual


```
pnpm add tailwindcss-animate class-variance-authority clsx tailwind-merge lucide-react
```

```
> pnpm add tailwindcss-animate class-variance-authority clsx tailwind-merge lucide-react
Packages: +5
+++++
Progress: resolved 296, reused 250, downloaded 4, added 5, done

dependencies:
+ class-variance-authority 0.7.1
+ clsx 2.1.1
+ lucide-react 0.469.0
+ tailwind-merge 2.5.5
+ tailwindcss-animate 1.0.7

Done in 1m 15.7s
```

設定ファイルに追記していく。

```diff_json
// tsconfig.app.json
    ...
    "module": "ESNext",
    "skipLibCheck": true,
    "baseUrl": ".",
+    "paths": {
+      "@/*": [
+        "./*"
+      ]
+    },
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    ...
```

nextjs準拠だから？
@がsrcじゃなくrootを指している。
まぁいいけど。

```tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  darkMode: ["class"],
  content: ["./src/**/*.tsx"],
  theme: {
    extend: {
      colors: {
        border: "hsl(var(--border))",
        input: "hsl(var(--input))",
        ring: "hsl(var(--ring))",
        background: "hsl(var(--background))",
        foreground: "hsl(var(--foreground))",
        primary: {
          DEFAULT: "hsl(var(--primary))",
          foreground: "hsl(var(--primary-foreground))",
        },
        secondary: {
          DEFAULT: "hsl(var(--secondary))",
          foreground: "hsl(var(--secondary-foreground))",
        },
        destructive: {
          DEFAULT: "hsl(var(--destructive))",
          foreground: "hsl(var(--destructive-foreground))",
        },
        muted: {
          DEFAULT: "hsl(var(--muted))",
          foreground: "hsl(var(--muted-foreground))",
        },
        accent: {
          DEFAULT: "hsl(var(--accent))",
          foreground: "hsl(var(--accent-foreground))",
        },
        popover: {
          DEFAULT: "hsl(var(--popover))",
          foreground: "hsl(var(--popover-foreground))",
        },
        card: {
          DEFAULT: "hsl(var(--card))",
          foreground: "hsl(var(--card-foreground))",
        },
      },
      borderRadius: {
        lg: `var(--radius)`,
        md: `calc(var(--radius) - 2px)`,
        sm: "calc(var(--radius) - 4px)",
      },
    },
  },
  plugins: [require("tailwindcss-animate")],
}
```

ここらへんはコピペ
一部だけ変更している。NextjsのApp Routerっぽい。Reactなので注意

```diff_javascript
-  content: ["app/**/*.{ts,tsx}", "components/**/*.{ts,tsx}"],
+  content: ["./src/**/*.tsx"],
```

とりあえずデフォルトの黒いやつをコピペ

```src/index.css
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --background: 0 0% 100%;
    --foreground: 222.2 47.4% 11.2%;
    --muted: 210 40% 96.1%;
    --muted-foreground: 215.4 16.3% 46.9%;
    --popover: 0 0% 100%;
    --popover-foreground: 222.2 47.4% 11.2%;
    --border: 214.3 31.8% 91.4%;
    --input: 214.3 31.8% 91.4%;
    --card: 0 0% 100%;
    --card-foreground: 222.2 47.4% 11.2%;
    --primary: 222.2 47.4% 11.2%;
    --primary-foreground: 210 40% 98%;
    --secondary: 210 40% 96.1%;
    --secondary-foreground: 222.2 47.4% 11.2%;
    --accent: 210 40% 96.1%;
    --accent-foreground: 222.2 47.4% 11.2%;
    --destructive: 0 100% 50%;
    --destructive-foreground: 210 40% 98%;
    --ring: 215 20.2% 65.1%;
    --radius: 0.5rem;
  }

  .dark {
    --background: 224 71% 4%;
    --foreground: 213 31% 91%;
    --muted: 223 47% 11%;
    --muted-foreground: 215.4 16.3% 56.9%;
    --accent: 216 34% 17%;
    --accent-foreground: 210 40% 98%;
    --popover: 224 71% 4%;
    --popover-foreground: 215 20.2% 65.1%;
    --border: 216 34% 17%;
    --input: 216 34% 17%;
    --card: 224 71% 4%;
    --card-foreground: 213 31% 91%;
    --primary: 210 40% 98%;
    --primary-foreground: 222.2 47.4% 1.2%;
    --secondary: 222.2 47.4% 11.2%;
    --secondary-foreground: 210 40% 98%;
    --destructive: 0 63% 31%;
    --destructive-foreground: 210 40% 98%;
    --ring: 216 34% 17%;
  }
}

@layer base {
  * {
    @apply border-border;
  }
  body {
    @apply font-sans antialiased bg-background text-foreground;
  }
}

```

ないので作成

```lib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}
```

同様に作成

```components.json
{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york",
  "rsc": false,
  "tsx": true,
  "tailwind": {
    "config": "tailwind.config.js",
    "css": "src/index.css",
    "baseColor": "zinc",
    "cssVariables": true,
    "prefix": ""
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks"
  },
  "iconLibrary": "lucide"
}
```

## やってみる！
無害そうなButtonを作成してみる

```
pnpm dlx shadcn@latest add button
```

いってこーい

```
> pnpm dlx shadcn@latest add button
✔ Checking registry.
✔ Installing dependencies.
✔ Created 1 file:
  - @\components\ui\button.tsx
```

![cap1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/12898aae-cad2-ff2a-5641-8e1ab7a67b3a.png)

・・・だいぶ残念な場所に作成されたｗ

まだどこか設定が漏れてるのかも？ただ、ファイル出力されたのは嬉しいので、移動させて使ってみる

```src/App.tsx
import './App.css'
import { Button } from '@/src/components/ui/button'

function App() {

  return (
    <>
      {/* 用意された複数のvariantが利用できる */}
      <Button>Button</Button>
      <Button variant="secondary">Secondary</Button>
      <Button variant="destructive">destructive</Button>
      <Button variant="ghost">ghost</Button>
      <Button variant="link">link</Button>
      <Button variant="outline">outline</Button>

      {/* 既に指定されているclassNameでも上書き可能 */}
      <Button className='p-20'>outline</Button>
    </>
  )
}

export default App
```

![cap3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/01b782f2-19e6-a336-21ee-2d5caef0d13c.png)

良い感じ！

https://github.com/suiwave/shadcn-hello-world