Web InnoventixFreeCode

Halftone Effect

Original · free

A halftone dot pattern overlays the image, clearing on hover.

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

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

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

  return (
    <section className="relative w-full overflow-hidden bg-neutral-50 px-6 py-20 sm:px-10 sm:py-28 dark:bg-neutral-950">
      <style>{`
        @keyframes imgfxhalftone_reveal {
          0%   { opacity: 0; transform: translateY(24px) scale(0.98); }
          100% { opacity: 1; transform: translateY(0) scale(1); }
        }
        @keyframes imgfxhalftone_drift {
          0%   { background-position: 0 0, 3px 3px; }
          100% { background-position: 6px 6px, 9px 9px; }
        }
        .imgfxhalftone_card {
          animation: imgfxhalftone_reveal 0.9s cubic-bezier(0.22, 1, 0.36, 1) both;
        }
        .imgfxhalftone_dots {
          background-image:
            radial-gradient(currentColor 42%, transparent 44%),
            radial-gradient(currentColor 42%, transparent 44%);
          background-size: 6px 6px, 6px 6px;
          background-position: 0 0, 3px 3px;
          animation: imgfxhalftone_drift 14s linear infinite alternate;
          transition: opacity 0.7s cubic-bezier(0.22, 1, 0.36, 1),
                      transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
        }
        .imgfxhalftone_frame:hover .imgfxhalftone_dots,
        .imgfxhalftone_frame:focus-within .imgfxhalftone_dots {
          opacity: 0;
          transform: scale(1.06);
        }
        .imgfxhalftone_img {
          transition: filter 0.7s cubic-bezier(0.22, 1, 0.36, 1),
                      transform 0.9s cubic-bezier(0.22, 1, 0.36, 1);
          filter: saturate(0.55) contrast(1.08);
        }
        .imgfxhalftone_frame:hover .imgfxhalftone_img,
        .imgfxhalftone_frame:focus-within .imgfxhalftone_img {
          filter: saturate(1.05) contrast(1);
          transform: scale(1.04);
        }
        @media (prefers-reduced-motion: reduce) {
          .imgfxhalftone_card { animation: none; }
          .imgfxhalftone_dots { animation: none; transition: opacity 0.4s ease; }
          .imgfxhalftone_img { transition: filter 0.4s ease; transform: none !important; }
          .imgfxhalftone_frame:hover .imgfxhalftone_img,
          .imgfxhalftone_frame:focus-within .imgfxhalftone_img { transform: none; }
        }
      `}</style>

      <div className="mx-auto flex max-w-3xl flex-col items-center text-center">
        <motion.p
          initial={reduceMotion ? false : { opacity: 0, y: 12 }}
          animate={inView && !reduceMotion ? { opacity: 1, y: 0 } : undefined}
          transition={{ duration: 0.6, ease: [0.22, 1, 0.36, 1] }}
          className="text-xs font-semibold uppercase tracking-[0.35em] text-indigo-600 dark:text-indigo-400"
        >
          Halftone Print
        </motion.p>
        <motion.h2
          initial={reduceMotion ? false : { opacity: 0, y: 16 }}
          animate={inView && !reduceMotion ? { opacity: 1, y: 0 } : undefined}
          transition={{ duration: 0.7, delay: 0.05, ease: [0.22, 1, 0.36, 1] }}
          className="mt-4 text-3xl font-semibold tracking-tight text-neutral-900 sm:text-4xl dark:text-neutral-50"
        >
          Ink dots dissolve into a clean photograph
        </motion.h2>
      </div>

      <div ref={ref} className="mx-auto mt-14 max-w-2xl">
        <figure
          tabIndex={0}
          className="imgfxhalftone_frame imgfxhalftone_card group relative block cursor-pointer overflow-hidden rounded-2xl bg-neutral-200 shadow-xl ring-1 ring-black/5 outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:bg-neutral-800 dark:ring-white/10"
          style={reduceMotion ? { animation: "none" } : undefined}
        >
          {/* eslint-disable-next-line @next/next/no-img-element */}
          <img
            src="/img/gallery/g14.webp"
            alt="A portrait rendered so the halftone dot screen lifts away on interaction"
            loading="lazy"
            draggable={false}
            className="imgfxhalftone_img aspect-[4/5] w-full object-cover"
          />

          <div
            aria-hidden="true"
            className="imgfxhalftone_dots pointer-events-none absolute inset-0 text-neutral-900/85 mix-blend-multiply dark:text-neutral-950"
          />

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

          <figcaption className="pointer-events-none absolute bottom-0 left-0 right-0 flex items-center justify-between gap-4 p-5">
            <span className="text-sm font-medium text-white drop-shadow">
              Grain Study No. 14
            </span>
            <span className="rounded-full bg-white/15 px-3 py-1 text-[11px] font-semibold uppercase tracking-wider text-white backdrop-blur-sm transition-opacity duration-500 group-hover:opacity-0 group-focus-within:opacity-0">
              Screened
            </span>
          </figcaption>
        </figure>

        <p className="mt-6 text-center text-sm text-neutral-500 dark:text-neutral-400">
          Hover or focus the image to clear the halftone screen and reveal full color.
        </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 →