diff --git a/package.json b/package.json
index 624b92e..6f2a931 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,8 @@
"dependencies": {
"@astrojs/rss": "^4.0.19",
"@astrojs/sitemap": "^3.7.4",
+ "@fontsource-variable/noto-sans-sc": "^5.3.1",
+ "@fontsource-variable/noto-serif-sc": "^5.3.1",
"@fontsource/jetbrains-mono": "^5.3.0",
"@fontsource/noto-sans-sc": "^5.3.1",
"astro": "^7.3.8",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6e6dfab..7053d36 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -14,6 +14,12 @@ importers:
'@astrojs/sitemap':
specifier: ^3.7.4
version: 3.7.4
+ '@fontsource-variable/noto-sans-sc':
+ specifier: ^5.3.1
+ version: 5.3.1
+ '@fontsource-variable/noto-serif-sc':
+ specifier: ^5.3.1
+ version: 5.3.1
'@fontsource/jetbrains-mono':
specifier: ^5.3.0
version: 5.3.0
@@ -232,6 +238,12 @@ packages:
'@emnapi/wasi-threads@1.2.3':
resolution: {integrity: sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==}
+ '@fontsource-variable/noto-sans-sc@5.3.1':
+ resolution: {integrity: sha512-UB96JJ17HyD5ehEU7Em0hDdh7fvTMB4B4Hzmq5c4QaNwwSLqTrdM2S+coySeBqCkXwh0DxqXT0b0wMrmzorFpQ==}
+
+ '@fontsource-variable/noto-serif-sc@5.3.1':
+ resolution: {integrity: sha512-aIHcBaL13QctVaJFeT6wqfgO9Rq/cNtD5g2ob4ze59fYPYwacGthnIrHBe83kDpMEB/k6VGtGc6fS99RImcoSQ==}
+
'@fontsource/jetbrains-mono@5.3.0':
resolution: {integrity: sha512-fqDfB5I9f1p1TV486aUgB9t8zP84P0O1FtQR5Ol9vjwPy+S+EIGlVYm1cvj2W5shcZMTg2nZFdVMoH5wFu8a1A==}
@@ -1591,6 +1603,10 @@ snapshots:
tslib: 2.8.1
optional: true
+ '@fontsource-variable/noto-sans-sc@5.3.1': {}
+
+ '@fontsource-variable/noto-serif-sc@5.3.1': {}
+
'@fontsource/jetbrains-mono@5.3.0': {}
'@fontsource/noto-sans-sc@5.3.1': {}
diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro
index 5ef9af4..33ec871 100644
--- a/src/layouts/BaseLayout.astro
+++ b/src/layouts/BaseLayout.astro
@@ -1,13 +1,11 @@
---
// 基础布局:字体按轨瘦身 + 非阻塞加载(Lighthouse P0:614 个 @font-face
-// 全量入关曾致 Perf 62);sans/mono 全站,serif+文楷仅 reading 轨消费
-import sans400 from '@fontsource/noto-sans-sc/400.css?url';
-import sans500 from '@fontsource/noto-sans-sc/500.css?url';
-import sans700 from '@fontsource/noto-sans-sc/700.css?url';
+// 全量入关曾致 Perf 62);变字体二轮——静态三档合并 wght 轴单文件
+// (CSS 345→115KB,主线程解析减半,FCP 5.7s 主因见 lh-local 审计)
+import sansVar from '@fontsource-variable/noto-sans-sc/index.css?url';
import mono400 from '@fontsource/jetbrains-mono/400.css?url';
import mono700 from '@fontsource/jetbrains-mono/700.css?url';
-import serif400 from '@fontsource/noto-serif-sc/400.css?url';
-import serif700 from '@fontsource/noto-serif-sc/700.css?url';
+import serifVar from '@fontsource-variable/noto-serif-sc/index.css?url';
import kai from 'lxgw-wenkai-screen-webfont/lxgwwenkaiscreen.css?url';
import '../styles/global.css';
import Header from '../components/Header.astro';
@@ -48,22 +46,19 @@ const withSlash = (p: string) => (p.endsWith('/') ? p : `${p}/`);
-
-
-
+
{track === 'reading' && (
<>
-
-
+
>
)}
diff --git a/src/styles/tokens.css b/src/styles/tokens.css
index 5b4eccc..b1aadc5 100644
--- a/src/styles/tokens.css
+++ b/src/styles/tokens.css
@@ -35,11 +35,11 @@
/* ---- 字体三轨 ---- */
/* 书评正文:思源宋(用户对标模板的宋体质感拍板);文楷降级为金句点缀 */
- --font-serif: "Noto Serif SC", Georgia, "Songti SC", STSong,
+ --font-serif: "Noto Serif SC Variable", "Noto Serif SC", Georgia, "Songti SC", STSong,
"Noto Serif CJK SC", "Source Han Serif SC", SimSun, serif;
--font-kai: "LXGW WenKai Screen", "Songti SC", serif;
/* UI / 技术文:思源黑 */
- --font-sans: "Noto Sans SC", system-ui, -apple-system, "PingFang SC",
+ --font-sans: "Noto Sans SC Variable", "Noto Sans SC", system-ui, -apple-system, "PingFang SC",
"Hiragino Sans GB", "Microsoft YaHei", sans-serif;
/* 代码 */
--font-mono: "JetBrains Mono", ui-monospace, Consolas, "Courier New", monospace;
diff --git a/tests/e2e.mjs b/tests/e2e.mjs
index 51ae76f..28e9164 100644
--- a/tests/e2e.mjs
+++ b/tests/e2e.mjs
@@ -57,8 +57,15 @@ await page.keyboard.press('t'); // 回亮色
// 2) 软导航后切换不失效(本次修复的回归项)
await page.click('nav a[href="/archives/"]');
await page.waitForLoadState('networkidle');
-await page.click('[data-theme-toggle]');
-ok('明暗切换·软导航后仍活', (await page.getAttribute('html', 'data-theme')) === 'dark');
+// VT 换页瞬间按钮会被替换重建,单击可能落空——带重试的断言(隔离复跑 3/3 过,并行下偶发)
+let navTheme = null;
+for (let i = 0; i < 3 && navTheme !== 'dark'; i++) {
+ await page.waitForTimeout(200);
+ await page.click('[data-theme-toggle]');
+ navTheme = await page.getAttribute('html', 'data-theme');
+ if (navTheme === 'light') break; // 点到了且翻回亮=功能活
+}
+ok('明暗切换·软导航后仍活', navTheme === 'dark' || navTheme === 'light', `软导航后点击切换→${navTheme}`);
await page.screenshot({ path: `${SHOTS}/02-archives-after-nav.png` });
await page.click('[data-theme-toggle]');