Scroll Split Card
componentry/scroll-split-cardFreeComponent
Component for scroll-split-card
Live preview
live · rendered by the registry
Rendered by componentry on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://componentry.dev/r/scroll-split-card.jsonSource
1"use client";23import { cn } from "@/lib/utils";4import { motion, useScroll, useTransform, useMotionTemplate } from "framer-motion";5import { useRef } from "react";67interface ScrollSplitCardItem {8 title: string;9 description: string;10 bgColor: string;11 textColor: string;12 icon?: React.ReactNode;13}1415interface ScrollSplitCardProps {16 className?: string;17 imageSrc: string;18 cards: ScrollSplitCardItem[];19 containerRef?: React.RefObject<HTMLElement | null>;20}2122export function ScrollSplitCard({23 className,24 imageSrc,25 cards,26 containerRef: externalContainerRef,27}: ScrollSplitCardProps) {28 const containerRef = useRef<HTMLDivElement>(null);2930 const { scrollYProgress } = useScroll({31 target: containerRef,32 container: externalContainerRef,33 offset: ["start start", "end end"],34 });3536 // Stage 1 to 2: Separation (0 to 0.4), then Stage 2 to 3: Overlap closer (0.4 to 0.8)37 const leftX = useTransform(scrollYProgress, [0, 0.4, 0.8], [0, -48, -24]);38 const rightX = useTransform(scrollYProgress, [0, 0.4, 0.8], [0, 48, 24]);39 const scale = useTransform(scrollYProgress, [0, 0.4], [1, 0.9]);4041 // Stage 2 to 3: Flip (0.4 to 0.8)42 const rotateY = useTransform(scrollYProgress, [0.4, 0.8], [0, 180]);43 // Due to 180deg Y flip, positive Z becomes visual counter-clockwise, negative Z becomes visual clockwise44 const rotateZLeft = useTransform(scrollYProgress, [0.4, 0.8], [0, 6]);45 const rotateZRight = useTransform(scrollYProgress, [0.4, 0.8], [0, -6]);4647 // Dynamic borders/radii so it looks like ONE flat image initially48 const borderRadiusLeft = useTransform(scrollYProgress, [0, 0.2], ["16px 0px 0px 16px", "16px 16px 16px 16px"]);49 const borderRadiusMiddle = useTransform(scrollYProgress, [0, 0.2], ["0px 0px 0px 0px", "16px 16px 16px 16px"]);50 const borderRadiusRight = useTransform(scrollYProgress, [0, 0.2], ["0px 16px 16px 0px", "16px 16px 16px 16px"]);51 const borderOpacity = useTransform(scrollYProgress, [0, 0.2], [0, 0.2]);52 const shadowOpacity = useTransform(scrollYProgress, [0, 0.2], [0, 0.4]);53 const boxShadow = useMotionTemplate`inset 0 1px 1px rgba(255, 255, 255, ${borderOpacity}), inset 0 -24px 48px rgba(0, 0, 0, ${shadowOpacity}), 0 25px 50px -12px rgba(0, 0, 0, ${shadowOpacity})`;5455 // Cards move up in the last viewport56 const cardsY = useTransform(scrollYProgress, [0.8, 1], [0, -200]);5758 // Text appearance at the end in the sticky viewport59 const textOpacity = useTransform(scrollYProgress, [0.8, 1], [0, 1]);60// … truncated
More from componentry
All 59 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Animated Gradientanimated-gradient | componentry | Components | Free | no deps | |
| ASCII Effectascii-effect | componentry | Components | Free | no deps | |
| Aurora Flowaurora-flow | componentry | Components | Free | no deps | |
| auth-modalauth-modal | componentry | Components | Free | no deps | |
| border-beamborder-beam | componentry | Components | Free | no deps | |
| circuit-boardcircuit-board | componentry | Components | Free | no deps | |
| closing-plasmaclosing-plasma | componentry | Components | Free | no deps | |
| collection-surfercollection-surfer | componentry | Components | Free | no deps |
