Web InnoventixFreeCode

Caption Slide Down Effect

Original · free

A caption panel slides down from the top on hover.

byWeb InnoventixReact + Tailwind
captionslidedownimageeffect
Open

Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.

npx shadcn@latest add https://webinnoventix.com/r/imgfx-caption-slide-down.json
imgfx-caption-slide-down.tsx
"use client";

export default function ImgfxCaptionSlideDown() {
  return (
    <section className="relative w-full bg-gradient-to-b from-neutral-50 to-neutral-100 px-6 py-20 dark:from-neutral-950 dark:to-neutral-900 sm:px-10 sm:py-28">
      <style>{`
        .csd-panel {
          transform: translateY(-100%);
          transition: transform 520ms cubic-bezier(0.22, 1, 0.36, 1);
          will-change: transform;
        }
        .csd-card:hover .csd-panel,
        .csd-card:focus-visible .csd-panel {
          transform: translateY(0);
        }
        .csd-img {
          transition: transform 640ms cubic-bezier(0.22, 1, 0.36, 1), filter 520ms ease;
        }
        .csd-card:hover .csd-img,
        .csd-card:focus-visible .csd-img {
          transform: scale(1.06);
          filter: saturate(1.05);
        }
        .csd-line {
          transform: translateY(-14px);
          opacity: 0;
          transition: transform 560ms cubic-bezier(0.22, 1, 0.36, 1), opacity 560ms ease;
        }
        .csd-card:hover .csd-line,
        .csd-card:focus-visible .csd-line {
          transform: translateY(0);
          opacity: 1;
          transition-delay: 120ms;
        }
        @media (prefers-reduced-motion: reduce) {
          .csd-panel,
          .csd-img,
          .csd-line {
            transition: none;
          }
        }
      `}</style>

      <div className="mx-auto max-w-6xl">
        <div className="mb-12 text-center">
          <p className="text-xs font-semibold uppercase tracking-[0.25em] text-indigo-600 dark:text-indigo-400">
            Image Effect
          </p>
          <h2 className="mt-3 text-3xl font-bold tracking-tight text-neutral-900 dark:text-neutral-50 sm:text-4xl">
            Caption Slide-Down
          </h2>
          <p className="mt-3 text-sm text-neutral-600 dark:text-neutral-400">
            Hover a photo and a caption panel glides in from the top.
          </p>
        </div>

        <div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
          {[
            {
              src: "/img/gallery/g07.webp",
              alt: "Sunlit mountain ridge fading into layered haze",
              eyebrow: "Landscape",
              title: "Ridgeline at First Light",
              body: "Cold air, warm horizon, and a valley still asleep below.",
            },
            {
              src: "/img/gallery/g18.webp",
              alt: "Quiet city street with long evening shadows",
              eyebrow: "Urban",
              title: "The Slow Commute",
              body: "Empty pavement holds the shape of everyone who just left.",
            },
            {
              src: "/img/gallery/g25.webp",
              alt: "Close detail of textured natural surface",
              eyebrow: "Detail",
              title: "Grain & Grit",
              body: "Step in close and the ordinary starts to look designed.",
            },
          ].map((item) => (
            <article
              key={item.src}
              tabIndex={0}
              className="csd-card group relative aspect-[4/5] overflow-hidden rounded-2xl bg-neutral-200 shadow-lg shadow-neutral-900/10 ring-1 ring-neutral-900/5 outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:bg-neutral-800 dark:shadow-black/40 dark:ring-white/10"
              aria-label={`${item.title} — ${item.body}`}
            >
              {/* eslint-disable-next-line @next/next/no-img-element */}
              <img
                src={item.src}
                alt={item.alt}
                loading="lazy"
                draggable={false}
                className="csd-img h-full w-full object-cover"
              />

              <div className="csd-panel pointer-events-none absolute inset-x-0 top-0 z-10 bg-gradient-to-b from-white/95 to-white/80 px-5 py-5 backdrop-blur-sm dark:from-neutral-950/95 dark:to-neutral-950/80">
                <p className="csd-line text-[0.65rem] font-semibold uppercase tracking-[0.2em] text-indigo-600 dark:text-indigo-400">
                  {item.eyebrow}
                </p>
                <h3 className="csd-line mt-1 text-lg font-bold leading-tight text-neutral-900 dark:text-neutral-50">
                  {item.title}
                </h3>
                <p className="csd-line mt-1.5 text-sm leading-snug text-neutral-600 dark:text-neutral-300">
                  {item.body}
                </p>
              </div>

              <span className="pointer-events-none absolute bottom-3 right-3 z-10 rounded-full bg-neutral-900/70 px-3 py-1 text-[0.65rem] font-medium text-white opacity-100 backdrop-blur-sm transition-opacity duration-300 group-hover:opacity-0 dark:bg-white/15">
                hover to reveal
              </span>
            </article>
          ))}
        </div>
      </div>
    </section>
  );
}

Dependencies

None (React + Tailwind only).

Licence

Built by Web Innoventix. Free for personal and commercial use, no attribution required.

Built by Web Innoventix

Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.

Get a free quote

Similar components

Browse all →