单文件人物档案页
产出一个完全自包含的 HTML 文件:图片以 base64 内嵌,零外部依赖,断网能打开、能直接发给别人、能扔到任意静态托管。视觉体系为「深色舞台感」——近黑底、冷光强调色、大字号标题、细线分隔、滚动入场动画。
核心承诺(不可退让)
- 零外部依赖:产物中不得出现任何
<script src>、<link href>、@import、http(s)://资源引用。字体只用系统字体栈。 - 不手写 base64:图片一律写占位符,由脚本替换。
- 只缩不放:素材小于目标尺寸时不放大,改用小承载位。
- 数据有出处:所有数字标明来源与统计截止月份;涉及人物履历、奖项、纪录、销量的必须联网核实。
开始前先确认三件事
用 AskUserQuestion 一次问清,不要自己猜:
- 主体是谁 —— 决定内容方向(找什么资料、突出什么数据)。
- 要哪几个章节 —— 默认给全套:简介 / 关键数据 / 量级配置 / 影像 / 代表作品。多余就砍掉,一节不写比写虚了好。
- 交付形态 —— 直接产出 HTML 文件,还是先出设计稿?默认直接产出 HTML。
另需知道:文案是用户给还是自己去查。用户没给资料就联网检索,并明确告知数据来源与截止日。
执行流程
步骤 1 · 收集并核实内容
- 联网检索主体资料:履历、代表作、关键数字、可引用的原话。
- 每个数字记下来源渠道和统计截止月份,后面要写进正文和页脚。
- 找 1–2 句有出处的原话放进引语区——一句真话胜过十句形容词。
- 拿不准的具体纪录("全球第一""刷新纪录")必须核实;核实不了就弱化为"据 XX 报道"。
步骤 2 · 盘点素材
先把用户给的图片集中到一个目录,再读真实尺寸:
python -c "import sys,glob,os;sys.path.insert(0,'scripts');from imgutil import image_size;[print(os.path.basename(f), image_size(f)) for f in glob.glob('<目录>/*')]"
按尺寸决定承载位:
- ≥800px → 可用于首屏主视觉
- 400–800px → 图集(contact sheet)
- <400px → 图集里的窄条位,或重度虚化后当首屏背景(blur(52px) 一上,像素缺陷全消失,只剩色调)
把素材重命名成有意义的名字(portrait.webp / hero-bg.webp / shot-1.webp…),方便在 HTML 里引用。
步骤 3 · 从模板生成页面
复制 assets/template.html 到目标路径,然后二选一:
A. 直接改文案(推荐,适合全新主体)
在副本上原地替换正文文字,保留所有类名与章节顺序。图片位置改写为 {{IMG:文件名}}。
B. 数据驱动(适合批量出稿,或想留一份可复用数据)
保留模板不动,把文案填进一份 JSON(键名参考 references/example-data.json)。先看键名清单:
python scripts/embed_assets.py --template assets/template.html --dump-keys
步骤 4 · 构建(图片内嵌 + 自动尺寸)
python scripts/embed_assets.py \
--template <工作副本.html> \
--out <产物.html> \
--img-dir <素材目录> \
[--data <数据.json>] \
[--max-w 480]
脚本做三件事:
- {{IMG:文件名}} → base64 data URI(在 --img-dir 下递归查找)
- data-autosize 标签 → 按图片真实尺寸等比写入宽高(<figure> 写内联宽高,<img> 写 width/height 属性);只在超过 data-max-w 时才缩,不放大
- {{TXT:key|默认值}} → 用 --data 覆盖,缺省保留默认值
退出码 1 表示有占位符未解析(产物仍会写出,方便排查)。必须跑,看到"占位符全部解析完毕"才能交付。
步骤 5 · 校验
python scripts/verify_page.py <产物.html>
必须输出「通过:零外部依赖,结构完整」。有 [错误] 必须修掉重跑;[警告] 逐条判断——它们都是已知踩坑点,详见 references/pitfalls.md。
步骤 6 · 交付
用 present_files 打开产物让用户预览,并把产出路径、素材尺寸情况、数据来源与截止日一并说清。
关键约束速查
| 事项 | 规则 |
|---|---|
| 首屏高度 | min-height:100vh 打底,@supports 里再覆盖 100svh。只写 svh 会在无头浏览器里塌成 0 |
| 图集窄屏 | @media(max-width:720px) 里用 !important 覆盖内联宽高,改两列固定高 |
| 数字滚动 | data-count 只认纯数字;"100万+""65.19万"这类写成静态值 + <small> 装单位 |
| 条形图 | data-w 是相对最大值的百分比,最大那条固定 100% |
| 新增动画 | 必须同时写 prefers-reduced-motion 降级 + IntersectionObserver 不存在的兜底分支 |
| 表格/锚点 | 表格包 .tbl-wrap;固定导航配 scroll-margin-top:66px |
| 参考模板 | 用户给的参考页(如模板站下载的)只借版式思路,不复制其资源、不外链其 CDN,一律用本地设计体系重做 |
| 体积 | base64 让素材膨胀约 33%,成品控制 5 MB 以内 |
文件说明
| 路径 | 用途 |
|---|---|
assets/template.html |
页面骨架:完整 CSS 设计体系 + 5 个章节 + 全部交互 JS + 图片占位符。唯一的设计来源。 |
scripts/imgutil.py |
纯 Python 图片尺寸读取(png/jpg/webp/gif/bmp),无需 Pillow |
scripts/embed_assets.py |
构建:图片内嵌 + 自动尺寸 + 文案覆盖 |
scripts/unembed_assets.py |
反向:把已内嵌的 base64 抽回素材文件 + {{IMG:}} 占位符 |
scripts/verify_page.py |
交付前自检:外链、占位符、已知踩坑、体积 |
references/design-system.md |
调性配色怎么换、每个组件用在哪、章节骨架、响应式策略、交互清单 |
references/pitfalls.md |
踩坑清单(症状→原因→做法)与交付前检查表 |
references/example-data.json |
一份完整的实例化数据(163 个键全填),复制改成自己的内容即可 |
改造已有页面时
用户拿一个已完成的此类页面来要求换主体 / 换素材 / 换配色:
- 先跑
verify_page.py摸清现状(有没有外链、结构是否完整)。 - 换主体 / 换素材:若原页面图片是 base64 内嵌的,先还原成素材文件 + 占位符,之后换图只需重跑构建:
bash python scripts/unembed_assets.py --page 旧页面.html --out 工作副本.html --img-dir ./imgs [--dry-run] python scripts/embed_assets.py --template 工作副本.html --out 新页面.html --img-dir ./imgs
改文案只动文字与{{IMG:}},不要动类名与结构;尺寸由脚本自动重算。 - 换配色:只改
:root令牌,并同步改design-system.md里列出的三处硬编码渐变色。 - 收尾必跑
verify_page.py。