Section Rail
Use Section Rail to navigate a long page, case study, or multi-step flow. The active line reflects the section in view and can reveal labels or a context card for nearby sections.
Preview
Overview
The goal, scope, and main ideas behind this page. A rail answers three questions at a glance: where am I, what is nearby, and where can I go.
Interaction
How the component responds to hover, focus, and scroll. A rail answers three questions at a glance: where am I, what is nearby, and where can I go.
Motion
Direct scroll progress, no bounce, no ambient animation. A rail answers three questions at a glance: where am I, what is nearby, and where can I go.
Accessibility
Labels on focus, aria-current, and reduced-motion support. A rail answers three questions at a glance: where am I, what is nearby, and where can I go.
Installation
Basic usage
Examples
Use scroll mode for heading anchors in a document. Use controlled mode when the active section is managed outside the rail.
Usage
API
| Property | Type | Default | Description |
|---|---|---|---|
items | SectionRailItem[] | required | The sections represented by the rail, in document order. |
mode | "scroll" | "controlled" | "scroll" | `"scroll"` resolves the active item from the page; `"controlled"` leaves it to `activeId` / `onActiveChange`. |
activeId | string | undefined | Active item id for controlled usage. |
defaultActiveId | string | undefined | Initial active item id for uncontrolled usage. |
onActiveChange | (id: string) => void | undefined | Called whenever the active item changes. |
showLabels | "hidden" | "always" | "active" | "hover" | "hidden" | When labels sit next to the rail on fine pointers. Defaults to `"hidden"` because the context card already names the section, so an inline label would only appear underneath it. Coarse pointers, which never get a card, always show labels. |
activeIndicator | "dot" | "fill" | "progress" | "progress" | How the active item is marked. `"progress"` fills the active line from the section's own scroll position. |
orientation | "vertical" | "horizontal" | "vertical" | Direction the rail runs in. |
side | "left" | "right" | "right" | Which edge the rail is anchored to, which flips label and card placement. |
scrollOffset | number | 96 | Pixels of fixed-header compensation used when scrolling to a section. |
getSectionElement | (id: string) => HTMLElement | null | undefined | Resolves a section element when ids are not real DOM ids. |
scrollRoot | React.RefObject<HTMLElement | null> | undefined | Scrolling element the sections live in. Defaults to the page itself. |
showPreview | "never" | "hover" | "focus" | "hover-and-focus" | "hover-and-focus" | When the context card appears. Cards never appear on coarse pointers. |
ariaLabel | string | "Section navigation" | Accessible name for the rail's landmark. |
className | string | undefined | Additional classes for the rail root. |
lineClassName | string | undefined | Additional classes for each rail line. |
labelClassName | string | undefined | Additional classes for the item labels. |
previewClassName | string | undefined | Additional classes for the context card surface. |
Accessibility
Scroll mode uses real anchor links, while controlled mode uses buttons with the current section exposed to assistive technology. Coarse pointers receive visible labels instead of hover-only targets.
Styling
Use the context-card options for additional editorial detail. Reduced-motion preferences simplify the rail and scroll behavior.
Contact
Found a bug or need help using this component? Open an issue on GitHub.
License & Usage
Sona UI is available under the MIT license. You may use, modify, and distribute this component in personal and commercial projects.
Was this page helpful?
Your response helps us improve component docs.