layout.tsx§
- 用
next/font/local加载 JetBrains Mono,挂到 CSS 变量--font-jetbrains - 导出全站
metadata(默认标题模板"%s · 墨栈")与viewport(浅色/深色地址栏颜色) - 结构:
<Providers>→SiteHeader→{children}→SiteFooter <html>上suppressHydrationWarning:主题脚本可能在水合前改data-theme,避免 React 报属性不一致
页眉导航含「文章 / 系列 / 摄影 / 标签」;Logo 回首页,文章 指向 /posts。小屏为抽屉菜单(焦点陷阱 + 打开时 main/footer inert),大屏为横向导航。usePathname 用路径前缀决定高亮。
providers.tsx§
客户端组件,仅包一层 ThemeProvider:attribute="data-theme"、defaultTheme="system"、本地键名 inkstack-theme。真正的颜色切换靠 app/styles/tokens.css 里 :root 与 :root[data-theme="light"] 两套变量。
page.tsx(首页)§
服务端组件,首页只突出最新文章与摄影:
- 全幅 Hero:最新摄影作品作背景;品牌、标语、CTA;右下角署名链到该作品
HomeReadingStrip叠在 Hero 顶部(本地进度最高的一篇散篇或系列章;无进度则不渲染)getAllPosts()→toMeta,取最近若干篇:第一篇PostCard featured,其余列表;「全部文章 →」链到/posts- 摄影马赛克:排除 Hero 已用作品后,精选优先再按日期取若干张;「发现全部 →」链到
/photos - 系列与标签不在首页堆砌,从导航进入
posts/page.tsx(文章归档)§
散篇时间线:全部 PostCard 双列展示,供首页「全部文章」与导航「文章」使用。
posts/[slug]/page.tsx(散篇文章页)§
关键配置:
export const dynamicParams = false;
export function generateStaticParams() {
return getAllPosts().map((p) => ({ slug: p.slug }));
}dynamicParams = false 表示:未出现在静态列表里的路径直接 404,不会在运行时再去读盘。
页面骨架(两栏):
- 主栏:返回、刊号与标题、
<MdxContent />、上一篇/下一篇 - 右侧大屏:
ReadingPathRail;小屏:ReadingPathFab ProgressBar+ArticleShell(进度键 = 文章路径名)
tags/page.tsx 与 tags/[tag]/page.tsx§
标签总览用条形长度表示相对篇数;单标签页同样 generateStaticParams,无文章则 notFound()。列表复用 PostCard。标签目前聚合自散篇的 tags 字段。
系列版块:三层路由§
| 路径 | 文件 | 作用 |
|---|---|---|
/series | app/series/page.tsx | 全部小册列表(SeriesCard) |
/series/[series] | app/series/[series]/page.tsx | 小册封面、章目录(SeriesToc)、从第一章开始 |
/series/[series]/[chapter] | …/[chapter]/page.tsx | 章节正文:三栏阅读台 |
章节静态参数展开「每一册 × 每一章」:
export function generateStaticParams() {
return getAllSeries().flatMap((s) =>
s.chapters.map((c) => ({ series: s.slug, chapter: c.slug }))
);
}章节页布局([chapter]/page.tsx)§
大屏(约 ≥1080px)三栏:
- 左
SeriesChapterRail——本册目录、册内进度、回小册首页 / 全部系列 - 中——面包屑、章标题、
MdxContent、ChapterPager(上一章 / 下一章) - 右
ReadingPathRail——本章标题目录(与散篇同组件)
窄屏隐藏左右栏:顶部 SeriesChapterStrip 横向切章;本章目录仍用 ReadingPathFab。
ArticleShell 传入的是 chapter.progressKey(形如 series/inkstack-source/01-orientation),与散篇进度隔离。
sticky 实现要点§
- Grid
align-items: stretch(默认),左右aside与正文同高 position: sticky写在 aside 内部的.series-chapter-sticky- 切勿在拉伸侧栏上再设
align-self: start后把 sticky 只放在塌缩后的盒子里——否则没有吸附行程
样式见 app/styles/site.css 中 .series-chapter-frame、.series-chapter-sticky、.series-rail*、.series-strip*。
内容模型与 lib/series API 的完整说明见专章「系列小册:内容模型与版块设计」;本章只固定页面层怎么挂。
not-found.tsx§
站点级 404 页,文案与视觉跟刊物气质一致即可,逻辑很薄。
下一章进入客户端最重的一块:MDX 标签映射与代码块组件(散篇与系列章节共用)。