Web InnoventixFreeCode

Hover Zoom Effect

Original · free

Image zooms in smoothly inside a fixed rounded frame on hover.

byWeb InnoventixReact + Tailwind
hoverzoomimageeffect
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-hover-zoom.json
imgfx-hover-zoom.tsx
"use client";

export default function ImgfxHoverZoom() {
  const photos = [
    {
      src: "/img/gallery/g07.webp",
      alt: "Sunlit coastal cliffs meeting a calm turquoise sea",
      caption: "Coastline",
    },
    {
      src: "/img/gallery/g14.webp",
      alt: "Quiet city street glowing under warm evening light",
      caption: "After hours",
    },
    {
      src: "/img/gallery/g22.webp",
      alt: "Dense forest canopy seen from below against soft sky",
      caption: "Canopy",
    },
  ];

  return (
    <section className="relative w-full bg-white px-6 py-20 text-slate-900 sm:px-10 sm:py-28 dark:bg-slate-950 dark:text-slate-50">
      <style>{`
        .ihz-frame {
          overflow: hidden;
          will-change: transform;
        }
        .ihz-media {
          transform: scale(1);
          transform-origin: center;
          transition: transform 700ms cubic-bezier(0.16, 1, 0.3, 1),
                      filter 700ms cubic-bezier(0.16, 1, 0.3, 1);
        }
        .ihz-frame:hover .ihz-media,
        .ihz-frame:focus-visible .ihz-media {
          transform: scale(1.12);
          filter: saturate(1.08);
        }
        .ihz-caption {
          transform: translateY(0.75rem);
          opacity: 0;
          transition: transform 500ms cubic-bezier(0.16, 1, 0.3, 1),
                      opacity 500ms cubic-bezier(0.16, 1, 0.3, 1);
        }
        .ihz-frame:hover .ihz-caption,
        .ihz-frame:focus-visible .ihz-caption {
          transform: translateY(0);
          opacity: 1;
        }
        @media (prefers-reduced-motion: reduce) {
          .ihz-media,
          .ihz-caption {
            transition: none;
          }
          .ihz-frame:hover .ihz-media,
          .ihz-frame:focus-visible .ihz-media {
            transform: scale(1);
            filter: none;
          }
          .ihz-frame:hover .ihz-caption,
          .ihz-frame:focus-visible .ihz-caption {
            opacity: 1;
            transform: translateY(0);
          }
        }
      `}</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 sm:text-4xl">
            Hover Zoom
          </h2>
          <p className="mt-3 text-sm text-slate-500 dark:text-slate-400">
            The photo scales up smoothly inside a fixed rounded frame. Hover or focus to reveal.
          </p>
        </div>

        <div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
          {photos.map((photo) => (
            <button
              key={photo.src}
              type="button"
              aria-label={`${photo.caption} — zoom on hover`}
              className="ihz-frame group relative block aspect-[4/5] w-full rounded-2xl bg-slate-200 shadow-sm ring-1 ring-slate-900/5 outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:bg-slate-800 dark:ring-white/10"
            >
              {/* eslint-disable-next-line @next/next/no-img-element */}
              <img
                src={photo.src}
                alt={photo.alt}
                loading="lazy"
                draggable={false}
                className="ihz-media h-full w-full object-cover"
              />
              <span
                aria-hidden="true"
                className="pointer-events-none absolute inset-0 bg-gradient-to-t from-slate-950/60 via-slate-950/0 to-transparent"
              />
              <span className="ihz-caption pointer-events-none absolute bottom-4 left-4 text-sm font-semibold text-white drop-shadow">
                {photo.caption}
              </span>
            </button>
          ))}
        </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 →