颜色格式转换与对比度检查(HEX · rgb · hsl · WCAG 相对亮度)
把颜色在 HEX、rgb()、hsl() 之间互转并算出 WCAG 相对亮度,再检查前景色与背景色的对比度能否达到 WCAG 2.1 的 AA / AAA 门槛,不达标时给出最小的改法。全部在这台设备的浏览器里算。
① 单个颜色:写法互转加相对亮度。输入认 #f0a、#FF00AA、FF00AA、rgb(255, 0, 170)。
| 写法 / 派生值 | 结果 |
|---|---|
| HEX(大写) | — |
| rgb() | — |
| hsl() | — |
| 线性化后的 R / G / B | — |
四组算好的颜色
| 打进去 | HEX(大写) | rgb() | 相对亮度 L(WCAG) |
|---|---|---|---|
#f0a | #FF00AA | rgb(255, 0, 170) | 0.2416 |
rgb(119,119,119) | #777777 | rgb(119, 119, 119) | 0.1845 |
#FFFFFF | #FFFFFF | rgb(255, 255, 255) | 1.0000 |
hsl(210, 60%, 40%) | — | — | — |
② 前景 / 背景对比度:逐条对照 WCAG 2.1 的五个门槛。上下两格各算各的,下面报错不会清掉上面的结果,反过来也一样。
| 要求 | 实测 | 结论 |
|---|---|---|
| 正文(普通文字)AA · 需 ≥ 4.5 | — | — |
| 正文(普通文字)AAA · 需 ≥ 7 | — | — |
| 大字(≥ 18.66px 粗体 或 ≥ 24px)AA · 需 ≥ 3 | — | — |
| 大字 AAA · 需 ≥ 4.5 | — | — |
| 非文本 UI 组件 / 图形 · 需 ≥ 3 | — | — |
结论按没有四舍五入的比值判定,所以极端情况下会看到显示「4.50:1」却判为不通过——那是 4.497x 被进位的结果。
换算的口径
一屏解决两件事
第一件是写法对不上。设计稿给 #FF00AA,CSS 里想要 rgb(),做主题变量时又用 hsl(),手工换算要么靠心算要么靠在线站点。这里三个写法加相对亮度一次列全,输入端宽松(# 可省、三位简写、rgb() 直接粘),读不懂时会指出是第几位、哪个字符不对,而不是笼统一句「格式错误」。
第二件是对比度到底够不够。#777777 放在白底上是经典的翻车案例:看着挺深,实测 4.48:1,差 0.02 就过不了正文 AA。这一栏把 WCAG 2.1 的五个门槛拆成表格逐条判,还会算出「最小改法」——只把一端挪几步就达标,而不是让你把整个配色推翻。
计算口径
解析:接受 RGB / RRGGBB(# 可选,忽略大小写与空格)和 rgb(r, g, b)(逗号或空格分隔,三项都得是 0–255 的整数)。拒绝颜色名称、四位/八位十六进制(含 alpha)、rgba()、带斜杠的新语法、百分比和小数,并给出具体的出错位置——先去掉所有空白,位置从 # 后面(rgb() 则是第几项)开始数起。
线性化:每个通道 c = v / 255,然后
c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ^ 2.4
相对亮度:L = 0.2126 × R + 0.7152 × G + 0.0722 × B(线性化之后的三个通道),显示 4 位小数。
对比度:ratio = (max(L1, L2) + 0.05) / (min(L1, L2) + 0.05),显示 2 位小数,判定用未取整的值。
门槛来自 WCAG 2.1:正文 AA ≥ 4.5、正文 AAA ≥ 7;大字 AA ≥ 3、大字 AAA ≥ 4.5,其中「大字」定义为 ≥ 18.66px 且字重 ≥ 700,或 ≥ 24px;非文本 UI 组件与图形(1.4.11)≥ 3。
HSL:标准 RGB→HSL 换算,色相四舍五入到整数度,S 与 L 保留 1 位小数;纯灰(最大最小相同)按约定记 hsl(0, 0.0%, l%)。
不达标时的建议:把前景或背景之一按通道等比推向纯黑或纯白(v ← v + (target − v) × i/255,等比移动所以色相和饱和度基本不变),四个方向各逐步试到 4.5:1 为止,报告所需步数最少的那个,并给出改完的 HEX 与新比值。
什么时候这些数字会骗你
- 它假设你的屏幕是 sRGB。未校色的显示器、广色域手机、开着的「护眼模式 / 原彩 / Night Shift」、被浏览器接管的色彩管理,都会让你实际看到的颜色和
119/255这个数不是一回事。同一串十六进制在不同机器上的对比度可以差出 0.2–0.5,这不是 bug,是配置文件的锅。 - 对比度是亮度数学,不是「好不好看」也不是「看不看得清」。字重、字体、行距、抗锯齿、文字压在图片上、纯色块的观感轻重都不在公式里。AA 是法定最低门槛,不是舒适线;能上 AAA 就上 AAA。
- 红绿色觉差异不在公式内。相对亮度对红色通道权重最低,
#FF0000在白底上只有 4.00:1,而同一亮度的绿色能轻松过 7:1——靠颜色区分状态的时候,请另外加图标或文字。 - 半透明不参与。带 alpha 的写法这里直接拒收,正确做法是先把它和真实背景合成一个不透明色,再拿合成结果来测。
- 阈值边界会抖动。4.497 显示成 4.50 却判不通过;反过来 4.504 判通过。真在乎就用 4.6 / 7.1 这类留了余量的目标值。
常见问题
Q:为什么 #FFFA、rgba() 收得这么严?
因为 alpha 一旦参与,「对比度」就不成立了:同一个 rgba(0,0,0,.5) 叠在白底和黑底上是两个完全不同的颜色。这里宁可让你先去合成、再来测,也不给一个看着像结论其实随背景漂移的数字。
Q:过 AA 就行,还是必须 AAA? AA 是合规底线(多数法条与内部规范引用的就是它)。AAA 适合长文正文、年龄层偏高或低照度的场景。表格把两档都列出来,就是为了让你自己决定,而不是替你决定。
Q:我填的颜色去了哪里? 解析、线性化、比值全部写在脚本里,没有哪个环节会把你的颜色发出去,你输入的颜色不会离开这台设备。