Editor: Keep the canvas height stable while resizing the canvas - #81163
Conversation
Dragging the canvas resize handles left the inline height of `.editor-resizable-editor` as a percentage of its container, such as `104.632%`, and collapsed the white page to its content height. `re-resizable` renders from its own state while resizing and ignores the `size` prop, converting the pixel height it started with into a percentage whenever that prop is a percentage. Switching the height to `100%` on resize start therefore never applied, it only changed the unit of the stale value. Hold the device height the drag started at until the drag ends, so the prop stays in pixels for the whole drag. The iframe body's `min-height: 100vh` was keyed off the device height, which stops applying as soon as the width leaves the preset, so the body collapsed on the first move and again on release. Key it off the canvas having a constrained width instead. Co-Authored-By: Claude <noreply@anthropic.com>
The two comments restated what the adjacent expressions already say, so they only added noise to the resize handling logic. Co-Authored-By: Claude <noreply@anthropic.com>
|
Size Change: +61 B (0%) Total Size: 7.82 MB 📦 View Changed
|
|
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. |
|
Should it always be full height I wonder? For preview/testing purposes, what matters most is the width of the device, not the height. |
Nice catch; I was missing tests for tall screens. If we are to align with the behavior of the site editor, I believe the aspect ratio should always be maintained. site-editor-resizable.mp4
Personally, I think this is fine. The important thing for responsive style is the width 🙂 |
The canvas frame was given a fixed pixel height derived from the device aspect ratio at the mobile and tablet preset widths, so the frame stopped short of the editor height and had to be held stable while resizing. Drop the ratio entirely and let the canvas fill the available height at every width, which also removes the now unused getCanvasHeight selector. While dragging, clearing the canvas width near the container edge turned resizing off mid-drag, unmounting the handle under the pointer along with the canvas frame styles. Only snap back to a fluid width once the drag has ended. Co-Authored-By: Claude <noreply@anthropic.com>
I tried to apply the full height at all times. What do you think? As a side effect, the private resizae-canvas-full-height.mp4 |
|
I'm curious why the height isn't as important? If a user has a cover block that's viewport height (e.g. a hero), then it might be handy to see that it looks good at different approximate device sizes. |
I'm not sure an approximate size is useful in that many scenarios. For instance, if the approximate preview shows that a CTA is above the fold, and then you look at in on an actual phone and it's not. The 100vh case makes sense but then I guess you can always resize the window to check different heights? I'm not super convinced either way, but thinking that if it's too complicated to make a reasonably proportional height work across device sizes and random in between sizes, the benefits might not outweigh the costs if that makes sense 😅 |
Should we perhaps allow resizing not only the width but also the height? However, it might be too late to introduce this approach in 7.1. |
A width-constrained canvas (a device preview or a dragged resize) kept the full editor height, which reads as an unnaturally elongated frame on tall screens. Derive the height from the canvas width by interpolating between the aspect ratio of the available space and a portrait target ratio, mirroring the site editor's resizable frame, and clamp it so it never exceeds the space available. Using the container width as the upper anchor keeps the height monotonic in the width and exactly 100% at full width. `re-resizable` ignores the `size` prop while a drag is in progress, so the height is animated through motion to stay in sync with the width during the drag. The CSS height transition is dropped to avoid animating it twice. Co-Authored-By: Claude <noreply@anthropic.com>
# Conflicts: # packages/editor/src/store/test/private-selectors.js
|
As an alternative approach, I tried implementing something similar to the resizable preview in the site editor. Essentially, this maintains the aspect ratio while ensuring it doesn't exceed 100% of the height. This means that when the browser height is narrow, the canvas will almost always be close to 100% height. Conversely, when the browser height is tall, the canvas will occupy a larger proportion while maintaining its aspect ratio. What do you think about this approach? resizable-canvas.mp4 |
|
Flaky tests detected in 9d92b0f. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/31169901353 should load styles added through enqueue_block_assets in |
Yes, this feels better. Certainly better than what we have now 😅 and also nicer than always using full height. Let's go with this! |
| : '100%' | ||
| } | ||
| onResizeStart={ () => setIsResizingCanvas( true ) } | ||
| onResizeStop={ () => setIsResizingCanvas( false ) } |
There was a problem hiding this comment.
It looks like these two props were introduced recently in #80553; do we need to keep them around now that we're not using them anymore?
There was a problem hiding this comment.
Nice catch, fixed in 9e1fafe. Since ResizableEditor is a private component, we can safely remove those props.
| * @param {Object} containerSize The available space, as `{ width, height }` in pixels. | ||
| * @return {number} The canvas height in pixels. | ||
| */ | ||
| function getCanvasHeight( width, containerSize ) { |
There was a problem hiding this comment.
Maybe worth adding some tests for the new function?
tellthemachines
left a comment
There was a problem hiding this comment.
Thanks for updating, LGTM now!
|
There was a conflict while trying to cherry-pick the commit to the wp/7.1 branch. Please resolve the conflict manually and create a PR to the wp/7.1 branch. PRs to wp/7.1 are similar to PRs to trunk, but you should base your PR on the wp/7.1 branch instead of trunk. |
|
Backported: #81374 |
* Editor: Keep the canvas height stable while resizing the canvas Dragging the canvas resize handles left the inline height of `.editor-resizable-editor` as a percentage of its container, such as `104.632%`, and collapsed the white page to its content height. `re-resizable` renders from its own state while resizing and ignores the `size` prop, converting the pixel height it started with into a percentage whenever that prop is a percentage. Switching the height to `100%` on resize start therefore never applied, it only changed the unit of the stale value. Hold the device height the drag started at until the drag ends, so the prop stays in pixels for the whole drag. The iframe body's `min-height: 100vh` was keyed off the device height, which stops applying as soon as the width leaves the preset, so the body collapsed on the first move and again on release. Key it off the canvas having a constrained width instead. Co-Authored-By: Claude <noreply@anthropic.com> * Editor: Drop redundant comments in the visual editor canvas sizing The two comments restated what the adjacent expressions already say, so they only added noise to the resize handling logic. Co-Authored-By: Claude <noreply@anthropic.com> * Editor: Always fill the editor height instead of a device aspect ratio The canvas frame was given a fixed pixel height derived from the device aspect ratio at the mobile and tablet preset widths, so the frame stopped short of the editor height and had to be held stable while resizing. Drop the ratio entirely and let the canvas fill the available height at every width, which also removes the now unused getCanvasHeight selector. While dragging, clearing the canvas width near the container edge turned resizing off mid-drag, unmounting the handle under the pointer along with the canvas frame styles. Only snap back to a fluid width once the drag has ended. Co-Authored-By: Claude <noreply@anthropic.com> * Editor: Scale the canvas height with its width instead of always filling A width-constrained canvas (a device preview or a dragged resize) kept the full editor height, which reads as an unnaturally elongated frame on tall screens. Derive the height from the canvas width by interpolating between the aspect ratio of the available space and a portrait target ratio, mirroring the site editor's resizable frame, and clamp it so it never exceeds the space available. Using the container width as the upper anchor keeps the height monotonic in the width and exactly 100% at full width. `re-resizable` ignores the `size` prop while a drag is in progress, so the height is animated through motion to stay in sync with the width during the drag. The CSS height transition is dropped to avoid animating it twice. Co-Authored-By: Claude <noreply@anthropic.com> * Editor: Remove unused blockEditorStore import from private selectors test * Editor: Remove unused onResizeStart and onResizeStop props from ResizableEditor * Editor: Add unit tests for the visual editor getCanvasHeight helper --------- Co-authored-by: t-hamano <wildworks@git.wordpress.org> Co-authored-by: tellthemachines <isabel_brison@git.wordpress.org> Co-authored-by: talldan <talldanwp@git.wordpress.org>

Follow up to #80553, #80271.
What?
Fixes an issue where the canvas height changes unexpectedly the moment you start resizing the editor canvas.Makes the editor canvas always fill the editor height. The device aspect ratio is no longer used.Gives the editor canvas an aspect ratio that follows its width, capped so it never exceeds the available editor height.
Why?
The canvas has a fixed height only while its width matches a device preset (Tablet / Mobile).
getCanvasHeight()intentionally returnsundefinedas soon as the width moves off that preset, so the frame is free to fill the editor.The problem is when that switch happens. Dragging the resize handle updates the canvas width continuously, so the very first pixel of movement takes the width off the preset and drops the fixed height. The frame jumps from the device height to full editor height right as the drag begins, and the content under the cursor shifts before the user has resized anything meaningfully.
How?
Snapshot the height when the drag starts and keep using it until the drag ends:- OnonResizeStart, store the currentcanvasHeightin local state and use that value for the frame height for the whole drag. The height now stays put while dragging and only settles to its new value on release.- Applymin-height:100vhto the iframe body whenever the canvas has an explicit width, instead of only at device preset widths. Previously the body lost its minimum height as soon as the width left a preset, so the content could collapse mid-drag.- Always give the canvas 100% height, and remove the aspect ratio code that is no longer used.- Applymin-height:100vhto the iframe body whenever the canvas has an explicit width, instead of only at device preset widths.- Snap back to a fluid width only once the drag has ended.The
getCanvasHeightprivate selector and the per-device aspect ratio table are removed, since the height is no longer derived from the device preset and does not belong in the store. Instead,VisualEditormeasures the canvas container withuseResizeObserverand computes the height from the canvas width, interpolating the aspect ratio between the container's own ratio (canvas at full width) and a target 9:16 (canvas at the 300px minimum width), then clamping the result to the container height; outside resizing, including zoom out, the canvas stays at100%.min-height:100vhis applied to the iframe body whenever the canvas has an explicit width, instead of only at device preset widths. The height is animated withmotion.divinResizableEditorinstead of a CSSheighttransition, and the animation is skipped while dragging so the height tracks the pointer exactly and eases only when the width changes from the Preview dropdown. Finally, the width snaps back to fluid only once the drag has ended.Testing Instructions
Screenshots or screencast
Before
resize-before.mp4
After
resizable-canvas.mp4
Use of AI Tools
Claude Code was used to help investigate the cause and draft this description. All changes were reviewed and tested by me.