Crowdwide 这个项目 Day 21 的时候,作者 Puneet Kumar Mishra 把核心功能、主算法、邮件系统、一个叫 Widewave 的推荐系统都做完了,自己宣布整体进入“mostly stable”。然后他干了一件听起来很对的事:花一整天专门打磨 UI——背景色组合、主题适配、sidebar。十五岁的全栈开发者,做到这个阶段还知道回头收拾界面,没什么可挑的。但这事儿后面翻出来的东西,刚好是我反复讲的一个点:视觉上觉得“UI 没问题”,和真实可验证的界面质量,经常是两把完全对不上的尺子。用眼睛看对比度,跟测性能不标机器配置一样,全是裸数字。
最具体的问题先摊开。他那个渐变色背景,用户往下滚得够深以后会跟着内容一起 stretch,viewport 顶部留下一块颜色不对的斑。典型得不能再典型:渐变挂在 body 或者某个高度不定的容器上,background-size 没锁死,滚动一深,背景被拉长。Oleksandr 在评论里给了两个修法,background-attachment: fixed,或者用一个 fixed 的伪元素单独铺背景。这两个都治“滚动拉伸”,但取舍完全不同。fixed attachment 在移动端浏览器上一向有脾气,不少 iOS Safari 版本不是忽略就是掉帧;固定伪元素加独立背景层,渲染更可控,代价是 DOM 多一层。Crowdwide 这种体量,我大概率直接选固定伪元素,就图移动端行为可预期。
sidebar 那个取舍我也想多说两句。作者原本纠结放左还是放右,展开时是压缩主内容,还是浮在压暗背景上。最后他选了 overlay + dimmed backdrop。Oleksandr 认可,理由是别压缩 feed、避免 layout shift。这判断没错,但有个更狠的前提他没点透:社媒产品里 sidebar 打开频率不低,而且经常发生在用户快速扫内容的中途。每开一次就让主内容区宽度重排,用户视觉焦点被迫重新定位,这跟 CLS 是同一类病——不是“闪一下”,是节奏被打断。overlay 的代价也很直接:遮挡。所以 dimmed backdrop 不是装饰,是视觉隔离。重叠层级、z-index、点击外部关闭、Esc 关闭,这四件不一起做,overlay sidebar 就是个半成品。作者在 Day 21 的表述里至少把视觉上的“improved”做了,但交互闭环配没配齐,原文没给够信息。
真正有意思的在后面。Odinaka 问了一句很到位的:对比度修复有没有对着 WCAG ratio 验证,还是纯凭眼睛看?作者很诚实,承认之前只是 visual check,准备在下次主题更新前跑系统检查,大概会用 axe DevTools。结果他真跑了,原话是“surprised to find accessibility problems on nearly every page”。我读到这儿笑了一下,不是嘲讽——这太标准了。你觉得自己界面做得挺干净,自动化工具一跑,每一页都在报问题。而且 dashboard 问题列表最长,分布在一堆 widget 里,不是某个共享组件炸了。这种结果最容易带偏:看着像满屏都是 bug,其实说明的是结构性语义缺失在重复发生,不是哪里突然爆炸。dashboard 问题最多,也不代表 dashboard 最差,只能说明它元素密度最高、widget 组合最多,是可访问性问题最容易露头的地方。
labeling 问题占了大头。作者说遇到 missing labels,还遇到“加了 label 看起来没反应”。这两种放在一起,太典型。Missing labels 是 htmlFor/id 没接上,或者干脆拿 div 当按钮用。加了 label 没效果的,要么角色不对——给不参与表单语义的元素硬挂 label,要么视觉上早被别的法子标过了,但可访问性树里那个 accessible name 还是空的。这个坑比我们愿意承认的要普遍得多。作者后来判断,大部分剩余 labeling issue 都是装饰性元素,准备把多余 label 删掉。这个结论我持保留。装饰性元素最该做的不是删 label,是改暴露方式:aria-hidden 或 role="presentation",让它别进 accessibility tree。如果它真只是装饰,label 本来就不该在那;如果它其实在传达视觉信息,那它就不是装饰,得给它一个真名字。这一步不是“扫掉剩余警告”,是分信息层级,不能图省事。
还有个边角料值得记一笔。作者提到评论里有人说 @mention search 找某些用户找不到,他解释是一张 AI 生成的 banner 图里混进去一个不是 Crowdwide 的 logo。这事跟可访问性没直接关系,但跟 UI polish 是同一个逻辑:视觉层的问题,是你最后一道工序才看得见的;结构层和内容层的问题,埋得又早又深。AI 图带错 logo,是内容来源不可控;可访问性问题,是语义层欠债。一个是品牌错,一个是结构错,但都指向同一件事——“看起来稳定”和“真的稳定”之间,隔着一层自动化验证。
所以我对整个 Day 21 的真正判断是:先做 UI polish 没错,错的是把 UI polish 当纯视觉工作。这不是 Puneet 一个人的毛病。我见过太多项目“功能完成”以后开始所谓“UI refinement”,只盯像素、间距、颜色、阴影,没人跑 axe、没人看语义结构、没人用键盘走一遍流程。上线以后用户反馈“点不到”“看不清”“屏幕阅读器全是乱的”,再返工,成本高得多。一个工具能提前告诉你的问题,别留到真人用户替你发现。
Odinaka 还有半句话我很认:“completing the core algorithm and mailing system before UI polish is the ordering that keeps an MVP alive.”他是在替作者排序辩护:核心算法和 mailer 先做完,UI polish 放后面,对。但这句成立有个前提:你得有一套“UI polish 不只是视觉”的验收标准。如果 UI polish 只是把渐变拉顺眼、主题切几套、sidebar 压暗,那这个排序只是把结构债往后拖了几周,没还。MVP alive 靠的是“能用”,“能用”不只是功能跑得通,还包括用户能操作、能理解、能通过任何输入方式拿到主要信息。否则一堆漂亮组件压在一个语义稀烂的页面上,MVP 是能跑,跛着跑而已。
再退一步,axe DevTools 跑出来的结果,本身就是个不错的 stress test。Dashboard 问题最多不代表它是罪人,它只是把“组件语义不规范”这个普遍问题,用最高元素密度放大出来了。所有 widget 都在那儿,所有交互都挤在桌面端最常走的路径上。要找底线能力,就该拿最吃力的真实场景测,别拿最小样本量自欺。信息越密,问题越真。这跟我做横评的逻辑完全一致。
EffessDev 提的 optimistic UI updates 也有意思。他说点赞感觉慢,想要乐观更新,加个 loading indicator;作者说在 to-do 里了。这需求跟可访问性问题并排看,正好是两个方向的工程债:一个“反馈太慢”,一个“结构不够健壮”。乐观更新解决前者,但底层 label 和语义没修干净,点击反馈再快,一部分用户照样拿不到。点赞按钮没有可访问名称,屏幕阅读器用户压根不知道那是个按钮,你乐观更新到 100ms 以内,对他来说还是不存在。很多人把 accessibility 当最后一道合规工作,其实它跟交互体验、状态管理是同一层的事。
那我的结论会有点偏:Crowdwide 现在最该做的,不是继续加功能,也不是继续做“好看”,是把 axe 暴露出的结构问题分级处理掉。装饰性元素该隐藏的隐藏,该命名的命名;dashboard 的 widget 问题按组件归因,能抽公共修法的集中修;对比度别再靠眼睛,直接按 WCAG AA 数值跑;@mention search 找不到人的事,顺带查索引条件。这些都不性感,也不会让首页截图变好看,但它们决定 Crowdwide 到底是真 mostly stable,还是只在视觉上 mostly stable。
把两种验收方式拉到一起横评的话,我会这么给:
| 验收方式 | 能发现的问题 | 漏掉的问题 | 适用阶段 |
|---|---|---|---|
| 视觉验收:看渐变、比主题、调 sidebar | 布局偏移、视觉遮挡、基础观感问题 | 结构语义、label 缺失、键盘可达性、对比度数值 | 快速设计迭代阶段 |
| 工具验证:axe、对比度数值、键盘流程 | 可访问性错误、命名问题、色彩对比不达标 | 视觉协调性、动效手感、真实用户焦点 | 收敛和交付前阶段 |
这张表不是要判视觉验收死刑。它在早期设计迭代里很值钱,因为快。但它最大的问题是不可复现。你看着“没问题”,不代表同一组条件下,不同设备、不同输入方式、不同对比度环境里它真没问题。视觉验收的条件只有一双眼睛、一块屏幕、一个主题状态;axe 至少给了你一组固定规则和可复现的扫描方式。后者当然有误报、有局限,但不会只报一句“看起来还行”。
也给作者说句公道话。Puneet 跑完 axe 的反应不是“这工具太吵别管了”,也不是“我眼睛看着没问题”。他把问题亮出来,承认之前的视觉判断不够,准备在 next theme update 前做系统检查。这态度比很多第一次跑 axe 就关掉扩展、挑几个能删的 label 改一改、然后在 PR 里写“fix accessibility issues”的人健康得多。他没停在第一步,这点可以。
最后的场景化建议:如果你也是小团队或独立开发者,核心功能刚做完,正好到 UI polish 阶段,别只做“好看”。把 axe 或 Lighthouse 的 accessibility 跑一遍,挑信息密度最高的页面看。如果你只是快速设计迭代,视觉验收可以;如果你准备对外说“stable”,那至少把可访问性问题降到你自己能接受的分级。装饰性元素不要乱删,先判断它到底进不进 accessibility tree,再决定隐藏还是命名。点赞这类交互,反馈速度是体验问题,可访问名称是能不能用的问题,两个都别拖。
没出现在建议里的不代表错。Crowdwide 完全可以继续只做视觉 polish,把结构问题留到下一版。但那样就别叫 mostly stable,叫“视觉 stable”。这是两个验收结论。当前版本下我给的就是:项目方向没问题,Day 21 的 UI 工作也确实有改进,但真“stable”要等 axe 那轮结构债清完才配得上。下个大版本如果他把 WCAG 对比度跑通、把 dashboard 的 widget 语义解决,我会重新给结论。盯着呢。
