在我们日常的前端开发里,CSS 字体与文本属性其实撑起了网页“颜值”的大半壁江山。很多人做页面时花了大把时间调布局、调颜色,最后却发现文字排出来要么发飘、要么拥挤、要么完全没有层级感,问题往往就出在字体和文本这两类最基础的属性上。这篇内容我会把字体到文本相关的常用 CSS 属性系统过一遍,结合我做项目时的真实用法和踩坑记录,从属性含义讲到实操技巧,争取让新手看完能直接用,老手也能从中翻出几个容易被忽略的细节。
这是“CSS 常用元素属性宝典”的上篇,核心锁定字体与文本控制。适合刚学完选择器和盒模型、正要进入样式细节阶段的前端学习者,也适合那些切图多年但从来没认真梳理过排版细节的开发者。理解这一篇的内容之后,你对“为什么有些页面看起来高级”这件事会有一个非常清晰的答案。
1. 字体与文本控制:网页颜值的底层逻辑
1.1 一个差点让我返工的项目印象
先说我印象很深的一个项目。当时做一个企业官网,设计稿上标题用了思源黑体,正文用苹方,整体走清爽路线。我第一版直接用系统字体,没做字体栈,结果在 Windows 上打开整站变成宋体,那种感觉就像西装革履的人突然换了一身睡衣。后来我花了大半天逐个模块调整字体族,又为不同操作系统配了对应的中文字体栈,才算把设计稿的质感找回来。
那次之后我意识到,字体和文本相关属性不是“随便写写就行”的细枝末节,而是决定用户第一观感的关键因素。浏览器对不同字体的渲染差异非常大,同样一段文字,字号、字重、行高、对齐方式有一项不对,整体感觉就会明显变差。尤其是中文网页,字体选择比英文更复杂,因为中文字体文件大、版权限制多、不同系统的默认字体差异也更明显。
1.2 字体属性与文本属性怎么分工
很多人把 font-* 和 text-* 两组属性混在一起,其实它们的职责边界很清晰。字体属性描述的是“字本身长什么样”,比如用哪套字体、字号多大、笔画多粗、是不是斜体;文本属性描述的是“这段文字怎么摆放和呈现”,比如颜色、对齐方式、行间距、装饰线、换行规则。
这两类属性合在一起,才构成完整的文本排版能力。打个比方,字体属性决定你去哪家店买什么款式的衣服,文本属性决定这件衣服怎么穿、怎么搭配、怎么展示。实际开发中,我习惯先把字体属性定好,再调整文本属性,顺序对了,效率会高很多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 字体属性全解析:从字体族到字号字重
2.1 font-family:字体栈的正确写法与中文字体搭配
font-family 可能是前端开发者最早接触的 CSS 属性之一,但真正写对的人比例并不高。它的核心逻辑是“备胎机制”:浏览器依次尝试列表中每个字体,直到找到用户系统里存在的那一个。所以字体栈的顺序非常重要,要把最想用的放在最前面,把兜底字体放在最后面。
先看一个我在中文项目中常用的字体栈:
css复制body {
font-family: "PingFang SC", "Microsoft YaHei", "Source Han Sans SC",
"Noto Sans SC", sans-serif;
}
这个字体栈的顺序安排背后是有讲究的。PingFang SC 是 macOS 和 iOS 的中文默认字体,字形现代、渲染清爽;Microsoft YaHei 是 Windows 的中文字体,覆盖率高;Source Han Sans SC 和 Noto Sans SC 是思源黑体的不同分发版本,适合 Linux 或已安装该字体的用户;最后的 sans-serif 是终极兜底,保证任何系统都能渲染出无衬线字体。
写 font-family 时有几个容易出错的细节需要特别注意。
- 字体名包含空格或中文时,必须加引号,比如 "PingFang SC"、"Microsoft YaHei"。
- 中英文字体要分开放置,先写英文字体再写中文字体,因为中文字体通常也包含英文字形,先声明英文字体可以让英文部分使用更优质的西文字形。
- 不要把不存在的字体放在列表里浪费性能,浏览器会逐个检查,虽然这个开销很小,但没意义的字体名纯属多余。
- 千万别在字体栈里使用“宋体”“楷体”这类系统默认衬线字体作为主力,除非你的设计明确要那种复古或正式的调性。
2.2 font-size:px、rem、em 怎么选,为什么主流方案是 rem
字号是排版的关键参数,也是新手最容易纠结的地方。常用的单位有 px、em、rem、vw 这几类,选哪个不是凭喜好,而是看你的需求是“固定尺寸”还是“响应式缩放”。
px 是最直观的绝对单位,1px 就是屏幕上的一个物理像素点。它的优点是可控性强、和设计稿标注一致,缺点是完全不跟随根字号变化,做全局缩放时很费劲。
rem 是相对根元素(html)的字号单位。如果 html 的 font-size 是 16px,那 1rem 就等于 16px。改变根字号,所有使用 rem 的属性都会跟着变化。这是目前主流响应式方案的核心原理。
em 是相对当前元素字号的单位。它的特点是会叠加继承,嵌套层级多了之后计算容易混乱,一般用于特定场景,比如按钮的内边距相对按钮文字大小自适应。
我在实际项目中的做法是:根元素用 px 定基准字号,布局和组件尺寸用 rem,某些需要随自身字号缩放的细节用 em。一个比较常见的基准设置是:
css复制html {
font-size: 16px;
}
@media (max-width: 768px) {
html {
font-size: 14px;
}
}
h1 {
font-size: 2rem; /* 32px,移动端变为 28px */
}
这样设置的好处很明显。设计稿是 375px 宽度还是 1440px 宽度,我只需要调整 html 的 font-size,整个页面的字号体系会自动跟随变化,不需要逐个组件去改。要注意的是,rem 的基准来自根元素,如果你在某个容器上给 html 的 font-size 改了,那同一页面所有 rem 单位都会变,所以一般只改 html 那一处,不要在局部覆盖。
2.3 font-weight:字重远不止 bold 这么简单
font-weight 控制的是一套字体里不同粗细的字形。常见取值包括 100 到 900 的数值,以及 normal、bold、bolder、lighter 这几个关键字。默认值 normal 相当于 400,bold 相当于 700。
这里有一个很多人忽略的细节:font-weight 并不是“无中生有”地加粗文字。它要求当前字体族本身包含对应的字重版本,否则浏览器会做“伪粗体”处理,也就是在渲染时将字形边缘向外扩张模拟加粗效果。伪粗体的观感普遍偏肉、边缘模糊,在高分辨率屏幕上尤其明显。
所以严谨的做法是,在引入 Web 字体或者使用系统字体时,提前确认这个字体提供了哪些字重。比如思源黑体有 ExtraLight、Light、Regular、Medium、Bold、Heavy 等多个字重,对应的数值分别是 200、300、400、500、700、900。实际项目中,我常用的是 400 做正文、500 做强调、700 做标题,层级感足够又不至于太跳。
使用可变字体(Variable Fonts)时,font-weight 可以设置任意数值,比如 450、620 这种中间值,渲染时会由字体引擎自动插值生成对应字形。可变字体在实际项目中的普及度还在增长,如果字体文件支持,你可以用 CSS 的 font-variation-settings 或直接设置 font-weight 的精确数值来获得更细腻的粗细控制。
2.4 font-style 与 font-variant:斜体和小型大写字母的正确打开方式
font-style 控制文字是否倾斜,取值有 normal、italic、oblique。italic 是调用字体自带的斜体字形,oblique 则是把正常字形做倾斜变换。对于大多数中文字体来说,并没有真正的斜体字形,浏览器通常会用 oblique 方式模拟,所以中文环境里这两个值的效果差别不大。
font-variant 里有一个比较常用的值是 small-caps,它会把小写英文字母显示成大写,但尺寸缩小。这个效果在英文排版中能营造出一种正式的复古感,适合用在文章开头、期刊标题这类场景。中文没有大小写概念,所以这个属性对中文内容基本无效,不过如果文章里有英文缩写、品牌名,它还是能派上用场。
2.5 字体简写属性 font:一行搞定所有,但别踩继承的坑
font 是字体相关属性的合并写法,一条规则可以同时设置 font-style、font-variant、font-weight、font-size、line-height 和 font-family。它的标准格式是:
css复制.element {
font: italic 700 20px/1.5 "PingFang SC", sans-serif;
}
这里斜杠前面是字号,后面是行高,顺序不能乱。使用简写属性最大的好处是代码干净,但有个很隐蔽的坑:font 简写会重置所有未显式声明的字体子属性为初始值。比如你原本在某处定义了 font-weight: 300,然后写了一个 font: 16px "Microsoft YaHei",那 font-weight 会被重置为 normal,而不是保留之前的 300。
所以我的建议是:如果一处的字体设置比较完整,就用简写;如果只想改其中某一个值,一定要单独声明,不要用简写去覆盖。这个坑我真实踩过,当时一个按钮的字重总是对不上设计稿,查了半天才发现是简写属性把字重重置了。
3. 文本属性详解:让文字有呼吸感和层次感
3.1 color:颜色选型不只是取色器的事
文本颜色是最直观的视觉元素。CSS 中设置颜色可以用十六进制(#333)、RGB(rgb(51, 51, 51))、RGBA(rgba(0, 0, 0, 0.5))、HSL(hsl(0, 0%, 20%))和 HSLA 等格式。
我在项目里更倾向于 HSL 和 HSLA,因为它更符合人的直觉。HSL 的三个参数分别是色相(Hue)、饱和度(Saturation)、亮度(Lightness),调颜色时可以直观地感知“这个颜色偏红还是偏蓝”“太亮了还是太暗了”。比如要把一个按钮的悬停颜色做深一点,直接降低 light 值就行,不需要像十六进制那样靠肉眼看。
实际开发中,文本颜色最好不要在页面里散落着几十个不同的色值。推荐的做法是在 CSS 变量中定义颜色体系:
css复制:root {
--text-primary: #1a1a1a;
--text-secondary: #666666;
--text-tertiary: #999999;
--text-link: #2a6bb1;
}
这样做的好处是,当产品经理某天说“正文颜色稍微淡一点”,你只需要改一个变量,全站引用这个变量的文本都会同步更新。我自己维护的组件库里,文本色阶基本控制在三到四个层级,主文本、次级文本、辅助说明、禁用状态,层次清晰。
3.2 text-align 与 text-indent:对齐方式的微妙差异
text-align 控制文本在容器内的水平对齐方式,取值有 left、right、center、justify。中文排版默认左对齐,英文排版也是左对齐最利于阅读。justify 两端对齐会让文字在每一行都填满整行宽度,英文排版里效果不错,但中文使用时要小心:由于中文字形宽度接近等宽,justify 在部分浏览器上可能会产生一些不自然的字间距拉伸,尤其是标点符号的处理不够理想时,段落看起来会有点“花”。
text-indent 用于首行缩进,常见于中文文章、古籍排版等场景。比较规范的写法是使用 em 单位,让它跟随字号变化:
css复制.article p {
text-indent: 2em; /* 首行缩进两个字宽 */
}
2em 刚好是中文里“缩进两个汉字”的标准做法。用 px 写虽然也行,但字号变化后缩进比例就歪了。另外要留意 text-indent 在某些元素上可能不生效,比如浮动元素内部的文本块,排查的时候可以从这里入手。
3.3 text-decoration 与 text-transform:装饰线、大小写转换与文字渐变
text-decoration 是下划线、删除线、上划线的控制属性。常用写法有三种:text-decoration-line 决定线的类型,text-decoration-color 决定线的颜色,text-decoration-style 决定线的样式(实线、虚线、波浪线)。实际项目里最常见的应用是给链接加下划线:
css复制a {
text-decoration: none;
color: var(--text-link);
}
a:hover {
text-decoration: underline;
text-underline-offset: 4px; /* 让下划线和文字之间有点间距,更透气 */
}
text-underline-offset 是近年才普及的属性,用于控制下划线和文字基线的距离,设置后下划线不再是紧贴文字底部的状态,视觉效果会精致很多。
text-transform 用于英文的大小写转换,取值有 uppercase、lowercase、capitalize。中文内容没有大小写概念,所以这个属性往往只对英文、品牌名、按钮文案有效。比如按钮文案想统一大写风格:
css复制.btn {
text-transform: uppercase;
}
这里有个容易被忽略的点:text-transform 只改变显示样式,并不改变 DOM 里的实际文本内容。如果用户复制这段文字,复制出来的是原始内容而不是大写后的内容。对视觉设计来说没问题,但如果涉及搜索、表单提交这类逻辑,就要谨慎使用了。
补充一个很多人问的花式技巧——文字渐变。纯文本默认只能设置一种颜色,但用 background-clip 配合 background-image 可以实现渐变文字:
css复制.gradient-text {
background: linear-gradient(90deg, #667eea, #764ba2);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
这个效果适合用在标题、海报文案上,用在正文里会严重影响可读性。color: transparent 是关键,否则底下的渐变背景会被文字默认颜色遮住。
3.4 line-height 与 letter-spacing:行距和字距是排版的高级技巧
line-height 控制行与行之间的距离,是中文排版里最值得雕琢的属性之一。行高太小时文字拥挤,太大时段落松散,都会直接影响阅读体验。
关于行高的单位选择,我有明确的建议:正文和通用文本用无单位数值,比如 1.5、1.75。无单位数值的行高是基于当前元素 font-size 计算的,子元素继承时按比例缩放,不会出现固定值导致的行距错位问题。如果用 px 写行高,某个子元素字号变大后行高可能就不够用了。
css复制body {
font-size: 16px;
line-height: 1.75; /* 等价于 28px,但子字号变化时会同步缩放 */
}
letter-spacing 控制字符之间的间距,也叫字距。中文正文通常不需要额外设置字距,默认字形间距就是字体设计师调过的。但在标题、按钮、导航这类字少且需要“高级感”的场景里,适当增加字距能让文字更舒展。比如导航菜单:
css复制.nav-link {
letter-spacing: 0.08em;
}
word-spacing 控制英文单词之间的间距,中文内容里基本用不到,因为中文没有空格分隔的词边界概念。
3.5 white-space、word-break 与 text-overflow:换行规则与省略号截断
white-space 控制文本的空白和换行方式。默认值 normal 会合并连续空格,并在容器边界自动换行。nowrap 禁止换行,pre 保留空格和换行,pre-wrap 在 pre 的基础上允许自动换行,pre-line 合并空白但保留换行。实际开发中,nowrap 常和省略号截断组合使用。
文本超出容器后的省略号是前端高频需求,完整的“单行省略”三件套如下:
css复制.ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
这里 white-space: nowrap 禁止换行,overflow: hidden 隐藏超出内容,text-overflow: ellipsis 在容器末尾显示省略号。三个属性缺一不可。多行省略则通常使用 -webkit-line-clamp 配合 display 实现:
css复制.ellipsis-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
word-break 控制换行时是否在单词中间断行。中文文本天然没有空格断点,浏览器默认就能在任意字符处换行;英文文本则需要按单词断行,默认不会在单词中间切断。如果引入了一长串 URL 或者连续数字,可能出现“溢出容器”的情况,这时可以设置 word-break: break-all 或 overflow-wrap: break-word。
4. 实战演练:手把手打造一个高颜值文章摘要卡片
4.1 需求分析与设计思路
理论说多了容易飘,我们直接做一个真实场景。假设产品设计稿里有一个“精选文章推荐”卡片模块,包含分类标签、文章标题、摘要、作者信息和发布时间,整体要传递一种干净、专业、有阅读欲的感觉。
字体与文本相关的设计要点是:标题要有存在感但不能笨重,正文要舒适耐读,元信息要弱化但不缺失。我用到的关键属性会覆盖前面讲到的 font-family、font-size、font-weight、line-height、text-decoration、text-overflow 等。
4.2 HTML 结构层
先搭建一段语义化良好的 HTML 结构:
html复制<article class="post-card">
<span class="post-tag">前端开发</span>
<h2 class="post-title">CSS 字体与文本属性实战指南:从入门到进阶</h2>
<p class="post-summary">
本文从字体族的选择、字号单位的取舍、行高字距的调整以及文本装饰的运用等多个角度,系统梳理了前端开发中提升网页排版的实用技巧与常见踩坑记录。
</p>
<div class="post-meta">
<span class="post-author">作者:林深</span>
<span class="post-date">2025-03-18</span>
<a href="#" class="post-link">阅读全文</a>
</div>
</article>
4.3 CSS 样式层与逐段解析
接下来把所有字体和文本属性融合进卡片样式中。先设置字体栈和全局基础样式,然后处理标题、正文、标签和元信息,我在关键属性旁边加了注释说明用途。
css复制:root {
/* 建立颜色变量,保证全站文本颜色一致 */
--text-primary: #1f2328;
--text-secondary: #57606a;
--text-tertiary: #8c959f;
--accent: #0969da;
}
body {
/* 中英文字体分工明确,先英文后中文 */
font-family: "Inter", "PingFang SC", "Microsoft YaHei", sans-serif;
font-size: 16px;
line-height: 1.6; /* 无单位行高,子元素按比例缩放 */
color: var(--text-primary);
}
.post-card {
max-width: 720px;
padding: 24px 28px;
border: 1px solid #e5e7eb;
border-radius: 12px;
background: #ffffff;
}
卡片外框处理完后,核心是内部文本的层次。标签用较小字号和强调色,标题用较大的字号与更重的字重,摘要用次级颜色并控制截断效果。
css复制.post-tag {
display: inline-block;
font-size: 0.8125rem; /* 13px */
font-weight: 500;
letter-spacing: 0.04em; /* 标签文字舒展开,更显品质感 */
color: var(--accent);
text-transform: uppercase;
}
.post-title {
margin: 12px 0 8px;
font-size: 1.5rem; /* 24px */
font-weight: 700;
line-height: 1.35; /* 标题行高可以适当收紧,避免松散 */
color: var(--text-primary);
/* 单行超长时打点截断 */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.post-summary {
margin: 0 0 16px;
font-size: 0.9375rem; /* 15px */
font-weight: 400;
line-height: 1.75; /* 正文行高稍大,阅读更轻松 */
color: var(--text-secondary);
display: -webkit-box;
-webkit-line-clamp: 2; /* 最多展示两行 */
-webkit-box-orient: vertical;
overflow: hidden;
}
.post-meta {
display: flex;
align-items: center;
gap: 12px;
font-size: 0.8125rem;
color: var(--text-tertiary);
}
.post-link {
margin-left: auto;
color: var(--accent);
text-decoration: none;
font-weight: 500;
}
.post-link:hover {
text-decoration: underline;
text-underline-offset: 4px; /* 下划线与文字拉开距离,更精致 */
}
4.4 最终效果与视觉检查要点
这套样式跑起来之后,卡片从上到下是这样的节奏:小字号标签作为“前缀”,引导视线进入标题;1.5rem 字号的标题有足够存在感,单行截断保证卡片高度稳定;摘要用两行截断控制卡片高度,同时通过 line-height 1.75 保持阅读舒适度;最底部的元信息用最小字号和最浅颜色退到背景层,把视觉重心留给标题和摘要。
做视觉检查时可以关注以下几点:标题和正文字号层级是否拉开,字重对比是否明显,行高在不同内容长度下是否均匀,标签与正文的间距是否足够透气。我自己在交付前有一个习惯,把卡片截图调成灰度模式看一遍,能直观地检查文本层级是否只靠颜色区分,这在后续换主题时会非常有用。
5. 常见问题排查与独家避坑技巧
5.1 字体设置无效或者没有按预期显示
字体属性不生效是新手最常遇到的困惑。我总结过一条排查路径:先确认该字体是否真的存在于当前系统中,再检查 font-family 的拼写是否准确、是否加了必要的引号,然后看有没有其他优先级更高的 CSS 规则覆盖了当前选择器。尤其是框架类项目里,UI 库自带的基础样式优先级可能会盖过你的手写规则,排查时可以直接在开发者工具里查看计算后的 font-family 到底是谁生效。
如果字体栈写的是 “PingFang SC” 而你在 Windows 上测试,这个字体根本不存在,浏览器会跳到下一个备选字体。这是正常预期,不代表代码有误。在跨团队协作里,我会在文档里专门说明字体栈是针对多平台的,让设计同学不要用 macOS 的渲染效果去验收 Windows 上的表现。
5.2 中文字体栈推荐与常见理解误区
接前面提到的例子,我再给两个可直接抄的中文字体栈。偏现代简约风的产品页面,可以这样写:
css复制font-family: -apple-system, "Segoe UI", Roboto, "PingFang SC",
"Microsoft YaHei", "Noto Sans SC", sans-serif;
这个栈覆盖了多数设备的系统 UI 字体,优点是打开速度快、和操作系统原生界面统一,缺点是视觉上没有很明显的品牌个性。偏品牌化和阅读感的内容型页面,可以引入 We 端 Web 字体:
css复制@font-face {
font-family: "BrandFont";
src: url("./fonts/BrandFont.woff2") format("woff2");
font-display: swap;
}
body {
font-family: "BrandFont", "PingFang SC", "Microsoft YaHei", sans-serif;
}
引入自定义字体时有两个提醒。第一,中文字体文件体积很大,一般只截取用到的字重,没必要全量加载,很多平台上都有字体子集化工具可以按需裁剪。第二,如果正文也用了自定义字体,页面首次加载时会有字体闪烁的现象,表现为先显示系统字体、字体文件加载完成后再替换成自定义字体,可以考虑给 font-display 设置 swap 或者 fallback 来降低这个影响。
5.3 font-weight 与跨平台渲染差异
我前面提过伪粗体的问题,这里展开说一句。中文字体在 Windows 上特别容易出现字体明明设置了 font-weight: 600,看起来却和 700 没有区别的情况。原因在于很多中文字体本身只提供了 400 和 700 两个字重,浏览器遇到 600 时没有对应的字形文件,就直接用 700 渲染或者做伪粗处理。
处理方法是,先用浏览器开发者工具查看字体实际加载的字重文件,再决定样式中使用哪个数值。如果字体文件只有 Regular 和 Bold 两种,那你的字重层级最多设置两档,强行加第三档反而吃力不讨好。如果希望字重变化细腻,可以考虑可变字体方案,但这需要设计阶段就确定字体文件支持,前端没法单方面解决。
5.4 排版细节调试清单:我每次上线前都会过一遍
最后分享一份我自己的排版自检清单,每次页面开发完成,我都会照着过一遍:
- 标题与正文的字号比例是否形成清晰的视觉层级,标题至少要比正文大 1.25 倍以上。
- 正文字体是否有合适的 line-height,我通常会用 1.6 到 1.8 区间做微调,低于 1.5 的正文行高在长段文字里会很闷。
- 文本颜色是否在不同层级之间有足够的对比度,正文和背景的对比度尤其要达标,保证可读性。
- 长文本模块里的标题是否设置了 text-wrap: balance 或类似的平衡换行属性,避免标题最后一行只有一个字。
- 所有链接的下划线是否在 hover 状态有反馈,text-underline-offset 是否设置,避免下划线和字母笔画粘连。
- 字号是否全部使用相对单位,尤其是针对响应式布局的页面,必须保证根字号变化后,正文和间距能同步缩放。
- 是否有任何文本在容器溢出后没有截断处理,比如用户昵称、文章标签这类动态内容,都要预设最坏情况。
- 检查原生控件和表单输入框的字体是否继承全局字体栈,输入框默认字体往往不是浏览器默认字体,需要显式声明。
这些细节单独拿出来都不起眼,但合在一起就是页面高级感和“土味”之间的分水岭。我自己每次改完样式都会肉眼扫一遍这些点,比测试用例还管用。
字体和文本属性的内容量很大,这一篇覆盖的是最常用、最能直接提升页面质感的部分。下一篇我计划继续讲颜色、背景、边框、阴影这些视觉增强类属性,到时候再结合具体案例把元素整体包装成更完整的“颜值组件”。如果你在开发中也碰到过字体不生效、行高控制不住、省略号不显示这类问题,照着上面的排查路径走一遍基本都能解决。排版这件事,说到底就是把每个细节都认真校正过的过程。
