hallmark 的 F2 Sticky-Scroll Stack 组件实战:双栏粘性停靠,让多子状态功能按序列滚动展示
导读
F2 Sticky-Scroll Stack 是 hallmark 设计技能中处理"多子状态功能"的标准组件形态:左侧信息面板 position: sticky 停靠,右侧滚动区循环展示与该功能相关的多张截图,形成"讲解不动、证据流动"的阅读节奏。本篇文章以 f2-sticky-scroll-stack.md 为骨架,结合 hallmark 的组件食谱、slop-test 门控与真实仓库代码,讲清它的适用边界、最小实现、与顶部导航共存的偏移与层级协议,以及移动端折叠规则。读完你可以在任意落地页中直接落地这个组件,并让它在 320–1920 px 全视口下不犯 AI 味。
一、这个组件解决什么问题
F2 属于 hallmark 组件食谱(component-cookbook.md)中的 Feature block 类别,一句话定义是:
Sticky left pane, scrolling right pane that cycles through related screenshots.(左侧粘性面板,右侧滚动面板循环展示相关截图)
它的设计初衷非常明确:
- Use when(何时使用): 一个功能具有多个值得按顺序展示的子状态(sub-states)——例如编辑器功能的"创建 → 编辑 → 发布"三个界面快照、数据看板的"原始日志 → 过滤 → 可视化"三步形态。
- Don't confuse with(勿混淆): F4 Step sequence——后者是线性编号的(
1.0 → 2.0 → 3.0竖直流),每一步有自己的标题与说明,彼此不同步;而 F2 不做编号,靠右侧滚动与左侧停靠形成"讲解始终可见、画面持续切换"的同步叙事。
核心差异一句话:F4 讲"流程有几站",F2 讲"一个功能有多少种状态"。当你发现文案里出现编号步骤(01 · Intake / 02 · Plan),应该走向 f4-step-sequence.md;当你发现反复描述"同一个东西在不同状态下的样子",才是 F2 的主场。
二、最小实现:两行代码的骨架
F2 的结构极简,只有一左一右两个面板。原文给出的 HTML 骨架:
<section class="sticky-stack">
<div class="pane-sticky"><h3>…</h3><p>…</p></div>
<div class="pane-scroll">
<figure>1</figure><figure>2</figure><figure>3</figure>
</div>
</section>
配套 CSS 只有两条声明:
.sticky-stack { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-2xl); }
.pane-sticky { position: sticky; top: calc(var(--banner-height, 0px) + var(--space-xl)); align-self: start; z-index: var(--z-sticky); }
逐条拆解这三个关键点:
-
grid-template-columns: 1fr 1fr——双栏对等宽度,gap: var(--space-2xl)使用 hallmark 命名间距 token。在 site/css/tokens.css 中可查到--space-xl: 2.5rem、--space-2xl: 4rem,即两栏间距 64px、粘性面板距导航底 40px,符合 hallmark 全项目统一的 4pt 间距体系(见 layout-and-space.md)。 -
top: calc(var(--banner-height, 0px) + var(--space-xl))——这是 F2 最精妙的一行。它让粘性面板停靠在页面顶部导航(banner)的正下方,并额外留出--space-xl(40px)呼吸空间。var(--banner-height, 0px)中的兜底值0px意味着:当页面没有粘性导航时,表达式退化为0 + 40px,行为依然正确。 -
align-self: start——防止 Grid 默认的stretch把粘性面板拉满整个轨道高度;只有start才能让面板以自己的内容高度停靠,这是position: sticky在 Grid 子项中生效的必备配套。
右侧 pane-scroll 无需任何特殊样式——它就是普通文档流,页面滚动时截图依次滚过,左侧面板纹丝不动,形成"证据流"效果。
三、与顶部导航共存的协议:gate 68 与 token 配对
F2 最容易翻车的地方不是自身,而是与页面顶部粘性导航打架。如果导航 position: sticky; top: 0,而 F2 的粘性面板也贴着 top: 0 停靠,滚动时两者会重叠——更深 DOM 的元素会盖住导航,视觉上就是"区块头渗进导航栏"的 bug。hallmark 把它固化为 slop-test 的第 68 号门禁:
Sticky element at
top: 0below a sticky page-level nav → bleed. 只要页面存在粘性 nav/banner/header,任何其他元素再声明top: 0即自动判失败。
修复方案就是 F2 原文注释里写的思路,配套 token 在 site/css/tokens.css 中真实存在:
--z-sticky: 200; /* in-page sticky elements (section heads, sidebars) */
--z-sticky-nav: 300; /* top nav / banner — must out-paint in-page sticky */
--banner-height: 44px;
Sticky 配对规则(来自 s3-sticky-pinned.md)是跨组件通用的硬约束:
- 页面只要发射了任何
position: sticky; top: 0的<header>/<nav>/.banner,就必须在tokens.css中声明--banner-height(px 值,匹配导航实际高度,约 44–64px)和--z-sticky-nav(比--z-sticky至少高 1 级)。 - 页内所有次级粘性元素(F2 面板、S3 粘性标题、粘性目录)一律用
top: calc(var(--banner-height, 0px) + …)或top: var(--banner-height, 0px)停靠,并用z-index: var(--z-sticky)。 - 这样导航永远用
--z-sticky-nav压住页内粘性层,次级粘性元素停靠时滑入导航下方而非盖上去。
hallmark 在 layout-and-space.md 中为 z-index 定义了六层命名刻度(--z-base: 1 → --z-tooltip: 600),F2 只用其中的 --z-sticky: 200 一层,绝不自由发挥 z-index: 9999。
仓库里有一个可直接对照的真实案例:site/_tests/06-anya-portfolio/style.css 中的 sidebar TOC 正是同一套模式——position: sticky; top: var(--space-2xl); align-self: start;,在无粘性导航的页面上用间距 token 直接做偏移,验证了"无导航时退化为纯间距停靠"的兜底思路。
四、变化旋钮:同一个组件不复制粘贴
hallmark 的核心纪律是结构化多样性:两个页面即使用同一组件,也不该长得一模一样。为此 component-cookbook.md 为 F2 定义了三个变体旋钮(variation knobs):
| 旋钮 | 取值 | 含义 |
|---|---|---|
| Pinned side | left · right | 粘性面板停靠左栏还是右栏;默认左,产品演示类页面可翻转到右以镜像阅读习惯 |
| Right pane content | code · screenshot · diagram | 右侧滚动区承载的内容类型——代码块、产品截图或示意图;决定滚动节奏与标注方式 |
| Pin steps | 3 · 4 · 5 | 右侧循环展示的"画面"数量;子状态少则 3 张,多则 5 张,超过 5 说明该换 macrostructure 了 |
选择旋钮值后必须写进 CSS stamp 注释,例如:
/* Hallmark · macrostructure: Workbench · F2 knobs: pinned=left, pane=screenshot, steps=4 · ... */
stamp 是 hallmark 项目记忆(.hallmark/log.json)的持久记录:下一次运行时,读取 stamp 就能判断这次 F2 与前一次是否在至少一个旋钮上不同——两次 pinned=left, pane=screenshot, steps=3 的 F2 会被 slop-test 第 34 号门禁视为"同一个 Bento"而判失败。
五、移动端折叠:60rem 与 40rem 两级降级
F2 在窄视口必须"仍然是自己"——层级、语气、节奏不变,但堆叠成单栏。根据 component-cookbook.md 的逐组件折叠表,F2 的折叠规则是:
| 断点 | 行为 |
|---|---|
| 60rem(~960px) | 粘性面板取消停靠(unsticks),双栏退化为线性序列:文本块与截图交替排列,成为成对的 text+visual 块 |
| 40rem(~640px) | 截图收缩为 16/9 内联展示,彻底无粘性行为——移动端滚动有自己的物理特性,滚动联动动画在手机上会被禁用(同一份折叠表中"Disable any scroll-linked animation below 40 rem"是跨组件规则) |
也就是说,position: sticky 与滚动联动的叙事效果是桌面端的特权;在手机上 F2 退化为一条普通的图文交替流,与 F4 Step sequence 的观感接近,但语义不变。实现时只需在 60rem 断点把 .sticky-stack 改为单列、去掉 .pane-sticky 的 position: sticky 即可。
六、在 hallmark 流程中的正确使用方式
F2 不是被"顺手用"的,而是按 hallmark 的 index-then-pick 纪律显式选取的:
-
选定 macrostructure 先行(SKILL.md Step 2)。F2 最常见的主场是 05 · Workbench 宏结构——"产品截图是主要内容,整页是对产品使用过程的导览"(macrostructures/05-workbench.md),其 DOM 骨架
<section class="screenshot-frame">序列与 F2 的右侧滚动区天然合拍;适合 SaaS、开发者工具、IDE 插件等"看到产品动起来才是卖点"的页面。若产品是显式工作流(项目管理、设计交付管线),则选 14 · Narrative Workflow(macrostructures/14-narrative-workflow.md)并配 F4 编号阶段——两者区分原则见本文第一节。 -
只加载选中的组件文件。从 component-cookbook.md 的索引里挑出 F2 后,只读取
references/components/f2-sticky-scroll-stack.md这一个文件,不整册加载(SKILL.md 明确警告整册加载 cookbook 是最大的 token 浪费之一)。典型页面总共加载 5–7 个组件文件(1 hero + 1 section head + 1–2 features + 1 CTA + 1 footer + 1 nav)。 -
同页去重:一个页面内不允许两个 section 使用同一 archetype——页面上有了 F2,就不能再来第二个 F2 或变体旋钮完全相同的段落。
-
发射后过 slop-test:F2 主要撞的关卡是 gate 68(粘性覆盖导航)与 gate 36(任意视口横向滚动)。后者要求
html, body { overflow-x: clip; }全局兜底——注意用clip而非hidden,因为hidden会创建新的滚动容器从而破坏position: sticky后代(见 layout-and-space.md § Page-edge clipping),这正是 F2 这类粘性组件能存活的前提。
七、总结:F2 的检查清单
落地 F2 前,对照这份清单逐项确认:
- [ ] 内容确实是"一个功能的多个子状态"而非编号流程——后者改用 F4
- [ ] 双栏 Grid:
grid-template-columns: 1fr 1fr; gap: var(--space-2xl) - [ ] 粘性面板
position: sticky; align-self: start; z-index: var(--z-sticky) - [ ]
top使用calc(var(--banner-height, 0px) + var(--space-xl))停靠于导航下方 - [ ] 页面存在粘性导航时,
tokens.css已声明--banner-height与--z-sticky-nav(≥ 300) - [ ] 旋钮值(pinned side / pane content / steps)已写入 CSS stamp,且与前一次输出至少一个旋钮不同
- [ ] 60rem 以下取消停靠、40rem 以下无任何滚动联动动画
- [ ] 已跑 gate 68(无粘性覆盖)与 gate 36(无横向滚动)
主要参考文件:
- 组件定义:skills/hallmark/references/components/f2-sticky-scroll-stack.md
- 旋钮与折叠表、nav/footer 路由:skills/hallmark/references/component-cookbook.md
- 停靠协议与 token 配对规则:skills/hallmark/references/components/s3-sticky-pinned.md
- 门禁 68 与粘性层级要求:skills/hallmark/references/slop-test.md
- 间距与 z-index 刻度、
overflow-x: clip:skills/hallmark/references/layout-and-space.md - 真实 token 定义与粘性目录案例:site/css/tokens.css、site/_tests/06-anya-portfolio/style.css
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust4.21 K635- DDeepSeek-V4.1-FlashDeepSeek-V4.1-Flash 是一个多模态混合专家(MoE)模型,拥有 5520 亿骨干参数,并支持最多一百万 token 的上下文长度。该模型原生支持图像和文本输入,并以自回归方式生成文本Python30
jforgamejforgame是一个一站式游戏服务器开发框架。包含游戏服务器开发所需要的各种组件,比如网关,socket服务端与客户端,自定义高效消息编解码,游戏热更新,游戏通用工具等等。包含游戏服,跨服,匹配服,后台管理系统等实现,同时提供大量业务案例以供学习。亦可用于其他socket应用,例如及时聊天等。Java131
fizz-gateway-nodeAn Aggregation API Gateway in Java . FizzGate 是一个基于 Java开发的微服务聚合网关,是拥有自主知识产权的应用网关国产化替代方案,能够实现热服务编排聚合、自动授权选择、线上服务脚本编码、在线测试、高性能路由、API审核管理、回调管理等目的,拥有强大的自定义插件系统可以自行扩展,并且提供友好的图形化配置界面,能够快速帮助企业进行API服务治理、减少中间层胶水代码以及降低编码投入、提高 API 服务的稳定性和安全性。Java70
certd开源SSL证书管理工具;全自动证书申请、更新、续期;通配符证书,泛域名证书申请;证书自动化部署到阿里云、腾讯云、主机、群晖、宝塔;https证书,pfx证书,der证书,TLS证书,nginx证书自动续签自动部署JavaScript80
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python290