AI配色方案生成与品牌一致性

智能色板生成、和谐性算法与品牌规范的工程保障


1. 配色在产品设计中的地位

颜色是用户对产品的第一印象,也是品牌辨识度的核心载体。在 AI 产品中,配色还承担额外的语义职责:区分用户内容与 AI 生成内容、传达置信度、标识不同模型/功能区域。

配色的三个维度:
+-------------------+
| 品牌维度           |  <- 企业 CI 规范、品牌识别
+-------------------+
| 功能维度           |  <- 信息层级、状态指示、交互反馈
+-------------------+
| 情感维度           |  <- 信任感、专业感、科技感
+-------------------+

1.1 AI 产品配色的特殊要求

需求 说明 约束
用户/AI 区分 用户消息与 AI 回复视觉可辨 色温/色相有明显差异
置信度色阶 高/中/低置信度需色彩编码 必须色盲安全
代码高亮 代码块需要语法高亮配色 深色背景高对比度
长时间阅读 用户长时间使用不疲劳 避免高饱和大面积色块
亮暗模式 同一色板适配两种模式 语义一致,视觉协调

2. 色彩理论基础

2.1 色彩空间

色彩空间 特点 适用场景
RGB 加色混合,设备直接使用 最终输出
HSL 色相/饱和度/亮度,直观 人工调色
HSB/HSV 类似 HSL,更贴合直觉 设计工具
LCH 感知均匀,人眼等距 算法生成
OKLCH 改进的 LCH,CSS 原生支持 现代 CSS
CIELAB 感知均匀,色差计算 色差评估

2.2 和谐配色理论

色轮上的经典和谐关系:

互补色 (Complementary):     三色 (Triadic):
    *                          *
   / \                        / \
  /   \                      /   \
 *-----*                    *-----*
(180度对立)                (120度间隔)

分裂互补 (Split-Complementary):  类似色 (Analogous):
    *                              * * *
   / \                            (相邻30度)
  /   \
 * --- *
(150+210度)

四色 (Tetradic):             正方 (Square):
 *---*                        *   *
 |   |                        |   |
 *---*                        *   *
(两对互补)                   (90度间隔)

3. AI 配色生成算法

3.1 基于种子色的色板扩展

from colormath.color_objects import sRGBColor, LabColor
from colormath.color_conversions import convert_color
import colorsys

def generate_palette_from_seed(seed_hex: str, scheme: str = 'analogous') -> list:
    """从种子色生成和谐色板"""
    r, g, b = hex_to_rgb(seed_hex)
    h, s, l = colorsys.rgb_to_hls(r/255, g/255, b/255)
    h_deg = h * 360

    if scheme == 'analogous':
        # 类似色: 种子 + 左右各 30 度
        hues = [h_deg, (h_deg + 30) % 360, (h_deg - 30) % 360]
    elif scheme == 'complementary':
        # 互补色
        hues = [h_deg, (h_deg + 180) % 360]
    elif scheme == 'triadic':
        # 三色
        hues = [h_deg, (h_deg + 120) % 360, (h_deg + 240) % 360]
    elif scheme == 'split_complementary':
        # 分裂互补
        hues = [h_deg, (h_deg + 150) % 360, (h_deg + 210) % 360]

    palette = []
    for hue in hues:
        r2, g2, b2 = colorsys.hls_to_rgb(hue/360, l, s)
        palette.append(rgb_to_hex(int(r2*255), int(g2*255), int(b2*255)))

    return palette


def generate_shade_scale(base_hex: str, steps: int = 10) -> dict:
    """生成单色的明暗阶梯 (50-950)"""
    r, g, b = hex_to_rgb(base_hex)
    h, s, l = colorsys.rgb_to_hls(r/255, g/255, b/255)

    # 明暗分布(模仿 Tailwind 的 50-950 阶梯)
    lightness_map = {
        50: 0.97,  100: 0.94,  200: 0.86,  300: 0.76,
        400: 0.62,  500: 0.50,  600: 0.40,  700: 0.32,
        800: 0.24,  900: 0.18,  950: 0.12,
    }

    scale = {}
    for step, target_l in lightness_map.items():
        # 在 OKLCH 空间中调整亮度更均匀
        adjusted_s = s * (1 - abs(target_l - 0.5) * 0.4)  # 极亮/极暗时降低饱和度
        r2, g2, b2 = colorsys.hls_to_rgb(h, target_l, adjusted_s)
        scale[step] = rgb_to_hex(
            clamp(int(r2*255)),
            clamp(int(g2*255)),
            clamp(int(b2*255))
        )

    return scale

3.2 OKLCH 感知均匀配色

OKLCH 是目前最适合程序化生成配色的色彩空间,因为它在人眼感知上是均匀的。

// 使用 OKLCH 生成感知均匀的色板
// CSS Color Level 4 原生支持: oklch(L C H)

function generateOKLCHPalette(baseHue, scheme = 'analogous') {
  const L = 0.65;  // 亮度 (0-1)
  const C = 0.15;  // 彩度 (0-0.4)

  let hues;
  switch (scheme) {
    case 'analogous':
      hues = [baseHue, baseHue + 30, baseHue - 30];
      break;
    case 'complementary':
      hues = [baseHue, baseHue + 180];
      break;
    case 'triadic':
      hues = [baseHue, baseHue + 120, baseHue + 240];
      break;
  }

  return hues.map(h => ({
    css: `oklch(${L} ${C} ${h % 360})`,
    hue: h % 360,
  }));
}

// 在 CSS 中使用
// .primary { color: oklch(0.65 0.15 250); }  /* 蓝色 */
// .accent  { color: oklch(0.65 0.15 30);  }  /* 橙色 */

3.3 LLM 辅助配色

def generate_palette_with_llm(description: str) -> dict:
    """使用 LLM 生成语义化配色方案"""
    prompt = f"""你是一位专业的 UI 配色设计师。根据以下描述生成一套产品配色方案。

描述: {description}

要求:
1. 输出 JSON 格式
2. 包含 primary, secondary, accent, success, warning, error 六组色
3. 每组色包含 main (主色) 和 contrast (对比色)
4. 所有文字/背景组合的对比度必须 >= 4.5:1
5. 配色必须色盲安全(红绿色盲可区分)
6. 适合长时间阅读,避免高饱和度

输出格式:
{{
  "palette_name": "名称",
  "mood": "情绪描述",
  "primary": {{ "main": "#hex", "contrast": "#hex", "subtle": "#hex" }},
  "secondary": {{ "main": "#hex", "contrast": "#hex", "subtle": "#hex" }},
  "accent": {{ "main": "#hex", "contrast": "#hex" }},
  "success": {{ "main": "#hex", "contrast": "#hex" }},
  "warning": {{ "main": "#hex", "contrast": "#hex" }},
  "error": {{ "main": "#hex", "contrast": "#hex" }},
  "neutral": {{
    "50": "#hex", "100": "#hex", "200": "#hex",
    "300": "#hex", "400": "#hex", "500": "#hex",
    "600": "#hex", "700": "#hex", "800": "#hex", "900": "#hex"
  }}
}}"""

    response = llm.generate(prompt)
    palette = json.loads(response)

    # 自动验证对比度
    validate_contrast(palette)

    # 自动验证色盲安全性
    validate_color_blind_safety(palette)

    return palette

4. 品牌一致性保障

4.1 品牌色彩规范

{
  "brand": {
    "name": "合规网",
    "primary": {
      "main": "#1E40AF",
      "range": ["#1E3A8A", "#2563EB"],
      "tolerance": 5
    },
    "secondary": {
      "main": "#7C3AED",
      "range": ["#6D28D9", "#8B5CF6"],
      "tolerance": 5
    },
    "forbidden_colors": ["#FF0000", "#00FF00"],
    "rules": {
      "max_primary_area_percent": 30,
      "min_neutral_area_percent": 40,
      "primary_only_for": ["CTA_buttons", "links", "active_states"],
      "logo_clear_space": "2x logo height",
      "logo_min_contrast": 4.5
    }
  }
}

4.2 自动一致性检查

interface BrandComplianceResult {
  score: number;        // 0-100
  issues: Issue[];
  suggestions: string[];
}

function checkBrandCompliance(
  pageColors: Color[],
  brandConfig: BrandConfig
): BrandComplianceResult {
  const issues: Issue[] = [];

  // 1. 检查是否使用了品牌色
  const usedPrimary = pageColors.some(c =>
    colorDistance(c, brandConfig.primary.main) <= brandConfig.primary.tolerance
  );
  if (!usedPrimary) {
    issues.push({
      type: 'missing_brand_color',
      severity: 'error',
      message: '页面中未使用品牌主色'
    });
  }

  // 2. 检查禁用色
  for (const color of pageColors) {
    for (const forbidden of brandConfig.forbidden_colors) {
      if (colorDistance(color, forbidden) < 10) {
        issues.push({
          type: 'forbidden_color',
          severity: 'error',
          message: `使用了禁用颜色 ${color}(接近 ${forbidden})`
        });
      }
    }
  }

  // 3. 检查色彩面积比例
  const primaryArea = calculateColorArea(pageColors, brandConfig.primary.main);
  if (primaryArea > brandConfig.rules.max_primary_area_percent) {
    issues.push({
      type: 'excessive_primary',
      severity: 'warning',
      message: `主色面积占比 ${primaryArea}%,超过上限 ${brandConfig.rules.max_primary_area_percent}%`
    });
  }

  // 4. 检查对比度
  for (const pair of getTextBackgroundPairs(pageColors)) {
    const ratio = contrastRatio(pair.text, pair.bg);
    if (ratio < 4.5) {
      issues.push({
        type: 'insufficient_contrast',
        severity: 'error',
        message: `对比度 ${ratio.toFixed(1)}:1 不足(要求 >= 4.5:1)`
      });
    }
  }

  const score = Math.max(0, 100 - issues.length * 15);
  return { score, issues, suggestions: generateSuggestions(issues) };
}

4.3 色彩距离计算

// CIEDE2000 色差公式(最准确的感知色差)
function ciede2000(lab1, lab2) {
  const [L1, a1, b1] = lab1;
  const [L2, a2, b2] = lab2;

  // 简化版 CIEDE2000
  const dL = L2 - L1;
  const Lbar = (L1 + L2) / 2;
  const C1 = Math.sqrt(a1*a1 + b1*b1);
  const C2 = Math.sqrt(a2*a2 + b2*b2);
  const Cbar = (C1 + C2) / 2;

  const dC = C2 - C1;
  const dH = Math.sqrt(
    Math.max(0, (a2-a1)**2 + (b2-b1)**2 - dC**2)
  );

  const SL = 1 + 0.015 * (Lbar - 50)**2 / Math.sqrt(20 + (Lbar - 50)**2);
  const SC = 1 + 0.045 * Cbar;
  const SH = 1 + 0.015 * Cbar;

  return Math.sqrt(
    (dL/SL)**2 + (dC/SC)**2 + (dH/SH)**2
  );
}

// 色差阈值参考
// < 1:   人眼不可分辨
// 1-2:   近距离仔细看可分辨
// 2-5:   一眼可见差异
// 5-10:  明显不同的颜色
// > 10:  完全不同的颜色

5. 主题生成系统

5.1 从品牌色自动生成完整主题

function generateThemeFromBrand(primaryHex: string): Theme {
  // 生成主色阶梯
  const primaryScale = generateShadeScale(primaryHex);

  // 自动选择互补色作为 accent
  const [h, s, l] = hexToHSL(primaryHex);
  const accentHue = (h + 180) % 360;
  const accentHex = hslToHex(accentHue, s * 0.8, l);
  const accentScale = generateShadeScale(accentHex);

  // 生成中性色(带品牌色调的灰)
  const neutralScale = generateNeutralScale(primaryHex);

  // 组装亮色主题
  const lightTheme = {
    primary: primaryScale[600],
    primaryHover: primaryScale[700],
    primarySubtle: primaryScale[50],
    accent: accentScale[500],
    textPrimary: neutralScale[900],
    textSecondary: neutralScale[600],
    textMuted: neutralScale[400],
    bgPrimary: '#FFFFFF',
    bgSecondary: neutralScale[50],
    border: neutralScale[200],
    success: '#22C55E',
    warning: '#F59E0B',
    error: '#EF4444',
  };

  // 自动推导暗色主题
  const darkTheme = deriveDarkTheme(lightTheme, primaryScale, neutralScale);

  return { light: lightTheme, dark: darkTheme, scales: { primaryScale, accentScale, neutralScale } };
}

function generateNeutralScale(brandHex: string): Record<number, string> {
  // 带品牌色调的中性灰色
  const [h, s] = hexToHSL(brandHex);
  const scale: Record<number, string> = {};

  const lightnessMap = {
    50: 97, 100: 94, 200: 86, 300: 75,
    400: 56, 500: 45, 600: 35, 700: 25,
    800: 15, 900: 8
  };

  for (const [step, lightness] of Object.entries(lightnessMap)) {
    // 在中性灰中混入少量品牌色相
    scale[Number(step)] = hslToHex(h, s * 0.05, lightness / 100);
  }

  return scale;
}

5.2 品牌色调的中性灰

纯灰:                    品牌灰 (带蓝调):
#F9FAFB  gray-50         #F8FAFC  slate-50
#F3F4F6  gray-100        #F1F5F9  slate-100
#E5E7EB  gray-200        #E2E8F0  slate-200
#D1D5DB  gray-300        #CBD5E1  slate-300
#9CA3AF  gray-400        #94A3B8  slate-400
#6B7280  gray-500        #64748B  slate-500
#4B5563  gray-600        #475569  slate-600
#374151  gray-700        #334155  slate-700
#1F2937  gray-800        #1E293B  slate-800
#111827  gray-900        #0F172A  slate-900

品牌灰的优势:
- 与品牌色更和谐
- 更有"品牌感"
- 避免纯灰的"廉价感"

6. 色盲安全验证

6.1 验证流程

def validate_color_blind_safety(palette: dict) -> list:
    """验证色板的色盲安全性"""
    critical_pairs = [
        ('success', 'error'),      # 成功/错误必须可区分
        ('primary', 'accent'),     # 主色/强调色必须可区分
        ('warning', 'error'),      # 警告/错误必须可区分
    ]

    issues = []
    cb_types = ['protanopia', 'deuteranopia', 'tritanopia']

    for type_name in cb_types:
        for color_a_key, color_b_key in critical_pairs:
            color_a = palette[color_a_key]['main']
            color_b = palette[color_b_key]['main']

            sim_a = simulate_color_blind(color_a, type_name)
            sim_b = simulate_color_blind(color_b, type_name)

            diff = ciede2000(hex_to_lab(sim_a), hex_to_lab(sim_b))

            if diff < 15:  # 色盲模拟后色差不足
                issues.append({
                    'type': type_name,
                    'pair': (color_a_key, color_b_key),
                    'original_diff': ciede2000(
                        hex_to_lab(color_a), hex_to_lab(color_b)
                    ),
                    'simulated_diff': diff,
                    'suggestion': f'{color_a_key} 和 {color_b_key} 在 {type_name} 色盲下难以区分'
                })

    return issues

6.2 安全色板模板

通用色盲安全色板 (经验证):

主要序列色:
  蓝    #2563EB   (安全: 色盲下仍为蓝)
  橙    #F59E0B   (安全: 色盲下仍为暖色)
  紫    #7C3AED   (安全: 色盲下偏蓝)
  灰    #6B7280   (安全: 中性)

状态色:
  成功  #2563EB + 对勾图标  (不用绿色! 用蓝色+图标)
  错误  #EF4444 + 叉号图标  (红色 + 图标双重编码)
  警告  #F59E0B + 感叹号    (橙色 + 图标)

替代方案:
  用蓝/橙替代红/绿
  用图标/形状/纹理辅助区分
  用标签文字直接说明

7. 配色工具链

7.1 生成工具

工具 功能 输出
Coolors.co 随机配色生成 + 锁定调整 HEX / RGB
Realtime Colors 实时预览配色在界面上的效果 CSS 变量
Huemint AI 配色生成 (品牌定制) HEX
Leonardo (Adobe) 基于对比度的配色生成 Design Token
OKLCH Color Picker OKLCH 空间手动调色 oklch()
Tailwind CSS Colors 预设色板参考 Tailwind Config

7.2 验证工具

工具 功能
WebAIM Contrast Checker 对比度计算
Color Oracle 色盲模拟(桌面应用)
Stark (Figma) 对比度 + 色盲检查
Chrome DevTools 强制色彩模拟
a11y.color 批量对比度检查

8. CSS 现代配色技术

8.1 OKLCH 在 CSS 中的使用

:root {
  /* OKLCH: 亮度 / 彩度 / 色相 */
  --primary-l: 0.55;
  --primary-c: 0.18;
  --primary-h: 250;

  --color-primary: oklch(
    var(--primary-l)
    var(--primary-c)
    var(--primary-h)
  );

  /* 自动生成明暗变体 */
  --color-primary-light: oklch(
    calc(var(--primary-l) + 0.2)
    calc(var(--primary-c) * 0.6)
    var(--primary-h)
  );

  --color-primary-dark: oklch(
    calc(var(--primary-l) - 0.15)
    var(--primary-c)
    var(--primary-h)
  );
}

/* 暗色模式: 只调整亮度和彩度,色相不变 */
[data-theme="dark"] {
  --primary-l: 0.72;
  --primary-c: 0.14;
}

8.2 color-mix() 函数

:root {
  --color-primary: #2563EB;

  /* 自动生成透明度变体 */
  --color-primary-10: color-mix(in oklch, var(--color-primary) 10%, transparent);
  --color-primary-20: color-mix(in oklch, var(--color-primary) 20%, transparent);
  --color-primary-50: color-mix(in oklch, var(--color-primary) 50%, transparent);

  /* 与白色混合生成浅色变体 */
  --color-primary-subtle: color-mix(in oklch, var(--color-primary) 15%, white);

  /* 与黑色混合生成深色变体 */
  --color-primary-dark: color-mix(in oklch, var(--color-primary) 80%, black);
}

9. 配色方案测试

9.1 自动化测试

// 配色方案自动化测试套件
describe('Color Palette Validation', () => {
  const palette = loadPalette('brand-theme');

  test('all text/background pairs meet WCAG AA contrast', () => {
    const pairs = [
      [palette.textPrimary, palette.bgPrimary],
      [palette.textSecondary, palette.bgPrimary],
      [palette.textPrimary, palette.bgSecondary],
      [palette.primaryContrast, palette.primary],
    ];

    for (const [fg, bg] of pairs) {
      const ratio = contrastRatio(fg, bg);
      expect(ratio).toBeGreaterThanOrEqual(4.5);
    }
  });

  test('success and error colors are distinguishable under color blindness', () => {
    const types = ['protanopia', 'deuteranopia'];
    for (const type of types) {
      const simSuccess = simulateColorBlind(palette.success, type);
      const simError = simulateColorBlind(palette.error, type);
      const diff = ciede2000(hexToLab(simSuccess), hexToLab(simError));
      expect(diff).toBeGreaterThan(15);
    }
  });

  test('primary color is within brand tolerance', () => {
    const diff = ciede2000(
      hexToLab(palette.primary),
      hexToLab(brandConfig.primary.main)
    );
    expect(diff).toBeLessThan(brandConfig.primary.tolerance);
  });

  test('neutral scale has consistent hue shift', () => {
    const hues = Object.values(palette.neutralScale).map(c => hexToHSL(c)[0]);
    const hueVariance = standardDeviation(hues);
    expect(hueVariance).toBeLessThan(5);  // 色相偏差 < 5 度
  });
});

9.2 视觉测试矩阵

测试维度 验证方法 通过标准
亮色模式 截图对比 无异常色块
暗色模式 截图对比 无异常色块
红绿色盲 Color Oracle 模拟 所有状态可区分
蓝黄色盲 Color Oracle 模拟 所有状态可区分
灰度模式 去色截图 层级仍可辨识
高对比度 Windows 高对比度 界面可用
阳光下 降低对比度模拟 文字仍可读

10. 完整工作流

1. 品牌输入
   品牌主色 + 品牌调性描述
        |
        v
2. 色板生成
   AI 生成候选方案 (3 套)
   + 手动微调
        |
        v
3. 自动验证
   对比度检查 + 色盲安全检查 + 品牌合规检查
        |
        v
4. 主题生成
   从验证通过的色板自动生成:
   - 亮色/暗色 Design Token
   - Tailwind 配置
   - CSS 自定义属性
        |
        v
5. 集成测试
   在真实界面中预览
   + 视觉回归测试
   + 无障碍自动化测试
        |
        v
6. 发布与文档
   Token 发布到设计系统
   + 使用指南文档
   + 品牌色彩使用规范

Maurice | maurice_wen@proton.me