Blocks

Banner 16

Banners
Text Align CenterImage
Banner 16

Code

relume-banner16.tsx
"use client";

import React, { useRef } from "react";
import { useScroll, useTransform, motion } from "motion/react";
import { useMediaQuery } from "@/hooks/use-media-query";

type ImageProps = {
  src: string;
  alt?: string;
};

type Heading = {
  title: string;
  image: ImageProps;
};

type Props = {
  headingsTop: Heading[];
  headingsBottom: Heading[];
};

export type Banner16Props = React.ComponentPropsWithoutRef<"section"> & Partial<Props>;

export const Banner16 = (props: Banner16Props) => {
  const { headingsTop, headingsBottom } = {
    ...Banner16Defaults,
    ...props,
  };

  const sectionRef = useRef<HTMLDivElement>(null);
  const { scrollYProgress } = useScroll({
    target: sectionRef,
    offset: ["start end", "end start"],
  });

  const isMobile = useMediaQuery("(max-width: 991px)");

  const headingTopTranslate = useTransform(
    scrollYProgress,
    [0, 1],
    isMobile ? ["5%", "0%"] : ["25%", "0%"],
  );
  const headingBottomTranslate = useTransform(
    scrollYProgress,
    [0, 1],
    isMobile ? ["-5%", "0%"] : ["-25%", "0%"],
  );

  const renderHeadings = (headings: Heading[], translate: any) => (
    <motion.div
      className="grid auto-cols-max grid-flow-col grid-cols-[max-content] items-center justify-around py-2"
      style={{ x: translate }}
    >
      {headings.map((heading, index) => (
        <Heading key={index} title={heading.title} image={heading.image} />
      ))}
    </motion.div>
  );

  return (
    <section
      ref={sectionRef}
      className="flex w-screen max-w-full flex-col justify-end overflow-hidden"
    >
      <div className="flex justify-end">{renderHeadings(headingsTop, headingTopTranslate)}</div>
      <div className="flex justify-start">
        {renderHeadings(headingsBottom, headingBottomTranslate)}
      </div>
    </section>
  );
};

const Heading = ({ title, image }: Heading) => {
  return (
    <React.Fragment>
      <div className="flex items-center justify-center px-4 text-center whitespace-nowrap lg:text-left">
        <h1 className="text-h5 font-bold">{title}</h1>
      </div>
      <div className="relative w-full overflow-hidden">
        <img
          src={image.src}
          alt={image.alt}
          className="aspect-square size-full max-h-16 rounded-image object-cover"
        />
      </div>
    </React.Fragment>
  );
};

export const Banner16Defaults: Props = {
  headingsTop: [
    {
      title: "Relume Library",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-landscape.svg",
        alt: "Relume Library 1",
      },
    },
    {
      title: "Relume Library",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-landscape.svg",
        alt: "Relume Library 2",
      },
    },
    {
      title: "Relume Library",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-landscape.svg",
        alt: "Relume Library 3",
      },
    },
    {
      title: "Relume Library",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-landscape.svg",
        alt: "Relume Library 4",
      },
    },
    {
      title: "Relume Library",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-landscape.svg",
        alt: "Relume Library 5",
      },
    },
    {
      title: "Relume Library",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-landscape.svg",
        alt: "Relume Library 6",
      },
    },
    {
      title: "Relume Library",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-landscape.svg",
        alt: "Relume Library 7",
      },
    },
    {
      title: "Relume Library",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-landscape.svg",
        alt: "Relume Library 8",
      },
    },
  ],

  headingsBottom: [
    {
      title: "Relume Library",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-landscape.svg",
        alt: "Relume Library 9",
      },
    },
    {
      title: "Relume Library",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-landscape.svg",
        alt: "Relume Library 10",
      },
    },
    {
      title: "Relume Library",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-landscape.svg",
        alt: "Relume Library 11",
      },
    },
    {
      title: "Relume Library",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-landscape.svg",
        alt: "Relume Library 12",
      },
    },
    {
      title: "Relume Library",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-landscape.svg",
        alt: "Relume Library 13",
      },
    },
    {
      title: "Relume Library",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-landscape.svg",
        alt: "Relume Library 14",
      },
    },
    {
      title: "Relume Library",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-landscape.svg",
        alt: "Relume Library 15",
      },
    },
    {
      title: "Relume Library",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-landscape.svg",
        alt: "Relume Library 16",
      },
    },
  ],
};

npm i @radix-ui/react-accordion @radix-ui/react-checkbox @radix-ui/react-label @radix-ui/react-radio-group @radix-ui/react-select @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