Web InnoventixFreeCode

Meta Panel Effect

Original · free

A meta panel slides in over part of the image on hover.

byWeb InnoventixReact + Tailwind
metapanelimageeffect
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-meta-panel.json
imgfx-meta-panel.tsx
"use client";

type MetaCard = {
  src: string;
  alt: string;
  title: string;
  place: string;
  tags: string[];
};

const CARDS: MetaCard[] = [
  {
    src: "/img/gallery/g07.webp",
    alt: "Sunlit ridge line fading into layered morning haze",
    title: "Ridgeline at First Light",
    place: "Dolomites, Italy",
    tags: ["Landscape", "Golden Hour", "35mm"],
  },
  {
    src: "/img/gallery/g14.webp",
    alt: "Quiet city street reflecting neon after light rain",
    title: "Wet Neon",
    place: "Shinjuku, Tokyo",
    tags: ["Street", "Night", "Reflections"],
  },
  {
    src: "/img/gallery/g22.webp",
    alt: "Close portrait lit by a single warm window",
    title: "Window Light Study",
    place: "Studio No. 4",
    tags: ["Portrait", "Natural Light"],
  },
];

export default function ImgfxMetaPanel() {
  return (
    <section className="relative w-full bg-neutral-50 px-6 py-20 dark:bg-neutral-950 sm:px-10 sm:py-28">
      <style>{`
        @keyframes imfxmp-rise {
          from { opacity: 0; transform: translateY(16px); }
          to { opacity: 1; transform: translateY(0); }
        }
        .imfxmp-in { animation: imfxmp-rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) both; }
        @media (prefers-reduced-motion: reduce) {
          .imfxmp-in { animation: none; }
          .imfxmp-panel { transition: none !important; }
        }
      `}</style>

      <div className="mx-auto max-w-6xl">
        <div className="imfxmp-in mb-12 text-center">
          <p className="text-xs font-semibold uppercase tracking-[0.28em] text-indigo-600 dark:text-indigo-400">
            Image Effect / Meta Panel
          </p>
          <h2 className="mt-3 text-3xl font-semibold tracking-tight text-neutral-900 dark:text-neutral-50 sm:text-4xl">
            Slide-in caption on hover
          </h2>
          <p className="mt-3 text-sm text-neutral-500 dark:text-neutral-400">
            Hover a photo — its title and tags glide up over the frame.
          </p>
        </div>

        <ul className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
          {CARDS.map((card, i) => (
            <li
              key={card.src}
              className="imfxmp-in"
              style={{ animationDelay: `${0.08 * i + 0.05}s` }}
            >
              <figure className="group relative overflow-hidden rounded-2xl bg-neutral-200 shadow-sm ring-1 ring-neutral-900/5 dark:bg-neutral-800 dark:ring-white/10">
                {/* eslint-disable-next-line @next/next/no-img-element */}
                <img
                  src={card.src}
                  alt={card.alt}
                  loading="lazy"
                  draggable={false}
                  className="aspect-[4/5] w-full select-none object-cover transition-transform duration-[900ms] ease-[cubic-bezier(0.22,1,0.36,1)] group-hover:scale-[1.05]"
                />

                <div
                  aria-hidden="true"
                  className="pointer-events-none absolute inset-x-0 bottom-0 h-2/3 bg-gradient-to-t from-neutral-950/80 via-neutral-950/25 to-transparent opacity-70 transition-opacity duration-500 group-hover:opacity-95"
                />

                <figcaption className="imfxmp-panel absolute inset-x-0 bottom-0 translate-y-[calc(100%-3.25rem)] p-5 transition-transform duration-[550ms] ease-[cubic-bezier(0.22,1,0.36,1)] group-focus-within:translate-y-0 group-hover:translate-y-0">
                  <p className="text-[0.65rem] font-semibold uppercase tracking-[0.22em] text-indigo-300">
                    {card.place}
                  </p>
                  <h3 className="mt-1 text-lg font-semibold leading-tight text-white">
                    {card.title}
                  </h3>
                  <ul className="mt-3 flex flex-wrap gap-2 opacity-0 transition-opacity duration-500 delay-100 group-focus-within:opacity-100 group-hover:opacity-100">
                    {card.tags.map((tag) => (
                      <li
                        key={tag}
                        className="rounded-full bg-white/15 px-2.5 py-1 text-[0.7rem] font-medium text-white ring-1 ring-white/25 backdrop-blur-sm"
                      >
                        {tag}
                      </li>
                    ))}
                  </ul>
                </figcaption>

                <a
                  href="#"
                  className="absolute inset-0 rounded-2xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-400 focus-visible:ring-offset-2 focus-visible:ring-offset-neutral-50 dark:focus-visible:ring-offset-neutral-950"
                  aria-label={`${card.title} — ${card.place}. Tags: ${card.tags.join(", ")}`}
                >
                  <span className="sr-only">{card.title}</span>
                </a>
              </figure>
            </li>
          ))}
        </ul>
      </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 →