/** * Cropper recipe — the crop UI chrome: framed viewport, darken-outside mask, * rule-of-thirds grid, and corner handles. The crop geometry is inline style on * the Selection (set by soma); the image is the composed ``. * * Tokens: `--cropper-*` (public, themeable) from `recipes/base.ts`. */ [data-cropper] { display: block; inline-size: 100%; } [data-cropper][data-disabled] { opacity: var(--opacity-disabled); pointer-events: none; } [data-cropper-viewport] { position: relative; overflow: hidden; inline-size: 100%; /* aspect-ratio is set inline from the image's natural size. Fallback 1:1. */ aspect-ratio: 1; border-radius: var(--cropper-viewport-radius); background: var(--cropper-viewport-bg); /* The image fills the viewport; no scrolling while dragging the selection. */ touch-action: none; user-select: none; } .eidos-cropper-image { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; } /* Stop the browser's native image drag-and-drop, which would cancel the pan. */ [data-cropper-viewport] img { -webkit-user-drag: none; user-select: none; } /* The crop rectangle. The huge box-shadow darkens everything outside it (clipped by the viewport's overflow:hidden) — it follows border-radius, so a round selection cuts a circular hole. */ [data-cropper-selection] { position: absolute; box-sizing: border-box; border: var(--cropper-selection-border-width) solid var(--cropper-selection-border); box-shadow: 0 0 0 9999px var(--cropper-mask-bg); cursor: move; touch-action: none; } [data-cropper-selection][data-shape='round'] { border-radius: 50%; } [data-cropper-selection][data-dragging] { cursor: grabbing; } /* When zoomed, the background image shows a `grab` cursor (drag it to pan / set the focus). The selection keeps `move` — it repositions the crop area. */ .eidos-cropper-viewport.is-zoomed .eidos-cropper-image { cursor: grab; } .eidos-cropper-viewport.is-zoomed .eidos-cropper-image:active { cursor: grabbing; } /* Rule-of-thirds — four thin lines at 1/3 and 2/3 on each axis. */ [data-cropper-grid] { position: absolute; inset: 0; pointer-events: none; background: linear-gradient(var(--cropper-grid-line), var(--cropper-grid-line)) 33.33% 0 / 1px 100% no-repeat, linear-gradient(var(--cropper-grid-line), var(--cropper-grid-line)) 66.66% 0 / 1px 100% no-repeat, linear-gradient(var(--cropper-grid-line), var(--cropper-grid-line)) 0 33.33% / 100% 1px no-repeat, linear-gradient(var(--cropper-grid-line), var(--cropper-grid-line)) 0 66.66% / 100% 1px no-repeat; } /* The thirds grid reads as noise inside a circular crop — hide it there. */ [data-cropper-selection][data-shape='round'] [data-cropper-grid] { display: none; } [data-cropper-handle] { position: absolute; box-sizing: border-box; inline-size: var(--cropper-handle-size); block-size: var(--cropper-handle-size); border: var(--cropper-handle-border-width) solid var(--cropper-handle-border); border-radius: 50%; background: var(--cropper-handle-bg); touch-action: none; } [data-cropper-handle][data-corner='nw'] { inset-block-start: 0; inset-inline-start: 0; transform: translate(-50%, -50%); cursor: nwse-resize; } [data-cropper-handle][data-corner='ne'] { inset-block-start: 0; inset-inline-end: 0; transform: translate(50%, -50%); cursor: nesw-resize; } [data-cropper-handle][data-corner='se'] { inset-block-end: 0; inset-inline-end: 0; transform: translate(50%, 50%); cursor: nwse-resize; } [data-cropper-handle][data-corner='sw'] { inset-block-end: 0; inset-inline-start: 0; transform: translate(-50%, 50%); cursor: nesw-resize; } /* Edge handles — centered on each side. */ [data-cropper-handle][data-corner='n'] { inset-block-start: 0; inset-inline-start: 50%; transform: translate(-50%, -50%); cursor: ns-resize; } [data-cropper-handle][data-corner='s'] { inset-block-end: 0; inset-inline-start: 50%; transform: translate(-50%, 50%); cursor: ns-resize; } [data-cropper-handle][data-corner='e'] { inset-block-start: 50%; inset-inline-end: 0; transform: translate(50%, -50%); cursor: ew-resize; } [data-cropper-handle][data-corner='w'] { inset-block-start: 50%; inset-inline-start: 0; transform: translate(-50%, -50%); cursor: ew-resize; } /* ── Zoom controls (− slider + reset) ───────────────────────────────── */ .eidos-cropper-zoom { display: flex; align-items: center; gap: var(--cropper-zoom-gap); margin-block-start: var(--space-2); } .eidos-cropper-zoom [data-slider] { flex: 1 1 auto; } .eidos-cropper-zoom-btn { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; inline-size: var(--cropper-zoom-btn-size); block-size: var(--cropper-zoom-btn-size); border: var(--border-width) solid var(--cropper-zoom-btn-border); border-radius: var(--radius-md); background: var(--cropper-zoom-btn-bg); color: var(--cropper-zoom-btn-fg); font-size: var(--font-size-md); line-height: 1; cursor: pointer; } .eidos-cropper-zoom-btn:hover { background: var(--color-surface-default); } .eidos-cropper-zoom-btn:focus-visible { outline: var(--focus-ring-width) solid var(--focus-ring-color); outline-offset: 0; }