feat(typography): 全站三层排版——富文本/页面级覆盖/艺术字体类

- 层 1 所有页面文案改富文本:site-content 新增 renderRich(md,{inline})
  复用既有全管线(raw→内容净化→class 白名单→链接卡片);接入
  home.tagline/subtitle、opensource.blurb、about.intro、blocks prose;
  新增 .rich 容器作用域并与 .prose-body 合并(77 处选择器覆盖四类容器)
  坑:renderRich 输出 <p> 塞进 <p class="tagline"> 会被浏览器拆散 →
  行内场景用 {inline:true} 剥外层 <p>
- 层 2 页面级排版覆盖:三单例加 typography{font?,accent?,scale?},读取器
  pageTypography() 走白名单(10 字族 / 18 色类,scale 夹 0.8–1.25,清单外
  忽略);页面容器挂 .page-typo + CSS 变量 --page-font/--page-accent/
  --page-scale,不动全站 tokens;后台三页加「页面排版覆盖」下拉
- 层 3 艺术字体:新增 font-art-kuaile(快乐体专用类,字距微调),白名单与
  可视化面板同步;站酷高端黑/阿里妈妈东方大楷/演示悠然小楷因无 npm 包或
  嵌入条款风险进候补清单
- 默认值清理:about.yaml 的 typography 测试值(整页汇文)清为注释示例——
  整页换字族会带来 694KB 字体成本,默认留空=全站默认排版

验证: 构建 48 页 exit 0;E2E 37/37;三页 page-typo 挂载与变量实测、四页
      (图库/书架/文章/装备)隔离为 0、font-art-kuaile 生效、about 清空后
      无覆盖

Spec: none (排版三层,先于 5g 的任务;T3/T4 另行交付)

Co-authored-by: GLM-5.3
This commit is contained in:
wait4xandGLM-5.3 committed 2026-11-02 18:40:32 +08:00
1 parent 2f7cbcf7bd
commit aaa9cd9274
10 files changed
+136 -48

No files matched your search

+51 -38
View File
@@ -148,7 +148,7 @@ pre code { background: none; border: none; padding: 0; font-size: inherit; }
}
/* 图注:**显式类驱动**(`<p class="fig-cap">说明</p>`)。此前按 `p > em:only-child` 自动命中,
把正文里任何独占成段的斜体都变成块级居中——用户实测的排版 bug(斜体单独一行并居中)由此而来。 */
.prose-reading p.fig-cap, .prose-tech p.fig-cap,
.prose-reading p.fig-cap, .prose-tech p.fig-cap, .rich p.fig-cap, .prose-body p.fig-cap,
.prose-reading p.fig-cap em, .prose-tech p.fig-cap em {
display: block;
text-align: center;
@@ -167,60 +167,66 @@ pre code { background: none; border: none; padding: 0; font-size: inherit; }
}
/* ── 受限排版类(文章内通过 <span class> 使用;rehype 白名单只放行这些类名) ──
颜色一律走 tokens,亮暗双态都可读;禁任意 hex/内联样式(保持灰阶骨架)。 */
.prose-reading .ink-1, .prose-tech .ink-1 { color: var(--ink-1); }
.prose-reading .ink-2, .prose-tech .ink-2 { color: var(--ink-2); }
.prose-reading .ink-3, .prose-tech .ink-3 { color: var(--ink-3); }
.prose-reading .ink-muted, .prose-tech .ink-muted { color: var(--ink-4); }
.prose-reading .ink-accent, .prose-tech .ink-accent { color: var(--accent); }
.prose-reading .ink-accent-soft, .prose-tech .ink-accent-soft { color: var(--accent-soft-text); }
.prose-reading .ink-warm, .prose-tech .ink-warm { color: var(--ink-warm); }
.prose-reading .ink-cool, .prose-tech .ink-cool { color: var(--ink-cool); }
.prose-reading .ink-faint, .prose-tech .ink-faint { color: var(--ink-faint); }
.prose-reading .ink-1, .prose-tech .ink-1, .rich .ink-1, .prose-body .ink-1 { color: var(--ink-1); }
.prose-reading .ink-2, .prose-tech .ink-2, .rich .ink-2, .prose-body .ink-2 { color: var(--ink-2); }
.prose-reading .ink-3, .prose-tech .ink-3, .rich .ink-3, .prose-body .ink-3 { color: var(--ink-3); }
.prose-reading .ink-muted, .prose-tech .ink-muted, .rich .ink-muted, .prose-body .ink-muted { color: var(--ink-4); }
.prose-reading .ink-accent, .prose-tech .ink-accent, .rich .ink-accent, .prose-body .ink-accent { color: var(--accent); }
.prose-reading .ink-accent-soft, .prose-tech .ink-accent-soft, .rich .ink-accent-soft, .prose-body .ink-accent-soft { color: var(--accent-soft-text); }
.prose-reading .ink-warm, .prose-tech .ink-warm, .rich .ink-warm, .prose-body .ink-warm { color: var(--ink-warm); }
.prose-reading .ink-cool, .prose-tech .ink-cool, .rich .ink-cool, .prose-body .ink-cool { color: var(--ink-cool); }
.prose-reading .ink-faint, .prose-tech .ink-faint, .rich .ink-faint, .prose-body .ink-faint { color: var(--ink-faint); }
/* 语义色板:彩色只作用于正文强调(站点外壳保持灰阶)。
每色相两档:标准 / soft(阈值内最淡);亮暗各取独立值,均实测 ≥4.5:1。 */
.prose-reading .c-blue, .prose-tech .c-blue { color: var(--c-blue); }
.prose-reading .c-blue-soft, .prose-tech .c-blue-soft { color: var(--c-blue-soft); }
.prose-reading .c-teal, .prose-tech .c-teal { color: var(--c-teal); }
.prose-reading .c-teal-soft, .prose-tech .c-teal-soft { color: var(--c-teal-soft); }
.prose-reading .c-green, .prose-tech .c-green { color: var(--c-green); }
.prose-reading .c-green-soft, .prose-tech .c-green-soft { color: var(--c-green-soft); }
.prose-reading .c-yellow, .prose-tech .c-yellow { color: var(--c-yellow); }
.prose-reading .c-yellow-soft, .prose-tech .c-yellow-soft { color: var(--c-yellow-soft); }
.prose-reading .c-orange, .prose-tech .c-orange { color: var(--c-orange); }
.prose-reading .c-orange-soft, .prose-tech .c-orange-soft { color: var(--c-orange-soft); }
.prose-reading .c-red, .prose-tech .c-red { color: var(--c-red); }
.prose-reading .c-red-soft, .prose-tech .c-red-soft { color: var(--c-red-soft); }
.prose-reading .c-pink, .prose-tech .c-pink { color: var(--c-pink); }
.prose-reading .c-pink-soft, .prose-tech .c-pink-soft { color: var(--c-pink-soft); }
.prose-reading .c-purple, .prose-tech .c-purple { color: var(--c-purple); }
.prose-reading .c-purple-soft, .prose-tech .c-purple-soft { color: var(--c-purple-soft); }
.prose-reading .font-serif, .prose-tech .font-serif { font-family: var(--font-serif); }
.prose-reading .font-kai, .prose-tech .font-kai { font-family: var(--font-kai); }
.prose-reading .font-sans, .prose-tech .font-sans { font-family: var(--font-sans); }
.prose-reading .font-mono, .prose-tech .font-mono { font-family: var(--font-mono); font-size: 0.92em; }
.prose-reading .c-blue, .prose-tech .c-blue, .rich .c-blue, .prose-body .c-blue { color: var(--c-blue); }
.prose-reading .c-blue-soft, .prose-tech .c-blue-soft, .rich .c-blue-soft, .prose-body .c-blue-soft { color: var(--c-blue-soft); }
.prose-reading .c-teal, .prose-tech .c-teal, .rich .c-teal, .prose-body .c-teal { color: var(--c-teal); }
.prose-reading .c-teal-soft, .prose-tech .c-teal-soft, .rich .c-teal-soft, .prose-body .c-teal-soft { color: var(--c-teal-soft); }
.prose-reading .c-green, .prose-tech .c-green, .rich .c-green, .prose-body .c-green { color: var(--c-green); }
.prose-reading .c-green-soft, .prose-tech .c-green-soft, .rich .c-green-soft, .prose-body .c-green-soft { color: var(--c-green-soft); }
.prose-reading .c-yellow, .prose-tech .c-yellow, .rich .c-yellow, .prose-body .c-yellow { color: var(--c-yellow); }
.prose-reading .c-yellow-soft, .prose-tech .c-yellow-soft, .rich .c-yellow-soft, .prose-body .c-yellow-soft { color: var(--c-yellow-soft); }
.prose-reading .c-orange, .prose-tech .c-orange, .rich .c-orange, .prose-body .c-orange { color: var(--c-orange); }
.prose-reading .c-orange-soft, .prose-tech .c-orange-soft, .rich .c-orange-soft, .prose-body .c-orange-soft { color: var(--c-orange-soft); }
.prose-reading .c-red, .prose-tech .c-red, .rich .c-red, .prose-body .c-red { color: var(--c-red); }
.prose-reading .c-red-soft, .prose-tech .c-red-soft, .rich .c-red-soft, .prose-body .c-red-soft { color: var(--c-red-soft); }
.prose-reading .c-pink, .prose-tech .c-pink, .rich .c-pink, .prose-body .c-pink { color: var(--c-pink); }
.prose-reading .c-pink-soft, .prose-tech .c-pink-soft, .rich .c-pink-soft, .prose-body .c-pink-soft { color: var(--c-pink-soft); }
.prose-reading .c-purple, .prose-tech .c-purple, .rich .c-purple, .prose-body .c-purple { color: var(--c-purple); }
.prose-reading .c-purple-soft, .prose-tech .c-purple-soft, .rich .c-purple-soft, .prose-body .c-purple-soft { color: var(--c-purple-soft); }
.prose-reading .font-serif, .prose-tech .font-serif, .rich .font-serif, .prose-body .font-serif { font-family: var(--font-serif); }
.prose-reading .font-kai, .prose-tech .font-kai, .rich .font-kai, .prose-body .font-kai { font-family: var(--font-kai); }
.prose-reading .font-sans, .prose-tech .font-sans, .rich .font-sans, .prose-body .font-sans { font-family: var(--font-sans); }
.prose-reading .font-mono, .prose-tech .font-mono, .rich .font-mono, .prose-body .font-mono { font-family: var(--font-mono); font-size: 0.92em; }
/* 系统字族回退类:不下载字体,命中访客本机字体——跨设备不保证一致(docs 已注明) */
/* 自托管优先 + 系统兜底(2026-11-02 口径):跨设备一致性由站内 webfont 保证,
系统字族只作最后兜底;font-fangsong 暂无 OFL 自托管包,仍为纯系统回退(docs 已注明)。 */
.prose-reading .font-song, .prose-tech .font-song {
.prose-reading .font-song, .prose-tech .font-song, .rich .font-song, .prose-body .font-song {
font-family: "Noto Serif SC Variable", "Noto Serif SC", "Songti SC", SimSun, serif;
}
.prose-reading .font-kai-sys, .prose-tech .font-kai-sys {
.prose-reading .font-kai-sys, .prose-tech .font-kai-sys, .rich .font-kai-sys, .prose-body .font-kai-sys {
font-family: "LXGW WenKai Screen", "Kaiti SC", STKaiti, KaiTi, serif;
}
.prose-reading .font-hei-sys, .prose-tech .font-hei-sys {
.prose-reading .font-hei-sys, .prose-tech .font-hei-sys, .rich .font-hei-sys, .prose-body .font-hei-sys {
font-family: "Noto Sans SC Variable", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
}
.prose-reading .font-huiwen, .prose-tech .font-huiwen {
.prose-reading .font-huiwen, .prose-tech .font-huiwen, .rich .font-huiwen, .prose-body .font-huiwen {
font-family: 'WF-Hwmc', var(--font-serif);
}
.prose-reading .font-kuaile, .prose-tech .font-kuaile {
.prose-reading .font-kuaile, .prose-tech .font-kuaile, .rich .font-kuaile, .prose-body .font-kuaile {
font-family: 'ZCOOL KuaiLe', var(--font-serif);
}
.prose-reading .font-fangsong, .prose-tech .font-fangsong {
/* 艺术字体专用类(层 3):仅用于标题/短句。艺术字字形大、CJK 切片体积高
(实测 163KB/用到页),长段使用会显著抬高单页字体成本——docs 有体积警告。 */
.prose-reading .font-art-kuaile, .prose-tech .font-art-kuaile, .rich .font-art-kuaile, .prose-body .font-art-kuaile {
font-family: 'ZCOOL KuaiLe', var(--font-serif);
letter-spacing: 0.02em;
}
.prose-reading .font-fangsong, .prose-tech .font-fangsong, .rich .font-fangsong, .prose-body .font-fangsong {
font-family: FangSong, STFangsong, "FangSong_GB2312", serif;
}
/* 文学常用:首字下沉(书评开篇)与金句块(引文式的独立强调段) */
.prose-reading p.drop-cap::first-letter, .prose-tech p.drop-cap::first-letter {
.prose-reading p.drop-cap::first-letter, .prose-tech p.drop-cap::first-letter, .rich p.drop-cap::first-letter, .prose-body p.drop-cap::first-letter {
float: left;
font-family: var(--font-serif);
font-size: 3.1em;
@@ -229,7 +235,7 @@ pre code { background: none; border: none; padding: 0; font-size: inherit; }
margin-top: 0.05em;
color: var(--paper-ink);
}
.prose-reading .quote-line, .prose-tech .quote-line {
.prose-reading .quote-line, .prose-tech .quote-line, .rich .quote-line, .prose-body .quote-line {
display: block;
font-family: var(--font-kai);
color: var(--paper-ink);
@@ -327,3 +333,10 @@ html.reader .prose-reading, html.reader .prose-tech {
}
html.reader #main > * { animation: none; } /* 入场动效关闭 */
/* ICP 备案链接永不隐藏(合规底线);分隔点号随之保留必要最小集 */
/* ── 层 2:页面级排版覆盖容器(只作用于当前页内容区,不动全站 tokens) ── */
.page-typo {
font-family: var(--page-font, inherit);
font-size: calc(1em * var(--page-scale, 1));
}
.page-typo .page-accent, .page-typo .link { color: var(--page-accent, var(--accent)); }