shadcn/ui って何?
以下の記事に全てのっています
- コピペ方式で自由にカスタマイズ可能
- 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をインストールします。
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形式で定義する。
// postcss.config.js
+ export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+ }
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
- content: [],
+ content: ["./src/**/*.tsx"],
theme: {
extend: {},
},
plugins: [],
}
// src/index.css
+ @tailwind base;
+ @tailwind components;
+ @tailwind utilities;
tailwindが適応され、画面が崩れたらOK
shadcnのインストール
ここからが本番!!
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
設定ファイルに追記していく。
// 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なので注意
- 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
・・・だいぶ残念な場所に作成されたw
まだどこか設定が漏れてるのかも?ただ、ファイル出力されたのは嬉しいので、移動させて使ってみる
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
良い感じ!