Web InnoventixFreeCode

Prism Effect

Original · free

A prism chromatic edge split shimmers on hover.

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

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

export default function ImgfxPrism() {
  const reduce = useReducedMotion();
  const ref = useRef<HTMLDivElement>(null);
  const inView = useInView(ref, { once: true, margin: "-15%" });

  const photo = "/img/gallery/g14.webp";
  const alt = "A cyclist crossing a rain-slicked city street at dusk";

  return (
    <section className="relative w-full overflow-hidden bg-white px-6 py-24 text-slate-900 dark:bg-neutral-950 dark:text-slate-100 sm:px-10 lg:py-32">
      <style>{`
        @keyframes imgfxprism-shimmer {
          0%   { transform: translate3d(var(--ix), var(--iy), 0) scale(var(--is)); filter: saturate(1); }
          50%  { transform: translate3d(calc(var(--ix) * 1.9), calc(var(--iy) * 1.9), 0) scale(var(--is)); filter: saturate(1.35); }
          100% { transform: translate3d(var(--ix), var(--iy), 0) scale(var(--is)); filter: saturate(1); }
        }
        @keyframes imgfxprism-sheen {
          0%   { transform: translateX(-120%) rotate(8deg); opacity: 0; }
          15%  { opacity: 0.9; }
          100% { transform: translateX(120%) rotate(8deg); opacity: 0; }
        }
        .imgfxprism-stage { isolation: isolate; }
        .imgfxprism-chan {
          position: absolute; inset: 0;
          mix-blend-mode: screen; isolation: isolate;
          transform: translate3d(0, 0, 0) scale(1);
          transition: transform 520ms cubic-bezier(0.16, 1, 0.3, 1),
                      filter 520ms ease;
          will-change: transform, filter;
        }
        .imgfxprism-tint { position: absolute; inset: 0; mix-blend-mode: multiply; }
        .imgfxprism-r { --ix: 0px;  --iy: 0px;  --is: 1; }
        .imgfxprism-g { --ix: 0px;  --iy: 0px;  --is: 1; }
        .imgfxprism-b { --ix: 0px;  --iy: 0px;  --is: 1; }

        .imgfxprism-stage:hover .imgfxprism-r,
        .imgfxprism-stage:focus-visible .imgfxprism-r { --ix: -9px; --iy: 4px;  --is: 1.015; }
        .imgfxprism-stage:hover .imgfxprism-g,
        .imgfxprism-stage:focus-visible .imgfxprism-g { --ix: 1px;  --iy: -3px; --is: 1.006; }
        .imgfxprism-stage:hover .imgfxprism-b,
        .imgfxprism-stage:focus-visible .imgfxprism-b { --ix: 9px;  --iy: 5px;  --is: 1.015; }

        .imgfxprism-stage:hover .imgfxprism-r,
        .imgfxprism-stage:hover .imgfxprism-b,
        .imgfxprism-stage:focus-visible .imgfxprism-r,
        .imgfxprism-stage:focus-visible .imgfxprism-b {
          animation: imgfxprism-shimmer 2.4s ease-in-out infinite;
        }

        .imgfxprism-sheen {
          position: absolute; top: -30%; left: 0; height: 160%; width: 42%;
          pointer-events: none; opacity: 0;
          background: linear-gradient(100deg,
            transparent 0%,
            rgba(244,114,182,0.35) 30%,
            rgba(129,140,248,0.45) 50%,
            rgba(52,211,153,0.35) 70%,
            transparent 100%);
          mix-blend-mode: screen;
          filter: blur(6px);
        }
        .imgfxprism-stage:hover .imgfxprism-sheen,
        .imgfxprism-stage:focus-visible .imgfxprism-sheen {
          animation: imgfxprism-sheen 1.5s ease-out forwards;
        }
        .imgfxprism-ring {
          position: absolute; inset: 0; pointer-events: none;
          border-radius: inherit;
          box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08);
          opacity: 0; transition: opacity 520ms ease;
          background:
            radial-gradient(120% 90% at 0% 0%, rgba(244,114,182,0.22), transparent 45%),
            radial-gradient(120% 90% at 100% 100%, rgba(96,165,250,0.22), transparent 45%);
          mix-blend-mode: screen;
        }
        .imgfxprism-stage:hover .imgfxprism-ring,
        .imgfxprism-stage:focus-visible .imgfxprism-ring { opacity: 1; }

        @media (prefers-reduced-motion: reduce) {
          .imgfxprism-chan { transition: none !important; animation: none !important; transform: none !important; }
          .imgfxprism-sheen { animation: none !important; opacity: 0 !important; }
          .imgfxprism-ring { transition: none !important; }
        }
      `}</style>

      <motion.div
        ref={ref}
        initial={reduce ? false : { opacity: 0, y: 26 }}
        animate={inView ? { opacity: 1, y: 0 } : undefined}
        transition={{ duration: 0.7, ease: [0.16, 1, 0.3, 1] }}
        className="mx-auto flex max-w-xl flex-col items-center text-center"
      >
        <span className="mb-4 inline-flex items-center gap-2 rounded-full border border-slate-200 bg-slate-50 px-3 py-1 text-xs font-medium uppercase tracking-[0.2em] text-slate-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-400">
          <span className="h-1.5 w-1.5 rounded-full bg-gradient-to-r from-rose-400 via-indigo-400 to-emerald-400" />
          Image Effect
        </span>

        <h2 className="text-balance text-3xl font-semibold tracking-tight sm:text-4xl">
          Prism Split
        </h2>
        <p className="mt-3 max-w-md text-pretty text-sm leading-relaxed text-slate-500 dark:text-slate-400">
          The photo&apos;s red, green and blue channels drift apart into a
          chromatic edge, shimmering like light through cut glass.
        </p>

        <div
          className="imgfxprism-stage group relative mt-10 aspect-[4/5] w-full max-w-md overflow-hidden rounded-2xl bg-black shadow-2xl shadow-slate-900/20 ring-1 ring-slate-900/10 outline-none transition-shadow duration-500 hover:shadow-indigo-500/20 focus-visible:ring-2 focus-visible:ring-indigo-400 dark:ring-white/10"
          tabIndex={0}
          role="img"
          aria-label={alt}
        >
          <div className="imgfxprism-chan imgfxprism-r" aria-hidden="true">
            {/* eslint-disable-next-line @next/next/no-img-element */}
            <img
              src={photo}
              alt=""
              loading="lazy"
              draggable={false}
              className="absolute inset-0 h-full w-full object-cover"
            />
            <div className="imgfxprism-tint" style={{ background: "#ff0000" }} />
          </div>

          <div className="imgfxprism-chan imgfxprism-g" aria-hidden="true">
            {/* eslint-disable-next-line @next/next/no-img-element */}
            <img
              src={photo}
              alt=""
              loading="lazy"
              draggable={false}
              className="absolute inset-0 h-full w-full object-cover"
            />
            <div className="imgfxprism-tint" style={{ background: "#00ff00" }} />
          </div>

          <div className="imgfxprism-chan imgfxprism-b" aria-hidden="true">
            {/* eslint-disable-next-line @next/next/no-img-element */}
            <img
              src={photo}
              alt={alt}
              loading="lazy"
              draggable={false}
              className="absolute inset-0 h-full w-full object-cover"
            />
            <div className="imgfxprism-tint" style={{ background: "#0000ff" }} />
          </div>

          <div className="imgfxprism-ring" aria-hidden="true" />
          <div className="imgfxprism-sheen" aria-hidden="true" />
        </div>

        <p className="mt-5 text-xs font-medium uppercase tracking-[0.18em] text-slate-400 dark:text-slate-500">
          Hover or focus to refract
        </p>
      </motion.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 →