跨产品设计一致性完整指南
当企业拥有多个产品线时,如何保持设计语言的统一是一个重要挑战。本文将系统讲解跨产品设计一致性的策略与实践,帮助团队建立统一的品牌体验。
为什么需要跨产品设计一致性
跨产品设计一致性带来的价值:
| 维度 | 价值描述 | 具体体现 |
|---|---|---|
| 品牌认知 | 强化品牌识别度 | 用户一眼识别出品牌旗下产品 |
| 学习成本 | 降低用户学习曲线 | 熟悉一个产品后快速上手其他产品 |
| 开发效率 | 复用设计资产与组件 | 减少重复设计和开发工作 |
| 质量保障 | 统一的质量标准 | 各产品线达到相同质量水平 |
| 协作效率 | 统一的沟通语言 | 设计师、开发者沟通更顺畅 |
一致性的层次模型
跨产品设计一致性可以分为多个层次:
1. 品牌层一致性
品牌层是最高级别的一致性,确保品牌形象统一。
// 品牌规范定义
interface BrandGuidelines {
// 品牌标识
logo: {
primary: string // 主Logo
secondary: string // 副Logo
icon: string // 图标
clearSpace: number // 安全区域比例
minSize: { width: number; height: number }
}
// 品牌色彩
colors: {
primary: string // 主品牌色
secondary: string // 辅助品牌色
accent: string // 强调色
neutrals: string[] // 中性色阶
}
// 品牌字体
typography: {
displayFont: string // 展示字体
headingFont: string // 标题字体
bodyFont: string // 正文字体
}
// 品牌语调
voice: {
tone: string[] // 语调关键词:专业、友好、可信
writingStyle: string // 写作风格指南
}
}
2. 系统层一致性
系统层定义了通用的设计原则和交互模式。
// 设计系统核心规范
interface DesignSystemCore {
// 布局系统
layout: {
grid: {
columns: number
gutter: number
margin: number
breakpoints: Record<string, number>
}
spacing: Record<string, number> // 间距系统
}
// 排版系统
typography: {
scale: Record<string, { size: number; lineHeight: number; weight: number }>
measure: { min: number; max: number } // 行长范围
}
// 圆角与阴影
shape: {
borderRadius: Record<string, number>
shadows: Record<string, string>
}
// 动效规范
motion: {
duration: Record<string, number>
easing: Record<string, string>
}
// 交互模式
interactions: {
hover: InteractionPattern
focus: InteractionPattern
active: InteractionPattern
disabled: InteractionPattern
}
}
3. 组件层一致性
组件层确保相同功能的组件在不同产品中表现一致。
<!-- 统一的按钮组件 API -->
<template>
<button
:class="[
'btn',
`btn--${variant}`,
`btn--${size}`,
{ 'btn--loading': loading, 'btn--disabled': disabled }
]"
:disabled="disabled || loading"
@click="handleClick"
>
<Spinner v-if="loading" class="btn__spinner" />
<Icon v-if="prefixIcon && !loading" :name="prefixIcon" class="btn__icon--prefix" />
<span class="btn__content">
<slot />
</span>
<Icon v-if="suffixIcon" :name="suffixIcon" class="btn__icon--suffix" />
</button>
</template>
<script setup lang="ts">
// 统一的组件接口定义
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger'
size?: 'small' | 'medium' | 'large'
loading?: boolean
disabled?: boolean
prefixIcon?: string
suffixIcon?: string
block?: boolean
}
const props = withDefaults(defineProps<ButtonProps>(), {
variant: 'primary',
size: 'medium'
})
</script>
4. 模式层一致性
模式层定义了解决特定问题的通用方案。
| 设计模式 | 适用场景 | 一致性要点 |
|---|---|---|
| 表单验证 | 数据输入 | 统一的错误提示位置和样式 |
| 空状态 | 无数据展示 | 统一的插图风格和引导文案 |
| 加载状态 | 异步操作 | 统一的loading样式和位置 |
| 错误处理 | 异常场景 | 统一的错误页面和提示方式 |
| 引导流程 | 新用户引导 | 统一的步骤指示和交互方式 |
建立跨产品设计系统
设计系统架构
设计系统架构
├── 核心层 (Core)
│ ├── Design Tokens # 设计令牌
│ ├── Brand Guidelines # 品牌规范
│ └── Principles # 设计原则
│
├── 基础组件层 (Foundation)
│ ├── Primitives # 原子组件
│ ├── Layout Components # 布局组件
│ └── Typography # 排版组件
│
├── 业务组件层 (Business)
│ ├── Product A Components # 产品A专用组件
│ ├── Product B Components # 产品B专用组件
│ └── Shared Components # 共享业务组件
│
└── 模式与模板层 (Patterns)
├── Page Templates # 页面模板
├── Interaction Patterns # 交互模式
└── Content Patterns # 内容模式
设计令牌的统一管理
使用设计令牌确保视觉一致性:
{
"color": {
"brand": {
"primary": { "value": "#2563EB", "description": "主品牌色" },
"secondary": { "value": "#7C3AED", "description": "辅助品牌色" }
},
"semantic": {
"success": { "value": "#10B981" },
"warning": { "value": "#F59E0B" },
"error": { "value": "#EF4444" },
"info": { "value": "#3B82F6" }
},
"neutral": {
"50": { "value": "#F9FAFB" },
"100": { "value": "#F3F4F6" },
"200": { "value": "#E5E7EB" },
"300": { "value": "#D1D5DB" },
"400": { "value": "#9CA3AF" },
"500": { "value": "#6B7280" },
"600": { "value": "#4B5563" },
"700": { "value": "#374151" },
"800": { "value": "#1F2937" },
"900": { "value": "#111827" }
}
},
"spacing": {
"xs": { "value": "4px" },
"sm": { "value": "8px" },
"md": { "value": "16px" },
"lg": { "value": "24px" },
"xl": { "value": "32px" },
"2xl": { "value": "48px" }
},
"typography": {
"fontFamily": {
"display": { "value": "'Plus Jakarta Sans', sans-serif" },
"body": { "value": "'Inter', sans-serif" },
"mono": { "value": "'Fira Code', monospace" }
},
"fontSize": {
"xs": { "value": "12px" },
"sm": { "value": "14px" },
"base": { "value": "16px" },
"lg": { "value": "18px" },
"xl": { "value": "20px" },
"2xl": { "value": "24px" },
"3xl": { "value": "30px" }
}
},
"shadow": {
"sm": { "value": "0 1px 2px 0 rgb(0 0 0 / 0.05)" },
"md": { "value": "0 4px 6px -1px rgb(0 0 0 / 0.1)" },
"lg": { "value": "0 10px 15px -3px rgb(0 0 0 / 0.1)" }
},
"borderRadius": {
"none": { "value": "0" },
"sm": { "value": "4px" },
"md": { "value": "8px" },
"lg": { "value": "12px" },
"full": { "value": "9999px" }
}
}
令牌转换与分发
// 将设计令牌转换为各平台可用的格式
import StyleDictionary from 'style-dictionary'
const config = {
source: ['tokens/**/*.json'],
platforms: {
// CSS 变量
css: {
transformGroup: 'css',
buildPath: 'dist/css/',
files: [{
destination: 'variables.css',
format: 'css/variables'
}]
},
// JavaScript/TypeScript
js: {
transformGroup: 'js',
buildPath: 'dist/js/',
files: [{
destination: 'tokens.ts',
format: 'javascript/es6'
}]
},
// iOS
ios: {
transformGroup: 'ios',
buildPath: 'dist/ios/',
files: [{
destination: 'StyleDictionary.swift',
format: 'ios-swift/class.swift'
}]
},
// Android
android: {
transformGroup: 'android',
buildPath: 'dist/android/',
files: [{
destination: 'tokens.xml',
format: 'android/resources'
}]
}
}
}
StyleDictionary.extend(config).buildAllPlatforms()
组件库的跨产品复用
分层组件架构
// packages/ui-core/src/Button/Button.tsx
// 核心按钮组件 - 无样式,仅包含逻辑
import { forwardRef } from 'react'
import { useButton } from './useButton'
export interface ButtonCoreProps {
variant?: string
size?: string
loading?: boolean
disabled?: boolean
children: React.ReactNode
onClick?: () => void
}
export const ButtonCore = forwardRef<HTMLButtonElement, ButtonCoreProps>((props, ref) => {
const { buttonProps, isLoading, isDisabled } = useButton(props)
return (
<button ref={ref} {...buttonProps}>
{isLoading && <span className="loading-spinner" />}
{props.children}
</button>
)
})
// packages/ui-product-a/src/Button/Button.tsx
// 产品A的按钮组件 - 继承核心组件,添加产品特定样式
import { ButtonCore, ButtonCoreProps } from '@company/ui-core'
import styles from './Button.module.css'
export interface ButtonProps extends ButtonCoreProps {
// 产品A特有的属性
rounded?: boolean
}
export function Button({ rounded, className, ...props }: ButtonProps) {
return (
<ButtonCore
{...props}
className={clsx(
styles.button,
styles[`variant-${props.variant}`],
styles[`size-${props.size}`],
rounded && styles.rounded,
className
)}
/>
)
}
组件变体管理
不同产品可能需要组件的不同变体:
// 定义组件变体配置
interface ComponentVariantConfig {
button: {
// 各产品共享的变体
shared: ['primary', 'secondary', 'outline', 'ghost']
// 产品特有变体
productSpecific: {
'product-a': ['gradient', 'neon']
'product-b': ['minimal', 'elevated']
}
}
input: {
shared: ['default', 'filled', 'outlined']
productSpecific: {
'product-a': ['floating-label']
'product-b': ['inline']
}
}
}
// 创建产品特定的组件配置
function createProductComponents(productId: string, coreComponents: ComponentLibrary) {
const config = getProductConfig(productId)
return Object.entries(coreComponents).reduce((acc, [name, Component]) => {
// 应用产品特定的主题和变体
acc[name] = withProductTheme(Component, config.theme)
return acc
}, {})
}
交互模式的统一
表单交互模式
<template>
<div class="form-field" :class="{ 'has-error': hasError, 'is-focused': isFocused }">
<!-- 统一的标签位置 -->
<label :for="fieldId" class="form-field__label">
{{ label }}
<span v-if="required" class="form-field__required">*</span>
</label>
<!-- 输入区域 -->
<div class="form-field__input-wrapper">
<slot />
</div>
<!-- 统一的辅助文本区域 -->
<div class="form-field__footer">
<!-- 错误信息优先显示 -->
<span v-if="hasError" class="form-field__error">
<Icon name="alert-circle" />
{{ errorMessage }}
</span>
<!-- 否则显示帮助文本 -->
<span v-else-if="helpText" class="form-field__help">
{{ helpText }}
</span>
<!-- 字数统计 -->
<span v-if="showCount" class="form-field__count">
{{ currentLength }} / {{ maxLength }}
</span>
</div>
</div>
</template>
反馈交互模式
// 统一的反馈模式定义
interface FeedbackPatterns {
// Toast 通知:轻量级、临时性反馈
toast: {
position: 'top-center' | 'top-right' | 'bottom-center'
duration: number
maxVisible: number
types: ['success', 'error', 'warning', 'info']
}
// Message 消息:页面级反馈
message: {
position: 'top'
showIcon: boolean
closable: boolean
}
// Modal 弹窗:需要用户确认的重要操作
modal: {
sizes: ['small', 'medium', 'large']
animation: 'fade' | 'scale' | 'slide'
closeOnOverlay: boolean
closeOnEsc: boolean
}
// Notification 通知:系统级、持久性通知
notification: {
position: 'top-right' | 'bottom-right'
duration: number | null
showProgress: boolean
}
}
// 使用统一的反馈 API
const feedback = useFeedback()
// Toast:操作成功
feedback.toast.success('保存成功')
// Modal:确认删除
feedback.modal.confirm({
title: '确认删除',
content: '删除后无法恢复,是否继续?',
onConfirm: () => deleteItem()
})
// Notification:后台任务完成
feedback.notification.info({
title: '导出完成',
content: '报表已生成,点击下载',
action: { label: '下载', onClick: downloadReport }
})
质量保障机制
设计评审清单
interface DesignReviewChecklist {
// 品牌一致性
brand: {
logoUsage: boolean // Logo使用是否规范
colorPalette: boolean // 色彩是否符合品牌色
typography: boolean // 字体是否符合规范
tone: boolean // 文案语调是否一致
}
// 系统一致性
system: {
spacing: boolean // 间距是否符合规范
layout: boolean // 布局是否使用栅格
components: boolean // 是否使用设计系统组件
interactions: boolean // 交互模式是否一致
}
// 可访问性
accessibility: {
contrast: boolean // 颜色对比度是否达标
focusVisible: boolean // 焦点状态是否可见
altText: boolean // 图片是否有替代文本
keyboardNav: boolean // 是否支持键盘导航
}
// 响应式
responsive: {
breakpoints: boolean // 断点是否符合规范
touchTargets: boolean // 触控目标是否足够大
contentReflow: boolean // 内容是否正确重排
}
}
自动化检测工具
// 设计一致性检测工具
import { createLinter } from '@company/design-linter'
const linter = createLinter({
rules: {
// 检测未使用设计令牌的颜色值
'no-hardcoded-colors': 'error',
// 检测未使用间距系统的数值
'use-spacing-tokens': 'error',
// 检测非标准字号
'valid-font-size': 'error',
// 检测非标准圆角
'valid-border-radius': 'warning',
// 检测组件命名规范
'component-naming': 'error',
// 检测过时组件使用
'no-deprecated-components': 'warning'
}
})
// 在 CI/CD 中运行检测
async function runDesignLint() {
const results = await linter.lint('./src/**/*.{vue,tsx,css}')
if (results.errorCount > 0) {
console.error('设计规范检测失败:')
results.errors.forEach(error => {
console.error(` ${error.file}:${error.line} - ${error.message}`)
})
process.exit(1)
}
}
视觉回归测试
// 使用 Playwright 进行视觉回归测试
import { test, expect } from '@playwright/test'
const products = ['product-a', 'product-b', 'product-c']
const commonPages = ['login', 'dashboard', 'settings', 'profile']
// 跨产品视觉一致性测试
for (const product of products) {
test.describe(`${product} 视觉一致性`, () => {
for (const page of commonPages) {
test(`${page} 页面`, async ({ page: browserPage }) => {
await browserPage.goto(`https://${product}.company.com/${page}`)
// 截图对比
await expect(browserPage).toHaveScreenshot(`${product}-${page}.png`, {
maxDiffPixels: 100,
threshold: 0.1
})
})
}
// 组件级测试
test('按钮组件一致性', async ({ page: browserPage }) => {
await browserPage.goto(`https://${product}.company.com/storybook/button`)
const variants = ['primary', 'secondary', 'outline']
for (const variant of variants) {
await browserPage.click(`[data-variant="${variant}"]`)
await expect(browserPage.locator('.button-demo')).toHaveScreenshot(
`${product}-button-${variant}.png`
)
}
})
})
}
治理与协作
设计系统治理模型
| 模式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 中心化 | 强品牌统一需求 | 一致性最高 | 灵活性较低 |
| 联邦制 | 多产品线协作 | 平衡一致与灵活 | 需要协调机制 |
| 去中心化 | 独立产品团队 | 最大灵活性 | 一致性难保证 |
贡献与变更流程
## 设计系统变更流程
### 1. 提案阶段
- 提交 RFC (Request for Comments)
- 说明变更原因和影响范围
- 征求相关产品团队意见
### 2. 设计阶段
- 设计师完成视觉设计
- 提供各产品的适配方案
- 进行设计评审
### 3. 开发阶段
- 在核心库实现功能
- 编写单元测试和文档
- 各产品进行集成测试
### 4. 发布阶段
- 发布新版本
- 更新迁移指南
- 通知各产品团队
### 5. 采用阶段
- 各产品规划升级时间
- 执行升级并验证
- 反馈问题和建议
总结
跨产品设计一致性的关键要点:
- 建立层次化的一致性模型:从品牌层到模式层逐层统一
- 使用设计令牌管理视觉属性:确保跨平台、跨产品的一致性
- 构建可复用的组件体系:核心组件 + 产品变体的架构
- 统一交互模式和反馈机制:让用户在不同产品间无缝切换
- 建立质量保障机制:评审清单、自动化检测、视觉回归测试
- 完善治理与协作流程:明确变更流程,促进团队协作
通过系统化的设计一致性管理,企业可以在保持品牌统一的同时,让各产品保持适度的灵活性和特色。


