第 1 章 · 设计原则
把「好看」拆成可执行的规则
布局不是装饰。对齐、留白、层级、栅格这四件事,决定了读者先看到什么、按什么顺序读、在哪里停下来。本章开始我们接手一个真实任务:为个人博客「远山手记」设计首页 —— 先用四条原则把一堆内容整理成一份可读的版面。
案例启动:「远山手记」首页。这是一份纯技术博客,作者希望读者 30 秒内判断「这里有没有我要的东西」,并能顺畅地连续读下去。本章交付物:一份对齐、间距、层级都成体系的线框结构。后面四章会依次补上骨架、实现、响应式与精修。
对齐:让所有边缘落在同一条线上
人的视觉系统会自动寻找对齐线;找不到时,页面读起来就「毛糙」。对齐的第一目的不是好看,而是减少读者需要解释的边缘数量。
讲解
对齐在解决什么问题
一条对齐线,等于一次「这几个元素是一伙的」的暗示。当你把页头的标题、正文的段首、卡片的名字都放在同一条左边界上,读者不需要再逐个判断它们的从属关系 —— 这是把认知成本从读者身上拿走。反过来,边缘每多一条,阅读就多一分噪声。
页面上通常有三类可以对齐的「线」,优先级从高到低:
- 版心边界:内容区左右两侧的竖线,是最强、最容易被感知的一条。所有主要区块都应对齐到它。
- 文本基线:不同字号的文字并排时,让它们「坐」在同一条基线(baseline)上,而不是几何中心。
- 栅格列线:当页面复杂到版心边界不够用时,用栅格细分(见 1.4)。
三个常见决策
| 场景 | 推荐 | 理由 |
|---|---|---|
| 多行正文 | 左对齐 | 每行起点一致,回扫成本最低。中英混排时两端对齐会把英文单词间拉出空洞(「河」)。 |
| 短标题 / 按钮 | 可以居中 | 一两行的短文本,居中不会破坏回扫节奏,反而更能聚焦。 |
| 长段落 | 不要居中 | 每行起点都在变,眼睛找不到下一行的开头,读三行就累了。 |
一个可操作的检查方法:眯起眼睛看页面(或者把截图缩到 25%)。如果这时还能看出几条清晰的竖线,说明对齐成立;如果看到的是边界互相错开的「毛边」,就说明有元素没落到线上。
- 严格对齐的代价
- 所有东西都严格落在栅格上,版面会显得「板」、缺少节奏,因为意外感被抹掉了。
- 有意的越界
- 让一张大图出血到版心之外,可以制造呼吸感与重点。但越界必须是唯一的、故意的 —— 两处越界就变成了失控。
关键代码
/* 三层对齐:基线、版心、栅格 */
.post-card {
display: flex;
align-items: baseline; /* 不同字号的文字并排 → 基线对齐,视觉上才「坐得住」 */
gap: 12px; /* 间距同样要成体系,别随手写 11px */
}
.post-card__title { font-size: 24px; }
.post-card__date { font-size: 13px; color: var(--muted); }
.post-card__tag {
margin-left: auto; /* 用 auto margin 靠右,不必再套一层容器 */
align-self: center;
}
.post-card__body {
text-align: left; /* 多行正文永远左对齐 */
max-width: 68ch; /* 顺手把行宽管住,理由见 2.1 */
margin-inline: auto; /* 注意是「容器居中」,不是「文字居中」 */
}
align-items: baseline与center的差别,是这个演示里最容易看出来的部分 —— 打开演示切一下就知道。margin-left: auto是 Flex 里最省事的靠边手法:它吃掉所有剩余空间,因此天然响应容器宽度变化。margin-inline是逻辑属性,在从左到右与从右到左的语言里都能正确居中。
动态演示
对齐不是为了整齐,而是为了让读者不必解释你的版面:标题、标签、按钮都落在同一条线上,眼睛一次就能扫完。
建议的尝试顺序:先关掉「启用对齐」看看边界错位的观感,再打开「列参考线」逐个把元素放回线上;最后把元素对齐从 center 切到 baseline,观察头像、标题、标签的相对位置变化。
留白:间距是信息结构的可见形式
留白不是「剩下的地方」,它是分组与层级的载体。判断一段留白是否合格,看的是比例关系,而不是绝对值。
讲解
接近性原则:距离即关系
格式塔心理学的接近性原则说:人会把彼此靠近的元素归为一组。这条规律给了留白一个可操作的判据 —— 组内间距必须小于组间间距。卡片内部的标题与摘要靠得近、卡片与卡片之间离得远,读者不用看分割线就知道「这几行是一张卡」。
一旦这个关系反转(组内比组间还大),版面立刻失去结构:读者会开始怀疑「这张卡到底包含哪几行」。这就是很多「内容都对、但看着乱」的页面真正的病因。
用刻度代替随手取值
不要每次需要间距时临时想一个数。做法是先定义一条间距刻度(例如 4 / 8 / 12 / 16 / 24 / 32 / 48 / 64),所有间距都从刻度里取。这样做有三个好处:
- 节奏感来自比例,而不是来自某个魔数;
- 改一处刻度,全站同步,不需要逐页调;
- 团队协作时有共同语言,review 时不必争论「16 还是 18」。
本站自己用的就是这条刻度(见 assets/css/tokens.css 里的 --s-1 … --s-9),并且全部间距都是它的倍数 —— 这正是「本站是自身所讲方法的示范」的一处具体体现。
- 留白不足
- 信息密度高、一屏能看到更多,但读者需要消耗更多注意力去分组,长文尤其吃力。
- 留白过度
- 呼吸感强、显得高级,但一屏信息太少,滚动成本上升;在工具型界面里会直接降低效率。
- 取舍依据
- 读得越「深」的场景(长文、详情页),留白越要足;扫得越「快」的场景(列表、后台表格),留白越要省。
关键代码
:root {
/* 一条刻度,全站共用;倍数关系比数值本身更重要 */
--space: 16px;
}
.post-list {
display: grid;
/* 组间:1.9 倍 —— 让「卡片之间」明显大于「卡片内部」 */
gap: calc(var(--space) * 1.9);
}
.post-card {
padding: var(--space); /* 卡片自己的呼吸空间 */
display: grid;
gap: var(--space); /* 组内:1 倍 */
}
/* 只在需要「更紧密」的地方,才用 calc 缩小一档 */
.post-card__meta {
display: flex;
gap: calc(var(--space) * 0.5); /* 日期与标签是同一组的细粒度信息 */
}
- 用
gap而不是margin:gap只作用在项目之间,不会在首尾多出空白,也就不必写:last-child { margin: 0 }这类补丁。 - 倍数让关系可读:看到
1.9与1,立刻知道哪一层是「组间」。 - 如果项目用了
clamp()做流体间距,刻度依然成立 —— 只是把刻度本身变成流体值。
动态演示
圣杯布局为什么还在被使用
三栏 + 通栏头尾 + 正文优先加载,这套需求今天依然常见。
Flexbox 与 Grid:按维度选型
一维用 Flex,二维用 Grid。判断标准只有一条。
把「组间 > 组内」关掉,你会看到同样的内容和同样的间距总量,但分组关系瞬间消失 —— 这是整章最值得盯住看的一秒。
视觉层次:告诉读者先看哪里
层级不是把重要的东西加粗放大,而是建立一套「顺序」。顺序清晰,读者三秒内就知道从哪儿读起。
讲解
可用的层级手段,按「副作用」从小到大
- 位置:越靠上、越靠左(在从左到右的语言里)越先被看到。这是最便宜的手段。
- 字号:最直接的「重要性声明」。但字号差距太小等于没做,差距太大则会压碎正文。
- 字重:在字号不变的前提下制造对比,副作用最小。适合在紧凑的界面里区分主次。
- 颜色与透明度:把次要信息「往后推」而不是把主要信息「往前拉」,整体更安静。
- 留白:环绕的空白越多,元素越被强调。这是最容易被忽略、也最高级的手段。
- 装饰:边框、底色、图标。最抢眼,也最容易过量 —— 通常放在最后用。
用比例尺,而不是逐个调字号
定义一条等比字号刻度:--base × ratio^n。基准 16px、比例 1.25,就得到 16 / 20 / 25 / 31 这样一组天生的层级。它的好处是层级关系由「比例」保证 —— 你不可能做出「标题和副标题差 1px」这种模糊的层次。
比例的选择本身就是设计判断:1.2 左右稳重、信息密度高,适合工具型界面;1.33–1.5 戏剧性强,适合杂志式版面与落地页。比例越大,可用的级数越少 —— 1.5 的比例下,四级之后字号就已经超出合理范围。
常见错误:层级过多。一个页面里出现五、六种字号,读者反而无法排序。多数页面的层级只需要三级:标题 / 正文 / 注释。超过三级时,先怀疑是不是把「装饰」当成了「层级」。
关键代码
:root {
--base: 16px;
--ratio: 1.25; /* 比例本身就是设计决策 */
--step-0: var(--base); /* 16px 正文 */
--step-1: calc(var(--base) * 1.25); /* 20px 副标题 */
--step-2: calc(var(--base) * 1.5625); /* 25px 标题 */
--step--1: calc(var(--base) / 1.25); /* 12.8px 注释 */
}
.post-title {
font-size: var(--step-2);
font-weight: 700; /* 字重:副作用最小的强调手段 */
line-height: 1.2; /* 字号越大,行高反而要越小 */
letter-spacing: -0.02em; /* 大字号需要收紧字距,否则显得松散 */
}
.post-meta {
font-size: var(--step--1);
color: var(--muted); /* 把次要信息往后推,而不是把主要信息往前拉 */
}
- 字号越大,需要的行高倍数越小、字距越紧 —— 这是排版里的一条反直觉规律,因为大字的「视觉体积」本来就大。
- 用
rem或calc表达刻度,而不是直接写25px:前者保留了「它从哪来」的信息。 - 本站的
--step-*刻度用了clamp()(见 tokens.css),于是层级在大屏上也保持同样的比例关系。
动态演示
精选
用栅格与留白,把首页安顿好
先把信息排序,再让版面承担顺序。
层次的本质是顺序,不是装饰。当标题、副标题、正文、注释之间的差距足够明确时,读者根本不需要「努力」就能知道从哪儿读起。
把比例一路调到 1.0:四个层级全部塌成同样大小,页面立刻变得「平」——这就是层级缺失的真实手感。
栅格:把版心切成可以复用的份数
栅格不是「限制」,而是一套共享的分割方案。它让不同区块在不知道彼此细节的情况下,依然对得上。
讲解
栅格由四样东西构成
- 列(column):内容的容器。列数决定了可组合的版式数量。
- 槽(gutter):列与列之间的固定空隙。它保证「任何两块内容之间都至少有这么宽」。
- 外边距(margin):版心与屏幕边缘的距离。它决定版心的最大宽度。
- 基线(baseline,可选):行高的整数倍垂直网格。跨列对齐容易,跨基线对齐难 —— 后者通常只在内容极其密集的出版物上才值得做。
为什么常见 12 列
12 可以被 2、3、4、6 整除。这意味着同一个栅格能同时表达「一半 / 三分之一 / 四分之一 / 六分之一」这些最常用的比例,因此 12 列成为事实标准。16 列(可同时表达 2/4/8)用在内容更细碎的站点,24 列常见于设计系统 —— 本质上都是为了「一次定义,多种组合」。
对博客首页这种结构来说,12 列绰绰有余:正文占 8 列、侧栏占 4 列,卡片一行三张各占 4 列。列数不是越多越好:列数越多,槽的总宽度占比越难控制,最后往往退化成「每块都占满,栅格名存实亡」。
CSS Grid 时代的栅格:过去栅格要靠 float + 百分比(并留下 clearfix 与四舍五入误差),今天一行 grid-template-columns: repeat(12, minmax(0, 1fr)) 就够了。minmax(0, 1fr) 里的那个 0 很重要 —— 它防止长单词或长代码把列撑破。
- 栅格的价值
- 一致性来自约束。有了共同的分割方案,跨页面的版式才可能是「一套」的,而不是每一页各自为政。
- 栅格的代价
- 强约束会让版式趋于雷同。成熟的做法是:结构严格对齐栅格,视觉重量(颜色、图幅、留白)允许越界。
关键代码
.layout {
display: grid;
grid-template-columns: repeat(12, minmax(0, 1fr)); /* 12 列:能被 2/3/4/6 整除 */
gap: 24px; /* 槽宽 */
max-width: 1180px; /* 版心 */
margin-inline: auto;
padding-inline: 16px; /* 小屏时的外边距 */
}
.layout > .hero { grid-column: span 12; } /* 通栏 */
.layout > .article { grid-column: span 8; } /* 正文 8/12 */
.layout > .aside { grid-column: span 4; } /* 侧栏 4/12 */
.layout > .card { grid-column: span 4; } /* 一行三张 */
span N比grid-column: 1 / 9更适合栅格:你只需要声明「占几份」,起始位置交给自动放置。- 槽宽用
gap,因此每一块内容都自动获得两侧的呼吸空间,不必再给单块算 margin。 - 栅格是「版心内部」的规则;版心本身的宽度与居中由
max-width+margin-inline: auto负责,两者分工明确。
动态演示
把列数从 12 调到 5:正文占 3、侧栏占 2、卡片一行只有一张 —— 你会直观看到「列数决定了可表达的比例」。再关掉栅格显示,检查内容边界是否仍然对得上。