Skip to content

设计系统中的色彩体系构建

色彩是设计系统的核心要素之一,一个科学合理的色彩体系不仅能够提升产品的视觉表现,更能够传达品牌价值和改善用户体验。本文将深入探讨如何构建一套完整的设计系统色彩体系。

色彩理论基础

色彩模型

在数字设计中,我们主要使用以下色彩模型:

HSB/HSV 模型

  • 色相 (Hue):色彩的基本属性,0-360°
  • 饱和度 (Saturation):色彩的纯度,0-100%
  • 明度 (Brightness/Value):色彩的亮度,0-100%

RGB 模型

  • 红 (Red):0-255
  • 绿 (Green):0-255
  • 蓝 (Blue):0-255

HEX 表示法

十六进制色彩表示,如 #1890FF

色彩心理学

不同色彩会引发不同的心理反应:

typescript
interface ColorPsychology {
  color: string
  emotions: string[]
  usage: string[]
}

const colorMeanings: ColorPsychology[] = [
  {
    color: 'blue',
    emotions: ['信任', '专业', '稳定', '冷静'],
    usage: ['主色调', '链接', '信息提示']
  },
  {
    color: 'green',
    emotions: ['成功', '自然', '成长', '和谐'],
    usage: ['成功状态', '确认操作', '环保主题']
  },
  {
    color: 'red',
    emotions: ['紧急', '警告', '激情', '重要'],
    usage: ['错误状态', '删除操作', '重要提醒']
  },
  {
    color: 'orange',
    emotions: ['活力', '创造', '温暖', '友好'],
    usage: ['警告状态', '突出显示', '行动召唤']
  }
]

色彩体系架构

基础色彩定义

主色调 (Primary Colors)

主色调代表品牌核心色彩:

scss
// 主色调定义
$ghuo-blue-6: #1890ff;  // 主色
$ghuo-blue-1: #e6f7ff;  // 最浅
$ghuo-blue-2: #bae7ff;
$ghuo-blue-3: #91d5ff;
$ghuo-blue-4: #69c0ff;
$ghuo-blue-5: #40a9ff;
$ghuo-blue-6: #1890ff;  // 标准色
$ghuo-blue-7: #096dd9;
$ghuo-blue-8: #0050b3;
$ghuo-blue-9: #003a8c;
$ghuo-blue-10: #002766; // 最深

辅助色调 (Secondary Colors)

用于丰富视觉层次:

scss
// 成功色
$ghuo-green-6: #52c41a;
$ghuo-green-1: #f6ffed;
$ghuo-green-10: #135200;

// 警告色
$ghuo-orange-6: #faad14;
$ghuo-orange-1: #fff7e6;
$ghuo-orange-10: #ad4e00;

// 错误色
$ghuo-red-6: #f5222d;
$ghuo-red-1: #fff1f0;
$ghuo-red-10: #5c0011;

中性色 (Neutral Colors)

用于文本、背景和边框:

scss
// 中性色定义
$ghuo-gray-1: #ffffff;   // 纯白
$ghuo-gray-2: #fafafa;   // 背景色
$ghuo-gray-3: #f5f5f5;   // 分割线
$ghuo-gray-4: #f0f0f0;   // 禁用背景
$ghuo-gray-5: #d9d9d9;   // 边框色
$ghuo-gray-6: #bfbfbf;   // 图标色
$ghuo-gray-7: #8c8c8c;   // 辅助文字
$ghuo-gray-8: #595959;   // 次要文字
$ghuo-gray-9: #434343;   // 主要文字
$ghuo-gray-10: #262626;  // 标题文字
$ghuo-gray-11: #1f1f1f;  // 最深文字
$ghuo-gray-12: #141414;  // 纯黑

色彩生成算法

色彩梯度生成

基于主色自动生成色彩梯度:

typescript
interface ColorPalette {
  50: string
  100: string
  200: string
  300: string
  400: string
  500: string  // 基准色
  600: string
  700: string
  800: string
  900: string
}

function generateColorPalette(baseColor: string): ColorPalette {
  const hsl = hexToHsl(baseColor)
  
  return {
    50: hslToHex({ ...hsl, l: 95 }),
    100: hslToHex({ ...hsl, l: 90 }),
    200: hslToHex({ ...hsl, l: 80 }),
    300: hslToHex({ ...hsl, l: 70 }),
    400: hslToHex({ ...hsl, l: 60 }),
    500: baseColor, // 基准色
    600: hslToHex({ ...hsl, l: 45 }),
    700: hslToHex({ ...hsl, l: 35 }),
    800: hslToHex({ ...hsl, l: 25 }),
    900: hslToHex({ ...hsl, l: 15 })
  }
}

// 色彩空间转换函数
function hexToHsl(hex: string): { h: number; s: number; l: number } {
  const r = parseInt(hex.slice(1, 3), 16) / 255
  const g = parseInt(hex.slice(3, 5), 16) / 255
  const b = parseInt(hex.slice(5, 7), 16) / 255
  
  const max = Math.max(r, g, b)
  const min = Math.min(r, g, b)
  const diff = max - min
  
  let h = 0
  let s = 0
  const l = (max + min) / 2
  
  if (diff !== 0) {
    s = l > 0.5 ? diff / (2 - max - min) : diff / (max + min)
    
    switch (max) {
      case r:
        h = ((g - b) / diff + (g < b ? 6 : 0)) / 6
        break
      case g:
        h = ((b - r) / diff + 2) / 6
        break
      case b:
        h = ((r - g) / diff + 4) / 6
        break
    }
  }
  
  return {
    h: Math.round(h * 360),
    s: Math.round(s * 100),
    l: Math.round(l * 100)
  }
}

语义化色彩

将色彩与具体用途关联:

typescript
interface SemanticColors {
  // 品牌色彩
  brand: {
    primary: string
    secondary: string
    tertiary: string
  }
  
  // 功能色彩
  functional: {
    success: string
    warning: string
    error: string
    info: string
  }
  
  // 界面色彩
  interface: {
    background: string
    surface: string
    border: string
    divider: string
  }
  
  // 文本色彩
  text: {
    primary: string
    secondary: string
    disabled: string
    inverse: string
  }
}

const semanticColors: SemanticColors = {
  brand: {
    primary: '#1890ff',
    secondary: '#722ed1',
    tertiary: '#13c2c2'
  },
  functional: {
    success: '#52c41a',
    warning: '#faad14',
    error: '#f5222d',
    info: '#1890ff'
  },
  interface: {
    background: '#ffffff',
    surface: '#fafafa',
    border: '#d9d9d9',
    divider: '#f0f0f0'
  },
  text: {
    primary: '#262626',
    secondary: '#8c8c8c',
    disabled: '#bfbfbf',
    inverse: '#ffffff'
  }
}

无障碍设计

对比度标准

确保色彩符合 WCAG 2.1 标准:

typescript
// 对比度计算
function calculateContrast(color1: string, color2: string): number {
  const luminance1 = getLuminance(color1)
  const luminance2 = getLuminance(color2)
  
  const lighter = Math.max(luminance1, luminance2)
  const darker = Math.min(luminance1, luminance2)
  
  return (lighter + 0.05) / (darker + 0.05)
}

function getLuminance(hex: string): number {
  const rgb = hexToRgb(hex)
  const [r, g, b] = [rgb.r, rgb.g, rgb.b].map(c => {
    c = c / 255
    return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)
  })
  
  return 0.2126 * r + 0.7152 * g + 0.0722 * b
}

// 对比度验证
interface ContrastCheck {
  ratio: number
  aa: boolean      // WCAG AA 标准 (4.5:1)
  aaa: boolean     // WCAG AAA 标准 (7:1)
}

function checkContrast(foreground: string, background: string): ContrastCheck {
  const ratio = calculateContrast(foreground, background)
  
  return {
    ratio,
    aa: ratio >= 4.5,
    aaa: ratio >= 7
  }
}

色盲友好设计

考虑色盲用户的需求:

typescript
// 色盲模拟
enum ColorBlindnessType {
  Protanopia = 'protanopia',      // 红色盲
  Deuteranopia = 'deuteranopia',  // 绿色盲
  Tritanopia = 'tritanopia',      // 蓝色盲
  Monochromacy = 'monochromacy'   // 全色盲
}

function simulateColorBlindness(color: string, type: ColorBlindnessType): string {
  // 色盲模拟算法实现
  const rgb = hexToRgb(color)
  
  switch (type) {
    case ColorBlindnessType.Protanopia:
      return rgbToHex({
        r: 0.567 * rgb.r + 0.433 * rgb.g,
        g: 0.558 * rgb.r + 0.442 * rgb.g,
        b: 0.242 * rgb.g + 0.758 * rgb.b
      })
    // 其他类型的实现...
    default:
      return color
  }
}

主题系统

多主题支持

支持亮色和暗色主题:

scss
// CSS 变量定义
:root {
  // 亮色主题
  --ghuo-color-bg-base: #ffffff;
  --ghuo-color-bg-container: #ffffff;
  --ghuo-color-bg-elevated: #ffffff;
  --ghuo-color-text-base: #000000;
  --ghuo-color-text-secondary: #666666;
  --ghuo-color-border: #d9d9d9;
}

[data-theme='dark'] {
  // 暗色主题
  --ghuo-color-bg-base: #000000;
  --ghuo-color-bg-container: #141414;
  --ghuo-color-bg-elevated: #1f1f1f;
  --ghuo-color-text-base: #ffffff;
  --ghuo-color-text-secondary: #a6a6a6;
  --ghuo-color-border: #434343;
}

主题切换实现

typescript
interface ThemeConfig {
  name: string
  colors: Record<string, string>
}

class ThemeManager {
  private currentTheme: string = 'light'
  private themes: Map<string, ThemeConfig> = new Map()
  
  registerTheme(theme: ThemeConfig) {
    this.themes.set(theme.name, theme)
  }
  
  switchTheme(themeName: string) {
    const theme = this.themes.get(themeName)
    if (!theme) return
    
    const root = document.documentElement
    
    // 移除旧主题类
    root.removeAttribute('data-theme')
    
    // 应用新主题
    root.setAttribute('data-theme', themeName)
    
    // 更新 CSS 变量
    Object.entries(theme.colors).forEach(([key, value]) => {
      root.style.setProperty(`--ghuo-${key}`, value)
    })
    
    this.currentTheme = themeName
    
    // 保存用户偏好
    localStorage.setItem('ghuo-theme', themeName)
  }
  
  getCurrentTheme(): string {
    return this.currentTheme
  }
  
  // 自动检测系统主题
  detectSystemTheme() {
    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)')
    
    const updateTheme = () => {
      this.switchTheme(prefersDark.matches ? 'dark' : 'light')
    }
    
    prefersDark.addEventListener('change', updateTheme)
    updateTheme()
  }
}

色彩应用规范

组件色彩规范

按钮组件

scss
.ghuo-button {
  // 主要按钮
  &--primary {
    background-color: var(--ghuo-color-primary);
    border-color: var(--ghuo-color-primary);
    color: #ffffff;
    
    &:hover {
      background-color: var(--ghuo-color-primary-hover);
      border-color: var(--ghuo-color-primary-hover);
    }
    
    &:active {
      background-color: var(--ghuo-color-primary-active);
      border-color: var(--ghuo-color-primary-active);
    }
    
    &:disabled {
      background-color: var(--ghuo-color-bg-disabled);
      border-color: var(--ghuo-color-border);
      color: var(--ghuo-color-text-disabled);
    }
  }
  
  // 次要按钮
  &--secondary {
    background-color: transparent;
    border-color: var(--ghuo-color-border);
    color: var(--ghuo-color-text-base);
    
    &:hover {
      border-color: var(--ghuo-color-primary);
      color: var(--ghuo-color-primary);
    }
  }
  
  // 危险按钮
  &--danger {
    background-color: var(--ghuo-color-error);
    border-color: var(--ghuo-color-error);
    color: #ffffff;
  }
}

状态色彩

scss
// 状态指示器
.ghuo-status {
  &--success {
    color: var(--ghuo-color-success);
    background-color: var(--ghuo-color-success-bg);
  }
  
  &--warning {
    color: var(--ghuo-color-warning);
    background-color: var(--ghuo-color-warning-bg);
  }
  
  &--error {
    color: var(--ghuo-color-error);
    background-color: var(--ghuo-color-error-bg);
  }
  
  &--info {
    color: var(--ghuo-color-info);
    background-color: var(--ghuo-color-info-bg);
  }
}

色彩使用指南

层级关系

typescript
interface ColorHierarchy {
  level: number
  usage: string
  color: string
  contrast: number
}

const textHierarchy: ColorHierarchy[] = [
  {
    level: 1,
    usage: '标题文字',
    color: 'var(--ghuo-color-text-heading)',
    contrast: 7.0
  },
  {
    level: 2,
    usage: '正文文字',
    color: 'var(--ghuo-color-text-base)',
    contrast: 4.5
  },
  {
    level: 3,
    usage: '辅助文字',
    color: 'var(--ghuo-color-text-secondary)',
    contrast: 3.0
  },
  {
    level: 4,
    usage: '禁用文字',
    color: 'var(--ghuo-color-text-disabled)',
    contrast: 2.0
  }
]

色彩搭配原则

  1. 60-30-10 原则

    • 60%:主色调(背景色)
    • 30%:辅助色(内容区域)
    • 10%:强调色(按钮、链接)
  2. 邻近色搭配

    • 使用色相环上相邻的颜色
    • 创造和谐统一的视觉效果
  3. 对比色搭配

    • 使用色相环上相对的颜色
    • 突出重要信息和操作

工具和流程

色彩管理工具

设计工具集成

typescript
// Figma 插件开发
interface FigmaColorToken {
  name: string
  value: string
  type: 'color'
  description?: string
}

class FigmaColorSync {
  async exportTokens(): Promise<FigmaColorToken[]> {
    const colorStyles = figma.getLocalPaintStyles()
    
    return colorStyles.map(style => ({
      name: style.name.replace(/\//g, '-').toLowerCase(),
      value: this.paintToHex(style.paints[0]),
      type: 'color',
      description: style.description
    }))
  }
  
  async importTokens(tokens: FigmaColorToken[]) {
    for (const token of tokens) {
      const paint: SolidPaint = {
        type: 'SOLID',
        color: this.hexToRgb(token.value)
      }
      
      const style = figma.createPaintStyle()
      style.name = token.name
      style.paints = [paint]
      style.description = token.description || ''
    }
  }
  
  private paintToHex(paint: Paint): string {
    if (paint.type === 'SOLID') {
      const { r, g, b } = paint.color
      return `#${Math.round(r * 255).toString(16).padStart(2, '0')}${Math.round(g * 255).toString(16).padStart(2, '0')}${Math.round(b * 255).toString(16).padStart(2, '0')}`
    }
    return '#000000'
  }
}

自动化测试

typescript
// 色彩对比度测试
describe('Color Contrast Tests', () => {
  const colorPairs = [
    { fg: '#1890ff', bg: '#ffffff', name: 'Primary on White' },
    { fg: '#ffffff', bg: '#1890ff', name: 'White on Primary' },
    { fg: '#262626', bg: '#ffffff', name: 'Text on White' },
    { fg: '#8c8c8c', bg: '#ffffff', name: 'Secondary Text on White' }
  ]
  
  colorPairs.forEach(({ fg, bg, name }) => {
    test(`${name} should meet WCAG AA standards`, () => {
      const contrast = calculateContrast(fg, bg)
      expect(contrast).toBeGreaterThanOrEqual(4.5)
    })
  })
})

// 主题一致性测试
describe('Theme Consistency Tests', () => {
  test('All themes should have required color tokens', () => {
    const requiredTokens = [
      'color-primary',
      'color-success',
      'color-warning',
      'color-error',
      'color-text-base',
      'color-bg-base'
    ]
    
    const themes = ['light', 'dark']
    
    themes.forEach(theme => {
      requiredTokens.forEach(token => {
        const value = getComputedStyle(document.documentElement)
          .getPropertyValue(`--ghuo-${token}`)
        
        expect(value).toBeTruthy()
        expect(value).toMatch(/^#[0-9a-fA-F]{6}$/)
      })
    })
  })
})

文档生成

typescript
// 自动生成色彩文档
interface ColorDocumentation {
  name: string
  value: string
  usage: string[]
  contrast: { bg: string; ratio: number }[]
}

class ColorDocGenerator {
  generateDocs(colors: Record<string, string>): ColorDocumentation[] {
    return Object.entries(colors).map(([name, value]) => ({
      name,
      value,
      usage: this.getColorUsage(name),
      contrast: this.calculateContrastRatios(value)
    }))
  }
  
  private getColorUsage(colorName: string): string[] {
    const usageMap: Record<string, string[]> = {
      'primary': ['按钮', '链接', '选中状态'],
      'success': ['成功提示', '确认操作', '完成状态'],
      'warning': ['警告提示', '注意事项', '待处理状态'],
      'error': ['错误提示', '删除操作', '失败状态']
    }
    
    return usageMap[colorName] || []
  }
  
  private calculateContrastRatios(color: string): { bg: string; ratio: number }[] {
    const backgrounds = ['#ffffff', '#f5f5f5', '#000000', '#141414']
    
    return backgrounds.map(bg => ({
      bg,
      ratio: calculateContrast(color, bg)
    }))
  }
}

最佳实践

1. 色彩命名规范

typescript
// 推荐的命名方式
const colorNaming = {
  // ✅ 语义化命名
  good: [
    'color-primary',
    'color-success',
    'color-text-base',
    'color-bg-container'
  ],
  
  // ❌ 避免的命名
  bad: [
    'color-blue',
    'color-light-gray',
    'color-button-background'
  ]
}

2. 渐进增强

scss
// 提供降级方案
.ghuo-button--primary {
  background-color: #1890ff; /* 降级方案 */
  background-color: var(--ghuo-color-primary, #1890ff);
  
  color: white; /* 降级方案 */
  color: var(--ghuo-color-primary-contrast, white);
}

3. 性能优化

typescript
// 色彩预加载
const preloadColors = () => {
  const link = document.createElement('link')
  link.rel = 'preload'
  link.as = 'style'
  link.href = '/themes/colors.css'
  document.head.appendChild(link)
}

// 懒加载主题
const loadTheme = async (themeName: string) => {
  const { default: theme } = await import(`/themes/${themeName}.js`)
  return theme
}

总结

构建科学的色彩体系需要考虑:

  1. 理论基础:掌握色彩理论和心理学原理
  2. 系统性:建立完整的色彩层级和语义体系
  3. 无障碍性:确保色彩符合可访问性标准
  4. 一致性:在不同主题和场景下保持一致
  5. 可维护性:建立规范的命名和管理流程
  6. 工具支持:使用自动化工具提高效率

一个优秀的色彩体系不仅能够提升产品的视觉表现,更能够为用户提供清晰、一致的交互体验。通过科学的方法和工具,我们可以构建出既美观又实用的色彩系统。


发布于 2024年3月10日

Released under the MIT License.