交互与页面配件
先看一个真实运行的 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、按字自适应大小,箭头在布局之后量位置画,换任何字体字都不会出框:
§3.4FlowDiagram——按字自适应的框
带文字的图一律用 FlowDiagram(或 mermaid 代码块)。手写坐标的 <svg> 加 <text> 是对着一种字体画的,换一种更宽的字体就出框——而每个站用的字体都不一样;LineageSvg 只留给不含文字、字体宽窄影响不了形状的 SVG。图的内容是数据:cols、nodes(col / row / span、tone、lines、tag、虚线说明框 ghost、等宽小字 mono)、edges(label、dashed、断线 off),可选 lanes 与 heads:
泳道给行加标签、画分隔线;tone 给框的边和标签上色;线绕开不相连的框和分组标题;线上的标注一定画出来:同一行相邻两框之间的线会为它把两框的空隙加宽;标注按找到的空间折行或缩小字号;找不到既不压框也不贴着别的线的位置时,标注换成线上的一个编号(线上没空处就放在线旁,用一小段线连回去),文字列在图下。
heads 里的一项是它下面那几行的标题,所以分组的图就是一行分组标题、下面跟着这一组的框;线绕开标题的文字,和绕开框一样。框与框之间找不到路的线,沿图两侧留出的空白走(框的左右两侧、图框内边距里的一条窄带);连线层把这条空白也包在里面,所以线和它的编号都留在图里:
每个框至少有十个字母宽,每一列又至少和它框里最长的单词一样宽,所以单词从不被拆开;snake_case 的名字可以在下划线后换行。页面那一栏放不下所有列时(比如在手机上),图在自己的框里左右滚动,标题和图注不动;图的结构保持原样,不按屏幕宽度重排。
一个框连出好几条带标注的线时,有的标注找不到不压框的位置,就换成编号。编号和标注一样摆放:不压框、不压分组标题、不压别的标注和编号。线上有空的地方,编号就放在线上;没有,就放在线旁边,用一小段线连回去:
§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、.arrow | hub 上的阅读路径框 |
.ref-tag | 文献列表里的等宽小标签(会议名、年份) |
.tag、.tag.teal / .mustard / .violet | 论文卡同款徽章,哪里都能用 |
.measured + .measured-head、.measured-body、.measured-pane(.doc)、.measured-tag | 「文档说 vs 实测」对比卡:一条实心青色标题栏压着两栏,.doc 栏放文档的说法,另一栏放测量结果——轮廓和任何 callout 都不一样 |
.paper .idea-label | PaperCard 正文里的大写小标签,如 <span class="idea-label">Key idea</span> |