Web InnoventixFreeCode

Reveal Clip Effect

Original · free

Image reveals via a clip-path wipe on scroll into view.

byWeb InnoventixReact + Tailwind
revealclipimageeffect
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-reveal-clip.json
imgfx-reveal-clip.tsx
"use client";

import { useRef } from "react";
import { useInView, useReducedMotion } from "motion/react";

type Panel = {
  src: string;
  alt: string;
  label: string;
};

const PANELS: Panel[] = [
  {
    src: "/img/gallery/g07.webp",
    alt: "Architectural facade catching low afternoon light",
    label: "Facade",
  },
  {
    src: "/img/gallery/g19.webp",
    alt: "Quiet interior with a single shaft of daylight",
    label: "Interior",
  },
  {
    src: "/img/gallery/g28.webp",
    alt: "Detail study of texture and shadow",
    label: "Detail",
  },
];

export default function ImgfxRevealClip() {
  const reduce = useReducedMotion();
  const ref = useRef<HTMLDivElement>(null);
  const inView = useInView(ref, { once: true, amount: 0.4 });
  const active = reduce ? true : inView;

  return (
    <section className="relative w-full overflow-hidden bg-neutral-50 px-6 py-24 text-neutral-900 sm:px-10 lg:py-32 dark:bg-neutral-950 dark:text-neutral-100">
      <style>{`
        @keyframes ifxclip_sheen {
          0%   { transform: translateX(-120%) skewX(-12deg); opacity: 0; }
          40%  { opacity: 0.9; }
          100% { transform: translateX(220%) skewX(-12deg); opacity: 0; }
        }
        @media (prefers-reduced-motion: reduce) {
          .ifxclip_sheen { animation: none !important; }
        }
      `}</style>

      <div className="mx-auto max-w-6xl">
        <header className="mb-14 max-w-2xl">
          <span className="inline-block text-xs font-semibold uppercase tracking-[0.28em] text-indigo-600 dark:text-indigo-400">
            Clip-Path Reveal
          </span>
          <h2 className="mt-4 text-3xl font-semibold tracking-tight sm:text-4xl">
            Images that wipe into view
          </h2>
          <p className="mt-4 text-base leading-relaxed text-neutral-600 dark:text-neutral-400">
            Each frame unmasks left-to-right as it enters the viewport, staggered
            for a clean, editorial reveal.
          </p>
        </header>

        <div ref={ref} className="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
          {PANELS.map((panel, i) => (
            <figure
              key={panel.src}
              className="group relative overflow-hidden rounded-2xl bg-neutral-200 ring-1 ring-neutral-900/5 dark:bg-neutral-800 dark:ring-white/10"
            >
              <div
                className="relative aspect-[4/5] w-full"
                style={{
                  clipPath: active ? "inset(0 0% 0 0)" : "inset(0 100% 0 0)",
                  transition: reduce
                    ? undefined
                    : "clip-path 1100ms cubic-bezier(0.22, 1, 0.36, 1)",
                  transitionDelay: reduce ? undefined : `${i * 160}ms`,
                }}
              >
                {/* eslint-disable-next-line @next/next/no-img-element */}
                <img
                  src={panel.src}
                  alt={panel.alt}
                  loading="lazy"
                  draggable={false}
                  className="h-full w-full scale-105 object-cover transition-transform duration-[1200ms] ease-out group-hover:scale-100"
                  style={{ transform: active ? undefined : "scale(1.12)" }}
                />

                <div
                  className="pointer-events-none absolute inset-0"
                  style={{
                    background:
                      "linear-gradient(180deg, transparent 55%, rgba(0,0,0,0.55) 100%)",
                  }}
                />

                <span
                  aria-hidden
                  className="ifxclip_sheen pointer-events-none absolute inset-y-0 left-0 w-1/3 bg-white/25"
                  style={{
                    animation: reduce
                      ? "none"
                      : active
                        ? `ifxclip_sheen 1100ms cubic-bezier(0.22, 1, 0.36, 1) ${i * 160}ms both`
                        : "none",
                  }}
                />

                <figcaption className="absolute bottom-4 left-4 text-sm font-medium text-white">
                  <span className="mr-2 text-white/60">0{i + 1}</span>
                  {panel.label}
                </figcaption>
              </div>
            </figure>
          ))}
        </div>

        <p className="mt-8 text-sm text-neutral-500 dark:text-neutral-500">
          Scroll the frames into view to trigger the wipe. Hover to settle the zoom.
        </p>
      </div>
    </section>
  );
}

Dependencies

motion

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 →