颜色选择不只是挑一个十六进制字符串。选择器展示的是某种颜色表示,最终外观还受源 Profile、传递函数、透明度合成、显示器和用户设置影响。本文区分记法与视觉效果,并说明如何构建可在真实界面中验证的调色板。
目录
核心要点
- RGB是加色模型:红、绿、蓝三原色混合产生其他颜色,用于屏幕显示
- HSL是编辑控制:色相、饱和度和亮度便于调色,但 HSL 不是感知均匀空间
- HEX是RGB的简写:十六进制格式是RGB值的紧凑表示
- 透明度支持:RGBA和HSLA支持alpha通道,实现半透明效果
- CSS变量:使用CSS自定义属性管理颜色,便于主题切换
颜色模型详解
RGB模型
RGB(Red, Green, Blue)是一种加色模型,通过红、绿、蓝三种光的不同强度组合来产生各种颜色。
特点:
- 每个通道值范围:0-255
- 总共可表示:256³ = 16,777,216种颜色
- 适用于屏幕显示(发光设备)
颜色混合示例:
| 颜色 | R | G | B |
|---|---|---|---|
| 红色 | 255 | 0 | 0 |
| 绿色 | 0 | 255 | 0 |
| 蓝色 | 0 | 0 | 255 |
| 黄色 | 255 | 255 | 0 |
| 青色 | 0 | 255 | 255 |
| 品红 | 255 | 0 | 255 |
| 白色 | 255 | 255 | 255 |
| 黑色 | 0 | 0 | 0 |
HSL模型
HSL(Hue, Saturation, Lightness)是便于编辑的圆柱形表示,不是感知尺度。不同色相即使使用相同的 L,人眼感知的明暗也可能不同。
参数说明:
- 色相(Hue):0-360度,表示颜色在色轮上的位置
- 0°/360°:红色
- 120°:绿色
- 240°:蓝色
- 饱和度(Saturation):0%-100%,颜色的鲜艳程度
- 0%:灰色
- 100%:纯色
- 亮度(Lightness):0%-100%,颜色的明暗程度
- 0%:黑色
- 50%:纯色
- 100%:白色
HSV/HSB模型
HSV(Hue, Saturation, Value)也称为HSB(Hue, Saturation, Brightness),与HSL类似但亮度定义不同。
与HSL的区别:
- HSL中50%亮度是纯色,100%是白色
- HSV中100%明度是纯色,降低饱和度趋向白色
CMYK模型
CMYK(Cyan, Magenta, Yellow, Key/Black)是减色模型,主要用于印刷。
特点:
- 青、品红、黄、黑四种油墨
- 适用于印刷品(反射光)
- Web开发中较少使用
Web颜色格式
HEX格式
在 CSS 中,十六进制记法表示 sRGB 通道值,可以使用 3、4、6 或 8 位十六进制数;4/8 位格式包含 alpha。
/* 6位格式 */
color: #FF5733;
color: #ffffff;
color: #000000;
/* 3位简写(每位重复) */
color: #F53; /* 等同于 #FF5533 */
color: #fff; /* 等同于 #ffffff */
/* 8位格式(带透明度) */
color: #FF573380; /* 最后两位是透明度 */
RGB/RGBA格式
/* RGB格式 */
color: rgb(255, 87, 51);
color: rgb(0, 0, 0);
/* RGBA格式(带透明度) */
color: rgba(255, 87, 51, 0.5);
color: rgba(0, 0, 0, 0.8);
/* 现代CSS语法(空格分隔) */
color: rgb(255 87 51);
color: rgb(255 87 51 / 50%);
HSL/HSLA格式
/* HSL格式 */
color: hsl(11, 100%, 60%);
color: hsl(0, 0%, 0%);
/* HSLA格式(带透明度) */
color: hsla(11, 100%, 60%, 0.5);
/* 现代CSS语法 */
color: hsl(11 100% 60%);
color: hsl(11 100% 60% / 50%);
颜色关键字
CSS 预定义了一组颜色关键字:
color: red;
color: blue;
color: green;
color: transparent;
color: currentColor;
/* 常用颜色关键字 */
color: white; /* #ffffff */
color: black; /* #000000 */
color: gray; /* #808080 */
color: silver; /* #c0c0c0 */
color: navy; /* #000080 */
color: teal; /* #008080 */
color: coral; /* #ff7f50 */
颜色格式转换
HEX转RGB
function hexToRgb(hex) {
const value = String(hex).replace(/^#/, '');
const expanded = value.length === 3 || value.length === 4
? [...value].map(char => char + char).join('')
: value;
if (!/^[\da-f]{6}([\da-f]{2})?$/i.test(expanded)) {
throw new RangeError('需要 #RGB、#RGBA、#RRGGBB 或 #RRGGBBAA');
}
return {
r: parseInt(expanded.slice(0, 2), 16),
g: parseInt(expanded.slice(2, 4), 16),
b: parseInt(expanded.slice(4, 6), 16),
...(expanded.length === 8
? { a: parseInt(expanded.slice(6, 8), 16) / 255 }
: { a: 1 })
};
}
// 使用示例
hexToRgb('#FF5733'); // { r: 255, g: 87, b: 51 }
RGB转HEX
function rgbToHex(r, g, b) {
const channels = [r, g, b];
if (!channels.every(Number.isInteger) ||
channels.some(channel => channel < 0 || channel > 255)) {
throw new RangeError('RGB 通道必须是 0 到 255 的整数');
}
return '#' + [r, g, b].map(x => {
const hex = x.toString(16);
return hex.length === 1 ? '0' + hex : hex;
}).join('');
}
// 使用示例
rgbToHex(255, 87, 51); // '#ff5733'
RGB转HSL
function rgbToHsl(r, g, b) {
r /= 255; g /= 255; b /= 255;
const max = Math.max(r, g, b), min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0;
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break;
case g: h = ((b - r) / d + 2) / 6; break;
case b: h = ((r - g) / d + 4) / 6; break;
}
}
return {
h: Math.round(h * 360),
s: Math.round(s * 100),
l: Math.round(l * 100)
};
}
// 使用示例
rgbToHsl(255, 87, 51); // { h: 11, s: 100, l: 60 }
代码示例
CSS变量管理颜色
:root {
/* 主色调 */
--color-primary: #3498db;
--color-primary-light: #5dade2;
--color-primary-dark: #2980b9;
/* 辅助色 */
--color-secondary: #2ecc71;
--color-accent: #e74c3c;
/* 中性色 */
--color-text: #333333;
--color-text-light: #666666;
--color-background: #ffffff;
--color-border: #e0e0e0;
/* 语义色 */
--color-success: #27ae60;
--color-warning: #f39c12;
--color-error: #e74c3c;
--color-info: #3498db;
}
/* 使用变量 */
.button-primary {
background-color: var(--color-primary);
color: white;
}
.button-primary:hover {
background-color: var(--color-primary-dark);
}
/* 深色主题 */
[data-theme="dark"] {
--color-text: #ffffff;
--color-background: #1a1a1a;
--color-border: #333333;
}
JavaScript颜色操作
class Color {
constructor(r, g, b, a = 1) {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
static fromHex(hex) {
const value = hexToRgb(hex);
return new Color(value.r, value.g, value.b, value.a);
}
toHex() {
const toHex = n => n.toString(16).padStart(2, '0');
return `#${toHex(this.r)}${toHex(this.g)}${toHex(this.b)}`;
}
toRgb() {
return `rgb(${this.r}, ${this.g}, ${this.b})`;
}
toRgba() {
return `rgba(${this.r}, ${this.g}, ${this.b}, ${this.a})`;
}
lighten(percent) {
if (!Number.isFinite(percent) || percent < 0 || percent > 100) {
throw new RangeError('percent 必须在 0 到 100 之间');
}
// 这里只演示通道算术;生产调色应考虑感知均匀空间。
const amount = Math.round(255 * percent / 100);
return new Color(
Math.min(255, this.r + amount),
Math.min(255, this.g + amount),
Math.min(255, this.b + amount),
this.a
);
}
darken(percent) {
const amount = Math.round(255 * percent / 100);
return new Color(
Math.max(0, this.r - amount),
Math.max(0, this.g - amount),
Math.max(0, this.b - amount),
this.a
);
}
}
// 使用示例
const color = Color.fromHex('#3498db');
console.log(color.toRgb()); // rgb(52, 152, 219)
console.log(color.lighten(20).toHex()); // #6db8f3
console.log(color.darken(20).toHex()); // #0278ab
最佳实践
1. 使用语义化颜色命名
/* 推荐:语义化命名 */
--color-primary: #3498db;
--color-success: #27ae60;
--color-error: #e74c3c;
/* 不推荐:直接使用颜色名 */
--blue: #3498db;
--green: #27ae60;
--red: #e74c3c;
2. 建立颜色系统
/* 主色调的不同深浅 */
--color-primary-50: #e3f2fd;
--color-primary-100: #bbdefb;
--color-primary-200: #90caf9;
--color-primary-300: #64b5f6;
--color-primary-400: #42a5f5;
--color-primary-500: #2196f3; /* 基础色 */
--color-primary-600: #1e88e5;
--color-primary-700: #1976d2;
--color-primary-800: #1565c0;
--color-primary-900: #0d47a1;
3. 确保对比度
// 计算对比度
function getContrastRatio(color1, color2) {
const getLuminance = (r, g, b) => {
const [rs, gs, bs] = [r, g, b].map(c => {
c /= 255;
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
});
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
};
const l1 = getLuminance(color1.r, color1.g, color1.b);
const l2 = getLuminance(color2.r, color2.g, color2.b);
const lighter = Math.max(l1, l2);
const darker = Math.min(l1, l2);
return (lighter + 0.05) / (darker + 0.05);
}
// WCAG 2.x AA 通常要求普通文本至少 4.5:1,大文本至少 3:1。
// “大文本”按渲染后的点数和字重定义,不是统一的 CSS 像素值。
4. 支持深色模式
/* 使用媒体查询自动切换 */
@media (prefers-color-scheme: dark) {
:root {
--color-text: #ffffff;
--color-background: #121212;
}
}
/* 或使用类切换 */
.dark-mode {
--color-text: #ffffff;
--color-background: #121212;
}
常见问题
HEX和RGB有什么区别?
HEX 是 RGB 通道值的十六进制表示,属于不同记法而非新的颜色模型;HEX 更紧凑,RGB 更便于表达通道和透明度。
什么时候使用HSL?
当需要调整颜色的亮度或饱和度时,HSL更方便。例如,创建同一色相的不同深浅变体。
如何选择合适的颜色对比度?
使用目标 sRGB 前景色和实际合成后的背景色计算,再验证真实组件。WCAG 比率不能替代焦点可见性、状态变化、图片、渐变和用户主题测试。
颜色选择器显示的是真实颜色吗?
不一定。颜色 Profile、显示器色域、亮度、环境光、浏览器行为和透明度合成都可能改变观感。处理图片时应保留 Profile 元数据,并把选择器值视为带假设的表示。
如何实现颜色主题切换?
使用CSS变量定义颜色,通过JavaScript切换根元素的类名或data属性来改变变量值。
总结
理解颜色模型和 Web 颜色格式有助于建立可维护的调色板,但单个十六进制值不能保证视觉效果或可访问性。应使用语义 Token,测试真实状态,并记录颜色空间与对比度假设。
快速总结:
- RGB用于屏幕通道值,HSL便于编辑但不保证感知一致
- HEX是RGB的紧凑表示,Web开发中最常用
- 使用CSS变量管理颜色,便于主题切换
- 确保文本和背景的对比度符合可访问性标准
- 建立系统化的颜色方案,包含主色、辅助色和语义色