Web InnoventixFreeCode

Sepia Clear Effect

Original · free

Sepia-toned image clears to full colour on hover.

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

import { useState } from "react";

type Photo = {
  src: string;
  alt: string;
  caption: string;
};

const photos: Photo[] = [
  {
    src: "/img/gallery/g07.webp",
    alt: "Sunlit market street framed by weathered stone archways",
    caption: "Old town, noon light",
  },
  {
    src: "/img/gallery/g19.webp",
    alt: "Quiet harbour with wooden boats resting against the pier",
    caption: "Harbour, low tide",
  },
  {
    src: "/img/gallery/g28.webp",
    alt: "Terraced hillside vineyard fading into afternoon haze",
    caption: "Hills, late summer",
  },
];

export default function ImgfxSepiaClear() {
  const [active, setActive] = useState<number | null>(null);

  return (
    <section className="relative w-full bg-neutral-50 px-6 py-20 dark:bg-neutral-950 sm:px-10 sm:py-28">
      <style>{`
        .isc-card { transition: transform 600ms cubic-bezier(0.22, 1, 0.36, 1); }
        .isc-img {
          filter: sepia(0.85) saturate(0.7) contrast(1.02) brightness(0.98);
          transform: scale(1.04);
          transition:
            filter 750ms cubic-bezier(0.22, 1, 0.36, 1),
            transform 750ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        .isc-card:hover .isc-img,
        .isc-card:focus-visible .isc-img {
          filter: sepia(0) saturate(1.08) contrast(1) brightness(1);
          transform: scale(1);
        }
        .isc-wash {
          background: linear-gradient(180deg, rgba(120, 78, 40, 0.22), rgba(80, 48, 20, 0.34));
          opacity: 1;
          transition: opacity 750ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        .isc-card:hover .isc-wash,
        .isc-card:focus-visible .isc-wash { opacity: 0; }
        .isc-cap {
          opacity: 0;
          transform: translateY(8px);
          transition: opacity 500ms ease, transform 500ms ease;
        }
        .isc-card:hover .isc-cap,
        .isc-card:focus-visible .isc-cap {
          opacity: 1;
          transform: translateY(0);
        }
        @media (prefers-reduced-motion: reduce) {
          .isc-card, .isc-img, .isc-wash, .isc-cap { transition: none !important; }
          .isc-img { transform: none; }
        }
      `}</style>

      <div className="mx-auto max-w-5xl">
        <div className="mb-12 text-center">
          <p className="text-xs font-semibold uppercase tracking-[0.28em] text-amber-700 dark:text-amber-400">
            Image Effect
          </p>
          <h2 className="mt-3 text-3xl font-semibold tracking-tight text-neutral-900 dark:text-neutral-50 sm:text-4xl">
            Sepia clears to colour
          </h2>
          <p className="mt-3 text-sm text-neutral-600 dark:text-neutral-400">
            Hover or focus a frame — the aged tone lifts and full colour floods back in.
          </p>
        </div>

        <div className="grid grid-cols-1 gap-6 sm:grid-cols-3">
          {photos.map((photo, i) => (
            <a
              key={photo.src}
              href="#"
              onClick={(e) => e.preventDefault()}
              onMouseEnter={() => setActive(i)}
              onMouseLeave={() => setActive((prev) => (prev === i ? null : prev))}
              onFocus={() => setActive(i)}
              onBlur={() => setActive((prev) => (prev === i ? null : prev))}
              aria-label={`${photo.caption} — hover to reveal full colour`}
              className="isc-card group relative block overflow-hidden rounded-2xl bg-neutral-200 shadow-sm ring-1 ring-neutral-900/10 outline-none focus-visible:ring-2 focus-visible:ring-amber-500 dark:bg-neutral-800 dark:ring-white/10"
            >
              <div className="relative aspect-[4/5] w-full overflow-hidden">
                {/* eslint-disable-next-line @next/next/no-img-element */}
                <img
                  className="isc-img h-full w-full object-cover"
                  src={photo.src}
                  alt={photo.alt}
                  loading="lazy"
                  draggable={false}
                />
                <span className="isc-wash pointer-events-none absolute inset-0" aria-hidden="true" />
                <span className="pointer-events-none absolute inset-x-0 bottom-0 h-24 bg-gradient-to-t from-black/60 to-transparent" aria-hidden="true" />
                <span className="isc-cap pointer-events-none absolute bottom-4 left-4 right-4 text-sm font-medium text-white drop-shadow">
                  {photo.caption}
                </span>
              </div>
            </a>
          ))}
        </div>

        <p className="mt-8 text-center text-xs text-neutral-500 dark:text-neutral-500" aria-live="polite">
          {active === null
            ? "Three frames waiting in tone."
            : `In colour: ${photos[active].caption}`}
        </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 →