Dither Image

PreviousNext

A compound figure built on next/image that applies a CSS-only Bayer dither through dither-plugin—full-frame dither, partial masked reveals, and typed props for every plugin CSS variable.

DitherImage

CSS-only Bayer dither via dither-plugin. Compound: DitherImage wraps a DitherImageFrame (the dithered surface) containing a DitherImageContent, with an optional DitherImageCaption that stays outside the filter.

Studio Ghibli-style still 1, dithered
xs · 8px cell, defaults
Studio Ghibli-style still 2, dithered
sm · 12px cell, defaults
Studio Ghibli-style still 3, dithered
md · 16px cell, defaults
Studio Ghibli-style still 4, dithered
lg · 20px cell, defaults
Studio Ghibli-style still 5, dithered
Color + soft · grayscale 0, contrast 80
Studio Ghibli-style still 6, dithered
Faded overlay · opacity 0.6, brightness 1.15
Studio Ghibli-style still 7, dithered
Circle · invert on dark · rounded-full, 12px cell, dots flip in dark mode

Reveal · DitherImageOverlay

Partial dither: DitherImageReveal stacks a masked DitherImageOverlay on the dithered frame. direction controls the gradient axis; from / to set mask stops (see component types for diagonals).

Studio Ghibli-style still 8, dithered
Studio Ghibli-style still 8, dithered
r · Clean left → dither right 0%→65%
Studio Ghibli-style still 9, dithered
Studio Ghibli-style still 9, dithered
l · Clean right → dither left 0%→65%
Studio Ghibli-style still 1, dithered
Studio Ghibli-style still 1, dithered
t · Clean top → dither bottom 0%→65%
Studio Ghibli-style still 2, dithered
Studio Ghibli-style still 2, dithered
b · Clean bottom → dither top 0%→65%
Studio Ghibli-style still 3, dithered
Studio Ghibli-style still 3, dithered
tl-br · Diagonal (clean top-left) 0%→70%
Studio Ghibli-style still 4, dithered
Studio Ghibli-style still 4, dithered
radial · Center clean, edges dither 25%→75%

Installation

The UI layer is a thin React wrapper; the visual effect comes from dither-plugin. Install the package and import it in your Tailwind v4 stylesheet alongside tailwindcss.

pnpm dlx shadcn@latest add https://cult-ui.com/r/dither-image.json

After the CLI finishes, ensure dither-plugin is installed and @import "dither-plugin"; is present in your global CSS (see Manual).

Usage

import { DitherImage, DitherImageCaption, DitherImageContent, DitherImageFrame, } from "@/components/ui/dither-image"

Full-frame dither

Wrap DitherImageContent in DitherImageFrame. The frame owns the dither-* utilities; captions belong in DitherImageCaption outside the frame so text is not blurred or grayscale-filtered.

<DitherImage> <DitherImageFrame aspectRatio="square" size="md"> <DitherImageContent src="/images/photo.jpg" alt="Example" fill sizes="(min-width: 768px) 33vw, 100vw" /> </DitherImageFrame> <DitherImageCaption> Caption stays crisp outside the filtered surface. </DitherImageCaption> </DitherImage>

Partial reveal (clean layer + dither)

Use DitherImageReveal as a relative overflow-hidden stage. Stack DitherImageFrame (dithered) and DitherImageOverlay (same image, masked) as siblings. The overlay’s direction, from, and to props control the gradient mask.

import { DitherImageContent, DitherImageFrame, DitherImageOverlay, DitherImageReveal, } from "@/components/ui/dither-image" export function DitherRevealExample() { return ( <DitherImageReveal className="size-72 overflow-hidden rounded-xl"> <DitherImageFrame invertOnDark size="lg" aspectRatio="square"> <DitherImageContent src="/photo.jpg" alt="" fill sizes="288px" /> </DitherImageFrame> <DitherImageOverlay src="/photo.jpg" alt="" fill sizes="288px" from={0} to={65} /> </DitherImageReveal> ) }

Dark mode and invertOnDark

When invertOnDark is set on DitherImageFrame, the frame is wrapped so the dither reads correctly in dark mode while photo colors are counter-inverted on DitherImageContent.

next/image and arbitrary sources

For remote URLs, blobs, or data URLs, the image optimizer may need unoptimized or remotePatterns in next.config. The upload playground below uses the same pattern you can copy into product code.

API

ExportRole
DitherImage<figure> wrapper; groups frame + caption without applying dither filters.
DitherImageFrameDither surface: size, aspectRatio, grayscale, contrast, brightness, blur, opacity, rounded, invertOnDark, and CSS variables via style.
DitherImageContentnext/image child; use fill + object-cover sizing inside the frame.
DitherImageRevealRelative, overflow-hidden stage for partial dither layouts.
DitherImageOverlayAbsolutely positioned duplicate image with a gradient maskImage; props direction, from, to, maskClassName.
DitherImageCaption<figcaption> with muted typography defaults.

Types: DitherSize, DitherAspectRatio, DitherRevealDirection are exported for props and demos.

Notes

  • The dither class must sit on a wrapper around the media: the plugin paints with ::after, which does not apply to raw <img> / <video> nodes.
  • The frame applies filter: grayscale() brightness() blur() contrast() to all descendants—keep captions and UI chrome outside DitherImageFrame.
  • Effect is CSS-only and Safari-friendly (no SVG filters). Runtime cost is negligible once styles are loaded.

Interactive playground (upload & URL)

Cult UI · sandbox

DitherImage — upload

Upload a file, paste an image or GIF URL, or use the sample. Tune dither-plugin live. Remote URLs, blobs, and data: use next/image with unoptimized so any host works without changing next.config.

Sample wallpaper — upload or paste a URL to preview
Sample wallpaper — upload or paste a URL to preview

02

Reveal (partial dither)

Layer a masked clean copy over the dither stack.

Show dither on one side and the original photo on the other via a CSS mask.

0%
65%

03

Dither parameters

Filters run on the source before the dot screen. All transitions are plain state updates — tweak freely.

1
120
1
0px
1

04

Frame

Cell size maps to the Bayer matrix; aspect crops the frame without stretching your asset.

This example depends on your local shadcn-style primitives (button, input, label, switch). Copy those from the demo or wire your own controls—the dither-image registry item is only the figure primitives above.