Image editor: reserve inner gutter so crop handles stay accessible#77547
Conversation
There was a problem hiding this comment.
Pull request overview
Reserves an inner “gutter” inside the image cropper so resize handles (including their 44×44 touch targets) remain accessible even when the crop sits against the cropper’s clipping boundary (overflow: hidden), without requiring interaction-controller math changes.
Changes:
- Adds an inset inner
__canvaswrapper to serve as the positioning/interaction surface for image, overlays, stencil, grid, handles, and ARIA live region. - Moves pointer/keyboard event handlers and native wheel listener binding to the inner canvas so geometry (
getBoundingClientRect) matches the box used for crop math. - Removes now-redundant padding from the media editor modal canvas container.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
packages/media-editor/src/image-editor/react/components/cropper.tsx |
Introduces inner canvas wrapper and retargets measurement + event handling to it. |
packages/media-editor/src/image-editor/react/components/cropper.scss |
Adds inset canvas styling to reserve handle gutter within the cropper. |
packages/media-editor/src/components/media-editor-modal/style.scss |
Drops outer canvas padding since the gutter is now owned by the cropper. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Size Change: +51 B (0%) Total Size: 7.76 MB 📦 View Changed
ℹ️ View Unchanged
|
| justify-content: center; | ||
| background: $gray-100; | ||
| overflow: auto; | ||
| padding: $grid-unit-20; |
There was a problem hiding this comment.
Now with controls after #77540
Kapture.2026-04-23.at.13.31.10.mp4
| // Canvas measurement via ResizeObserver. The canvas is the inner | ||
| // positioning context for image/stencil/handles — inset from the root | ||
| // by the handle gutter, so crop math operates on the reduced box. | ||
| const canvasRef = useRef< HTMLDivElement >( null ); |
There was a problem hiding this comment.
Renamed to stress that the ref now represents the inner canvas (inset) size, not the outer/root container.
|
Flaky tests detected in 60ceb08. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/24814694756
|
bbc8538 to
60ceb08
Compare
andrewserong
left a comment
There was a problem hiding this comment.
Nice one, the added canvas area gives this more comfortable breathing room and it feels easier to use to me 👍
Not exactly what this PR is addressing but it's still a little difficult to expand the crop out again after reducing its size:
2026-04-23.14.51.21.mp4
Not something to deal with in this PR, I reckon let's get this one in and we can follow-up tweaking the cropping behaviour a bit further down the track once we've built everything else out.
LGTM!
|
Thanks for testing again!
Yeah, that's a separate issue and not a trivial update unfortunately. Using crop with zoom is the right way for now. What we have now was the result of a balance and some compromise in order to allow full crop area interaction (scrollwheel zoom, pan) and auto centering + zoom. Google photos handles zoom exclusively using the crop area, but others like react advanced cropper demos prioritize tooling over magic handles. I have another stream running to experiment with this, so 🤞🏻 we can get it working |



Description
Part of:
Follow up to #77479
Crop handles on the image editor use negative offsets and a 44×44 touch target that extends past the crop edge. The cropper root is
overflow: hidden(required for the dimming overlay'sbox-shadowtrick), so when the crop sits flush against the root, the handle rim and most of its touch target get clipped.After
Kapture.2026-04-22.at.16.44.02.mp4
Before
Kapture.2026-04-20.at.12.00.18.mp4
__canvaswrapper inset by 22px (half the touch target). Absolutely-positioned children (image, dimming, stencil, grid, handles, aria-live) now render inside the wrapper; the root stays as the clipping boundary for the dimming shadow.tabIndex,role, andaria-labelonto the wrapper so pointer geometry (e.currentTarget.getBoundingClientRect()) resolves against the same box crop math uses — no changes needed ininteraction-controller.ts.padding: $grid-unit-20from.media-editor-modal__canvas; the gutter lives in the cropper where it belongs.Test steps
+/-.MediaEditor/ImageEditor → Rectangle Cropstory: toggle freeform, confirm handles stay on-canvas.