Componentregistry:component
Etching
Renders an image as a copperplate engraving on canvas: hatch lines that bow around the forms and swell with the shadows, cross-hatching in the darks, a third pass in the deepest shadows, and ink on paper with plate tone. The lines cut themselves in, stroke by stroke, when it scrolls into view, or follow a controlled progress value.
Installation
$ npx shadcn@latest add @creative-ui/etchingOn an older shadcn CLI, use the URL: npx shadcn@latest add https://ui.krutikraut.com/r/etching.json
Preview
Line spacing4px
Angle35°
Layers
Ink
Usage
Example.tsx
import { Etching } from "@/components/etching"
export function Plate() {
return (
<Etching
src="/marble-bust.webp"
alt="Marble bust of a woman in profile"
className="aspect-[4/5] w-80"
/>
)
}
// Finer lines in oxblood, straight-ruled, drawn as the reader scrolls.
<Etching src="/still-life.jpg" lineSpacing={3} curve={0} ink="#6e2a24" progress={scrollProgress} className="w-full" />Props
| Prop | Type | Default | Description |
|---|---|---|---|
| src* | string | — | Image URL. Cross-origin images must be served with CORS headers, or the canvas cannot read them. |
| alt | string | "" | Accessible description. Leave empty for decorative images. |
| lineSpacing | number | 4 | Gap between hatch lines in CSS px. Smaller is finer and takes longer to lay out. |
| angle | number | 35 | Angle of the first pass of lines, in degrees. |
| curve | number | 0.5 | How much the lines bow and swerve around the light and shadow of the image, 0 to 1. 0 is ruled straight. |
| layers | 1 | 2 | 3 | 3 | Passes of the burin. 1 hatches only. 2 adds cross-hatching in the darker tones. 3 adds a third pass in the deepest shadows. |
| ink | string | "#241d17" | Ink colour. Any CSS colour, or currentColor to follow the theme. |
| paper | string | "#f4f1ea" | Paper colour, or transparent. |
| draw | boolean | true | Cut the lines in the first time it scrolls into view: the first pass, then the cross-hatching, stroke by stroke. Plays once, pauses off screen, and not at all for reduced motion. |
| duration | number | 2600 | Length of the engraving in ms. |
| progress | number | — | Controlled progress, 0 to 1, for driving it from scroll or a slider. When set, it replaces the scroll-in animation. |
| fit | "cover" | "contain" | "cover" | How the image fills the plate. |
| seed | number | 5 | Seed for the hand tremor and the stroke order. |
| crossOrigin | "anonymous" | "use-credentials" | "anonymous" | Pass undefined for same-origin images that must not send a CORS request. |
| children | React.ReactNode | — | Content layered over the plate, such as a caption. |
Also accepts every div prop, which is spread onto the root element.
Notes
- Without an
aspect-*class or a height it takes the image's own aspect ratio once the image loads. - It draws to a canvas, so the image must be same-origin or served with CORS headers. If the pixels cannot be read, it leaves bare paper and logs a warning.
- Each frame inks only the newly cut stretch of each line, so the animation costs about the same as drawing the plate once. Moving
progressbackwards redraws from blank paper. - Images with a clear subject and a plain ground read best. Busy photos turn into dense cross-hatching at small sizes; raise
lineSpacingor lowerlayers. - Changing an option after the engraving has played redraws it at once instead of replaying. To replay, remount the component with a new
key. - The canvas has
role="img"andaltas its label. With an emptyaltit is hidden from assistive tech.
Dependencies
shadcn/utils
