# Gallery 4

> gallery

A shadcn/ui block from the [@cnippet-blocks](https://blocks.cnippet.dev/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/cnippet-blocks/gallery-4).

- Page, with a live preview: https://shadcn.exchange/r/cnippet-blocks/gallery-4
- Screenshot: https://shadcn.exchange/shot/cnippet-blocks/gallery-4/light.png
- Registry item: https://blocks.cnippet.dev/r/gallery-4.json
- Categories: gallery
- Listed: 2026-11-01, source last changed 2026-11-01

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/cnippet-blocks/gallery-4.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `@cnippet/utils`

## Files

### `registry/default/sections/gallery-4.tsx`

```tsx
"use client";

import { Marquee } from "@/components/ui/marquee";

const galleryImages = [
  {
    alt: "Office workspace",
    id: "1",
    src: "https://images.unsplash.com/photo-1497366216548-37526070297c?w=500&h=700&fit=crop",
  },
  {
    alt: "Team collaboration",
    id: "2",
    src: "https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=500&h=700&fit=crop",
  },
  {
    alt: "Working at desk",
    id: "3",
    src: "https://images.unsplash.com/photo-1600880292203-757bb62b4baf?w=500&h=700&fit=crop",
  },
  {
    alt: "Team meeting",
    id: "4",
    src: "https://images.unsplash.com/photo-1517245386807-bb43f82c33c4?w=500&h=700&fit=crop",
  },
  {
    alt: "Creative workspace",
    id: "5",
    src: "https://images.unsplash.com/photo-1552581234-26160f608093?w=500&h=700&fit=crop",
  },
  {
    alt: "Conference room",
    id: "6",
    src: "https://images.unsplash.com/photo-1542744173-8e7e53415bb0?w=500&h=700&fit=crop",
  },
];

function GalleryImage({ image }: { image: (typeof galleryImages)[0] }) {
  return (
    <div className="relative h-112.5 w-72 shrink-0 overflow-hidden rounded-2xl">
      <img
        alt={image.alt}
        className="h-full w-full object-cover transition-transform duration-500 hover:scale-105"
        src={image.src}
      />
    </div>
  );
}

export function Gallery4() {
  return (
    <section className="w-full overflow-hidden bg-gray-50 py-16 md:py-20 dark:bg-neutral-900">
      {/* Header - Centered */}
      <div className="mx-auto mb-12 w-full max-w-4xl px-6 text-center">
        <span className="mb-4 block font-medium text-sm text-violet-600 tracking-wide">
          Gallery
        </span>

        <h2 className="mb-6 font-normal text-3xl text-gray-900 leading-tight md:text-4xl lg:text-5xl dark:text-gray-100">
          Team{" "}
          <span className="relative inline-block">
            <span className="relative z-10">Gallery</span>
            <span
              className="absolute inset-0 z-0 -skew-x-3 bg-violet-200 dark:bg-violet-900/30"
              style={{
                bottom: "10%",
                left: "-4%",
                right: "-4%",
                top: "10%",
              }}
            />
          </span>
        </h2>

        <p className="text-gray-600 text-sm sm:text-base dark:text-gray-400">
          Check out what our team has been up to lately
        </p>
      </div>

      {/* Gallery Marquee - Tall images */}
      <Marquee className="[--duration:45s] [--gap:1.5rem]" pauseOnHover>
        {galleryImages.map((image) => (
          <GalleryImage image={image} key={image.id} />
        ))}
      </Marquee>
    </section>
  );
}

export default Gallery4;
```

### `components/ui/marquee.tsx`

```tsx
//biome-ignore-all lint/suspicious/noArrayIndexKey:<>

import type { ComponentPropsWithoutRef } from "react";
import { cn } from "@/lib/utils";

interface MarqueeProps extends ComponentPropsWithoutRef<"div"> {
  className?: string;
  reverse?: boolean;
  pauseOnHover?: boolean;
  children: React.ReactNode;
  vertical?: boolean;
  repeat?: number;
}

export function Marquee({
  className,
  reverse = false,
  pauseOnHover = false,
  children,
  vertical = false,
  repeat = 4,
  ...props
}: MarqueeProps) {
  return (
    <div
      {...props}
      className={cn(
        "group flex gap-(--gap) overflow-hidden p-2 [--duration:40s] [--gap:1rem]",
        {
          "flex-col": vertical,
          "flex-row": !vertical,
        },
        className,
      )}
    >
      {Array(repeat)
        .fill(0)
        .map((_, i) => (
          <div
            className={cn("flex shrink-0 justify-around gap-(--gap)", {
              "animate-marquee flex-row": !vertical,
              "animate-marquee-vertical flex-col": vertical,
              "direction-[reverse]": reverse,
              "group-hover:paused": pauseOnHover,
            })}
            key={i}
          >
            {children}
          </div>
        ))}
    </div>
  );
}
```
