docs(tasks): serif headings via self-hosted Noto Serif SC slices; birth-sky cover follows
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0199rbQDTsUbCVw84wc8BTFe
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
aa33399ae5
commit
ebcee58419
@@ -249,6 +249,7 @@
|
||||
| `TASK-chart-types-and-report-buttons-20260928.md` | `PROGRESS-chart-types-and-report-buttons-20260928.md`、`PROGRESS-ayanamsa-report-buttons-20260928.md` | **星盘盘型扩充 + 表格随盘换 + 七政隐藏 + 岁差 6 项 + 我的报告按钮配色**:分盘表一直显示 D1 数据(BUG-1076);盘型条 + 「星盘类型」面板共 23 种(加 月亮盘 / Bhava / 行运 / D40 / D45 / D60);Bhava 端点缺 MC 静默等宫(BUG-1077);报告深色主按钮黑字深红、禁用半透明(BUG-1078,只改报告范围) | 已验收、已部署 staging `6a4e312c`(真机欠) | `af27709d`…`4189750c`(子代理实现 + Claude 验收补丁);BUG-1076~1078;含迁移 `20260928010000_profile_ayanamsa_six` |
|
||||
| `TASK-site-button-contrast-20260928.md` | `PROGRESS-site-button-contrast-20260928.md` | **全站实心按钮配色与禁用态统一**:层外 `button { color: inherit }` 压掉 Tailwind 文字色(61 个 `<Button>` 浅 3.33 / 深 2.57),`.button-primary` 30 个浅色 3.14,全部禁用态 45% 透明;产品选 A:全站用报告页配色(浅 4.85 / 深 5.77),推翻 DESIGN「已知缺口」;禁用改灰;删 BUG-1078 局部覆盖(BUG-1079) | 已验收、已部署 staging `dc640931`(真机欠) | `12aac87b` + `fa58df16`;BUG-1079 |
|
||||
| `TASK-birth-sky-cover-20260928.md` | `PROGRESS-birth-sky-cover-20260928.md` | **「那一刻的天空」封面**:出生那一刻头顶的真实天空做成 3:4 封面(产品从 A 年轮 / B 一笔画 / C 天空三版样稿中选 C,样稿 `assets/birth-sky-cover-sample-20260928.html`);星盘页按钮 + 保存图片、报告封面(须对得上报告出生资料)、首次建盘揭幕;不印出生资料、句库只描述天空、无新依赖 | 待领取 | 新功能,不开 BUG |
|
||||
| `TASK-serif-headings-20260928.md` | `PROGRESS-serif-headings-20260928.md` | **全站标题改用自托管宋体、正文保持黑体**:产品推翻 BUG-737「CJK 不用衬线」结论(保留「声明的字体必须可加载」「不落系统宋体」两条);Noto Serif SC SemiBold 按通用规范汉字表 6500 字 unicode-range 切片自托管(改名 Jyotisha Serif SC),swap 不 preload;首页宋体流量 ≤300 KB;先于天空封面单 | 待领取 | 不开新 BUG;BUG-737 追加说明 |
|
||||
| `TASK-cend-ui-claude-alignment-20260916.md` | `PROGRESS-cend-ui-r1/r2/r3-20260916.md` | **C 端界面向 claude.ai 产品界面对齐(三轮串行 R1→R2→R3,都动 `globals.css`,不得并行)**:根因是 `frontend/CLAUDE_DESIGN.md` 扒的是 **claude.com 营销官网**,它自己在 Known Gaps 里写明 claude.ai 产品界面不在范围内,而 `DESIGN.md:3` 把它当成了产品界面的实现契约。**R1**:`--font-display` 里 Tiempos Headline / StyreneB **从未加载**(无 `@font-face`、`public/` 无字体、`layout.tsx` 只 vendor 了 Inter),中文标题全站落到 **宋体 / SimSun**,波及 20 处含助手回答的 h2/h3(BUG-737);亮色强调色 `#85432f` 与暗色 `#d78064` 不同源,产品拍板亮色换 **Claude coral `#cc785c`**,**易漏点**是 `globals.css:16` 的 `--color-ring` 硬编码在 `@theme inline` 里不跟随 `:root`,另有第四个 `:root` 亮色块(`:4358`)必须同步(BUG-738);`.composer-footer` 常驻 44px + 顶栏 68px + `--composer-reserve` 148px,每屏固定吃掉 216px,模型选择器移进输入框内部、删掉底栏、顶栏收到 46px 并删「分析对象」副标题。**R2**:空状态是营销落地页(hero 卡 + 两张 132px 入口大卡 + 3 列 156px 主题卡),输入框被压在 **800px 以上**内容之下,重排成「问候 + 居中输入框 + 两枚入口 pill + 一排 chip」。**R3**:侧栏两个 `<details>` 拍平成一条「最近」、星盘的两个入口(侧栏分组 + 账户菜单)收敛到一处、删掉逐条助手头像。**决策记录 D3 推翻 DESIGN.md「报告强调色与应用同源」一句**(报告刻意保留深棕)。原型图 https://claude.ai/code/artifact/da275da6-2954-4f50-99aa-32bb8694d38b(三套画面 + 明暗,页面标题就是建议字体栈的实际渲染)。环境缺口:无登录态无 Chrome,四项真机观感留 `docs/testing/`。BUG 段 737–738 | 待领取 | — |
|
||||
| `TASK-cend-surfaces-claude-alignment-20260916.md` | `PROGRESS-cend-shell-20260916.md`、`PROGRESS-cend-report-20260916.md`、`PROGRESS-cend-rectification-20260916.md`、`PROGRESS-cend-chart-eph-20260916.md` | **次级页面对齐(上一单的续篇,R4→R5/R6,R7、R8 可并行)**:星盘 `/chart`、星历 `/ephemeris`、报告 `/reports` **各是脱离 app 外壳的独立全屏页**,顶部只有一个「返回对话」链接、侧栏整个消失,且三家各写了一套一模一样的 `*-shell`/`*-topbar`/`*-hero` 骨架——与上一单 E5 同根因(营销站 band 结构被套到产品界面)。**R4** 抽只读导航外壳 `AppNavRail`(只用现成的 `GET /api/sessions` + `GET /api/account`,会话行走 `sessionHref` 跳 `/?c=<uuid>`;**刻意不带**重命名/删除/收藏/归档——那套连着 `Home()` 的乐观更新与回滚,搬过来会撞 useState 增长门禁)。**R5** 星盘五 tab 下划线化 + 参数合表 + 行星表横向滚动;星历日期导航改 `‹ 日期 ›`。**R6** 报告中心卡片网格改行式列表;阅读页加常驻目录。**R7** 生时校正把可信区间从盘面板标题行提成常驻条(窄屏 `.is-compact` 下盘面板是 overlay,现在默认看不到区间),五个 `technique-audit` 折叠块收成两段。**R8** 设置内容区收窄(880px 弹窗里表单铺了 690px)、套餐卡三修饰符收敛成两态。**已解锁**:原挡路的设置单已于 `111b4a84`(BUG-698)合入。**两条不得回退**:BUG-698 的 `@supports (height: 1dvh)` 写法(重复声明回退会被 Lightning CSS 折叠)、BUG-616/617 的报告盘面 grid 实现。默认不占 BUG 号 | **R4–R8 全部已实现并验收合入** | R4:抽出 `AppNavRail`(只读,两个 GET,零写操作)+ `SecondaryShell`,三个次级页并入 app 外壳并删掉各自的 shell/topbar/hero;四个路由渲染标记**完全不变**(`/` `/chart` `/ephemeris` 仍 Static);CSS gzip −0.25%。`/reports/[reportId]` 留给 R6 与目录一起做。两处自身健壮性问题被测试抓到:`usePathname()` 可为 null、`fetch` 可能不存在。差点弄丢 BUG-717 的 eyebrow 文案(已放回)。R8:表单分区收窄到 440px(列表分区不变)、套餐卡三修饰符收敛成互斥的 `is-current` / `is-recommended`,`--highlighted` 删除改为滚动定位;手机端 `order:-1` 改挂 `[data-plan-alias]`(版位不是状态)。测试 3350→3354(净增 4),失败清单与基线逐条一致;`/` 仍 Static;我的干净构建实测 CSS gzip −3 字节。**遗留待产品拍板**:`?plan=` 深链现在完全没有视觉指向,只有滚动位置。R6:报告中心卡片网格改行式列表、阅读页并入外壳并把目录挪到右侧常驻。**任务书 E10 过期**——目录在 `cfcd369d` 就已存在,本轮是挪位置定稿而非从零加。挂外壳带出一个真实打印风险已处理:`.chat-app`/`.chat-panel` 是 `height:100%;overflow:hidden`,裸 `window.print()` 会把九节报告裁成一页,阅读页因此多挂一条只在挂载期生效的 print 样式解锁外壳。「生成中的分节进度」做不了——`REPORT_LIST_COLUMNS` 不返回节数,按 VOICE.md 不许前端编。R7:区间常驻条与盘面折叠收敛。**任务书 E9 也不准确**——对话区顶部早有常驻条 `RectificationTimeline` 且窄屏可见,真正只在盘面标题行的是**代表分钟**;因此没另造第二条,在既有条上补齐代表分钟与已答题数(与盘面同一次 `workingRectificationTime()` 调用)。折叠块实际是 **8 个**不是 5 个。**触发让步顺序第 5 条**:收窄进度未做——服务端无该字段,且 `candidate_range` 会放宽(BUG-572),前端相减会把一次放宽报成收窄,已写进 `BLOCKED.md`。顺带修掉一个**静默失效的旧断言**(`slice(indexOf(A), indexOf(B))` 在 B 改名后变成几乎整份文件,四条 `doesNotMatch` 假通过)|
|
||||
| — (产品口头拍板,无任务书) | `PROGRESS-chart-glyphs-20260918.md` | **星盘改用行星符号,度数进行星表(非 Bug,无 BUG 号)**:北印星盘(星盘页 / 校正右栏 / 我的报告三处共用 `VedicChartSvg`)宫内不再画「水 19°」「罗逆 29°」这类文本——一宫三颗星就叠三行、字号压到 8。改成九个彩色符号(☉☽☿♀♂♃♄☊☋),每行最多 3 个、逆行改符号下同色横线、第 1 宫加 `As`、盘下两行图例。颜色只认星不表吉凶,罗计共用中性灰,`--color-planet-*` 九个 token 在 `:root` 与两个深色块各一份;颜色挂 `is-*` 修饰类而非行内 `style`(报告标记合同禁 `style=`)。符号字体是 vendored 的 9 字形 Noto Sans Symbols 子集(OFL,1.5 KB),`--font-planet-glyphs`,♀♂ 带 U+FE0E 防 iOS 画成表情。星盘页行星表四列扩到八列(行星 / 黄道经度 / 速度 / 宫位 / 星宿 / 宿主 / pada / 状态),度数精确到分,首行是上升(`vedic.ascendant` 新字段)。数据来源:`toNorthIndianChart()` 加结构化 `occupantGlyphs`;校正与报告两条路线的模型只有引擎/golden 显示文本,按首字解析并有测试锁住。**星宿未译中文**——仓库无对照表,印度 27 宿与中国二十八宿不同源,不自造 | 已实现,待验收 | `codex/chart-glyphs-20260918`:tsc 0 / lint 0 error(118 warning 不变)/ npm test 3471→3484 条、36 红与基线 `84b293fb` 逐条相同、0 新红 / `○ /` Static / 首屏 JS gzip 1,450,774→1,455,017(+0.29%),CSS gzip +2.23%(构成见 PROGRESS §2)。真机走查欠(iOS 符号呈现、深浅两主题配色、375px 横滚、打印) |
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
- `origin/staging` = `dc640931`(2026-09-28)。开工时 `git fetch origin --prune`,以最新 `origin/staging` 为基线,实际 SHA 写进 PROGRESS。
|
||||
- 分支 `codex/birth-sky-cover-20260928`,工作树 `.worktrees/birth-sky-cover-20260928`。
|
||||
- 进度记录 `docs/tasks/PROGRESS-birth-sky-cover-20260928.md`。
|
||||
- 串行关系:`TASK-site-button-contrast-20260928`(BUG-1079)改全站按钮配色。它若已合入 staging,就以它为基线;若还没合入,本单新增的按钮一律用现有共享按钮类(`.button-secondary` / `<Button variant="outline">`),不自己写按钮颜色,这样两单没有文件交集。同日其它任务书与本单无交集。
|
||||
- 串行关系:**`TASK-serif-headings-20260928` 先做**(两单都动 `globals.css` 与 DESIGN.md §3);本单以它合入后的 staging 为基线。它若未合入,本单照写家族名,加载不到时自动退回黑体。`TASK-site-button-contrast-20260928`(BUG-1079)改全站按钮配色。它若已合入 staging,就以它为基线;若还没合入,本单新增的按钮一律用现有共享按钮类(`.button-secondary` / `<Button variant="outline">`),不自己写按钮颜色,这样两单没有文件交集。同日其它任务书与本单无交集。
|
||||
- 视觉样稿:`docs/tasks/assets/birth-sky-cover-sample-20260928.html`,看第三张「C · 那一刻的天空」。A、B 两张是被否掉的方案,只作对照,不实现。
|
||||
|
||||
## 这是什么(产品背景,不是事故)
|
||||
@@ -37,7 +37,7 @@
|
||||
4. **图上的那句话只描述天空,不讲运势**:不写大运、上升、性格。句子从下文「句库」按天空状态确定性地选出,同一张盘永远同一句,不调模型、不扣点。
|
||||
5. **出生时间未采用校正的也能生成**:`birthTimeStatus` 不是 `accepted` / `confirmed` 时,图底加一行小字「按你填写的时间」。
|
||||
6. **不另开页面,不加侧栏入口,不做第六个 Tab**;星盘页只在标题栏右侧加一个按钮。
|
||||
7. **中文不用衬线字体**:样稿里用了 Noto Serif SC,但 DESIGN.md §3(BUG-737)规定「CJK never takes a serif here」。本单按站内字体栈与字重 500 实现,**不引入新字体**。产品若想给封面单独开衬线例外,另立单。
|
||||
7. **标题用宋体**(产品 2026-09-28 追加决定,见 `TASK-serif-headings-20260928`):封面主句与顶部小字用字体家族 `"Jyotisha Serif SC"`,由那份任务书自托管;事实句、底部说明仍用站内黑体栈。画布绘制前先 `await document.fonts.load('600 30px "Jyotisha Serif SC"', 主句)`,最多等 1.5 秒;超时就用黑体画,不出现等待态。**不引入 Google Fonts**。
|
||||
8. **第一期不接社交平台分享 SDK**。保存图片走浏览器原生能力(见 T4)。
|
||||
9. 罗睺、计都不是可见天体,**不画**。月亮在地平线下时不画,只进句库的事实句。
|
||||
|
||||
@@ -214,7 +214,7 @@
|
||||
- 投影与方位约定、两套 palette、三个位置;
|
||||
- 预取规则、揭幕时限;
|
||||
- 不印出生资料;
|
||||
- 中文不用衬线。
|
||||
- 标题用 Jyotisha Serif SC。
|
||||
- `frontend/docs/VOICE.md`:收录句库四句与事实句规则,并注明「只描述天空,不讲运势」。
|
||||
- `CHANGELOG.md`:一条,写明 Skill 版本不 bump。
|
||||
- `CONTEXT.md`:若新增「那一刻的天空 / 天空封面」术语,先进 glossary。
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
# TASK · 全站标题改用自托管宋体,正文保持黑体(2026-09-28)
|
||||
|
||||
## 基线
|
||||
|
||||
- `origin/staging` = `aa33399a`(2026-09-28)。开工时 `git fetch origin --prune`,以最新 `origin/staging` 为基线,实际 SHA 写进 PROGRESS。
|
||||
- 分支 `codex/serif-headings-20260928`,工作树 `.worktrees/serif-headings-20260928`。
|
||||
- 进度记录 `docs/tasks/PROGRESS-serif-headings-20260928.md`。
|
||||
- **串行关系**:本单先于 `TASK-birth-sky-cover-20260928`。两单都会动 `frontend/src/app/globals.css` 和 `frontend/DESIGN.md` §3,必须串行。天空封面单的画布标题用本单定义的字体家族名;如果本单还没合入,封面单照样写这个家族名,字体加载不到时会自动退回黑体,所以两单之间没有硬依赖。
|
||||
- `TASK-site-button-contrast-20260928`(BUG-1079)已部署 `dc640931`,与本单无交集:按钮文字属于正文,不改字体。
|
||||
|
||||
## 现状实证(Claude 2026-09-28 读码,行号按符号定位)
|
||||
|
||||
| 事实 | 位置 |
|
||||
| --- | --- |
|
||||
| `--font-display` 与 `--font-body` 是同一条无衬线栈(Inter → 系统 → PingFang SC / Microsoft YaHei) | `frontend/src/app/globals.css` `:root` 中的 `--font-display` / `--font-body` |
|
||||
| `globals.css` 里有 40 条规则使用 `var(--font-display)`,另有 1 处在 `.tsx` 里。覆盖:品牌字、页面与弹窗标题、助手回答的 `h2`/`h3`(`.message-markdown h2, h3`)、登录页大标题、首次引导卡标题、校正各卡标题、`.section-title h2` 等 | `grep -n "var(--font-display)" src/app/globals.css` |
|
||||
| 例外(刻意用正文字体的标题):校正叙述里的 `h2`/`h3`(`.conversational-narrative .message-markdown h2, h3` 用 `--font-body` 600)、报告分盘卡标题(`.personal-report-chart-card h3`) | 同文件 |
|
||||
| 当前只加载了两份自托管字体:`InterVariable-latin.woff2`(`next/font/local`)和行星符号子集;**没有任何中文字体文件** | `frontend/src/app/layout.tsx`、`frontend/src/app/fonts/` |
|
||||
| 三条合同测试把「CJK 不用衬线」锁成断言 | `frontend/tests/font-stack-loadable-contract.test.ts`(`no CJK serif is reachable from the display stack`,以及「声明的 family 必须可加载」)、`personal-report-theme-contract.test.ts`(标题用 `--font-display`、字重 500)、`consultation-entrypoint.test.ts` |
|
||||
| 错误页边界(`error.tsx` / `not-found.tsx` / `forbidden.tsx` / `global-error.tsx`)不能引 `globals.css`,字体栈是内联字面量 | DESIGN.md §3 最后一条 |
|
||||
|
||||
## BUG-737 的教训(本单必须保住的部分)
|
||||
|
||||
BUG-737 的真正问题是**声明了从未加载的字体**,导致标题落到各设备的系统宋体:
|
||||
|
||||
- 苹果设备:Songti SC,效果尚可;
|
||||
- Windows:SimSun,笔画细、发虚;
|
||||
- 多数安卓:没有中文衬线,退回系统默认字体。
|
||||
|
||||
当时的结论「CJK 不用衬线」是在「不打算自己加载中文字体」的前提下做出的。本单推翻的只是这个结论,以下两条**保留**:
|
||||
|
||||
1. 字体栈里每一个带引号的 family,都必须真能加载(自托管文件存在),或者属于系统字体白名单。`font-stack-loadable-contract` 的这一半断言保留。
|
||||
2. 标题里的中文**永远不得落到 SimSun 或浏览器默认 serif**。自托管字体没加载到时,退回无衬线,不退回系统宋体。
|
||||
|
||||
## 决策记录(产品 2026-09-28 授权)
|
||||
|
||||
1. **推翻 DESIGN.md §3 与 BUG-737 的「CJK never takes a serif here」**。执行方不得以这两处为由拒改;DESIGN.md、BUG-737 记录和合同测试注释要同步改写(见 T5)。
|
||||
2. **范围:标题用宋体,正文保持黑体。**
|
||||
- 用宋体:凡是使用 `--font-display` 的规则,加上报告封面与章节标题。
|
||||
- 不用宋体:聊天正文、按钮、表格、输入框、标签、后台管理界面,以及上表列出的两处「刻意用正文字体的标题」。
|
||||
- 产品在三个选项(仅标题 / 标题加报告正文 / 全站)中选了「仅标题」。
|
||||
3. **字体:自托管思源宋体 Noto Serif SC**(SIL OFL 1.1,可商用、可再分发)。
|
||||
- 只收一个字重 SemiBold 600,`@font-face` 声明为 `font-weight: 500 700`,让现有字重 500 的规则直接命中,不用逐条改字重。
|
||||
- **不用 Google Fonts CDN**,理由是国内访问不到,而且构建镜像不能联网下载字体。
|
||||
4. **按 unicode-range 切片**:浏览器只下载页面实际用到的字所在的切片。收录范围如下,范围外的生僻字退回无衬线:
|
||||
- 《通用规范汉字表》一级字加二级字(6500 字);
|
||||
- 基本拉丁;
|
||||
- 中文与全角标点;
|
||||
- 数字与常用符号。
|
||||
5. **中英混排统一用宋体**:Noto Serif SC 自带拉丁字形,像「D10 事业盘怎么读」这样的标题会整行都是宋体,不会劈成两种字体。当年否决 Newsreader 的理由(拉丁和中文分成两种字体)在这里不成立。
|
||||
6. `font-display: swap`,**不 preload**:首屏先用黑体画出来,宋体到了再替换,不阻塞首屏,也不加任何等待态。
|
||||
|
||||
## 硬红线
|
||||
|
||||
1. **不新增 npm 依赖**。切片用 Python `fontTools`(`pyftsubset`)离线生成。生成脚本进仓 `scripts/fonts/build_serif_slices.py`,生成的 woff2 文件也进仓;构建期不联网、不重新生成。
|
||||
2. **字体文件交给 Next 打包**,不要放进 `public/`:在 `src/app/fonts/serif-sc/` 下用 CSS `url()` 相对引用,这样文件会落到 `/_next/static/media/`,带内容哈希,可以长期缓存(immutable)。放进 `public/` 就拿不到长期缓存。
|
||||
3. 字体栈写法固定为:
|
||||
|
||||
```css
|
||||
--font-display: "Jyotisha Serif SC", var(--font-inter, Inter), -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
```
|
||||
|
||||
- 栈里**不得出现** `Songti SC`、`STSong`、`SimSun`、`Noto Serif CJK SC`,也不得以通用的 `serif` 结尾;
|
||||
- `--font-body` 不改。
|
||||
4. 错误页边界文件不改:它们仍用内联无衬线栈,在 DESIGN.md 里写明这是例外。
|
||||
5. `next build` 后 `/` 仍是 `○ Static`。首屏 JS gzip ±2%。CSS gzip 会因为 `@font-face` 声明而增加,允许超过 2%,但要在 PROGRESS 里写实测数字和原因;如果超过 +8%,把 `@font-face` 拆到单独的 CSS 文件(仍由 `layout.tsx` 引入)。
|
||||
6. 字体流量(在 PROGRESS 里实测,用 `next start` 加无头 Chrome 统计 `.woff2` 请求):
|
||||
- 首页首屏(未登录落地页和已登录空白首页各测一次)下载的宋体切片合计 **≤ 300 KB**;
|
||||
- 一篇完整个人报告页 **≤ 900 KB**;
|
||||
- 超过就调整切片粒度(常用字切小一些),不许删减收录范围。
|
||||
7. 许可:
|
||||
- `src/app/fonts/serif-sc/OFL.txt` 放原许可证;
|
||||
- `SOURCE.txt` 写明上游版本、下载地址、sha256、切片方式;
|
||||
- 家族名改成 `Jyotisha Serif SC`,因为按 OFL 的保留字体名条款,修改后的衍生字体不得沿用原名(切片属于修改)。
|
||||
8. 改任何既有断言,都要在测试文件注释和 PROGRESS 里写「原值 / 新值 / 原因」三栏(AGENTS §7.3)。
|
||||
|
||||
## 任务分解
|
||||
|
||||
### T1 · 生成切片
|
||||
|
||||
- `scripts/fonts/build_serif_slices.py`:
|
||||
- 输入上游 `NotoSerifSC-SemiBold.otf`(Google Fonts 或 Adobe source-han-serif 的 release,二选一,写进 SOURCE.txt;源文件不进仓);
|
||||
- 输出 `src/app/fonts/serif-sc/*.woff2` 和 `serif-sc.css`(`@font-face` 列表)。
|
||||
- 切片策略:
|
||||
- 最高频的约 1000 字单独切成 2~3 片,因为首页和标题最常用到;
|
||||
- 其余一级字、二级字每片约 300~400 字;
|
||||
- 拉丁、数字、标点单独一片。
|
||||
- 字表来源:《通用规范汉字表》(2013),字表文件放在 `scripts/fonts/` 下并写明来源。
|
||||
- 脚本可以重复运行:同一输入,输出逐字节一致(固定 fontTools 参数,去掉时间戳)。
|
||||
|
||||
**验收**
|
||||
|
||||
- `tests/test_serif_font_slices.py`:
|
||||
- 6500 字每一个都至少被一片的 unicode-range 覆盖;
|
||||
- 各片 range 不重叠;
|
||||
- `serif-sc.css` 里引用的每一个文件都存在;
|
||||
- 单片不超过 120 KB。
|
||||
|
||||
### T2 · 接入字体栈
|
||||
|
||||
- 由 `layout.tsx` 引入 `serif-sc.css`(或把 `@font-face` 并进 `globals.css`,二选一,理由写进 PROGRESS)。
|
||||
- 按硬红线第 3 条改 `--font-display`。
|
||||
- 报告封面 `h1`、章节 `h2`(`.personal-report-cover h1`、`.personal-report-section-heading h2` 等)若没走 `--font-display`,就改成走它。改之前先 grep 报告的标题规则,列出清单写进 PROGRESS。
|
||||
- 打印(`@media print`)同样用宋体,字体切片随页面已加载。
|
||||
- 例外保留不改:
|
||||
- `.conversational-narrative .message-markdown h2, h3`(校正叙述,刻意用正文字体);
|
||||
- `.personal-report-chart-card h3`;
|
||||
- 后台管理全部。
|
||||
- 如果执行方认为其中哪一条应该跟着改,写进 PROGRESS 由 Claude 定,不自行改。
|
||||
|
||||
**验收**:`tsc --noEmit` 0 错;`npm run lint` 0 error;`npm test` 总数不低于基线,失败清单与基线逐条一致;`/` 仍 Static。
|
||||
|
||||
### T3 · 合同测试改写
|
||||
|
||||
- `font-stack-loadable-contract.test.ts`:
|
||||
- 保留「每个带引号的 family 必须可加载或在白名单里」,并把「可加载」扩展为:在 `next/font/local` 里声明过,**或者**在 `serif-sc.css` 的 `@font-face` 里声明过且文件存在;
|
||||
- 删除 `no CJK serif is reachable from the display stack`,替换为新断言:`--font-display` 的第一个 family 是 `Jyotisha Serif SC`,且栈里不含 `Songti SC` / `STSong` / `SimSun` / `Noto Serif CJK SC`、不以通用 `serif` 结尾。
|
||||
- `personal-report-theme-contract.test.ts`、`consultation-entrypoint.test.ts`:只更新注释里「`--font-display` 是无衬线」的说法;字重 500 的断言保持不变(`@font-face` 的 500~700 范围会让它命中 SemiBold)。
|
||||
- 新增 `serif-headings-contract.test.ts`:断言上面「例外」清单里的规则仍然用 `--font-body`,防止以后顺手被改掉。
|
||||
|
||||
### T4 · 真机与性能实测
|
||||
|
||||
- PROGRESS 里给出:
|
||||
- 首页(未登录、已登录)和一篇报告页各自下载的宋体切片数量与总字节;
|
||||
- CSS gzip 前后对比;
|
||||
- 首屏 JS gzip 前后对比。
|
||||
- 本机无头 Chrome 截三张图:首页、带 `h2` 的咨询回答、报告封面。截图不进仓,放 PROGRESS 附注的路径即可。确认中文标题是宋体、正文是黑体。
|
||||
|
||||
### T5 · 文档
|
||||
|
||||
- `frontend/DESIGN.md` §3:
|
||||
- 重写 Display 条目:写新字体栈、自托管切片、SemiBold 用 500~700 范围、先显示黑体再替换、例外清单;
|
||||
- 删除「CJK never takes a serif here」,改成「CJK 标题用自托管 Jyotisha Serif SC;绝不落到系统宋体」;
|
||||
- 同时修掉 §3 表格下那句过时的「Display headings use the serif stack at weight 400」。
|
||||
- `docs/BUG_HISTORY.md` 的 BUG-737 记录:
|
||||
- 追加一行「2026-09-28 产品决定标题改用自托管宋体(TASK-serif-headings-20260928),本条『CJK 不用衬线』的结论被推翻;『声明的字体必须可加载』与『不得落到系统宋体』两条防复发保留」;
|
||||
- 状态不变。
|
||||
- `CHANGELOG.md`:一条「标题改用宋体」,Skill 不 bump。
|
||||
- `docs/testing/serif-headings-checklist.md` 真机清单:
|
||||
- iPhone Safari、Windows Chrome / Edge、安卓 Chrome(至少一台国产机)各看一遍首页标题、回答小标题、弹窗标题、报告封面与打印 PDF;
|
||||
- 确认没有细瘦发虚的 SimSun;
|
||||
- 弱网(Chrome 开发者工具的 Slow 4G)下先显示黑体、再换成宋体,没有空白字。
|
||||
|
||||
## 让步顺序
|
||||
|
||||
1. 报告页流量上限 900 KB 可以放宽到 1.2 MB,要写原因。
|
||||
2. T4 的截图可以省,改由真机清单覆盖。
|
||||
3. 收录范围(6500 字)、「不得落到系统宋体」、「字体栈只含可加载字体」**不可让步**。
|
||||
|
||||
## 开工前置命令
|
||||
|
||||
```bash
|
||||
cd /workspace/Jyotisha && git status -sb | head -1
|
||||
git fetch origin --prune
|
||||
git worktree add -b codex/serif-headings-20260928 .worktrees/serif-headings-20260928 origin/staging
|
||||
cd .worktrees/serif-headings-20260928
|
||||
grep -oE "BUG-[0-9]+" docs/BUG_HISTORY.md | sort -t- -k2 -n | tail -1
|
||||
python3 -c "import fontTools; print(fontTools.version)" # 没有就 pip install fonttools brotli(仅本机生成用,不进依赖)
|
||||
cd frontend && npm ci && ./node_modules/.bin/tsc --noEmit && npm run lint && npm test 2>&1 | tail -5
|
||||
```
|
||||
|
||||
Node 用 22(本机在 `/exec-daemon/node`)。
|
||||
|
||||
## BUG 编号
|
||||
|
||||
这是产品决定,不开新 BUG;BUG-737 追加说明(见 T5)。实现中若发现既有缺陷,从开工时的最大号 +1 起编,写作时最大号是 **BUG-1079**。
|
||||
|
||||
## 交付
|
||||
|
||||
- 单个分支,两个提交:
|
||||
1. 切片、脚本与测试(T1);
|
||||
2. 接入、合同测试与文档(T2~T5)。
|
||||
- PROGRESS 写全:基线 SHA、测试数、`/` Static、JS / CSS gzip、字体流量、三栏说明、环境缺口。
|
||||
- 用 `git push origin HEAD:staging` 快进推送,核对远端 SHA 与 `/api/health` 的 `deployment.gitCommit`。
|
||||
Reference in New Issue
Block a user