The crop model
A normalized {x,y,w,h} rectangle, the exact cover math that fills a tile, and why it is not background-position.
The imageCrop specimen makes one promise: the rectangle you choose always fills the tile, at any tile aspect ratio, with no letterboxing and no guesswork. This page is how that promise is kept.
A normalized rectangle
A crop is four numbers, each a fraction between 0 and 1:
crop: { x: 0.36, y: 0.28, w: 0.3, h: 0.4 }They are fractions of the image's intrinsic pixels, with a top-left origin:
x,y— the top-left corner of the crop (0 = left/top edge, 1 = right/bottom edge).w,h— the width and height of the crop.
So { x: 0, y: 0, w: 0.34, h: 1 } is "the left 34%, full height," independent of whether the source is 1600×1067 or 4000×3000. Normalizing to fractions means a crop is portable across re-exports and resolutions.
The schema enforces the rectangle is real and in-bounds:
// from @lucentive-labs/loupe-schema
w > 0 && h > 0 // a crop must have area
x + w <= 1 // ...and must fit inside the image
y + h <= 1A crop that violates these fails parseConfig immediately — you cannot ship an out-of-bounds rectangle.
Why intrinsic dimensions are required
Declare every image asset with its real pixel size:
assets: {
board: { src: "https://.../valley.jpg", width: 1600, height: 1067 },
}The dimensions are not decoration. They are the input the cover math needs to map a normalized rect onto a fixed-aspect tile. Without them, Loupe falls back to an object-fit: cover focal point (centered on the crop) — usable, but approximate. With them, the framing is exact.
If a tile shows letterboxing or lands on the wrong detail, the intrinsic width/height are wrong, or the rect is. Fix the numbers and re-render — the math itself does not letterbox.
The exact cover math
Loupe renders a real <img> inside an overflow: hidden tile, absolutely positioned and scaled so the chosen rect exactly covers the tile. The tile has aspect ratio TILE_AR (4:3). This is the actual algorithm from cropToCss in @lucentive-labs/loupe-core:
export function cropToCss(
rect: TRect,
intrinsic: { width: number; height: number },
tileAspect: number,
): { widthPct: number; leftPct: number; topPct: number } {
const IW = intrinsic.width;
const IH = intrinsic.height;
const TH = 1;
const TW = tileAspect; // tile width in tile-height units
// Scale the full image so the rect covers the tile in BOTH axes (max = cover).
const Wimg = Math.max(TW / rect.w, IW / (rect.h * IH));
const Himg = (Wimg * IH) / IW;
// Offset the image so the rect's box lands centered on the tile.
const OX = -(rect.x * Wimg) - (rect.w * Wimg - TW) / 2;
const OY = -(rect.y * Himg) - (rect.h * Himg - TH) / 2;
return {
widthPct: round((Wimg / TW) * 100), // img width as % of tile width
leftPct: round((OX / TW) * 100), // img left offset as % of tile width
topPct: round((OY / TH) * 100), // img top offset as % of tile height
};
}Reading it in plain terms:
- Work in tile-height units. Set the tile height to
1; the tile width is thentileAspect. Everything is measured against that. - Scale to cover.
Wimgis the rendered full-image width that makes the rect cover the tile. TheMath.maxof "cover horizontally" and "cover vertically" guarantees the rect fills the tile in both axes — the defining property of cover. - Offset to center. Shift the image left/up so the rect's box sits centered in the tile, then split any overflow evenly.
- Return percentages. The three numbers become
width,left, andtoppercentages on the<img>, relative to the tile. Results are rounded to 3 decimals and-0is normalized to0, so the emitted CSS is clean and deterministic.
The result is three inline percentages on a real image:
<img class="loupe-crop__img" src="…" alt="…"
style="width:212.5%; left:-41.3%; top:0%" />Why not background-position
background-position (and background-size: cover) can center an image on a focal point, but it cannot reproduce an arbitrary normalized rectangle at an arbitrary tile aspect ratio without distortion or cropping you did not ask for. The percentage basis of background-position is the container minus image difference, which is non-linear and resolution-dependent.
Loupe instead computes the exact width-and-offset of a real <img>, so:
- the rect is reproduced precisely — what you specify is what frames;
- it is a real
<img>with realalt, so it is accessible and showsnaturalWidth > 0(the verification step asserts exactly that — no broken crops); - the same numbers render identically in the vanilla renderer, the React adapter, and the static artifact, because all three call the same core function.
See it switch
Same source image, four different normalized rects. Each one fills the 4:3 tile exactly. Lock a tile to send that crop into the featured preview band.
{ x: 0, y: 0, w: 1, h: 1 } // full wide
{ x: 0, y: 0, w: 0.34, h: 1 } // left third
{ x: 0.36, y: 0.28, w: 0.3, h: 0.4 } // center detail
{ x: 0, y: 0, w: 1, h: 0.34 } // top bandIterate crops against screenshots, not by eye in code. The agent method captures desktop + mobile shots with reduced motion; if a crop is off, nudge {x,y,w,h} and re-verify.