11 KiB
Design System
完整的设计系统,提供可复用的 UI 组件和设计令牌,确保整个应用的一致性。
目录结构
src/design-system/
├── tokens/ # 设计令牌(颜色、间距、字体等)
├── lib/ # 工具函数
├── base/ # 基础组件
│ ├── button/
│ ├── input/
│ ├── textarea/
│ ├── card/
│ ├── checkbox/
│ ├── radio/
│ ├── switch/
│ └── select/
├── feedback/ # 反馈组件
│ ├── alert/
│ ├── progress/
│ ├── skeleton/
│ └── toast/
├── overlay/ # 覆盖组件
│ └── modal/
├── data-display/ # 数据展示组件
│ ├── badge/
│ └── divider/
├── layout/ # 布局组件
│ ├── container/
│ ├── grid/
│ └── stack/
├── navigation/ # 导航组件
│ └── tabs/
快速开始
安装依赖
pnpm add class-variance-authority clsx tailwind-merge
导入组件
// 使用显式导入以获得更好的 tree-shaking
import { Button } from '@/design-system/base/button';
import { Input } from '@/design-system/base/input';
import { Card } from '@/design-system/base/card';
使用组件
import { Button } from '@/design-system/base/button';
import { Card } from '@/design-system/base/card';
export function MyComponent() {
return (
<Card>
<h1>标题</h1>
<p>内容</p>
<Button variant="primary">点击我</Button>
</Card>
);
}
组件列表
基础组件
| 组件 | 说明 | 状态 |
|---|---|---|
| Button | 按钮 | ✅ |
| Input | 输入框 | ✅ |
| Textarea | 多行文本输入 | ✅ |
| Card | 卡片容器 | ✅ |
| Checkbox | 复选框 | ✅ |
| Radio | 单选按钮 | ✅ |
| Switch | 开关 | ✅ |
| Select | 下拉选择框 | ✅ |
反馈组件
| 组件 | 说明 | 状态 |
|---|---|---|
| Alert | 警告提示 | ✅ |
| Progress | 进度条 | ✅ |
| Skeleton | 骨架屏 | ✅ |
| Toast | 通知提示 | ✅ |
覆盖组件
| 组件 | 说明 | 状态 |
|---|---|---|
| Modal | 模态框 | ✅ |
数据展示组件
| 组件 | 说明 | 状态 |
|---|---|---|
| Badge | 徽章 | ✅ |
| Divider | 分隔线 | ✅ |
布局组件
| 组件 | 说明 | 状态 |
|---|---|---|
| Container | 容器 | ✅ |
| Grid | 网格布局 | ✅ |
| Stack | 堆叠布局 | ✅ |
导航组件
| 组件 | 说明 | 状态 |
|---|---|---|
| Tabs | 标签页 | ✅ |
组件 API
Button
按钮组件,支持多种变体和尺寸。
import { Button } from '@/design-system/base/button';
<Button variant="primary" size="md" onClick={handleClick}>
点击我
</Button>
变体 (variant): primary | secondary | success | warning | error | ghost | outline | link
尺寸 (size): sm | md | lg
快捷组件: PrimaryButton, SecondaryButton, SuccessButton, WarningButton, ErrorButton, GhostButton, OutlineButton, LinkButton
Input
输入框组件。
import { Input } from '@/design-system/base/input';
<Input
variant="bordered"
placeholder="请输入内容"
error={hasError}
/>
变体 (variant): default | bordered | filled | search
尺寸 (size): sm | md | lg
Textarea
多行文本输入组件。
import { Textarea } from '@/design-system/base/textarea';
<Textarea
variant="bordered"
placeholder="请输入内容"
rows={4}
/>
变体 (variant): default | bordered | filled
Card
卡片容器组件。
import { Card, CardHeader, CardTitle, CardBody, CardFooter } from '@/design-system/base/card';
<Card>
<CardHeader>
<CardTitle>标题</CardTitle>
</CardHeader>
<CardBody>
<p>内容</p>
</CardBody>
<CardFooter>
<Button>确定</Button>
</CardFooter>
</Card>
变体 (variant): default | bordered | elevated | flat
内边距 (padding): none | xs | sm | md | lg | xl
Checkbox
复选框组件。
import { Checkbox } from '@/design-system/base/checkbox';
<Checkbox checked={checked} onChange={setChecked}>
同意条款
</Checkbox>
Radio
单选按钮组件。
import { Radio, RadioGroup } from '@/design-system/base/radio';
<RadioGroup name="choice" value={value} onChange={setValue}>
<Radio value="1">选项 1</Radio>
<Radio value="2">选项 2</Radio>
</RadioGroup>
Switch
开关组件。
import { Switch } from '@/design-system/base/switch';
<Switch checked={enabled} onChange={setEnabled} />
Alert
警告提示组件。
import { Alert } from '@/design-system/feedback/alert';
<Alert variant="success" title="成功">
操作成功完成
</Alert>
变体 (variant): info | success | warning | error
Progress
进度条组件。
import { Progress } from '@/design-system/feedback/progress';
<Progress value={60} showLabel />
Skeleton
骨架屏组件。
import { Skeleton, TextSkeleton, CardSkeleton } from '@/design-system/feedback/skeleton';
<Skeleton className="h-4 w-32" />
<TextSkeleton lines={3} />
<CardSkeleton />
Toast
通知提示组件(基于 sonner)。
import { toast } from '@/design-system/feedback/toast';
toast.success("操作成功!");
toast.error("发生错误");
toast.promise(promise, {
loading: "加载中...",
success: "加载成功",
error: "加载失败",
});
Modal
模态框组件。
import { Modal } from '@/design-system/overlay/modal';
<Modal open={open} onClose={() => setOpen(false)}>
<Modal.Header>
<Modal.Title>标题</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>内容</p>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={() => setOpen(false)}>
取消
</Button>
<Button variant="primary">确定</Button>
</Modal.Footer>
</Modal>
Badge
徽章组件。
import { Badge } from '@/design-system/data-display/badge';
<Badge variant="success">成功</Badge>
<Badge dot />
变体 (variant): default | primary | success | warning | error | info
Divider
分隔线组件。
import { Divider } from '@/design-system/data-display/divider';
<Divider />
<Divider>或者</Divider>
<Divider orientation="vertical" />
Container
容器组件。
import { Container } from '@/design-system/layout/container';
<Container size="lg" padding="xl">
<p>内容</p>
</Container>
Grid
网格布局组件。
import { Grid } from '@/design-system/layout/grid';
<Grid cols={3} gap={4}>
<div>项目 1</div>
<div>项目 2</div>
<div>项目 3</div>
</Grid>
Stack
堆叠布局组件。
import { Stack, VStack, HStack } from '@/design-system/layout/stack';
<VStack gap={4}>
<div>项目 1</div>
<div>项目 2</div>
</VStack>
Tabs
标签页组件。
import { Tabs } from '@/design-system/navigation/tabs';
<Tabs value={activeTab} onValueChange={setActiveTab}>
<Tabs.List>
<Tabs.Trigger value="tab1">标签 1</Tabs.Trigger>
<Tabs.Trigger value="tab2">标签 2</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="tab1">
<p>内容 1</p>
</Tabs.Content>
<Tabs.Content value="tab2">
<p>内容 2</p>
</Tabs.Content>
</Tabs>
设计令牌
颜色
import { colors } from '@/design-system/tokens';
// 主色
colors.primary.500 // #35786f
// 语义色
colors.success.500 // #22c55e
colors.warning.500 // #f59e0b
colors.error.500 // #ef4444
colors.info.500 // #3b82f6
在组件中使用:
<div className="bg-primary-500 text-white">主色背景</div>
<div className="text-success-600">成功文本</div>
间距
基于 8pt 网格系统:
<div className="p-4"> // 16px
<div className="p-6"> // 24px
<div className="p-8"> // 32px
字体
<div className="text-sm">小文本</div>
<div className="text-base">正常文本</div>
<div className="text-lg">大文本</div>
<div className="font-semibold">半粗体</div>
<div className="font-bold">粗体</div>
圆角
<div className="rounded-lg"> // 8px
<div className="rounded-xl"> // 12px
<div className="rounded-2xl"> // 16px
阴影
<div className="shadow-sm"> // 小阴影
<div className="shadow-md"> // 中阴影
<div className="shadow-lg"> // 大阴影
<div className="shadow-xl"> // 超大阴影
工具函数
cn
合并 Tailwind CSS 类名的工具函数。
import { cn } from '@/design-system/lib/utils';
const className = cn(
'base-class',
isActive && 'active-class',
'another-class'
);
最佳实践
1. 组件导入
对于更好的 tree-shaking,建议从子路径导入:
// ✅ 使用显式导入
import { Button } from '@/design-system/base/button';
import { Input } from '@/design-system/base/input';
import { Card } from '@/design-system/base/card';
2. 样式覆盖
使用 className 属性覆盖样式:
<Button className="w-full">全宽按钮</Button>
3. 组合组件
利用组件组合来构建复杂 UI:
<Card>
<CardHeader>
<CardTitle>标题</CardTitle>
</CardHeader>
<CardBody>
<VStack gap={4}>
<Input placeholder="输入框" />
<Button>提交</Button>
</VStack>
</CardBody>
</Card>
4. 可访问性
所有组件都内置了可访问性支持:
- 正确的 ARIA 属性
- 键盘导航支持
- 焦点管理
- 屏幕阅读器友好
迁移指南
从旧组件迁移
旧的组件路径:
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
新的组件路径:
import { Button } from '@/design-system/base/button';
import { Input } from '@/design-system/base/input';
API 变化
大部分 API 保持兼容,但有以下变化:
-
颜色不再使用硬编码值
// 旧 style={{ backgroundColor: '#35786f' }} // 新 className="bg-primary-500" -
变体命名更加一致
// 旧 <Button variant="icon" /> // 新 <Button variant="ghost" /> -
新增语义色变体
<Button variant="success">成功</Button> <Button variant="warning">警告</Button> <Button variant="error">错误</Button>
贡献
添加新组件时,请遵循以下规范:
- 在对应的目录下创建组件
- 使用
cva定义变体样式 - 使用
forwardRef支持 ref 转发 - 添加完整的 TypeScript 类型
- 编写详细的 JSDoc 注释和示例
- 在导出文件中添加导出
许可证
AGPL-3.0-only