Skip to content
WANCRAFT

E.169Backgrounds & Effects

Halftone Image

Any image re-printed as a rotated halftone screen of dots, squares or lines, with a pointer lens that reveals the original.

NewReactCanvasTailwindadvanced

Overview

Halftone Image redraws a picture as a print screen on a single canvas. The image is sampled a few times per cell, and each cell of a rotated grid prints a dot, square or line whose size carries the local tone, with an adjustable contrast curve. Mono mode prints in the theme ink — dark ink for shadows on light pages, light ink for highlights on dark ones, so the picture stays positive — while duotone lays an accent screen under the ink screen at a second angle, like a two-colour riso print. Move a mouse (or press and drag a finger) to open a lens that reveals the original image or magnifies the screen locally. The canvas is cached and redraws only when a prop, the size, the theme or the lens changes; without a src it paints a built-in gradient landscape, so it never needs a network request.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/halftone-image.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/halftone-image.json

Method 02 · Copy and paste

  1. 1. No extra dependencies — React and Tailwind CSS only.
  2. 2. Copy the source from the Code tab into components/ui/halftone-image.tsx.
  3. 3. Import it and adjust props.
Dependencies
None
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { HalftoneImage } from "@/components/ui/halftone-image";

export function HalftoneImageExample() {
  return (
    <div className="relative h-80 w-full overflow-hidden rounded-xl">
      <HalftoneImage
        alt="Evening ridges under a low sun, printed as a halftone"
      />
    </div>
  );
}

03Props

PropTypeDefaultDescription
srcstring—Image URL. Remote images need CORS headers because pixels are read; without src (or if loading fails) a built-in gradient landscape is used.
altstring—Alternative text. When set, the root is role="img" with this aria-label; otherwise the whole component is aria-hidden.
cellSizenumber10Screen pitch: distance between dot centres in px.
anglenumber45Screen angle in degrees.
shape"circle" | "square" | "line""circle"Dot shape; line draws a line screen whose thickness carries the tone.
colorMode"mono" | "duotone""mono"Mono prints in the theme ink; duotone adds an accent screen 30° off the ink screen.
contrastnumber1.2Tone contrast around the midpoint (1 = linear).
lens"reveal" | "magnify" | "none""reveal"Pointer lens: show the original image, magnify the screen, or nothing.
lensRadiusnumber110Lens radius in px.
accentstring"#FF7448"Duotone screen and lens ring colour (any CSS colour).
fit"cover" | "contain""cover"How a src image fits the box.
...propsHTMLAttributes<HTMLDivElement>—Forwarded to the root, which is absolute inset-0 — place it inside a sized, positioned parent.

04Features

  • 01Rotated print screen with circle, square or line cells; tone sampled with downscale averaging (no aliasing)
  • 02Theme-aware ink: shadows in dark ink on light themes, highlights in light ink on dark themes
  • 03Duotone mode: accent and ink screens at two angles, like a two-colour riso print
  • 04Pointer lens that reveals the original image or bulges and enlarges the dots
  • 05Static when idle: the screen is cached and redrawn only on prop, size, theme or lens changes
  • 06Single canvas, devicePixelRatio-aware (capped at 2), resized with ResizeObserver; built-in generated image, no network needed
motion · tweeninput · pointerinput · hover

05Accessibility

  • With alt, the root is role="img" with that aria-label; without it, the component is aria-hidden as decoration.
  • The canvas itself is aria-hidden; the lens is a visual extra and carries no information that alt text must not.
  • The lens works with touch and pen while pressed; listeners are passive, so page scrolling is never blocked.
  • With prefers-reduced-motion the lens appears and disappears instantly instead of easing.

06Limitations

  • Cross-origin images must be served with CORS headers; otherwise the generated art is shown instead.
  • Very small cells on very large canvases cost more to rebuild (a one-off on resize or prop change).