做前端这些年我几乎逢人就问一个基础问题你能默写完整写出font简写属性的顺序吗能答对的人大概一半。这个现象很说明问题——CSS 字体样式在外行人眼里是“入门级”知识点但在实际项目里从字体回退、单位换算到真假斜体坑多到能写一本小册子。这篇内容我就围绕“字体类型、大小、粗细、样式”这四个方向展开把埋在我项目里的经验和排查过的怪问题一并讲清楚。不管你是刚学 HTML 和 CSS 的新手还是想补基础细节的前端从业者都能在这篇里找到能直接抄作业的部分。1. 字体样式到底在管哪几件事先把四个属性分工搞清楚先说个总纲。标题里提到的“字体类型 / 大小 / 粗细 / 样式”对应到 CSS 就是四个最核心的属性中文叫法CSS 属性典型取值控制什么字体类型font-familyPingFang SC, Microsoft YaHei, sans-serif选哪套字体来显示文字大小font-size16px、1rem、1.2em文字显示得有多大粗细font-weightnormal、bold、400、700文字的笔画粗细样式font-stylenormal、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-facefont-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 relstylesheet hrefstyle.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 classfont-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加载可变字体实现更细腻的粗细切换都是很实用的进阶路线。