应使用 :root 声明字体变量(如 --font-sans),再在所有文本类元素中显式应用 font-family: var(--font-sans);因原生表单控件不继承 body 字体,且 system-ui 兼容性差、解析不可控。

用 :root 声明字体变量,而不是在每个选择器里重复写 font-family
直接把字体定义塞进 :root,所有组件才能真正共享一套字体策略。否则改个主字体就得全局搜 font-family,漏掉一个就视觉分裂。
常见错误是只在 body 或某个 wrapper 里设 font-family,结果 button、input、select 这些原生控件根本不继承——它们有自己默认的字体链,尤其在 macOS 和 Windows 上表现不一致。
-
:root里定义--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; - 所有文本类元素(包括
button、input、textarea)都显式用font-family: var(--font-sans); - 避免用
inherit或省略,因为浏览器对表单控件的字体继承行为不可靠
为什么不能只靠 font-family: system-ui 一招鲜
system-ui 看似省事,但它只是 CSS 关键字,不是真实字体名,实际渲染依赖系统解析逻辑,且老浏览器(如 Chrome
更关键的是:它不解决“中文字体 fallback 链混乱”这个核心问题。中文界面必须手动拼接 "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif 这类链,否则 macOS 显示简体中文时可能 fallback 到不带汉字的西文字体。
立即学习“前端免费学习笔记(深入)”;
- Chrome 90+ 开始支持
font-family: system-ui, -apple-system, sans-serif;,但依然要补全中文字体链 - Windows 下
system-ui可能映射到 Segoe UI,但没包含中文字符集,得额外加"Microsoft YaHei" - 用
font-family: var(--font-sans)+ 多层 fallback 是唯一可控方式
字体变量怎么分层才不乱:基础簇 vs 场景变体
别把所有字体都塞进一个 --font-base。标题、代码块、按钮需要不同气质,硬套同一套会削弱信息层级。
典型分层是三组变量:--font-sans(正文/界面)、--font-serif(品牌文案/长文)、--font-mono(代码/配置项)。每组都带完整 fallback 链,且优先级按设备和语言区分。
-
--font-sans:开头放系统字体(-apple-system/BlinkMacSystemFont),再接通用中文字体,最后sans-serif -
--font-mono:必须包含"SF Mono"(macOS)、"Consolas"(Win)、"Ubuntu Mono"(Linux),顺序影响渲染质量 - 避免用
font-family: var(--font-sans), var(--font-mono)这种混搭——字体族之间没有 fallback 关系,只会取第一个有效值
字体加载失败时的视觉降级怎么兜底
CSS 变量本身不解决字体加载问题,但配合 @font-face 和 font-display: swap 才算闭环。否则变量再统一,自定义字体加载卡住时,页面先闪出丑陋的 fallback 字体,再跳变成目标字体。
重点不是“让字体快”,而是“让降级过程不刺眼”。用户感知最深的是字体宽度突变导致的布局抖动(layout shift)。
- 给
@font-face加font-display: swap,确保文本立即显示 fallback,等自定义字体就绪后平滑替换 - 用
font-weight和font-stretch的预设值匹配 fallback 字体,减小宽度差异(比如用Robotofallback 时,设font-weight: 400对应Inter的 regular) - 不要在
:root里直接@import字体 CSS——它会阻塞渲染,改用<link rel="preload">提前抓取
变量管的是“写法统一”,但字体是否真能稳稳落进用户屏幕,还得看加载策略和 fallback 容错设计。这点最容易被忽略:调完变量以为万事大吉,结果上线后 iOS 用户看到一堆方块字。


















