超级块响应式布局最佳实践
以实际内容区计算比例和空间预算,按信息优先级切换布局,并验证文字、进度与操作区域不会重叠。
Documentation content is currently available in Simplified Chinese.
超级块的布局应遵循三个步骤:测量 iframe 的实际内容区,按宽高分配空间,再按信息优先级选择布局。 比例计算适合字号和间距的小范围调整;信息数量和排列方向需要根据内容预算切换。
#1. 用页面内容区计算,不用网格占位或屏幕分辨率
在清单中声明的 cols × rows 是网格占位,包含宿主保留的间隔和标题区域,并不等于页面可以使用的宽高。当前桌面画布与移动设备采用 160 CSS 像素的逻辑网格;已放置组件的页面尺寸为:
W = cols × 160 − 2 × 24
H = rows × 160 − 2 × 12 − 24| 网格占位 | 已放置组件的页面内容区 |
|---|---|
| 1×1 | 112×112 |
| 2×1 | 272×112 |
| 2×2 | 272×272 |
| 4×1 | 592×112 |
| 4×2 | 592×272 |
| 4×4 | 592×592 |
组件目录卡片可能使用未扣除间隔的展示尺寸,例如 160×160。上述数字用于理解当前宿主和建立测试用例,不要把它们写成插件的运行时尺寸协议。实际排版应始终读取页面根元素的 clientWidth、clientHeight,并用 ResizeObserver 监听变化。
宿主会缩放整张逻辑画布。因此,屏幕截图中的像素大小、screen.width、devicePixelRatio、外层 iframe 的变换后矩形,都不适合决定页面布局。不要再按屏幕缩放一次页面。context.surface 用来对齐内缩描边和圆角,不是需要从页面宽高中再次扣除的网格间隔,详见外轮廓几何。
#2. 先做空间预算,再做有限的比例缩放
推荐以下起点,具体比例和上下限需要结合组件内容验证:
D = min(W, H)
P = clamp(8, 0.07 × D, 14) // 内边距,CSS px
G = clamp(4, 0.035 × D, 8) // 区域间距,CSS px
B = H − 2P − T − F − 2G // 头部、主体、底部三行时的主体高度这里 T、F 是头部与底部实际预留高度。例如 272×112 的活动卡片取 P=8、G=4、T=24、F=24,主体只剩 40px。两行 12px 标题约占 32.4px,不能再叠加一行预览。此时应收起预览,或者移除冗余底部操作并重新计算预算。
字号也要同时满足两条约束:
- 高度约束:主数值字号不得超过主体扣除标签、进度条、间距后的剩余高度。
- 宽度约束:为
100%、负数、单位、长模型名等内容预留空间。对于可变字符串,限制行数或省略;必须完整显示时,测量文本并切换布局。
例如主数值使用 line-height: 1,标签占 14px,进度条与间距共占 8px,则可以先计算 fontSize ≤ B − 22。再根据数值列宽度和最坏字符串约束字号,最后限制最大字号。不能只用 vw 放大字体:592×112 的长条高度仍然只有 112px。
当预算小于最小可读字号时,应减少信息、换行布局、提供详情入口,或缩小清单中的 allowedSizes 范围。不要持续压缩字号到无法阅读。
#3. 以内容容量切换布局
为每项信息规定最小空间,再确定展示优先级。推荐从最小布局向外增加内容:
| 组件 | 最小布局保留 | 空间增加后展示 |
|---|---|---|
| 活动 | 状态、当前标题、主要操作 | 消息预览、模型时间、最近对话列表 |
| 用量 | 窗口名称、百分比、进度、重要警告 | 重置时间、积分、次窗口、Token 统计 |
| 运行配置 | 模型入口、当前推理强度、速度开关 | 标题栏、完整选项、说明 |
横向布局至少需要满足 可用宽度 ≥ 模型最小列宽 + 两个操作列宽 + 间距;纵向布局至少需要满足 主体高度 ≥ 标题区域 + 操作行 + 间距。长宽比只能帮助选择方向,不能代替这两个条件。
在 112×112 的运行配置中,可以让模型独占上半区,推理强度和速度各占下方一列;将完整推理选项收起,由当前强度按钮承担循环切换。模型名最多两行,“标准”等短标签保持不换行。不要把横向三个完整面板直接缩进小方块。
错误提示同样需要预算。紧凑布局可用错误区域替换主体,保留刷新或重试操作;长错误文本在该区域内部滚动。不要把错误作为没有高度预留的额外一行追加。
#4. 可直接使用的布局骨架
下面的示例显示一个标签、数值、进度条和重要提示,按主体剩余空间决定数值字号。它采用保守的四字符宽度上限,适合 100%;换成金额或任意文本时,需要重新计算宽度预算。
<style>
* { box-sizing: border-box; }
html, body { margin: 0; width: 100%; height: 100%; overflow: hidden; }
.card {
width: 100%; height: 100%; min-width: 0;
display: grid;
grid-template-rows: minmax(0, 1fr) 14px;
padding: var(--pad, 8px); gap: var(--gap, 4px);
background: #101010; color: #eee; font-family: system-ui;
}
.primary {
min-height: 0; min-width: 0;
display: grid; grid-template-rows: 14px minmax(0, 1fr) 4px;
gap: 4px;
}
.label, .notice {
min-width: 0; font-size: 10px; line-height: 14px;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.value { min-width: 0; align-self: center; font-size: var(--value-size, 24px); line-height: 1; white-space: nowrap; }
.track { height: 4px; background: #333; }
.track > span { display: block; height: 100%; width: 100%; background: #e18d7b; }
.notice { color: #dd955b; }
</style>
<div class="card" id="card">
<main class="primary">
<span class="label">每周用量</span>
<strong class="value">100%</strong>
<div class="track"><span></span></div>
</main>
<span class="notice">用量暂不可用</span>
</div>
<script>
const card = document.querySelector('#card');
const clamp = (min, value, max) => Math.max(min, Math.min(value, max));
function layout() {
const W = card.clientWidth, H = card.clientHeight;
if (!W || !H) return;
const D = Math.min(W, H);
const P = clamp(8, D * .07, 14);
const G = clamp(4, D * .035, 8);
const B = H - 2 * P - 14 - G;
// Label 14 + two gaps 8 + track 4. Width allows four full ems.
const fontSize = Math.min(56, B - 26, (W - 2 * P) / 4);
card.style.setProperty('--pad', P + 'px');
card.style.setProperty('--gap', G + 'px');
card.style.setProperty('--value-size', Math.max(16, fontSize) + 'px');
}
new ResizeObserver(layout).observe(card);
layout();
</script>此骨架按不小于 112×112 的内容区设计。若需要支持更小尺寸,应另设最小布局并验证,不能仅把公式的最小字号调低。实际插件中的数值仍应来自真实状态。
使用 Grid 或 Flex 时,主体设置 min-height: 0、文本列设置 min-width: 0,让浏览器能够分配有限空间;同时确保子内容确实适合该空间。min-height: 0 和根元素的 overflow: hidden 都不会自动解决重叠,后者只会把超出部分裁掉。
#5. 验证的是区域关系,不只是页面溢出
发布前,至少完成以下验证:
- 从清单的
allowedSizes枚举全部规格,检查当前宿主对应的真实内容区;同时检查目录卡片和布局断点前后 1px。默认规格通过不代表其它规格可用。 - 覆盖加载、空数据、长中文和无空格英文、
0%、100%、长模型名、最多选项、警告、长错误文本,以及隐藏可选字段后的状态。 - 在浏览器中测量关键区域的
getBoundingClientRect():兄弟区域不能相交,文本和进度条不能越过各自分配的区域,按钮不能互相覆盖。允许滚动的列表只在自己的容器内滚动。 - 同时检查
scrollWidth、scrollHeight。它们只能发现越界,发现不了发生在根元素内部的文字、进度条和底部操作重叠。仅用模拟 DOM 的业务测试也无法验证 CSS 排版。 - 对最小方块、最矮长条和最大规格保存截图,检查字号、截断、圆角边缘和实际可点击区域;最后在桌面预览与设备页面中验证宿主缩放效果。
这些公式是可验证的设计起点,不是统一的强制断点。每个插件应根据实际文本、操作数量和展示优先级建立自己的尺寸预算,并把最小支持规格纳入回归检查。