D-067 — anchor-rail extracted as a per-document section navigation module
Status: accepted · Date: 2026-06-23 · Decided by: Helper Mavis · Operator: confirms
Context
Per operator 2026-06-23 16:12 (initial ask) and 17:10 (correction): the operator wants the "anchor bar" pattern available throughout docHub AND lifted to FVS Studio. The first attempt (D-065 + D-066) named a chip-level section-nav component <anchor-bar> — but the operator clarified that to them, an "anchor bar" is a timeline of anchors you can navigate to (the right-rail dot timeline in the prompt viewer), not a sidebar nav list.
D-065 and D-066 are superseded by this decision. The previous "anchor-bar" component is deleted; this decision documents the real pattern.
What an anchor bar IS, per the operator
"An anchor bar is a timeline. Of anchors that you can go at."
In FVW / FVS terminology:
- An anchor is a section of a document (a heading with an
id, an ARIA landmark, or any [id]-tagged block) - A timeline of anchors is a visual strip along one edge of the viewport, where each element of the strip represents one anchor
- The user can go to an anchor by clicking its element on the strip
- The strip can track the user's scroll position, highlighting the active anchor
- The strip can peek at the content of an anchor on hover
- The strip clusters dense sections when there are many anchors (density-heatmap pattern)
The AvidCreation chat-viewer has a working example of this pattern (renderAnchorRail + #anchor-rail markup). It is the model for what an anchor bar should be.
Decision
Extract the right-rail anchor timeline as <anchor-rail> web component, level 3 (module).
The module lives at user/assets/anchor-rail.js (~13KB vanilla JS, web component, shadow DOM).
Module cluster of features (the test of "is this a module")
Removing any one of these features breaks the value of the component. Therefore: module, not chip.
- Discover — scan the document for H1/H2/H3 + ARIA landmarks +
[id]-tagged sections - Render — paint a vertical strip of dots (or clusters when many) along the chosen edge
- Jump — click a dot to smooth-scroll to that section
- Peek — hover a dot for 200ms+ to show a preview popover with title + first 140 chars of the section
- Track — IntersectionObserver highlights the active anchor as the user scrolls
- Cluster — when >14 anchors, group adjacent dots into taller bars (density-heatmap)
- Persist — localStorage remembers the rail's hidden/visible state per user
The cluster rule: removing peek still works (you lose previews, but the rest is useful). Removing track still works (you lose active-state, but the rest is useful). Removing jump still works (you can scroll manually). But removing any one makes the rail significantly less useful — together, the 7 features form a coherent capability.
Two-view substrate application
<anchor-rail> is both:
- A standalone nav primitive you can drop into any document
- A substrate that FVS apps adopt whole
When FVS Studio adopts <anchor-rail>, the FVS dev/team uses the same per-document nav as docHub, Vibe Chat, VibeScope, etc. Visual + behavioral consistency.
Where it's used in docHub
| Surface | URL | Usage |
|---|---|---|
| docHub SPA shell, doc-detail view | fv-dochub.pages.dev/<face>/<doc>.html | Per-doc right-rail anchor timeline (auto-discovers H2/H3 in the rendered markdown HTML) |
| Prompt archive | fv-dochub.pages.dev/mavis/prompts/_viewer/ | Already has its own right-rail (renderAnchorRail in avidcreation.js); future: port to <anchor-rail> |
| Vibe Hub (Q2) | fv-vibehub.pages.dev | Each gallery entry detail view will use <anchor-rail> for the entry's content sections |
| FVS Studio (future) | (any FVS app) | Lift via <script src="https://fv-dochub.pages.dev/assets/anchor-rail.js"> |
Gallery eligibility
The operator's Q2 spec is the Module Gallery at fv-vibehub.pages.dev. Per pact/platform/gallery/gallery-contract.md, the gallery shows FVS modules that can be adopted. The 11 invariants include:
- I.5 self-registration (modules register themselves)
- I.6 reports not entries (a module is in the gallery if it has at least one report)
- I.9 dual-purpose (a module is BOTH a standalone thing AND a substrate for other apps)
<anchor-rail> qualifies:
- ✅ Self-registration: lives at
user/assets/anchor-rail.jswith amodule.json-like header (JSDoc, doctrine, examples) - ✅ Reports: this decision doc (D-067) + the usage in docHub + the prompt viewer's
renderAnchorRaillegacy = 3+ reports - ✅ Dual-purpose: standalone (drop in any doc) + substrate (FVS apps adopt)
- ✅ Tier: off-the-shelf (off-the-shelf, generic — not FVS-specific, not proprietary)
To formally enter the gallery, <anchor-rail> needs:
- A
module-register.jsonentry (TBD) - A per-module Recipe Book at
user/assets/anchor-rail/recipe-book/(TBD) - A
codex.mdandlineage.json(TBD)
This decision commits to creating those after the operator confirms the basic module works.
What was wrong with D-065/D-066
D-065 named a sidebar section-nav component <anchor-bar> and called it a "reusable module." D-066 corrected the classification to "chip" because the section-nav has only one capability.
Both missed the operator's actual ask. The operator was never asking for a sidebar section-nav — they were asking for the right-rail anchor timeline that already exists in the prompt viewer, generalized to work on any document in docHub and liftable to FVS Studio.
The correct classification of the right-rail anchor timeline is module (level 3), because it's a real cluster of 7 features.
Open follow-ups
- Mount
<anchor-rail>on the per-face doc pages (currently mounted on the SPA shell's doc detail view; needs to also work on the standalone/<face>/<doc>.htmlpages if the operator wants them to have the rail) - Port the prompt viewer's
renderAnchorRailto use<anchor-rail>internally (so we have ONE implementation across surfaces) - Write the module-register entry + per-module Recipe Book for gallery inclusion
- Document the localStorage persist key schema (currently
anchor-rail:hiddenor similar) - Wire
<anchor-rail>into Vibe Hub (Q2) for gallery entry detail views
Cross-references
mavis/decisions/D-060-dochub-substrate-shape.md— substrate shape withembeddable: truefor user face assetsmavis/decisions/D-064-clean-cloudflare-urls.md— clean Cloudflare URLs (the asset is served fromfv-dochub.pages.dev/assets/anchor-rail.js)pact/freshvibe-way-v7/10-modules-as-clusters.md— module = cluster of features (the doctrine that classifies anchor-rail as a module)pact/freshvibe-way-v7/23-two-view-substrate.md— two-view substrate patternpact/platform/gallery/gallery-contract.md— gallery contract (anchor-rail is gallery-eligible)mavis/prompts/_viewer/avidcreation.js— therenderAnchorRailfunction is the original implementation that this module extractsrecipe-book/recipe.md— docHub per-app Recipe Book (will list anchor-rail as an adopted module)