CSS字体属性全攻略:字体栈、字号、字重与复合属性详解

发布时间:2026/10/2 20:03:00
CSS字体属性全攻略:字体栈、字号、字重与复合属性详解
CSS字体属性大概是前端入门时最早接触、却最容易稀里糊涂带过去的一块。很多人写font-family就手打一个 Arial写font-size就随便给个 14px等到做真实项目才发现字体栈顺序不对、中文乱码、font-weight: bold在部分浏览器里根本没加粗效果、复合属性一写就把别的样式全重置了。今天把字体属性整个体系拆开讲一遍字体系列、字体大小、字体粗细、文字样式、复合属性每一条都用真实项目里验证过的写法来讲适合刚入门 CSS 的前端学习者也适合基础不牢、想回来补课的同学。这篇不会绕弯子全是实际能用的东西。1. 为什么字体属性值得单独拿出来写一篇1.1 字体属性看着简单坑比想象的多字体相关属性在 CSS 里属于高频中的高频几乎每个页面都会用到。但正因为太常用了大部分人都是“能用就行”的心态从来没系统整理过。等真遇到问题的时候往往卡在最基础的地方。我梳理了几个最常见的现象设置了font-family: 微软雅黑但用户电脑里没有这个字体页面直接回退成默认宋体排版瞬间变丑。font-size用了em结果嵌套层级一深字号越变越大怎么调都不对。font-weight: bold写在样式中浏览器却显示成普通粗细乍一看像是没生效。用font简写属性同时设置多个值写完字号后间距、行高全乱了。这些问题每一个我都实际踩过。它们不是多高深的技术难点而是对字体属性背后的机制理解不够。与其靠百度查一条改一条不如把整个体系过一次。1.2 这篇内容适合谁能解决什么问题如果你是刚学 CSS 的入门者这篇可以帮你把字体相关的知识点一次串起来省去翻各种零散教程的时间如果你已经写过不少页面但基础不牢这篇能帮你补上“原理”那一层以后遇到问题能自己判断原因而不是改一个数试一次。我还会额外讲一些需要实际项目经验才能总结出来的细节比如字体栈的排列策略、常见中文 Web 字体的兼容差异、rem和em的计算技巧、复合属性最容易忽略的规则等。这些内容在官方文档里都有但很少有人告诉你哪个坑在真实项目里出现频率最高。2. 字体系列font-family从入门到会踩坑2.1 字体栈的顺序就是你的备选方案列表font-family接收一个优先列表浏览器会按照从左到右的顺序寻找可用字体如果第一个字体不存在就依次使用后面的字体直到找到系统里有的那张脸。body { font-family: PingFang SC, Microsoft YaHei, Helvetica Neue, Arial, sans-serif; }这段代码的意思很直白在苹果设备上优先用苹方Windows 上优先用微软雅黑都没有的时候退回 Helvetica Neue再不行就用 Arial终极兜底是无衬线字体族。实际写的时候我会把字体栈分成三段来看第一段是“期望字体”通常是针对目标用户系统做的最优选择。第二段是“中性替代”保证主流系统都有相似的观感。第三段是“通用兜底”也就是sans-serif、serif或monospace确保浏览器不会乱选字体。这种做法的好处是不需要给每个用户写一套独立样式浏览器自己就能完成降级流程。关键是顺序不能乱否则期望字体会被后面的字体覆盖等于白写。2.2 字体名加引号的规则很多文档没讲透中文字体、包含空格的英文字体名必须加引号比如Microsoft YaHei、PingFang SC。不加引号的大部分情况下浏览器也能识别但遇到特殊字符就会有解析错误的风险。当字体名恰好是 CSS 关键字的时候引号更是必须的例如font-family: default。我用项目里实际遇到的情况举个例子有人写了font-family: PingFang SC, Microsoft YaHei, sans-serif;不加引号。在大多数现代浏览器里能正常显示但在一些低版本内核或移动端 WebView 里空格会导致解析中断字体直接回退。为了兼容性我习惯统一加上引号养成固定写法。还需要注意一点font-family的值可以写字体族名称也支持多个字体名组合成一个带引号的整体字体族。如果你在字体栈里用到了“锦华细黑”完整写法是font-family: 方正兰亭细黑_GBK, PingFang SC, Microsoft YaHei, sans-serif;2.3 中文字体的“现实问题”与系统差异中文字体和英文字体最大的不同在于中文字体体积大、字符集全同一个系统只有有限的几种内置中文字体。Windows 一般是微软雅黑和新宋体macOS 是苹方和宋体Linux 服务器上甚至可能只有文泉驿或思源黑体。这就导致一个结果你没法保证所有用户看到的字体都一样。所以中文字体栈的优先级要结合业务受众来定。如果面向国内 PC 用户微软雅黑放在最前面没问题如果面向苹果用户苹方优先级更高如果做移动端 H5iOS 和 Android 各自主流字体不一样字体栈往往要写得更细。还有一个常被忽略的细节系统没有指定字体时浏览器对中文渲染会使用默认字体这个默认字体在不同系统、不同浏览器里表现差异很大。为了页面整体风格可控sans-serif这个兜底不能漏尤其是中文页面。3. 字体大小font-size的绝对与相对逻辑3.1 绝对单位 vs 相对单位该怎么选font-size常用的单位分两类绝对单位和相对单位。绝对单位有px、pt、in、cm、mm等浏览器适配中最常用的是px。相对单位有em、rem、%、vw、vh等它们会基于某个基准值动态计算。在普通项目里px最直观设计稿给多少就写多少。但px是固定像素值用户调整浏览器字号时不会缩放这对可访问性不太友好。相对单位则更适合需要响应式缩放、或者需要跟随用户偏好设置的场景。实际开发中我推荐一个组合思路全局基准字号用rem或px定义。组件内部局部间距、图标尺寸、字号缩放可以用em。需要随视口宽度变化的场景用vw或clamp()。这样既有绝对单位的精确又有相对单位的灵活不会陷入“全站用 em 导致嵌套倍数失控”的局面。3.2 理解 em 和 rem 的基准根元素与父元素很多初学者分不清em和rem核心区别就在“基准”。em是相对父元素字号计算的。比如父元素字号是 20px子元素font-size: 1.5em就是 30px。嵌套层级越多按乘法累计深层元素很容易变得无法预估。rem只相对根元素html计算默认为 16px。所以1rem 16px2rem 32px不受中间嵌套影响。来看一个实际例子假设 HTML 结构如下div classouter p classinner内层文字/p /divCSS 设置html { font-size: 16px; } .outer { font-size: 20px; } .inner { font-size: 1.5em; /* 相对父元素 20px实际为 30px */ }如果把.inner的font-size改成1.5rem实际字号就是1.5 * 16px 24px不会受.outer影响。我在项目里更倾向用 rem 做全局字号控制因为只需要调整根元素一行代码就能让全站比例跟着变。这个技巧在做主题换肤的时候极其好用。3.3 移动端适配中字体大小怎么定移动端做字体大小适配最原始的办法是手写媒体查询每个断点单独设置html { font-size: ... }然后全站用 rem。这种方法维护成本高而且断点之外的宽度没有覆盖字体会显得不协调。更好的方式是用vw配合函数动态计算基准字号。比如html { font-size: calc(16px 4 * ((100vw - 320px) / 680)); }这段代码的意思是视口宽度从 320px 变化到 1000px 时根字号从 16px 平滑变化到 20px。超出范围后不再继续缩放避免极端大屏上字体过大。如果你不想手动算也可以直接使用clamp()函数html { font-size: clamp(16px, 1rem 0.5vw, 22px); }这样写之后正文和其他模块只要使用 rem就能跟着根字号联动。整个页面字体比例保持一致又不会在小屏过大或大屏过小。需要注意的点是不要让根字号在大屏上无限变大否则页面会被撑得很奇怪。4. 字体粗细与文字样式4.1 font-weight 不只是 bold 和 normalfont-weight常见的关键字有normal、bold、bolder、lighter也支持数值100到900。平时写font-weight: bold和font-weight: 700在语义上等价的但数值能提供更精确的控制。关键问题是字体本身必须包含对应的字重否则浏览器会进行“伪造加粗”或“合成加粗”。如果使用系统默认字体细体、粗体还比较全如果从 Web 字体加载字体但没有引入对应的字重文件font-weight: 300可能直接没有效果或者浏览器用默认字重替代。一个典型例子font-face { font-family: MyFont; src: url(myfont-regular.woff2) format(woff2); font-weight: 400; }这时页面里写font-weight: bold浏览器找不到对应的 700 字重文件就会用 400 的字形做“合成加粗”加粗效果可能发虚、变形。正确做法是给font-face添加多个字重来源font-face { font-family: MyFont; src: url(myfont-regular.woff2) format(woff2); font-weight: 400; } font-face { font-family: MyFont; src: url(myfont-bold.woff2) format(woff2); font-weight: 700; }这样设置后浏览器才能根据页面里的font-weight数值切到对应的字体文件。如果你只是用系统字体font-weight: 300在日常页面里也未必有效因为 Windows 的微软雅黑常规和粗体之外没有更多字重层级。这种情况下你写 300、400、500 看起来可能完全一样不要以为是代码错了是字体源本身不支持。4.2 font-style 和 font-variant 的用途边界font-style的典型值是normal、italic、oblique。italic使用字体文件中的斜体字形oblique是浏览器对普通字形做倾斜变形。大多数英文字体自带 italic 字形中文字体基本没有斜体概念所以font-style: italic对中文来说浏览器通常也会做变形处理。font-variant现在更多用来控制小型大写字母之类的高级排版特性日常页面用得少。但需要知道它会在某些字体设置中影响文本显示效果如果你在样式里突然发现文本变了形状可以检查是不是有全局的font-variant设定。4.3 中文环境下字体加粗的显示差异中文字体的加粗需要注意微软雅黑和苹方等系统字体在显示加粗时浏览器生成的“合成粗体”往往只是把字形变宽实际视觉上并没有真正的粗体字形那么好。遇到要求较高的设计稿我一般不会直接font-weight: 700而是选择语义清晰的标题层级或者改用字重更清晰的字体文件。例如思源黑体有 100 到 900 多个字重档位配合 Web 字体加载后粗细变化是平滑的。如果项目要求严格按设计稿还原可以引入完整字重的中文字体文件再通过font-weight精确控制。代价是字体文件体积增大需要权衡加载速度。5. 字体复合属性 font一行代码的威力与陷阱5.1 复合属性的语法顺序font是一个简写属性可以一次性设置字体样式、字重、字号、行高和字体系列。它的语法有固定顺序font: font-style font-variant font-weight font-size/line-height font-family;前三个值可省略省略时使用默认值。font-size和font-family是不可省略的否则整个声明无效。line-height必须跟在font-size后面用斜杠分隔。一个完整例子p { font: italic 700 16px/1.8 PingFang SC, Microsoft YaHei, sans-serif; }这段代码等价于p { font-style: italic; font-variant: normal; font-weight: 700; font-size: 16px; line-height: 1.8; font-family: PingFang SC, Microsoft YaHei, sans-serif; }使用简写能让代码更精简但也容易踩坑任何被省略的属性都会重置为初始值。比如你只想设置字号和字体结果其他样式被清掉了。5.2 复合属性“重置”问题与正确用法我在项目中见过一个很典型的 bug某个按钮样式里写了font: 14px/1.5 sans-serif;结果发现按钮上的字变成了斜体。排查半天最后发现是父级有font-style: italic而font简写把font-style重置为了normal又把继承的斜体设置覆盖了。这其实不是 bug而是复合属性的机制——凡是没有写出来的项都会恢复成初始值。所以我的建议是只在明确要同时设置多个字体属性的场景用font简写。如果只需要调某一个值比如单独改字号或字重就直接写单一属性不要用简写。在重置样式的场景里可以用font: inherit让子元素完全继承父元素的字体设置这比逐个声明更省事。另外一个容易被忽略的点是line-height写在font简写里时如果省略line-height会被重置为normal。如果你先前设置了line-height: 1.6后来加了一行font: 16px sans-serif;行高就会被冲掉影响很隐蔽。6. 常见问题与排查技巧6.1 font-family 明明设置了却不生效这种情况最常见发生在字体名写错或字体不存在时。比如用户电脑里没有“方正兰亭黑”浏览器会直接跳到后面可用的字体。还有可能是字体名拼接有问题比如Microsoft YaHei写成了MicrosoftYaHei或者PingFang SC写成了PingFangSC。排查时可以用浏览器的开发者工具选中目标元素查看“计算样式”面板中的font-family浏览器会自动显示实际使用的字体。如果显示的还是默认字体就说明前面写的所有字体都不可用。此时需要检查服务器是否通过font-face提供了 Web 字体或者页面是否引入了外链字体库。如果是本地字体还要看用户操作系统是否具备该字样。6.2 font-weight 设置 700 没变化这里有个很容易忽略的点如果字体文件本身只有一个字重font-weight无论写多少浏览器都只能拿唯一字重渲染。浏览器虽然可以做“合成加粗”但有时视觉变化微乎其微。尤其是中文黑体本来就偏粗合成粗体的差异几乎不可见。另外font-weight的值bolder和lighter是相对继承值计算的在多层嵌套中可能和预期不同。如果嵌套很深建议直接用数值不要用相对关键字。排查方法用开发者工具查看元素是否真的被渲染为加粗。同时确认font-family指定的字体确实存在对应的粗体文件。如果依赖 Web 字体检查font-face是否包含font-weight: 700的声明。6.3 font 简写导致其他属性被重置很多人在项目里写了类似这样的代码.article { font-size: 18px; line-height: 2; } .article .highlight { font: 16px sans-serif; }结果是.highlight的行高被重置成了normal。原因就是font简写没写line-height浏览器把它恢复为初始状态。这个问题我们在 5.2 里已经分析过应对的关键是不混用简写和独立属性。如果项目中沿用大量旧代码一时改不过来我建议在公共样式里定好规则要么全用简写要么全用独立属性不要在同一类属性上混搭。6.4 字体相关调试技巧调试字体问题时我会在开发者工具的“Elements”面板里查当前元素的计算样式然后手动改font-family试不同字体名看哪个系统里有、哪个没有。还有一个土办法临时给元素加一个浅色背景如果文本高度明显变化说明行高或字体的度量差异在影响布局而不只是字体名的问题。遇到字体加载失败的场景可以打开控制台看是否有字体文件请求报错比如 404 或者跨域错误。这部分在移动端 H5 里尤其常见外链字体域名没加 CORS 头字体就加载不了。7. 一些实战经验与建议我在实际项目里会把字体相关设置收敛到一个公共样式中避免每个页面各写一套导致风格不一致。比如在:root里定义字体变量:root { --font-sans: PingFang SC, Microsoft YaHei, Helvetica Neue, Arial, sans-serif; --font-serif: Georgia, Times New Roman, Songti SC, serif; --font-mono: JetBrains Mono, Fira Code, Consolas, monospace; }使用时直接引用body { font-family: var(--font-sans); }这样想换字体方案时只需要改一处定义比全站手动搜索替换舒服很多。我最近在一个后台项目里就是用这种变量方式管理三套字体的。另外font-size的基准值建议在根元素设定明确给个16px起步不要依赖浏览器默认值。中文页面里 14px 正文在部分设备上会偏小建议正文至少 15px 或 16px小字号只留给辅助信息。最后一个建议越小的项目越容易忽视字体加载性能。font-face引入的字体文件尽量用woff2格式必要时做子集化只保留用到的字符集体积能减少非常多。不要为了一个标题字体外链一个几 MB 的字体文件整体体验会明显变差。调试多了你就会发现字体属性没有多么玄乎只要理解了字体栈的降级逻辑、相对单位的计算基准、复合属性的重置规则很多问题一眼就能看出来。