Layout 516
edge_to_edgeText Align Left2 ColumnsImage/Video RightButtonsMultiple ImagesImage

Code
relume-layout516.tsx
"use client";
import React, { useRef } from "react";
import { motion, useScroll, useTransform } from "motion/react";
import { useMediaQuery } from "@/hooks/use-media-query";
import { Button, type ButtonProps } from "@/components/ui/button";
import { ChevronRight } from "relume-icons";
type ImageProps = {
src: string;
alt?: string;
};
type ContentProps = {
tagline: string;
heading: string;
description: string;
buttons: ButtonProps[];
image: ImageProps;
imageMobile: ImageProps;
};
type Props = {
data: ContentProps[];
};
export type Layout516Props = React.ComponentPropsWithoutRef<"section"> & Partial<Props>;
export const Layout516 = (props: Layout516Props) => {
const { data } = {
...Layout516Defaults,
...props,
};
const isMobile = useMediaQuery("(max-width: 767px)");
const containerRef = useRef<HTMLDivElement>(null);
const { scrollYProgress } = useScroll({
target: containerRef,
offset: ["start center", "end center"],
});
return (
<section ref={containerRef} className="px-[5%] py-16 md:px-0 md:py-0">
<div className="grid grid-cols-1 md:grid-cols-2">
<div>
<div className="flex flex-col gap-y-12 md:sticky md:top-0 md:items-end md:gap-y-0">
{data.map((item, index) => {
const startProgress = index / data.length;
const endProgress = (index + 1) / data.length;
const opacity = useTransform(
scrollYProgress,
[
Math.max(0, startProgress - 0.07),
startProgress,
endProgress - 0.07,
Math.min(1, endProgress),
],
[0, 1, 1, 0],
);
const motionStyles = {
opacity: opacity,
};
if (isMobile) {
return (
<div key={index}>
<div className="max-w-md">
<p className="mb-3 font-semibold">{item.tagline}</p>
<h2 className="mb-5 text-h3 font-bold">{item.heading}</h2>
<p>{item.description}</p>
<div className="mt-6 flex flex-wrap items-center gap-4">
{item.buttons.map((button, buttonIndex) => (
<Button key={buttonIndex} {...button}>
{button.title}
</Button>
))}
</div>
</div>
<div className="mt-8">
<img
src={item.imageMobile.src}
alt={item.imageMobile.alt}
className="size-full object-cover"
/>
</div>
</div>
);
}
return (
<motion.div
key={index}
style={motionStyles}
className="md:absolute md:flex md:h-screen md:flex-col md:justify-center"
>
<div className="max-w-md md:mr-20 md:ml-[5vw]">
<p className="font-semibold md:mb-4">{item.tagline}</p>
<h2 className="text-h2 font-bold md:mb-6">{item.heading}</h2>
<p className="text-medium">{item.description}</p>
<div className="flex flex-wrap items-center gap-4 md:mt-8">
{item.buttons.map((button, buttonIndex) => (
<Button key={buttonIndex} {...button}>
{button.title}
</Button>
))}
</div>
</div>
</motion.div>
);
})}
</div>
</div>
<div className="hidden md:block">
{data.map((item, index) => (
<div key={index} className="md:h-screen">
<img src={item.image.src} alt={item.image.alt} className="size-full object-cover" />
</div>
))}
</div>
</div>
</section>
);
};
export const Layout516Defaults: Props = {
data: [
{
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.",
image: {
src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-1-portrait.svg",
alt: "Relume placeholder image 1",
},
imageMobile: {
src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-1.svg",
alt: "Relume placeholder image 1",
},
buttons: [
{ title: "Button", variant: "secondary" },
{
title: "Button",
variant: "link",
size: "link",
iconRight: <ChevronRight className="text-scheme-text" />,
},
],
},
{
tagline: "Tagline",
heading: "Short heading 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.",
image: {
src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-2-portrait.svg",
alt: "Relume placeholder image 2",
},
imageMobile: {
src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-2.svg",
alt: "Relume placeholder image 2",
},
buttons: [
{ title: "Button", variant: "secondary" },
{
title: "Button",
variant: "link",
size: "link",
iconRight: <ChevronRight className="text-scheme-text" />,
},
],
},
{
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.",
image: {
src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-3-portrait.svg",
alt: "Relume placeholder image 3",
},
imageMobile: {
src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-3.svg",
alt: "Relume placeholder image 3",
},
buttons: [
{ title: "Button", variant: "secondary" },
{
title: "Button",
variant: "link",
size: "link",
iconRight: <ChevronRight className="text-scheme-text" />,
},
],
},
{
tagline: "Tagline",
heading: "Short heading 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.",
image: {
src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-4-portrait.svg",
alt: "Relume placeholder image 4",
},
imageMobile: {
src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-4.svg",
alt: "Relume placeholder image 4",
},
buttons: [
{ title: "Button", variant: "secondary" },
{
title: "Button",
variant: "link",
size: "link",
iconRight: <ChevronRight className="text-scheme-text" />,
},
],
},
],
};npm i @radix-ui/react-dialog @radix-ui/react-slot @radix-ui/react-tabs class-variance-authority@0.7.1 clsx@^2.1.1 motion@^12.15.0 relume-icons@1.3.0 tailwind-merge@^2.2.2


