翻新博客 + 加书架

2026-05-22T00:00:00-05:00 | 5分钟阅读

@

起因

第一章搭好的博客是一个"扁平流"——所有文章都在 content/posts/ 下,按时间倒序排列。这种结构很适合写日记或杂记,但凡是想要写长篇连载(比如复健笔记、菜谱、相机使用说明书)就会发现:

  • 同一系列的文章被时间打散
  • 读者点进单篇看不到上下文,不知道还有别的章节
  • 写作者自己也容易忘了之前写到哪

我想加一个书架——和"杂记"并列的另一个内容类型,每本书内部有章、节两层结构。

整体方案

Claude 看完 dream 主题的源码之后给出的方案:

Hugo 自带 page bundle 结构能完美表达"书 → 章 → 节"三层关系。书是一个 branch bundle(带 _index.md 的目录),章是嵌套在书目录下的 branch bundle,节是章目录下的普通 .md 文件。

具体的内容目录:

content/books/
├── _index.md                  (书架根,渲染时跳过)
├── rehab/                     (一本书:复健笔记)
│   ├── _index.md
│   ├── chapter-01/
│   │   ├── _index.md          (章简介)
│   │   ├── section-01.md
│   │   └── section-02.md
│   └── chapter-02/
└── blog/                      (也可以"平结构"——章本身就是节)
    ├── _index.md
    ├── chapter-01.md          (没有节,章就是叶节点)
    └── chapter-02.md

dream 主题本身没有书架概念,所以需要写项目级 layout 覆盖。

自定义的 layouts

layouts/ 下面新建了一堆文件,每一个都覆盖主题的同路径文件。Hugo 的查找顺序是项目级优先于主题,所以放对位置就生效。

文件干嘛的
layouts/books/list.html按 URL 深度分支:/books/ 渲染书架卡片、/books/<book>/ 渲染书目录、/books/<book>/<chapter>/ 渲染章节首页
layouts/books/single.html节(具体文章)页面:左侧 sticky 目录 + 中间正文 + 右侧 H2/H3 滚动目录
layouts/partials/book-toc.html侧栏目录组件,递归渲染章节
layouts/partials/article-meta.html统一的文章 meta 块(发表 / 更新 / 阅读时长 + 作者 / 分享)
layouts/partials/share.html主题原版的扩展,加了复制链接按钮,HTTP fallback 也实现了
layouts/partials/nav.html顶部 nav 改:home 图标 + 关于改图标 + tooltip
layouts/_default/single.html + zen-summary.html单篇文章和首页摘要的小调整
layouts/section/posts.html + search.html归档和搜索:合并杂记 + 书章节

每个文件平均 50-200 行 Hugo 模板。让 Claude 写 + 我看着改,几乎都是一遍过。

自定义 CSS 和 JS

主题用的是预编译的 Tailwind CSS(output.css),所以我自己加的工具类(lg:max-h-[...] 这种)根本不在 output 里。一开始踩了这个坑:脚本里加了 lg:sticky 之类的类名,浏览器里完全无效。

Claude 的解决方案是:

不依赖 Tailwind,自己写一个 static/css/book.css,在 hugo.toml[params.advanced] 里通过 customCSS 加载。所有书架相关的样式(侧栏粘性、归档时间线、卡片样式、暗色模式微调、tooltip)都写进这一个文件。

book.css 最终大概 500 行,覆盖:

  • 书侧栏 sticky 行为 + 暗色模式背景调整
  • 首页书卡片 grid 布局 + 帖子卡片
  • /posts/ 归档按"年 → 月"两层分组
  • nav 桌面悬浮 tooltip(::after + attr(title)
  • Waline 评论框布局微调(这一节后面会讲)
  • Mastodon 评论区样式

JS 只写了一个:static/js/book-scrollspy.js。节阅读时右侧的 TOC 会跟随滚动高亮当前 H2/H3,用 IntersectionObserver 实现,约 60 行。

顶部 nav:sticky → fixed

dream 主题的 <nav> 用的是 position: sticky。本地预览没问题,但部署到线上后我发现往下滚几下 nav 就消失了。Claude 排查后说:

主题的 .flip-container 有一个写死的 height: calc(100vh - 80px - 2rem),作为 sticky 元素的祖先打破了 sticky 的滚动上下文。靠谱的修复是把 nav 改成 position: fixed,然后给 body 加一个 padding-top 补上。

最终 CSS:

body > nav {
  position: fixed !important;
  top: 0; left: 0; right: 0;
  z-index: 40;
  background-color: oklch(var(--b1));
  border-bottom: 1px solid oklch(var(--bc) / 0.12);
}
body { padding-top: 5rem; }
@media (min-width: 1024px) {
  body { padding-top: 6rem; }
}

颜色闪烁问题

日间模式下,每次切换页面会先黑一闪再变白。Claude 一眼看出问题:

系统在暗色模式时,浏览器在 JS 加载完之前默认背景是黑色,然后 Alpine.js(defer 加载)才把网页切到 emerald(亮色),出现"黑一下→白"的闪烁。修法是在 <head> 顶部加一段同步执行的 inline script,比 Alpine 早一步把 class 和 data-theme 设到 <html> 上。

覆盖 layouts/partials/head.html,在文件开头加:

<script>
  (function() {
    var pref = localStorage.getItem('hugo-theme-dream-is-dark');
    var prefersDark = matchMedia('(prefers-color-scheme: dark)').matches;
    var isDark = pref === 'y' || (pref !== 'n' && prefersDark);
    document.documentElement.classList.add(isDark ? 'dark' : 'light');
    document.documentElement.setAttribute('data-theme', isDark ? 'forest' : 'emerald');
    document.documentElement.style.backgroundColor = isDark ? '#1a1a1a' : '#ffffff';
  })();
</script>

这段同步执行,挡在 paint 之前完成,黑闪消失了。

写作辅助脚本

最后让 Claude 帮我加了两个 .command 脚本(双击执行):

脚本用途
2_new_book_section.command交互式选书、选章、新建节,自动生成 frontmatter,自动算 weight = max + 1,建好同名图片目录
5_book_images.command把书节配套图片目录里的原图压缩成 webp、按 <section>_pNN.webp 重命名、上传到 S3 图床、打印 centered-image shortcode 复制粘贴

加上之前已有的 1_new_post.command(新建杂记)、3_hugo_server.command(本地预览)、4_post_images.command(杂记图片)、6_deploy.command(部署),现在写作流程一脚本一动作,不用记 Hugo 命令。

我自己需要做的事

虽然代码大部分 Claude 写,下面这些我得亲自做:

  • 在 hugo server 本地预览每个改动,盯 layout 排版(暗色模式卡片背景偏黑、字号层级、行距、章节编号 0. 的怪 bug 都是边看边告诉 Claude 调)
  • 把已有的相机说明书、复健草稿手动整理成书的 章/节 结构(Claude 写脚本,我自己迁内容)
  • 写几本书的开头介绍(_index.md 里的"为什么这本书"那段)
  • 移动文件夹位置(从 Hugo_Blog/blog_main 搬到 GitHub/blog_main 统一管理,下一节讲)

现在的样子

书架功能上线后的首页是"杂记 + 书架"两栏。杂记是单篇文章流,书架是 5 本连载(复健笔记 / 相机使用说明书 / 数据化生活 / 搭建静态博客 / 菜谱)。点开书后左侧固定目录、右侧滚动高亮、章节内部上下页导航能跨章。整个改造分了几十次小迭代,每次一两个 prompt + 几行代码。

© 2025 - 2026 Leona的田园牧歌

🌱 Powered by Hugo with theme Dream.

关于我

Leona

  • 女的。
  • INFJ。
  • 长毛象链接点这里!
  • 做了八年数据科学家,但是每次管自己叫”科学家“,都很羞愧。职业+J人,习惯把日常生活数据化。
  • 玩了十几年相机,三十岁以前拍人像擅长无痕修图,三十岁以后拍动物植物大自然。
  • 厌倦城市,旅行目标大多是看动物。
  • 嘴太叼,不好吃宁愿饿着,还好手艺不错,不会饿死自己。
  • 没体能天赋,从小长跑最后一名,但是热爱运动,练过跑步、举铁、泰拳、网球、crossfit,现在重心转向功能性训练。
  • 现阶段人生目标是找个山头归隐,自给自足,田园牧歌,种菜养花,遛猫逗狗,养鸡喂牛。