博客的四项导航修复
昨天用手机打开自己的博客,想看看「关于」页面——找不到入口。顶部导航消失了,页脚只有一行邮箱和一行 GitHub · RSS(这两个词甚至不是链接)。我在自己的网站上迷路了。
这很尴尬。于是一天之内修了四个跟"指路"有关的问题。记录一下。
一、文章页的「返回」按钮
博客正文页打开之后,标题上方除了分类标签什么都没有。想看其他文章?只能点浏览器后退按钮。「随心」板块反而有返回链接(← 随心时间线),它俩体验不一致。

修起来不复杂——在标题上方加一行 ← 返回博客 · 展示分析,然后让随心页的返回链接也用同一套样式。关键是做对了细节:
- 静止时用低存在感的灰色(
--soft),悬停时才变成电光青。返回链接不该抢占注意力,正文才是主角。 - 箭头用
aria-hidden包裹,可访问名只保留文字部分。data-pagefind-ignore标记不让搜索引擎把导航文字当正文索引。 - 悬停时箭头向左微移 0.35rem——跟列表页
→向右移是一个逻辑,互为镜像。
改动很小,三个文件,加了几行。但它补上了正文页最基本的"回到列表"路径。
二、系列文章——「书架」设计搭建
这是当天最大的工程。
想法
博客有时会围绕一个主题写多篇长文。比如分享一本书的解读,可能有总序、逐章分析、参考资料——它们天然有层级结构,但在传统的博客列表里只能按时间散落排列,读者看不出它们的关联。
已有的双链系统(正文里 [[某篇]] 会自动变成链接,文章底部会显示"谁链到了这篇")解决了横向跳转的问题,但还缺一层纵向结构——哪些文章属于同一个系列、谁是谁的上级。
怎么建模
最直观的办法是按文件夹组织,让 URL 跟随层级(/blog/book/chapter1/section1/)。但这会引入几个麻烦:现有文章要迁移 URL、旧的分享链接全部失效、搜索引擎要重新索引。
换了一个思路:URL 保持现状,层级关系用指针表达。
每篇文章的 frontmatter 里多两个字段:parent 指向它的上一级(存目标文章的 slug,也就是文件名),order 控制同级排序。构建时读所有文章的这两条指针,在内存里重建出一棵树。这套路跟现有的双链系统一致——构建期把所有信息算好、运行时零开销。
这样一来,随便哪篇文章都可以有两种身份:有正文就是"内容节点",有子节点就是"目录节点"。两者不互斥——一个章节可以有自己的引言正文,同时下面挂两个子节。body 为空、有子节点的就是纯目录页(只展示子封面墙,不渲染空白正文区)。
建树的几个坑
指针关系说起来简单,但数据是人在后台手填的,什么情况都可能发生:
- 上级不存在。你填了一个
parent: abc,但abc可能是草稿(没发布,不在集合里),或者干脆被删了。解法:构建时校验,指向不存在的按顶层处理,同时打一条 warning。 - 指向自身。
parent填了自己的文件名。直接降级成顶层。 - 父子成环。A 的上级是 B,B 的上级又是 A。沿链上溯,如果碰到已经见过的节点就打断这条链。内容守卫(
check-content)也会在构建前独立扫一遍,成环的话报清楚链路上每一步的 slug,不让你带着坏数据上线。 - 大小写。后台 relationship 下拉框里选的文章 slug 可能在手填时大小写不对。建表时建一个归一化映射(全小写 → 真实 id),容忍这种偏差。
同级排序用三个规则递减:order 升序 → date 升序 → id 字典序。order 不填就是 0,日期一样就按文件名。稳定可预期。
渲染层怎么用
所有逻辑在 series-util.ts(一个不碰 Astro 内容层的纯函数,输入扁平条目数组、输出一棵可查询的树),series.ts 负责从 posts 集合拉数据喂给它。[...slug].astro 里调用得到的 SeriesIndex:
文章打开时,先判断它是独立文章还是系列的一部分——看它有没有 parent、有没有 children。两种走不同布局:
独立文章:跟改造前一模一样,零变化。← 返回博客 链接、正文、右侧页内 TOC。
系列内的文章:展示的东西多了几层——
- 顶部的
← 返回博客换成面包屑(首页 › 博客 › 系列根 › 父章 › 当前),每一级可点击回跳。 - 系列根页面多一行门楣——"系列 · SERIES / N 篇 · M 章",告诉读者这里是一个合集入口。
- 正文下方如果有子节点,渲染一面"子封面墙"——跟列表页的卡片网格同款,但封面上压了 01、02 的大号编号。
- 右侧栏从"本页目录"升级成系列导航树——读者在系列中任意一页都能看到整本书的结构,点标题跳转。当前页高亮成电光青。
- 正文底部多一对"上一篇 / 下一篇"导航,按深度优先的顺读序排列。
博客列表页也改了逻辑:系列的子文章折叠进根卡,不再散落在列表里。根卡增加了"叠纸"视效——::before / ::after 两张伪元素片稍微错位,悬停时同时移动。右上角挂一个黄底黑字的角标"系列 · N 篇"。不哗众取宠,但足够让人知道"点开里面还有更多"。
边角打磨:星图里属于同一系列的文章之间多一条 50% 透明的青色连线(区别于普通引用关系的灰色边);RSS 订阅源排除纯目录节点,子文章标题前自动补上 《系列根标题》· 的书名前缀。
总结这一大坨:文章之间有了父子关系,但不需要改 URL、不需要新内容类型、不破坏现有任何页面。
三、RSS 的浏览器样式
顺手修的一个美观问题。/rss.xml 在浏览器里直接打开是裸 XML——Firefox 会友好地提示"此 XML 文件未包含关联样式信息",Chrome 直接糊一墙标签。

给 RSS 加了一个 XSL 样式表。125 行,完全按站点的杂志/电光风格来——裁切角标、Big Shoulders Display 大标题、青门楣入场动画、黄扫线、mono 编号列表。浏览器打开时不再是 XML 源码,而是一页"这里是 RSS 订阅源"的说明 + 如何订阅的指引 + 最新文章列表。
附带一个 public/_headers 文件,强制 Cloudflare Pages 把 .xsl 以 text/xsl 的 Content-Type 提供——CF 的默认 MIME 映射对 .xsl 不可靠,Firefox 拿不到正确的类型就不会套用样式,会退回到裸 XML。
四、移动端的导航缺失

就是开头说的那个问题。看代码找到了罪魁祸首:global.css 里一行 @media(max-width:860px){.navlinks{display:none}}——桌面端的「博客/随心/星图/关于」四个导航链接,在 860px 断点以下直接整组隐藏,且没有任何替代入口。
移动端的顶栏只剩头像、搜索、动效开关、主题切换和 RSS 五个小圆钮。能搜到文章,但找不到"关于"页面——页脚那行的 GitHub 和 RSS 甚至不是链接。
不做什么
没有做常规的汉堡菜单 + 侧滑抽屉。一个原因是这个站从头到尾都在用杂志/编辑设计的品牌语言——刊号、裁切角标、衬线标题、mono 标注。通用汉堡菜单太"App 了",跟站点的气质不搭。
另一个原因是汉堡菜单有个通病:打开后整个页面被遮挡,用户如果在黑暗里想切浅色模式,得先关菜单、点顶栏按钮、再重新打开菜单——多此一举。
做了什么
一个全屏的"杂志目录"层。触发是顶栏第五个圆钮(两根短线,分别青色和黄色,点击时旋转成 ×)。目录层是毛玻璃全屏面板,内容模仿杂志的目录页排版:
- 刊眉一行 mono 小字:
目录 / CONTENTS ‖ VOL.01 / 2026 - 两条扫线(青 + 黄),跟首页 loader 同款的 scaleX 入场动画
- 四行目录:mono 编号 + 衬线大标题 + 点线引导符 + 右对齐英文小标(BLOG / NOTES / STAR MAP / ABOUT)。当前页那一行标题变青、编号变黄、英文右侧多一个黄点
- 页脚收了 RSS 订阅入口(移动端顶栏的 RSS 圆钮隐藏了,空间留给目录按钮)
层级上,目录层 z-index 55,压在页面内容之上,但垫在顶栏(z-index 60)之下。这个顺序很关键——打开目录层之后,顶栏的主题切换和搜索按钮仍然可以直接点击。不会被遮挡。
交互上做了五路关闭路径:点 × 钮、按 Esc、点选任一目录链接后自动收起、视口宽度超 861px 自动关闭、View Transitions 路由切换时兜底清除。最后一条尤其重要:html 元素会挂一个 rs-lock class 锁背景滚动,如果不清理,路由跳转后页面会卡在不可滚动的状态。
页脚也顺带修了——中间那栏从无用的"GitHub · RSS"死文本改成完整的站内导航链接(博客 / 随心 / 星图 / 关于 / RSS),全端可见,算是个冗余入口。
四项改动全推上去了。Cloudflare Pages 自动部署,打开手机一看——能导航了。
这些修的不算什么大事,但加在一起,还是挺多!