返回首页
🎨 前端 / Web

shadcn/ui + 设计系统 2026:现代 Web 产品的 UI 实战

shadcn/ui 是 2026 年最火的前端组件库 + 设计系统方案。本文从 0 到完整设计系统,含 5 个真实项目 + 主题定制 + 组件库扩展 + A11y。

shadcn/ui · 设计系统 · Tailwind v4 · Radix UI · 前端 · UI 设计 · 设计令牌
📰

今日技术简讯

📰 技术简讯 · 2026-07-30

今日聚合 6 条热门技术内容(中文素材优先)。

🤖 AI / LLM

1. Figma AI 推出"自动设计系统"

2. v0.dev 推出 shadcn/ui 模板市场

🎨 前端 / Web

3. shadcn/ui 推出 2026 路线图

4. Radix UI 推出 Themes 2.0

  • 链接https://radix-ui.com/themes
  • 来源:Radix
  • 摘要:Radix Themes 2.0 推出完整设计系统,颜色 / 字体 / 圆角 / 阴影全面可定制。

⚙️ 后端 / 架构

5. Tailwind v4 GA

🚀 独立开发 / OPC

6. 即刻"shadcn/ui 设计系统"专题


数据来源:掘金 / InfoQ 中文 / 即刻 / 少数派 / HN 采集时间:2026-07-30 09:00 (UTC+8)

📝

今日深度文

shadcn/ui + 设计系统 2026:现代 Web 产品的 UI 实战

一句话结论:shadcn/ui + Tailwind v4 = 2026 年前端 UI 终极方案。无头组件 + 复制即用 + 完全可控 + 设计令牌统一。本文从 0 到完整设计系统。

背景

shadcn/ui 是 2026 年最火的前端组件库,GitHub 60K+ stars:

  • 不是传统组件库:复制源码到项目,完全可控
  • 基于 Radix UI:完全可访问(A11y)
  • 基于 Tailwind:原子化样式
  • TypeScript 原生:完整类型支持

为什么 shadcn/ui 取代 MUI / Ant Design:

维度 shadcn/ui MUI Ant Design
Bundle 0 200KB 500KB
定制 完全 中等 困难
A11y 优秀 优秀 中等
设计 现代 Material 传统
学习曲线

6 大核心优势

1. 复制即用,无依赖

# ✅ shadcn/ui
npx shadcn@latest add button
# 自动复制 Button.tsx 到项目,完全可控

# ❌ MUI / Ant
npm install @mui/material
# 引入整个库,难定制

2. 完全可控的源码

// shadcn/ui 生成的 Button.tsx(在项目中)
// 你可以随意修改

import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";

const buttonVariants = cva(
  "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
        destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
        outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
        secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
        ghost: "hover:bg-accent hover:text-accent-foreground",
        link: "text-primary underline-offset-4 hover:underline",
      },
      size: {
        default: "h-9 px-4 py-2",
        sm: "h-8 rounded-md px-3 text-xs",
        lg: "h-10 rounded-md px-8",
        icon: "h-9 w-9",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
);

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean;
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, asChild = false, ...props }, ref) => {
    const Comp = asChild ? Slot : "button";
    return (
      <Comp
        className={cn(buttonVariants({ variant, size, className }))}
        ref={ref}
        {...props}
      />
    );
  }
);
Button.displayName = "Button";

export { Button, buttonVariants };

3. 完全可访问(A11y)

// shadcn/ui 基于 Radix UI
// 自动支持:
// - 键盘导航(Tab / Shift+Tab / Enter / Esc)
// - 屏幕阅读器(ARIA 标签)
// - 焦点管理
// - 国际化

// 示例:Dialog
import { Dialog } from "@/components/ui/dialog";

<Dialog>
  <DialogTrigger>Open</DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>提示</DialogTitle>
    </DialogHeader>
    <DialogDescription>
      你确定要删除这个项目吗?
    </DialogDescription>
    <DialogFooter>
      <Button variant="outline">取消</Button>
      <Button variant="destructive">删除</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>

4. 完美集成 React 19(与 7/22 关联)

// shadcn/ui + React Server Components
// components/ui/button.tsx(Client Component)
"use client";

import * as React from "react";
// ...

// app/page.tsx(Server Component)
import { Button } from "@/components/ui/button";

export default function Page() {
  return (
    <Button onClick={() => alert("Hi")}>
      Click me
    </Button>
  );
}

5. 设计令牌系统

/* 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%;
    /* ... */
    
    /* 字体 */
    --font-sans: ui-sans-serif, system-ui, sans-serif;
    --font-mono: ui-monospace, monospace;
    
    /* 圆角 */
    --radius: 0.5rem;
  }
  
  .dark {
    --background: 222.2 84% 4.9%;
    --foreground: 210 40% 98%;
    /* ... */
  }
}

6. Tailwind v4 集成(与 7/22 关联)

/* app.css - Tailwind v4 CSS-first 配置 */
@import "tailwindcss";

@theme {
  --color-primary: hsl(222.2 47.4% 11.2%);
  --color-primary-foreground: hsl(210 40% 98%);
  /* ... */
}

5 个实战项目

项目 1:SaaS Dashboard

// app/dashboard/page.tsx
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";

export default function DashboardPage() {
  return (
    <div className="grid grid-cols-1 md:grid-cols-3 gap-4 p-6">
      {/* 数据卡片 */}
      <Card>
        <CardHeader>
          <CardTitle>总用户数</CardTitle>
        </CardHeader>
        <CardContent>
          <p className="text-3xl font-bold">12,345</p>
          <Badge variant="success" className="mt-2">+12%</Badge>
        </CardContent>
      </Card>
      
      {/* 图表 */}
      <Card className="col-span-2">
        <CardHeader>
          <CardTitle>用户增长</CardTitle>
        </CardHeader>
        <CardContent>
          <LineChart data={data} />
        </CardContent>
      </Card>
      
      {/* 最近活动 */}
      <Card className="md:col-span-3">
        <CardHeader>
          <CardTitle>最近活动</CardTitle>
        </CardHeader>
        <CardContent>
          <Tabs defaultValue="all">
            <TabsList>
              <TabsTrigger value="all">全部</TabsTrigger>
              <TabsTrigger value="users">用户</TabsTrigger>
              <TabsTrigger value="orders">订单</TabsTrigger>
            </TabsList>
            <TabsContent value="all">
              <ActivityList />
            </TabsContent>
          </Tabs>
        </CardContent>
      </Card>
    </div>
  );
}

项目 2:登录 / 注册页

// app/login/page.tsx
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";

export default function LoginPage() {
  return (
    <div className="flex items-center justify-center min-h-screen">
      <Card className="w-[400px]">
        <CardHeader>
          <CardTitle>登录</CardTitle>
          <CardDescription>使用邮箱登录</CardDescription>
        </CardHeader>
        <CardContent>
          <form action={login}>
            <div className="space-y-4">
              <div className="space-y-2">
                <Label htmlFor="email">邮箱</Label>
                <Input id="email" name="email" type="email" required />
              </div>
              <div className="space-y-2">
                <Label htmlFor="password">密码</Label>
                <Input id="password" name="password" type="password" required />
              </div>
              <Button type="submit" className="w-full">登录</Button>
            </div>
          </form>
        </CardContent>
        <CardFooter>
          <p className="text-sm text-muted-foreground">
            还没有账号?<a href="/signup" className="text-primary">注册</a>
          </p>
        </CardFooter>
      </Card>
    </div>
  );
}

项目 3:数据表格

// components/data-table.tsx
"use client";

import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Button } from "@/components/ui/button";
import { ArrowUpDown } from "lucide-react";

export function DataTable({ data }: { data: User[] }) {
  return (
    <div className="rounded-md border">
      <Table>
        <TableHeader>
          <TableRow>
            <TableHead>
              <Button variant="ghost" onClick={() => sort('name')}>
                姓名 <ArrowUpDown className="ml-2 h-4 w-4" />
              </Button>
            </TableHead>
            <TableHead>邮箱</TableHead>
            <TableHead>角色</TableHead>
            <TableHead>创建时间</TableHead>
          </TableRow>
        </TableHeader>
        <TableBody>
          {data.map((user) => (
            <TableRow key={user.id}>
              <TableCell className="font-medium">{user.name}</TableCell>
              <TableCell>{user.email}</TableCell>
              <TableCell>{user.role}</TableCell>
              <TableCell>{user.createdAt.toLocaleDateString()}</TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </div>
  );
}

项目 4:完整 SaaS UI(多页)

src/
├── app/
│   ├── (marketing)/        # 营销页(公开)
│   │   ├── page.tsx        # Landing
│   │   ├── pricing/page.tsx
│   │   └── about/page.tsx
│   ├── (auth)/             # 认证(公开)
│   │   ├── login/page.tsx
│   │   └── signup/page.tsx
│   ├── (dashboard)/        # 仪表板(认证)
│   │   ├── dashboard/page.tsx
│   │   ├── projects/page.tsx
│   │   ├── settings/page.tsx
│   │   └── billing/page.tsx
│   └── (admin)/            # 管理(管理员)
│       └── admin/page.tsx
└── components/
    ├── ui/                 # shadcn 组件
    ├── marketing/          # 营销组件
    ├── dashboard/          # 仪表板组件
    └── admin/              # 管理组件

项目 5:主题定制(多品牌)

// app/layout.tsx
import { ThemeProvider } from "@/components/theme-provider";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="zh" suppressHydrationWarning>
      <body>
        <ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
          disableTransitionOnChange
        >
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

// 切换主题
"use client";

import { useTheme } from "next-themes";
import { Moon, Sun } from "lucide-react";

export function ThemeToggle() {
  const { theme, setTheme } = useTheme();
  
  return (
    <Button
      variant="ghost"
      size="icon"
      onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
    >
      <Sun className="h-5 w-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
      <Moon className="absolute h-5 w-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
    </Button>
  );
}

5 个常见坑

坑 1:组件修改不生效

// ❌ 直接修改 node_modules/shadcn/...(不可行)
// ✅ shadcn/ui 是源码复制,修改项目里的文件即可

坑 2:图标库选择

// ✅ 推荐:lucide-react(Tree-shakeable)
import { ChevronDown } from "lucide-react";

// ❌ 避免:react-icons(体积大)

坑 3:表单处理

// ✅ 推荐:react-hook-form + zod
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";

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

const { register, handleSubmit, formState: { errors } } = useForm({
  resolver: zodResolver(schema),
});

坑 4:深色主题

// ❌ 忘记 suppressHydrationWarning
<html>...</html>

// ✅ 加上
<html suppressHydrationWarning>

坑 5:Server Components 限制

// ❌ useState 不能在 Server Component 用
export default function Page() {
  const [open, setOpen] = useState(false);  // ❌
}

// ✅ 加 "use client"
"use client";
export default function Page() {
  const [open, setOpen] = useState(false);  // ✅
}

性能对比

方案 Bundle 定制 学习曲线
shadcn/ui 0 完全
MUI 200KB
Ant Design 500KB 困难
Chakra UI 150KB

shadcn/ui 优势:

  • 0 Bundle:只复制用到的组件
  • 完全定制:源码在你手里
  • 现代设计:基于 Tailwind v4
  • A11y 优秀:基于 Radix UI

与之前内容的关系

7/16 TypeScript 5.6        → 前端基础
7/22 React 19 RSC          → 前端框架
7/24 Cursor + Claude Code   → 编程工具
7/29 WebGPU                 → 前沿技术
7/30 shadcn/ui 设计系统    → UI / 设计  ← 今天
→ "基础 → 框架 → 工具 → 前沿 → 设计"完整闭环

7 天落地路径

Day 1:初始化 shadcn/ui

npx create-next-app@latest my-app
cd my-app
npx shadcn@latest init

Day 2:添加核心组件

npx shadcn@latest add button card input label
npx shadcn@latest add dialog dropdown-menu tabs

Day 3:设计令牌

/* globals.css */
@layer base {
  :root {
    --primary: 222.2 47.4% 11.2%;
    /* ... */
  }
}

Day 4:深色主题

<ThemeProvider attribute="class" defaultTheme="system" />

Day 5:表单 + 验证

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";

Day 6:扩展组件

// 自定义组件:基于 shadcn/ui
<DataTable columns={columns} data={data} />

Day 7:部署

# Vercel(关联 7/18)
vercel --prod

我的看法

shadcn/ui 是 2026 年前端 UI 的"终极方案"

  1. 0 Bundle:只复制用到的
  2. 完全可控:源码在项目里
  3. A11y 优秀:基于 Radix UI
  4. 现代设计:基于 Tailwind v4
  5. 完美集成:React 19 + RSC

对独立开发者的建议:

  • 新项目首选:替代 MUI / Ant Design
  • 快速原型:v0 + shadcn 模板
  • 设计系统:统一令牌 + 组件
  • 多主题:内置深色模式

参考


本文基于 shadcn/ui 2026 + Tailwind v4,2026 年 7 月最新实战。

📚 同主题文章

🎨 前端 / Web 分类更多