:root用于定义全局字体变量,如--font-zh、--font-ja等,结合[lang]选择器动态切换,确保语系字体统一可控、解耦逻辑与样式,避免硬编码和class污染。

用 :root 定义语系字体变量,别写死在 class 里
直接在 CSS 中给 .title 写 font-family: "Noto Sans SC", sans-serif 是最常见也最容易出问题的做法——它把语言逻辑和样式耦合死了,换一种语言就得改所有选择器。真正可控的方式是统一在 :root 里定义变量,比如:
:root {
--font-zh: "Noto Sans SC", "PingFang SC", system-ui, sans-serif;
--font-ja: "Noto Sans JP", "Hiragino Kaku Gothic Pro", system-ui, sans-serif;
--font-ko: "Noto Sans KR", "Malgun Gothic", system-ui, sans-serif;
--font-en: "Inter", -apple-system, sans-serif;
}
然后全局用 font-family: var(--font-zh),切换语言时只改一个变量值,结构、类名、JS 都不用动。
-
system-ui必须放在自定义字体之后、sans-serif之前,否则 macOS 和 Windows 渲染行为不一致 - 中文字体名必须加双引号,如
"Noto Sans SC",否则编译或解析会失败 - 不要用
.font-jp这类 class 控制字体——漏改一个地方,字体就错位
用 [lang] 属性触发字体变量切换,不是靠 JS 改 class
很多人用 document.body.className = 'lang-ja' 然后写 .lang-ja .title { font-family: var(--font-ja) },这会漏掉第三方组件、SSR 内容、shadow DOM 里的文本。标准且可靠的方式是依赖浏览器原生的 [lang] 继承机制:
[lang="zh"] { --font-zh: "Noto Sans SC", "PingFang SC", system-ui, sans-serif; }
[lang="ja"] { --font-zh: var(--font-ja); }
[lang="ko"] { --font-zh: var(--font-ko); }
只要 一设置,所有后代自动生效,无需 JS 干预。
- 确保
lang值符合 BCP 47 标准:用zh-CN不用zh-ch,用ja-JP不用jp - i18n 库(如 Vue I18n)初始化时必须同步设置
document.documentElement.lang,否则 CSS 选择器不匹配 - 别只写
:lang(ja),要补全:lang(ja-JP)和:lang(ja),iOS 和安卓 WebView 发的语言标签不一致
切换变量后立即读取 font-family 拿不到新值
执行 document.documentElement.style.setProperty('--font-zh', '"Noto Sans JP", sans-serif') 后,马上调用 getComputedStyle(el).fontFamily 得到的仍是旧值——CSS 变量更新是异步的,且不会触发字体回流校验。
- 必须等
requestAnimationFrame后再读取,否则样式未应用就做判断会出错 - 如果要做字体加载状态检测(比如等 Web Font 加载完再显示内容),得监听
document.fonts.load(),不能只靠变量变更 - 切换后页面不会自动重排字体,
font-display: swap要配在@font-face里,否则首屏可能闪空白
RTL 布局和字体切换要分开处理,别混在一起
阿拉伯语、希伯来语需要 dir="rtl" 触发 rtl: 类,但字体切换仍走 [lang] 或 :root 变量。两者逻辑正交,强行合并会导致维护混乱。
- 不要用
[dir="rtl"][lang="ar"]这种复合选择器写字体规则——增加复杂度,且 RTL 语言不止阿拉伯语 - Tailwind 的
rtl:变体只响应dir="rtl"属性,不是 class,所以class="rtl"对rtl:ml-4无效 - 字体栈顺序影响 fallback 行为:中文排版要把中文字体放最前,比如
["Noto Sans SC", system-ui, sans-serif],否则系统字体可能优先被选中
最易忽略的是字体变量作用域和刷新时机:变量改了,但没等 RAF 就读取;或者把变量写在局部作用域(比如某个组件的 :host),导致子元素拿不到;又或者没配 font-display: swap,Web Font 加载慢时文字长时间不可见。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











