Web InnoventixFreeCode

Gradient Border Draw Effect

Original · free

A gradient border draws itself around the image on hover.

byWeb InnoventixReact + Tailwind
gradientborderdrawimageeffect
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-gradient-border-draw.json
imgfx-gradient-border-draw.tsx
"use client";

const PHOTOS = [
  {
    src: "/img/gallery/g07.webp",
    alt: "Sunlight breaking through a dense forest canopy",
    caption: "Canopy light",
  },
  {
    src: "/img/gallery/g19.webp",
    alt: "Calm coastline at dawn with soft pastel sky",
    caption: "Dawn coast",
  },
  {
    src: "/img/gallery/g28.webp",
    alt: "Quiet city street after rain reflecting neon signs",
    caption: "After the rain",
  },
] as const;

export default function ImgfxGradientBorderDraw() {
  return (
    <section className="relative w-full bg-white px-6 py-20 sm:px-10 sm:py-28 dark:bg-neutral-950">
      <style>{`
        .igbd-track {
          stroke-dasharray: 100;
          stroke-dashoffset: 100;
          transition: stroke-dashoffset 1100ms cubic-bezier(0.65, 0, 0.35, 1);
        }
        .igbd-card:hover .igbd-track,
        .igbd-card:focus-visible .igbd-track,
        .igbd-card:focus-within .igbd-track {
          stroke-dashoffset: 0;
        }
        .igbd-img {
          transition: transform 1000ms cubic-bezier(0.22, 1, 0.36, 1),
            filter 700ms ease;
          filter: saturate(0.92);
        }
        .igbd-card:hover .igbd-img,
        .igbd-card:focus-visible .igbd-img,
        .igbd-card:focus-within .igbd-img {
          transform: scale(1.045);
          filter: saturate(1.08);
        }
        .igbd-cap {
          opacity: 0;
          transform: translateY(8px);
          transition: opacity 600ms ease 120ms, transform 600ms cubic-bezier(0.22, 1, 0.36, 1) 120ms;
        }
        .igbd-card:hover .igbd-cap,
        .igbd-card:focus-visible .igbd-cap,
        .igbd-card:focus-within .igbd-cap {
          opacity: 1;
          transform: translateY(0);
        }
        @media (prefers-reduced-motion: reduce) {
          .igbd-track { transition: none; }
          .igbd-img {
            transition: none;
            transform: none !important;
          }
          .igbd-cap {
            transition: none;
            opacity: 1;
            transform: none;
          }
        }
      `}</style>

      <div className="mx-auto max-w-5xl">
        <header className="mb-12 text-center">
          <p className="text-xs font-semibold uppercase tracking-[0.2em] text-indigo-600 dark:text-indigo-400">
            Image effect
          </p>
          <h2 className="mt-3 text-3xl font-semibold tracking-tight text-slate-900 sm:text-4xl dark:text-slate-50">
            Gradient border draw
          </h2>
          <p className="mt-3 text-sm text-slate-500 dark:text-slate-400">
            Hover a photo and a gradient stroke traces its way around the frame.
          </p>
        </header>

        <div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
          {PHOTOS.map((photo, i) => {
            const gradId = `igbd-grad-${i}`;
            return (
              <figure
                key={photo.src}
                tabIndex={0}
                aria-label={`${photo.caption} — hover to draw the gradient border`}
                className="igbd-card group relative aspect-[4/5] cursor-pointer overflow-hidden rounded-[20px] bg-slate-100 outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:bg-neutral-900 dark:focus-visible:ring-offset-neutral-950"
              >
                {/* eslint-disable-next-line @next/next/no-img-element */}
                <img
                  src={photo.src}
                  alt={photo.alt}
                  loading="lazy"
                  draggable={false}
                  className="igbd-img h-full w-full select-none object-cover"
                />

                <div
                  aria-hidden="true"
                  className="pointer-events-none absolute inset-0 bg-gradient-to-t from-slate-900/50 via-transparent to-transparent"
                />

                <svg
                  aria-hidden="true"
                  viewBox="0 0 400 500"
                  preserveAspectRatio="xMidYMid meet"
                  className="pointer-events-none absolute inset-0 h-full w-full"
                >
                  <defs>
                    <linearGradient id={gradId} x1="0%" y1="0%" x2="100%" y2="100%">
                      <stop offset="0%" stopColor="#818cf8" />
                      <stop offset="45%" stopColor="#a78bfa" />
                      <stop offset="100%" stopColor="#34d399" />
                    </linearGradient>
                  </defs>

                  {/* faint resting frame */}
                  <rect
                    x="6"
                    y="6"
                    width="388"
                    height="488"
                    rx="16"
                    fill="none"
                    stroke="currentColor"
                    strokeWidth="2"
                    vectorEffect="non-scaling-stroke"
                    className="text-slate-300/60 dark:text-white/15"
                  />

                  {/* drawing gradient stroke */}
                  <rect
                    x="6"
                    y="6"
                    width="388"
                    height="488"
                    rx="16"
                    pathLength={100}
                    fill="none"
                    stroke={`url(#${gradId})`}
                    strokeWidth="3"
                    strokeLinecap="round"
                    vectorEffect="non-scaling-stroke"
                    className="igbd-track"
                  />
                </svg>

                <figcaption className="igbd-cap absolute inset-x-0 bottom-0 p-4">
                  <span className="text-sm font-medium text-white drop-shadow">
                    {photo.caption}
                  </span>
                </figcaption>
              </figure>
            );
          })}
        </div>

        <p className="mt-10 text-center text-xs text-slate-400 dark:text-slate-500">
          Hover or focus a photo to reveal the effect.
        </p>
      </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 →