Getting Started
Scroll Animations
Text Animations
Hover & Cursor
Sliders & Carousels
Galleries & Grids
Menus & Navigation
Preloaders & Intros
Page Transitions
3D & WebGL
import { RotateStackScroll } from "@/registry/tween/rotate-stack-scroll"
export function RotateStackScrollDemo() {
return (
<div className="w-full [font-family:ui-sans-serif,system-ui,sans-serif]">
<RotateStackScroll />
</div>
)
}
import { RotateStackScroll } from "@/components/ui/rotate-stack-scroll"<RotateStackScroll
panels={[
{ bg: "#d8cfc0", color: "#000", heading: "Overture", copy: "…" },
{
bg: "#141210",
color: "#fff",
heading: "Reach",
img: "https://images.unsplash.com/photo-1788507121644-6f068e46cfa0?q=80&w=900&h=1125&fit=crop&crop=center&auto=format",
},
]}
/>Each panel starts rotated by angle degrees around its bottom-left corner. A scrubbed ScrollTrigger rotates it back to flat as it travels from the bottom of the viewport up to the top, while a second trigger pins every panel except the last as it leaves — so each colored sheet swings upright and stacks over the previous one, the page assembling like a tilted deck of cards righting itself.
The triggers resolve their scroller via usePreviewScroller, so it works inside the docs preview and on a real page; a ResizeObserver plus a window.resize listener rebuild them on layout changes. When prefers-reduced-motion: reduce is set, the panels render flat and stacked with no rotation or pinning.
| Prop | Type | Default | Description |
|---|---|---|---|
panels | RotateStackPanel[] | 6 defaults | Each is { bg, color, heading, copy?, img? }. |
angle | number | 30 | Starting tilt of each panel in degrees. |
scroller | HTMLElement | string | null | — | Optional ScrollTrigger scroller; defaults to the ambient preview scroller. |
className | string | — | Extra classes on the outer container. |