Every project opens with a week of questions and no drawings at all.
Early ideas stay on paper, where they are cheap to change.
Card and timber models tell the truth about light and scale.
The same small team stays with the work until the keys are handed over.
A visit a year later, to see how the place is being lived in.
A horizontal rail that carries any content sideways: chapters, a timeline, case studies or cards. Travel comes from a number or a live scroll value, or the rail drifts slowly on its own. Inside a panel, data-rail-depth makes an element drift against the travel for depth and data-rail-focus dims it until its panel reaches the middle. One animation loop writes transforms on whole pixels and pauses offscreen. Reduced motion turns the rail into a plain horizontal scroll.
The import and the props worth knowing about, in one place.
import { ScrollRail } from "@/components/beste/component/scroll-rail";
<ScrollRail className="h-[32rem]" gap={48}>
<div className="w-[26rem]">
<span data-rail-depth="0.3" className="text-7xl">01</span>
<h3 data-rail-focus="0.4">Listen first</h3>
</div>
<div className="w-[26rem]">
<span data-rail-depth="0.3" className="text-7xl">02</span>
<h3 data-rail-focus="0.4">Draw by hand</h3>
</div>
</ScrollRail>
// Driven by scroll: pass a framer-motion MotionValue (or any { get() }) as progress
const { scrollYProgress } = useScroll({ target: ref, offset: ["start start", "end end"] });
<ScrollRail progress={scrollYProgress} autoplay={false}>{panels}</ScrollRail>Read from the component's own type, so this cannot drift from what it accepts.
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | The panels along the rail, in order. Each child is one panel and sets its own width. Inside a panel, `data-rail-depth="0.3"` makes an element drift against the travel and `data-rail-focus="0.35"` dims it until its panel reaches the middle. |
gap | number | — | Space between panels in pixels; narrow screens use less. |
align | "start" | "center" | "end" | "stretch" | — | Where panels sit vertically inside the rail. |
progress | number | ScrollRailProgressSource | — | Travel along the rail from start (0) to end (1): a number or a live source such as a scroll MotionValue. |
autoplay | boolean | — | Drift slowly along the rail and back on its own. Defaults to on when no progress is given. |
speed | number | — | Pace of the autoplay drift. |
smoothing | number | — | How softly the rail follows its progress, 0 (instant) to 1 (very soft). |
paused | boolean | — | Stop the rail where it is. |
className | string | — |