界面设计师的色彩科学:我从构建一个零色彩网站中学到了什么
W3C网页内容无障碍指南要求普通文本的最低对比度为4.5:1,然而2023年WebAIM的调查发现,排名前一百万的网站中有83.6%的首页存在可检测到的WCAG 2对比度不合规问题。我在构建blakecrosley.com时面临的是相反的问题:所有元素都采用最大对比度(21:1),然后有选择地降低对比度。1
TL;DR
我的个人网站设计中没有使用任何品牌色彩。整个视觉层级体系通过白色文字在纯黑背景(#000000)上以四个不透明度层级运作:100%、65%、40%和10%。这一决策迫使我学习了感知色彩科学——为什么sRGB在均匀间距方面存在偏差,OKLCH如何解决这个问题,以及为什么深色模式需要与浅色模式不同的对比度关系。下方的交互工具可以让您探索对比度和色彩空间差异。核心结论是:理解色彩感知背后的科学原理,比依赖审美直觉能产生更好的设计决策。
我的”无色彩”调色板
大多数设计系统从调色板开始。我的设计系统从调色板的缺席开始:
:root {
--color-bg-dark: #000000;
--color-bg-elevated: #111111;
--color-bg-surface: #1a1a1a;
--color-text-primary: #ffffff;
--color-text-secondary: rgba(255,255,255,0.65);
--color-text-tertiary: rgba(255,255,255,0.4);
--color-border: rgba(255,255,255,0.1);
--color-border-subtle: rgba(255,255,255,0.05);
--color-accent: #ffffff;
}
十个设计令牌。没有品牌色。没有语义化的错误/成功/警告调色板。整个视觉层级体系通过四个透明度层级运作。2
为什么四个层级足够
每个层级承担特定的传达功能:
| 层级 | 不透明度 | CSS令牌 | 功能 | WCAG比率(#000背景) |
|---|---|---|---|---|
| 主要 | 100% | --color-text-primary |
标题、正文、关键内容 | 21:1(AAA) |
| 次要 | 65% | --color-text-secondary |
副标题、导航、元数据 | 13.7:1(AAA) |
| 第三级 | 40% | --color-text-tertiary |
时间戳、辅助文字、禁用状态 | 8.4:1(AAA) |
| 结构性 | 10% | --color-border |
边框、分隔线、背景区分 | 不适用(非文本) |
每个层级的文本对比度都通过了WCAG AAA(7:1)标准。第三级层级在40%不透明度下产生8.4:1的比率——几乎是AA最低标准4.5:1的两倍。纯黑背景(#000000而非#0a0a0a或#1a1a1a)的粗犷主义选择为每个文本层级提供了最大的对比度余量。3
--spacing-2xs事件
我在使用--spacing-2xs作为副标题边距时,发现了严格设计令牌的价值。这个令牌在我的:root定义中并不存在。CSS静默失败,布局崩溃,我花了20分钟调试一个间距问题——而这本应是一个编译时错误。修复方法:改用--spacing-xs(我定义的最小令牌)。教训:如果一个值在系统中不存在,那是设计的问题,而不是系统的问题。4
为什么sRGB欺骗了设计师
感知非均匀性问题
sRGB(网页的标准色彩空间)将颜色映射到一个立方体中,每条轴(红、绿、蓝)的范围为0-255。在红色通道中移动50个单位与在绿色通道中移动50个单位产生的感知变化并不相同。人眼中对绿色敏感的视锥细胞多于红色或蓝色,这使得绿色变化比等量的红色变化更容易被感知。5
实际后果是:如果设计师通过均匀间隔十六进制值来创建调色板,产生的颜色在感知上看起来是不均匀的。#000000和#FFFFFF之间的”中间”灰色不是#808080(数学中点),而大约是#777777(感知中点),因为人类的亮度感知遵循幂律而非线性函数。6
我的网站完全规避了这个问题。通过仅使用不同不透明度的白色,我避开了sRGB均匀性陷阱。在黑色背景上,不透明度与感知透明度呈线性关系——这是sRGB颜色混合所不具备的特性。
OKLCH解决方案
OKLCH(Oklab亮度、色度、色相)是一个感知均匀的色彩空间,其中相等的数学距离对应相等的感知差异。无论起始颜色如何,10个单位的亮度步进看起来始终是相同程度的变化。7
/* sRGB: mathematically even, perceptually uneven */
--gray-100: #f5f5f5;
--gray-200: #e5e5e5;
--gray-300: #d4d4d4;
/* OKLCH: perceptually even steps */
--gray-100: oklch(96% 0 0);
--gray-200: oklch(88% 0 0);
--gray-300: oklch(80% 0 0);
现代CSS原生支持oklch()。在我下一个需要调色板的项目中,我将使用OKLCH定义调色板。对于当前网站,基于不透明度的系统通过不同的方式实现了同样的感知均匀性。
我的深色模式决策:没有浅色模式
我的网站没有prefers-color-scheme媒体查询。它完全在深色模式下运行。这是一个深思熟虑的决定。8
支持双模式的论点: 用户期望有浅色模式选项。集成系统偏好设置体现了对用户选择的尊重。
反对双模式的论点(也是我的选择): 维护两套视觉系统不可避免地会削弱两者的质量。一个在黑色背景上以65%不透明度工作的设计,在白色背景上需要不同的不透明度(接近45%)才能达到相同的感知权重。每一个交互状态、每一条边框、每一个阴影都需要重新校准。我选择把一个系统做好,而不是把两个系统做得凑合。
纯黑背景(#000000)为每个文本层级提供了最大的可用对比度:
/* My actual typography contrast hierarchy */
.hero__title { color: var(--color-text-primary); } /* 21:1 */
.hero__subtitle { color: var(--color-text-secondary); } /* 13.7:1 */
.nav a { color: var(--color-text-secondary); } /* 13.7:1 */
.nav a:hover { color: var(--color-text-primary); } /* 21:1 */
悬停状态的过渡(次要→主要)完全通过对比度变化提供功能性反馈——无需任何颜色变化。
对比度与可读性
WCAG对比度要求
| 级别 | 普通文本(< 18pt) | 大文本(≥ 18pt或14pt粗体) |
|---|---|---|
| AA | 4.5:1 | 3:1 |
| AAA | 7:1 | 4.5:1 |
对比度衡量的是前景色与背景色之间的相对亮度差异。1:1的比率意味着没有对比度(颜色完全相同)。21:1的比率意味着最大对比度(黑底白字或白底黑字)。9
超越WCAG:APCA
WCAG 2的对比度算法存在已知局限。该算法将所有颜色一视同仁,不考虑极性(深色文字在浅色背景上与浅色文字在深色背景上),尽管研究表明人类对两种模式下的对比度感知存在显著差异。10
APCA(无障碍感知对比度算法)通过以下方面弥补了这些不足: - 极性敏感性: 在相同的数学对比度下,浅色背景上的深色文字比深色背景上的浅色文字更易读 - 空间频率: 相同字号下,细体字需要比粗体字更高的对比度 - 适应性: 眼睛会适应页面周围的亮度环境,影响感知到的对比度
APCA预计将成为WCAG 3.0对比度要求的基础。我的网站受益于极性洞察:由于我专门使用浅色文字在深色背景上,我需要比浅色模式网站更高的对比度。我最低的文本层级(40%不透明度,8.4:1比率)甚至超过了APCA对深色背景上正文的推荐最低值。
无色彩的语义化色彩
生产环境的色彩系统通常会为功能分配颜色(绿色表示成功,红色表示错误)。我的网站完全避免了功能性色彩,因为它没有事务性界面——没有表单、没有状态消息、没有成功/错误状态。内容是静态的。
如果我需要语义化色彩,我会有针对性地添加:
| 功能 | 典型方案 | 我的假设方案 |
|---|---|---|
| 成功 | 绿色 | 白色文字 + 对勾图标 |
| 错误 | 红色 | 白色文字 + 叉号图标 + 边框强调 |
| 警告 | 琥珀色 | 白色文字 + 感叹号图标 |
将图标与文字配对,消除了仅依赖颜色传达信息的做法——这种做法对约8%患有色觉缺陷的男性来说是无效的。这种方式同时也保持了我的单色系统。色彩将作为点缀而非结构性元素使用。11
排版优先的层级体系
在没有色彩承载层级关系的情况下,我的网站完全依赖排版来传达层级:
:root {
--font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text",
"SF Pro Display", "Helvetica Neue", Arial, sans-serif;
--font-size-display: 5rem; /* 80px — hero headlines */
--font-size-7xl: 3.875rem; /* 62px */
--font-size-base: 1rem; /* 16px — body text */
--font-size-xs: 0.75rem; /* 12px — metadata */
}
使用系统字体,而非自定义网页字体。这既是一个粗犷主义的决定(使用平台的原生素材),也是一个性能决策(零字体加载延迟,助力实现Lighthouse满分100)。显示字号(80px)配合紧凑的字间距(-0.03em)赋予标题庄重感,无需额外装饰。正文字号16px配合充裕的行高(1.7)优先考虑可读性而非信息密度。12
从0.75rem到5rem的13级字体比例尺提供了足够的精细度,仅通过字号即可表达层级关系。结合四个不透明度层级,我拥有52种潜在组合(13种字号 × 4种不透明度)——足以表达任何内容层级,而无需借助色彩。
核心要点
面向设计师: - 使用OKLCH而非sRGB定义调色板;感知均匀的色彩空间能产生可预测的层级关系和整个调色板中一致的对比度 - 将第三级文本层级对照WCAG AAA(7:1)标准测试,而不仅仅是AA(4.5:1);AAA阈值为现实屏幕条件(低亮度、眩光、老化显示器)提供了足够的余量 - 思考您的项目是否真正需要色彩;我的网站证明仅凭排版和不透明度就能承载完整的视觉层级体系
面向开发者:
- 使用CSS oklch()定义颜色,并同时用WCAG 2(现行标准)和APCA(即将推出的标准)测试对比度;所有现代浏览器已支持oklch()
- 通过在OKLCH空间中调整亮度和饱和度来实现深色模式,而非反转十六进制值;感知调整比数学反转能产生更好的效果
- 严格的设计令牌执行可防止CSS静默失败;如果一个令牌不存在,应该改变的是设计,而不是令牌系统
参考文献
-
WebAIM,“The WebAIM Million: 2023 Accessibility Analysis,” 2023。 ↩
-
作者的CSS自定义属性,取自
critical.css。10个颜色令牌,均基于白色在黑色背景上的不透明度关系派生。 ↩ -
作者的WCAG对比度计算。主要(21:1)、次要(13.7:1)、第三级(8.4:1),均超过AAA 7:1最低标准。 ↩
-
作者的调试经历。
--spacing-2xs被使用但从未在:root中定义。记录于MEMORY.md错误条目中。 ↩ -
Hunt, R.W.G., The Reproduction of Colour, Wiley, 2004。 ↩
-
Poynton, Charles, Digital Video and HD, Morgan Kaufmann, 2012。伽马校正与感知线性度。 ↩
-
Ottosson, Bjorn, “A perceptual color space for image processing,” 2020。OKLCH规范。 ↩
-
作者的设计决策。单一深色模式避免了维护平行明暗系统时固有的视觉妥协。 ↩
-
W3C, “Web Content Accessibility Guidelines (WCAG) 2.1,” 2018。 ↩
-
Somers, Andrew, “APCA Contrast Calculator,” 2023。 ↩
-
W3C, “WCAG 2.1 Success Criterion 1.4.1: Use of Color,” 2018。 ↩
-
作者的排版系统。从0.75rem(12px)到5rem(80px)的13级字体比例尺。系统字体栈消除了FOIT/FOUT问题。 ↩