参考组件常青更新 2026.08阅读约 5 分钟
总览›第 III 部 · 交互与配件
第 III 部 · 交互与配件

交互与页面配件

先看一个真实运行的 canvas 演示和它背后的契约;再看给每篇笔记搭框的那些安静组件——从第一页起,你一直在看的就是它们。

§3.1DemoMount 现场演示

分工还是这个包一贯的分法:挂载标记(舞台、控件槽、图注槽)随包提供;演示模块和它的加载器归站点所有。本站把加载器放在 src/lib/demo-loader.ts,在布局里调用一次 mountAllDemos();这个调用每页都执行,但只有页面上真的带着 [data-demo] 挂载点,才会去拉取对应演示模块的 chunk。

要在画布上接收指针输入的演示,传 interactive——舞台随即接管触摸手势并显示十字准星。这一个是被动的(只渲染),所以不传这个开关,在画布上滑动仍然是翻页。它用页面自己的墨色 token 画双源波干涉,所以跟着主题走:

演示 · 双源波干涉
演示 · 双源波干涉

两个点源发出圆形波;波峰遇上波峰,墨色汇聚,波峰遇上波谷,纸色透出来。切换一下主题——演示会从实时 token 里重新取墨。

模块契约只有一条:默认导出一个 mount(ctx),返回清理函数;像 waves/interference 这样的模块 id 怎么解析,全由站点自定(本站用 import.meta.glob)。

§3.2页面配件

剩下这些组件不填充笔记,而是给笔记搭框:

  • Hero 给一篇笔记开场:眉题、展示标题、一段走完整 markdown 管线的导语、一行元信息,还有可选的 tocLabel——它会在侧栏加一行不编号的「引子」。frontmatter 的 title 仍是给 <title> 与卡片用的简短正名;hero 的标题可以写长。
  • PartHero 给 hub 的一章开场——本页页头就是它。它的眉题从来不用手写:路由把这一章的「第 III 部 · …」标签作为 props.partLabel 传进来。
  • Part 把一篇长的单页笔记切成几部;编号预设把它变成侧栏里「PART II · …」那样的分组行。全要素演示就是这么切的。
  • HubCard 与 .hub-grid 是 hub 落地页的卡片,.path 是它的阅读路径框——两样都在本合集的总览页上。
  • LocalToc 渲染页内跳转列表;两种编号预设产出的两种 ToC 形状它都接受,条目数低于可配置的下限时自动隐藏。下面就拿本章自己的 ToC 现场演示。
  • SidebarMemory 让可滚动的侧栏跨页面记住读者上次停的位置,当前项(默认认 [aria-current="page"])快要滚出视野时自动居中。把它放进侧栏的滚动盒里、导航标记之后;它在首次绘制前运行,侧栏不会先闪到顶部再跳回来。本站的侧栏就装着它。
  • LineageSvg 把不含文字的 SVG(谱系树、分类树)装成卡片;带文字的图用 FlowDiagram,见下文。
  • PageFooter 是笔记脚下那行小字——本站每篇笔记底下那行就是。
  • 导览类组件在wiki 与导览一章;本合集的 markdown 侧则是全要素演示。

§3.3LocalToc 与 FlowDiagram 现场演示

把本页构建期生成的 ToC 喂给 LocalToc——和侧栏消费的是同一份数据,以 props.toc 递给笔记:

同一张图改成 FlowDiagram——框是 HTML、按字自适应大小,箭头在布局之后量位置画,换任何字体字都不会出框:

图 — 外观层在哪一层
tokens.css
base.css阅读栏
browse.css浏览书架
components两边都吃

§3.4FlowDiagram——按字自适应的框

带文字的图一律用 FlowDiagram(或 mermaid 代码块)。手写坐标的 <svg> 加 <text> 是对着一种字体画的,换一种更宽的字体就出框——而每个站用的字体都不一样;LineageSvg 只留给不含文字、字体宽窄影响不了形状的 SVG。图的内容是数据:cols、nodes(col / row / span、tone、lines、tag、虚线说明框 ghost、等宽小字 mono)、edges(label、dashed、断线 off),可选 lanes 与 heads:

图 — 一次改动,三条泳道
作者
评审
构建
写一篇笔记或一个组件
提 PR一个分支
评审通过或提问
构建并部署合并即跑

泳道给行加标签、画分隔线;tone 给框的边和标签上色;线绕开不相连的框和分组标题;线上的标注一定画出来:同一行相邻两框之间的线会为它把两框的空隙加宽;标注按找到的空间折行或缩小字号;找不到既不压框也不贴着别的线的位置时,标注换成线上的一个编号(线上没空处就放在线旁,用一小段线连回去),文字列在图下。

heads 里的一项是它下面那几行的标题,所以分组的图就是一行分组标题、下面跟着这一组的框;线绕开标题的文字,和绕开框一样。框与框之间找不到路的线,沿图两侧留出的空白走(框的左右两侧、图框内边距里的一条窄带);连线层把这条空白也包在里面,所以线和它的编号都留在图里:

图 — 一列里往回走的线
计划要改什么
实现
测量和真值比
图 — 两组之间的线
模型拿到什么
模型从中算出什么
照片几个视角
相机位姿和镜头
每个视角的特征
融合各视角
闭合曲面

每个框至少有十个字母宽,每一列又至少和它框里最长的单词一样宽,所以单词从不被拆开;snake_case 的名字可以在下划线后换行。页面那一栏放不下所有列时(比如在手机上),图在自己的框里左右滚动,标题和图注不动;图的结构保持原样,不按屏幕宽度重排。

一个框连出好几条带标注的线时,有的标注找不到不压框的位置,就换成编号。编号和标注一样摆放:不压框、不压分组标题、不压别的标注和编号。线上有空的地方,编号就放在线上;没有,就放在线旁边,用一小段线连回去:

图 — 一个框连出五条带标注的线
光线进入这一级的带子
沿带子走这一段预算 M 个格子
退回粗一级重做区间仍含真交点
不设预算走完这段只在最粗一级
输出区间一定含真首次交点
处理下一段穿越都没有:光线没碰到物体

§3.5没有组件形态的配件

有几件配件只是 base.css 里的普通类,留给站点在自己的布局里渲染,或在行文里直接写 HTML:

类是什么
.page-head + .kicker、.page-desc、.page-date由 frontmatter 驱动的页头(眉题、h1、描述、日期),由布局渲染,替代 <Hero>
.banner、.banner-fallback页头下面的一行通告;fallback 变体是弱化样式的那种
.page-foot两栏小字页脚(左槽、右槽)
.speak深入小节标题下的大白话注解;标签文案在 --speak-label
.path + .title、.route、.arrowhub 上的阅读路径框
.ref-tag文献列表里的等宽小标签(会议名、年份)
.tag、.tag.teal / .mustard / .violet论文卡同款徽章,哪里都能用
.measured + .measured-head、.measured-body、.measured-pane(.doc)、.measured-tag「文档说 vs 实测」对比卡:一条实心青色标题栏压着两栏,.doc 栏放文档的说法,另一栏放测量结果——轮廓和任何 callout 都不一样
.paper .idea-labelPaperCard 正文里的大写小标签,如 <span class="idea-label">Key idea</span>
搜标题、小节与正文,本语言内检索。
    ↑↓ · Enter · Escastro-inkstone