和 Claude Code 一起重做个人主页:被我否掉的二十几版
想要一个让招聘方点进来眼前一亮的作品站。和 Claude Code 来回改了二十几版:首页最后回到档案卡墙,作品列表照着我找的参考站才定下来。记录参考、我说过的"不"、做法和踩过的坑。
我在找前端的工作,想要一个点进来就让人眼前一亮的个人主页,从设计上就能看出审美。站点用 Nuxt 写,内容是我在 X、抖音、linux.do 上刷到的界面和动效:每个复刻一遍,写成一篇带演示和复现提示词的文章。现在已经上线:lab.lzwcc.xyz ↗。
第一版首页是 Codex 做的:大字标题、像素头像、可以拖动的文字、结构查看、入场重播,东西很多。我看了觉得审美太低,整个推翻。这次换 Claude Code 重做,过程中我说了很多次”不”。
三个参考站#
- adixcode.com ↗:bento 卡片墙。每张卡片只讲一件事,而且画成一个小物件:简历是一张纸,经历是
git log --career终端,音乐是一张唱片。很多是真实数据:当地时间、GitHub 贡献、最近在听的歌。 - samuelrizzon.dev ↗:整站一个比喻,“用方块搭出来”。他的建站记录 ↗是这篇的参考,后半程我照着他的办法来。
- imdaryl.com ↗:作品集是一张设计图纸。开场先在蓝图上画出带尺寸标注的线框,再一条横线扫过,揭成白色的成品页;成品页上还留着标尺。
三个站的共同点:都不靠大字号;页面里每一块都有真东西,没有只为好看的装饰。
首页:被否掉的版本#
| 版本 | 做了什么 | 我的反馈 |
|---|---|---|
| 1. 三个比喻的静态稿 | 标本馆、实验记录本、剪贴簿,各做一个首页 | 都太平淡,抓不住眼球 |
| 2. 放大标题 | 标题放到 120px,加暗色展厅、可拖动的缓动曲线 | 靠字号抓人反而不好看,参考站都没有大字 |
| 3. 采集盒第一版 | bento 布局,每格塞一个缩小版的演示 | 卡片太简陋,“这样展示还不如不展示” |
| 4. 仿 adixcode 的卡片 | 等距视角的技术栈、唱片、拍立得,大圆角卡片 | 和 adixcode 太像,卡片太大,信息密度低 |
| 5. 收录颗粒渐变封面时 | 自作主张加了漂移、颗粒滑杆、悬停柔光 | 原作都没有,全部删掉 |
| 6. 档案卡 bento 墙 | 12 栏格子,卡片做成档案卡(编号索引条、宋体中文名);开场是像素头像聚合,再一圈波纹依次点亮卡片 | 一度很满意。看了 Samuel 的记录发现,和他让 AI 随手出的那套 Bento 首页很像;“采集频率""生活标本”这类卡片名也别扭。后来又回到了这一版 |
| 7. 两个首屏 | 只做首屏:“这一页由我收藏的效果搭成”和”刷到” | 太平淡;只看首屏我判断不了 |
| 8. 由收藏搭成(整页) | 每个部件都是一件收藏在运行,页尾清单和部件互相点亮 | 信息密度太低 |
| 9. 动态放进手机里 | 页面中间立一部手机,两边的模块用标注线连到手机 | 太刻意,不明所以 |
| 10. 三栏信息流的三个变体 | 各改一处:置顶求职帖;参数标签和操作栏;大号可戳头像 | 在玩、在读不能混进信息流也不能缩小;标签没必要;小圆头像就好 |
| 11. 资料头 | 宽横幅、资料和”现在”区占一整条,下面两栏 | 顶部占太多空间,彩色横幅没有意义 |
| 12. 顶部数据带 | 资料、AI 用量、GitHub 排成一排 | 这三块太大,不如之前精致 |
一些原话:
感觉字更大完全没必要啊,你太过于通过文字的大小抓人了。vibecoding 人人都会做,关键在于怎么把东西做好。
卡片太简陋了,就是根据文章里的实际内容简单编写出来的组件。
AI 产出的东西太同质化了,一眼就能看出 AI 味。
你现在在中间塞个手机屏幕,完全不明所以,有点太刻意了。
照 Samuel 的办法换思路#
第 6 版被否以后,我回去重读了 Samuel 的记录。AI 一次给了他四套完整的首页,他一套都没选:挑一套是最省事的做法,网站是因为他一直说”不”,直到像他自己会做的东西,才有了自己的样子。
他的做法我照搬了几条:
- 先说出只属于自己的那句话。我的是”刷到让我停下来的界面,就拆开重做一遍”。
- 要选项,再挑。每次让 Claude 出几个方向,我看完说哪里满意、哪里不满意。
- 被否的也留着。每一版都提交成存档,喜欢的打标签,改坏了一条命令回去。
- 先给参考。这一条我一开始没做,到作品列表页才吃到亏,见下文。
有一条不适用:他先把范围缩到只做首屏,我只看首屏判断不了,要做成整页才看得出好坏。文字描述也不行,方案得直接做成页面给我挑。
最后回到档案卡墙#
换思路以后做出了第 13 版:像推特的三栏信息流,左边资料、中间动态、右边数据,复刻的效果是一条条能直接上手的动态。我很喜欢,存档打了标签。但定首页时我想清楚了:这个站是作品展示,档案卡墙一屏就能看全”我是谁、做过什么、在用什么”,放在这里没问题;三栏信息流更像博客首页,单独留着。
回到档案卡墙后只改了卡片名,换成直白的说法:关于我、AI 用量、作品、工具、提交记录、生活、在听、在玩 · 在读。Claude 提过统一成”在找工作、在烧 token、在复刻……”,我觉得太模板。
各个格子的内容都是真实数据或实物:
| 格子 | 内容 |
|---|---|
| AI 用量 | 自己写的 token-stats 汇总的 Claude Code 与 Codex 用量:总量、各模型占比的环形图、近 30 天折线 |
| 提交记录 | GitHub 一年的贡献热力图,悬停显示日期和次数 |
| 工具 | 五层等距视角的薄片对应五类技术,右边是彩色 logo |
| 作品 | 左边大封面,右边列表,每 4.5 秒切到下一件 |
| 在听 | 孙燕姿《雨天》,封面做成唱片,点播放会放 Apple Music 的 30 秒试听 |
| 在玩 · 在读 | 照实拍照片画的 Switch 卡带;《动物农场》是一本能看到书脊的 3D 书 |
| 生活 | 三张错落的拍立得,悬停的那张抽到最上面摆正 |
三栏信息流版没有复制这些卡片的代码:卡片外壳读一个 inject("draftsBare"),新页面 provide("draftsBare", true) 以后,外壳只渲染正文,原来的卡片内容直接放进新布局。所有颜色都是 CSS 变量,浅色、深色各一套;卡带、书、唱片这些实物保持本色。
卡片左上角的编号用 CSS 计数器按显示位置数,从左到右、从上到下是 01–12,挪动卡片不用改编号。
手机版#
640px 以下做了两版给我挑:A 把桌面的格子压缩重排成一列;B 只把重点放在首屏(关于我、四张数字小卡、作品),其余六张卡分成两组横向滑动,一次一张,像相册一样停在中间。我选了 B。
为了按手机的先后顺序排,HTML 里卡片的顺序改成了手机的顺序,桌面和平板用 order 排回原位,两组横滑的外层是 display: contents,在桌面上不占位置。改完逐张核对过桌面和平板下卡片的位置和尺寸,和原来一致。
开场在手机上也要单独处理:卡片都挤在头像附近,按桌面的速度波纹 0.15 秒就扫完了,看不出先后。手机上波纹放慢到 0.9px/ms,第一屏的卡片跟着波纹亮,屏幕外的等滑到时再浮上来。
其他页面跟着统一#
首页定了以后,列表页和文章页还是早期的米白底、陶土橙、衬线大标题,一点进去就是另一个站。
- 顶栏和页脚抽成两个组件,所有页面共用。颜色都走首页那套变量,再把 Nuxt UI 的
--ui-bg、--ui-primary指过去,组件库自带的控件也变成同一种蓝。 - 文章页整篇是一张大档案卡。正文里的演示要套一个”Demo”外框,但各个演示组件的根元素写法不统一,CSS 套不上。做法是用
import.meta.glob找出components/content/下的组件,各包一层外框,再通过ContentRenderer的components参数替换原来的组件。文章照常写::grain-gradient-cover,Markdown 一行不用改。 - 文章页在宽屏右侧加了一栏:目录和阅读进度、作品档案(来源、原作者、技术),以及复制提示词的按钮。窄屏放不下侧栏,读过标题区以后底部浮出一个目录胶囊,显示当前小节和已读百分比,点开是目录。
- 空的关于页、占位的项目页删掉,
/ui、/animation两个列表合成一个/works,旧地址重定向过去。后来自己做的项目改成作品里的一类:第一件是 MaintPilot ↗,用同一个文章页,侧栏多了在线地址和合作情况;编号和效果分开,效果 001 起,项目 P01 起。
作品列表又否了十几版#
当时只有 4 件作品,列表页反而最难做:
| 版本 | 我的反馈 |
|---|---|
| 3 列卡片网格 | 太平淡 |
| 封面换成正在运行的演示(只是提议) | 完全没必要,写出来的效果很简陋 |
| 左边目录,右边吸顶预览,预览里放提示词的开头几行 | 左边像标准表格组件;列表里不需要写”刷到自 X”这样的来源 |
| 把参数画成图:缓动曲线、波纹半径、色板、尺寸标注 | 普通的卡片陈列,无聊 |
| 复刻日志:十月日期刻度,下面竖向时间线 | 相对满意,但模板化 |
| 只放大标题,封面跟着鼠标浮出 | 太简单,完全没有感觉 |
| 剪辑时间轴:作品、每天提交数、每天 AI 用量三条轨 | 丑 |
| 两张月历,格子按提交数着色,做完作品那天放封面 | 太占地方 |
| 印样和杂志跨页:每件作品截 4 帧 | 不细看就像在重复同一个内容 |
连否七八版以后,我找了六个自己觉得好看的列表页:innei.in ↗、simedw.com ↗、railcode.dev ↗、zla.pub ↗、blog.ichr.me ↗、axi404.top ↗。Claude 从里面总结出:主体都是文字列表,不是图片网格;每条的元信息很多;只在一处有特点;内容少的时候用置顶和侧栏撑满。它照着做了四版,我挑了仿 innei 和仿 axi 的两版,合成现在的样子:
- 最新一件置顶,封面完整清晰;
- 下面是横条卡片,右半边淡淡透出封面,顶上一行写日期、编号、分类、阅读时长;
- 侧栏是合集、标签、技术,带数量,点一下筛选。
阅读时长按正文字数估,每分钟 400 字,不算代码块,因为提示词是拿来复制的。
回头看,前面那些版本的问题不在排法:作品少、截图素材又平,换排版救不了。我说”平淡""模板化”的时候,Claude 只能猜反面是什么;给了参考,一轮就对了。
页面动效只要浮上来#
作品列表定了以后,又给它加进场动效,也否了三版:
| 版本 | 我的反馈 |
|---|---|
| 首页作品卡的封面”飞”进作品页 | 十分简陋 |
| 照 imdaryl 做蓝图开场:先画线框和尺寸标注,再揭成成品 | 基本上是完全照抄 |
| 同样的过程,换成本站配色的骨架 | 不是我要的 |
我要的其实很简单:内容进入视口时从下方浮上来。现在是一个 v-reveal 指令,淡入 0.6 秒、上移 18px 用 0.7 秒,同一批进入视口的元素按从上到下、从左到右错开 70ms,最多错开到第 8 个;开了减弱动效就直接显示。服务端渲染时元素已经带着隐藏的类,不会先闪一下再消失。站内换页用 View Transitions:旧页面 120ms 淡出,新页面 180ms 淡入。
借参考只借过程,外观和手法要用自己的。
数据为什么做成快照#
我的 Mac 连不上 raw.githubusercontent.com,国内访客多半也连不上。所以网页不在运行时请求这些地址,而是用一个只依赖 Python 标准库的脚本,把数据写成 JSON,构建时直接读。网站是静态导出的,发布脚本只导出已经提交的代码,在导出的副本里先跑一遍同步脚本再构建,所以线上的数据是发布当天的;仓库里的 JSON 是旧快照,同步失败时就用它。各项数据的来源:
- token 用量:浅克隆
token-stats-data仓库,汇总每天的文件。汇总出的数和 token-stats 自己生成的卡片逐项对过,一致。 - GitHub 贡献:公开的 contributions 接口。
- 仓库数、文章数:GitHub API,以及直接数博客仓库里不是草稿的文章。
音乐试听是点播放时才去 Apple 取的,我在国内网络下测过能访问。
让 AI 先自己看效果#
Claude Code 跑在一台远程机器上,代码在我的 Mac 上,它没有浏览器。前几版都是我打开页面才发现问题。后来让它写脚本遥控 Mac 上的 Edge,改完先自己截图核对、量数据,再交给我:外观有没有变、深浅色和手机宽度对不对,以及开场卡顿、线上第一次打开慢的原因,都是这样查出来的。脚本后来整理成了一个 skill,用法和查出的问题写在让 Claude Code 自己看网页。
开场卡顿查出两处:深浅色按钮的图标在浏览器端才渲染,插入样式表时整页重算样式;卡片淡入用了 blur(),没法交给合成线程。按钮改成服务端渲染、淡入只动透明度和缩放以后,我觉得比原来顺。
动画的手感,还是要我自己在浏览器里看。
踩过的坑#
| 现象 | 原因 | 解决 |
|---|---|---|
| 三张拍立得缩成一条竖线 | 容器类名叫 .table,撞上了 Tailwind 的 table 工具类 | 改名;同理 .ring 改成 .ripple-ring |
| 深色模式下整页被反色 | Vue 作用域样式里写了 :global(.dark) .logo { … },:global() 会把整条选择器换成 .dark | 直接写 .dark .logo |
| 画出来的 Switch 卡带像 SD 卡 | 凭印象画 | 先找实拍照片再画 |
| 颗粒渐变上看到一圈圈的环 | 径向渐变色标太少,衰减速度突变 | 用十几个色标按 (1 − t²)² 的曲线逼近 |
地址加 ?theme=dark 不生效 | 颜色模式插件初始化时会用本地存储里的偏好覆盖 | 延迟 100ms 再设置 colorMode.preference |
| 索引条里英文编号和中文名对不齐 | 等宽小字和宋体大字按盒子居中,中文往下沉 | align-items: baseline,左边的小方块单独 align-self: center |
| 手机宽度下卡片比屏幕宽 | grid-template-columns: 1fr 的最小宽度是内容宽,不换行的索引条把格子撑开 | 改成 minmax(0, 1fr) |
| 新建截图页时覆盖了已有的同名文件 | 写文件前没看目标路径 | 从 git 恢复;之后新建文件前先确认路径不存在 |
附录:文件位置#
| 路径 | 内容 |
|---|---|
app/pages/index.vue | 首页档案卡墙 |
app/components/home/ | 首页的各张卡片 |
app/pages/works.vue | 作品列表 |
app/components/WorkArticle.vue | 文章页,演示外框,窄屏目录胶囊 |
app/components/ArticleToc.vue | 文章页侧栏的目录和阅读进度 |
app/components/SiteBar.vue、SiteFoot.vue | 全站顶栏、页脚 |
app/composables/useWorks.ts | 作品数据:效果(ui、animation)和项目合并,按日期编号 |
app/components/BentoCard.vue | 档案卡外壳,draftsBare 时只渲染正文 |
app/plugins/reveal.ts | v-reveal 浮入动效 |
app/assets/css/home.css | 深浅两套颜色变量 |
标签 drafts-archive | 被否掉的各版,原来在 app/pages/drafts/:三栏信息流 feed.vue(另有标签 homepage-feed-v1),作品列表 works-*.vue |
scripts/sync-profile-data.py | 同步数据,写到 app/data/*.json |
scripts/deploy.sh | 导出已提交的代码、同步数据、构建、上传到服务器 |