Files
task_pilot/docs/decisions/0006-visual-theme-and-offline-fonts.md
CNWei 14fb3563c5 chore(fonts): 字体二进制不入库,改用脚本生成
- 远程仓库对单次推送体积有限制,6.7 MB 字体无法一次推送,先从版本库移除
- 新增 tools/build-fonts.py:下载官方分包、合并并子集化,一条命令重建
- @font-face 保留,字体缺失时回退系统字体;离线用例改为按可用性断言
2026-10-26 00:46:20 +08:00

3.1 KiB
Raw Permalink Blame History

ADR-0006:视觉风格对齐主题并自托管字体

  • 状态:已接受
  • 日期:2026-10-25

背景

产品要求前端视觉与参考站点(Alright / nicetheme)一致,包括字体、配色和交互,但不改变既有布局。参考站点使用 Bootstrap 5 + 自定义变量,正文依赖 CDN 的 lxgw-wenkai-webfont,标题使用自托管的 Outfit-Medium。

本项目的目标是"本机、离线可复现":依赖 CDN 会在断网、内网或 CDN 故障时导致字体回退,从而破坏视觉一致性。

决策

  1. 从参考站点抓取其 CSS 的变量定义,把真实色值映射到本项目既有语义变量(--primary、--text-main、--border、--success 等),布局相关声明一律不改。
  2. 交互细节对齐主题:0.3s ease-in-out 过渡、主/危险按钮 hover 使用 opacity: .85、输入框聚焦使用主色半透明光环、选中项使用主色浅染。
  3. 字重遵循主题的 --font-weight-bold: 500;标题与指标数字使用 Outfit-Medium,正文使用 LXGW WenKai,等宽使用主题的 SFMono-Regular…Consolas 栈。
  4. 字体全部自托管:Outfit-Medium.ttf 直接取自主题;LXGW WenKai 由官方 webfont 的 97 个 woff2 子集合并后,再裁剪到 GB2312 + 常用符号子集,输出简体中文 Regular 与 Bold 两个文件。
  5. 移除所有 Google Fonts / CDN 字体链接,index.html 只 preload 本地字体文件。
  6. 新增端到端用例"renders fully offline":拦截并拒绝一切非 127.0.0.1 请求,断言页面正常渲染、document.fonts.check 命中两种字体且没有任何外部请求。

原因

  • 直接用主题的变量值,颜色不会"看起来像"而是确实一致。
  • 字体子集化把 8.8 MB 的 194 个子集压到 6.7 MB 的两个文件,仓库与安装包体积可控,浏览器只需一次请求。
  • 离线断言把"可复现"变成可回归的测试,而不是口头承诺。

边界

  • 字体子集覆盖 GB2312(6763 字)+ Latin + CJK 标点 + 全角符号;更生僻的汉字(GB18030 扩展区)会按浏览器逐字回退到系统字体,显示正常但字形风格不一致。
  • 只包含 400 / 700 两个字重,500 / 600 由 CSS 字重匹配落到 400 / 700;主题的 Light(300)未包含。
  • 未实现参考站点的暗色模式(data-scheme="dark")。
  • 字体文件随 frontend/public/fonts 一起进入安装包,体积增加约 6.7 MB。

后续调整(2026-10-26)

仓库托管的远程仓库对单次推送体积有上限,6.7 MB 的字体二进制无法一次推上去。因此:

  • 中文字体二进制 不再纳入版本库(/frontend/public/fonts/LXGWWenKai-*.woff2 已加入 .gitignore);
  • 新增 tools/build-fonts.py,一条命令即可从官方 CDN 重新下载、合并并子集化生成这两个文件;
  • @font-face 仍然保留,字体缺失时浏览器回退到系统字体,功能不受影响;
  • 对应的端到端用例改为「无外部网络渲染」:始终断言没有外部请求,只有字体文件存在时才断言字体命中;
  • Outfit-Medium.ttf(55 KB)体积很小,继续随仓库提供。