许多开发者习惯直接在 中写入 body { font-family: ...; font-size: ...; } 来定义页面字体,这确实是基础做法,也能覆盖大部分文本。但其中有一个常见误区——必须添加通用字体族作为后备。否则,在 macOS 或 Linux 系统下,中文往往会回退到系统默认的宋体,视觉效果难以令人满意。
此外,button、input 等表单元素拥有比想象中更顽固的默认样式,例如 font-family: -webkit-system-font。你在 body 上设置的字体属性,可能无法穿透这些元素的 UA 样式。因此,更可靠的做法是:一次性显式声明 body, button, input, select, textarea, th, td 等关键元素,确保字体统一。
另外,有两个细节容易被忽略:中文字体名称必须用引号包裹,例如 "Microsoft YaHei",否则包含空格时解析会出错;同时,字体栈末尾必须添加通用族名(如 sans-serif),否则 iOS Safari 可能因找不到字体而回退到 Times New Roman。
为什么仅设置 body { font-family: "Microsoft YaHei"; } 无效
简单的“继承”思维在这里并不适用。浏览器不会自动将 h1、button、input 等元素的字体继承自 body,除非这些元素本身没有字体定义。但问题在于,许多内置元素(如 button)的 UA 样式暗中设置了 font-family,优先级更高,直接覆盖了你的 body 设置。
- 最有效的方法是统一显式声明
body, button, input, select, textarea, th, td等关键元素 - 中文字体名称必须加引号包裹,例如
"Microsoft YaHei",否则包含空格时解析会失败 - 字体栈结尾必须带通用族名,如
sans-serif,否则 iOS Safari 大概率会渲染为 Times New Roman
如何编写跨平台一致的 font-family 字体栈
仅写 "Microsoft YaHei", "Helvetica Neue" 行不通,后果很分裂:macOS 上未安装微软雅黑时,会直接跳转到 Helvetica Neue,但中文又回退到系统默认宋体,一行字两种风格,效果极差。
- 推荐使用系统字体栈:
-apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", sans-serif - 顺序有讲究:iOS/macOS 优先
-apple-system,Windows 用"Segoe UI",国产系统放在后边兜底 - 避免混用中英专有字体(如
"Noto Sans SC", Arial),Noto Sans SC 在 Windows 默认不预装,Arial 不支持中文,容易造成灾难性后果
全局字号:px 还是 rem
如果项目无需考虑用户缩放或无障碍需求,px 是最直接、最稳定的方案。但一旦用户在系统设置中调大“显示大小”或启用“粗体文本”,px 纹丝不动,而 rem 能响应变化。这是 WCAG 1.4.4 的要求,不止是技术问题。
- 使用
rem时,必须先重置html根元素的字号,例如html { font-size: 16px; },否则各浏览器默认值不一(Chrome 为 16px,Firefox 可能为 17px) - 别在
body中写完font-size: 1rem就了事——记得同步修改button、input等元素,否则它们仍按 UA 默认字号渲染,视觉效果不统一 px虽然简单,但在 iOS「更大字体」辅助功能下完全不放大,这直接踩了无障碍的雷区
最后,还有一个容易被忽视的关键点:CSS 优先级。你写了 body { font-family: ... },但只要某个组件库的 CSS 文件在后面加载,并使用了 !important 或更具体的选择器(如 div p),你的全局设置就会失效。排查方法很简单:打开 DevTools,选中任意段落文字,查看 Styles 面板中 font-family 被划掉的声明——被划掉的就是被覆盖的。这并非高深理论,而是日常调试的实用经验。
