shadcn-ui-development

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
This skill provides strict, opinionated guidelines for building polished, production-grade UIs in projects that use shadcn/ui, Tailwind CSS, and Next.js.
本技能为使用shadcn/ui、Tailwind CSS和Next.js的项目提供严格、明确的规范,用于构建精致的生产级UI。

Package Manager

包管理器

ALWAYS use
pnpm
.
Never use
npm
or
yarn
.
bash
pnpm install
pnpm add package-name
pnpm dlx shadcn@latest add button
始终使用
pnpm
,禁止使用
npm
yarn
bash
pnpm install
pnpm add package-name
pnpm dlx shadcn@latest add button

Component Rules

组件规则

Use only shadcn/ui components. Never import directly from Radix UI primitives or other UI libraries (react-bootstrap, @mui/material, etc.).
tsx
// ✅ CORRECT
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader } from "@/components/ui/card"

// ❌ WRONG
import * as Dialog from "@radix-ui/react-dialog"
import { Button } from "@mui/material"
Install missing components with:
bash
pnpm dlx shadcn@latest add [component-name]
仅使用shadcn/ui组件。禁止直接从Radix UI原语或其他UI库(如react-bootstrap、@mui/material等)导入组件。
tsx
// ✅ 正确写法
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader } from "@/components/ui/card"

// ❌ 错误写法
import * as Dialog from "@radix-ui/react-dialog"
import { Button } from "@mui/material"
安装缺失组件的命令:
bash
pnpm dlx shadcn@latest add [component-name]

Available shadcn/ui Components

可用的shadcn/ui组件

Layout: Card, Separator, Tabs, Sheet, Collapsible
Forms: Button, Input, Textarea, Select, Checkbox, Radio Group, Switch, Slider, Form, Label
Data Display: Table, Badge, Avatar, Progress, Skeleton
Overlays: Dialog, Alert Dialog, Popover, Tooltip, Alert, Toast (Sonner), Dropdown Menu, Command, Drawer
Navigation: Navigation Menu, Breadcrumb, Pagination, Scroll Area
布局类:Card、Separator、Tabs、Sheet、Collapsible
表单类:Button、Input、Textarea、Select、Checkbox、Radio Group、Switch、Slider、Form、Label
数据展示类:Table、Badge、Avatar、Progress、Skeleton
浮层类:Dialog、Alert Dialog、Popover、Tooltip、Alert、Toast(Sonner)、Dropdown Menu、Command、Drawer
导航类:Navigation Menu、Breadcrumb、Pagination、Scroll Area

Styling

样式设计

Use Tailwind CSS utility classes exclusively. No custom CSS files, no inline styles.
tsx
// ✅ CORRECT
<div className="flex items-center gap-4 p-6 rounded-lg border bg-card">

// ❌ WRONG
<div style={{ display: 'flex', padding: '24px' }}>
Use shadcn design tokens for color:
tsx
bg-background text-foreground
bg-primary text-primary-foreground
bg-secondary text-secondary-foreground
bg-muted text-muted-foreground
bg-card text-card-foreground
bg-destructive text-destructive-foreground
border-border border-input
Use
cn()
from
@/lib/utils
for conditional classes.
仅使用Tailwind CSS工具类,禁止使用自定义CSS文件或内联样式。
tsx
// ✅ 正确写法
<div className="flex items-center gap-4 p-6 rounded-lg border bg-card">

// ❌ 错误写法
<div style={{ display: 'flex', padding: '24px' }}>
使用shadcn设计令牌设置颜色:
tsx
bg-background text-foreground
bg-primary text-primary-foreground
bg-secondary text-secondary-foreground
bg-muted text-muted-foreground
bg-card text-card-foreground
bg-destructive text-destructive-foreground
border-border border-input
使用
@/lib/utils
中的
cn()
处理条件类。

Notifications

通知组件

Use Sonner for all notifications. Never use
react-hot-toast
or other toast libraries.
tsx
import { toast } from "sonner"

toast.success("Saved successfully!")
toast.error("Something went wrong")
toast.loading("Saving...")
toast.promise(saveData(), {
  loading: "Saving...",
  success: "Saved!",
  error: "Failed to save"
})
Add
<Toaster />
from
@/components/ui/sonner
to the root layout.
所有通知均使用Sonner,禁止使用
react-hot-toast
或其他Toast库。
tsx
import { toast } from "sonner"

toast.success("保存成功!")
toast.error("出现错误")
toast.loading("保存中...")
toast.promise(saveData(), {
  loading: "保存中...",
  success: "保存完成!",
  error: "保存失败"
})
在根布局中添加
@/components/ui/sonner
中的
<Toaster />
组件。

Forms

表单实现

Use shadcn Form + React Hook Form + Zod for all forms.
tsx
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import * as z from "zod"
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"

const schema = z.object({ email: z.string().email() })

export function MyForm() {
  const form = useForm({ resolver: zodResolver(schema) })
  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit((v) => console.log(v))} className="space-y-6">
        <FormField control={form.control} name="email" render={({ field }) => (
          <FormItem>
            <FormLabel>Email</FormLabel>
            <FormControl><Input {...field} /></FormControl>
            <FormMessage />
          </FormItem>
        )} />
        <Button type="submit">Submit</Button>
      </form>
    </Form>
  )
}
所有表单均使用shadcn Form + React Hook Form + Zod组合。
tsx
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import * as z from "zod"
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"

const schema = z.object({ email: z.string().email() })

export function MyForm() {
  const form = useForm({ resolver: zodResolver(schema) })
  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit((v) => console.log(v))} className="space-y-6">
        <FormField control={form.control} name="email" render={({ field }) => (
          <FormItem>
            <FormLabel>邮箱</FormLabel>
            <FormControl><Input {...field} /></FormControl>
            <FormMessage />
          </FormItem>
        )} />
        <Button type="submit">提交</Button>
      </form>
    </Form>
  )
}

Typography & Fonts

排版与字体

Use modern fonts. Recommended choices: DM Sans, Public Sans, Plus Jakarta Sans, Outfit. Avoid Inter, Roboto, Arial.
tsx
// app/layout.tsx
import { DM_Sans } from "next/font/google"
const dmSans = DM_Sans({ subsets: ["latin"], weight: ["400","500","600","700"], variable: "--font-dm-sans" })

// tailwind.config.ts
fontFamily: { sans: ["var(--font-dm-sans)", "system-ui", "sans-serif"] }
Typography scale:
tsx
<h1 className="text-4xl font-bold tracking-tight">Heading</h1>
<h2 className="text-2xl font-semibold">Section</h2>
<p className="text-base text-muted-foreground">Body</p>
<span className="text-xs font-medium uppercase tracking-wide">Label</span>
使用现代字体,推荐选择:DM SansPublic SansPlus Jakarta SansOutfit。避免使用Inter、Roboto、Arial。
tsx
// app/layout.tsx
import { DM_Sans } from "next/font/google"
const dmSans = DM_Sans({ subsets: ["latin"], weight: ["400","500","600","700"], variable: "--font-dm-sans" })

// tailwind.config.ts
fontFamily: { sans: ["var(--font-dm-sans)", "system-ui", "sans-serif"] }
排版层级示例:
tsx
<h1 className="text-4xl font-bold tracking-tight">标题</h1>
<h2 className="text-2xl font-semibold">章节标题</h2>
<p className="text-base text-muted-foreground">正文</p>
<span className="text-xs font-medium uppercase tracking-wide">标签</span>

Theming (globals.css) — OKLCH Colors

主题设置(globals.css)—— OKLCH颜色空间

CRITICAL: Always use OKLCH color space for all CSS custom properties. Never use HSL, RGB, or hex values for theme tokens. OKLCH provides perceptually uniform lightness, better color interpolation, and access to wide-gamut P3 colors.
关键要求:所有CSS自定义属性必须始终使用OKLCH颜色空间,禁止为主题令牌使用HSL、RGB或十六进制值。OKLCH提供感知均匀的亮度、更好的颜色插值效果,并且支持广色域P3颜色。

Why OKLCH

为什么选择OKLCH

  • Perceptually uniform — changing lightness actually looks uniform across hues
  • Wide-gamut support — can express colors beyond sRGB (P3, Rec2020)
  • Better for generating consistent tints/shades programmatically
  • Native in all modern browsers; Tailwind v4 uses it by default
  • 感知均匀——调整亮度时,所有色调的视觉效果保持一致
  • 支持广色域——可以表达sRGB之外的颜色(如P3、Rec2020)
  • 更便于程序化生成一致的色调/阴影
  • 所有现代浏览器原生支持;Tailwind v4默认使用该颜色空间

OKLCH Syntax

OKLCH语法

css
oklch(L C H)
/* L = lightness  0–1     (0 = black, 1 = white) */
/* C = chroma     0–0.4   (0 = gray, higher = more saturated) */
/* H = hue        0–360   (degrees on color wheel) */
css
oklch(L C H)
/* L = 亮度  0–1     (0=黑色,1=白色) */
/* C = 色度     0–0.4   (0=灰色,值越高饱和度越高) */
/* H = 色相        0–360   (色轮上的度数) */

Full globals.css Template

完整globals.css模板

css
@import "tailwindcss";

@custom-variant dark (&:is(.dark *));

:root {
  /* Neutral base */
  --background:        oklch(1 0 0);
  --foreground:        oklch(0.145 0 0);

  --card:              oklch(1 0 0);
  --card-foreground:   oklch(0.145 0 0);

  --popover:           oklch(1 0 0);
  --popover-foreground: oklch(0.145 0 0);

  /* Primary accent — change H to shift the hue */
  --primary:           oklch(0.55 0.22 29);   /* e.g. vivid red-orange */
  --primary-foreground: oklch(0.985 0.01 29);

  --secondary:         oklch(0.97 0 0);
  --secondary-foreground: oklch(0.205 0 0);

  --muted:             oklch(0.97 0 0);
  --muted-foreground:  oklch(0.556 0 0);

  --accent:            oklch(0.97 0 0);
  --accent-foreground: oklch(0.205 0 0);

  --destructive:       oklch(0.577 0.245 27.3);
  --destructive-foreground: oklch(0.985 0 0);

  --border:            oklch(0.922 0 0);
  --input:             oklch(0.922 0 0);
  --ring:              oklch(0.55 0.22 29);

  --radius: 0.5rem;

  /* Charts */
  --chart-1: oklch(0.646 0.222 41.1);
  --chart-2: oklch(0.6   0.118 184.7);
  --chart-3: oklch(0.398 0.07  227.4);
  --chart-4: oklch(0.828 0.189 84.4);
  --chart-5: oklch(0.769 0.188 70.1);

  /* Sidebar */
  --sidebar:                oklch(0.985 0 0);
  --sidebar-foreground:     oklch(0.145 0 0);
  --sidebar-primary:        oklch(0.55 0.22 29);
  --sidebar-primary-foreground: oklch(0.985 0 0);
  --sidebar-accent:         oklch(0.97 0 0);
  --sidebar-accent-foreground: oklch(0.205 0 0);
  --sidebar-border:         oklch(0.922 0 0);
  --sidebar-ring:           oklch(0.55 0.22 29);
}

.dark {
  --background:        oklch(0.145 0 0);
  --foreground:        oklch(0.985 0 0);

  --card:              oklch(0.145 0 0);
  --card-foreground:   oklch(0.985 0 0);

  --popover:           oklch(0.145 0 0);
  --popover-foreground: oklch(0.985 0 0);

  --primary:           oklch(0.55 0.22 29);
  --primary-foreground: oklch(0.985 0.01 29);

  --secondary:         oklch(0.269 0 0);
  --secondary-foreground: oklch(0.985 0 0);

  --muted:             oklch(0.269 0 0);
  --muted-foreground:  oklch(0.708 0 0);

  --accent:            oklch(0.269 0 0);
  --accent-foreground: oklch(0.985 0 0);

  --destructive:       oklch(0.396 0.141 25.7);
  --destructive-foreground: oklch(0.985 0 0);

  --border:            oklch(0.269 0 0);
  --input:             oklch(0.269 0 0);
  --ring:              oklch(0.55 0.22 29);

  /* Charts — dark */
  --chart-1: oklch(0.488 0.243 264.4);
  --chart-2: oklch(0.696 0.17  162.5);
  --chart-3: oklch(0.769 0.188  70.1);
  --chart-4: oklch(0.627 0.265 303.9);
  --chart-5: oklch(0.645 0.246  16.4);

  /* Sidebar — dark */
  --sidebar:                oklch(0.145 0 0);
  --sidebar-foreground:     oklch(0.985 0 0);
  --sidebar-primary:        oklch(0.55 0.22 29);
  --sidebar-primary-foreground: oklch(0.985 0 0);
  --sidebar-accent:         oklch(0.269 0 0);
  --sidebar-accent-foreground: oklch(0.985 0 0);
  --sidebar-border:         oklch(0.269 0 0);
  --sidebar-ring:           oklch(0.55 0.22 29);
}

@layer base {
  * { @apply border-border; }
  body { @apply bg-background text-foreground antialiased; }
  html { scroll-behavior: smooth; }
  :focus-visible { @apply outline-none ring-2 ring-ring ring-offset-2 ring-offset-background; }
}
css
@import "tailwindcss";

@custom-variant dark (&:is(.dark *));

:root {
  /* 中性基础色 */
  --background:        oklch(1 0 0);
  --foreground:        oklch(0.145 0 0);

  --card:              oklch(1 0 0);
  --card-foreground:   oklch(0.145 0 0);

  --popover:           oklch(1 0 0);
  --popover-foreground: oklch(0.145 0 0);

  /* 主强调色——修改H值可切换色相 */
  --primary:           oklch(0.55 0.22 29);   /* 例如:鲜艳的红橙色 */
  --primary-foreground: oklch(0.985 0.01 29);

  --secondary:         oklch(0.97 0 0);
  --secondary-foreground: oklch(0.205 0 0);

  --muted:             oklch(0.97 0 0);
  --muted-foreground:  oklch(0.556 0 0);

  --accent:            oklch(0.97 0 0);
  --accent-foreground: oklch(0.205 0 0);

  --destructive:       oklch(0.577 0.245 27.3);
  --destructive-foreground: oklch(0.985 0 0);

  --border:            oklch(0.922 0 0);
  --input:             oklch(0.922 0 0);
  --ring:              oklch(0.55 0.22 29);

  --radius: 0.5rem;

  /* 图表颜色 */
  --chart-1: oklch(0.646 0.222 41.1);
  --chart-2: oklch(0.6   0.118 184.7);
  --chart-3: oklch(0.398 0.07  227.4);
  --chart-4: oklch(0.828 0.189 84.4);
  --chart-5: oklch(0.769 0.188 70.1);

  /* 侧边栏颜色 */
  --sidebar:                oklch(0.985 0 0);
  --sidebar-foreground:     oklch(0.145 0 0);
  --sidebar-primary:        oklch(0.55 0.22 29);
  --sidebar-primary-foreground: oklch(0.985 0 0);
  --sidebar-accent:         oklch(0.97 0 0);
  --sidebar-accent-foreground: oklch(0.205 0 0);
  --sidebar-border:         oklch(0.922 0 0);
  --sidebar-ring:           oklch(0.55 0.22 29);
}

.dark {
  --background:        oklch(0.145 0 0);
  --foreground:        oklch(0.985 0 0);

  --card:              oklch(0.145 0 0);
  --card-foreground:   oklch(0.985 0 0);

  --popover:           oklch(0.145 0 0);
  --popover-foreground: oklch(0.985 0 0);

  --primary:           oklch(0.55 0.22 29);
  --primary-foreground: oklch(0.985 0.01 29);

  --secondary:         oklch(0.269 0 0);
  --secondary-foreground: oklch(0.985 0 0);

  --muted:             oklch(0.269 0 0);
  --muted-foreground:  oklch(0.708 0 0);

  --accent:            oklch(0.269 0 0);
  --accent-foreground: oklch(0.985 0 0);

  --destructive:       oklch(0.396 0.141 25.7);
  --destructive-foreground: oklch(0.985 0 0);

  --border:            oklch(0.269 0 0);
  --input:             oklch(0.269 0 0);
  --ring:              oklch(0.55 0.22 29);

  /* 图表颜色——深色模式 */
  --chart-1: oklch(0.488 0.243 264.4);
  --chart-2: oklch(0.696 0.17  162.5);
  --chart-3: oklch(0.769 0.188  70.1);
  --chart-4: oklch(0.627 0.265 303.9);
  --chart-5: oklch(0.645 0.246  16.4);

  /* 侧边栏颜色——深色模式 */
  --sidebar:                oklch(0.145 0 0);
  --sidebar-foreground:     oklch(0.985 0 0);
  --sidebar-primary:        oklch(0.55 0.22 29);
  --sidebar-primary-foreground: oklch(0.985 0 0);
  --sidebar-accent:         oklch(0.269 0 0);
  --sidebar-accent-foreground: oklch(0.985 0 0);
  --sidebar-border:         oklch(0.269 0 0);
  --sidebar-ring:           oklch(0.55 0.22 29);
}

@layer base {
  * { @apply border-border; }
  body { @apply bg-background text-foreground antialiased; }
  html { scroll-behavior: smooth; }
  :focus-visible { @apply outline-none ring-2 ring-ring ring-offset-2 ring-offset-background; }
}

Changing the Accent Color

修改强调色

Only touch the
--primary
,
--primary-foreground
, and
--ring
variables. Change the H (hue) value to shift the accent across the color wheel:
ColorH valueExample OKLCH
Red29
oklch(0.55 0.22 29)
Orange60
oklch(0.65 0.20 60)
Yellow90
oklch(0.75 0.18 90)
Green145
oklch(0.55 0.18 145)
Teal185
oklch(0.55 0.14 185)
Blue250
oklch(0.55 0.20 250)
Violet290
oklch(0.55 0.22 290)
Pink340
oklch(0.60 0.22 340)
仅修改
--primary
--primary-foreground
--ring
变量。通过修改**H(色相)**值在色轮上切换强调色:
颜色H值OKLCH示例
红色29
oklch(0.55 0.22 29)
橙色60
oklch(0.65 0.20 60)
黄色90
oklch(0.75 0.18 90)
绿色145
oklch(0.55 0.18 145)
蓝绿色185
oklch(0.55 0.14 185)
蓝色250
oklch(0.55 0.20 250)
紫色290
oklch(0.55 0.22 290)
粉色340
oklch(0.60 0.22 340)

Never Use These in Theme Tokens

主题令牌中禁止使用的写法

css
/* ❌ WRONG */
--primary: #e53e3e;
--primary: rgb(229, 62, 62);
--primary: hsl(0, 72%, 50%);

/* ✅ CORRECT */
--primary: oklch(0.55 0.22 29);
css
/* ❌ 错误写法 */
--primary: #e53e3e;
--primary: rgb(229, 62, 62);
--primary: hsl(0, 72%, 50%);

/* ✅ 正确写法 */
--primary: oklch(0.55 0.22 29);

Polished UI Checklist

精致UI检查清单

Before shipping any component, verify:
  • Consistent padding/margins using Tailwind spacing scale
  • Uniform border-radius across elements
  • Subtle shadows (
    shadow-sm
    ,
    shadow-md
    ) for depth
  • Smooth transitions (
    transition-all duration-200
    ) on interactive elements
  • Hover and focus states on all interactive elements
  • Loading states handled with
    <Skeleton>
    or
    toast.loading()
  • Destructive actions gated behind
    <AlertDialog>
  • Responsive layout using Tailwind breakpoints (
    sm:
    ,
    md:
    ,
    lg:
    )
  • Design tokens used for all colors (not raw hex or rgb values)
在交付任何组件前,验证以下内容:
  • 使用Tailwind间距刻度保持一致的内边距/外边距
  • 所有元素的圆角保持统一
  • 使用细微阴影(
    shadow-sm
    shadow-md
    )增加层次感
  • 交互元素添加平滑过渡(
    transition-all duration-200
  • 所有交互元素包含悬停和聚焦状态
  • 使用
    <Skeleton>
    toast.loading()
    处理加载状态
  • 破坏性操作需通过
    <AlertDialog>
    确认
  • 使用Tailwind断点(
    sm:
    md:
    lg:
    )实现响应式布局
  • 所有颜色使用设计令牌(禁止使用原始十六进制或RGB值)

Common Patterns

常见模式示例

Data Table

数据表格

tsx
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"

export function DataTable({ data }) {
  return (
    <Table>
      <TableHeader>
        <TableRow>
          <TableHead>Name</TableHead>
          <TableHead>Status</TableHead>
          <TableHead>Actions</TableHead>
        </TableRow>
      </TableHeader>
      <TableBody>
        {data.map((item) => (
          <TableRow key={item.id}>
            <TableCell>{item.name}</TableCell>
            <TableCell><Badge>{item.status}</Badge></TableCell>
            <TableCell><Button variant="ghost" size="sm">Edit</Button></TableCell>
          </TableRow>
        ))}
      </TableBody>
    </Table>
  )
}
tsx
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"

export function DataTable({ data }) {
  return (
    <Table>
      <TableHeader>
        <TableRow>
          <TableHead>名称</TableHead>
          <TableHead>状态</TableHead>
          <TableHead>操作</TableHead>
        </TableRow>
      </TableHeader>
      <TableBody>
        {data.map((item) => (
          <TableRow key={item.id}>
            <TableCell>{item.name}</TableCell>
            <TableCell><Badge>{item.status}</Badge></TableCell>
            <TableCell><Button variant="ghost" size="sm">编辑</Button></TableCell>
          </TableRow>
        ))}
      </TableBody>
    </Table>
  )
}

Dialog with Form

带表单的对话框

tsx
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogFooter } from "@/components/ui/dialog"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"

export function CreateDialog() {
  return (
    <Dialog>
      <DialogTrigger asChild><Button>Create New</Button></DialogTrigger>
      <DialogContent>
        <DialogHeader><DialogTitle>Create Item</DialogTitle></DialogHeader>
        <div className="grid gap-4 py-4">
          <div className="grid gap-2">
            <Label htmlFor="name">Name</Label>
            <Input id="name" placeholder="Enter name" />
          </div>
        </div>
        <DialogFooter>
          <Button variant="outline">Cancel</Button>
          <Button>Create</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}
tsx
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogFooter } from "@/components/ui/dialog"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"

export function CreateDialog() {
  return (
    <Dialog>
      <DialogTrigger asChild><Button>创建新项</Button></DialogTrigger>
      <DialogContent>
        <DialogHeader><DialogTitle>创建新项</DialogTitle></DialogHeader>
        <div className="grid gap-4 py-4">
          <div className="grid gap-2">
            <Label htmlFor="name">名称</Label>
            <Input id="name" placeholder="输入名称" />
          </div>
        </div>
        <DialogFooter>
          <Button variant="outline">取消</Button>
          <Button>创建</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

Loading Skeleton

加载骨架屏

tsx
import { Skeleton } from "@/components/ui/skeleton"
import { Card, CardContent, CardHeader } from "@/components/ui/card"

export function LoadingCard() {
  return (
    <Card>
      <CardHeader><Skeleton className="h-4 w-[250px]" /></CardHeader>
      <CardContent className="space-y-2">
        <Skeleton className="h-4 w-full" />
        <Skeleton className="h-4 w-[200px]" />
      </CardContent>
    </Card>
  )
}
tsx
import { Skeleton } from "@/components/ui/skeleton"
import { Card, CardContent, CardHeader } from "@/components/ui/card"

export function LoadingCard() {
  return (
    <Card>
      <CardHeader><Skeleton className="h-4 w-[250px]" /></CardHeader>
      <CardContent className="space-y-2">
        <Skeleton className="h-4 w-full" />
        <Skeleton className="h-4 w-[200px]" />
      </CardContent>
    </Card>
  )
}

Prohibited Practices

禁止的操作

❌ Never Do✅ Do Instead
import * as Dialog from "@radix-ui/react-dialog"
import { Dialog } from "@/components/ui/dialog"
import "./custom.css"
Use Tailwind classes
style={{ color: 'red' }}
className="text-destructive"
import { Button } from "@mui/material"
import { Button } from "@/components/ui/button"
import toast from "react-hot-toast"
import { toast } from "sonner"
npm install
/
yarn add
pnpm add
❌ 禁止做法✅ 正确做法
import * as Dialog from "@radix-ui/react-dialog"
import { Dialog } from "@/components/ui/dialog"
import "./custom.css"
使用Tailwind工具类
style={{ color: 'red' }}
className="text-destructive"
import { Button } from "@mui/material"
import { Button } from "@/components/ui/button"
import toast from "react-hot-toast"
import { toast } from "sonner"
npm install
/
yarn add
pnpm add