单文件网页制品
做一个自包含、能双击打开、随数据重新生成的网页。它的读者是懂行但不在 Workbench 里的人, 他的任务是挑错——所以这份东西要经得起离线打开、要说清自己从哪来、要让意见能带回来。
什么时候用
四条同时成立:产物是一个网页 · 内容由数据生成而非手写 · 要发给不在 Workbench 里的人 · 没有活的可被驱动的状态。最后一条不成立就不是本技能的活——有活状态的目标需要一个可被 驱动的 App,本技能只负责静态网页这一条腿。
| 相邻职责 | 分工 |
|---|---|
| 数据契约与提取工艺 | 它们拥有数据契约。本技能只渲染数据,不规定数据里必须有什么 |
| 工程几何 | 它管几何与基准对不对;本技能管把关系画得能让人看懂 |
本技能不规定数据形状。 节点与边上除渲染必需字段之外的属性一律原样透传到侧栏—— 有位号就显示位号,有出处就显示出处,什么都没有也能正常渲染。数据该长什么样,由上游技能 的契约或 harness 的校验决定,调用时自然带进来。
先校准处理档位
校准的是处理规格,而非要不要设计。三档:
- 内部核对——自己或同事看一眼就丢。够读就行,别花时间。
- 专家评审(默认档)——外人要在上面挑错。可标注、能离线、有来历,排版清楚。
- 对外交付——会被转发、会被存档。加完整的视觉身份。
多数任务落在中间档。拿不准时按中间档做:一个排版干净的页面从不会是错的,一个过度设计的 页面有时是。
自包含是硬约束
依赖全部内联,运行时零网络请求。构建期联网随意(本技能自己就在构建期跑 ELK)。
这条守的是「没网的会议室里也能打开」,而不是「不许用 CDN」这句字面禁令。理解了本意,
遇到新情况就能自己判断:Google Fonts 也是外部请求,所以字体只用系统栈;图片走 data URI;
连 <link rel="preconnect"> 都不必留。
被移植进来的上游方案正是栽在这里——它从 esm.sh 与 jsdelivr 取 React 与 React Flow, 而「拿给不在 Workbench 里的专家看」是它唯一的用途。
从数据生成,不手工摆位
几何由布局器算,渲染器只画。手改坐标、手挪标签、在布局器之外再跑一次路由,都会让制品与 数据脱节——下次重新生成时那些手工调整全部丢失,而你多半已经忘了改过什么。
需要更紧凑或更松散时,调布局器的参数,不要动它的输出。
为什么不用框架
默认无框架:布局在构建期算完,浏览器端用原生 SVG/DOM 加少量 JS 渲染。产物含数据约 40 KB, 脚本直出,没有构建步骤。
两条根本理由。制品是生成物,随数据重新生成,框架最大的卖点——可维护性与组件复用—— 在这里不成立。运行时状态规模小且耦合低,检查类制品通常只有选中项、过滤集、视图变换、 标注列表几个状态,每个的影响都是局部且直接的,没有一处需要虚拟 DOM 去 diff。
具体到量级:zoom/pan 约 30 行,点选高亮约 20 行,侧栏约 30 行,过滤约 40 行,标注导出 约 60 行,minimap 约 40 行。自己写是实事求是,不是逞强。作为对照,内联 React 与 React Flow 约 450 KB,换来的只是画盒子、画路径、放标签、pan/zoom 四件事。
想引框架时,先回头看那道岔路
需要虚拟 DOM 的信号是 UI 要随状态重算大量节点:虚拟滚动、多视图联动、表单编辑、undo/redo。 出现这些时先回答另一个问题——
一个「制品」复杂到需要框架与构建步骤,多半已经跨过了「要不要 App」的分水岭。
需要复杂状态管理,通常意味着有活的、可被驱动的状态,那正是建 App 的判据。真到那一步,走
web-app-v1 scaffold(vite 构建,任何 npm 包随便用,vite 负责打成自包含产物),而不是在
这里手搓半个框架。
让读者知道从哪里开始看
专家的时间很贵。让他从头扫一遍图,和让他先看那 12 处存疑,效率差一个量级。
所以摘要先于细节,状态用形式编码而非只给数字。数据里带了置信度、告警、缺口、未覆盖区域时, 把它们提到顶部做成入口,点一下就跳到现场。具体报什么由数据决定——本技能只要求有入口, 没有可报的就不占地方。
制品要自陈来历
页脚固定交代:从什么数据生成、什么时候、生成器什么版本,以及这份东西证明了什么、不证明 什么。最后一项最容易被略过,也最容易避免误读——一张拓扑图证明设备间的连接关系,不证明 阀门、控制回路与管径。
只声称观察到的设置支持的结论。
反馈要能回来
读者能在制品上标注,导出结构化反馈,且反馈带得回源数据的版本(稳定 id 加源数据摘要)。 口头反馈会丢失、会走样、说不清针对哪一版;一份带 id 的 JSON 可以直接被下一轮生成消费。
交付前
先跑确定性检查,它比人眼可靠:
python3 scripts/check_artifact.py out/topology.html --source data.json --json out/check.json
它查外部引用、内嵌数据合法性、id 唯一性、悬挂边、来历字段、主题三态。全 PASS 才算可交付。
--json 写出的那份结果是证据门的实体供给:done_criteria 直接引用它就行(例如
「out/check.json 的 ok 为 true」),Evaluator跑一条命令便有定论,不必读散文。本技能是加载
进上下文的文本,没有执行力——能规则化的判据交给脚本与证据门,别交给自觉。
然后在浏览器里看一眼:页面不空白、控制台无错误、计数与源数据一致、缩放平移只影响该影响的
东西。先证明渲染在跑——file:// 下某些宿主根本不跑 requestAnimationFrame,所以要用
http origin 验证,不要只看双击打开的那一份。
设计基本功
双主题三态。读者的浏览器有三种状态:显式浅色、显式深色、以及什么都没标的系统态——最后
一种最常见也最常被漏掉。所以在裸 :root 里定义完整的浅色令牌,在
@media (prefers-color-scheme: dark) 里以 :root:not([data-theme="light"]) 为守卫重定义,
再在 :root[data-theme="dark"] 里重定义一次。组件只用令牌,任何颜色都不要只定义在媒体查询
或属性选择器里面。body 必须显式上底色,否则会借用宿主的底,深浅一串就露馅。
排版与布局。正文宽度收在约 65 字符;定一套字号阶梯并守住;数字列用
font-variant-numeric: tabular-nums;宽内容(表格、代码、图)各自 overflow-x: auto,
别让页面横向滚动。业务字符串用 textContent 渲染,不要拼进 innerHTML。
避开一眼假的默认。当前 AI 生成的页面高度雷同:暖奶油底配衬线标题与陶土色点缀、近黑底 配一抹荧光绿、紫蓝渐变大标题、Inter 或 Space Grotesk 当万能字体、emoji 当章节标记、什么都 居中、到处圆角卡片。用户明确要求某种风格时照做;没要求时,别把这份自由花在这些默认上。 从主题本身找线索——工业流程图的调性在控制室与蓝图里,不在营销落地页里。
门类
- when-drawing-a-topology.md — 流程与空间拓扑图:
布局参数基线、关系分类、排序与密度、节点内容克制。配
scripts/render_topology.js。 - when-the-page-must-work-offline.md — 内联的具体做法、体积预算、与 App 侧 freeze 的区别。
已知盲区
由失败复盘回填。写进来的条件是:作者读过本技能,仍然在同一个地方栽了。
- 「不引 CDN」被当成字面禁令,于是在「要双击即开」的需求下被判定为不适用。被移植的上游 方案与本仓的一次微体素沙盘开发都栽在这里。补上本意(运行时可离线)之后,正确解(依赖内联) 是自明的。本技能因此不写禁令,写理由。
- 制品可能在 0 尺寸的容器里初始化(面板折叠、iframe 未显示、打印预览)。此时
getBoundingClientRect()返回 0×0,据此算出的取景位移会把内容推到画布外,表现为「打开就 是空白,手动点一下适应窗口才好」。取景要等到容器真有尺寸,用 ResizeObserver 兜底。