一个设计师视角的 Portfolio 系统:为什么用 Notion 当 CMS,内容如何流动,视觉决定从何而来。
作为设计师,个人网站往往是最难做的一个"项目"——不是因为技术难度,而是因为它要同时承载你对自己工作的理解、对展示方式的判断,以及一套能持续维护下去的工作流。
这篇文章记录的是 diw.craft 这个站点当前的实现思路,从内容管理、卡片布局、视觉决定,到发布工作流,尽量用设计师能理解的语言来说清楚。
市面上给设计师用的 Portfolio 工具不少——Behance、Readymag、Cargo——但我最终选择自建,原因很具体:我想要对展示结构有完全的控制。
商业工具的模版逻辑是"让大多数人的作品看起来不错",但这也意味着它很难呈现不同类型内容之间的关系——项目案例、设计随笔、实验性探索、照片集,这些内容应该能放在同一个框架下,形成有结构的整体,而不是各自孤立的页面。
自建的代价是需要维护。为了让这个代价可接受,我把内容管理交给了 Notion。
我日常用 Notion 管理项目、写文档、做规划。把它作为内容数据库是很自然的延伸——在哪里思考,就在哪里写作。
站点所有内容存放在一个 Notion 数据库里,每一条记录对应一个展示单元,配置字段如下:
yami-design-system)内容从 Notion 到网站,走的是显式同步路径,而不是实时拉取:
npm run sync
这个命令执行 scripts/sync-notion.ts,做三件事:
public/content-images/{slug}/content/metadata.json(所有条目元数据)+ content/items/{slug}/body.md(正文)
同步是增量的——它会比对每个页面的 last_edited_time,跳过没有改动的内容,所以即使内容库很大,sync 也很快。同步完成后,提交到 Git,Vercel 自动拉取并部署,整个过程 2-3 分钟。
首页是一个 Bento 网格,内容以卡片形式排列。我用三种卡片样式对应不同的内容性质:
图片封面有一个细节:sync 时会分析封面图底部区域的亮度,生成一个 blur placeholder(低分辨率模糊预览),并记录底部应该用深色还是浅色 overlay——这样图片加载前,卡片就已经有正确的色调基底,避免文字和图片抢色的问题。
字体:中英文混排用了三套字体——Bitter 作为英文衬线(Variable 字重,能在标题和正文之间灵活切换)、Noto Serif SC 覆盖中文(多字重支持排版层级)、Caveat 作为签名手写字体。英文衬线 + 中文宋体的组合,在保持阅读舒适度的同时,给站点一种有质感的气质。
深色模式:通过 HTML 根元素的 .dark class 切换,用户偏好持久化到 localStorage。没有跟随系统自动切换——我认为这类站点的深浅色状态应该是用户的主动选择。
背景:全局有一个轻量的 BackgroundFx 组件,提供微妙的视觉纹理,避免纯白/纯黑背景的单调感,同时不喧宾夺主。
内容多了之后,依赖分类导航很低效。站点集成了 MiniSearch 做本地全文搜索,按 Cmd+K 触发。
搜索索引在 sync 时生成,静态 JSON 文件部署后由浏览器直接加载,不走服务器,延迟极低。搜索对象包括标题、摘要、标签和正文前 100 字,覆盖绝大多数检索意图。
SearchDialog 组件用 dynamic import 延迟加载,不影响首屏性能。
显式同步 vs 实时发布:sync 是手动触发的,这意味着在 Notion 里改完内容后,需要本地跑一次命令再提交。好处是发布过程透明可控,坏处是多了一个步骤。如果要优化,可以做一个 GitHub Action 在定时或 webhook 触发时自动 sync。
内容格式锁定在 Markdown:Notion 正文经过 blocks → Markdown 的转换,格式基本保留,但 Notion 里的某些富文本特性(比如彩色文字、toggle block)会在转换中丢失。对我来说这是可接受的取舍——Markdown 的结构性更利于渲染和维护。
图片托管在本地:封面图下载后存在项目仓库,随代码一起部署到 Vercel CDN。这避免了 Notion 图片 URL 过期的问题,但会增加仓库体积,大量高质量图片后续可能需要迁移到专用的图片服务。