Web InnoventixFreeCode

Diamond Clip Effect

Original · free

A diamond clip-path expands to reveal the image on hover.

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

import { useId } from "react";

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

const shots: Shot[] = [
  {
    src: "/img/gallery/g07.webp",
    alt: "Sunlit mountain ridge fading into layered morning haze",
    label: "Ridgeline",
  },
  {
    src: "/img/gallery/g18.webp",
    alt: "Quiet coastline where slow waves meet pale wet sand",
    label: "Shoreline",
  },
  {
    src: "/img/gallery/g25.webp",
    alt: "Dense forest canopy seen from below against soft daylight",
    label: "Canopy",
  },
];

export default function ImgfxDiamondClip() {
  const uid = useId().replace(/[^a-zA-Z0-9]/g, "");
  const cls = `dcx${uid}`;

  return (
    <section className="relative w-full overflow-hidden bg-neutral-50 px-6 py-24 dark:bg-neutral-950 sm:px-10">
      <style>{`
        .${cls}-tile {
          --dcx-clip: polygon(50% 42%, 58% 50%, 50% 58%, 42% 50%);
        }
        .${cls}-reveal {
          clip-path: var(--dcx-clip);
          transition: clip-path 620ms cubic-bezier(0.22, 1, 0.36, 1);
          will-change: clip-path;
        }
        .${cls}-tile:hover .${cls}-reveal,
        .${cls}-tile:focus-within .${cls}-reveal {
          --dcx-clip: polygon(50% -55%, 155% 50%, 50% 155%, -55% 50%);
        }
        .${cls}-edge {
          transition: opacity 620ms cubic-bezier(0.22, 1, 0.36, 1),
            transform 620ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        .${cls}-tile:hover .${cls}-edge,
        .${cls}-tile:focus-within .${cls}-edge {
          opacity: 0;
          transform: scale(1.35) rotate(45deg);
        }
        @media (prefers-reduced-motion: reduce) {
          .${cls}-reveal,
          .${cls}-edge {
            transition: none;
          }
          .${cls}-tile:hover .${cls}-reveal,
          .${cls}-tile:focus-within .${cls}-reveal {
            --dcx-clip: polygon(50% -55%, 155% 50%, 50% 155%, -55% 50%);
          }
        }
      `}</style>

      <div className="mx-auto max-w-5xl">
        <div className="mb-14 text-center">
          <p className="text-xs font-semibold uppercase tracking-[0.35em] text-indigo-600 dark:text-indigo-400">
            Image Effect
          </p>
          <h2 className="mt-4 text-3xl font-semibold tracking-tight text-neutral-900 dark:text-neutral-50 sm:text-4xl">
            Diamond Clip Reveal
          </h2>
          <p className="mt-4 text-sm text-neutral-500 dark:text-neutral-400">
            A faceted diamond opens outward to unveil the full photograph.{" "}
            <span className="font-medium text-neutral-700 dark:text-neutral-200">
              Hover or focus a tile to reveal.
            </span>
          </p>
        </div>

        <div className="grid grid-cols-1 gap-6 sm:grid-cols-3">
          {shots.map((shot, i) => (
            <figure
              key={shot.src}
              tabIndex={0}
              aria-label={`${shot.label}: ${shot.alt}`}
              className={`${cls}-tile group relative aspect-[3/4] overflow-hidden rounded-2xl bg-neutral-200 outline-none ring-1 ring-neutral-900/10 transition-shadow duration-500 focus-visible:ring-2 focus-visible:ring-indigo-500 dark:bg-neutral-800 dark:ring-white/10 ${
                i === 1 ? "sm:-translate-y-4" : ""
              }`}
            >
              {/* Base layer: dimmed, desaturated resting state */}
              {/* eslint-disable-next-line @next/next/no-img-element */}
              <img
                src={shot.src}
                alt=""
                aria-hidden="true"
                loading="lazy"
                draggable={false}
                className="absolute inset-0 h-full w-full scale-105 object-cover opacity-40 grayscale transition-all duration-700 group-hover:scale-100 group-focus-within:scale-100"
              />

              {/* Reveal layer: full-color image inside the expanding diamond */}
              <div className={`${cls}-reveal absolute inset-0`}>
                {/* eslint-disable-next-line @next/next/no-img-element */}
                <img
                  src={shot.src}
                  alt={shot.alt}
                  loading="lazy"
                  draggable={false}
                  className="h-full w-full object-cover"
                />
              </div>

              {/* Diamond outline hint that fades as the clip expands */}
              <span
                aria-hidden="true"
                className={`${cls}-edge pointer-events-none absolute left-1/2 top-1/2 h-16 w-16 -translate-x-1/2 -translate-y-1/2 rotate-45 rounded-md border border-white/80 shadow-[0_0_24px_rgba(255,255,255,0.35)]`}
              />

              <figcaption className="absolute inset-x-0 bottom-0 z-10 flex items-center justify-between bg-gradient-to-t from-black/70 via-black/25 to-transparent px-4 pb-4 pt-10 text-white">
                <span className="text-sm font-medium tracking-wide">
                  {shot.label}
                </span>
                <span className="text-[10px] font-semibold uppercase tracking-[0.2em] text-white/70">
                  0{i + 1}
                </span>
              </figcaption>
            </figure>
          ))}
        </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 →