phase-5-design-system
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePhase 5: Design System
第五阶段:设计系统
Build platform-independent design system
构建跨平台设计系统
Purpose
目标
Build a reusable UI component library. Enable consistent design and fast development.
构建可复用的UI组件库,实现一致性设计与快速开发。
What is a Design System?
什么是设计系统?
Definition
定义
A design system is a collection of reusable components and clear standards that enables building consistent user experiences at scale.
设计系统是由可复用组件和明确标准组成的集合,可支持规模化构建一致性用户体验。
Why is it Needed? (Framework Agnostic)
为何需要设计系统?(框架无关)
| Problem | Design System Solution |
|---|---|
| Designer-developer mismatch | Single Source of Truth |
| Duplicate component development | Reusable component library |
| Inconsistent UI/UX | Unified design tokens and rules |
| Increased maintenance cost | Centralized change management |
| Delayed new member onboarding | Documented component catalog |
| 问题 | 设计系统解决方案 |
|---|---|
| 设计师与开发者认知偏差 | 单一事实来源 |
| 组件重复开发 | 可复用组件库 |
| UI/UX不一致 | 统一的设计令牌与规则 |
| 维护成本增加 | 集中式变更管理 |
| 新成员上手缓慢 | 文档化组件目录 |
3 Layers of Design System
设计系统的三层结构
┌─────────────────────────────────────────────────────┐
│ Design Tokens │
│ Color, Typography, Spacing, Radius, Shadow, ... │
├─────────────────────────────────────────────────────┤
│ Core Components │
│ Button, Input, Card, Dialog, Avatar, Badge, ... │
├─────────────────────────────────────────────────────┤
│ Composite Components │
│ Form, DataTable, Navigation, SearchBar, ... │
└─────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────┐
│ Design Tokens │
│ Color, Typography, Spacing, Radius, Shadow, ... │
├─────────────────────────────────────────────────────┤
│ Core Components │
│ Button, Input, Card, Dialog, Avatar, Badge, ... │
├─────────────────────────────────────────────────────┤
│ Composite Components │
│ Form, DataTable, Navigation, SearchBar, ... │
└─────────────────────────────────────────────────────┘Platform-specific Implementation Tools
各平台推荐实现工具
| Platform | Recommended Tools | Design Token Method |
|---|---|---|
| Web (React/Next.js) | shadcn/ui, Radix | CSS Variables |
| Web (Vue) | Vuetify, PrimeVue | CSS Variables |
| Flutter | Material 3, Custom Theme | ThemeData |
| iOS (SwiftUI) | Native Components | Asset Catalog, Color Set |
| Android (Compose) | Material 3 | MaterialTheme |
| React Native | NativeBase, Tamagui | StyleSheet + Theme |
| 平台 | 推荐工具 | 设计令牌实现方式 |
|---|---|---|
| Web(React/Next.js) | shadcn/ui, Radix | CSS Variables |
| Web(Vue) | Vuetify, PrimeVue | CSS Variables |
| Flutter | Material 3, 自定义主题 | ThemeData |
| iOS(SwiftUI) | 原生组件 | Asset Catalog, Color Set |
| Android(Compose) | Material 3 | MaterialTheme |
| React Native | NativeBase, Tamagui | StyleSheet + Theme |
What to Do in This Phase
本阶段需完成的工作
- Install Base Components: Button, Input, Card, etc.
- Customize: Adjust to project style
- Composite Components: Combine multiple base components
- Documentation: Document component usage
- 安装基础组件:Button、Input、Card等
- 自定义配置:根据项目风格调整组件
- 组合组件开发:将多个基础组件组合为复合组件
- 文档编写:记录组件使用方法
Deliverables
交付物
components/
└── ui/ # shadcn/ui components
├── button.tsx
├── input.tsx
├── card.tsx
└── ...
lib/
└── utils.ts # Utilities (cn function, etc.)
docs/02-design/
└── design-system.md # Design system specificationcomponents/
└── ui/ # shadcn/ui组件
├── button.tsx
├── input.tsx
├── card.tsx
└── ...
lib/
└── utils.ts # 工具函数(如cn函数)
docs/02-design/
└── design-system.md # 设计系统规范文档PDCA Application
PDCA循环应用
- Plan: Required component list
- Design: Component structure, variants design
- Do: Implement/customize components
- Check: Review consistency
- Act: Finalize and proceed to Phase 6
- 计划:确定所需组件列表
- 设计:组件结构与变体设计
- 执行:实现/自定义组件
- 检查:审核一致性
- 处理:定稿并进入第六阶段
Level-wise Application
分场景应用建议
| Level | Application Method |
|---|---|
| Starter | Optional (simple projects may skip) |
| Dynamic | Required |
| Enterprise | Required (including design tokens) |
| 项目规模 | 应用方式 |
|---|---|
| 入门项目 | 可选(简单项目可跳过) |
| 动态项目 | 必需 |
| 企业级项目 | 必需(包含设计令牌) |
shadcn/ui Installation
shadcn/ui 安装步骤
bash
undefinedbash
undefinedInitial setup
初始化配置
npx shadcn@latest init
npx shadcn@latest init
Add components
添加组件
npx shadcn@latest add button
npx shadcn@latest add input
npx shadcn@latest add card
undefinednpx shadcn@latest add button
npx shadcn@latest add input
npx shadcn@latest add card
undefinedRequired Component List
必需组件列表
Basic
基础组件
- Button, Input, Textarea
- Card, Badge, Avatar
- Dialog, Sheet, Popover
- Button、Input、Textarea
- Card、Badge、Avatar
- Dialog、Sheet、Popover
Form
表单组件
- Form, Label, Select
- Checkbox, Radio, Switch
- Form、Label、Select
- Checkbox、Radio、Switch
Navigation
导航组件
- Navigation Menu, Tabs
- Breadcrumb, Pagination
- Navigation Menu、Tabs
- Breadcrumb、Pagination
Custom Theme Building
自定义主题构建
Design Token Override
设计令牌覆盖
shadcn/ui is CSS variable-based, so customize themes in .
globals.csscss
/* globals.css */
@layer base {
:root {
/* ===== Colors ===== */
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 221.2 83.2% 53.3%; /* Brand main color */
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--accent: 210 40% 96.1%;
--destructive: 0 84.2% 60.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
/* ===== Typography ===== */
--font-sans: 'Pretendard', sans-serif;
--font-mono: 'JetBrains Mono', monospace;
/* ===== Spacing (rem units) ===== */
--spacing-xs: 0.25rem; /* 4px */
--spacing-sm: 0.5rem; /* 8px */
--spacing-md: 1rem; /* 16px */
--spacing-lg: 1.5rem; /* 24px */
--spacing-xl: 2rem; /* 32px */
/* ===== Border Radius ===== */
--radius: 0.5rem;
--radius-sm: 0.25rem;
--radius-lg: 0.75rem;
--radius-full: 9999px;
/* ===== Shadows ===== */
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--primary: 217.2 91.2% 59.8%;
/* ... dark mode overrides */
}
}shadcn/ui 基于CSS变量实现,可在中自定义主题。
globals.csscss
/* globals.css */
@layer base {
:root {
/* ===== 颜色 ===== */
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 221.2 83.2% 53.3%; /* 品牌主色 */
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--accent: 210 40% 96.1%;
--destructive: 0 84.2% 60.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
/* ===== 排版 ===== */
--font-sans: 'Pretendard', sans-serif;
--font-mono: 'JetBrains Mono', monospace;
/* ===== 间距(rem单位) ===== */
--spacing-xs: 0.25rem; /* 4px */
--spacing-sm: 0.5rem; /* 8px */
--spacing-md: 1rem; /* 16px */
--spacing-lg: 1.5rem; /* 24px */
--spacing-xl: 2rem; /* 32px */
/* ===== 圆角 ===== */
--radius: 0.5rem;
--radius-sm: 0.25rem;
--radius-lg: 0.75rem;
--radius-full: 9999px;
/* ===== 阴影 ===== */
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--primary: 217.2 91.2% 59.8%;
/* ... 深色模式覆盖配置 */
}
}Tailwind Config Extension
Tailwind 配置扩展
js
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
brand: {
50: 'hsl(var(--brand-50))',
500: 'hsl(var(--brand-500))',
900: 'hsl(var(--brand-900))',
},
},
fontFamily: {
sans: ['var(--font-sans)', 'system-ui'],
mono: ['var(--font-mono)', 'monospace'],
},
spacing: {
'xs': 'var(--spacing-xs)',
'sm': 'var(--spacing-sm)',
'md': 'var(--spacing-md)',
'lg': 'var(--spacing-lg)',
'xl': 'var(--spacing-xl)',
},
borderRadius: {
'sm': 'var(--radius-sm)',
'DEFAULT': 'var(--radius)',
'lg': 'var(--radius-lg)',
'full': 'var(--radius-full)',
},
},
},
}js
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
brand: {
50: 'hsl(var(--brand-50))',
500: 'hsl(var(--brand-500))',
900: 'hsl(var(--brand-900))',
},
},
fontFamily: {
sans: ['var(--font-sans)', 'system-ui'],
mono: ['var(--font-mono)', 'monospace'],
},
spacing: {
'xs': 'var(--spacing-xs)',
'sm': 'var(--spacing-sm)',
'md': 'var(--spacing-md)',
'lg': 'var(--spacing-lg)',
'xl': 'var(--spacing-xl)',
},
borderRadius: {
'sm': 'var(--radius-sm)',
'DEFAULT': 'var(--radius)',
'lg': 'var(--radius-lg)',
'full': 'var(--radius-full)',
},
},
},
}Design Token Documentation
设计令牌文档
Recommended to create per project:
docs/02-design/design-tokens.md| Token | Value | Purpose |
|---|---|---|
| | Brand main color |
| | Default border-radius |
| | Body font |
建议为每个项目创建:
docs/02-design/design-tokens.md| 令牌 | 值 | 用途 |
|---|---|---|
| | 品牌主色 |
| | 默认圆角 |
| | 正文字体 |
Component Customization
组件自定义示例
tsx
// Extend default button to project style
const Button = React.forwardRef<
HTMLButtonElement,
ButtonProps & { isLoading?: boolean }
>(({ isLoading, children, ...props }, ref) => {
return (
<ButtonPrimitive ref={ref} {...props}>
{isLoading ? <Spinner /> : children}
</ButtonPrimitive>
);
});tsx
// 扩展默认按钮以适配项目风格
const Button = React.forwardRef<
HTMLButtonElement,
ButtonProps & { isLoading?: boolean }
>(({ isLoading, children, ...props }, ref) => {
return (
<ButtonPrimitive ref={ref} {...props}>
{isLoading ? <Spinner /> : children}
</ButtonPrimitive>
);
});Mobile App Design System
移动端应用设计系统
Flutter: Custom Theme Building
Flutter:自定义主题构建
Flutter defines design tokens through .
ThemeDatadart
// lib/theme/app_theme.dart
import 'package:flutter/material.dart';
class AppTheme {
// ===== Design Tokens =====
// Colors
static const Color primary = Color(0xFF3B82F6);
static const Color secondary = Color(0xFF64748B);
static const Color destructive = Color(0xFFEF4444);
static const Color background = Color(0xFFFFFFFF);
static const Color foreground = Color(0xFF0F172A);
// Spacing
static const double spacingXs = 4.0;
static const double spacingSm = 8.0;
static const double spacingMd = 16.0;
static const double spacingLg = 24.0;
static const double spacingXl = 32.0;
// Border Radius
static const double radiusSm = 4.0;
static const double radiusMd = 8.0;
static const double radiusLg = 12.0;
static const double radiusFull = 9999.0;
// ===== Theme Data =====
static ThemeData get lightTheme => ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: primary,
brightness: Brightness.light,
),
fontFamily: 'Pretendard',
// Button Theme
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(
horizontal: spacingMd,
vertical: spacingSm,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusMd),
),
),
),
// Card Theme
cardTheme: CardTheme(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusLg),
),
),
// Input Theme
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(radiusMd),
),
contentPadding: EdgeInsets.all(spacingSm),
),
);
static ThemeData get darkTheme => ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: primary,
brightness: Brightness.dark,
),
fontFamily: 'Pretendard',
// ... dark theme overrides
);
}Flutter 通过定义设计令牌。
ThemeDatadart
// lib/theme/app_theme.dart
import 'package:flutter/material.dart';
class AppTheme {
// ===== 设计令牌 =====
// 颜色
static const Color primary = Color(0xFF3B82F6);
static const Color secondary = Color(0xFF64748B);
static const Color destructive = Color(0xFFEF4444);
static const Color background = Color(0xFFFFFFFF);
static const Color foreground = Color(0xFF0F172A);
// 间距
static const double spacingXs = 4.0;
static const double spacingSm = 8.0;
static const double spacingMd = 16.0;
static const double spacingLg = 24.0;
static const double spacingXl = 32.0;
// 圆角
static const double radiusSm = 4.0;
static const double radiusMd = 8.0;
static const double radiusLg = 12.0;
static const double radiusFull = 9999.0;
// ===== 主题数据 =====
static ThemeData get lightTheme => ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: primary,
brightness: Brightness.light,
),
fontFamily: 'Pretendard',
// 按钮主题
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(
horizontal: spacingMd,
vertical: spacingSm,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusMd),
),
),
),
// 卡片主题
cardTheme: CardTheme(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusLg),
),
),
// 输入框主题
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(radiusMd),
),
contentPadding: EdgeInsets.all(spacingSm),
),
);
static ThemeData get darkTheme => ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: primary,
brightness: Brightness.dark,
),
fontFamily: 'Pretendard',
// ... 深色主题覆盖配置
);
}Flutter: Reusable Components
Flutter:可复用组件示例
dart
// lib/components/app_button.dart
import 'package:flutter/material.dart';
import '../theme/app_theme.dart';
enum AppButtonVariant { primary, secondary, destructive, outline }
class AppButton extends StatelessWidget {
final String label;
final VoidCallback? onPressed;
final AppButtonVariant variant;
final bool isLoading;
const AppButton({
required this.label,
this.onPressed,
this.variant = AppButtonVariant.primary,
this.isLoading = false,
super.key,
});
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: isLoading ? null : onPressed,
style: _getStyle(),
child: isLoading
? SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: Text(label),
);
}
ButtonStyle _getStyle() {
switch (variant) {
case AppButtonVariant.destructive:
return ElevatedButton.styleFrom(
backgroundColor: AppTheme.destructive,
);
case AppButtonVariant.outline:
return ElevatedButton.styleFrom(
backgroundColor: Colors.transparent,
side: BorderSide(color: AppTheme.primary),
);
default:
return ElevatedButton.styleFrom();
}
}
}dart
// lib/components/app_button.dart
import 'package:flutter/material.dart';
import '../theme/app_theme.dart';
enum AppButtonVariant { primary, secondary, destructive, outline }
class AppButton extends StatelessWidget {
final String label;
final VoidCallback? onPressed;
final AppButtonVariant variant;
final bool isLoading;
const AppButton({
required this.label,
this.onPressed,
this.variant = AppButtonVariant.primary,
this.isLoading = false,
super.key,
});
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: isLoading ? null : onPressed,
style: _getStyle(),
child: isLoading
? SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: Text(label),
);
}
ButtonStyle _getStyle() {
switch (variant) {
case AppButtonVariant.destructive:
return ElevatedButton.styleFrom(
backgroundColor: AppTheme.destructive,
);
case AppButtonVariant.outline:
return ElevatedButton.styleFrom(
backgroundColor: Colors.transparent,
side: BorderSide(color: AppTheme.primary),
);
default:
return ElevatedButton.styleFrom();
}
}
}Flutter: Project Structure
Flutter:项目结构
lib/
├── theme/
│ ├── app_theme.dart # ThemeData + Design Tokens
│ ├── app_colors.dart # Color constants
│ ├── app_typography.dart # TextStyle definitions
│ └── app_spacing.dart # Spacing constants
├── components/
│ ├── app_button.dart
│ ├── app_input.dart
│ ├── app_card.dart
│ └── app_dialog.dart
└── main.dartlib/
├── theme/
│ ├── app_theme.dart # ThemeData + 设计令牌
│ ├── app_colors.dart # 颜色常量
│ ├── app_typography.dart # 文本样式定义
│ └── app_spacing.dart # 间距常量
├── components/
│ ├── app_button.dart
│ ├── app_input.dart
│ ├── app_card.dart
│ └── app_dialog.dart
└── main.dartCross-Platform Design Token Sharing
跨平台设计令牌共享
Design Token JSON (Platform Independent)
平台无关的设计令牌JSON
Centrally manage tokens with Figma Tokens or Style Dictionary.
json
// tokens/design-tokens.json
{
"color": {
"primary": { "value": "#3B82F6" },
"secondary": { "value": "#64748B" },
"destructive": { "value": "#EF4444" }
},
"spacing": {
"xs": { "value": "4px" },
"sm": { "value": "8px" },
"md": { "value": "16px" },
"lg": { "value": "24px" }
},
"radius": {
"sm": { "value": "4px" },
"md": { "value": "8px" },
"lg": { "value": "12px" }
},
"font": {
"family": { "value": "Pretendard" },
"size": {
"sm": { "value": "14px" },
"md": { "value": "16px" },
"lg": { "value": "18px" }
}
}
}通过Figma Tokens或Style Dictionary集中管理令牌。
json
// tokens/design-tokens.json
{
"color": {
"primary": { "value": "#3B82F6" },
"secondary": { "value": "#64748B" },
"destructive": { "value": "#EF4444" }
},
"spacing": {
"xs": { "value": "4px" },
"sm": { "value": "8px" },
"md": { "value": "16px" },
"lg": { "value": "24px" }
},
"radius": {
"sm": { "value": "4px" },
"md": { "value": "8px" },
"lg": { "value": "12px" }
},
"font": {
"family": { "value": "Pretendard" },
"size": {
"sm": { "value": "14px" },
"md": { "value": "16px" },
"lg": { "value": "18px" }
}
}
}Platform-specific Conversion
平台专属转换
bash
undefinedbash
undefinedGenerate tokens for each platform with Style Dictionary
使用Style Dictionary为各平台生成令牌
npx style-dictionary build
npx style-dictionary build
Output:
输出文件:
- build/css/variables.css (Web)
- build/css/variables.css (Web)
- build/dart/app_tokens.dart (Flutter)
- build/dart/app_tokens.dart (Flutter)
- build/swift/AppTokens.swift (iOS)
- build/swift/AppTokens.swift (iOS)
- build/kt/AppTokens.kt (Android)
- build/kt/AppTokens.kt (Android)
---
---Design System Checklist (Platform Agnostic)
设计系统检查清单(平台无关)
Required Items
必需项
-
Design Tokens Definition
- Colors (Primary, Secondary, Semantic)
- Typography (Font Family, Sizes, Weights)
- Spacing (xs, sm, md, lg, xl)
- Border Radius
- Shadows/Elevation
-
Core Components
- Button (variants: primary, secondary, outline, destructive)
- Input/TextField
- Card
- Dialog/Modal
- Avatar
- Badge
-
Composite Components
- Form (with validation)
- Navigation (Header, Sidebar, Bottom Nav)
- Data Display (Table, List)
-
Documentation
- Component catalog (Storybook / Widgetbook)
- Usage guidelines
- Do's and Don'ts
-
设计令牌定义
- 颜色(主色、辅助色、语义色)
- 排版(字体、字号、字重)
- 间距(xs、sm、md、lg、xl)
- 圆角
- 阴影/层级
-
核心组件
- Button(变体:主按钮、辅助按钮、轮廓按钮、危险按钮)
- Input/TextField
- Card
- Dialog/Modal
- Avatar
- Badge
-
复合组件
- Form(带校验)
- Navigation(头部、侧边栏、底部导航)
- 数据展示(Table、List)
-
文档
- 组件目录(Storybook / Widgetbook)
- 使用指南
- 注意事项
Template
模板
See
templates/pipeline/phase-5-design-system.template.md请查看
templates/pipeline/phase-5-design-system.template.mdNext Phase
下一阶段
Phase 6: UI Implementation + API Integration → Components are ready, now implement actual screens
第六阶段:UI实现 + API集成 → 组件已就绪,开始实现实际页面