博客 UI 翻新记:三轮翻新,两个没抓到的 bug
九月初,这个博客的 UI 做了三轮翻新:主站换了视觉语言,二次元板块推倒重来,然后又做了一轮性能收敛。三轮都上线了,过程里翻过一次车,还抓出一个上一轮埋下、这一轮才现形的样式 bug。
先定规矩:换语言,不是微调
一开始的共识是「换视觉语言,不是细节微调」。范围定了四件事:只动主站全部表面(ACG 板块隔离不动)、方向交给 AI 提开放方案、锚定「技术/冷静极简」、字体保持自托管。
然后就有了一个我现在觉得最值钱的做法:方向不由 AI 决定,AI 只负责出变体,用户看实物拍板。 文字描述定不了视觉,说一百句「暖一点、冷静一点」也不如并排摆三张画板。
第一轮出了三个方向——Swiss Grid、Terminal、Warm Editorial。用户否掉了 Swiss,在另外两个之间纠结,于是又补了三个变体,最后选中的是「暖纸 + 等宽骨架」:米色纸底、松木绿点缀、IBM Plex 双字体,导航和日期全部用等宽。
实现侧的三件套
方向选定之后的实现,有三样东西是验收前必须实测、不接受自报的:
性能门。新的视觉不能引新的性能债。头部保留毛玻璃就要实测滚动帧率——CPU 四倍节流下跑脚本化滚动数帧,只允许一处 blur 且必须配不透明回退。
对比度台账。每个文字色对底色逐对算 WCAG 比值。设计稿里的「日期灰」实测只有 3.87:1,不够 4.5,就顺着色相往下压一档到 4.72。艺术稿的颜色和可用性的颜色,该分家就分家。
键盘走查。全站可交互元素一个个 Tab 过去,焦点环必须是高对比实色,不是半透明装饰。
这三条后来在第二、第三轮里原样复用,成了固定的验收清单。
翻车:固定 header 盖住了正文
上线当天,用户报告:文章页的开头文字被盖住了。
根因很小:全局 CSS 里有一条 header { position: fixed } 的元素选择器,作用域是整个页面。翻新之后文章页的标题块用了语义化 <header> 标签,于是正文里的这个 header 也被拽成固定定位,钉在页面顶部,正好盖住正文开头。
但为什么三轮审查、连安全审查子代理都过了,没人抓到?因为全页截图会把 fixed 重叠拍平——截图里所有元素按文档流排开,看起来一切正常。只有真实视口顶部那一帧,滚动位置为零的截图,才会露出被盖住的文字。
修复是一行:header 改成 body > header。教训进了约定:UI 交付验证必须带一帧真实视口顶部的截图,不只是全页截图。
二次元板块:保留个性,不收敛
主站翻新之后轮到 ACG 板块。这里用户拍了一个和直觉相反的决定:不往主站的视觉语言收拢,保留二次元的独立个性。收敛会让整个站点更整齐,但 ACG 板块是「玩」的空间,整齐不是它的目标。
方向最终选的是「贴纸本」:米白纸底、便签卡、2px 墨色描边、按顺序确定性旋转的卡片、胶带条和荧光笔涂抹。原来的粉彩黏土拟态整层删掉——阴影从多层内阴影改成 3px 硬偏移,装饰动画全删,字体换成 Bricolage Grotesque 加 Nunito。
上一轮埋下的 bug
第三轮做性能收敛时,实测抓到一个前一轮的 bug:二次元板块里列表行的便签样式,从来就没生效过。
原因是 Astro 的 scoped 样式不作用于 slot 内容。样式写在共享子页组件的 scoped 块里,行元素却由各页面的模板渲染,带着页面自己的作用域标记——组件里的规则根本匹配不到它们。没人发现,是因为行照样显示、颜色继承自全局样式,只是少了描边和阴影,不并排对比根本看不出来。
讽刺的是,这个 bug 的暴露方式印证了前面那条教训:不是看出来的,是第三轮做性能瘦身时逐项测出来的。
第三轮:性能收敛
前两轮是换皮,第三轮是体检。最大的问题是二次元音乐页:两千多条记录全部服务端渲染,页面 gzip 51KB。改成只渲染前 120 行,其余数据在点「展开」或搜索时才按需拉取,页面降到 6.9KB。顺手恢复了代码块语法高亮——之前的 sanitize 流水线把高亮器的内联样式全剥掉了,解法是把高亮挪到 sanitize 之后跑,XSS 边界不动。