Alias Archive 最初只是一个个人网站骨架。它没有简历、学校、所在地、经历和联系邮箱,也不准备把“我是谁”写成首页主角。它要保存的是另一类东西:正在研究的问题、可玩的实验、声音制作,以及暂时无法归入前三者的工作记录。
这条边界很早就确定了:它是匿名创作档案,不是个人履历页。 由此产生的首页、导航、内容模型和部署方案,都应围绕“作品如何被理解”而不是“作者如何被介绍”展开。
到现在,它已经不再只是四个入口。站内同时存在长文、系列研究笔记、开发日志、图片组、音频、视频、SVG 图解,以及一段从 Godot 游戏本体抽取的浏览器战斗 Demo。中、英、日三种语言共享同一套 slug 与内容关系;网站仍然输出为静态文件,并可随时换一处托管重新发布。
这篇记录的不是一次从零到一的标准建站,而是一个不断生长的档案,怎样在多轮内容交付、设计修改和技术迁移中保持同一性。
先规定“不放什么”
个人网站最容易从自我介绍开始:头像、所在地、学校、经历、联系方式。Alias Archive 刻意没有走这条路。匿名并不是临时缺少资料,而是产品边界。
最终保留下来的四个一级领域是:
- Research:问题、阅读、方法和工作笔记;
- Games:项目档案、战斗系统、素材管线和可玩实验;
- Music:作品、制作过程、编曲与声音记录;
- Other:影像、摄影、网站、AI 工作流,以及暂时不应被硬塞进前三类的内容。
这四项不是作者身份的四个标签,而是四个独立维护的档案入口。首页首先帮助访客选择路径;个人信息不参与竞争。
匿名不等于没有人格。视觉、语言、分类方式、材料选择和判断标准本身,就在持续形成作者的轮廓。
最初的技术栈为什么需要迁移
早期版本采用 Next.js 16、vinext 和面向 Cloudflare Workers 的配置,还预留了实际上没有使用的 D1。这个组合并非不能运行,但它解决的问题大于网站真实拥有的问题:站点内容主要来自本地文件,交互只集中在少数筛选、展开和 Demo 启动区域,没有登录、交易、数据库查询或服务器渲染需求。
技术复杂度开始表现为负担:
- 一个纯内容档案需要理解两套兼容层和运行时边界;
- 未使用的 Tailwind 与数据库配置增加误导;
- 部署目标与框架适配器的方向不完全一致;
- 内容和视觉才是命根子,运行时却占据了过多注意力。
因此迁移目标不是“换一个更时髦的框架”,而是让结构重新匹配问题:Astro 负责生成静态页面,React 只留在确实需要状态的局部,Cloudflare Pages 直接发布 dist。
迁移不是重做:四条零回归约束
迁移在独立的 astro-migration 分支进行,原有 main 保留为可运行对照。更重要的是,迁移依据不是最后一次提交,而是当时的实际工作树:尚未提交的字体、游戏和音乐内容同样属于现状,不能在“整洁迁移”中被意外丢掉。
工作由四条约束保护:
- 视觉原样搬运:全局 CSS、画布质感、网格、金与青的语义色不重画;
- 内容逐字保留:三语数据和各板块文案先迁移,再讨论编辑;
- 路由保持稳定:英文为默认路径,中日文使用
/zh/与/ja/前缀; - 先并行验证,再决定去留:旧版不删,新旧同时可运行。
迁移完成后,未使用的 Tailwind 和 D1 被移除;Google Fonts 中的思源宋、思源黑、EB Garamond 与 Courier Prime 继续保留,X Typewriter 以自托管 WOFF2 接入。英文界面的等宽层使用 X Typewriter;中文和日文界面中的英文片段则回到更协调的旧字体组合,避免一套“游戏打字机”吞掉所有语言的气质。
仓库成为多个 Agent 的共同接口
内容不是由一个会话从头包办。研究、游戏、音乐、影像和网站制作各自有独立 Agent 或 Claude 会话;Codex 负责整合、翻译、前端实现、测试与发布;主人决定内容边界、审阅成品并给出最终判断。
如果这些角色只靠聊天记录衔接,长期一定会丢信息。真正可复用的做法,是把仓库本身设计成接口。
每一份交付包尽量包含:
- Markdown 正文与完整 frontmatter;
- 图片、SVG、音视频的固定文件名和目标路径;
- README 中的插入位置、署名、公开边界与验收条件;
- 系列关系、关联文章和语言要求;
- 哪些内容可以改,哪些必须原样保留。
Codex 接入时不只“复制文章”,还要检查 schema、slug、素材路径、三语对应、关联关系、列表排序与移动端表现。对这个项目而言,README 不是附件,而是一种轻量 API:它把一个会话里的隐含判断,变成下一个会话可验证的输入。
任何只存在于对话、没有进入文件的约束,都会在后续会话中变成猜测。重要判断必须落进 frontmatter、README、测试或源码注释中的至少一处。
内容契约:分类、系列、关联与三语
Astro Content Collections 为文章规定统一 schema。每篇内容除了标题和日期,还包含:
category:games / research / music / other,决定主归属;tags:自由细标签;series与seriesOrder:表达连续写作顺序;related:建立文章之间的人工关系;abstract:在列表首项或悬停展开时提供比 description 更完整的概述;draft:决定是否进入公开列表;locale与共享slug:把三种语言绑定为同一条记录。
这里特意同时保留 category 和 tags。category 回答“主要属于哪个板块”,tags 回答“还涉及什么”。前者让全部日志页能够迅速辨认来源,后者保留跨领域内容的真实复杂度。
研究板块还在这一层之上建立四条系列线索。默认进入页面时显示全部记录;主动选择某条线索后才收窄列表。桌面端通过主线卡与紧凑条目建立层级,手机端则避免“点卡片后自动滚走,展开说明又留在上方”的动作冲突。
三语不是把中文机器翻译两遍。标题、abstract、正文、图内文字、界面标签都分别处理,并在构建测试中检查同 slug 的 en / zh / ja 是否齐全。英文负责生成默认路由,因此缺少英文同伴会直接影响页面存在性——这也解释了为什么本地审阅不能只看一份孤立中文文件。
视觉系统:从“有风格”到“有层级”
最初真正确定网站气质的,是一张黑底、金色制图线、纸张颗粒和四块抽象图形组成的视觉稿。它带来了档案、测绘、旧仪器和舞台手册之间的感觉。后来页面沿用这套语言:深色画布、极细网格、低饱和金、研究青、衬线标题与等宽技术标签。
问题在于,有风格不等于有系统。随着文章和板块增加,曾出现过这些情况:
- 大标题过大,小标签又过小;
- 中文和日文沿用英文间距后显得局促;
- 首页卡片、板块主卡和列表条目的体量接近,层级不清;
- 分类标签加框后基线错位;
- 移动端内部滚动区把页面滚动“锁”在顶部或底部;
- 点击研究线索后列表确实变化,但变化发生在屏幕下方,用户以为没有响应。
后来引入 Impeccable skill,并不是让它替换既有审美,而是让它把既有审美整理成可执行的界面判断:先建立 PRODUCT.md 与 DESIGN.md,明确内容层级和视觉语法,再检查响应式、可访问性、字体尺度、悬停反馈、滚动行为和状态差异。它更像一次设计系统的校准,而不是一次换肤。
最终保留了档案感,同时减少装饰性噪音:卡片不靠统一发光表达“高级”,而靠尺寸、留白、编号、边线和展开内容区分层级;移动端优先保证动作后果可见;中文与日文获得独立的标题尺度和行距;正文英文仍使用适合长时间阅读的字体,而非把 X Typewriter 铺满全文。
它没有创造 Alias Archive 的视觉母题;它帮助我们把母题变成稳定规则,并用真实页面检查那些只在移动端、长列表和多语言里出现的问题。
静态外壳不等于只能放文字
随着内容增长,静态站里出现了多种媒体:
- MV 工作流中的 SVG、filmstrip 与调色对比;
- 音乐文章中的音频和制作图;
- AI 主播文章中的视频,但不自动播放;
- Games 页中的 Godot Web Demo,访客无需安装引擎;
- 摄影与图像记录中的大图和说明文字。
原则是让媒体按需启动。视频使用 controls 和封面,不在进入页面时抢夺声音与带宽;Godot Demo 先显示网站自己的启动与加载界面,用户主动点击后才下载 Web 包;内容页面仍然是可索引、可阅读的静态 HTML。
这正是 Astro 的适配点:大部分页面在构建时完成,只有筛选、展开、媒体启动和游戏等局部需要客户端状态。静态不是功能贫乏,而是默认不运行不必要的东西。
失败比技术选型更值得保留
Alias Archive 的许多规则来自具体失败:
| 现象 | 原因 | 后来形成的规则 |
|---|---|---|
| 修改后 localhost 没变化 | 预览进程停了或仍在服务旧产物 | 构建、重启、再用浏览器检查真实路由 |
| draft 文章“消失” | 列表按公开内容过滤 | 本地审阅稿明确设为可见,发布前再确认状态 |
| 中文标题像短视频标题 | 信息被情绪化措辞包住 | 标题正式、简洁,信息放在名词和动作里 |
| 分类颜色难辨 | Research 与 Other 色相过近 | 使用不同语义色,并保持无框基线对齐 |
| 手机端点筛选无反馈 | 结果在首屏下方 | 仅在合适条件下自动滚动,并处理展开冲突 |
| 内部滚动到头后页面不动 | 嵌套滚动链被困住 | 长列表回归主页面自然流,避免独立滚轴 |
| Godot Demo 能运行但手感不对 | 用通用近似规则重写本体 | 回到本体校准素材、碰撞、韧性和输入时序 |
这些问题看似来自不同板块,实质都与“错误地近似真实状态”有关。页面不是看起来差不多就够,交互也不是能触发就算完成。
发布是一条可重复的闭环
一次改动从内容交付到线上发布,要经过同一条链路:
- 接收交付并核对公开边界;
- 接入素材、frontmatter 与三语同伴;
- 更新关联、系列和列表表现;
- 运行内容测试与生产构建;
- 在英文默认路由和中日文前缀路由上做浏览器检查;
- 检查桌面与手机尺寸;
- 主人本地审阅;
- 提交、推送,再由 Cloudflare Pages 发布。
测试不是只检查 TypeScript 是否通过。项目还检查每个公开 slug 是否有三语版本、内容统计与数据哈希是否符合预期、静态路由是否生成、关键素材是否存在。视觉问题则必须在浏览器里看;任何快照都替代不了滚动、悬停、焦点和真实字体加载。
Cloudflare Pages 与可迁移性
生产站以 Astro 的静态输出为边界:Node.js 22.13 以上环境运行 pnpm build,结果进入 dist,Cloudflare Pages 直接发布这个目录。Astro 7 的 Cloudflare adapter 面向 Workers,因此这里没有为了“看起来官方”而套不匹配的适配器。
域名使用自定义 apex,www 与 pages.dev 入口重定向到正式域名,并保留路径和查询参数。robots.txt、sitemap、404 页面和三语静态路由都随构建生成。站点没有依赖某个不可导出的数据库;将来换托管,只需在新平台发布同一份 dist 并重新指向 DNS。
这与网站的匿名边界是一致的:不建设不需要的联系系统和评论后台,不收集暂时没有用途的个人数据。博客结构已经预留,但功能只在内容需要时加入。
结语:让档案可以继续长
Alias Archive 的难点不在“把一个首页做漂亮”,而在于每次新增文章、语言、媒体或交互后,它仍然像同一个网站。做到这一点,靠的不是某一个框架或 Agent,而是几层约束同时存在:产品边界决定什么不做,内容契约决定材料怎样进入,设计系统决定界面怎样变化,测试与本地审阅决定何时可以发布。
工具会继续替换,Agent 也会继续增加。真正值得保留的是一种工作方式:把判断写进仓库,把控制权留在主人手里,让每次增长都可以检查、回退和迁移。