Wensity Layout
布局是一种方式,让页面在人们阅读任何文字之前,就能告诉他们什么重要、什么属于一起。好的布局来自三个习惯:固定的间距比例、按距离分组,以及统一的对齐系统。本技能会建立这三个习惯,并涵盖响应式结构。
如果项目已经有间距比例和容器宽度,请使用它们。
1. 间距比例
使用 4px 作为基准。每个 margin、padding 和 gap 都来自这个列表:
| 值 | px | 典型用途 |
|---|---|---|
| 1 | 4 | 图标到标签,紧凑的内联间距 |
| 2 | 8 | 控件内部,标签与其输入框之间 |
| 3 | 12 | 密集列表中的项目之间,按钮垂直内边距 |
| 4 | 16 | 卡片内边距(紧凑),相关控件之间的间距,移动端页面边距 |
| 5 | 20 | 按钮水平内边距(大) |
| 6 | 24 | 卡片内边距,表单字段之间的间距 |
| 8 | 32 | 区块内群组之间 |
| 10 | 40 | 区块标题与其内容之间(大) |
| 12 | 48 | 移动端区块内边距 |
| 16 | 64 | 应用内区块之间,平板端区块内边距 |
| 20 | 80 | |
| 24 | 96 | 桌面端营销页面区块内边距 |
| 32 | 128 | 英雄区域内边距,一个大陈述周围的空间 |
Tailwind 的默认比例已经遵循 4px 基准(p-4 是 16px),所以请使用它的步长,不要发明 p-[13px]。只有在渲染时检查过视觉校正(例如图标看起来偏了 1px)的情况下才允许使用奇数值。
2. 按距离分组
最重要的布局规则:组内间距小于组间间距。 标签距离其输入框 8px;下一个字段距离 24px。卡片标题距离其文本 8px;下一张卡片距离 24px。当内部和外部间距相等时,所有内容都会漂浮散开,无法形成分组感。
- 目标是将内部和外部间距的比例控制在 1:2 或 1:3 左右。
- 标题上方留出更多空间,下方留出更少空间。 标题属于其后的内容。典型:标题上方 48px,下方 16px。
- 在盒子之前先考虑间距。 先通过距离进行分组。只有当距离不够时(如密集表格、工具栏、复杂背景上的内容),才添加边框、分割线或背景。给每个分组都加盒子会使页面显得沉重。
- 使用
gap处理同级元素(flex gap-4、grid gap-6),而不是给子元素加 margin。这能保持间距集中管理,并避免列表端部出现多余的 margin。
3. 节奏,而非雷同
如果一个页面的每个区块都有 96px 内边距、一个居中的标题和一个三列网格,那它就毫无节奏可言。有目的地变化密度:
- 将密集的区块(功能网格、表格、标志)与开放的区块(一个陈述、一张大图)交替排列。
- 让重要的区块占据更多空间。空间是你分配注意力的方式。
- 只有当内容确实是同类事物时才重复布局。
4. 对齐
- 一个起始边。 一个区块内的标题、段落、按钮和图像共享同一起始线。大多数“看起来不对劲”的抱怨都源于两条线相距几像素。
- 默认从起始边对齐(
text-align: start,在英文中为左对齐,在阿拉伯语或希伯来语中为右对齐)。仅居中简短、独立的内容:英雄标题、单个 CTA 块、空状态。表单、表格、列表和较长的文本都从起始边对齐。 - 表格和统计列中的数字靠右对齐。
- 按文本对齐,而非按盒子对齐。 图标旁边的标签应与标签的 cap-height 或中心对齐,而非与行框顶部对齐。在 flex 行中,单行使用
items-center;多行文本则使用items-start,并在图标上添加一个小的顶部 margin。 - 视觉校正: 播放图标、箭头和圆形形状在数学上居中时看起来会偏。在看到效果后,将其微调 1 到 2px。
5. 容器和宽度
| 容器 | 最大宽度 | 用途 |
|---|---|---|
| Prose | 65ch(约 640 到 720px) | 文章、文档、长段落 |
| Narrow | 560 到 640px | 表单、登录、聚焦任务、居中的英雄陈述 |
| Content | 1120 到 1280px | 大多数营销区块和应用页面 |
| Wide | 1440 到 1600px | 仪表板、画廊、编辑器 |
| Full bleed | 100% | 背景、图像、应与边缘接触的产品展示图 |
- 页面边距: 手机端 16px,平板端 24px,桌面端 32 到 48px。保持一致:整个站点每个断点只用一个边距值。
- 将背景与内容宽度分开。 区块的背景可以全宽铺开,而其内容保持在内容容器内。
- 在宽屏上不要让文本占满全宽。 图像和网格可以扩展,段落不行。
6. 网格
- 营销布局用 12 列 可以获得一半、三分之一、四分之一以及不对称分割(7 和 5,8 和 4)。不对称分割看起来比 6 和 6 更有设计感。
- 卡片使用自动填充网格,使列数跟随空间变化:
.cards { display: grid; gap: 24px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); }
min(100%, 280px) 可防止在窄于 280px 的屏幕上溢出。auto-fill 在只有一两张卡片时能保持卡片宽度稳定;auto-fit 则会拉伸卡片以填满行。根据需要选择。
- 让灵活轨道能够收缩。 默认情况下,Grid 和 flex 子元素拒绝收缩到其内容大小以下,这就是为什么长文本或宽表格会溢出仪表板。对灵活轨道使用
minmax(0, 1fr),对包含文本的 flex 子元素使用min-width: 0(Tailwindmin-w-0):
.app-shell { display: grid; grid-template-columns: 16rem minmax(0, 1fr); }
.row > .text { min-width: 0; }
- 仅对内容等高的元素使用等高。 默认情况下,一行中的卡片会拉伸到最高卡片的高度。这对于一组计划(价格比较)是可以的;但当一张卡片显然是特色卡片时就不对了。
- Bento 网格: 根据重要性改变瓦片大小,保持一个间距值,并给每个瓦片分配真实内容。空的装饰性瓦片看起来像模板。
- 使用 subgrid(
grid-template-rows: subgrid)当一行中的卡片需要其标题、正文和按钮互相垂直对齐时。
7. 响应式结构
移动端优先设计,然后随着空间增加添加结构。
- 断点跟随内容,而非设备。 在布局开始看起来不对劲的地方添加一个断点。Tailwind 的
sm 640、md 768、lg 1024、xl 1280是很好的默认值。 - 重组结构,而非仅仅缩小。 在移动端,三列功能网格变为一列,并排的文本和图像堆叠且图像放在后面(如果图像承载信息则放在前面),表格变为卡片列表或在其容器内水平滚动,侧边栏变为抽屉。
- 对出现在不同宽度的组件使用容器查询(例如侧边栏和主列中的卡片):
.card-wrap { container-type: inline-size; }
@container (min-width: 480px) { .card { grid-template-columns: 160px 1fr; } }
在 Tailwind v4 中:父元素上使用 @container,子元素上使用 @md:grid-cols-2。
- 根据屏幕缩放间距。 区块内边距:移动端 48px,平板端 64px,桌面端 96px。间距可以从 16 到 24 再到 32 逐步变化。
- 视口高度: 对于全高布局,使用
min-h-dvh(或100dvh),而不是h-screen(100vh)。在带有工具栏的移动浏览器上,100vh比可见区域更高,因此内容会隐藏在其下方。 - 带刘海手机的安全区域: 固定底部栏使用
padding-bottom: env(safe-area-inset-bottom),并在视口 meta 标签中使用viewport-fit=cover。 - 从右到左的语言: 使用逻辑属性(
margin-inline-start、padding-inline、inset-inline-end;Tailwindms-*、pe-*、start-*)替代 left 和 right,并使用dir="rtl"进行测试。镜像方向性图标(返回箭头、尖角符),但不要镜像标志或所有图标。 - 永远不要出现水平页面滚动。 在 320px 宽度下检查。常见原因有:固定宽度、长单词或 URL、
w-screen元素(包含滚动条宽度)、或负 margin。
8. 表格
- 表头行 扮演标签角色,当表格滚动时使用 sticky(
position: sticky; top: 0),并设置带色调的背景或更强的底部边框。 - 行高按密度: 紧凑型 36px,默认型 44px,舒适型 52px。当用户需求不同时,提供一个切换选项。
- 行之间通过细线分隔或斑马纹分隔,不可同时使用。
- 数字使用
tabular-nums靠右对齐;文本靠左对齐;状态和操作放在最后几列。 - 移动端的宽表格: 在其自身容器内水平滚动(
overflow-x: auto),第一列 sticky;或者切换到每行堆叠的卡片。绝不能让表格将页面推向侧面。 - 空状态、加载状态和错误状态 位于表格框架内部,保留表头,以便用户知道即将显示什么。
9. 密度
根据屏幕的使用频率匹配密度:
- 营销和引导: 开放。每屏项目少,间距大,字号大。
- 日常使用的应用屏幕: 紧凑。行高 36 到 44px,间距 8 到 12px,字号 14px。每天使用同一屏幕的人希望一次看到更多内容。
- 表格和数据工具: 密集,如果用户需求不同则提供密度切换。
- 不要在一个面板内混合不同的密度。
10. 触摸目标和点击区域
- 任何可触摸点击的元素,目标尺寸至少 44x44px,即使可见图标只有 16px。(WCAG 2.2 AA 要求至少 24x24px;44px 是舒适尺寸。)通过 padding 增大点击区域,或者使用一个以控件为中心的不可见伪元素,这样布局不会改变:
.icon-btn { position: relative; }
.icon-btn::before {
content: ""; position: absolute; left: 50%; top: 50%;
width: max(100%, 44px); height: max(100%, 44px);
transform: translate(-50%, -50%);
}
- 单独的触摸目标之间至少要有 8px 间距,以免拇指同时点到两个。
11. 层级
保持一个小型的 z-index 范围,并且只使用这些值:
:root { --z-dropdown: 10; --z-sticky: 20; --z-overlay: 30; --z-modal: 40; --z-toast: 50; --z-tooltip: 60; }
使用 showModal() 打开的本地 <dialog> 以及具有 popover 属性的元素会渲染在浏览器的顶层,高于所有 z-index,因此它们不需要设置 z-index。z-index: 9999 意味着堆叠上下文未被理解。应该修复堆叠上下文(通常是父元素上意外的 transform、filter 或 isolation),而不是提高数值。
12. Sticky 和固定元素
- Sticky 头部:保持简短(56 到 64px),并且仅在页面滚动到其下方时才给它们添加背景(实色或模糊),这样页面顶部保持干净。
- 在被锚定的标题上添加等于头部高度的
scroll-margin-top,以便页面内链接不会落在头部下方。 - 移动端的固定底部栏需要上述的安全区域 padding,并且不能覆盖页面上的最后内容;页面底部应填充等于该栏高度的内边距。
13. 完成前检查
- 每个间距值都在 4px 比例上。
- 组内间距明显小于组间间距。
- 标题上方空间多于下方。
- 每个区块有一个起始边;表格中数字靠右对齐。
- 灵活轨道使用
minmax(0, 1fr);文本子元素可以收缩。 - 即使在大屏上,段落宽度也限制在约 65ch。
- 移动端布局会重组结构,而不仅仅是缩小。
- 320px 宽度下没有水平滚动;全高区块使用
dvh。 - 触摸目标至少 44px(绝不小于 24px)。
- z-index 值来自定义的范围。
- 眯眼测试:最重要的内容和分组在模糊细节后仍然清晰可辨。