调色板生成与色彩系统设计:从和谐配色到 WCAG 可访问性

为什么需要调色板生成器

设计师与前端工程师在日常工作中频繁面临色彩决策:品牌主色选什么?同一颜色的明暗阶梯怎么定?配色是否对色盲用户友好?文字在背景上的对比度是否达标?

传统流程是设计师在 Figma / Sketch 中手动调色,导出 HEX 后工程师再写入 CSS 变量或 Tailwind 配置。这个过程有几个痛点:

  1. 手动调色效率低:从主色生成 10 档色阶需要逐个调整明度与饱和度,难以保证档位间过渡均匀
  2. 可访问性检查易遗漏:WCAG 对比度需手动计算,色盲模拟需依赖设计软件插件
  3. 多平台导出繁琐:同一套色板要分别输出 CSS、Tailwind、SCSS、Android XML、iOS Swift 等格式
  4. 配色理论门槛高:互补色、类似色、分割互补等方案需要理解色环理论

在线调色板生成器正是为解决这些问题而生:输入一个主色,自动生成 6 种和谐方案、Tailwind/Material 色阶、明度色调阶梯,同时检查 WCAG 对比度与色盲可访问性,一键导出 6 种格式。

色彩理论基础:HSL 色环

调色板生成的数学基础是 HSL 色彩空间(Hue 色相、Saturation 饱和度、Lightness 亮度):

  • 色相 H:0-360°,对应色环上的位置(0° 红、120° 绿、240° 蓝)
  • 饱和度 S:0-100%,0% 灰色,100% 纯色
  • 亮度 L:0-100%,0% 黑,100% 白,50% 纯色

相比 RGB 的三通道数值,HSL 更符合人眼对颜色的直觉感知:「这是什么颜色(H)→ 多鲜艳(S)→ 多亮(L)」。所有调色板算法都基于 HSL 的色相旋转与明度调整。

RGB 与 HSL 的转换是双向可逆的(见颜色格式转换工具),本工具内部统一以 RGB 为中介,需要色相操作时转 HSL,操作完成后再转回 RGB。

6 种和谐配色方案

和谐配色基于色环上色相的几何关系。本工具实现 6 种方案:

方案色相旋转色数适用场景
互补色0°, 180°2CTA 按钮强调(蓝底橙按钮)
类似色-30°, 0°, 30°3背景渐变、图表系列
三角色0°, 120°, 240°3品牌识别、三栏布局
分割互补0°, 150°, 210°3插画配色(对比但不刺眼)
四角色0°, 90°, 180°, 270°4多主题、数据可视化
单色阶同色相不同明度5企业站主色调(统一沉稳)

算法实现核心是 generateHarmony(rgb, type):先转 HSL,按方案的 rotations 数组逐个旋转色相,保持饱和度与亮度不变。单色阶方案特殊处理:固定色相,生成 5 档明度(20/40/55/70/85)。

function generateHarmony(rgb: RGB, type: HarmonyType): RGB[] {
  const hsl = rgbToHsl(rgb);
  if (type === 'monochromatic') {
    // 单色阶:同色相,明度 20/40/55/70/85 共 5 档
    return [20, 40, 55, 70, 85].map((l) => hslToRgb({ h: hsl.h, s: hsl.s, l }));
  }
  return HARMONY_META[type].rotations.map((delta) =>
    hslToRgb({ h: hsl.h + delta, s: hsl.s, l: hsl.l })
  );
}

选型建议:先选 1 个主色 + 1 个强调色(互补或分割互补),再用类似色或灰色填充背景。避免同时使用 4 色以上强对比方案,会显得杂乱。

明度色调:Tints / Shades / Tones

同一颜色的明度变化是设计系统的核心。本工具实现三种色调阶梯:

  • Tints(明色):向纯白渐变,明度提升 + 饱和度略降,适合背景与悬停态(hover)
  • Shades(暗色):向纯黑渐变,明度降低,适合按下态(active)与边框
  • Tones(浊色):向纯灰渐变,饱和度降低,适合次要元素与禁用态

算法核心是 generateTones(rgb, type, steps):根据 factor = (i + 1) / (steps + 1) 计算每档的渐变比例,tints 提升 l + (100 - l) * factor,shades 降低 l * (1 - factor),tones 降低 s * (1 - factor * 0.7)

// Tints 算法:向纯白渐变
const l = hsl.l + (100 - hsl.l) * factor;
const s = hsl.s * (1 - factor * 0.3);
result.push(hslToRgb({ h: hsl.h, s, l }));

与设计系统色阶的区别:色阶覆盖从最亮到最暗的完整范围(50-950 共 11 档),色调阶梯只覆盖单一方向(仅亮 / 仅暗 / 仅浊)的 5 档。色调阶梯适合「我需要这个色的 hover 态」这类即时需求。

设计系统色阶:Tailwind 与 Material

主流设计系统都采用 10-11 档色阶,500 为主色,向亮暗两端递进。本工具实现 Tailwind(50-950 共 11 档)与 Material(50-900 共 10 档)两种规范。

生成算法

以输入色为 500 锚点,按经验公式向两端推导:

// 各档位相对 500 的明度偏移与饱和度系数
const adjustments = {
  50: { lDelta: 42, sFactor: 0.7 },   // 最亮,明度+42,饱和度×0.7
  100: { lDelta: 35, sFactor: 0.75 },
  200: { lDelta: 27, sFactor: 0.82 },
  300: { lDelta: 18, sFactor: 0.88 },
  400: { lDelta: 8, sFactor: 0.95 },
  500: { lDelta: 0, sFactor: 1 },      // 锚点,不变
  600: { lDelta: -8, sFactor: 1.02 },
  700: { lDelta: -16, sFactor: 1.05 },
  800: { lDelta: -24, sFactor: 1.08 },
  900: { lDelta: -32, sFactor: 1.1 },
  950: { lDelta: -42, sFactor: 1.15 }, // 最暗,明度-42,饱和度×1.15
};

规律:向亮端明度提升 + 饱和度降低(避免亮色过于刺眼),向暗端明度降低 + 饱和度略提升(避免暗色发灰)。

与官方色板的差异

算法生成 ≠ 官方色板。Tailwind 与 Material 官方色板是设计师人工精调的结果,每个颜色的明度、饱和度、色相都经过仔细校准。算法生成的色阶接近官方风格但非完全等同,适合:

  • 快速搭建原型(无需手动调色)
  • 自定义品牌色生成完整色阶
  • 学习色阶规律(理解明度与饱和度的变化趋势)

生产环境建议在算法生成基础上手动微调,或直接使用官方色板。

WCAG 2.1 对比度与无障碍设计

对比度计算

WCAG(Web Content Accessibility Guidelines)2.1 标准定义了文字与背景对比度的计算公式:

对比度 = (亮色相对亮度 + 0.05) / (暗色相对亮度 + 0.05)

相对亮度(relative luminance)的计算考虑了 sRGB 的 gamma 校正:

function relativeLuminance({ r, g, b }: RGB): number {
  const channel = (n: number) => {
    const c = n / 255;
    return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
  };
  return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);
}

对比度范围 1-21:1 表示无对比(同色),21 表示最大对比(黑白)。

合规等级

等级普通文字大字号(18pt 或 14pt 粗体)
AA≥ 4.5:1≥ 3:1
AAA≥ 7:1≥ 4.5:1

本工具同时检查当前色与白字 / 黑字的对比度,标注合规等级。例如深蓝色背景白字通常通过 AA,浅黄色背景白字可能不达标需改用黑字。

实践建议

  1. 不要仅靠颜色传达信息:约 8% 男性存在色觉异常,若仅用红绿区分错误/成功,色盲用户无法识别。应同时用图标或文字标注
  2. 关键文字至少达到 AA:正文、按钮文字、表单标签必须 ≥ 4.5:1
  3. 大字号可放宽:标题、大图标文字 ≥ 3:1 即可
  4. 暗色模式重新检查:同一配色在亮色与暗色模式下对比度不同,需分别验证

色盲模拟:线性 RGB 矩阵变换

色盲类型

类型患病率影响
红色盲(Protanopia)约 1% 男性红色感知缺失
绿色盲(Deuteranopia)约 1% 男性绿色感知缺失(最常见)
蓝色盲(Tritanopia)极罕见蓝色感知缺失

矩阵变换原理

色盲模拟通过线性 RGB 变换矩阵实现。算法分三步:

  1. sRGB → 线性 RGB:反 gamma 校正(c <= 0.04045 ? c/12.92 : ((c+0.055)/1.055)^2.4
  2. 矩阵乘法:3×3 矩阵变换,每种色盲一个矩阵
  3. 线性 RGB → sRGB:重新 gamma 校正

矩阵来源是 Machado et al. 2009 论文的简化版:

// 绿色盲变换矩阵(最常见)
const deuteranopia = [
  0.367322, 0.860646, -0.227968,
  0.280085, 0.672501, 0.047413,
  -0.011820, 0.042940, 0.968881
];
// 矩阵乘法:new_r = m[0]*r + m[1]*g + m[2]*b

矩阵的物理含义:色盲患者缺少某种视锥细胞,大脑用其他细胞的响应「近似」缺失通道。矩阵的每一行描述了「新通道如何由原始三通道线性组合」。

为什么要做色盲模拟

约 8% 男性存在不同程度的色觉异常。若配色仅靠红绿区分(如红=错误、绿=成功),色盲用户无法识别。本工具展示当前色在色盲眼中的实际呈现,便于调整配色。建议:

  • 同时用颜色 + 图标 / 文字标注状态
  • 避免红色与绿色作为唯一区分手段
  • 用本工具检查关键配色在三种色盲下的可区分性

多格式导出实践

本工具支持 6 种格式导出,覆盖主流前端与移动端开发场景:

CSS 变量

:root {
  --primary-50: #eff6ff;
  --primary-100: #dbeafe;
  --primary-500: #2b6cff;
  --primary-900: #1e3a8a;
}

用法:color: var(--primary-500);。适合原生 CSS 与 CSS-in-JS。

Tailwind 配置

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        primary: {
          50: '#eff6ff',
          500: '#2b6cff',
          900: '#1e3a8a',
        }
      }
    }
  }
};

用法:className="bg-primary-500 text-primary-900"。Tailwind 4.0 后也支持 CSS 变量配置。

SCSS 变量

$primary-50: #eff6ff;
$primary-500: #2b6cff;
$primary-900: #1e3a8a;

用法:color: $primary-500;。适合传统 SCSS 项目。

JSON

{
  "50": "#eff6ff",
  "500": "#2b6cff",
  "900": "#1e3a8a"
}

供 JavaScript 动态加载或设计工具导入。

Android XML

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <color name="primary_500">#2B6CFF</color>
</resources>

放入 res/values/colors.xml,用法:@color/primary_500

iOS Swift

extension UIColor {
    static var primary500: UIColor { UIColor(red: 0.169, green: 0.424, blue: 1.000, alpha: 1.0) }
}

用法:UIColor.primary500

黄金角度随机配色

算法原理

随机配色采用黄金角度旋转采样(137.508°):从随机起始色相开始,每次旋转 137.508° 取下一个色相,保证色相在色环上分布最均匀。

function randomHarmony(count = 5): RGB[] {
  const goldenAngle = 137.508;
  const startH = Math.floor(Math.random() * 360);
  const s = 60 + Math.floor(Math.random() * 30); // 60-90 饱和度
  const l = 45 + Math.floor(Math.random() * 15); // 45-60 亮度
  const result: RGB[] = [];
  for (let i = 0; i < count; i++) {
    const h = (startH + i * goldenAngle) % 360;
    result.push(hslToRgb({ h, s, l }));
  }
  return result;
}

为什么是黄金角度

黄金角度(137.508°)是数学上最优的均匀分布角度,源自向日葵种子排列、松果鳞片等自然现象。其数学性质是:任何两个相邻采样的角度差都是黄金角度的整数倍,保证色相在色环上既不扎堆也不遗漏。

对比纯随机采样:纯随机可能产生色相扎堆(如 5 个色都集中在红色区域)或遗漏(如完全没有蓝色),黄金角度采样保证 5 个色均匀分布在 360° 色环上。

饱和度与明度固定在中等范围(S: 60-90, L: 45-60),保证生成的配色既鲜艳又不刺眼。

纯原生 TypeScript 实现要点

本工具的算法库 colorPalette.ts 约 460 行,零第三方依赖,全部基于浏览器原生 API:

  1. RGB ↔ HSL 互转:标准公式,clampByte/clampPercent/clampHue 三个工具函数处理边界
  2. 色相循环((n % 360) + 360) % 360 保证负数也能正确映射到 0-360
  3. WCAG 相对亮度:注意 gamma 校正的两个分支(0.03928 阈值)
  4. 色盲矩阵:3×3 矩阵乘法,注意 sRGB ↔ 线性 RGB 的转换
  5. 导出格式:switch 分支处理 6 种格式,字符串模板拼接

避坑指南

  • Astro 模板中 { 会被当作表达式开始,FAQ 中含字面量花括号需用 &#123; / &#125; 转义(详见 CSS 格式化工具博客
  • useMemo 依赖数组要包含所有派生输入,避免缓存过期
  • 拾色器 input[type=color]value 必须是 6 位 HEX,3 位简写会报错

工具联动与实战流程

站点提供三个颜色相关工具,互补使用形成完整色彩工作流:

  1. 颜色格式转换工具:HEX / RGB / HSL / HSV / CMYK 五格式互转 + 简单和谐方案
  2. 调色板生成器(本工具):调色板生成 + 设计系统色阶 + 多格式导出
  3. 颜色对比度检查工具:双色对比度精确检查 + WCAG 合规判定

典型流程

  1. 在 /color 选基础色(拾色器或输入 HEX),获取 HSL 值了解色相
  2. 在 /color-palette 生成完整色阶与和谐方案,导出 CSS 变量或 Tailwind 配置
  3. 在 /color-contrast 验证关键组合(如主色背景 + 白字、强调按钮 + 主色背景)的对比度
  4. 用色盲模拟检查关键配色在三种色盲下的可区分性

局限性与改进方向

本工具仍有改进空间:

  1. 色阶生成非官方精调:算法生成接近但非完全等同 Tailwind/Material 官方色板,未来可引入 OKLCH 色彩空间提升感知均匀性
  2. 色盲模拟为简化版:使用 Machado 2009 简化矩阵,未模拟异常三色视觉(anomalous trichromacy,如红色弱)
  3. 未支持 OKLCH / LCH 色彩空间:CSS Color Level 4 引入的感知均匀色彩空间,色阶过渡更自然
  4. 未支持色彩主题切换:当前仅生成单一色板,未来可生成亮色 / 暗色双主题色板
  5. 未支持色彩提取:从图片提取主色调生成配色方案

小结

调色板生成器是色彩工作流的核心工具:从单一主色出发,自动生成完整设计系统色阶、和谐配色方案、明度色调阶梯,同时检查 WCAG 对比度与色盲可访问性,一键导出 6 种格式。配合颜色格式转换工具与对比度检查工具,覆盖了从选色、生成、验证到导出的完整流程。

理解色彩理论(HSL 色环、和谐方案、明度色调)、无障碍标准(WCAG 对比度、色盲模拟)、设计系统规范(Tailwind/Material 色阶)是做好配色的基础。希望本文与配套工具能帮助你更高效地完成色彩决策。