# 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)体积很小,继续随仓库提供。