Crop Guard

Checks a crop against faces, headlines and platform overlays, so a profile photo or Story doesn't cut off a face or hide text under the UI.

StopsProfile crop cut off face

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/crop-guard.json

The files land in your project. They're yours to edit.

Usage

crop-guard-usage.tsx
import { CropGuard, type DetectedBox } from "@/components/ui/crop-guard"

<CropGuard
  src={src}
  alt="Two teammates smiling outdoors above the caption Meet the team"
  imageWidth={1200}
  imageHeight={900}
  boxes={boxes}
  defaultPreset="story"
  defaultValue={{ x: 420, y: 60, w: 400, h: 711 }}
/>

What it catches

  • Faces or text boxes cut by the crop edge
  • Content hidden by a circle mask or a platform's Story UI
  • Subjects too small to recognise at thumbnail size
  • A nearest fit that keeps everything in, moving the crop as little as possible

Props

CropGuard props
PropTypeDefault
src* (required)

Image to crop.

string—
imageWidth* (required)

Its natural size in pixels. Crops and boxes use these pixels.

number—
imageHeight* (required)

Natural height of the image in pixels.

number—
alt

Accessible description of the image.

string""
boxes

Faces and text already found, in image pixels.

DetectedBox[][]
detect

Finds faces and text once the image loads (the FaceDetector API, a model, a server call). Merged with `boxes`.

(image: HTMLImageElement) => Promise<DetectedBox[]>—
presets

Crop shapes and safe zones to offer.

CropPreset[]DEFAULT_PRESETS
preset

Controlled preset id.

string—
defaultPreset

Initially selected preset id when uncontrolled.

string—
onPresetChange

Called when the user picks another preset.

(id: string) => void—
value

Controlled crop in image pixels.

Rect—
defaultValue

Initial crop when uncontrolled. Defaults to the largest centred crop.

Rect—
onValueChange

Called with the crop rectangle whenever it changes.

(crop: Rect) => void—
nouns

Words used in the warnings.

{ face: string; faces: string; text: string; texts: string }{ face: "a face", faces: "faces", text: "text", texts: "l…
cutText

Templates: `{what}`, `{ui}` and `{size}` are filled in.

string"Cuts through {what}."
coveredText

Message when a subject sits under platform UI.

string"{what} sits under {ui}."
tinyText

Message when a subject is too small at preview size.

string"Text is too small to read at {size} px."
noFitText

Message when no crop can fit every subject.

string"They don't all fit this shape."
fixLabel

Text for the fit button.

string"Fit them"
previewLabel

Caption when the preview is at the real size, and when it's scaled down.

string"Actual size, {size} px"
scaledLabel

Label for the scaled-down preview.

string"Shown {size} px wide"
previewMax

Largest preview side, in px.

number96
bothText

Joins faces and text in a warning: "{a} and {b}".

string"{a} and {b}"
uiLabel

`{ui}` when a preset has no `unsafeLabel`.

string"the app's UI"
wholeText

Status when nothing is cut, and when there is nothing to check.

string"Every face and line of text is whole."
nothingText

Message when there are no subjects to check.

string"Nothing detected to protect."
shapesLabel

Screen reader names of the shape picker and the crop box.

string"Crop shape"
cropAreaLabel

Accessible label for the crop area.

string"Crop area. Arrow keys move it, plus and minus resize it."

* required. Also accepts the native props of <div> (className, aria-*, data-*…), passed through to it.

Accessibility

  • The crop area is keyboard-operable: arrow keys move it, + and - resize
  • Shape presets are a role="radiogroup" with arrow-key navigation
  • The image takes alt text; problems are described in text

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

  • Needs face and text boxes from you or a detect function; it has no built-in detector
  • Safe zones are presets and may not match a platform's latest layout
  • Sometimes no crop can fit everything; it says so

What it stops

Profile crop cut off face

Also known as

image crop · avatar upload · profile photo · instagram story · safe zone · cropper · thumbnail · dp

Field type

Dependencies

Pairs well with