ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

不装库、不调 API:我从零实现 WCAG 对比度,还拿 W3C 官方真值逐条对拍

不装库、不调 API:我从零实现 WCAG 对比度,还拿 W3C 官方真值逐条对拍 不装库、不调 API我从零实现 WCAG 对比度还拿 W3C 官方真值逐条对拍一键开通华为云码道 CodeArts 代码智能体 https://developer.huaweicloud.com/codeartsco.html?sourcedmzntgwatomgit1sourceaddmzntgwatomgithd作品介绍一个纯前端、零第三方依赖的颜色工具。选一个前景色、一个背景色它实时算出对比度并告诉你这组配色在 WCAG 无障碍标准下过不过 AA / AAA顺带把 HEX/RGB/HSL/HSV 互转、色阶tints/shades、互补/类似/三分配色全给你。但取色器和对比度工具网上一抓一把WebAIM Contrast Checker 之类我做这个不是为了再造一个。我要的是把这套色彩科学从零手写出来然后拿 W3C 官方给的真值逐条对拍把误差摊在桌面上。我算的#767676对白底对比度是 4.5422W3C 文档里那个经典例子就是 4.54我算的#777777是 4.4781刚好掉到 AA 门槛 4.5 以下——一个十六进制数的差别一个过一个不过而这正是规范的原意。换句话说别人给你一个数字我给你这个数字是怎么来的、跟官方对不对得上、差多少。68 项单元测试全绿对比度对拍 W3C 误差 0.003色彩空间转换对拍 CSS Color 规范样例值 10/10。仓库公开欢迎 clone 下来npm run verify自己复现。为什么非要从零写色彩科学起因很小。有次我改前端把一段辅助文字设成#777放白底上看着挺淡但没多想。后来查无障碍规范才知道#777对白底对比度 4.48刚好没过 AA 的 4.5而#767676是 4.54过了。一个数字之差合规与不合规就分家了。我当时用的在线对比度工具只给我一个数不告诉我这数怎么来的、准不准。我就想WCAG 那套相对亮度、对比度公式其实就几行——sRGB 分量线性化、加权和、(L10.05)/(L20.05)——我能不能自己实现并且用 W3C 文档里给的那些 worked example 去验证我实现对了。说白了我不信任一个只吐结果、不给过程的黑盒。尤其无障碍合规这种事差 0.02 就从合规变违规我更想知道这 0.02 是怎么算出来的、我复算一遍对不对得上。与其信 WebAIM不如自己写一遍再拿它文档里的官方数字来验两边对上我才踏实。这跟这次码道比赛我想试的一条路一脉相承让 AI 去啃有客观真值可对拍的活。颜色好不好看是主观的但#767676对白底到底是不是 4.54是 W3C 白纸黑字写死的是或否赖不掉。先跑起来看先看几组真实的配色体检。经典 AA 边界例#767676放白底对比度 4.54普通文本 AA 过、AAA 不过大文本全过——判定跟 W3C 完全一致。浅灰#aaaaaa放白底对比度不够普通文本 AA/AAA 双双飘红深蓝底配亮黄字对比度拉满四个徽章全绿然后是我最看重的对拍。跑npm run verify对比度逐条跟 W3C 真值比、色彩转换跟 CSS Color 规范样例比sRGB 线性化一个 0.03928 的阈值WCAG 对比度的第一步是把 sRGB 分量线性化——人眼的感知是非线性的显示器存的也是伽马编码值算亮度前得先还原成线性光。码道第一轮就把这个写对了exportfunctionsrgbToLinear(c){cc/255;if(c0.03928)returnc/12.92;returnMath.pow((c0.055)/1.055,2.4);}那个0.03928是个有讲究的数——它是 WCAG 2.x 沿用的分段阈值。顺带一提更新的 sRGB 规范里这个拐点其实是0.04045、线性段斜率是12.92两者在极暗处有微小差异。WCAG 官方选的是 0.03928 这套我就老老实实照 W3C 的来因为我要对拍的正是 W3C 的数。这种到底用哪个常数的细节正是手写和调库的区别——调库你压根不知道它内部用的哪套。有了线性分量相对亮度就是加权和exportfunctionluminance(r,g,b){constRsrgbToLinear(r),GsrgbToLinear(g),BsrgbToLinear(b);return0.2126*R0.7152*G0.0722*B;}0.2126/0.7152/0.0722 这三个系数是 ITU-R BT.709 规定的人眼对绿最敏感、蓝最不敏感。为什么非要线性化、不能直接拿 0-255 的原始值加权因为显示器存的 RGB 是伽马编码过的跟真实光强不是线性关系——直接平均会把中间调的亮度算高对比度就跟着错。我特意拿#808080中灰验过线性化后它的相对亮度约 0.216而不是直觉的 0.5差着一大截。这个 0.216 也正是 WCAG 文档里给的标准值对上了我才敢往下走。WCAG 对比度与 AA/AAA对比度公式一行但有个细节谁亮谁做分子。exportfunctioncontrastRatio(fg,bg){constL1luminance(fg.r,fg.g,fg.b);constL2luminance(bg.r,bg.g,bg.b);constlighterMath.max(L1,L2),darkerMath.min(L1,L2);return(lighter0.05)/(darker0.05);}判定阈值分普通文本和大文本两套exportfunctionwcagLevel(ratio,{largefalse}{}){if(large)return{aa:ratio3,aaa:ratio4.5};return{aa:ratio4.5,aaa:ratio7};}#767676对白底 4.54普通文本 AA≥4.5刚好过、AAA≥7不过#777777是 4.48AA 就挂了。这俩颜色肉眼几乎没差别但一个合规一个不合规——这就是眼见不一定为实、得算的最好例子我特意把它做成了页面上的权威真值对拍卡alpha 被忽略的坑三轮专家评审里技术专家揪出一个我没想到的洞contrastRatio只取了 r/g/balpha 被解析出来了却没参与计算。也就是说rgba(255,0,0,0.5)这种半透明红会被当成不透明纯红去算对比度结论直接误导。修法是对前景先按 alpha 合成到背景上over 合成再算exportfunctioncontrastRatio(fg,bg){constfgRgb(fg.a!undefinedfg.a1)?compositeOver(fg,bg):{r:fg.r,g:fg.g,b:fg.b};// ... 再用 fgRgb 算亮度}补完还加了测试rgba(255,0,0,0)全透明合成到白底就该≈白、对比度≈1。这种功能看着对、边界一测就漏的坑正是手写实现最容易翻车、也最需要第三方评审来挑的地方。色彩空间互转 色阶配色除了对比度还有一堆颜色换算HEX/RGB/HSL/HSV 互转以及色阶和配色生成。tints/shades 是往白/黑方向等比插值互补/类似/三分是色环上取角度// tints向白插值shades向黑插值complementary色相 180// analogous±30triadic120/240页面里这些排成一排色块带 hex 小标签点一下能复制还有个自动选可读文字色按钮给定背景色它拿 bestTextOn 在黑/白里挑对比度更高的那个exportfunctionbestTextOn(bg){constwhite{r:255,g:255,b:255},black{r:0,g:0,b:0};returncontrastRatio(white,bg)contrastRatio(black,bg)?white:black;}从单栏到两栏UI 也是一轮轮磨出来的第三轮刚做出来的页面是单栏的——取色、对比度、色彩空间、色阶、配色从上往下堆一长条右边大片留白看着像没排完。而且那会儿还有个格式错误误报的 bug 混在里面。第四轮我按 UI 评审的意见改成两栏左栏放取色 对比度达标 实时预览这条主操作线右栏放色彩空间 色阶 配色这些参考信息窄屏再堆回单列并把预览置顶。这样改一个色、右边立刻跟着变的动线才顺。色彩空间转换这块HSL/HSV 的公式看着眼熟真手写才知道坑在哪——比如纯灰s0时色相 h 是未定义的得约定成 0hue 要环绕到 [0,360)#RGB三位的简写要每位复制成#RRGGBB。这些边角调库的人根本不会去想但对拍的时候一测一个准。// rgbToHsl 关键分支maxmin 时灰度h 与 s 都归 0避免除零// hue 环绕h0 加 360、h360 减 360// #abc 展开为 #aabbcc那个把合法 HEX 判成格式错误的 bug第三轮做完页面我本地一起服务就发现个哭笑不得的 bugHEX 输入框里明明填的是合法的#FFFFFF、#17233B下面却标红格式错误。值其实解析对了预览、对比度、色阶全在正常算就是校验函数误判。这种 bug 最阴——功能没坏但 UI 一直冲你喊你输入错了用户体验直接崩。我在 R4 里点名让码道修合法#RGB/#RRGGBB大小写都收不许报错只有真非法才提示。顺带发现parseHex对#gggggg这种是静默返回 NaN不抛错跟convert.hexToRgb的校验口径还不一致也一并统一了。可验证护城河对拍 W3C CSS 规范这是整个项目的魂。verify/下两条线对比度对拍 W3C 权威值——#000000/白21、#767676/白4.54、#777777/白4.48、#0000FF/白8.59、#FF0000/白≈4.0误差全在 0.003 以内AA 判定跟 W3C 边界一致。色彩转换对拍 CSS Color 规范样例值——这里还有一段整改。我最初让码道写的 roundtrip 是hex→hsl→hex 往返一致专家评审直接点破你用同一对逆函数自己转回来当然一致这是自证不是对拍。于是改成拿 CSS Color 规范里给定的官方样例#ff0000↔hsl(0,100%,50%)、#800000↔hsl(0,100%,25%)、#7f7f7f↔hsl(0,0%,49.8%) 等去比10/10 过。#767676 vs #FFFFFF computed4.5422 reference4.54 err0.0022 AAtrue #777777 vs #FFFFFF computed4.4781 reference4.48 err0.0019 AAfalse ROUNDTRIP (CSS Color spec): 10/10三轮专家评审揪出的硬伤R1-R4 做完我拉了 UI、技术、产品三个专家评审读代码和截图打分技术 80、UI 76、产品 82。分不算高但揪出来的都是真问题技术parse.js是唯一入口却零测试roundtrip 是自证alpha 被忽略parseHex不校验。UIAA/AAA 徽章没列头读者分不清哪个是 AA 哪个是 AAA两栏失衡白底预览块隐形。产品把权威真值对拍从 README 搬进 UI 做成一等公民卡评委一眼看到护城河。这些我合成一轮全修了补 parse 测试测试数从 55 涨到 68、roundtrip 改对拍 CSS 规范、alpha 合成、徽章加列头、对拍卡进 UI、两栏平衡。产品专家那句钩子我记着——别人给你对比度数字我给你这个数字为什么可信。码道 push 认证又横跳了一次跟上一篇一样工具本身的折腾也得记。color-lab 这个会话推完 R1-R4 后到 R5README时 push 直接挂了fatal: could not read Username for https://atomgit.com。好在这回码道老实——我在提示词里写死了认证失败就原样贴报错别碰 credential.helper、别写 token、别 force push它照做了没像上次那样自作聪明往凭据文件里塞假 token 把沙箱搞坏。解法还是那招开一个全新会话干净沙箱、新凭据把 R5 的 README 和 R6 的专家评审修复合成一轮重推一次成功。CodeArts 上下文涨满了会自动压缩压回 30% 左右又能接着干。这里我踩出一个可复用的经验写下来给同样在码道上批量做项目的人同一个会话里连着推好几轮git 凭据很容易在某一刻失效表现就是码道要么push 成功其实是谎报远端根本没动要么直接could not read Username。对策有两条——一是提示词里强制它 push 完再跑一次git ls-remote origin main把远端 SHA 打出来自证逼它别拿我执行了命令糊弄成我推成功了二是凭据一死就果断开新会话重推别在旧会话里跟它耗新沙箱的凭据是干净的一次就过。公开仓库仓库公开从 R1 的颜色解析一路到 R5R6 的评审修复提交链清楚。README 里功能亮点、可验证护城河、复现命令、架构分层都写了。提效数据环节轮次测试数关键产出解析与转换R132hex/rgb/hsl/hsv sRGB 线性化 相对亮度WCAGR245对比度 AA/AAA 对拍 W3C 真值渲染与页面R355色阶/配色 白色系 SPA 零依赖服务器修校验取证R455修 HEX 误报 往返取证 两栏 UI评审修复READMER5R668parse 测试 CSS 规范对拍 alpha 合成 对拍卡进 UI五维自检交作业前我自己过了一遍也给同样在用码道做可验证型项目的人一个参照原创度色彩解析、sRGB 线性化、相对亮度、WCAG 对比度、HSL/HSV 转换、色阶配色全手写不碰 chroma-js/tinycolor/any-color 任何一行。可验证性68 项单元测试锚定 W3C 与 CSS Color 规范的外部真值npm run verify把 computed/reference/err 逐条打出来任何人 clone 都能复现。完成度取色、双向 HEX 输入、实时预览、AA/AAA 判定、色彩空间、色阶、配色、自动可读色、JSON 导出能当日常工具真用。踩坑深度0.03928 与 0.04045 的阈值之争、#767676 与 #777777 一个数之差卡 AA 边界、alpha 被漏算、合法 HEX 误判、roundtrip 自证——都是真撞上的。诚实度评审分不高76~82硬伤我全列全修对拍口径从自造往返改成了对 CSS 规范样例不敢再拿自证冒充第三方。本地怎么跑gitclone https://atomgit.com/gcw_QMYlF6Ie/color-lab.gitcdcolor-labnpmtest# 68 项单元测试npmrun verify# 对比度对拍 W3C 色彩转换对拍 CSS 规范样例npmrun serve# 打开 http://127.0.0.1:8099/ 看白色系页面写在最后连着做几个可验证的小工具我越来越确信一件事AI 时代能生成真的不稀缺了稀缺的是能自证。这个颜色实验室代码是码道从零写的没装任何色彩库、没调任何 API但我逼它把每一处计算都拿去跟 W3C 和 CSS 规范的官方真值对答案——#767676是不是 4.54、#777777是不是刚好挂、#ff0000的 HSL 是不是 (0,100%,50%)。它中途把 alpha 漏算了、把合法 HEX 误判了、把 roundtrip 写成了自证全被测试和三轮评审逮出来修掉。trust but verify。给个对比度数字谁都会敢把这数字怎么来的、误差多少、跟规范对不对得上全摊在页面上才是它跟又一个取色器拉开差距的地方。仓库在这欢迎 clone 下来npm run verify打我脸https://atomgit.com/gcw_QMYlF6Ie/color-lab
返回列表