Files
blog/docs/写作指南.md
wait4xandGLM-5.3 5c015c6577 docs(writing): 取消彩色强调次数限制,补灯箱 caption 排版说明
用户原话:「色彩强调不要设置限制,取消一页不超过三处的限制」。原「一页 ≤3 处」
是文档纪律(代码层从未硬性拦截),故改动集中在文档、模板文章与后台提示:

- 写作指南 / 管理台:改为「彩色不限次数」,同时明确仍守的另一条红线是
  **外壳灰阶**(导航/页脚/按钮/背景不染色)——外壳是气质,正文是表达
- 写作模板文章中英双语同步:章节改名「想用多少用多少」,删掉"三处之后不再上色"
  的示范约束,表格里「一页 ≤3 处」改为「不限次数(外壳仍灰阶)」
- 顺带补灯箱 caption 文档:照片说明支持 markdown + 排版类,深底自动取暗档;
  以及灯箱四种关闭路径(右上 ×、Esc、点背景、1× 时点图)

验证: 构建 exit 0;全仓 grep 「≤3 处/页」只剩「已取消」的历史说明

Spec: 2026-11-02_blog-5i-fonts-polish (T5)

Co-authored-by: GLM-5.3
2026-11-03 11:18:14 +08:00

23 KiB
Raw Permalink Blame History

写作指南:在文章里插入图片 / 视频 / 音频

适用:content/posts/*.md(书评与技术轨通用)。资产目录约定 + 三种媒体的写法 + 体积纪律。

写作模板(从这里开始,2026-11-02 起)

别从空白文件起步——复制现成的模板:

cp content/posts/writing-template.md content/posts/my-new-post.md
# 改 frontmatter 的 title / pubDate / tags / description,删掉不需要的段落

模板本身是一篇真文章(线上读:/archives/writing-template/),每节都在跑一种能力,你看到的效果就是源码的效果:标题层级、彩色强调、字族切换、引用、列表、代码窗条、表格、图片放大、链接卡片、<details> 折叠、分隔线。

写之前只需记住三条纪律:

  • 彩色不限次数(2026-11-02 用户拍板取消原「一页 ≤3 处」限制):正文想用多少处颜色都可以;仍守的是另一条——外壳灰阶(导航/页脚/按钮/背景不染色),因为外壳是气质,正文是表达
  • 整页换字族要谨慎:会给访问者拉一份中文字体(汇文明朝整页约 420KB,已子集化)
  • 折叠放细节不放结论:<details> 里的内容可能永远没人展开

置顶文章

首页、归档、书架三处列表默认按日期倒序。想让某篇长期排在最前,frontmatter 加一行:

pinned: true

语义边界(刻意设计成这样):

  • 只影响列表顺序,不改 pubDate、不改 URL、不影响 RSS 的时间序列(订阅者不会看到时间错乱)
  • 多篇同时置顶时,它们内部仍按日期倒序
  • 列表里会显示一个灰阶「置顶」小徽标,取消置顶后立刻回到时间序
  • 后台「文章」编辑页有「置顶」开关,不必手改 frontmatter

目录约定

content/posts/
├── my-post.md                    # 文章
└── assets/
    └── my-post/                  # 与文章 slug 同名:该篇私有资产
        ├── photo.jpg
        ├── clip.mp4
        └── voice.mp3

相对引用一律 ./assets/{slug}/文件名,构建时 Astro 图片服务自动出多尺寸 webp。

图片

标准 markdown 语法,alt 必填(无障碍 + 暗色降亮依据):

![外滩夜景](./assets/my-post/bund.jpg)
<p class="fig-cap">图:外滩的傍晚</p>   ← 显式图注类(居中小字)
  • 图注可选;必须用 <p class="fig-cap">…</p>(2026-11-02 起)
  • ⚠️ 旧写法 *斜体独立段* 已不再自动当图注:此前它会把正文里所有独占成段的斜体都变成块级居中(用户实测的排版 bug),现在斜体恢复为正常行内强调
  • 压缩纪律:入仓前长边 ≤2400px(可跑 python3 scripts/photos_ingest.py,对 posts 资产同样有效的思路,图片请自行用同规格压缩)

视频 / 音频

markdown 原生不认视音频,写 HTML 标签;raw HTML 的相对路径不走 Astro 资产管线(只有 ![]() 图片语法走),所以媒体文件放 public/media/{slug}/ (构建原样拷入 dist),引用用绝对路径 /media/...:

public/media/my-post/clip.mp4      ← 文件放这里
<video controls preload="metadata" src="/media/my-post/clip.mp4"></video>
*片头三秒试拍*

<audio controls preload="metadata" src="/media/my-post/voice.mp3"></audio>
*朗读:面纱选段*
  • preload="metadata" 必写(不预拖流量,只取时长/首帧)
  • 图注警示:斜体图注行内不要裸写 <video>/<audio> 字样——markdown 会把它渲染成真实元素(黑盒+吞文字),提及时用反引号 `
  • 样式已全局处理(视频全宽圆角黑底、音频胶囊条)
  • /media/ 命名空间同时覆盖"仓内小文件"与"仓外 store 大文件"(见体积纪律表)

体积纪律(git 仓红线)

媒体 入仓上限 超限怎么办
图片 单张 ≤1MB(2400px 压缩后通常 200-800KB) 重压
音频 ≤5MB(3 分钟 128kbps ≈ 2.8MB) 降码率
视频 ≤5MB 放仓外 ~/docker_apps/blog/media/(photos-originals 同模式),文章里用绝对路径 /media/xxx.mp4;build.sh 同步段待该场景首次出现时补

历史教训:21 张原图 196M 直接入仓曾撞死推送通道(git 历史 7a2880b 重写才甩掉)。

三媒齐用的样例

见 content/posts/media-sample.md(draft 文章,仅构建验证用,不上线)。

灯箱说明文字(图库 caption)

图库照片的 caption 字段走同一套富文本管线:markdown(**加粗**、换行)+ 排版类(<span class="c-blue font-kai">)。灯箱里深底上的彩色会自动取暗底档。

图库灯箱关闭:右上 ×、Esc、点背景、点图(1× 时)都能关;缩放后移动鼠标召唤控件。

链接卡片(OG 预览卡)

一行只放一个链接、且它独立成段时,构建期会抓取该页的 OG 信息渲染成卡片(标题 / 描述 / 站点图标 / 可选缩略图):

https://astro.build/

[也可以是 markdown 链接形态](https://astro.build/)

规则与边界:

  • 内联链接不变:夹在句子里的链接保持普通样式(这是一句带 [链接](https://example.com) 的话 → 不会变卡),所以不会把整篇变成卡片墙
  • 抓取失败一律回落普通链接:404、无 OG、超时(4s)、站点拒爬都不影响构建
  • 每篇最多 10 张卡,超出的回落普通链接(防止文章变卡片流)
  • 缓存:结果存 content/.link-cache.json(入仓,保证构建确定性);条目 30 天后自动重抓。想强制刷新某条——删掉该 URL 的条目即可
  • 缩略图取 og:image,无图不占位;图标取站点而非第三方 favicon 服务

文章排版类(受限白名单)

正文里可用 <span class="…"> / <p class="…"> 做受限排版。构建期有白名单过滤:只放行下表类名,其它 class 与 style、on* 属性一律被剥离(防注入 + 保护灰阶骨架)。

灰阶层级类(全部实测亮暗对比度 ≥4.5:1)

类名 作用
ink-1 / ink-2 / ink-3 指定灰阶层(标题/正文/次要)
ink-muted 最淡灰(注释感)
ink-faint 最淡可读档(再淡即不达 AA)
ink-warm 暖调正文(书评气质)
ink-cool 冷调正文(技术气质)
ink-accent 强调(链接色,少量点缀)
ink-accent-soft 弱强调(比强调淡一档)

语义色板(8 色相 × 标准/soft;亮暗各自取值,实测全部 ≥4.5:1)

色相 标准类 soft 类 亮态对比度(标准 / soft) 暗态(标准 / soft)
蓝 c-blue c-blue-soft 6.46 / 4.58 7.70 / 4.61
青 c-teal c-teal-soft 5.00 / 4.57 9.77 / 4.52
绿 c-green c-green-soft 5.38 / 4.67 10.38 / 4.67
黄 c-yellow c-yellow-soft 6.45 / 4.57 11.73 / 4.52
橙 c-orange c-orange-soft 6.26 / 4.65 9.44 / 4.58
红 c-red c-red-soft 6.68 / 4.55 7.51 / 4.50
粉 c-pink c-pink-soft 6.96 / 4.73 8.87 / 4.52
紫 c-purple c-purple-soft 7.46 / 4.53 8.25 / 4.64
  • 黄色在亮态被压暗到 #7a5900(原色在白底只有 ~1.8:1),这是必须的:彩色文字要能读
  • soft 档是"阈值内最淡值",适合次要强调;标准档用于关键强调

使用纪律(重要):彩色只作用于文章正文的强调文字。站点外壳(导航、页脚、按钮、背景、卡片边框)一律保持灰阶——整站不因正文用色而花掉。写作时一个页面里的彩色强调建议不超过 3 处。

字体类

类名 作用 字体 许可证 加载
font-serif 书评正文族 Noto Serif SC OFL-1.1 自托管
font-kai 金句族 霞鹜文楷 Screen OFL-1.1 自托管
font-sans UI/技术族 Noto Sans SC OFL-1.1 自托管
font-mono 标签/数值 JetBrains Mono OFL-1.1 自托管
font-song 宋体族 Noto Serif SC 优先,系统宋体兜底 OFL-1.1 自托管优先
font-kai-sys 楷体族 霞鹜文楷优先,系统楷体兜底 OFL-1.1 自托管优先
font-hei-sys 黑体族 Noto Sans SC 优先,系统黑体兜底 OFL-1.1 自托管优先
font-fangsong 仿宋族 FangSong/STFangsong — ⚠️ 纯系统回退(暂无 OFL 自托管仿宋包)

文学排版类(段落级)

类名 作用
drop-cap 段落首字下沉(书评开篇)
quote-line 金句块(左侧竖线 + 文楷)
fig-cap 图注(居中小字)

首批字体(已落地,6 族 + 3 档自托管优先)

类名 字体 许可证 webfont 嵌入 用到该字体的页面增量
font-serif Noto Serif SC OFL-1.1 ✅ OFL 允许 基线内
font-sans Noto Sans SC OFL-1.1 ✅ 基线内
font-kai 霞鹜文楷 Screen OFL-1.1 ✅ 基线内
font-mono JetBrains Mono OFL-1.1 ✅ 基线内
font-huiwen 汇文明朝体(文风切片包 339 片) OFL-1.1 ✅ 535KB
font-kuaile 站酷快乐体 OFL-1.1 ✅ 163KB
font-song / font-kai-sys / font-hei-sys 上述自托管字体优先,系统字族兜底 OFL-1.1 ✅ +0KB

体积账结论(2026-11-02 实测,本地无 gzip 的相对值):未使用某字体的页面,字体字节增量恒为 0(构建期按页子集化 + unicode-range 按需拉取)。汇文采用文风开源的 cn-font-split 切片包(339 片),实测只拉用到字符的 19 片。

再裁一刀(按本站实际用字子集化):切片包本身粒度粗(一片含数百字),只做标题/短句的艺术字体往往只用到几十个字符。scripts/build-perf/subset-fonts.py 读字体 CSS 的 @font-face、收集内容里真实出现的字符、逐片求交集后用 pyftsubset 重写——实测汇文整页 694KB → 420KB(约 −40%)。命令:

python3 scripts/build-perf/subset-fonts.py \
  --css node_modules/@fontsource/huiwen-mincho/index.css \
  --out public/fonts/subset/huiwen \
  --chars-from content/posts content/moments content/site/*.yaml
# 可选:--chars-mode all|headings(只统计标题字)、--min-bytes 4096(小于该值的片不裁)

子集化产物输出到 --out 指定目录(如 public/fonts/subset/);把它接进站点(构建链挂守卫 + 字体 CSS 指向子集)是独立一步,接线后随构建自动生成,未接线前需手动跑。换字/加字后重跑一次,否则新字会回落到下一个切片或系统字体。

候补字体清单(未落地,加一款约"一条命令 + 一次体积账")

字体 许可证 允许 webfont 嵌入 未落地原因
HarmonyOS Sans 华为字体许可(免费商用) ✅ 明文允许"嵌入、捆绑、重新分发",需标注 npm 无包,官网整包 52MB
MiSans 小米字体许可(免费商用) ✅ 官方 FAQ 明确 "embedded fonts: Yes",需注明 npm 无包
OPPO Sans 免费授权(含商用) ✅(附带"不向他方提供下载渠道"限制) npm 无包
阿里巴巴普惠体 免费商用全场景 ✅ npm 无包,9 字重体积巨大
得意黑 Smiley Sans OFL-1.1 ✅ 仅 GitHub release 整包,直链在当前网络不可达
更纱黑体 Sarasa Gothic / Sarasa Mono OFL-1.1 ✅ npm 包为空壳(需 cli 自生成切片)
花园明朝 HanaMinA/B IPA Font License ✅(允许嵌入) 无 npm 包,仅兜底用
站酷高端黑 / 站酷快乐体(已落) OFL-1.1 ✅ 高端黑无 @fontsource 版本
阿里妈妈东方大楷 / 演示悠然小楷 免费商用 / 个人免费 ⚠️ 需逐款核嵌入条款 无包;"个人免费"字体常禁 @font-face
霞鹜文楷 Light/Bright OFL-1.1 ✅ 实测 +8605KB(CJK 切片粒度所致)

加新字体的流程:①核许可证与 web 嵌入条款(很多"个人免费"字体明文禁 @font-face,只能进系统回退)②npm 装或放入 public/fonts/<family>/(优选 cn-font-split 类切片包)③global.css 加类 + rehype-class-whitelist 放行 + public/admin/typography-classes.js 加一项 ④跑体积账,超 150KB/页 的要评估是否值得;展示类字体再跑一次 scripts/build-perf/subset-fonts.py 按实际用字裁切(命令见上节)。

<span class="ink-muted">次要说明</span>
<span class="font-kai">金句可以换文楷</span>
<p class="drop-cap">开篇段落,首字会下沉。</p>
<p class="quote-line">所谓暮色,就是光慢慢地承认自己要走。</p>
  • 颜色只能走上面这些类(内部用 tokens,亮暗双态都可读);写 style="color:red" 会被构建期剔除
  • 斜体就用普通 markdown *斜体*——行内强调,不会换行也不会居中

后台可视化选择(推荐,不用写 HTML)

后台编辑器里:选中文字 → Insert 菜单 → 「排版(字体/颜色)」→ 面板里点一个类,文字即被 <span class="…"> 包住;点已套用的片段可改类,Backspace 可整体删除。 面板里的两类与上表一一对应(字体 8 / 颜色 9),类清单单一事实源在 public/admin/typography-classes.js。

三层排版架构(全站,不只文章正文)

层 1 · 页面文案富文本:后台「页面文案」里的 tagline / subtitle / blurb / blurb_after / intro / 关于页 blocks 的 prose 块,全部走同一条富文本管线(markdown + raw + 内容净化 + class 白名单 + 链接卡片)。可以直接写:

tagline:
  zh: '<span class="font-kai c-blue">写作者</span> · 读者 · 快门按压者,拥抱开源。'

渲染入口唯一:src/lib/site-content.ts 的 renderRich()(行内场景传 { inline: true } 自动剥外层 <p>,否则 <p> 套 <p> 会被浏览器拆散)。 不开放:导航/页脚/按钮等硬编码界面外壳——那是 UI 不是内容。

层 2 · 页面级排版覆盖:每个页面单例(content/site/*.yaml)可加:

typography:
  font: font-kai        # 本页正文默认字体类(可选)
  accent: c-blue        # 本页强调色类(可选)
  scale: 1.1            # 字号缩放 0.8–1.25(可选)

实现是在该页内容容器上挂 .page-typo + CSS 变量(--page-font / --page-accent / --page-scale),不写全站 tokens——其它页面零影响(已在图库/书架/文章/装备页验证隔离)。白名单外的类名会被忽略。 后台入口:「页面文案 → 各自页面 → 页面排版覆盖」。

层 3 · 艺术字体:font-art-kuaile(站酷快乐体,艺术字专用类)。⚠️ 仅用于标题/短句:艺术字字形大,CJK 切片体积高(实测用到页 +163KB),长段使用会显著抬高单页字体成本。

瞬间(/moments)怎么写

瞬间与文章一样支持富文本,但入口在后台「瞬间」集合(content/moments/*.md):

---
pubDate: 2026-11-02
tags: [随手拍]
text: 今天把**置顶**做完了,顺手试了 <span class="c-teal">彩色</span>
---
  • text 支持 markdown(**加粗**、列表、> 引用)与同一套排版类(字体/颜色),因为走的是与全站文案相同的净化管线:危险标签整棵丢弃,只放行 35 项白名单类
  • 一条瞬间 = 一行时间线卡片,每条都带独立评论区(展开才挂载,双语页共用同一评论流,评论不分裂)
  • 每条的「浏览 / 评论」数字是只读统计(不会因为别人点开评论而虚增浏览)
  • text 与 image 至少填一项;图片建议先压缩

页面块(关于页 blocks,字段速查)

关于页正文可由九个块拼成(后台拖拽排序,每块有「显示」开关)。完整操作见 管理台;这里是写内容时对照的字段表——文本字段一律 {zh, en}。

块型 必填 可选 一句话
prose body title、width(narrow/full) markdown 长文
tracks title、items[].name items[].icon、items[].desc 主线/能力面
cards items[].title title、columns(2/3)、items[].desc/href/tag 并列卡片墙
timeline items[].time、items[].title title、items[].desc 时间线(时间可自由文本)
quote text cite 单句引文
figure image alt、caption 单图(必须上传,禁外链)
linklist items[].label、items[].href title、items[].note 链接清单
cta text、href、label — 行动按钮
divider — style(line/space) 分隔
blocks:
  - type: prose
    body: { zh: 正文 markdown,可含排版类 span, en: Body markdown }
  - type: cards
    columns: 3
    items:
      - { title: { zh: 写作, en: Writing }, desc: { zh: 书评与技术文, en: Reviews & tech } }

边界:块数 ≤24、单块文本 ≤4000 字(超限截断并 WARN);未知块型跳过 + WARN;blocks 为空时回落 intro markdown,再空回落内置文案。图片字段走 image(),只能后台上传进 content/site/assets/。

发布提醒

  • push main → webhook 自动构建发布(约 10 秒)
  • 新文章要英文版:跑 python3 scripts/translate/translate_posts.py(幂等,代码块不会被翻译破坏的风险见其 README 注释;锁定润色用 frontmatter locked: true)

书封图(/books 书架)

阅读轨文章可选加书封:

  1. 图片放 content/posts/assets/books/<slug>.jpg(建议宽 480px、高宽比约 2:3)
  2. 对应文章 frontmatter 加一行:cover: ./assets/books/<slug>.jpg
  3. 构建自动优化出 96/192 两档;没有 cover 的文章优雅回退现有色脊卡设计,无需改任何代码

示例 frontmatter:

---
title: "面纱"
pubDate: 2024-06-01
category: reading
bookAuthor: "毛姆"
mood: "#8a6f4a"
cover: ./assets/books/mian-sha.jpg
---

后台上传(Sveltia 后台,推荐路径)

后台地址 /admin/(gitea 账号登录)。三类媒体都有后台入口,不必手写路径:

图片

文章编辑器里插入图片 → 自动上传到 content/posts/assets/ 并写入 ./assets/... 相对路径(走 Astro 图片管线,自动优化)。不要手改成 /assets/... 绝对路径。

视频(frontmatter 字段,正文后自动渲染)

  1. 文章编辑页「附视频」字段上传(自动落 public/media/,路径写 /media/<name>.mp4)
  2. 正文读完后自动出现 <video controls preload="metadata" playsinline>,全宽圆角黑底
  3. 「视频封面」填 /media/xxx.jpg 可自定首帧;留空浏览器自取
  4. 无视频字段 → 该区块不渲染(零占位)

体积纪律:mp4/webm ≤50MB 走 public/media/(构建原样拷贝,不转码); 更大的请按上文「体积纪律」放仓外 store(该场景 build.sh 同步段待首次出现时补)。

后台列表日期

文章/瞬间列表显示「日期 · 标题」,可按日期排序;图库照片没有 frontmatter 日期 (时间在 EXIF/文件侧),列表按「分组 · 标题」展示、按标题/分组排序。

链接

markdown 原生语法,无需任何配置:

[文字](https://example.com)

想让链接显示成 OG 预览卡(带标题/描述/图标),把它单独放一行即可——规则见上文「链接卡片(OG 预览卡)」。

页面文案(主页/关于/开源)

后台「页面文案」下三个入口:

入口 文件 可改内容
主页设置 content/site/home.yaml 身份语 tagline、头像 avatar、站点图标 favicon、社交链接 socials
关于页 content/site/about.yaml markdown 正文 intro(多段/列表/粗斜体/链接)
开源页 content/site/opensource.yaml 叙述 blurb/blurb_after、仓链接、置顶项目 pinned

头像与站点图标

  • 头像:主页设置里「头像」上传 → 落 content/site/assets/,构建期优化成 60px WebP; 留空则显示内置渐变占位(w4 徽标)
  • 站点图标 favicon:先经「图标」媒体库上传(public/icons/),再把路径粘到 「站点图标」字段(如 /icons/my.ico、/icons/my.svg);留空回退 /favicon.svg。 支持 .svg / .ico / .png(直出,不经图片优化)

社交链接

主页设置里「社交链接」是列表:可增删、拖动排序。每条 = 名称(无障碍文字)+ 链接 + 图标。 图标可选 github / rss / email / bilibili / x,留空则显示纯文字按钮。 首页名片区、以及任何引用该组件的页面都吃同一份数据(单一事实源,改一次全站生效)。

首页可配项(home.yaml)

字段 取值 默认 作用
subtitle {zh, en}(markdown 短文本) 空 身份语下方的副标题;留空不渲染
latestPhotos 0-6 4 「最新影像」张数;0 = 隐藏该区块
perTrackLimit 4-8 6 首页阅读/技术两轨各显示条数
sections {tracks, photos, heatmap, projects} 布尔 全开 区块开关,关掉即不渲染
projectsLimit 1-99 6 首页项目墙条数(越界夹取)
announcement {zh, en}(markdown 富文本) 空 名片区下方公告;空 = 不渲染
postsTracks {reading, tech, photo} 布尔 全开 关掉某轨则该轨文章不在首页列出(归档页仍可见)

越界值会被夹取(如 perTrackLimit=20 按 8 处理),不报错;全部留空 = 现有形态不变。

开源页项目墙(opensource.yaml)

字段 取值 默认 作用
mode all / only / exclude all 可见性模式
include 仓短名数组 空 mode=only 时的白名单
exclude 仓短名数组 空 mode=exclude 时的黑名单
sort pinned-first / stars / updated pinned-first 排序方式
perSource both / github / gitea both 来源过滤
intro_from "off" / github-readme off 开篇文案来源

intro_from 建议带引号写:YAML 1.1 里裸 off 会被解析成布尔 false。 选 github-readme 时构建期取 wait4xx/wait4xx 的 README 渲染为开篇, 成功后缓存到 content/.github-readme.md(入仓);token 缺位/取失败/仓无 README 都会打印日志并回落 yaml 的 blurb。注意 README 里的相对链接与外部徽章会原样输出。

首页文章列表

首页现在分两段:阅读轨 6 条 + 技术轨 6 条(各自带「全部文章」出口:阅读→书架、技术→归档)。 照片轨文章不进首页(在图库看)。完整列表见 /archives/。

  • 中文/英文各一个字段,页面按语言取;留空则该字段回落默认文案(构建不会炸)
  • pinned 填仓库短名(如 blog、pi-board),顺序即展示顺序;留空回落 blog