#91 建立 Design Fragments - Vibe Coding 实践
仅需 21600 分钟,用 AI 建立网站!点击查看。

Design Scenes Weekly 停更在 900 多天前的 AI 潮流前夕(22年11月),之后我一直在想如何以其他形式呈现搜集到的这些信息。Tana 的分享功能薄弱,Craft 的不作为,许多在线服务起起伏伏,让我觉得数据还是应该掌握在自己手中。
代码能力越来越强的 LLM 让我觉得时机已经到来。经过两个 Figma 插件的 vibe coding 过程熟悉后,我决定自己托管这些数据。

TL;DR
Design Fragments 是 Design Scenes Weekly 的精神续作,它依然展示的是我每个月搜集的有趣信息。支持 RSS 和 Newsletter 订阅(每周日更新),以及网站的直接浏览和搜索。
搜集的内容不只是设计,也有科学新闻和整活项目等等。我根据多年的搜集经验分为了很多类别,你可以在搜索页查看。没有 cookie 没有收藏没有登录,自己的信息自己保管。
它的幕后是 Astro + Supabase + Netlify。技术逻辑上很简单,所以下面主要谈下 Fragments 的设计灵感与符号,以及 vibe coding 数月后的感想。想来我既不是才华横溢的设计师,也不是经纶满腹的开发者。但是对开发者分享一些设计经验,对设计师分享一些开发感触,内心还是蠢蠢欲动。
设计实践玩笑
Design Fragments 的主设计语言是「自由」。嗯。
很多人喜欢极简,我也是。向上可追溯至「形式追随功能」,从建筑、家具到瑞士国际主义风格的渲染,界面设计脱胎于平面设计,形成了集人类行为和心理观察的独特范式。但在饱受工业化的冲洗过程中,失去了太多颜色。而极简风格又恰巧迎合了无奈的效率至上主义。

有时你可能会感叹一些界面的清新简洁,而这背后并非总是某把剃刀在剪裁,可能单纯是没时间做太多思考,甚至懒惰。作为一项反思计划,我不想让 Design Fragments 成为 LLM 眼中形容词堆砌的玩物,也不想让它网格约束太多。但代码语言的设计无法其实让界面设计独善其身,也算是一种痛苦中前行。
Design Fragments 项目前身是,我想做一个界面用于展示一些优雅的字体,有着「源文件级别的」的丰富参数展示和筛选操作。

但是像类似 Fonts Ninja 网站的存在让我暂时搁置了计划,毕竟 Tana 中还有那么条目静悄悄地躺在那里。但我依然保留了脑中的「具象概念」——书籍、纸片——通常来说,这是日常界面设计中所不必拥有的, Design System 会排斥掉很多想法。

拓展为浅色模式,首页双栏布局,变成下面这种结构。上方是样式,下方是样式布局的拆解。

纸张上面要放信息,排版一定要适合快速浏览。快速浏览即代表结构的稳定,遵从最基本左到右阅读习惯,结构清晰,这个时候就不便放飞自我了。

虽说是一本书,但排版的基础并非栅格,而依然是自由的布局。我很喜欢一页文字一页图片的感觉,所以在右侧试着放了一张图占位图,感觉还不错。
但这里面还有很多问题,比如无用的虚线让布局徒增复杂,内容宽度在一些屏幕上过小。需要再次妥协——况且我也不是很喜欢左侧这个布局。

这次让左侧占满宽度,右侧则更自由地放一些图片展示。导航栏也增加了收起右侧页面的图标。但在改进的过程中,它其实越来越不像一本书了。包括渐变效果的削减、背景明度的提升。
更致命的是,我做好了后台可以设置右侧每张图片的定位、角度和大小时,自适应后没法达到我预想的效果。以小屏幕优化,转到大屏幕上则显得空旷;反之则又拥挤重叠。我引入了一个自适应的缩放系数,这让布局系统更加难以控制,还有一些缩放上的问题……
此时距离第一次提交 commit 已经过去一个月。虽然期间一直在调试左侧页面的布局,但事到如今右侧布局的删除,影响的是 “root” 层级的具象概念——当它不用局限在「书」中时,所有与书的灵感思路其实都走到了尽头。
细节上我略去了很多,这是一段艰难的过程。之所以把废弃概念讲出来,其实是想说设计只重视开头结果的话,其实很容易陷入虚无主义的漩涡。竹篮打水一场空在旁人看来你仿佛什么都没做,因为不会有人去翻找你在 Figma 中的层层草稿。
如果是实际的项目,那么可能连一开始尝试的机会都没有——我觉得可以这么安慰自己。哦,后台都省去了,网站也更安全了。
设计符号抽离
再次回到 Figma 埋头苦干。我需要一套新的具象概念来支撑起网站的设计。现在是「功能性溢出」的年代,我想继续尝试形式追随形式。殊途同归,但是意义上还是不一样的。
我看着之前的设计稿,既然它不再是书了,那该是什么呢?背景色和阴影……可能更像一堵墙。
每次在逛展览时,我都会疑问,同样都是垂直的空间,白底黑字印在墙上和屏幕上,有着天然而不可逾越的差距。很多展览同样是网格的构筑,但在空间的「气氛」中——或者说作为呈现展览的「机械」——实现了材质上的升华。
反观界面设计则是理性的集合,数据驱动的读心机械。它以去个人化的结构,作为物体功能的媒介提供价值。往好处说,它确实提供了价值;但另一面,设计师亲自设计出来的矩形样式又不是自己的,而像是从集体意识中借用的。即便使用了昂贵的设计素材,有着非常新鲜的潮流感,但如果背后表达的「具象概念」一片虚无的话,终究不是自己的。
所以我试图这样诠释「形式追随功能」:
形式追随表达,表达追随功能
这点上,我很喜欢 mmm.page 这个产品。
回到「墙」的话题,究其本质是对材质的渴望。所以他既可以是 plane,也可以是 backdrop。我在 Wall* 页面设计了如下样式:

这幅《Sich putzende Katze》来自第 111 期提到的 Gottfried Mind 的文章。这里在背景添加了更亮的白色模糊椭圆形,模仿远处的光照聚焦效果。一只鸽子不知疲倦地在画框上面行走,很符合我的想象。

我找了个真实的木质素材,做了个木架。上面放了几张我最近爱听的专辑。一切都只用简单的 flex 布局就可以实现。
由「墙」我联想到了那些迷人的建筑,并以此重新设计了 tab 样式。

每枚图标和使用的字体,基本代表了我对这个关键词的理解。当然这些建筑并非凭空想象,AI 所使用的图示便是来自安藤忠雄建筑研究所的外形,没有大量的资料便没有灵感。
类似这样,透过「墙」面这个概念,结合具体的表达需求,诞生了很多想法,实现想法需要各种实践,也让这个项目变成了游乐场。
正如上面所说,我希望 Design Fragments 是一个形式自由的实践载体,而没有局限单一概念:

- 在导航栏设计了四枚像素图标,灰度主题的灵感来自于 System 7;

包括之前在 X 上公布一些内容(3D Logo, 二维码),最后都因为性能问题删去。但我很开心的是,有些事实依然摆在那里,只是因为想法上的不同,去重新学习与构建,不论结果如何都是件促进多巴胺分泌的事。
后续有机会我会在 X 上分享更多(尽量)有趣的想法。
开发框架选择
我的技术背景大概是这样:
- 选用 Astro 完全是一个误会。事实上我只看到 Astro 宣称的速度飞快,Netlify 的大力捧场。没有考虑到架构的特殊性导致的额外理解成本。不过事后看来大部分都是静态页也没完全走错;
- Supabase 则是之前自部署 umami 时用的数据库,虽然不太懂 PostgreSQL;
- Vercel 国内需要梯子,Netlify 还没有,也是按量付费,所以……
- 之前没用过 Tailwind CSS,现在趁此机会用用;
- Warp 救我;
- 本来想自己写搜索功能,但奈何 Algolia 很方便,体验定制很详细,免费计划看起来也够用;
- Spline 和 Rive 一直都想试试嵌入,算是调研一下可行性;
其中 Supabase、Netlify 和 Rive 都有 AI 助手问答。用 AI 的好处是不挑语言,中文问答即可。
代码知行合一
目前的 Vibe Coding 算是低代码领域而非零代码,除了代码能力的限制,AI IDE 产品也在发展中。Lovable 以「零代码」去营销的后果是,几乎随便找到一个项目都能看到明文的敏感信息。好在大多数基础开发问题,都可以通过多轮沟通解决。完全不用 LLM 和完全信任 LLM 的过激左右派都不可取。
反而,对设计师来说这是一个巨大的机会。Should Designer Code 的问题应该终结了。有想法有需求有设计,自己就可以开始着手实现,这在之前是不敢想的。
另一个角度,Vibe Coding 就像多邻国式学习法,把不通当地语言的人突然扔到国外,虽然刚开始一句话不会说,但是慢慢通过视听逐渐了解新的语言用法。同样也不要想着一蹴而就,界面也是一个一个矩形堆起来的。
最开始拿 Figma 插件实践时,使用的是 Cursor 的 Claude 3.5 sonnect,项目开始时用的是 3.7。相对比 3.5,3.7 主动遍历项目目录,对 brief 有一定理解不会跑题。但也经常将一个简单的逻辑复杂化。有时真不能怪 AI,每当这个时候我都会检查自己的需求写的是否明确。
很多人从产品文档(PRD)开始推荐用 AI 完成,我觉得如果想法还没有具体到能写到 PRD 时,与 LLM 聊天通常会得到无聊甚至错误的结果。所以我更推荐用 AI 去完善 PRD 而不是创造。
完整的 PRD 包括技术要求都可以放到 project rules 中,比起「你是一名优秀的 typescript 工程师…」这种角色扮演描述,像是项目架构、数据库字段、color token 更重要一些。

说起来 Figma 最近更新的取色器添加对比度数值展示,应该算是最近做的为数不多的良心了。我的主题蓝色在 深色模式上对比度比较低,所以换了一种蓝色。
Cursor 在这个期间对 rules 做了迭代,将其分为 4 种类型:
- 总是添加到上下文;
- 根据文件名自动添加;
- 根根据描述让 LLM 自动选择是否添加;
- 仅手动添加;
这对节省 token 也有一定的帮助。
设计师自己开发界面的好处是不用走查了,哪里出问题了一眼就能看出来直接上手修改,不用截图写报告也不用建任务,爽。我并没有使用 Figma 相关 MCP 去构建界面,一是会浪费大量上下文,Figma REST API 返回的 node 信息冗余太多,二是藉此创建的页面依然有很多瑕疵。
所以一开始我使用详细的自然语言描述布局,从间距字号字重字色样式尽可能完善的叙述。这样需要打很多字,效率也有点低。Figma 其实在 Dev Mode™ 是支持 Tailwind 样式输出的,但是有付费墙。后来出于懒惰我在自然语言中夹杂了 CSS 语言一起扔给 AI。实际效果依然不错。
熟悉 Tailwind CSS 后,直接像报菜名一样报出 class 即可。 现在基本是我负责样式,让 AI 写逻辑和自动补全。
Tailwind CSS 不喜欢的地方在于,它是移动端优先,所以一些组件样式在写完 2xl 桌面端后,又重写回移动端样式。因为并没有在移动端投入太多精力所以只能将就看。我曾遇到一个组件同时需要区分自适应和深浅色样式,导致 sm: md: lg: xl: 2xl: 和 dark: 排列组合……下次网页开发我会试试去定制 DaisyUI。
由于现在 LLM 依然靠概率吃饭的原因,有时一项功能的实现或错误的修复,好几轮对话依然没有改善。那么此时没有任何必要继续对话下去。回到最开始的状态(checkpoint 或 上个 commit),附加上轮对话的总结,再度进行对话效果会更好一些。
但通常我遇到这种情况时,大多再提供几个很明确的文档页面便可以解决。归根到底,在 AI 的脑中很多编程知识没有联系到一起,手动提及,或者 prompt 里明确可以联网搜索,都会增加解决的概率。所以一定要勤翻文档……特别是像 Astro 这种有点与众不同的框架。最近新出的 context7 搜集了很多库的最新文档,省事了不少。不过我依然觉得,用好一个框架的前提是,把文档翻烂。遇到不懂的就打开 AI 对话客户端进行对话。AI 在通俗解释方面也很有优势,也能节省一些 Cursor 的 credit。
在 Design Fragments 的开发期间,Gemini 2.5 Pro 也问世了。我使用的感受是,它解决问题的能力比 Claude 3.7 更加专注且准确。但同时对工具的调用不灵敏,「经常偷懒」。Claude 3.7 可以自己计划执行多次 grep 命令(查找文件字符串),但是 Gemini 2.5 Pro 至今没看到过……也不会主动读取 .mdc 规则,很多地方都需要手动提及。
如果要用 Gemini 2.5 Pro 深入构建项目的话,使用 Cline 可能会更好点。
即使像 Design Fragments 这样的小型项目,随着需求的增加,代码总行数也有 16k+ 了。如果要添加一些复杂的功能或样式,我会在一个专门的文件夹中先去实现和测试核心功能查看效果。这样脱离生产环境后暂时不用担心样式问题,之后再拿着已经实现效果的代码作为示例,让 AI 加进项目中。AI 照猫画虎能力是一流的,大部分时候这样多步处理,调试和回归测试更清晰,成功率也会高一点。
属于手动建立测试环境了。
最重要的是安全。目前已经有一些 checklist,我在用 Vibe Security: Web App Security Checklist,直接在 Cursor 里扔给 Claude 3.7 或者在 Cline 中扔给 Gemini 2.5 Pro。由于网站几乎不涉及提交操作,所以检查起来倒也省心。
Extra
在网站开发之外也做了一些事情。
数据导入
从第 76 期开始,搜集的数据到 90期为止在 Walling,最新的部分在 Tana。我在上次的文章提到,Tana 的导出格式很混乱。导出 markdown 辨识度又低,只能拿 pastes 格式导出,每个条目大致是这样的结构:
- Glass #Product
- 出处::
- glass.photo #url
- Domain:: glass.photo
- URL:: https://glass.photo/
- 分类:: [[Life^fui25DnyYWNS]]
- 精选:: [ ]
- 付费摄影社区。纯粹的摄影分享,不像 ins 那样杂乱。
#Product 是标签,双冒号代表 field 区域。这样可以提取标签一行前面的文本作为条目标题,网址和域名,以及不带有标签和双冒号的一行文本为条目描述。
将这个示例结构和提取规则交给 AI,几次便可输出符合需求的 python 文件。我觉的这么简单的流程应该不用放到 Dify 上,就拿 Shortcuts 封装了一下文本保存和终端操作的流程:

Walling 的 html 导出更规范一些,流程也是如法炮制。麻烦点在于更早期的文章,搜集的格式并不标准,无法直接导出,只有成型的 newsletter 文章。最终还是需要使用 LLM 来结构化输出。
测试结构化导出时,Qwen3 发布。我用了 8B 和 14B 两个版本,在精细化的需求面前依然不太行(Gemma3 也是)。不过我对 Qwen3 的主要需求还是 OCR……说回来最终还是用 Gemini 2.5 Pro 处理,Grok 3 总是放飞自我。
将之前的条目导入数据库也是接下来的主要目标,目前已导入了 700 多条目,还有一年份的内容没有导入,估计最后可以达到 1000 多条目,这里面我人工筛去了一些时效性信息,让整个数据库更有一些价值。尴尬的是 Design Scenes Weekly 是按周计数的,现在是按月计数,所以数字肯定会对不上。
部署飞书 Webhook 通知
之前在 Ghost 部署飞书 webhook 时使用的是飞书机器人助手的触发器,通过过滤消息在群里发送通知。网址类型是:
www.feishu.cn/flow/api/trigger-webhook/
如果从群机器人建立的话,走的是开放平台的端口:
open.feishu.cn/open-apis/bot/v2/hook/
该死,当时就把我弄混了……不过「将错就错」,开放平台这边有更多的功能(比如签名校验),而且支持飞书卡片消息类型。我使用飞书卡片搭建工具设计了个卡片消息:

之前在 Coze 体验过卡片构建,原理差不多,只是对接受的消息结构比较严格,详细可以在文档看到。Netlify 那边则是要构建 Functions,根据 Trigger on events 文档可以列出部署和登录相关几个事件。像「部署时间」文档中没有提到的字段,可以在后台的 Deploy log 中找到。
点击统计
之前在搭建 Ghost 时顺手自部署了 umami 统计,这次查找文档时发现 umami 也可以统计出站链接,原理是为<a>
加上点击事件统计。可以使用下面代码为所有<a>
加上统计:
<script type="text/javascript">
(() => {
const name = 'outbound-link-click';
document.querySelectorAll('a').forEach(a => {
if (a.host !== window.location.host && !a.getAttribute('data-umami-event')) {
a.setAttribute('data-umami-event', name);
a.setAttribute('data-umami-event-url', a.href);
}
});
})();
</script>
name 可以重命名,并在 <script>
排除 localhost 访问。
Scripting 小组件
顺便一提我尝试了 Supabase 的 Edge Function 功能,封装了一条 API 用于输出数据库中的随机条目。然后在 Scripting 中,编写了 Widget.tsx 用于展示小组件。用 iOS 的系统调度随机展示条目,点击可以直接在浏览器中打开。

Scripting 使用的是一种 TypeScript 和 SwiftUI 混合的特殊语法,默认情况下 AI 直接两眼一抹黑。好在我啃完文档后可以直接写样式(毕竟比较简单),API 调用交给 Gemini 完成。Scripting 支持远程桌面调试,要同步代码就要开会员,不过目前只需要开发环境的 linter 信息就够了。Cursor 的 doc 功能貌似也支持爬取文档,虽然看不到爬取的内容。
在以上内容帮助下,完成了一个新领域的探索。在我的 X 上可以查看演示。
Newsletter 订阅
当年停更 Design Scenes Weekly 的原因一是休息,二是在成文过程中花费了大量时间,导致没有时间去做别的事了。我不想成为动笔不动手的人,所以这次本着「来都来了」的心理,在 newsletter 方面尽量减少时间支出。
Quaily 是我一直在观察的 newsletter 平台,竹白倒闭后吸收了一些用户,应该是中文圈唯一还在迭代的产品。Quaily 支持 CLI 命令,这个很不错,可以直接在终端发布本地 md 文件到草稿。
quail-cli post upsert your_markdown_file.md -l your_list_slug
目前设想的流程是,使用 Edge Function 于每周日运行获取过去 7 天的条目,存为 csv 发给一个 python 脚本处理,最后输出 md 文件。

存 csv 没有存 json 原因是,csv 更简单直接,没有花里胡哨的花括号和中括号。Supabase 中也可以直接导出 csv 格式,Edge Function 出现问题后也可以手动转换调试。
最后是封面图。Quaily 的封面图有 4 种尺寸!确实是让人眼前一黑的数字,一张图需要裁剪 4 次。

单文件中我设计了这样的结构:

如上图,红线处即是居中分割的地方。为此我有设计了像素版的 logo 标识,翻出了之前设计的英文像素字母,新配套一批数字字型,规格是 5x7。

目前他只是 Figma 中一个一个矩形,之后有时间再看看封装为字体文件分享出来。
※ 订阅说明(重要)
你可能看到 newsletter 有会员订阅的选项,每年 12 美刀。上面写的「阅读付费专属内容」等字样。其实是因为 Quaily 无法自定义这里的文本。
这里申明一下:
Design Fragments 内容为免费阅读和查询,就像 Design Scenes 正在做的一样。
这里的付费订阅选项请当做一种「捐赠行为」。我并非淡迫名利之人,你的认可让我每次更新条目充满动力,你的捐赠可以让 Design Fragments 和 Design Scenes 走得更远。但信息不应该放在付费墙后面。
目前,付费会员可以使用 Algolia 的 MCP 在对话中搜索 Design Fragments 所有内容。除了标签和正文,还可以返回内容网址,你可以使用返回的网址结果继续构建 agent 流程。希望能对你有所帮助。
结语
这两个月来每天都很充实。朝 8 晚 10,工作之余基本都在这上面探索。

Design Fragments 是一个长期,非盈利为目标的项目。在我的 to-do 清单依然有很多要做的,比如无障碍方面、音效和动画都是我想尝试的。不过当前主要目标填充完所有的条目。
另外,放弃 Craft 后,之前的 Design Scenes Weekly 所有内容也放到 GitHub 上了。
AI 让各种表达有了更多机会,让个人有了更大的接触世界的权限,希望大家都能勇于尝试,找到自己的赛博范式。
如果你觉得文章对你有些帮助,可以请我的猫吃罐头 ↓
