容器永远不够大
问题的本质
你设计了一个表单字段。标签位置刚好,输入框对齐,描述文字工整。一切看起来完美。
然后真实数据进来了。描述不是 20 个字符,而是 200 个。标签不是"Prompt",而是"Describe the video content you want to generate (max 10000 characters)"。
容器炸了。
这不是代码问题。是设计假设的问题。
隐含的假设
每个 UI 设计都包含一个假设:内容会适配容器。
这个假设从未明说,但无处不在。我们设置固定宽度,期待文本在其中"刚好"排列。我们设计卡片布局,假设所有卡片的内容长度相似。我们创建标签,认为它们都会是简短的一两个词。
现实从不配合。
💡 Click the maximize icon to view in fullscreen
问题不在于我们没想到长文本。问题在于我们把"适配"当作默认状态,把"溢出"当作异常。
事实恰恰相反。溢出是常态,适配是巧合。
空间的有限性
一个表单字段的宽度是有限的。一个卡片的高度是有限的。屏幕本身就是有限的。
但信息是无限的。
描述可以是一句话,也可以是一段话。标签可以是"提示词",也可以是"Describe the video content you want to generate, including scene details, character actions, camera movements, and style preferences (maximum 10000 characters)"。
有限容器装无限信息,数学上就不成立。
设计的任务不是让信息"刚好合适",而是管理不合适。
Tooltip 的真正意义
遇到长文本,第一反应是截断。60 个字符,超出显示省略号。
这看起来像妥协。但其实不是。
Tooltip 不是因为"空间不够",而是一种信息密度的权衡。
💡 Click the maximize icon to view in fullscreen
用户扫描界面时,不需要看到所有细节。他们需要快速识别。60 个字符的描述足以判断这个字段是什么。如果需要更多信息,悬停就能看到。
这不是把信息藏起来,而是分层呈现。主要信息显示,次要信息可访问。
截断 + Tooltip 不是 UI 的补救措施,是信息架构的设计决策。
从数据层到表现层
数据库中存储这样的字段:
{
"label": "提示词",
"ui:description": "描述你想要生成的视频内容"
}
看起来没问题。中文简洁、清晰。
但在 UI 层,这会变成:
标签:提示词
描述:描述你想要生成的视频内容
现在假设产品需要支持多语言。英文版本变成:
Label: Prompt
Description: Describe the video content you want to generate (max 10000 characters)
描述突然变长了。容器溢出了。
问题的根源不在翻译,在数据层的语言选择影响了表现层的空间需求。
中文"提示词"3 个字符,英文"Prompt"6 个字符。中文描述 15 个字符,英文描述 80+ 字符。数据库中看不出区别,UI 中差异明显。
这揭示了一个更深层的问题:接口设计的语言假设。
如果数据层假设内容是中文(简短、紧凑),表现层迁移到英文(冗长、详细)时,所有长度假设都会失效。
数据库字段设计时,语言选择不只是"用什么语言存储",而是:
- 字段长度限制是否考虑了多语言
- 描述文本的详细程度是否一致
- UI 组件是否为长文本做了准备
中文友好的数据结构,不一定英文友好。
实践方法论
面对容器溢出,有一套递进的处理方式。
1. 承认空间的有限性
不要假设内容会适配。在设计阶段就问:如果文本是当前长度的 5 倍,会发生什么?
这不是悲观,是现实。
2. 设计截断策略
不同内容类型需要不同的截断长度:
- 标签:30-50 字符
- 描述:60-80 字符
- 列表项:100 字符
- 段落:200 字符
这些不是规则,是起点。根据实际内容调整。
3. 分层呈现信息
用户不需要一次看到所有信息。核心的显示,细节的隐藏:
- Tooltip 用于补充说明
- 可折叠区域用于可选内容
- 模态框用于复杂详情
重要的是信息可访问,不是一直可见。
4. 测试极端情况
用真实数据测试。不是"Hello World",而是:
- 最短的可能文本
- 最长的可能文本
- 包含特殊字符的文本
- 不同语言的文本
极端情况暴露假设。
创建测试数据集:
const testCases = [
{ label: "OK", description: "简短" },
{
label: "Very Long Label That Definitely Won't Fit",
description: "A description that goes on and on and on..."
},
{
label: "中文标签",
description: "中英文混合 Mixed language content 会有不同的长度表现"
}
]
在开发环境中切换这些数据,看 UI 如何响应。
权衡的艺术
处理容器溢出,本质是在权衡:
信息完整性 vs 界面整洁:显示所有信息会破坏布局,截断信息会损失细节。
用户控制 vs 自动处理:让用户展开查看更多,还是自动调整容器大小。
一致性 vs 灵活性:所有卡片保持相同高度,还是根据内容动态调整。
没有绝对正确的答案。选择取决于:
- 用户的主要任务是什么
- 信息的重要性层级如何
- 界面的整体美学目标
权衡不是妥协,是设计。
从修复到预防
遇到溢出问题时,修复很简单:加个 truncate 类,加个 Tooltip。
但真正的改进是从修复到预防。
下次设计新组件时,不要等到溢出发生。在设计阶段就问:
- 这个文本可能有多长
- 如果超出预期会怎样
- 用户如何获取完整信息
把容错机制作为设计的一部分,而不是事后补救。
这就是从"修 bug"到"设计系统"的转变。
技术背景
这篇文章源于一次真实的 UI 修复。两个组件出现了文本溢出:
修复的组件
DynamicForm.tsx:参数表单
- 问题:字段描述过长导致容器溢出
- 方案:创建
TruncatedDescription组件,60 字符截断 + Tooltip
ModelSelector.tsx:模型选择器
- 问题:模型描述破坏卡片网格布局
- 方案:
line-clamp-2限制两行 + Tooltip 显示完整信息
核心实现
function TruncatedDescription({ text, maxLength = 60 }) {
const needsTruncation = text.length > maxLength
if (!needsTruncation) {
return <span className="text-xs text-muted-foreground">{text}</span>
}
return (
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center gap-1 cursor-help">
<span className="text-xs truncate">{text}</span>
<HelpCircle className="h-3 w-3" />
</div>
</TooltipTrigger>
<TooltipContent>
<p className="text-xs">{text}</p>
</TooltipContent>
</Tooltip>
)
}
关键 CSS 类
truncate:单行截断line-clamp-2:多行截断flex-shrink-0:防止元素收缩min-w-0:允许 flex 子元素正确收缩
数据层修复
数据库中的中文标签需要迁移为英文:
UPDATE ai_models
SET config_schema = jsonb_set(
config_schema,
'{properties,prompt,label}',
'"Prompt"'
)
WHERE api_identifier = 'model-name';
完整的迁移脚本包含 13 个模型的字段对齐。
相关双链:编码前的思考 讨论了 Want-Have-Need 框架,这次修复也遵循了这个思路。
容器的哲学
容器永远不够大,因为信息没有边界。
设计的任务不是让内容适配容器,而是在有限空间中管理无限可能。
这需要承认限制,设计权衡,提供选择。
下次遇到文本溢出,不要只是加个省略号。问问自己:我假设了什么?这个假设成立吗?如果不成立,设计应该如何响应?
这就是从修 bug 到做设计的区别。
Related Posts
Articles you might also find interesting