shadcnUI组件库Skill shadcn-ui

shadcn/ui 是一个前端开发技能,用于快速构建基于 React 的应用用户界面。它提供了一系列预设计的、美观且可访问的 UI 组件,基于 TypeScript、Tailwind CSS 和 Radix UI,支持通过 CLI 工具复制和粘贴组件到项目中,实现高效的 UI 开发和自定义。关键词:前端开发、UI 组件、React、Tailwind CSS、shadcn/ui、Radix UI、TypeScript、可访问性、深色模式、表单验证。

前端开发 0 次安装 0 次浏览 更新于 3/18/2026

名称: shadcn-ui 描述: 实现 shadcn/ui 的指南 - 一个使用 Radix UI 和 Tailwind CSS 构建的、设计美观、可访问的 UI 组件集合。在构建用户界面、添加 UI 组件或在基于 React 的应用中实现设计系统时使用。 许可证: MIT 版本: 1.0.0

shadcn/ui 技能

shadcn/ui 是一个基于 TypeScript、Tailwind CSS 和 Radix UI 原语的、设计美观、可访问的组件集合和代码分发平台。它不是传统的组件库,而是一个可重用组件的集合,您可以复制并粘贴到您的应用中。

参考

https://ui.shadcn.com/llms.txt

何时使用此技能

在以下情况下使用此技能:

  • 使用基于 React 的框架(如 Next.js、Vite、Remix、Astro 等)构建用户界面
  • 向应用添加预构建、可访问的 UI 组件
  • 使用 Tailwind CSS 实现设计系统
  • 设置带验证的表单(使用 React Hook Form + Zod)
  • 添加数据表、图表或复杂的 UI 模式
  • 通过一致的主题实现深色模式
  • 自定义组件外观和行为

核心概念

关键原则

  • 开放代码:将组件复制到您的项目中,自由修改
  • 组合性:基于 Radix UI 的可组合原语构建
  • 分发:通过 CLI 分发组件,而非 npm 包
  • 美观默认值:经过深思熟虑的设计,具有卓越的美学
  • AI 就绪:结构化以轻松集成 AI 工具

架构

shadcn/ui 遵循独特的分发模型:

  1. CLI 工具:通过 npx shadcn@latest 安装和管理组件
  2. 组件注册表:组件的中央存储库
  3. 本地组件:组件位于您的 components/ui/ 目录中
  4. 完全所有权:您拥有代码,按需修改

技术栈

  • TypeScript:完整的类型安全
  • Tailwind CSS:优先使用实用类的样式(支持 v3 和 v4)
  • Radix UI:可访问的、无样式的原语
  • Class Variance Authority:组件变体
  • React 19:与最新 React 兼容

安装与设置

初始设置

使用 CLI(推荐):

npx shadcn@latest init

CLI 将提示:

  • 框架偏好(如 Next.js、Vite 等)
  • TypeScript 或 JavaScript
  • 组件安装位置
  • CSS 变量或 Tailwind 配置
  • 颜色主题偏好
  • 全局 CSS 文件位置

手动设置:

  1. 安装依赖:
npm install tailwindcss-animate class-variance-authority clsx tailwind-merge lucide-react
  1. 创建 components.json
{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "config": "tailwind.config.ts",
    "css": "app/globals.css",
    "baseColor": "zinc",
    "cssVariables": true
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils"
  }
}
  1. 配置 Tailwind:
// tailwind.config.ts
import type { Config } from "tailwindcss"

const config: Config = {
  darkMode: ["class"],
  content: [
    './pages/**/*.{ts,tsx}',
    './components/**/*.{ts,tsx}',
    './app/**/*.{ts,tsx}',
  ],
  theme: {
    extend: {},
  },
  plugins: [require("tailwindcss-animate")],
}

export default config
  1. 创建实用文件:
// lib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

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

添加组件

通过 CLI:

# 添加单个组件
npx shadcn@latest add button

# 添加多个组件
npx shadcn@latest add button card dialog

# 添加所有组件
npx shadcn@latest add --all

添加组件时发生的情况:

  1. 组件文件被复制到 components/ui/
  2. 依赖自动安装
  3. 组件准备好导入和使用

组件类别

表单和输入组件

按钮:

import { Button } from "@/components/ui/button"

<Button variant="default">点击我</Button>
<Button variant="destructive">删除</Button>
<Button variant="outline" size="sm">小号</Button>
<Button variant="ghost" size="icon">
  <Icon />
</Button>

输入框:

import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"

<div>
  <Label htmlFor="email">邮箱</Label>
  <Input id="email" type="email" placeholder="you@example.com" />
</div>

表单(带验证):

import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import * as z from "zod"
import {
  Form,
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"

const formSchema = z.object({
  username: z.string().min(2).max(50),
  email: z.string().email(),
})

function ProfileForm() {
  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      username: "",
      email: "",
    },
  })

  function onSubmit(values: z.infer<typeof formSchema>) {
    console.log(values)
  }

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
        <FormField
          control={form.control}
          name="username"
          render={({ field }) => (
            <FormItem>
              <FormLabel>用户名</FormLabel>
              <FormControl>
                <Input placeholder="shadcn" {...field} />
              </FormControl>
              <FormDescription>
                这是您的公共显示名称。
              </FormDescription>
              <FormMessage />
            </FormItem>
          )}
        />
        <Button type="submit">提交</Button>
      </form>
    </Form>
  )
}

选择框:

import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select"

<Select>
  <SelectTrigger className="w-[180px]">
    <SelectValue placeholder="主题" />
  </SelectTrigger>
  <SelectContent>
    <SelectItem value="light">浅色</SelectItem>
    <SelectItem value="dark">深色</SelectItem>
    <SelectItem value="system">系统</SelectItem>
  </SelectContent>
</Select>

复选框:

import { Checkbox } from "@/components/ui/checkbox"
import { Label } from "@/components/ui/label"

<div className="flex items-center space-x-2">
  <Checkbox id="terms" />
  <Label htmlFor="terms">接受条款和条件</Label>
</div>

日期选择器:

import { Calendar } from "@/components/ui/calendar"
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
import { Button } from "@/components/ui/button"
import { CalendarIcon } from "lucide-react"
import { format } from "date-fns"

const [date, setDate] = useState<Date>()

<Popover>
  <PopoverTrigger asChild>
    <Button variant="outline">
      <CalendarIcon className="mr-2 h-4 w-4" />
      {date ? format(date, "PPP") : "选择日期"}
    </Button>
  </PopoverTrigger>
  <PopoverContent className="w-auto p-0">
    <Calendar mode="single" selected={date} onSelect={setDate} />
  </PopoverContent>
</Popover>

布局和导航

卡片:

import {
  Card,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"

<Card>
  <CardHeader>
    <CardTitle>卡片标题</CardTitle>
    <CardDescription>卡片描述</CardDescription>
  </CardHeader>
  <CardContent>
    <p>卡片内容</p>
  </CardContent>
  <CardFooter>
    <p>卡片页脚</p>
  </CardFooter>
</Card>

标签页:

import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"

<Tabs defaultValue="account">
  <TabsList>
    <TabsTrigger value="account">账户</TabsTrigger>
    <TabsTrigger value="password">密码</TabsTrigger>
  </TabsList>
  <TabsContent value="account">账户设置</TabsContent>
  <TabsContent value="password">密码设置</TabsContent>
</Tabs>

手风琴:

import {
  Accordion,
  AccordionContent,
  AccordionItem,
  AccordionTrigger,
} from "@/components/ui/accordion"

<Accordion type="single" collapsible>
  <AccordionItem value="item-1">
    <AccordionTrigger>它是可访问的吗?</AccordionTrigger>
    <AccordionContent>
      是的。它遵循 WAI-ARIA 设计模式。
    </AccordionContent>
  </AccordionItem>
</Accordion>

导航菜单:

import {
  NavigationMenu,
  NavigationMenuContent,
  NavigationMenuItem,
  NavigationMenuLink,
  NavigationMenuList,
  NavigationMenuTrigger,
} from "@/components/ui/navigation-menu"

<NavigationMenu>
  <NavigationMenuList>
    <NavigationMenuItem>
      <NavigationMenuTrigger>项目一</NavigationMenuTrigger>
      <NavigationMenuContent>
        <NavigationMenuLink>链接</NavigationMenuLink>
      </NavigationMenuContent>
    </NavigationMenuItem>
  </NavigationMenuList>
</NavigationMenu>

覆盖层和对话框

对话框:

import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"

<Dialog>
  <DialogTrigger asChild>
    <Button>打开对话框</Button>
  </DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>您确定吗?</DialogTitle>
      <DialogDescription>
        此操作无法撤销。
      </DialogDescription>
    </DialogHeader>
  </DialogContent>
</Dialog>

抽屉:

import {
  Drawer,
  DrawerClose,
  DrawerContent,
  DrawerDescription,
  DrawerFooter,
  DrawerHeader,
  DrawerTitle,
  DrawerTrigger,
} from "@/components/ui/drawer"

<Drawer>
  <DrawerTrigger>打开</DrawerTrigger>
  <DrawerContent>
    <DrawerHeader>
      <DrawerTitle>您确定吗?</DrawerTitle>
      <DrawerDescription>此操作无法撤销。</DrawerDescription>
    </DrawerHeader>
    <DrawerFooter>
      <Button>提交</Button>
      <DrawerClose>取消</DrawerClose>
    </DrawerFooter>
  </DrawerContent>
</Drawer>

弹出层:

import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover"

<Popover>
  <PopoverTrigger>打开</PopoverTrigger>
  <PopoverContent>在此处放置内容。</PopoverContent>
</Popover>

提示框:

import { useToast } from "@/hooks/use-toast"
import { Button } from "@/components/ui/button"

const { toast } = useToast()

<Button
  onClick={() => {
    toast({
      title: "已安排:赶上",
      description: "2023年2月10日星期五下午5:57",
    })
  }}
>
  显示提示
</Button>

命令框:

import {
  Command,
  CommandDialog,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from "@/components/ui/command"

<Command>
  <CommandInput placeholder="输入命令或搜索..." />
  <CommandList>
    <CommandEmpty>未找到结果。</CommandEmpty>
    <CommandGroup heading="建议">
      <CommandItem>日历</CommandItem>
      <CommandItem>搜索表情符号</CommandItem>
      <CommandItem>计算器</CommandItem>
    </CommandGroup>
  </CommandList>
</Command>

反馈和状态

警告框:

import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"

<Alert>
  <AlertTitle>注意!</AlertTitle>
  <AlertDescription>
    您可以使用 CLI 向应用添加组件。
  </AlertDescription>
</Alert>

<Alert variant="destructive">
  <AlertTitle>错误</AlertTitle>
  <AlertDescription>
    您的会话已过期。请重新登录。
  </AlertDescription>
</Alert>

进度条:

import { Progress } from "@/components/ui/progress"

<Progress value={33} />

骨架屏:

import { Skeleton } from "@/components/ui/skeleton"

<div className="flex items-center space-x-4">
  <Skeleton className="h-12 w-12 rounded-full" />
  <div className="space-y-2">
    <Skeleton className="h-4 w-[250px]" />
    <Skeleton className="h-4 w-[200px]" />
  </div>
</div>

显示组件

表格:

import {
  Table,
  TableBody,
  TableCaption,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table"

<Table>
  <TableCaption>您最近发票的列表。</TableCaption>
  <TableHeader>
    <TableRow>
      <TableHead>发票</TableHead>
      <TableHead>状态</TableHead>
      <TableHead>金额</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell>INV001</TableCell>
      <TableCell>已支付</TableCell>
      <TableCell>$250.00</TableCell>
    </TableRow>
  </TableBody>
</Table>

数据表(带排序/过滤):

npx shadcn@latest add data-table

头像:

import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"

<Avatar>
  <AvatarImage src="https://github.com/shadcn.png" />
  <AvatarFallback>CN</AvatarFallback>
</Avatar>

徽章:

import { Badge } from "@/components/ui/badge"

<Badge>默认</Badge>
<Badge variant="secondary">次要</Badge>
<Badge variant="destructive">破坏性</Badge>
<Badge variant="outline">轮廓</Badge>

主题和自定义

深色模式设置

Next.js(App Router):

  1. 安装 next-themes:
npm install next-themes
  1. 创建主题提供者:
// components/theme-provider.tsx
"use client"

import * as React from "react"
import { ThemeProvider as NextThemesProvider } from "next-themes"

export function ThemeProvider({
  children,
  ...props
}: React.ComponentProps<typeof NextThemesProvider>) {
  return <NextThemesProvider {...props}>{children}</NextThemesProvider>
}
  1. 用提供者包装应用:
// app/layout.tsx
import { ThemeProvider } from "@/components/theme-provider"

export default function RootLayout({ children }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
          disableTransitionOnChange
        >
          {children}
        </ThemeProvider>
      </body>
    </html>
  )
}
  1. 添加主题切换:
import { Moon, Sun } from "lucide-react"
import { useTheme } from "next-themes"
import { Button } from "@/components/ui/button"

export function ThemeToggle() {
  const { setTheme, theme } = useTheme()

  return (
    <Button
      variant="ghost"
      size="icon"
      onClick={() => setTheme(theme === "light" ? "dark" : "light")}
    >
      <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
      <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
      <span className="sr-only">切换主题</span>
    </Button>
  )
}

颜色自定义

使用 CSS 变量:

/* globals.css */
@layer base {
  :root {
    --background: 0 0% 100%;
    --foreground: 222.2 84% 4.9%;
    --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%;
    --muted: 210 40% 96.1%;
    --muted-foreground: 215.4 16.3% 46.9%;
    --accent: 210 40% 96.1%;
    --accent-foreground: 222.2 47.4% 11.2%;
    --destructive: 0 84.2% 60.2%;
    --destructive-foreground: 210 40% 98%;
    --border: 214.3 31.8% 91.4%;
    --input: 214.3 31.8% 91.4%;
    --ring: 222.2 84% 4.9%;
    --radius: 0.5rem;
  }

  .dark {
    --background: 222.2 84% 4.9%;
    --foreground: 210 40% 98%;
    --primary: 210 40% 98%;
    --primary-foreground: 222.2 47.4% 11.2%;
    /* ... */
  }
}

使用 Tailwind 配置:

// tailwind.config.ts
export default {
  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))",
        },
        // ...
      },
    },
  },
}

组件自定义

由于组件位于您的代码库中,您可以直接修改它们:

// components/ui/button.tsx
// 修改变体,添加新变体,更改样式
const buttonVariants = cva(
  "inline-flex items-center justify-center rounded-md text-sm font-medium",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground",
        destructive: "bg-destructive text-destructive-foreground",
        outline: "border border-input bg-background",
        // 添加自定义变体
        custom: "bg-gradient-to-r from-purple-500 to-pink-500 text-white",
      },
      size: {
        default: "h-10 px-4 py-2",
        sm: "h-9 rounded-md px-3",
        lg: "h-11 rounded-md px-8",
        // 添加自定义大小
        xl: "h-14 rounded-md px-10 text-lg",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

高级模式

服务器操作(Next.js)

// app/actions.ts
"use server"

import { z } from "zod"

const schema = z.object({
  email: z.string().email(),
  password: z.string().min(8),
})

export async function createUser(formData: FormData) {
  const validatedFields = schema.safeParse({
    email: formData.get("email"),
    password: formData.get("password"),
  })

  if (!validatedFields.success) {
    return {
      errors: validatedFields.error.flatten().fieldErrors,
    }
  }

  // 创建用户
}

// app/signup/page.tsx
import { createUser } from "@/app/actions"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"

export default function SignupPage() {
  return (
    <form action={createUser}>
      <Input name="email" type="email" />
      <Input name="password" type="password" />
      <Button type="submit">注册</Button>
    </form>
  )
}

可重用表单模式

// lib/form-utils.ts
import { UseFormReturn } from "react-hook-form"
import { FormField, FormItem, FormLabel, FormControl, FormMessage } from "@/components/ui/form"
import { Input } from "@/components/ui/input"

export function TextFormField({
  form,
  name,
  label,
  placeholder,
  type = "text",
}: {
  form: UseFormReturn<any>
  name: string
  label: string
  placeholder?: string
  type?: string
}) {
  return (
    <FormField
      control={form.control}
      name={name}
      render={({ field }) => (
        <FormItem>
          <FormLabel>{label}</FormLabel>
          <FormControl>
            <Input type={type} placeholder={placeholder} {...field} />
          </FormControl>
          <FormMessage />
        </FormItem>
      )}
    />
  )
}

响应式组件组合

import { useMediaQuery } from "@/hooks/use-media-query"
import { Dialog, DialogContent } from "@/components/ui/dialog"
import { Drawer, DrawerContent } from "@/components/ui/drawer"

export function ResponsiveDialog({ children, ...props }) {
  const isDesktop = useMediaQuery("(min-width: 768px)")

  if (isDesktop) {
    return (
      <Dialog {...props}>
        <DialogContent>{children}</DialogContent>
      </Dialog>
    )
  }

  return (
    <Drawer {...props}>
      <DrawerContent>{children}</DrawerContent>
    </Drawer>
  )
}

最佳实践

  1. 使用 TypeScript:利用完整的类型安全性以获得更好的开发体验
  2. 自定义组件:直接在代码库中修改组件
  3. 组合原语:通过组合简单组件构建复杂 UI
  4. 遵循可访问性:组件基于可访问的 Radix UI 原语构建
  5. 使用表单验证:集成 React Hook Form + Zod 以实现健壮的表单
  6. 深色模式:实现主题切换以改善用户体验
  7. 响应式设计:使用 Tailwind 响应式工具
  8. 性能:对大型组件集使用代码拆分和懒加载
  9. 一致间距:一致使用 Tailwind 间距比例
  10. 图标库:使用 lucide-react 以获取一致的图标

框架特定设置

Next.js

  • 支持 App Router 和 Pages Router
  • 服务器组件兼容性
  • 服务器操作集成

Vite

  • 通过 HMR 快速开发
  • 轻松设置 TypeScript

Remix

  • 基于路由的架构
  • 渐进增强

Astro

  • 静态站点生成
  • 岛屿架构

Laravel(Inertia.js)

  • 与 Laravel 的后端集成
  • 使用 Inertia 的 React 前端

常见模式

加载状态

import { Skeleton } from "@/components/ui/skeleton"

export function UserCardSkeleton() {
  return (
    <div className="flex items-center space-x-4">
      <Skeleton className="h-12 w-12 rounded-full" />
      <div className="space-y-2">
        <Skeleton className="h-4 w-[250px]" />
        <Skeleton className="h-4 w-[200px]" />
      </div>
    </div>
  )
}

export function UserCard({ user }: { user?: User }) {
  if (!user) return <UserCardSkeleton />

  return (
    <div className="flex items-center space-x-4">
      <Avatar>
        <AvatarImage src={user.avatar} />
        <AvatarFallback>{user.initials}</AvatarFallback>
      </Avatar>
      <div>
        <p className="text-sm font-medium">{user.name}</p>
        <p className="text-sm text-muted-foreground">{user.email}</p>
      </div>
    </div>
  )
}

错误处理

import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { AlertCircle } from "lucide-react"

export function ErrorAlert({ error }: { error: Error }) {
  return (
    <Alert variant="destructive">
      <AlertCircle className="h-4 w-4" />
      <AlertTitle>错误</AlertTitle>
      <AlertDescription>{error.message}</AlertDescription>
    </Alert>
  )
}

确认对话框

import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@/components/ui/alert-dialog"

export function DeleteConfirmation({ onConfirm }: { onConfirm: () => void }) {
  return (
    <AlertDialog>
      <AlertDialogTrigger asChild>
        <Button variant="destructive">删除</Button>
      </AlertDialogTrigger>
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogTitle>您完全确定吗?</AlertDialogTitle>
          <AlertDialogDescription>
            此操作无法撤销。这将永久删除您的
            帐户并从我们的服务器中移除您的数据。
          </AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <AlertDialogCancel>取消</AlertDialogCancel>
          <AlertDialogAction onClick={onConfirm}>继续</AlertDialogAction>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  )
}

故障排除

常见问题

  1. “模块未找到”错误

    • 检查 tsconfig.json 中的路径别名
    • 验证 components.json 别名与项目结构匹配
    • 确保组件安装在正确的目录中
  2. 样式未应用

    • 验证 Tailwind CSS 是否已正确配置
    • 检查 globals.css 是否导入了 CSS 变量
    • 确保 tailwindcss-animate 插件已安装
  3. 深色模式不工作

    • 检查 ThemeProvider 是否包装了您的应用
    • 验证 <html> 标签上的 suppressHydrationWarning
    • 确保深色模式类在 CSS 中定义
  4. 表单验证问题

    • 安装所需包:react-hook-form@hookform/resolverszod
    • 检查模式是否与表单字段匹配
    • 验证解析器是否正确配置
  5. TypeScript 错误

    • 更新 @types/react@types/react-dom
    • 检查组件属性类型
    • 确保 TypeScript 版本兼容(>= 4.5)

资源

实施清单

实施 shadcn/ui 时:

  • [ ] 运行 npx shadcn@latest init 以设置项目
  • [ ] 配置 Tailwind CSS 和路径别名
  • [ ] 使用 ThemeProvider 设置深色模式
  • [ ] 通过 CLI 安装所需组件
  • [ ] 创建实用函数(如 cn 助手)
  • [ ] 设置表单处理(React Hook Form + Zod)
  • [ ] 配置图标(lucide-react)
  • [ ] 实现主题切换组件
  • [ ] 在浅色和深色模式下测试组件
  • [ ] 如有需要,自定义颜色调色板
  • [ ] 使用骨架屏添加加载状态
  • [ ] 实施错误处理模式
  • [ ] 测试可访问性功能
  • [ ] 优化捆绑包大小(树摇)
  • [ ] 添加响应式设计工具