做前端这些年,我几乎逢人就问一个基础问题:你能默写完整写出font简写属性的顺序吗?能答对的人大概一半。这个现象很说明问题——CSS 字体样式在外行人眼里是“入门级”知识点,但在实际项目里,从字体回退、单位换算到真假斜体,坑多到能写一本小册子。这篇内容我就围绕“字体类型、大小、粗细、样式”这四个方向展开,把埋在我项目里的经验和排查过的怪问题一并讲清楚。不管你是刚学 HTML 和 CSS 的新手,还是想补基础细节的前端从业者,都能在这篇里找到能直接抄作业的部分。
1. 字体样式到底在管哪几件事:先把四个属性分工搞清楚
先说个总纲。标题里提到的“字体类型 / 大小 / 粗细 / 样式”,对应到 CSS 就是四个最核心的属性:
| 中文叫法 | CSS 属性 | 典型取值 | 控制什么 |
|---|---|---|---|
| 字体类型 | font-family | "PingFang SC", "Microsoft YaHei", sans-serif | 选哪套字体来显示文字 |
| 大小 | font-size | 16px、1rem、1.2em | 文字显示得有多大 |
| 粗细 | font-weight | normal、bold、400、700 | 文字的笔画粗细 |
| 样式 | font-style | normal、italic、oblique | 是否倾斜、正常 |
这四个属性可以单独写,也可以合并到font简写里一口气搞定。简写是很多新手最先踩坑的地方,因为它的属性顺序是固定的:
/* 顺序:font-style font-weight font-size/line-height font-family */ font: italic 700 16px/1.5 "PingFang SC", sans-serif;这里我得提醒一句:font简写里,font-size和font-family是必填项,少了任何一个,整条声明可能直接失效。而font-style、font-weight是可选项,可以省略。实操中我看到不少人写font: 16px "PingFang SC"时漏了font-family的引号,看起来没事,但如果字体名是“Microsoft YaHei”这种带空格的,不写引号就会失效。
另外要注意,font简写会把line-height也一起吃进来,font: 16px "Microsoft YaHei"这行代码实际会把line-height重置为默认值normal。如果你的页面里正好在其他地方设了line-height,写了font简写后又发现行距不对,多半就是被它“顺手”重置了。
初学的时候我习惯把四个属性分开写,可读性高,后续排查也方便。等基本用法熟练了,再用简写也不迟。但不管怎么写,先把这四个属性的底层逻辑摸透,比记住简写重要得多。
2. font-family:字体类型不是随便选的,字体栈才是关键
2.1 字体栈为什么一定要写一串
font-family看起来就是填一个字体名,但真正的做法是给出一串候选列表,这叫“字体栈”。比如:
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif; }这一串的意思是:浏览器从左到右逐个检查,用户系统里装了哪个就用哪个。用户电脑没有PingFang SC,就看看有没有"Microsoft YaHei",都没有就落到最后的通用族sans-serif。
有些人图省事,只写一个中文字体名,比如只写"Microsoft YaHei"。结果用户是 Mac,没装这个字体,浏览器就按自己的默认字体渲染,页面观感完全失控。字体栈的本质是一种降级策略,保证在任何设备上,文字至少落在“可用且风格相近”的字体上。
2.2 引号和大小写细节
字体名带空格,或者本身就是中文名的时候,必须加引号。比如font-family: "Microsoft YaHei"。如果你用的是PingFang SC,也要引号,因为中间有空格。而像arial、sans-serif这种单字、系统认识的,不用引号更稳。
大小写方面,字体名一般不区分大小写,Arial和arial都生效。真正容易出错的是中英文混排的场景。我的习惯是:西文字体放前面,中文字体放后面,因为中文字体会连带影响西文字符的渲染。顺序反过来的话,中文可能没问题,但英文和数字的视觉效果往往不如专业的西文字体。
2.3 加载 Web 字体的两个实操点
如果只用系统字体,样式可控性有限。项目里要上品牌字体,就得用@font-face:
@font-face { font-family: "MyBrandFont"; src: url("./fonts/my-brand.woff2") format("woff2"); font-display: swap; font-weight: 400; }这里最容易犯的错是忽略font-display: swap。没有它,字体加载期间页面默认会隐藏文字,造成 FOUT(闪烁)现象;加了swap后,网页会先用备选字体渲染,等自定义字体加载完再换,观感顺很多。
第二个点是@font-face会区分字重。你只加载了font-weight: 400的字体文件,页面上却写了font-weight: 700,大多数浏览器不会再去给你加载 700 的文件,而是会把 400 的字体做“算法加粗”。看起来笔画变粗了,但字形细节并不对,尤其是在大字号标题下很容易露馅。所以项目里用自定义字体时,400、500、600、700 这些常用字重要一起准备好,分开写@font-face。
3. font-size:像素、em、rem,单位选择的门道
3.1 px 最直观,但别用它管全站
font-size最简单的写法是像素:
html { font-size: 16px; } h1 { font-size: 32px; }px是绝对单位,写多少就是多少,不随父级变化,调试起来非常直观。但问题也在这里:用户如果在浏览器设置了“更大字号”的辅助功能,写死的 px 无法自动缩放,对可访问性不友好。
更常见的问题是:px 写多了以后,很难做整站比例调整。你想把所有字体放大 10%,就得逐个改 px 值,工作量不说,漏改的风险很高。
3.2 em 和 rem:为什么排查这类问题特别烧脑
热词里“css em”搜得挺多,我猜不少人是在学习em和rem时糊涂了。简单区分:
rem相对于根元素html的字体大小。html默认 16px,那1rem = 16px。em相对于当前元素自身继承来的字体大小。如果一个元素继承到了 16px,它的1em = 16px;如果它自己被设成了font-size: 24px,那这个元素内部的1em就等于 24px。
em的经典用法是做按钮内边距:
.btn { font-size: 16px; padding: 0.5em 1em; }这样当按钮字号变大时,内边距会自动跟随,很省心。但em的坑在嵌套。父子元素如果都用em设置字体大小,字会越套越大,最后连你自己都算不清当前字体到底是几 px。
rem就没有这个烦恼,它始终锚定根元素。所以我现在的常态是:
- 结构的字号、行高、整体比例用
rem; - 某个独立组件内部想等比缩放时用
em; - 需要精确到像素的设计稿还原,才直接用
px。
3.3 标题字号别拍脑袋:按层级定比例
给标题定字号,很多新手是“凭感觉调大一点”。做久了你会发现,字号乱定是页面显得“业余”的头号原因。常规做法是定一套比例尺:
h1 { font-size: 2.5rem; } /* 40px */ h2 { font-size: 2rem; } /* 32px */ h3 { font-size: 1.5rem; } /* 24px */ p { font-size: 1rem; } /* 16px */如果html根字号保持默认 16px,上面这套就是很常见的 4:3:2 比例。你也可以用更激进的展示型比例,但关键是保持级差一致,而不是每个标题各调各的。
这里尤其注意正文的默认大小。现在很多浏览器默认字号是 16px,如果你把body写成font-size: 14px,整个页面看起来会更紧凑,但阅读舒适度会明显下降。我的建议是中文阅读环境正文至少 15px,稳妥点直接 16px,除非页面是大量数字、英文的工具型界面。
4. font-weight 与 font-style:粗细和斜体背后的字体加载逻辑
4.1 font-weight 用数字比用关键词更可控
font-weight有两个关键词normal和bold,分别对应 400 和 700。但实际生产环境里,我基本只用数字:
strong { font-weight: 600; /* 或者 700,看字体支持情况 */ } p { font-weight: 400; }像思源黑体、苹方这类现代字体,通常提供 300、400、500、600、700 等多档字重。你用font-weight: 500去匹配字体的中等字重,比单纯用normal和bold两级要精细得多。这里的关键点是:字体文件里得有对应的字重。数字写了,字体没有这个档位,浏览器要么找最近的一档,要么自行加粗模拟,效果不可控。
如果你想改变字体粗细的同时让整站有统一观感,我的落地方式是配合 CSS 变量:
:root { --font-weight-regular: 400; --font-weight-medium: 500; --font-weight-bold: 700; }后续在组件里都用变量引用,哪天设计稿改了粗细标准,只改变量就好,不用满项目替换。
4.2 斜体是“真的斜”,还是浏览器“硬掰的”
font-style有三个值:normal、italic、oblique。很多教程只会告诉你 italic 是斜体,但真实情况是:
italic:如果字体文件里有专门的“斜体字”,就用那个。斜体字的字形是重新设计的,不只是旋转,笔画细节更符合手写倾斜感。oblique:没有斜体字时,浏览器把正常字形做倾斜变形。看起来也斜,但细节会有点“硬”。- 中文字体绝大多数没有 italic 斜体文件,所以中文网页里用了
font-style: italic,实际是浏览器硬倾斜,视觉效果在长段落里往往不好看。
所以在中文内容里,我做斜体一般是两个思路:要么是真的需要强调语气,用italic接受浏览器的倾斜处理;要么直接改font-weight加重,观感更稳。不建议在中文正文里大量用斜体,排版上容易显得乱。
4.3 鼠标移入时改字体样式的正确姿势
热词里有“css 鼠标移入事件”,其实就是:hover。修改字体样式时,最典型的做法是:
.menu-item { font-weight: 400; transition: font-weight 0.2s ease; } .menu-item:hover { font-weight: 700; cursor: pointer; }这个写法有个视觉问题:字重从 400 跳到 700,元素宽度会变,旁边的文字会被挤动。我一般会预留一点宽度,或者用font-weight变化的同时保持布局稳定的技巧。更实用的是改颜色或加下划线:
.menu-item { border-bottom: 2px solid transparent; transition: border-color 0.2s ease, color 0.2s ease; } .menu-item:hover { color: #1677ff; border-bottom-color: #1677ff; }如果你的确非做字重变化不可,可以用text-shadow模拟“占位加粗”。这个思路是给文字预加一层透明阴影,hover 时让阴影变实:
.menu-item { text-shadow: 0 0 0 transparent; } .menu-item:hover { text-shadow: 0 0 0 #000; /* 颜色和文字相同,相当于加粗不占位 */ }这招在文字居中的导航里特别好用,不会因为 hover 改变宽度导致整体跳动。
5. 删除线、渐变文字这类“非典型字体样式”怎么做
5.1 删除线和 font-style 是两回事
标题里说的“样式”如果只理解成font-style就太窄了。日常开发里,文字上的删除线需求非常多,比如原价促销、任务完成态。它的正确实现是:
.del { text-decoration: line-through; text-decoration-color: #999; }text-decoration是独立于font-style的属性,很多人误以为font-style: oblique是删除线,完全不是。删除线在 CSS 里归“文本装饰”管。
进阶一点,你还可以控制删除线的样式:
.del { text-decoration: line-through wavy #ff4d4f; /* 波浪删除线,红色 */ }这个写法同时设置了线型、花样和颜色,在商品场景里挺出效果。需要提醒的是,text-decoration的细节各浏览器支持情况有差异,wavy 在部分旧浏览器里会不生效,发布前记得在目标设备上看一眼。
5.2 字体颜色渐变:background-clip 的妙用
热词里“css 字体渐变”搜得很多,这属于用渐变色填充文字。标准的做法是:
.gradient-text { background: linear-gradient(90deg, #ff7a18, #af002d); -webkit-background-clip: text; background-clip: text; color: transparent; }原理其实不难:先给元素设置渐变色背景,再通过background-clip: text让背景只在文字笔画内显示,最后把文字本身颜色设为透明,这样看到的文字就是渐变色的。
这里有三个容易翻车的细节:
color: transparent最好保留在最后,如果你后面又写了个color属性覆盖掉,文字会直接盖住渐变背景,效果消失。- 部分浏览器仍然只认
-webkit-background-clip: text,所以带前缀的写法别删。 - 渐变文字不支持
background尺寸变化,复杂的动画渐变背景需要额外处理,普通静态渐变没问题。
5.3 图片和文字一行、文本位置调整
热词里“图片和文字一行css”和“怎么调整css容器里的文本位置”都是高频搜索。这其实是布局与文本对齐的问题,不是字体属性本身,但和字体呈现密切相关。我在项目里最常用的方案是:
.inline-item { display: flex; align-items: center; gap: 8px; } .inline-item img { width: 20px; height: 20px; } .inline-item span { line-height: 1; }display: flex配合align-items: center基本解决“图片和文字不在一条中轴线”的问题。至于文本在容器里的位置,常规是:
- 左右居中:
text-align: center - 垂直居中:靠
line-height或者 flex 的align-items: center - 想从容器左上角开始:直接把父容器设
position: relative,文字用正常流排版,不需要额外定位
如果遇到“文字和图片怎么都对不齐”的情况,绝大多数原因在于图片自身带着line-height产生的底部间隙,给图片加display: block再配合 flex 就能消除。
5.4 文字外圈的涟漪光圈扩散效果
“css涟漪光圈扩散”是最近的热词,很多场景是把按钮或文字做成“光圈扩散”的动效。它其实不直接改字体,而是靠伪元素在文字所在容器外扩散:
.ripple { position: relative; } .ripple::after { content: ""; position: absolute; left: 50%; top: 50%; width: 100%; height: 100%; border: 2px solid rgba(22, 119, 255, 0.5); border-radius: 50%; transform: translate(-50%, -50%) scale(0.8); opacity: 1; animation: ripple 1.6s ease-out infinite; } @keyframes ripple { to { transform: translate(-50%, -50%) scale(1.4); opacity: 0; } }这个做法在按钮上很出效果。需要注意的是,border-radius: 50%只对宽高接近的容器才有“正圆”效果,如果文字容器本身是长方形的按钮,纹理会变成椭圆光圈,视觉上需要单独调比例。另外光圈会溢出父容器,父容器别忘加overflow: hidden,不然页面会横向滚动条。
6. 样式不生效,一大半卡在引入方式上
6.1 CSS 样式引入的三种方式
很多新人发现“我写的 font-size 怎么没用”,问题往往不在属性本身,而在引入方式。CSS 引入主流有三种:
- 外部样式表:
<link rel="stylesheet" href="style.css">,项目标准做法。 - 内部样式表:
<style> ... </style>写在 HTML 的<head>里,适合单页临时调试。 - 内联样式:写在标签的
style属性里,优先级最高,但不建议日常使用。
外部样式表的写法要放在<head>里,并且注意放在其他可能有冲突的样式之前还是之后,这直接影响层叠结果。
6.2 CSS 文件需要写<style>吗
热词里“css文件需要写<style>吗”是个典型的新手疑问。答案是:独立的 .css 文件里不需要写<style>标签,直接写 CSS 规则就行。
/* style.css 里面这样写 */ body { font-family: "PingFang SC", "Microsoft YaHei", sans-serif; font-size: 16px; }<style>标签只在 HTML 页面内部嵌样式时使用。如果你在 .css 文件里写了<style>,浏览器会把整个标签文本当成无效规则忽略掉,后面所有样式全部失效。我见过不止一次,整个页面裸奔成默认字体,原因就是文件里多了这一对标签。
6.3 原子性 CSS 对字体样式的启示
热词里“原子性 css”指的是原子 CSS 思路,也就是把样式拆成极小的工具类,比如:
.font-bold { font-weight: 700; } .text-lg { font-size: 1.25rem; } .font-sans { font-family: sans-serif; }然后在 HTML 里直接组合:
<p class="font-sans text-lg font-bold">加粗大字</p>这种做法的好处是复用性强、样式不易重复定义。它的代价是 HTML 里类名会变长,而且如果团队约定不严,后期会堆积出大量无用的工具类。我的建议是:别在正经项目里从头手写一套原子类,但可以借鉴它的思路,把最常用的字体工具类抽出来,比如.text-ellipsis、.font-mono这类,配合组件开发效率很高。
6.4 优先级是从高到低还是从低到高
样式不生效还有个隐蔽原因——优先级被无意覆盖。CSS 里!important的权重最高,其次是内联样式,然后是选择器。选择器按 id 数量、类名数量、标签数量计算,记不住就记住一条经验:能不用选择器嵌套就别嵌套,越复杂的选择器越容易出优先级事故。
字体样式被覆盖的典型场景是:在.card p里设了font-size: 14px,又在.card里设font-size: 16px,结果.card p更具体,样式生效。想调整时只改.card没用,必须同样改.card p,很多人就卡在这。
一个更稳的写法是用 CSS 变量做主题值:
.card { --card-font-size: 14px; } .card p { font-size: var(--card-font-size); }以后只在--card-font-size一个位置改,其他不用动。
写在实际操作之后
做 CSS 字体样式这一块,我最大的体会是:属性本身不难背,难的是理解浏览器怎么加载字体、怎么渲染字重、怎么处理单位,以及样式之间的覆盖关系。很多看着奇怪的问题,追到最后都是字体文件没加载全、单位换算错了一环、或者某条优先级更高的规则把样式吞了这类基础原因。所以排查时不要急着改代码,先顺着字体栈、单位、引入方式、优先级这条链路过一遍,往往能省下大量时间。
如果你正在写自己的第一个正经页面,建议找一个带完整字体栈和字重体系的模板认真读一遍——我自己就是从模仿优秀项目的字体配置开始,才真正看懂font-family背后那串长长的列表到底在防什么问题。这个内容后续你还可以继续往两个方向加码:一是做一套整套的字体设计规范,二是引入@font-face加载可变字体实现更细腻的粗细切换,都是很实用的进阶路线。