CSS 排版与字体

系统掌握 CSS 排版的所有关键属性:字体系统、行高与基线、文本对齐、字间距、以及现代字体技术(font-feature-settings、font-display)。

CSS 排版与字体

排版是 Web 设计的根基

无论 UI 多么精美,内容的可读性最终还是取决于排版。字体的选择、字号的大小、行高的设置、字间距的调整——这些细节决定了用户阅读内容的舒适度。

理解 CSS 排版不只是记住几个属性,更是理解字体度量系统(baseline、x-height、cap height)和文本渲染模型

面试定位:排版是前端面试的常考点。面试官通过 line-height 的计算、行内元素的垂直对齐、font-feature-settings 的用法等问题,问的是候选人对可读性和字体渲染的深层理解。


字体属性

font-family

.font {
  /* 字体栈 */
  font-family: "SF Pro Display", "Helvetica Neue", Arial, sans-serif;

  /* 具名字体族 */
  font-family: serif;      /* Times, Georgia, serif */
  font-family: sans-serif; /* Arial, Helvetica, sans */
  font-family: monospace; /* Courier, monospace */
  font-family: cursive;   /* cursive, script */
  font-family: fantasy;   /* fantasy, decorative */

  /* 系统字体栈 */
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}

font-size

.size {
  /* 绝对单位 */
  font-size: 16px;
  font-size: 12pt; /* 不推荐,pt 是印刷单位 */

  /* 相对单位 */
  font-size: 1rem;    /* 相对于根元素 */
  font-size: 1em;     /* 相对于父元素 */
  font-size: 100%;    /* 相对于父元素 */

  /* 视口单位 */
  font-size: 2vw;   /* 视口宽度的 2% */
  font-size: 2vh;   /* 视口高度的 2% */
  font-size: 2vmin; /* 较小者 */
  font-size: 2vmax; /* 较大者 */

  /* clamp() 响应式字体 */
  font-size: clamp(1rem, 2vw + 0.5rem, 2rem);
}

font-weight

.weight {
  font-weight: normal;    /* 400 */
  font-weight: bold;      /* 700 */
  font-weight: lighter;   /* 相对于父元素更轻 */
  font-weight: bolder;    /* 相对于父元素更重 */

  /* 数值 */
  font-weight: 100;  /* Thin */
  font-weight: 200;  /* Extra Light */
  font-weight: 300;  /* Light */
  font-weight: 400;  /* Regular */
  font-weight: 500;  /* Medium */
  font-weight: 600;  /* Semi Bold */
  font-weight: 700;  /* Bold */
  font-weight: 800;  /* Extra Bold */
  font-weight: 900;  /* Black */
}

font-style 和 font-variant

.style {
  font-style: normal;    /* 默认 */
  font-style: italic;    /* 斜体 */
  font-style: oblique;   /* 倾斜(使用倾斜版本字体或算法倾斜) */

  font-variant: normal;
  font-variant: small-caps; /* 小型大写字母 */
}

文本属性

line-height

.line-height {
  /* 推荐值:1.4 - 1.6(正文) */
  line-height: 1.5;      /* 无单位,最常用,相对当前 font-size */
  line-height: 1.5em;   /* 有单位,相对于当前元素的 font-size */
  line-height: 24px;    /* 固定值,不推荐 */
  line-height: normal;   /* 浏览器默认,约 1.2 */
}

无单位 line-height 的计算:行高 = 无单位值 × 元素的 font-size。无论 font-size 如何变化,比例关系保持。

letter-spacing 和 word-spacing

.spacing {
  /* 字间距 */
  letter-spacing: normal; /* 默认 */
  letter-spacing: 0.05em; /* 增加字间距 */
  letter-spacing: -0.05em; /* 减少字间距(用于紧凑标题) */

  /* 词间距 */
  word-spacing: normal;  /* 默认 */
  word-spacing: 0.25em;  /* 增加词间距 */
}

text-align

.align {
  text-align: left;    /* 左对齐(默认 LTR) */
  text-align: right;   /* 右对齐 */
  text-align: center;  /* 居中 */
  text-align: justify; /* 两端对齐(对英文有效,中文通常不需要) */
  text-align: start;   /* 文本开头对齐 */
  text-align: end;     /* 文本末尾对齐 */
}

text-transform

.transform {
  text-transform: none;
  text-transform: uppercase;   /* 全大写 */
  text-transform: lowercase;    /* 全小写 */
  text-transform: capitalize;   /* 首字母大写 */
}

vertical-align 垂直对齐

基础值

.vertical {
  vertical-align: baseline;    /* 默认,与基线对齐 */
  vertical-align: top;         /* 与行盒顶部对齐 */
  vertical-align: bottom;      /* 与行盒底部对齐 */
  vertical-align: middle;      /* 与 x-height 中点对齐 */
  vertical-align: text-top;    /* 与父元素文本顶部对齐 */
  vertical-align: text-bottom; /* 与父元素文本底部对齐 */

  /* 数值 */
  vertical-align: 10px;   /* 正值向上 */
  vertical-align: -5px;   /* 负值向下 */
  vertical-align: 50%;    /* 相对于 line-height */
}

行内块元素的垂直对齐

/* 图片和文字垂直对齐 */
img {
  vertical-align: middle; /* 让图片与同行文字居中对齐 */
}

/* 解决 inline-block 基线对齐问题 */
.inline-block {
  vertical-align: top; /* 而不是默认的 baseline */
}

高级字体技术

font-feature-settings

.features {
  /* 启用 OpenType 特性 */
  font-feature-settings: "liga" 1;  /* 连字(ligatures) */
  font-feature-settings: "kern" 1;  /* 字距调整(kerning) */
  font-feature-settings: "smcp" 1;  /* 小型大写(small caps) */
  font-feature-settings: "onum" 1;  /* 旧式数字(oldstyle figures) */
  font-feature-settings: "tnum" 1;  /* 等宽数字(tabular figures) */

  /* 简写 */
  font-feature-settings: "liga" 1, "kern" 1, "onum" 1;

  /* 更简单的写法(现代) */
  font-variant-ligatures: common-ligatures;
  font-variant-numeric: oldstyle-nums tabular-nums;
}

font-display

/* 自定义字体加载策略 */
@font-face {
  font-family: "MyFont";
  src: url("/fonts/myfont.woff2") format("woff2");
  font-display: swap; /* 加载策略 */
}

/* font-display 值 */
font-display: auto;    /* 浏览器默认 */
font-display: block;    /* 短暂阻塞,后交换 */
font-display: swap;     /* 立即显示后备字体,后交换 */
font-display: fallback; /* 极短阻塞,后无后备 */
font-display: optional; /* 尝试使用,否则用后备 */

font-synthesis

/* 控制字体合成 */
font-synthesis: none;    /* 不合成,依赖字体文件 */
font-synthesis: weight;   /* 仅合成粗细 */
font-synthesis: style;   /* 仅合成斜体 */
font-synthesis: weight style; /* 两者都合成 */

文本省略与截断

单行省略

.single-line {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

多行省略

.multi-line {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3; /* 行数 */
  overflow: hidden;
}

实战:排版系统

/* 基础排版比例 */
:root {
  --font-size-xs: 0.75rem;   /* 12px */
  --font-size-sm: 0.875rem;  /* 14px */
  --font-size-base: 1rem;     /* 16px */
  --font-size-lg: 1.125rem;   /* 18px */
  --font-size-xl: 1.25rem;    /* 20px */
  --font-size-2xl: 1.5rem;   /* 24px */
  --font-size-3xl: 1.875rem;  /* 30px */

  --line-height-tight: 1.25;
  --line-height-normal: 1.5;
  --line-height-relaxed: 1.625;

  --letter-spacing-tight: -0.025em;
  --letter-spacing-normal: 0;
  --letter-spacing-wide: 0.025em;
}

/* 标题系统 */
h1, .h1 {
  font-size: var(--font-size-3xl);
  line-height: var(--line-height-tight);
  letter-spacing: var(--letter-spacing-tight);
  font-weight: 700;
}

h2, .h2 {
  font-size: var(--font-size-2xl);
  line-height: var(--line-height-tight);
  font-weight: 600;
}

/* 正文样式 */
.body {
  font-size: var(--font-size-base);
  line-height: var(--line-height-normal);
  letter-spacing: var(--letter-spacing-normal);
}

面试高频问题

Q: line-height 为什么推荐使用无单位数值?

回答要点:无单位 line-height 是相对于当前元素的 font-size 计算的,值会继承给子元素但保持相同的比例关系。如果使用有单位(如 em 或 px),子元素会继承计算后的绝对值,可能导致不同字号下的行高不合理。

Q: vertical-align: middle 是相对于什么对齐?

回答要点vertical-align: middle 是让元素的垂直中点与父元素的 x-height(字母 x 的高度)的中点对齐,而不是完全居中。如果要完全居中行内元素,可以使用 flexbox(display: inline-flex; align-items: center)或者调整 line-height 使其与容器高度匹配。

Q: font-display: swap 有什么副作用?

回答要点font-display: swap 先显示后备字体(如系统字体),等自定义字体加载完成后切换。这避免了文字闪动(FOIT),但会导致文字闪烁(FOUT)。适用于需要尽快显示内容的场景,但可能导致布局跳动(如果字体宽度差异较大)。


延展阅读