Blocks

Portfolio 22

standard
2 ColumnsText Align LeftImage/Video LeftTagsMultiple ImagesImage
Portfolio 22

Code

relume-portfolio22.tsx
"use client";

import { useState } from "react";
import clsx from "clsx";
import { Badge } from "@/components/ui/badge";

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

type HoverLinkProps = {
  url: string;
  heading: string;
  tag: string;
  image: ImageProps;
};

type Props = {
  hoverLinks: HoverLinkProps[];
};
export type Portfolio22Props = React.ComponentPropsWithoutRef<"section"> & Partial<Props>;

export const Portfolio22 = (props: Portfolio22Props) => {
  const { hoverLinks } = {
    ...Portfolio22Defaults,
    ...props,
  };
  const [hoveredIndex, setHoveredIndex] = useState<null | number>(null);

  const handleMouseLeave = () => () => {
    setHoveredIndex(null);
  };
  return (
    <section className="px-[5%] py-16 md:py-24 lg:py-28">
      <div className="container">
        <div className="lg:grid-auto-cols-fr relative block lg:grid lg:grid-cols-2 lg:items-start">
          <div
            className="lg:col-start-2 lg:col-end-3 lg:row-start-1 lg:row-end-2 lg:pl-10"
            onMouseLeave={handleMouseLeave}
          >
            {hoverLinks.map((link, index) => (
              <div key={index}>
                <div className="absolute top-0 right-0 left-0 hidden h-full w-1/2 pr-10 lg:block">
                  <div
                    className={clsx(
                      "sticky top-8 h-full max-h-[60vh] overflow-hidden",
                      index === 0 ? "z-0" : "-z-10",
                      hoveredIndex === index || hoveredIndex === null ? "opacity-100" : "opacity-0",
                    )}
                  >
                    <img
                      className="size-full rounded-image object-cover"
                      src={link.image.src}
                      alt={link.image.alt}
                    />
                  </div>
                </div>
                <a
                  href="#"
                  className="relative z-10 flex flex-col flex-wrap items-start justify-start gap-4 border-b border-scheme-border py-5 no-underline transition-all duration-300 sm:flex-row sm:items-center md:gap-8 md:py-6"
                  onMouseEnter={() => setHoveredIndex(index)}
                >
                  <div
                    className={clsx(
                      "text-h5 font-bold transition-colors duration-300",
                      hoveredIndex === null || hoveredIndex === index
                        ? "text-scheme-text"
                        : "lg:text-scheme-text/20",
                    )}
                  >{`${index < 10 && "0"}${index + 1}`}</div>
                  <div className="flex flex-wrap items-center gap-4">
                    <h3
                      className={clsx(
                        "text-h4 font-bold transition-colors duration-300",
                        hoveredIndex === null || hoveredIndex === index
                          ? "text-scheme-text"
                          : "lg:text-scheme-text/20",
                      )}
                    >
                      {link.heading}
                    </h3>
                    <Badge>{link.tag}</Badge>
                  </div>
                </a>
              </div>
            ))}
          </div>
        </div>
      </div>
    </section>
  );
};

export const Portfolio22Defaults: Props = {
  hoverLinks: [
    {
      url: "#",
      heading: "Project name",
      tag: "Tag",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-1.svg",
        alt: "Relume placeholder image 1",
      },
    },
    {
      url: "#",
      heading: "Project name",
      tag: "Tag",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-2.svg",
        alt: "Relume placeholder image 2",
      },
    },
    {
      url: "#",
      heading: "Project name",
      tag: "Tag",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-3.svg",
        alt: "Relume placeholder image 3",
      },
    },
    {
      url: "#",
      heading: "Project name",
      tag: "Tag",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-4.svg",
        alt: "Relume placeholder image 4",
      },
    },
    {
      url: "#",
      heading: "Project name",
      tag: "Tag",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-5.svg",
        alt: "Relume placeholder image 5",
      },
    },
    {
      url: "#",
      heading: "Project name",
      tag: "Tag",
      image: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-6.svg",
        alt: "Relume placeholder image 6",
      },
    },
  ],
};

npm i @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