Section Rail
A compact reading rail that tracks progress through page sections.
Demo
Overview
A quick read of the page.
Goals
What the work needs to solve.
Scope
The boundaries of the work.
Approach
The chosen direction.
Structure
How the pieces fit together.
Components
The parts used in the interface.
States
Pending, active, and complete.
Motion
How the rail responds.
Accessibility
Labels and keyboard focus.
Performance
Keeping updates lightweight.
Delivery
What ships with the component.
Summary
The final result.
Installation
pnpm dlx shadcn@latest add https://ui.arjayby1.xyz/r/section-rail.jsonCode
import { SectionRail } from "@/components/ui/section-rail";
const sections = [
{ id: "overview", label: "Overview" },
{ id: "installation", label: "Installation" },
{ id: "api-reference", label: "API reference" },
];
export function ArticlePage() {
return (
<>
<SectionRail
sections={sections}
activeMarkerLength="short"
gap={0}
className="fixed top-1/2 left-4 -translate-y-1/2"
/>
<article>{/* Sections with matching IDs */}</article>
</>
);
}API reference
| Prop | Type | Default | Purpose |
|---|---|---|---|
sections | { id: string; label: string }[] | Required | Maps each marker to an element ID and readable label. |
activeOffset | number | 0.36 | Sets the reading line within the nearest scroll container. |
activeMarkerLength | "long" | "short" | "long" | Sets the resting width of the active marker. |
ariaLabel | string | "Page sections" | Names the navigation landmark. |
gap | CSSProperties["gap"] | 0 | Sets the gap between section markers. |
className | string | None | Controls placement in the consuming page. |
The active link receives aria-current="location". Use activeMarkerLength="short" to keep it at the base width or "long" to extend it. Both options retain the full-opacity active color. Every link also exposes data-state="pending", active, or complete for styling. Hovering or focusing a link expands the nearby markers into a tapered group.