# Gallery 1

> 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-1).

- Page, with a live preview: https://shadcn.exchange/r/cnippet-blocks/gallery-1
- Screenshot: https://shadcn.exchange/shot/cnippet-blocks/gallery-1/light.png
- Registry item: https://blocks.cnippet.dev/r/gallery-1.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-1.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-1.tsx`

```tsx
"use client";

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

const galleryImages = [
  {
    height: "short",
    id: "1",
    location: "New York, USA",
    src: "https://images.unsplash.com/photo-1500382017468-9049fed747ef?w=800&h=600&fit=crop",
    year: "2024",
  },
  {
    height: "tall",
    id: "2",
    location: "Berlin, Germany",
    src: "https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=800&h=900&fit=crop",
    year: "2027",
  },
  {
    height: "short",
    id: "3",
    location: "Cape Town, South Africa",
    src: "https://images.unsplash.com/photo-1433086966358-54859d0ed716?w=800&h=600&fit=crop",
    year: "2028",
  },
  {
    height: "short",
    id: "4",
    location: "Rio de Janeiro, Brazil",
    src: "https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?w=800&h=600&fit=crop",
    year: "2025",
  },
  {
    height: "tall",
    id: "5",
    location: "Tokyo, Japan",
    src: "https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=800&h=900&fit=crop",
    year: "2026",
  },
  {
    height: "short",
    id: "6",
    location: "Sydney, Australia",
    src: "https://images.unsplash.com/photo-1469474968028-56623f02e42e?w=800&h=600&fit=crop",
    year: "2029",
  },
];

function GalleryCard({ image }: { image: (typeof galleryImages)[0] }) {
  return (
    <div className="flex flex-col">
      <div
        className={cn(
          "relative overflow-hidden rounded-xl",
          image.height === "tall" ? "h-80" : "h-56",
          "w-72",
        )}
      >
        <img
          alt={image.location}
          className="h-full w-full object-cover transition-transform duration-500 hover:scale-105"
          src={image.src}
        />
      </div>
      <div className="mt-4 flex items-center justify-between px-1">
        <span className="font-medium text-neutral-800 text-sm">
          {image.location}
        </span>
        <span className="text-neutral-500 text-sm">{image.year}</span>
      </div>
    </div>
  );
}

export function Gallery1() {
  return (
    <section className="w-full overflow-hidden bg-[#f5f5f5] py-16 md:py-24">
      <div className="mx-auto mb-12 w-full max-w-6xl px-6">
        {/* Header - Two column */}
        <div className="grid grid-cols-1 gap-8 lg:grid-cols-2">
          {/* Left */}
          <div>
            <span className="mb-4 block font-medium text-neutral-800 text-sm">
              [Gallery]
            </span>

            <h2 className="font-bold text-4xl text-neutral-900 leading-tight md:text-5xl">
              Moments That Move
              <br />
              the Planet Forward
            </h2>
          </div>

          {/* Right */}
          <div className="flex items-end lg:justify-end">
            <p className="max-w-sm text-base text-neutral-600">
              A visual journey of our commitment to creating a positive impact
              for the planet and future generations.
            </p>
          </div>
        </div>
      </div>

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

export default Gallery1;
```

### `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>
  );
}
```
