Layout 415
standardText Align Left2 ColumnsIconsButtons

Code
relume-layout415.tsx
"use client";
import { useRef } from "react";
import { MotionStyle, motion, useScroll, useTransform } from "motion/react";
import { useMediaQuery } from "@/hooks/use-media-query";
import { Button, type ButtonProps } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { ChevronRight, RelumeIcon } from "relume-icons";
type FeatureSectionProps = {
title: string;
description: string;
};
type Props = {
tagline: string;
heading: string;
description: string;
buttons: ButtonProps[];
featureSections: FeatureSectionProps[];
};
export type Layout415Props = React.ComponentPropsWithoutRef<"section"> & Partial<Props>;
const MotionCard = motion.create(Card) as any;
export const Layout415 = (props: Layout415Props) => {
const { tagline, heading, description, buttons, featureSections } = {
...Layout415Defaults,
...props,
};
const isMobile = useMediaQuery("(max-width: 767px)");
const containerRef = useRef<HTMLDivElement>(null);
const { scrollYProgress } = useScroll({
target: containerRef,
offset: isMobile ? ["20% start", "end end"] : ["start start", "end end"],
});
return (
<section ref={containerRef} className="px-[5%]">
<div className="container">
<div className="relative h-[300svh] lg:h-[300vh]">
<div className="static grid h-full grid-cols-1 content-start items-center gap-x-20 gap-y-16 py-16 md:sticky md:top-0 md:h-svh md:grid-cols-2 md:content-normal md:py-0 lg:h-screen">
<div>
<p className="mb-3 font-semibold md:mb-4">{tagline}</p>
<h2 className="mb-5 text-h2 font-bold md:mb-6">{heading}</h2>
<p className="text-medium">{description}</p>
<div className="mt-6 flex flex-wrap items-center gap-4 md:mt-8">
{buttons.map((button, index) => (
<Button key={index} {...button}>
{button.title}
</Button>
))}
</div>
</div>
<div className="sticky top-[25%] flex min-h-[24.5rem] flex-col items-center justify-center md:relative md:top-0 md:min-h-[auto]">
{featureSections.map((section, index) => (
<FeatureSection
key={index}
section={section}
index={index}
totalSections={featureSections.length}
scrollYProgress={scrollYProgress}
/>
))}
</div>
</div>
</div>
</div>
<div className="absolute inset-0 -z-10 mt-[100vh]" />
</section>
);
};
const FeatureSection = ({
section,
index,
totalSections,
scrollYProgress,
}: {
section: FeatureSectionProps;
index: number;
totalSections: number;
scrollYProgress: any;
}) => {
const sectionScrollStart = index / totalSections;
const sectionScrollEnd = (index + 1) / totalSections;
const rotate = useTransform(
scrollYProgress,
[sectionScrollStart, sectionScrollEnd],
[0 + index * 3, -30],
);
const y = useTransform(
scrollYProgress,
[sectionScrollStart, sectionScrollEnd],
["0vh", "-100vh"],
);
const x = useTransform(scrollYProgress, [sectionScrollStart, sectionScrollEnd], ["0vw", "-10vw"]);
return (
<MotionCard
className="absolute mx-6 flex flex-col justify-between p-8 md:ml-0"
style={
{
rotate: index === totalSections - 1 ? "9deg" : rotate,
y: index === totalSections - 1 ? undefined : y,
x: index === totalSections - 1 ? undefined : x,
zIndex: totalSections - index,
} as MotionStyle
}
>
<div className="mb-6 md:mb-8">
<RelumeIcon className="size-12 text-scheme-text" />
</div>
<h3 className="mb-3 text-h5 font-bold md:mb-4">{section.title}</h3>
<p>{section.description}</p>
</MotionCard>
);
};
export const Layout415Defaults: Props = {
tagline: "Tagline",
heading: "Medium length section heading goes here",
description:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat.",
buttons: [
{ title: "Button", variant: "secondary" },
{
title: "Button",
variant: "link",
size: "link",
iconRight: <ChevronRight className="text-scheme-text" />,
},
],
featureSections: [
{
title: "Subheading one",
description:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat.",
},
{
title: "Subheading two",
description:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat.",
},
{
title: "Subheading three",
description:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat.",
},
{
title: "Subheading four",
description:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat.",
},
],
};npm i @radix-ui/react-slot class-variance-authority@0.7.1 clsx@^2.1.1 motion@^12.15.0 relume-icons@1.3.0 tailwind-merge@^2.2.2


