Skip to content

Editor: Keep the canvas height stable while resizing the canvas - #81163

Merged
t-hamano merged 9 commits into
trunkfrom
fix/canvas-height-during-resize
Aug 10, 2026
Merged

Editor: Keep the canvas height stable while resizing the canvas#81163
t-hamano merged 9 commits into
trunkfrom
fix/canvas-height-during-resize

Conversation

@t-hamano

@t-hamano t-hamano commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

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 returns undefined as 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:

- On onResizeStart, store the current canvasHeight in 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.
- Apply min-height:100vh to 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.
- Apply min-height:100vh to 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 getCanvasHeight private 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, VisualEditor measures the canvas container with useResizeObserver and 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 at 100%. min-height:100vh is applied to the iframe body whenever the canvas has an explicit width, instead of only at device preset widths. The height is animated with motion.div in ResizableEditor instead of a CSS height transition, 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

  1. Open the post editor and add enough content to scroll.
  2. Open the Preview dropdown and choose Tablet, then Mobile. The canvas narrows and takes a device-like shape, and its bottom edge never goes past the editor area.
  3. Make the browser window shorter. The canvas stays within the editor height and ends up close to full height.
  4. Make the browser window taller. The canvas keeps its aspect ratio instead of stretching to fill the extra height.
  5. Drag the left or right resize handle slowly. The height follows the width continuously, with no jump at the moment the drag starts or ends.
  6. Drag close to the container edge and confirm the resize handles stay visible while the pointer is down.
  7. Release the handle near the edge. The canvas returns to a fluid full width and full height.

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.

t-hamano and others added 2 commits August 4, 2026 23:58
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>
@github-actions github-actions Bot added the [Package] Editor /packages/editor label Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

Size Change: +61 B (0%)

Total Size: 7.82 MB

📦 View Changed
Filename Size Change
build/scripts/compose/index.min.js 11.7 kB -6 B (-0.05%)
build/scripts/editor/index.min.js 510 kB +94 B (+0.02%)
build/scripts/viewport/index.min.js 1.24 kB -7 B (-0.56%)
build/styles/editor/style-rtl.css 31.5 kB -3 B (-0.01%)
build/styles/editor/style-rtl.min.css 26.8 kB -6 B (-0.02%)
build/styles/editor/style.css 31.6 kB -4 B (-0.01%)
build/styles/editor/style.min.css 26.8 kB -7 B (-0.03%)

compressed-size-action

@t-hamano t-hamano added the Backport to WP 7.1 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta label Aug 4, 2026
@t-hamano t-hamano added the [Type] Bug An existing feature does not function as intended label Aug 4, 2026
@t-hamano t-hamano self-assigned this Aug 4, 2026
@t-hamano t-hamano moved this to 🔎 Needs Review in WordPress 7.1 Editor Tasks Aug 4, 2026
@t-hamano
t-hamano marked this pull request as ready for review August 4, 2026 15:26
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

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 props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

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>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@talldan

talldan commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

I personally think it feels like a bug now that it expands to full height whenever dragging:

Kapture.2026-08-04.at.17.12.44.mp4

My feeling is that if we're showing mobile/tablet as selected on the topbar, then it should maintain the right aspect ratio:
Screenshot 2026-08-04 at 5 14 36 pm

@tellthemachines

Copy link
Copy Markdown
Contributor

Should it always be full height I wonder? For preview/testing purposes, what matters most is the width of the device, not the height.

@t-hamano

t-hamano commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

I personally think it feels like a bug now that it expands to full height whenever dragging:

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

Should it always be full height I wonder?

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>
@t-hamano

t-hamano commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

Should it always be full height I wonder? For preview/testing purposes, what matters most is the width of the device, not the height.

I tried to apply the full height at all times. What do you think? As a side effect, the private getCanvasHeight selector has been removed.

resizae-canvas-full-height.mp4

@talldan

talldan commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

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.

@tellthemachines

Copy link
Copy Markdown
Contributor

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 😅

@t-hamano

t-hamano commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

I guess you can always resize the window to check different heights?

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.

t-hamano and others added 2 commits August 7, 2026 19:15
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
@t-hamano

t-hamano commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

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

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Flaky tests detected in 9d92b0f.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/31169901353
📝 Reported tests:

should load styles added through enqueue_block_assets in /test/e2e/specs/editor/plugins/iframed-equeue-block-assets.spec.js, passed after 1 failed attempt.

@tellthemachines

Copy link
Copy Markdown
Contributor

What do you think about this approach?

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 ) }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth adding some tests for the new function?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 55fd27d

@tellthemachines tellthemachines left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating, LGTM now!

@t-hamano
t-hamano merged commit 107fb08 into trunk Aug 10, 2026
47 of 49 checks passed
@t-hamano
t-hamano deleted the fix/canvas-height-during-resize branch August 10, 2026 07:22
@github-project-automation github-project-automation Bot moved this from 🔎 Needs Review to ✅ Done in WordPress 7.1 Editor Tasks Aug 10, 2026
@github-actions github-actions Bot added this to the Gutenberg 23.8 milestone Aug 10, 2026
@github-actions

Copy link
Copy Markdown

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.

# Checkout the wp/7.1 branch instead of trunk.
git checkout wp/7.1

# Create a new branch for your PR.
git checkout -b my-branch

# Cherry-pick the commit.
git cherry-pick 107fb08d0e8fbea88c8fae5b6a8d0376f38bbea1

# Check which files have conflicts.
git status

# Resolve the conflict...
# Add the resolved files to the staging area.
git status
git add .
git cherry-pick --continue

# Push the branch to the repository
git push origin my-branch

# Create a PR and set the base to the wp/7.1 branch.
# See https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-base-branch-of-a-pull-request.

@t-hamano

Copy link
Copy Markdown
Contributor Author

Backported: #81374

@t-hamano t-hamano added Backported to WP Core Pull request that has been successfully merged into WP Core and removed Backport to WP 7.1 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta labels Aug 10, 2026
shail-mehta pushed a commit that referenced this pull request Aug 12, 2026
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backported to WP Core Pull request that has been successfully merged into WP Core [Package] Editor /packages/editor [Type] Bug An existing feature does not function as intended

Projects

Development

Successfully merging this pull request may close these issues.

3 participants