Web InnoventixFreeCode

Chromatic Effect

Original · free

Chromatic aberration fringes shift on hover.

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

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

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

  return (
    <section className="relative w-full overflow-hidden bg-gradient-to-b from-slate-50 to-slate-100 px-6 py-24 dark:from-neutral-950 dark:to-neutral-900 sm:py-28">
      <style>{`
        @keyframes imgfxchroma-pulse {
          0%, 100% { opacity: 0.35; transform: scale(1); }
          50% { opacity: 0.7; transform: scale(1.04); }
        }
        .imgfxchroma-ring { animation: imgfxchroma-pulse 6s ease-in-out infinite; }
        @media (prefers-reduced-motion: reduce) {
          .imgfxchroma-ring { animation: none !important; }
          .imgfxchroma-layer { transition: none !important; }
        }
      `}</style>

      {/* Channel-isolation filters — reconstruct the photo when screened together */}
      <svg aria-hidden="true" className="absolute h-0 w-0" focusable="false">
        <filter id="imgfxChromaR" colorInterpolationFilters="sRGB">
          <feColorMatrix
            type="matrix"
            values="1 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 1 0"
          />
        </filter>
        <filter id="imgfxChromaG" colorInterpolationFilters="sRGB">
          <feColorMatrix
            type="matrix"
            values="0 0 0 0 0  0 1 0 0 0  0 0 0 0 0  0 0 0 1 0"
          />
        </filter>
        <filter id="imgfxChromaB" colorInterpolationFilters="sRGB">
          <feColorMatrix
            type="matrix"
            values="0 0 0 0 0  0 0 0 0 0  0 0 1 0 0  0 0 0 1 0"
          />
        </filter>
      </svg>

      <div className="mx-auto max-w-2xl">
        <motion.div
          ref={ref}
          initial={reduceMotion ? false : { opacity: 0, y: 28 }}
          animate={inView ? { opacity: 1, y: 0 } : undefined}
          transition={{ duration: 0.7, ease: [0.22, 1, 0.36, 1] }}
        >
          <div className="mb-8 text-center">
            <p className="text-xs font-semibold uppercase tracking-[0.28em] text-indigo-500 dark:text-indigo-400">
              Image Effect
            </p>
            <h2 className="mt-3 text-3xl font-semibold tracking-tight text-slate-900 dark:text-slate-50 sm:text-4xl">
              Chromatic Aberration
            </h2>
          </div>

          <figure
            tabIndex={0}
            aria-label="Photograph rendered with a chromatic aberration effect that intensifies on hover"
            className="group relative mx-auto block cursor-pointer overflow-hidden rounded-2xl outline-none ring-1 ring-slate-900/10 transition-shadow duration-500 focus-visible:ring-2 focus-visible:ring-indigo-500 dark:ring-white/10"
          >
            {/* pulsing glow ring behind the frame */}
            <div
              aria-hidden="true"
              className="imgfxchroma-ring pointer-events-none absolute -inset-4 rounded-3xl bg-[radial-gradient(circle_at_30%_20%,rgba(99,102,241,0.35),transparent_60%),radial-gradient(circle_at_70%_80%,rgba(244,63,94,0.3),transparent_60%)] blur-2xl"
            />

            <div className="relative isolate aspect-[4/5] w-full overflow-hidden rounded-2xl bg-black">
              {/* RED channel */}
              {/* eslint-disable-next-line @next/next/no-img-element */}
              <img
                src="/img/gallery/g07.webp"
                alt=""
                aria-hidden="true"
                loading="lazy"
                draggable={false}
                style={{ filter: "url(#imgfxChromaR)", mixBlendMode: "screen" }}
                className="imgfxchroma-layer absolute inset-0 h-full w-full object-cover transition-transform duration-500 ease-out will-change-transform group-hover:-translate-x-[9px] group-hover:-translate-y-[4px] group-focus-visible:-translate-x-[9px] group-focus-visible:-translate-y-[4px]"
              />

              {/* GREEN channel */}
              {/* eslint-disable-next-line @next/next/no-img-element */}
              <img
                src="/img/gallery/g07.webp"
                alt=""
                aria-hidden="true"
                loading="lazy"
                draggable={false}
                style={{ filter: "url(#imgfxChromaG)", mixBlendMode: "screen" }}
                className="imgfxchroma-layer absolute inset-0 h-full w-full object-cover transition-transform duration-500 ease-out will-change-transform group-hover:translate-x-[4px] group-hover:translate-y-[3px] group-focus-visible:translate-x-[4px] group-focus-visible:translate-y-[3px]"
              />

              {/* BLUE channel — carries the accessible description */}
              {/* eslint-disable-next-line @next/next/no-img-element */}
              <img
                src="/img/gallery/g07.webp"
                alt="Portrait study lit with soft directional light, split into red, green and blue colour channels"
                loading="lazy"
                draggable={false}
                style={{ filter: "url(#imgfxChromaB)", mixBlendMode: "screen" }}
                className="imgfxchroma-layer absolute inset-0 h-full w-full object-cover transition-transform duration-500 ease-out will-change-transform group-hover:translate-x-[9px] group-hover:translate-y-[4px] group-focus-visible:translate-x-[9px] group-focus-visible:translate-y-[4px]"
              />

              {/* subtle edge vignette to seat the fringes */}
              <div
                aria-hidden="true"
                className="pointer-events-none absolute inset-0 rounded-2xl shadow-[inset_0_0_60px_rgba(0,0,0,0.55)]"
              />
            </div>
          </figure>

          <figcaption className="mt-5 text-center text-sm text-slate-500 dark:text-slate-400">
            Hover or focus the photo — the RGB channels drift apart into clean colour fringes.
          </figcaption>
        </motion.div>
      </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 →