根本原因是操作系统字体渲染引擎对同一font-weight值的加粗实现逻辑不同:Windows DirectWrite保清晰、macOS Core Text重对比、Android常靠描边模拟导致发虚;各平台字重支持不一,@font-face声明必须严格匹配真实字重,Web Font加载回退会放大差异。

根本原因不是 CSS 写错了,而是操作系统级字体渲染引擎(Core Text / DirectWrite / FreeType)对同一 font-weight 值的“加粗”实现逻辑完全不同——你无法靠改一个数值统一所有平台。
font-weight 数值在不同系统里压根不对应同一物理字形
Windows 的 DirectWrite 和 macOS 的 Core Text 对 “700 是什么样子” 有各自的理解:前者优先保清晰、防糊,笔画加粗幅度小;后者主动增强对比度和厚度,视觉上更“实”。安卓则更极端——多数系统字体(如 Noto Sans CJK)只内置 400 一个字重,font-weight: 700 只能靠描边模拟,必然发虚、膨胀。
-
font-weight: 500在 macOS 上可能真有 Medium 字形,Windows 下直接 fallback 到400 -
font-weight: 600在 Android 12+ 部分设备才开始支持,且必须搭配含 600 字重的 Web Font 才生效 -
font-weight: bolder或lighter完全不可控,各平台计算基准不同,嵌套深了会彻底失控
@font-face 声明不匹配是常见静默失效点
浏览器不会报错,但会悄悄降级。比如你写了 font-weight: 500,但实际加载的字体文件是 medium.woff2 且 @font-face 里声明的是 font-weight: 400,那它永远当 400 用。
- 每个字重必须单独声明一个
@font-face块,font-weight值要和文件真实字重严格一致(不能写medium,必须写500) - Google Fonts 要显式加参数:
https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700,否则默认只加载400和700 - 用 DevTools 的 Computed 面板确认最终解析值,别信 Styles 面板里写的那个数字
Web Font 加载期间的回退行为放大差异
自定义字体没加载完时,浏览器先用系统字体渲染(此时 font-weight 行为由系统决定),加载完成后切回 Web Font(此时行为由字体文件决定)。这个切换在安卓低端机上可能卡顿数百毫秒,造成“先粗后细”或“先细后粗”的闪动。
立即学习“前端免费学习笔记(深入)”;
- 用
font-display: swap是必要选择,但得接受首屏短暂回退 - 避免在关键标题上依赖
font-weight: 600这类非标准档位——它在回退期和加载期都极不稳定 - 如果必须强一致性,干脆放弃系统字体,全程用 Web Font,并确保
400/500/700全档位覆盖
最常被忽略的一点:所谓“调好了”,往往只在你当前设备 + 当前浏览器 + 当前缩放比例下成立。只要换一台 Windows 笔记本、一个 Android WebView、甚至系统字体缩放设为“更大文本”,font-weight: 700 就可能突然变回 400 —— 因为背后根本没有那个字形。


















