KQ-1490 · CROP/MARKS

Crop Marks

A selection frame laid out like a print sheet: drag on the picture to draw a frame from the press point, inside it to move it, or on a corner or edge to resize it, with printer's crop marks off each corner, the picture dimmed outside and Shift keeping the aspect. The hand is never pulled — snap lines within reach only light up — and on release the frame settles onto them on springs.snap while the marks, stretched while held, draw back in on the same spring and each caught line flashes with a tick; aspect changes glide on springs.glide. The frame is a focusable group: arrow keys move it, Shift with an arrow resizes it, Alt moves ten steps, Home centres it, and a key that would cross a snap line stops on it first.

KQ-1490 · CROP/MARKS
Drag on the picture to draw a frame. Arrow keys move the frame, Shift with an arrow resizes it, Home centres it.

cover photo · drag on the photo to draw

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/crop-marks

Props

value / defaultValue
{ x: number; y: number; width: number; height: number }
default — / { x: 0.15, y: 0.15, width: 0.7, height: 0.7 }
The frame as fractions (0 to 1) of the picture, controlled or uncontrolled.
onValueChange
(value: CropRect, size: { width: number; height: number }) => void
default —
Fires with the settled frame from the release, key or aspect change that set it (not on every move of a drag), and its size in source units.
aspect
"free" | "1:1" | "4:3" | "3:2" | "16:9" | number
default "free"
A locked width ÷ height; the frame glides to it about its centre. Shift locks the current ratio for one drag.
snap
number
default 10
The magnet's reach in px: edges released this close to a snap line settle onto it. 0 turns snapping off.
grid
"thirds" | "golden" | "quarters" | "none"
default "thirds"
The lines it snaps to besides the picture's edges and centre, ticked in the margin.
step
number
default 0.01
One arrow key's move or resize, as a fraction of the picture; Alt moves ten.
minSize
number
default 24
The smallest frame a release leaves, in px.
dim
number
default 0.5
How dark the picture is outside the frame, 0 to 1.
sourceSize
{ width: number; height: number }
default the picture's own pixels
The source's size in pixels, for the W × H readout and the size `onValueChange` reports. Give the picture area the source's aspect so the two agree.
label
string
default "Crop frame"
The frame's accessible name.
color
string
default —
The crop marks' colour. By default they are white blended by difference, so they read on any picture in both themes.
guideColor
string
default "var(--accent-bright)"
The snap guides' colour.
size
"sm" | "md" | "lg"
default "md"
Mark length (8, 12 or 16px) and the sheet's margin.
sound
boolean
default false
Tick as the frame settles on a line, on keyboard detents, and faintly as a guide lights mid-drag.
disabled
boolean
default false
No drawing, moving or keys; the frame leaves the tab order.
className
string
default "h-[220px] w-full"
Sizes the sheet.
children
React.ReactNode
default —
The picture, filling the area inside the margin.