Skip to content

pastetaste

Taper Rail

MediaAnimationInteraction

A row of cards tapering away from a big front card. On a timer the row steps and the next card grows into the middle; slide content gets an active flag to start its own motion. Drag, click, keys, pause.

Paste it into Claude Code: it installs the component, asks for your brand and content, and places it on the page you name.

Install

Or by hand, in a Next.js project with shadcn set up. It writes components/pastetaste/taper-rail.tsx.

npx shadcn@latest add https://pastetaste.com/r/taper-rail.json

Prompt

Install the pastetaste component "Taper Rail" (taper-rail) into this Next.js project and use it.

What it is: A row of cards tapering away from a big front card. On a timer the row steps and the next card grows into the middle; slide content gets an active flag to start its own motion. Drag, click, keys, pause.

Before you place it, ask me (one short message, in my language; "skip" = sensible default): which page and section it goes in, my brand colour (hex), the text, labels or images it should show, and whether I want my logo or my own copy in the demo content. Then:

Steps:
1. If the project has no components.json, run: npx shadcn@latest init -y -d. Then run: npx shadcn@latest add https://pastetaste.com/r/taper-rail.json
   (it writes components/pastetaste/taper-rail.tsx; install any npm packages it reports).
2. Import it: import { TaperRail } from "@/components/pastetaste/taper-rail" (check the file for the exact named export).
3. Put it where it fits on the page I name, keep the rest of the page untouched, and pass props for my colours instead of editing the installed file.
4. It does not depend on Tailwind; colours come from CSS variables (--pt-*) or props.
5. It supports prefers-reduced-motion with a still state; do not remove it.

Props (all optional unless the type has no default):
- slides: TaperRailSlide[] — The cards: a label for screen readers and the pagination, plus an image, content, or both. content can be a function that receives { active, index, offset } so it animates only while in front.
- start: number = 0 — Slide in front on the first render.
- index: number — Bring this slide to the front whenever it changes, for outside controls.
- onActiveChange: (index: number) => void — Called when the slide in front changes (at the start of the move).
- interval: number = 3 — Seconds each slide holds the front before the row steps on; 0 turns autoplay off and hides the pause button.
- duration: number = 0.7 — Seconds one step of travel takes; jumps of several places take a little longer.
- visible: number = 4 — Cards shown on each side of the front card (fewer when there are not enough slides to loop).
- falloff: number = 0.4 — How fast cards shrink with distance: scale = 1 / (1 + falloff × places). 0 keeps them all the same size.
- reveal: number = 0.5 — Share of each side card that shows past its inner neighbour, 0.2 to 0.9.
- aspect: number = 1 — Card width over height.
- cardWidth: string = fits the row to the width — CSS width of the front card, e.g. 'clamp(240px, 30%, 420px)'. Cards past the edges are clipped.
- radius: number = 18 — Corner radius in px at front-card size; side cards scale it with them.
- drift: number = 0.05 — Slow push-in on the front card's image while it holds the front; 0 turns it off.
- loop: boolean = true — Wrap around endlessly. Off, the row stops at both ends and drags past them with resistance.
- controls: boolean = true — Control bar under the row: previous, one dash per slide with the hold timer in the current one, next, pause.
- label: string = 'Carousel' — Accessible name of the carousel.
- className / style: string / CSSProperties — On the root. Restyle with --pt-taper-card, --pt-taper-shadow, --pt-taper-ink, --pt-taper-button, --pt-taper-pad and --pt-taper-bar-gap.

Live preview: https://pastetaste.com/c/taper-rail
Then run the dev server, check it in the browser at desktop and mobile width, and tell me the page URL.

Props 17

PropTypeDefaultDescription
slidesTaperRailSlide[]-The cards: a label for screen readers and the pagination, plus an image, content, or both. content can be a function that receives { active, index, offset } so it animates only while in front.
startnumber0Slide in front on the first render.
indexnumber-Bring this slide to the front whenever it changes, for outside controls.
onActiveChange(index: number) => void-Called when the slide in front changes (at the start of the move).
intervalnumber3Seconds each slide holds the front before the row steps on; 0 turns autoplay off and hides the pause button.
durationnumber0.7Seconds one step of travel takes; jumps of several places take a little longer.
visiblenumber4Cards shown on each side of the front card (fewer when there are not enough slides to loop).
falloffnumber0.4How fast cards shrink with distance: scale = 1 / (1 + falloff × places). 0 keeps them all the same size.
revealnumber0.5Share of each side card that shows past its inner neighbour, 0.2 to 0.9.
aspectnumber1Card width over height.
cardWidthstringfits the row to the widthCSS width of the front card, e.g. 'clamp(240px, 30%, 420px)'. Cards past the edges are clipped.
radiusnumber18Corner radius in px at front-card size; side cards scale it with them.
driftnumber0.05Slow push-in on the front card's image while it holds the front; 0 turns it off.
loopbooleantrueWrap around endlessly. Off, the row stops at both ends and drags past them with resistance.
controlsbooleantrueControl bar under the row: previous, one dash per slide with the hold timer in the current one, next, pause.
labelstring'Carousel'Accessible name of the carousel.
className / stylestring / CSSProperties-On the root. Restyle with --pt-taper-card, --pt-taper-shadow, --pt-taper-ink, --pt-taper-button, --pt-taper-pad and --pt-taper-bar-gap.
View all →