使用方法
- 在「HEX 颜色值」里填色号,示例填 #315FE8;也可以直接点左边的方块用系统取色器挑。三个滑块会跟着联动,你拖哪一边都会同步回写这个框。
- 在「对比度目标」里选标准档位,示例选 AA 正文 4.5:1。三个可选值就是 WCAG 2.1 里最常用的三档,选它只影响「反解达标明度」按钮的目标线,不影响对比度本身。
- 点「计算对比度」,或按 Enter,结果区立刻给出这个色的
rgb(49, 95, 232)、HSL(225° / 79.9% / 55.1%)、相对亮度 0.146636,以及对白 5.34:1、对黑 3.93:1。 - 看三枚达标徽标:示例色对白是 5.34:1,过 AA 正文 4.5:1(绿标),对黑只有 3.93:1,卡在 AA 大字 3:1 与 AA 正文 4.5:1 之间(黄标)。
- 如果不满意,点「反解达标明度」,页面会保持你的色相和饱和度不动,告诉你明度该调到多少才过目标线;下方 20 行明度阶梯表可以逐档核对,CSV 导出带 BOM。
这个调色工具能解决什么问题
做界面时最难判断的一件事是「这个颜色配白字还是配黑字」。看着差不多的两个灰,落到 16px 正文上,一个清楚一个糊成一团;不对比一下根本看不出来。调色工具把这件事变成一次可计算的判断:给定一个前景色,立刻告诉你它和纯白、纯黑各自的对比度,以及过不过 AA 那一档。
第二个场景是改色不改品牌。品牌主色是定了的,但用在深色背景上不够清楚、用在浅色背景上又太刺眼。真正的解法往往不是换色相(换了就变品牌了),而是只调明度,保持色相与饱和度不变,让这个色在深浅两种背景上都找到能过线的那一档。本页的「反解达标明度」就是干这个的。
第三个场景是排查设计稿的可访问性问题。设计给了一版配色,你想知道按钮文字、输入框边框、图标这几个位置到底卡在哪一档。调色工具把三枚徽标(AA 正文 4.5、AA 大字与非文本 3、AAA 7)分别标出来,能一次看完,不用逐个心算;下方 20 行明度阶梯表还会把「白字 / 黑字 / 都不达标」的分界线直接画在行上。
本页不做的事要划清。只想看颜色叫什么名字,那是颜色名字查询,不是本页;想把 HEX 和 RGB 互转,那是两个独立的单功能转换器,本页的结果区虽然会显示两种写法,但主功能是校验不是转换;想要一整套配色方案、邻近色或互补色配色,那是颜色配色方案生成器,本页只处理单个颜色的明度与对比度;想从图片里吸色,那是图片颜色取色。另外,本页算的是 WCAG 2.x 相对亮度对比度,不是 APCA(WCAG 3 草案里的新算法),两者在深色底与细字号的判定上有差异,需要 APCA 结果请用专门页面。
原理与规则说明
对比度公式来自 WCAG 2.1 / 2.2 的相对亮度定义,三步:
// 1) 每个通道线性化(sRGB 反伽马)
c = v / 255
lin = c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ^ 2.4
// 2) 相对亮度
L = 0.2126 * lin(R) + 0.7152 * lin(G) + 0.0722 * lin(B)
// 3) 对比度
ratio = (max(L1, L2) + 0.05) / (min(L1, L2) + 0.05)
三个系数 0.2126 / 0.7152 / 0.0722 不是随便取的,它们是 sRGB primaries 在 D65 illuminant 下的相对亮度加权,绿色权重最大是因为人眼对绿色最敏感。整条链的规范出处是 W3C 的 Web Content Accessibility Guidelines 2.1 成功准则 1.4.3(对比度最低)与 1.4.11(非常规文本对比度),国内等同要求见 GB/T 37668-2019《信息技术 互联网内容无障碍可访问性技术要求与测试方法》。
为什么必须先线性化。0.2126/0.7152/0.0722 这套权重是定义在线性光上的,直接拿 sRGB 的 0~255 数值去加权是错的 —— 那等于把伽马编码又算了一遍。分界点 0.03928 是规范里写死的常量(而不是常见的 0.04045,那是另一版反伽马算法的阈值,差异出现在极暗的通道上)。示例色 #315FE8 的三个通道线性化后是 0.030713 / 0.114435 / 0.806952,加权得 0.146636 —— 注意蓝色通道贡献了 0.0722 × 0.806952 = 0.0583,占总亮度的四成,这就是它虽然最亮却不是最亮的原因。
+0.05 这个偏移不能省。它来自韦伯-费希纳定律的视觉模型:纯黑与纯黑的对比度按公式算是 0/0,实际观察到的黑底黑字完全看不见,所以规范在分子分母各加 0.05 做平滑。这也直接决定了公式的上限是 21:1 —— 纯白对纯黑是 (1+0.05)/(0+0.05) = 21,永远不会超过 21。看到有工具报出 40:1 或者 100:1,那不是更准,是算错了。
HSL 三个分量的取舍。调色工具把色相 H 定为 0~360 的整数,饱和度与明度给到 0.1% 的步进。为什么明度要细到 0.1%:临界点上 0.5% 的明度差就能让对比度跨过 4.5:1 这条线,举个实测例子,色相 225°、饱和 79.9% 的这一族颜色里,明度 58% 的 #3E69E9 对白是 4.77:1(达标),明度 60% 的 #4770EB 是 4.39:1(不达标)—— 差 2% 明度,结论就反了。色相反而不给小数,是因为 1° 的色相变化在明度固定时对相对亮度的影响极小,没必要浪费精度。
阈值的三档具体是什么,这是必须写清的:
- 4.5:1 —— 正文级文本的 AA 要求(成功准则 1.4.3)。所谓「正文」指小于 18pt(24px)的常规字,或小于 14pt(18.66px)的粗体。
- 3:1 —— 两处都用它:大号文本的 AA 要求,以及非常规文本(UI 控件边界、图标、图表线条)的 1.4.11 要求。这一档最常被忽略,因为大家以为「只有文字才需要管对比度」。
- 7:1 —— 增强对比度 AAA(1.4.6),给正文用。
实测的临界中性灰能把这三档的严肃性看出来:对白要过 4.5:1,最亮的中性灰是 #767676(4.542:1),再亮一档 #777777 就掉到 4.478:1,不达标了。对白过 3:1 的最亮灰是 #949494(3.033:1),再亮一档 2.995:1 同样不达标。所以「灰色文字」这件事在设计上是有硬边界的,#888 恰好落在两者之间。中文网页常用的 #999 对白只有 2.85:1,连大号文字都不达标。
反解明度用的是什么方法。保持 H 和 S 不动,只改 L,然后对 L 做单调步进搜索:0.1% 为一步,从当前 L 出发朝能改善对比度的方向走,走到第一个满足目标线的 L 就停。之所以用步进而非二分,是因为 sRGB 空间的 L 与 WCAG 相对亮度之间不是严格单调的线性关系(HSL 的 L 是 (max+min)/2,和 WCAG 亮度定义完全不同),二分的单调性前提不成立。这个设计的代价是保持色相,但代价换不来的是风格不跑偏 —— 换成降饱和度也能过线,但那样颜色就「脏」了。
完整示例(数值仅作演示)
示例条件:HEX #315FE8,校验背景 #FFFFFF,目标档 AA 正文 4.5:1。逐项算下来:
- 解析:
#315FE8→rgb(49, 95, 232); - HSL:H = 224.9°、S = 79.9%、L = 55.1%;
- 通道线性化:R 0.030713、G 0.114435、B 0.806952;
- 相对亮度:
0.2126×0.030713 + 0.7152×0.114435 + 0.0722×0.806952 = 0.146636; - 对白:
(1 + 0.05) ÷ (0.146636 + 0.05) = 5.34:1→ 过 AA 正文 4.5:1; - 对黑:
(0.146636 + 0.05) ÷ (0 + 0.05) = 3.93:1→ 过 AA 大字 / 非文本 3:1,不够 AA 正文 4.5:1; - 反解:想让白字过 AAA 7:1,页面会保持 H=224.9°、S=79.9% 不变,把 L 往暗调,报出一个具体色值与实测对比度。
这组数字与下方对照表是同一套算法产出的:明度 55% 那一行的 #315FE8 正是这个色,明度 58% 的 #3E69E9 对白 4.77:1 达标、再往下到 60% 的 #4770EB 对白 4.39:1 不达标,边界就在 58% 与 60% 之间。文字里说的「5.34」和表里的「5.34」是同一个数。以上数值仅作演示,不构成任何合规结论 —— 正式的无障碍验收需要人工测试辅助技术实测。
输入范围与对照表
下表固定你当前的色相与饱和度,把明度从 10% 逐档推到 100%,共 20 行,让你看清对比度随明度单调变化的形状,以及「白字 / 黑字 / 都不达标」的分界线落在第几行。表格默认显示 12 行,点「加载更多」展开全部 20 行,也可导出 CSV(带 BOM)。当前颜色的明度所在行会高亮。
| 明度 L | 色块 | HEX | 对白对比度 | 对黑对比度 | 建议文字色 |
|---|---|---|---|---|---|
| 10% | #050F2E | 18.85:1 | 1.11:1 | #FFFFFF | |
| 15% | #081745 | 17.23:1 | 1.22:1 | #FFFFFF | |
| 20% | #0A1F5C | 15.40:1 | 1.36:1 | #FFFFFF | |
| 25% | #0D2673 | 13.62:1 | 1.54:1 | #FFFFFF | |
| 30% | 0F2E8A | 11.81:1 | 1.78:1 | #FFFFFF | |
| 35% | #1236A1 | 10.15:1 | 2.07:1 | #FFFFFF | |
| 40% | #143DB8 | 8.79:1 | 2.39:1 | #FFFFFF | |
| 45% | #1745CE | 7.56:1 | 2.78:1 | #FFFFFF | |
| 50% | #1A4DE5 | 6.49:1 | 3.24:1 | #FFFFFF | |
| 52% | #2354E6 | 6.03:1 | 3.48:1 | #FFFFFF | |
| 55% | #315FE8 | 5.34:1 | 3.93:1 | #FFFFFF | |
| 58% | #3E69E9 | 4.77:1 | 4.40:1 | #FFFFFF | |
| 60% | #4770EB | 4.39:1 | 4.79:1 | #000000 | |
| 65% | #5E82ED | 3.56:1 | 5.89:1 | #000000 | |
| 70% | #7594F0 | 2.90:1 | 7.25:1 | #000000 | |
| 75% | #8CA6F2 | 2.37:1 | 8.85:1 | #000000 | |
| 80% | #A3B8F5 | 1.96:1 | 10.73:1 | #000000 | |
| 85% | #BACAF7 | 1.63:1 | 12.88:1 | #000000 | |
| 92% | #DAE2FB | 1.29:1 | 16.25:1 | #000000 | |
| 100% | #FFFFFF | 1.00:1 | 21.00:1 | #000000 |
上表的色相与饱和度固定为 H = 224.9°、S = 79.9%,也就是默认示例那一族。读表要点:对白对比度随明度单调下降、对黑单调上升,两条曲线在 58% 与 60% 之间交叉于 4.5:1 附近 —— 交叉点就是「白字还是黑字」的分界,交叉之后建议改用黑字。整张表里没有任何一行出现「都不达标」,那是因为白与黑总能挑出一个 4.5:1 以上的;真正会两边都不达标的,是背景与前景都取中间调子的情况。
输入范围、边界与精度说明
- HEX 的四种合法写法都会被归一:
#315FE8、315fe8(省略井号)、#F0F(三位缩写,每位复制一次展开成六位)、rgb(49,95,232)(数值形式)。归一后一律以大写六位形式回写,所以你从别处粘来的小写色号不会在结果里变成另一个值。 - 非法值逐条报错,不静默变黑或变白。
#12345(只有 5 位)会提示「十六进制必须是 3 位或 6 位,当前 5 位」;#GGGGGG(含非十六进制字符 G)会指出「第 2 位是 G,不是 0-9 或 A-F」;rgb(300,0,0)(越界)会说明 RGB 通道允许 0 ~ 255 而当前是 300。静默归零或归白比报错危险得多,因为你会拿着一份错误的配色去改设计稿。 - 三个通道的贡献极不均衡,这是调色时最容易忽略的一点。蓝通道权重只有 7.22%,红通道 21.26%,绿通道 71.52%。所以同样是 50% 明度,纯绿的 #00FF00 对白只有 1.37:1(远不达标),而纯蓝的 #0000FF 是 8.59:1(轻松过 AAA)—— 同样亮,颜色却完全不是一回事。这也是为什么不能只凭「看起来够亮」来判断。
- 对比度上限就是 21:1,由 +0.05 的偏移决定(见原理一节)。白底黑字是 21:1,中灰 #808080 对白只有 3.95:1。如果任何工具给出超过 21 的数,那是实现有 bug。
- HSL 的 L 与 WCAG 亮度不是一回事。HSL 的 L = (max+min)/2,只看最大最小通道,忽略中间通道的贡献;WCAG 亮度是三通道按 0.2126/0.7152/0.0722 加权。所以滑块上显示 L=55%,实际相对亮度是 0.146636,两者是独立量。本页在结果区把两个都列出来,就是为了避免你把它们混为一谈。L=100% 是个退化点,此时饱和度失去意义,色相再准也是纯白 —— 表里最后一行 100% 对应 #FFFFFF 就是这个原因。
- 「反解达标明度」可能无解,页面会直说而不是硬凑。比如 #0D2673 对白已经 13.62:1,目标定成 7:1 时它本来就已经过了;反过来如果目标定得比当前好、且朝改善方向走到 0% 也不够,页面会提示「保持色相与饱和度无解,请改背景色或改色相」。
- 小数位固定 2 位。对比度在 3.00 和 3.01 之间的差别,人眼在屏幕上分辨不出,但 3.94 与 3.93 跨过 4.5 与不跨过是实质区别 —— 所以位数不是为了好看,而是为了让「3.99 不达标、4.01 达标」这种情况能看清边界。
常见问题
为什么对比度最高只能到 21:1,不是无限大?
因为公式的分母里那个 +0.05。纯黑对纯黑在数学上是 0/0,规范为了模拟「黑底黑字完全看不见」这个事实,在分子分母各加 0.05 做平滑,于是上限被锁定在 (1+0.05)/(0+0.05) = 21。实测纯白 #FFFFFF 对纯黑 #000000 正好是 21.00:1。这也说明超过 21 的对比度在无障碍层面没有额外意义,业界不再区分 21 和 100。
WCAG 3 的 APCA 算出来和这个不一样,该信哪个?
两个都算,但用途不同。WCAG 2.x 的相对亮度对比度是现行标准(WCAG 2.1 和 2.2 都用它),法规与合规审计仍以它为准。APCA 是 WCAG 3 草案引入的新模型,在深色主题、细字重、半透明层叠这些场景下更符合实际观感,但目前还不是推荐标准。所以:对外要合规证明用 2.x,做深色主题的精细调校可以看 APCA。本页给的是 2.x 口径,不提供 APCA 结果。
我的品牌色改明度之后还是不满意,还有别的办法吗?
有三条路,按推荐顺序:一是改背景而不是改前景,很多场景下主色是固定的、可变的是底色,把底色压到 L=10% 附近,主色不用动就过了;二是降低饱和度,深色主题里降低饱和度能提亮而不刺眼,代价是颜色变「灰」,品牌感会弱一些;三是换色相,这个代价最大,通常意味着换品牌色。本页只提供第一条路里的「调背景」和「调前景明度」两种,因为它们不引入第三方库就能算准。
中文字号怎么算「18pt」?CSS 里的 px 和它什么关系?
规范用 pt,浏览器用 px。换算基准是 1pt = 1/72 英寸,在 CSS 的 96dpi 假设下 1pt = 96/72 = 1.3333px。所以 18pt = 24px,14pt = 18.67px。这条界线对中文页面影响不小:14px 的中文正文按规范属于「小字」,必须 4.5:1;而 24px 以上才算「大号」,可以用 3:1。也就是说中文网页里几乎所有正文都该按 4.5:1 来做,别拿 3:1 给自己放宽。
图标和边框也要 4.5:1 吗?
不要,这是最常见的误用。非常规文本(成功准则 1.4.11)的门槛是 3:1,适用对象是:UI 控件的边界、输入框边框、图标、图表的线条与色块、状态指示灯。判定对象是「这个元素与它相邻颜色的对比」,不是与纯白纯黑 —— 一个浅灰按钮在白底上只需要 3:1,因为它要让人看出边界在哪,而不是读懂内容。用 4.5:1 去卡图标会做出很难看的界面,而用 3:1 去卡正文则会牺牲可读性,两者正好搞反。
半透明背景怎么算?
先合成再算。半透明前景色要先和它背后的实际颜色做 alpha 混合,得到不透明的等效 RGB,再套亮度公式。同理,文字压在图片上时,图片那部分的颜色得逐处采样 —— 这正是图片取色工具的用途。本页只处理纯色,填 #RRGGBBAA 八位形式会被拒绝并提示「本页只接受六位 HEX,半透明请先与背景色手动合成」,因为 alpha 合成需要知道背后到底是什么色。
表格里「建议文字色」那一列是怎么判的?
规则只有一条:看对白与对黑哪个的对比度高,就建议哪个,同时要求它至少过 4.5:1。58% 那行对白 4.77 高于对黑 4.40,所以建议 #FFFFFF;60% 那行对黑 4.79 高于对白 4.39,翻转成 #000000。这一列的价值在于省掉你逐行心算,尤其在明度 58% 到 60% 这种两条曲线刚交叉的区间里,人眼判断非常不准。
这个页面会把颜色值发到别处吗?
不会。全部计算是三次 Math.pow 加两次乘加,加上一次 HSL 二分转换,没有 fetch、没有 XHR、没有第三方库。你可以在开发者工具的 Network 面板确认页面加载后不再有任何请求,刷新一下颜色就清空了。唯一的输入是 HEX 和几个滑块的数值,它们本来也不是敏感信息。
相关工具
- 颜色对比度检测:一次只比一对颜色、不带调节滑块,适合批量核对设计稿里已有的配色。
- WCAGAccessibility对比度检测:按成功准则逐条列出符合性判定,本页给数值、它给合规结论。
- RGB转HSL转换器:只要两种写法的互换结果,不做对比度计算。
- 十六进制转RGB:单独把 HEX 拆成三个通道数值,便于对账。
- 互补色颜色查询:给色相加 180° 找配色参考 —— 但要提醒,互补色只改色相不改明度,对比度不会因此变高。
- 色盲模拟:校验「红绿状态区分」这类不靠亮度、只靠色相传达的信息。
- 颜色工具:查看全部颜色工具。