这套站一开始只有深色。深色好看、好写、也安全(背景一黑,什么颜色上去都「挺酷」)。后来要加浅色,第一版的做法很朴素:html 上挂一个 data-theme="light",把一组 CSS 变量换掉,完事。
看着确实变了。问题是变了不等于能读。
先量,再改
对比度有标准算法(WCAG 的相对亮度),不难写:把颜色转成线性 RGB,算相对亮度,再按 (L1 + 0.05) / (L2 + 0.05) 求比值。正文要 4.5:1,大字与图形元素要 3:1。我写了个二十来行的脚本,把主题里每一个「会被当文字用」的颜色和三个背景色两两算一遍——结果不好看:
深色主题 14 组 全部通过
浅色主题 --mint #34d399 on #f7f9ff 1.36 ✗(要 3.0)
浅色主题 --rose #fb7185 on #f7f9ff 1.69 ✗
浅色主题 --violet #a78bfa on #f7f9ff 1.65 ✗
浅色主题 --gold #b45309 on #eef2fb 4.49 ✗(要 4.5,差 0.01)
根因很直白:这三个颜色当初是给深色底调的亮色,浅色底上它们就是「浅色字写白纸」。修法不是加阴影,是换颜色——#34d399 换成 #047857,#fb7185 换成 #be123c,#a78bfa 换成 #6d28d9,金色 #b45309 换成 #9d470a。改完复算:4.9 / 5.6 / 6.3 / 5.6,全部达标。深色主题一个像素都不用动。
比变量更容易漏的:另一套样式表
真正尴尬的发现是工具箱。/Timarsky/tools/ 和 160 个详情页,跑的是自己那份 tools.css,它压根没接过主题——主站有浅色,工具站没有。/Timarsky/ 主页面又有一套自己的灯开关,写的是 body.light。
结果是:同一个域名下,首页有主题、工具站的首页有另一个主题、工具站的详情页没有主题。点了半天天色不变。
这轮把三者统一:页面 <head> 里放一段**同步**执行的预判脚本(读 localStorage、没存过就看系统偏好、把 theme-light 挂到 html),样式里的选择器从 body.light 改成 html.theme-light,切换按钮写到 .header .nav 和详情页的 .tp-actions,localStorage 键跟主站共用同一个(xkmyth-theme)。这样在一处切过,另一处打开就是同一个天色。
顺带说说搜索
同一轮还修了搜索的三件事,都很「不好看但也不报错」:
- 关键词太少。索引里原来只有标题和分类,搜「github ignore」找不到
gitignore,搜「字体」找不到品牌页。现在每个工具都手写别名,页面也补了同义词,索引从 186 条涨到 218 条、关键词最长补到 240 字符。 - 多词被当成一个词。「json 格式化」被当成一个整串去匹配,当然匹配不上。改成先切词,每个词都要在标题/描述/关键词里出现过(AND),标题里命中的额外加权。
- 中文单字噪音。「时间」按单字匹配,半个站都命中。改成双字(bigram)参与打分,且只在查询里真有中文时才启用,避免把英文查询也切碎。
验收用的是二十条真实查询(时间戳、github ignore、json 格、字体、密码、子网 …),每条都要命中目标,缺一条就回去补别名——而不是「大概能搜到」。
记下来的三条
- 主题切换是 **CSS + 预判脚本 + 共享存储键** 三件套,少一件就会出现「点了没变」或「闪一下白」。
- 量化优先:对比度能算就别靠眼睛,脚本二十行,跑一次省掉一整轮返工。
- 搜索的关键词是**数据**,不是代码逻辑。塞进数据里、幂等重建,比在打分函数里写一堆
if好维护得多。
顺手提一句:这两件事都写进了 CI 之外的复查脚本(audit_contrast.py、test_search.py),下次改配色或加工具,跑一遍就知道有没有把可读性还回去。