Componentregistry:component
Duotone & Print Filter
Two-ink colour treatment for photos in the spirit of risograph and screen prints: seven ink presets or any two colours, a mix slider and optional misregistration that can slide into register when the image scrolls into view or as you scroll. Pure SVG filters.
Installation
$ npx shadcn@latest add @creative-ui/duotone-imageOn an older shadcn CLI, use the URL: npx shadcn@latest add https://ui.krutikraut.com/r/duotone-image.json
Preview
Inks
#1d3fb0 → #ffe0e6
Mix1.00
Contrast1.1
Misregistration3px
Usage
Example.tsx
import Image from "next/image"
import { DuotoneImage } from "@/components/duotone-image"
export function Cover() {
return (
<DuotoneImage
src="https://images.unsplash.com/photo-1577720643272-265f09367456?w=900&q=80&auto=format&fit=crop"
alt="Woman carrying a water jar"
preset="riso"
offset={3}
className="aspect-square w-72 rounded-xl"
/>
)
}
// Slide the shadow pass into register the first time it scrolls into view.
<DuotoneImage src="/poster.jpg" preset="fluoro" offset={2} registration="view" className="aspect-[4/5] w-72" />
// Or filter an element you already render:
<DuotoneImage shadow="#0a2a6e" highlight="#dbe7f5" className="w-72">
<Image src="/portrait.jpg" alt="Portrait" width={600} height={600} />
</DuotoneImage>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| src | string | — | Image URL. Omit when wrapping your own element with children. |
| alt | string | "" | Accessible description. Leave empty for decorative images. |
| preset | "riso" | "newsprint" | "cyanotype" | "sepia" | "fluoro" | "forest" | "midnight" | "riso" | A named ink pair. Overridden by shadow and highlight. |
| shadow | string | from preset | Ink for the shadows. Hex or rgb() only. |
| highlight | string | from preset | Ink for the highlights. Hex or rgb() only. |
| mix | number | 1 | Strength of the effect over the original, 0 to 1. Ignored when wrapping children. |
| contrast | number | 1.1 | Tone contrast before inking, roughly 0.5 to 2. |
| offset | number | 0 | Riso-style misregistration: offsets a second pass of the shadow ink by this many px. |
| offsetAngle | number | 30 | Direction of the misregistration in degrees. |
| registration | "static" | "view" | "scroll" | "static" | Animate the shadow pass into register, like a riso pull. view slides it in once, the first time the image scrolls into view. scroll ties it to the image's position in the viewport. It lands on offset. Needs src. |
| registrationOffset | number | 24 | How far out of register the pass starts, along offsetAngle, in px. It also starts turned by about 0.06° per px. |
| registrationDuration | number | 1200 | Length of the view slide in ms. |
| fit | "cover" | "contain" | "cover" | How the image fills the box. |
| imageClassName | string | — | Class names for the img element. |
| children | React.ReactNode | — | Filter an existing element, such as next/image, instead of rendering an img. |
Also accepts every div prop, which is spread onto the root element.
Also exported
- duotonePresets
- The seven ink pairs as
{ shadow, highlight }hex values, for building your own picker. - parseColor(input)
- Parses
#rgb,#rrggbb,#rrggbbaa,rgb()andrgba()into 0 to 1 channels, ornull.
Notes
- Inks are baked into an SVG filter, so CSS variables and named colours are not supported. An unreadable shadow falls back to black and an unreadable highlight to white.
offsetadds a second copy of the image.mixbelow 1 adds one more.offsetandregistrationneedsrcand are ignored when wrappingchildren.- With
registration, the pass travels ontransformandopacityonly.viewis a CSS transition triggered once;scrollwrites the transform fromrequestAnimationFramewhile the image is on screen, with no React renders. - With reduced motion the pass is in register from the first paint. To replay
view, remount with a newkey. - It is a client component because of the registration animation. With the default
registration="static"it renders the same markup on the server and adds no observers. ImportduotonePresetsandparseColorfrom client code.
Dependencies
shadcn/utils
