news 2026/9/11 16:22:42

基于 skills 仓库的 UI Prototype 实战指南:单路由多变体切换与浮动切换栏实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于 skills 仓库的 UI Prototype 实战指南:单路由多变体切换与浮动切换栏实现

基于 skills 仓库的 UI Prototype 实战指南:单路由多变体切换与浮动切换栏实现

【免费下载链接】skillsSkills for Real Engineers. Straight from my .agents directory.项目地址: https://gitcode.com/GitHub_Trending/skills13/skills

导读:本文围绕开源仓库 GitHub_Trending/skills13/skills 中prototypeskill 的 UI 分支(UI.md)展开,系统讲解如何在单个路由上生成若干结构截然不同的 UI 变体、用?variant=搜索参数与浮动底部切换栏(floating bottom bar)让用户在浏览器里来回对比、挑选甚至"偷取"不同变体的局部设计,然后只把胜出者合入真实代码。读完本文,你将掌握两种子形态(Sub-shape A / Sub-shape B)的选型标准、六步落地流程、键盘/路由交互约定、生产环境隐藏策略,以及该 skill 在整套工程技能中的定位与边界。


一、UI Prototype 是什么:在哪个分支干活

在 prototype/SKILL.md 中,prototype是一条**由模型自动调用(model-invoked)**的工程 skill,定位是"为回答一个设计问题而写的、用完即弃的代码"(throwaway code that answers a question)。SKILL.md 的 frontmatter 明确说明它的适用时机:

Build a throwaway prototype to answer a design question. Use when the user wants to sanity-check whether a state model or logic feels right, or explore what a UI should look like.

skill 内部按问题类型分成两个分支(详见 skills/engineering/README.md 对 prototype 的描述):

  • "这套逻辑 / 状态模型手感对吗?"→ 走 LOGIC.md,产出一个单一自包含 HTML 文件(含自由点击按钮与分标签的引导式演练),让非开发人员也能亲手推动状态机。
  • "这个东西看起来应该是什么样?"→ 走本文主角 UI.md,在单条路由上生成若干结构上完全不同的 UI 变体,用 URL 搜索参数加浮动底部栏在浏览器中切换。

分支选错会浪费整个原型,因为两者产出的工件差异极大。一个实用的默认判断:问题是关于逻辑/状态而不是外观,就走 LOGIC.md;问题是"某个页面该长什么样",就走 UI.md。SKILL.md 还给了补充规则:如果问题确实模糊且用户不在场,就依据周边代码猜——后端模块偏向逻辑分支,页面/组件偏向 UI 分支,并在原型顶部写明该假设。

二、什么时候该用这个形态(When this is the right shape)

UI.md 给出的典型触发场景非常直白:

  • "这个页面应该长什么样?"
  • "我想在拍板之前,先看几个 dashboard 的候选方案。"
  • "给设置页试一种不同的布局。"
  • 任何用户原本要花一整天在脑子里比较三张模糊线框图的时刻。

它对应的是 docs/engineering/prototype.md 里那句判断:"你遇到一个靠谈话无法拍板的问题"——比如一个无法在脑中完整推演的屏幕设计,需要同时看到三个版本并排对比。当 grilling 类会话(如 grill-me、grill-with-docs)在这些问题上不断膨胀时(agent 换个说法、你继续猜、范围越滚越大),正确动作是停止 grilling,去构建一个一次性版本,看一眼,然后用一句话回答。反之,如果问题已经讨论清楚、只是要按规格落地实现,则应按 to-spec / implement 的流程走,而不是原型。

三、两种子形态:优先选择 Sub-shape A

UI 原型最容易评判的时刻,是它贴住应用其余部分的时候:真实的 header、真实的 sidebar、真实的数据、真实的密度(density)。一条孤立的临时路由是"真空"——每个变体单独看都好看。因此 UI.md 给出默认策略:只要存在一个合理的宿主页面,就默认用 Sub-shape A

子形态 A:对既有页面的调整(首选)

  • 路由已经存在;变体渲染在同一条路由上,由?variant=URL 搜索参数门控。
  • 已有的数据获取、参数、鉴权全部保留,只有渲染子树(rendered subtree)随变体切换。
  • 即便原型针对的"页面"还不存在,但它天然属于某个页面内部(dashboard 的新区块、设置页的新卡片、既有流程中的新步骤),依然归为子形态 A——把变体挂载进宿主页面即可。

从源码结构看,这种做法的收益是"零上下文丢失":评审者看到的是变体在真实数据密度下的表现,而不是一条与世隔绝的临时页面上孤零零的布局。

子形态 B:新页面(最后的退路)

仅当被原型化的东西确实没有任何可嵌入的既有页面时才使用(例如一个全新的顶级 surface,或一段无法嵌进任何合理位置的流程):

  • 创建一条临时路由(throwaway route),遵循项目既有的路由约定,不要发明新的顶级结构
  • 命名要让人一眼看出是原型(路径或文件名中包含prototype字样)。
  • 使用同样的?variant=模式。

提交到子形态 B 之前要自检:真的没有任何既有页面可以承载它吗?一条空路由会掩盖空路由才能暴露的设计问题——有内容的页面才会把问题逼出来。两个子形态下,浮动底部栏的实现完全一致。

四、六步落地流程(Process)

第 1 步:陈述问题并确定变体数量 N

默认生成3 个变体;超过 5 个就不再是"截然不同"而是"噪音",因此上限为5。随后用一行话写下计划,放在原型所在位置或文件顶部注释里:

"Three variants of the settings page, switchable via?variant=, on the existing/settingsroute."

这句话无论用户当下在不在场、要不要反驳,都是有效的检查锚点。

第 2 步:生成"结构上截然不同"的变体

为每个变体起草,并逐条守住三条约束:

  1. 页面的目的与它能拿到的数据;
  2. 项目既有的组件库/样式体系(TailwindCSS、shadcn、MUI、纯 CSS,whatever);
  3. 一个清晰的导出组件名,例如VariantAVariantBVariantC

关键判据(原文档反复强调):变体必须在结构上不同——不同的布局、不同的信息层级(information hierarchy)、不同的主操作入口(primary affordance),而不是仅仅颜色不同。三个微调过的卡片网格不是 UI 原型,是"壁纸"。如果两个草稿太相似,就按"明确禁止使用卡片网格"的要求重做其中一个。

第 3 步:把它们接线到一起

在路由上创建一个单一的切换器组件,UI.md 给出的伪代码如下(按项目框架适配):

// pseudo-code, adapt to the project's framework const variant = searchParams.get('variant') ?? 'A'; return ( <> {variant === 'A' && <VariantA {...data} />} {variant === 'B' && <VariantB {...data} />} {variant === 'C' && <VariantC {...data} />} <PrototypeSwitcher variants={['A','B','C']} current={variant} /> </> );
  • 子形态 A(既有页面):所有数据获取保持在切换器之上,只有渲染子树随变体变化;
  • 子形态 B(新页面):临时路由挂在/prototype/<name>下,挂载同一个切换器。

这样?variant=参数既是门控开关,也是可分享、可刷新还原的状态来源——这正是后续评审与交接的基础。

第 4 步:构建浮动切换栏(floating switcher)

一个位于屏幕底部居中fixed定位小条,包含三块:

  • 左箭头:循环切到上一个变体(可回绕 wrap around);
  • 变体标签:显示当前变体 key;若变体导出了名字,一并显示,例如B (Sidebar layout)
  • 右箭头:循环切到下一个变体(可回绕)。

行为约定(可复用的检查清单):

关注点约定
URL 同步点击箭头更新 URL 搜索参数,用框架自带 router(Next 用router.replace,React Router 用navigate等),保证变体可分享、刷新后稳定
键盘操作/方向键也能循环切换
焦点豁免<input><textarea>[contenteditable]获得焦点时不得拦截方向键
视觉区分与页面明显区分(如高对比度胶囊、细微阴影),让人一看就知道"这不是被评审的设计本身"
生产隐藏process.env.NODE_ENV !== 'production'或等价检查门控,防止原型误合并后把切换栏带给真实用户

切换器应做成单一共享组件,让两个子形态都能复用;位置放在项目共享 UI 所在处。注意最后一行的用意:一次意外的原型合并不该把切换栏发到用户端,这是原型纪律的最后一道保险。

第 5 步:交接(Hand it over)

把 URL(连同各?variant=key)交出去,用户有空就翻一翻。原文档点出最有价值的反馈形态通常是——"我想要 B 的 header 配 C 的 sidebar",那才是用户真正想要的设计。也就是说:变体不是用来"单选"的,也可以被"拆借"。

第 6 步:捕获答案并清理

胜出变体确定后:

  1. 捕获答案(哪个变体、为什么);
  2. 按 prototype/SKILL.md 描述的方式捕获原型本身
  3. 把胜出者合入真实代码,其余内容放上一次性分支(throwaway branch),而非 main:
  • 子形态 A:把胜出者折入既有页面;失败的变体和切换器从 main 移除;
  • 子形态 B:把胜出变体升级为真实路由;临时路由和切换器从 main 移除。

一个关键细节:全套变体是"一手来源(primary source)",所以它落在一次性分支上而不是垃圾桶里——因为留在 main 分支上的变体组件和切换器会快速腐烂(rot fast),还会误导下一位读者。这与 docs/engineering/prototype.md 中"原型不再是删掉,而是作为 evidence 停在 main 之外的分支上"的原则完全一致。

五、反模式清单(Anti-patterns)

UI.md 收尾处明确列出四条红线,逐条展开:

  1. 变体只在颜色或文案上不同——那是"微调(tweak)"不是原型;真正的变体必须在外观结构上互相矛盾。
  2. 变体之间共享过多代码——共享一个<Header>没问题;共享一个<Layout>就违背了初衷。每个变体都应能自由地抛弃布局。
  3. 把变体接到真实的写操作(mutations)上——只读原型是没问题的;如果某个变体需要写数据,就指向一个 stub。要回答的问题是"这应该长什么样",而不是"后端能不能跑"。
  4. 直接把原型提升到生产环境——变体代码是在原型约束下写的(无测试、最小错误处理)。折入正式代码时应当按正式标准重写

这四条与 SKILL.md 的通用规则互相呼应:prototype 是"从第一天起就可抛弃的",无测试、无超出可运行所需之外的错误处理、无抽象、默认无持久化(状态只存内存,持久化恰恰是原型要检验的东西,而不是它依赖的东西)。

六、在整套 skills 中的位置与上下文

从仓库层面看,prototype位于 skills/engineering/ 的Model-invoked(模型自动调用)分组,即模型或用户都能触发,Agent 在任务匹配时会自动拾取。其 frontmatter 与agents/openai.yamlinterface.display_name: "Prototype")共同构成 Agent 可检索的元数据。

上下游关系(依据 docs/engineering/prototype.md 整理):

  • 上游/并行的邻居:可 grill 的问题交给grill-me/grill-with-docs;grill 不动的"不可谈"问题(比如屏幕长什么样)才落到原型,产出的一句话答案再回填到对话中。
  • 最大消费者wayfinder的地图由决策 ticket 组成,prototype是四类 ticket 之一——当阻塞性问题是"这看起来/动起来应该是什么样"且讨论无法解决时使用。原型 ticket 由答案解决,原型本身作为 asset 挂在地图上。
  • 下游:验证过的状态模型或 UI 方向,成为to-spec的已定稿输入,可以内联原型产出的决策片段,而不是用散文描述。
  • 运行方式:原型应尽量在独立会话/独立目录中运行(避免污染提出问题的线程上下文),只把答案带回;handoff 是双向桥梁。

七、判定"工作正常"的验收信号(It's working if)

结合 docs/engineering/prototype.md 的验收清单,UI 分支做对了的标志是:

  • 你能用一句话说清这个原型要回答什么问题;
  • UI 变体在布局与信息层级上分歧,而不只是颜色与文案;你收到的反馈是"B 的 header 配 C 的 sidebar"这种结构级意见;
  • 问题一次性得到回答——如果一天后还在搭原型,说明问题太大,需要拆分;
  • 收尾时,main 里只有决策、没有任何原型代码,实现 issue 上留了指向保留原型分支的上下文指针(context pointer)。

参考路径

  • UI 原型主文档(本文主体)
  • prototype skill 总入口(双分支路由与通用规则)
  • 逻辑分支:单文件可分享 demo
  • 原型 skill 的完整说明文档
  • 工程 skills 索引(Model-invoked 分组)
  • 仓库 README 中的安装与使用说明

【免费下载链接】skillsSkills for Real Engineers. Straight from my .agents directory.项目地址: https://gitcode.com/GitHub_Trending/skills13/skills

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 16:21:10

如何用 OCRmyPDF 的 --mode strip 移除 PDF 中已有的不可见 OCR 文字层

如何用 OCRmyPDF 的 --mode strip 移除 PDF 中已有的不可见 OCR 文字层 【免费下载链接】OCRmyPDF OCRmyPDF adds an OCR text layer to scanned PDF files, allowing them to be searched 项目地址: https://gitcode.com/GitHub_Trending/oc/OCRmyPDF 当你手里的扫描 P…

作者头像 李华
网站建设 2026/9/11 16:19:27

用Matlab搭建SIRS传染病模型:从微分方程到参数标定与随机模拟

简介&#xff1a;SIRS传染病学模型Matlab完整仿真资源&#xff0c;面向从事数学模型仿真、传染病动力学研究的学生与科研人员&#xff0c;用于描述易感(Susceptible)、感染(Infectious)、康复(Recovered)、免疫(Immune)四种人群状态的动态转化过程。模型遵循易感个体接触感染者…

作者头像 李华
网站建设 2026/9/11 16:17:48

expo-font 完全指南:在 Expo 与 React Native 中运行时加载字体

expo-font 完全指南&#xff1a;在 Expo 与 React Native 中运行时加载字体 【免费下载链接】expo An open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web. 项目地址: https://gitcode.com/GitHub_Trending/ex/exp…

作者头像 李华
网站建设 2026/9/11 16:17:11

AI Agent后端选型:PolarDB Agent Express与VM级安全隔离实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华