ns-ui / mercury-minimap

Mercury Minimap

A table of contents where scroll progress behaves like liquid metal. A blob climbs the rail on the right as you read, gooey-merging with each section tick it reaches, stretching into a droplet under fast scroll, and bulging toward whichever tick your cursor approaches.

Scroll this page slowly, then fling it. Hover the rail on the right to see the labels slide in and the liquid reach for your pointer. Click any tick to travel there.

See usage

Install

Zero dependencies beyond React. The whole effect is one SVG goo filter and a spring integrated in a requestAnimationFrame loop that sleeps when the liquid settles.

npx shadcn add @ns-ui/mercury-minimap

Everything is direct-DOM: the scroll listener is passive and only writes a ref, attribute writes happen via setAttribute, and no React state is touched on the hot path.

Usage

Pass sections explicitly, or mount it bare and it will discover every section[id] on the page, reading labels from data-minimap-label.

<MercuryMinimap
  sections={[
    { id: "intro", label: "00 INTRO" },
    { id: "api", label: "04 API" },
  ]}
/>

The blob target is a piecewise-linear map from your scroll position through the section offsets, so the liquid always agrees with where you actually are in the document.

Theming

The liquid fills with --color-foreground and unreached ticks stroke with --color-muted, falling back to the house values. Monochrome by design: the accent color is reserved for focus rings only.

foreground #ededed
muted #8f8f8f

Under prefers-reduced-motion the goo filter is swapped for a plain 2px filled line with dots, active states switch instantly, and no animation frame ever runs.

API

sections{ id, label }[]auto-discovered
selectorstring"section[id]"
offsetnumber0.35
stiffnessnumber120
dampingnumber20

Stiffness and damping tune the spring: 120 / 20 sits just under critical damping, so a hard scroll stop lands with one small wobble, exactly like a droplet coming to rest.