那次检查一个模板页面时,我先被一片整齐的灰色块吸引了。页面看起来像是在认真等待资源,每张卡片都有一块很大的视觉区域,下面还有标题和操作入口。可是继续往下查才发现,这不是图片加载得慢,而是这个页面里的模板本来就没有对应海报。
这两种情况在视觉上很像,在用户心里却完全不同。加载中的意思是“再等一下可能就会有”,没有资源的意思是“这个内容本身没有这项东西”,加载失败则意味着“本来应该有,但这次没有拿到”。如果界面用同一块灰色临时图形把它们盖住,用户就无法判断自己应该等待、继续浏览,还是尝试恢复。
我一开始以为这是一个卡片样式问题,后来发现它其实是状态表达问题。真正要修的,不是把灰块换成更好看的灰块,而是让页面诚实地告诉用户:现在发生了什么,接下来还能做什么。
灰色块为什么会制造错误期待
加载中的临时图形本来是有用的。网络请求还没有结束时,先保留布局,可以避免页面跳动,也能让用户知道内容即将出现。但它有一个前提:系统确实在等待一份应该存在的资源。这个前提一旦不成立,临时图形就从辅助信息变成了误导。
这次页面里的卡片几乎都没有海报,却仍然为每张卡保留了很大的图片区域。用户看到一排排相同的灰块,很自然会猜测资源正在加载,或者网络暂时有问题。他可能会反复刷新,也可能一直滚动等待。实际上,等待不会改变结果,因为数据源里没有那张图片。
没有资源时继续展示像加载中的大灰块,等于把“内容不存在”伪装成“内容马上存在”。
如果不处理,后果不只是页面难看。用户会误判数据完整性,搜索和筛选的结果也会变得难以解释:为什么筛选后还有很多空白卡?为什么翻页按钮像是没有内容?更麻烦的是,开发者也会被视觉表象带偏,把时间花在图片请求、缓存和尺寸适配上,却没有确认资源是否从一开始就存在。
先把4种状态分开
我后来把这个问题拆成4种状态:加载中、没有资源、加载失败、没有匹配。它们都可能让卡片的图片区域暂时没有画面,但原因和行动完全不同。
加载中表示请求尚未完成,适合使用短暂的骨架或轻量临时图形;没有资源表示记录存在,但没有可展示的海报,这时应该展示记录本身的事实信息;加载失败表示应该拿到资源却没有拿到,需要给出重试或恢复入口;没有匹配则表示当前筛选条件下没有结果,用户需要清除筛选或调整条件。
这4种状态不能只靠颜色区分。颜色在低对比度、暗色模式、屏幕缩放或辅助技术下都不稳定。更可靠的做法是直接写清状态,并保留和状态相匹配的动作。比如加载中不应出现“重新加载”按钮,因为请求还没有结束;没有资源不应放一个看起来像图片加载中的空框,而应把名称、类型、作者或其他已知信息提到前面;加载失败要有明确重试;无匹配要有清除条件的入口。
表面上我是在整理4个文案,实际上是在给系统补一套状态机。只有先承认状态不同,后面的布局、按钮和测试才有依据。
没有海报,也要让卡片有内容
确认没有海报之后,我没有再找一张通用图片去填满卡片。最容易误判的错误捷径,就是认为“卡片必须有大图才像一个完整卡片”。这会让视觉结构看起来整齐,却把真正有用的信息挤到很小的位置。
我选择把它改成紧凑的事实卡,是因为记录本身并没有消失。模板名称、分类、适用人物或其他可确认的信息仍然可以帮助用户判断是否值得打开。缺少图片并不等于缺少内容,界面应该把还存在的事实交还给用户。
紧凑并不意味着把卡片做成一串难读的文字。它仍然需要清晰的标题层级、稳定的操作区域和足够的点击范围,只是不给不存在的视觉资源预留主角位置。这样做还有一个好处:图片资源恢复后,卡片可以自然回到完整状态;资源始终不存在时,布局也不会像失败了一半。
空数据设计的目标不是把空白藏起来,而是在缺少某项资源时,保留用户仍然可以使用的事实和动作。
这件事让我重新意识到,界面完整感不应该靠装饰来维持。一个页面即使没有封面,也可以是可理解、可筛选、可继续浏览的页面。反过来,一个塞满灰色临时图形的页面,可能只是把未知包装得更像已知。
修视觉时,不能漏掉分页和筛选
卡片变紧凑以后,页面一次能容纳更多记录,原有的分页行为就必须重新检查。之前已经有“继续加载”一类的入口,但它没有真正带来新增内容。这个问题很容易被视觉修复掩盖:如果只看第一屏,卡片变好看了,按钮也还在那里,似乎任务已经完成。
但用户真正关心的是能不能继续找到内容。修复后我验证了分页数量从一档增加到下一档时,新增记录确实进入页面,而不是只改变按钮文案;焦点也落到了新增内容的第1个位置,让键盘用户不必从页面顶部重新寻找。数量变化不是装饰,它会影响阅读顺序、焦点位置和用户对“我已经看到哪里”的判断。
筛选同样不能只看结果是否变少。筛选条件应该在状态变化后仍然可见,清除筛选后应能回到完整列表;如果没有匹配,空态要告诉用户怎样恢复,而不是留下一个看似仍在加载的网格。一个视觉修复只有在分页、筛选和恢复入口仍然互相说得通时,才算真正完成。
这里有一个常见的连锁风险:为了让空卡片更紧凑,开发者顺手隐藏了某些区域,结果把按钮、筛选条件或键盘焦点一起隐藏掉。布局改变后,原来依赖固定高度、固定索引或固定焦点的逻辑都值得重新走一遍。页面越是“只是改样式”,越不能假设交互不会受影响。
错误状态必须给用户一条回来路
我还专门检查了加载失败和无效入口,因为错误状态最能暴露一个页面是否真正为用户着想。失败时,如果只显示“出错了”,用户知道问题存在,却不知道下一步该做什么。没有恢复入口,错误就会把页面变成死胡同。
这次补上的恢复路径包括重新尝试、清除无效筛选、从空态回到列表,以及从审看状态返回原来的内容位置。审看器本身也要作为状态切换的一部分来测:打开后能否用左右键切换,按 Esc 是否关闭,关闭后焦点是否回到触发它的卡片。如果这些动作有一个断掉,用户就会被迫用鼠标重新定位,甚至误以为页面丢失了自己的位置。
深链也属于恢复能力的一部分。用户可能直接打开某个具体模板的链接,而不是从列表第一项开始。链接有效时,应能直达对应内容;链接无效时,也要提供回到可用列表的入口。不能因为正常路径能走通,就把异常路径交给浏览器的空白页面。
这件事让我重新意识到,恢复不是错误发生后的附加礼貌,而是状态设计的一部分。一个流程成熟的标志,不是它只在数据完整时顺利,而是它在缺图、失败、无匹配和深链失效时,仍然知道怎样把用户送回可用的位置。
用状态矩阵代替凭感觉验收
这次之后,我给自己留下了一个很简单的检查方法。先列资源状态,再列用户动作,最后检查两者有没有对应关系。资源状态至少包括加载中、没有资源、加载失败、没有匹配;用户动作至少包括继续看、重试、清除条件、打开详情、返回列表和使用键盘。
如果某个状态没有动作,先问它是否真的不需要动作,还是漏了恢复入口。如果某个动作在多个状态下出现,确认它的含义是否一致。例如“继续加载”应该增加记录,“重试”应该重新请求资源,不能只是让界面重新画一遍同样的灰块。状态名称相似,不代表行为可以复用。
然后再检查结构不变性:分页后新增内容是否真实出现,筛选后条件是否保留,焦点是否跟着内容移动,深链是否能直达,关闭审看器后是否回到原处。最后用不同宽度检查是否有横向溢出,并查看页面错误数量。这个顺序很重要,因为先验收外观,容易把“看起来整齐”误认为“行为正确”。
可复用的方法是:先把加载中、无资源、失败和无匹配写成4种明确状态,再为每种状态配事实、动作和恢复路径,最后从分页、筛选、焦点和深链回测整条链路。
它不只适用于海报。头像、附件、缩略图、报告封面、音频波形,只要资源可能缺失,都可以用同样的方法处理。每次遇到空白区域,我都会先问:这是暂时没有拿到,还是本来就没有?这个问题一旦问清,设计就不会被一块灰色带着走。
下次我会怎么做
下次再遇到“页面里有很多卡片,但图片不出来”,我会先确认资源事实,而不是先调临时图形的颜色和尺寸。若资源确实在加载,就保留轻量等待;若资源不存在,就把事实信息提到前面;若请求失败,就给重试;若筛选无结果,就给清除条件的入口。
接着我会把分页、筛选、焦点、深链和详情审看一起列入验收范围。因为用户不是来欣赏一张卡片的,他要在页面里找到东西、继续往下看、打开又回来。任何一个入口被视觉改动悄悄弄坏,页面就只是局部变漂亮,整体仍然不可靠。
最后,我会用多种宽度和错误路径各走一遍,并确认页面没有横向溢出、没有未处理错误,焦点也没有消失。**我更愿意把“空数据诚实可见”当成一种长期工作习惯,而不是一次性的界面技巧。**当系统没有某项资源时,最好的设计不是替它编一个存在,而是把真实状态说清楚,同时保留用户继续行动的路。