Web InnoventixFreeCode

Torn Edge Effect

Original · free

A torn-paper edge frames the image.

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

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

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

  return (
    <section className="relative w-full overflow-hidden bg-neutral-100 px-6 py-20 md:py-28 dark:bg-neutral-950">
      <style>{`
        @keyframes imgfxTorn_float {
          0%, 100% { transform: translateY(0) rotate(-5deg); }
          50%      { transform: translateY(-7px) rotate(-3.5deg); }
        }
        @keyframes imgfxTorn_sway {
          0%, 100% { transform: rotate(-1.4deg); }
          50%      { transform: rotate(-0.4deg); }
        }
        .imgfxTorn_scrap { animation: imgfxTorn_float 6.5s ease-in-out infinite; }
        .imgfxTorn_frame { animation: imgfxTorn_sway 11s ease-in-out infinite; }
        @media (prefers-reduced-motion: reduce) {
          .imgfxTorn_scrap, .imgfxTorn_frame { animation: none !important; }
        }
      `}</style>

      {/* SVG filter defs — the torn edge is generated live with turbulence + displacement */}
      <svg
        aria-hidden="true"
        className="pointer-events-none absolute h-0 w-0"
        focusable="false"
      >
        <defs>
          <filter
            id="imgfxTorn_paper"
            x="-20%"
            y="-20%"
            width="140%"
            height="140%"
            colorInterpolationFilters="sRGB"
          >
            <feTurbulence
              type="fractalNoise"
              baseFrequency="0.009 0.021"
              numOctaves={4}
              seed={11}
              result="n"
            />
            <feDisplacementMap
              in="SourceGraphic"
              in2="n"
              scale={17}
              xChannelSelector="R"
              yChannelSelector="G"
              result="d"
            />
            <feDropShadow
              dx="0"
              dy="9"
              stdDeviation="11"
              floodColor="#000000"
              floodOpacity={0.2}
            />
          </filter>

          <filter
            id="imgfxTorn_photo"
            x="-20%"
            y="-20%"
            width="140%"
            height="140%"
            colorInterpolationFilters="sRGB"
          >
            <feTurbulence
              type="fractalNoise"
              baseFrequency="0.011 0.024"
              numOctaves={4}
              seed={31}
              result="n"
            />
            <feDisplacementMap
              in="SourceGraphic"
              in2="n"
              scale={13}
              xChannelSelector="R"
              yChannelSelector="G"
            />
          </filter>

          <filter
            id="imgfxTorn_scrap"
            x="-25%"
            y="-25%"
            width="150%"
            height="150%"
            colorInterpolationFilters="sRGB"
          >
            <feTurbulence
              type="fractalNoise"
              baseFrequency="0.02 0.05"
              numOctaves={3}
              seed={5}
              result="n"
            />
            <feDisplacementMap
              in="SourceGraphic"
              in2="n"
              scale={9}
              xChannelSelector="R"
              yChannelSelector="G"
              result="d"
            />
            <feDropShadow
              dx="0"
              dy="5"
              stdDeviation="6"
              floodColor="#000000"
              floodOpacity={0.18}
            />
          </filter>
        </defs>
      </svg>

      <div className="relative mx-auto max-w-3xl">
        <motion.p
          initial={play ? { opacity: 0, y: 12 } : false}
          animate={play ? { opacity: 1, y: 0 } : undefined}
          transition={{ duration: 0.5, ease: [0.22, 1, 0.36, 1] }}
          className="mb-3 text-center text-xs font-semibold uppercase tracking-[0.24em] text-rose-600 dark:text-rose-400"
        >
          Image Effect · Torn Edge
        </motion.p>

        <motion.h2
          initial={play ? { opacity: 0, y: 14 } : false}
          animate={play ? { opacity: 1, y: 0 } : undefined}
          transition={{ duration: 0.55, delay: 0.05, ease: [0.22, 1, 0.36, 1] }}
          className="text-center text-4xl font-semibold tracking-tight text-neutral-900 md:text-5xl dark:text-neutral-50"
        >
          Torn Edge
        </motion.h2>

        <motion.p
          initial={play ? { opacity: 0, y: 14 } : false}
          animate={play ? { opacity: 1, y: 0 } : undefined}
          transition={{ duration: 0.55, delay: 0.1, ease: [0.22, 1, 0.36, 1] }}
          className="mx-auto mt-4 max-w-md text-center text-sm leading-relaxed text-neutral-500 dark:text-neutral-400"
        >
          A ragged, hand-torn paper border frames the photograph — every ragged
          fibre rendered live with SVG turbulence, no image seams.
        </motion.p>

        <motion.div
          ref={ref}
          initial={play ? { opacity: 0, y: 30, rotate: -2 } : false}
          animate={play ? { opacity: 1, y: 0, rotate: 0 } : undefined}
          transition={{ duration: 0.8, delay: 0.14, ease: [0.16, 1, 0.3, 1] }}
          className="group relative mx-auto mt-14 w-full max-w-md [perspective:1200px]"
        >
          {/* stacked composition, gently swaying + lifting on hover */}
          <div className="imgfxTorn_frame relative mx-auto aspect-[4/5] w-full transition-transform duration-500 ease-out will-change-transform group-hover:-translate-y-1.5">
            {/* kraft under-sheet */}
            <div
              className="absolute inset-0 translate-x-2 translate-y-3 rotate-[2.5deg] rounded-sm bg-amber-100 dark:bg-neutral-800"
              style={{ filter: "url(#imgfxTorn_paper)" }}
            />
            {/* white paper mount */}
            <div
              className="absolute inset-[-14px] rotate-[-1.2deg] rounded-sm bg-white dark:bg-neutral-100"
              style={{ filter: "url(#imgfxTorn_paper)" }}
            />

            {/* the photograph, torn to a ragged edge */}
            <div
              className="absolute inset-[6px] rounded-sm"
              style={{ filter: "url(#imgfxTorn_photo)" }}
            >
              {/* eslint-disable-next-line @next/next/no-img-element */}
              <img
                src="/img/gallery/g14.webp"
                alt="Sunlit mountain ridgeline fading into layered morning haze"
                loading="lazy"
                draggable={false}
                className="h-full w-full select-none object-cover"
              />
            </div>

            {/* strips of tape holding it down (pure CSS) */}
            <div className="absolute -top-3 left-1/2 h-7 w-20 -translate-x-1/2 -rotate-2 bg-neutral-200/60 shadow-sm backdrop-blur-[1px] dark:bg-neutral-400/30" />
            <div className="absolute -bottom-3 right-6 h-6 w-16 rotate-6 bg-neutral-200/60 shadow-sm backdrop-blur-[1px] dark:bg-neutral-400/30" />
          </div>

          {/* torn caption scrap tucked in the corner */}
          <div
            className="imgfxTorn_scrap absolute -bottom-6 -left-4 rotate-[-5deg] rounded-sm bg-white px-4 py-2 dark:bg-neutral-100"
            style={{ filter: "url(#imgfxTorn_scrap)" }}
          >
            <span className="font-mono text-[11px] font-medium uppercase tracking-wider text-neutral-700 dark:text-neutral-800">
              Field notes — no. 07
            </span>
          </div>
        </motion.div>

        <p className="mt-16 text-center text-xs text-neutral-400 dark:text-neutral-500">
          Hover to lift the print off the page.
        </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 →