Strata Headers
Section headers pin to the top and stack up in order once you scroll past them, accumulating into a live table of contents built out of your reading progress.
npx shadcn@latest add https://webberui.com/r/strata-headers.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<StrataHeaders />
Installation
npx shadcn@latest add https://webberui.com/r/strata-headers.jsonUsage
Wrap each section in a StrataSection and its header row stacks at the top automatically once you scroll past it; clicking any header row scrolls back to that section:
import { StrataHeaders, StrataSection } from "@/components/ui/strata-headers";
<StrataHeaders headerHeight={40}>
<StrataSection title="Chapter one: Origins">
<p>Section content…</p>
</StrataSection>
<StrataSection title="Chapter two: Growth">
<p>Section content…</p>
</StrataSection>
<StrataSection title="Chapter three: Today">
<p>Section content…</p>
</StrataSection>
</StrataHeaders>The scroll container can be the page itself or any block with overflow-y: auto (like the demo above) — the component detects the nearest scrollable ancestor automatically, so there is no container ref to pass in.
Props
StrataHeaders
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | — | Put StrataSection children directly inside; each child is one section |
headerHeight | number | 40 | Height of each header row (px), which also sets the spacing between stacked layers |
className | string | — | Class appended to the outermost container |
StrataSection
| Prop | Type | Default | Description |
|---|---|---|---|
title | React.ReactNode | — | Section title, shown on the stackable header row |
children | React.ReactNode | — | Section content |
id | string | — | Anchor id applied to the header row |
className | string | — | Class appended to the header row button (for example, to override the default bg-white dark:bg-neutral-950 to match your page background) |
How it works
- The stacking is done natively in CSS: header row
igetsposition: sticky; top: i * headerHeight, so the layout is not driven by scroll events and scrolling performs exactly as it does natively - Pinning detection uses IntersectionObserver sentinels — each section places a 1px in-flow sentinel before its header row, and the pinned state flips as soon as the sentinel is scrolled past the pinning line, which costs almost nothing
- When pinned, the type size shrinks (a transform scale, which does not trigger layout), an underline and a shadow appear, and the section number marks its place in the stack
- Clicking any header row (pinned or not) smoothly scrolls back to the top of that section, with the scroll target already offset by the height of the stack above, so the start of the section is never covered
- When the user has "reduce motion" enabled at the system level, the sticky stacking is kept because it is layout rather than motion, but the scale / shadow transitions and the smooth scrolling are both disabled (jumping straight into position)
- The header row is a native
<button>: keyboard focusable, activated with Enter, and the pinned section is markedaria-current
Atlas Plate
A field-guide style scroll walkthrough — a full-bleed plate is pinned on one side and numbered notes run down the other; scrolling to a note pans and zooms the camera to its hotspot and lights up the numbered leader line.
Waypoint Stage
Place each passage of content by coordinate on an oversized canvas, and let scroll drive a camera that pans, zooms, and rotates along a waypoint path for a Prezi-style spatial narrative.