-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Image: Fix layout shift when lightbox is opened and closed #53026
Conversation
Size Change: +812 B (0%) Total Size: 1.62 MB
ℹ️ View Unchanged
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this seems reasonable to me 👍
I added the accesibility feedback label. Cause @artemiomorales mentioned there could be accesibility issues with this approach. @alexstine can you help me here? Thanks! |
CC: @joedolson or @afercia . |
Allowing the background to scroll can be disorienting if a user dismisses the lightbox and is no longer in the same location on the page as they were previously. Focus is set on close, but that won't necessarily land the user in exactly the same place they were when they started. What about using There's no evidence that |
Renaming to wp-has-lightbox-open is not a problem, and we avoid future problems. |
@joedolson Just to clarify the use case and ensure I understand it correctly:
Should
Regarding this, as part of this pull request, I added the |
While testing, I noticed that the I also found out that, if one scrolls quickly after opening the lightbox on iOS, that one is able to scroll even through the Since it seems like CSS may not be the best tool to prevent scrolling in this scenario considering that hiding the scrollbar causes a layout shift and potential errors with the lightbox calculations, I can explore how to prevent scrolling using JavaScript while the lightbox is open. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without overflow: hidden
, one can scroll freely while the lightbox is open on iOS, breaking the experience.
We can either try to find another CSS solution or solve it with JavaScript. I'm leaning towards JavaScript because even with overflow: hidden
, the experience breaks sometimes on mobile.
I'll look into addressing this as soon as I have bandwidth, but would be happy for someone else to hop onto this as well 😄
You're right that removing Lightbox.lightbox.-.8.August.2023.mp4We don't see it on other devices because the lightbox is closed on
By the way, it seems that mousewheel is deprecated, so I guess we should use Maybe another option is using overscroll-behavior, although I am not familiar with that. Or just keep the |
1ceddee
to
1fa6b22
Compare
This pull request has changed or added PHP files. Please confirm whether these changes need to be synced to WordPress Core, and therefore featured in the next release of WordPress. If so, it is recommended to create a new Trac ticket and submit a pull request to the WordPress Core Github repository soon after this pull request is merged. If you're unsure, you can always ask for help in the #core-editor channel in WordPress Slack. Thank you! ❤️ View changed files❔ lib/block-supports/behaviors.php |
@SantosGuillamot How does this approach look? It feels a bit awkward to put the callback outside of the store, but I can't think of a good reason to have it otherwise. Defining a general callback for the
I haven't tested on a physical mobile device yet so I'm not sure what the UX is like in that case, and dragging the scrollbar can feel a bit awkward, but otherwise it seems to work fairly well considering we're overriding the browser's native scrolling behavior. |
I didn't have time to test it, and I'll be AFK for ten days. I am not a big fan of adding global If I understood it correctly, the other option would be to use:
And add the arrow keys in the Escape conditional: link. And scrolling with the scrollbar wouldn't close the lightbox. Is this right? I don't know which one is better, to be honest. |
To ensure pinch to zoom works as expected when the lightbox is open on mobile, I added logic to disable the scroll override when touch is detected. Without this, the scroll override kicks in whenever one tries to pinch to zoom, and it breaks the experience. I also revised the styles for the scrim to make it opaque, as having content visible outside of the lightbox is distracting when pinching to zoom on a mobile device, and I think having a consistent lightbox across devices will make for the best user experience.
a81710c
to
2c94042
Compare
Thanks everyone for the discussion! I've gone ahead and disabled closing the lightbox on scroll 👍 I've also added a few improvements to make sure scrolling is indeed disabled when the lightbox is open on mobile. I've also added logic to ensure that pinch to zoom works as expected. Worth noting is that, after testing, I've come to the conclusion that the scrim should be 100% opaque, as having any visible content in the background is distracting when one is doing the pinch to zoom action, and I think it's best to have a consistent user experience for the lightbox across devices. I believe this PR is ready for a hopefully final review 😄 |
Flaky tests detected in 82eb2ba. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/6341647450
|
Making sure pinch to zoom works and a fully opaque scrim are both wins from my perspective. Thanks @artemiomorales! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there's talk in the patch about differentiating mobile and desktop behavior, but I didn't see anything that detects mobile or behaves differently on desktop other than the isTouching
event.
is there a point where we could know that we're not on mobile and pre-emptively unload the scroll handler?
it's too bad that the extra runtime overhead is only meant for mobile where it would matter more.
code wise nothing jumped out other than some places where I think we might be able to communicate more clearly what we're doing.
// to is triggered on the window; so we define it outside of | ||
// the store to signal that the behavior here is different. | ||
// If we find a compelling reason to move it to the store, | ||
// feel free to do so. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we turn these into JSDoc comments then the explanation will follow the variables and functions. otherwise they are only evident to those who look at this spot where they're created.
/**
* Stores a context-bound scroll handler.
*
* This callback could be defined inline inside of the store
* object but it's created externally to avoid confusion about
* it's logic…
*
* @var {function}
*/
let scrollCallback;
/**
* Tracks whether user is touching screen; used to differentiate mobile and desktop behaviors.
*
* @var {boolean}
*/
let isTouching = false;
/**
* Used to differentiate mobile and desktop behaviors.
*
* @var {boolean}
*/
let isTouching = false;
/**
* Lightbox page-scroll handler: prevents scrolling.
*
* This handler is added to prevent scrolling behaviors that
* trigger content shift while the lightbox is open…
*
* It would be better to accomplish this through CSS alone, but
* because … a JavaScript version is required to avoid … in
* order to provide the best visual experience.
*
* @param {object} context Interactivity page context?
*/
function handleScroll( context ) {
…
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the heads up! I've converted these all to JSDoc comments.
// because the scroll event can't be canceled, so we reset the position instead. | ||
window.scrollTo( | ||
context.core.image.lastScrollLeft, | ||
context.core.image.lastScrollTop |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess these are already defined and used, but they feel more like context.core.image.initialScrollLeft
and initialScrollTop
to me since they aren't updated; since they are resetting the scroll to the position it was in when initially opening the lightbox.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it! While initialScrollTop
and initialScrollLeft
may be better, they still don't ring true to me, so I've renamed them to scrollTopReset
and scrollLeftReset
, which I think more clearly expresses the intent.
// animation is defined in the styles.scss and depends on if the | ||
// animation is 'zoom' or 'fade', but in any case we should wait | ||
// a few milliseconds longer than the duration, otherwise a user | ||
// may scroll too soon and cause the animation to look sloppy. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a practical way to cancel the animation upon scroll without causing the page to shift? sorry if someone has already asked about this; it seems like another approach to avoid jitter in the page without actively preventing someone from doing what they're trying to do. asking because it can be irritating on a website when you try to scroll but have to wait for a prolonged period for some animation to complete.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pardon, the comment here was out of date. The lightbox no longer closes on scroll; users needs to click on the overlay or close button to close it.
That being said, I was testing, and we could just get rid of the delay. It's less than half a second though, and if we did get rid of it, the lightbox in that case may look sloppy, especially to less technically inclined users.
While we could add robust handling and logic to optimize the UX for a canceled zoom animation, I'm not sure that would be worth the time invested. I feel it requires a lot of effort to close the lightbox then immediately scroll and beat the 450 millisecond delay, after which scroll input is recognized immediately anyway.
The robust handling could be added in the future, though I think that can be a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested on a physical mobile device thanks to the Gutenberg PR Previewer
Looks great to me. Thanks Artemio! 👍
@dmsnell Thanks for taking a look!
I looked further into this and we actually need to use both listeners, on mobile and desktop, the reason being that many devices support both touch and mouse input. The Surface Pro laptop is one example; one can even use a mouse with an iPad or an iPhone. In these cases, we'll want to have the scroll callback, but still recognize the moment when touch input starts so that we can override the scroll reset and allow pinch to zoom to work as expected. |
* Replace overflow: hidden with JavaScript callback to prevent scrolling * Disable scroll callback on mobile; add comments; fix scrim styles The page jumps around when trying to override the scroll behavior on mobile, so I disabled it altogether. I've also added comments to clarify this and other decisions made around the scroll handling. While testing, I realized that the scrim was completely opaque during the zoom animation, which does not match the design, so I added a new animation specifically for the scrim to fix that. * Add handling for horizontally oriented pages * Move close button so that it's further from scrollbar on desktop * Fix call to handleScroll() and add touch callback to new render method * Improve lightbox experience on mobile To ensure pinch to zoom works as expected when the lightbox is open on mobile, I added logic to disable the scroll override when touch is detected. Without this, the scroll override kicks in whenever one tries to pinch to zoom, and it breaks the experience. I also revised the styles for the scrim to make it opaque, as having content visible outside of the lightbox is distracting when pinching to zoom on a mobile device, and I think having a consistent lightbox across devices will make for the best user experience. * Fix spacing * Add touch directives to block supports * Fix spacing in block supports * Rename attribute for clarity * Update comment * Update comments * Fix spacing --------- Co-authored-by: Ricardo Artemio Morales <[email protected]>
I just cherry-picked this PR to the commits-for-6.4-beta2 branch to get it included in the next release: 0d63803 |
* Fix the install of system fonts from a font collection (#54713) * Fix set upload dir test (#54762) * Site Editor: Prevent unintended actions on the classic theme (#54422) * Add action and selector to track access to Patterns page * Add a URL parameter to check if the Patterns page was accessed directly * Revert lib changes * Fix critical error in the Post Editor * Explicitly add `! isBlockBasedTheme` with `isTemplatePartsMode` * Fix critical error in the Post Editor * Patterns: Fix back navigation after pattern creation (#54852) * Patterns: Fix category control width in site editor (#54853) * Patterns: Allow non-user patterns under Standard filter (#54756) * Performance Tests: Separate page setup from test (#53808) # Conflicts: # test/performance/specs/post-editor.spec.js * Performance Tests: Support legacy selector for expanded elements (#54690) * Paragraph: Make 'aria-label' consistent with other blocks (#54687) * Paragraph: Make 'aria-label' consistent with other blocks * Update tests * Try using BC labels in performance tests * Move lightbox render function to filter (#54670) * syntax refactor repace strpos with str_contains (#54832) * Font Library: avoid deprected error in test (#54802) * fix deprecated call * removing unwanted line * Fix the ShortcutProvider usage (#54851) Co-authored-by: Marin Atanasov <[email protected]> * Image: Ensure `false` values are preserved in memory when defined in `theme.json` (#54639) * Modify conditional when parsing config * Only drop the value if it's undefined. --------- Co-authored-by: Michal Czaplinski <[email protected]> * Use "Not synced" in place of "Standard" nomenclature for patterns (#54839) * Standard -> Not synced * Fix broken test --------- Co-authored-by: Glen Davies <[email protected]> * Format Library: Try to fix highlight popover jumping (#54736) * Move mime-type collection generation to a function that can be tested… (#54844) * Move mime-type collection generation to a function that can be tested. Refactored to use that function. * linting changes * Add unit tests to mime type getter * Fixed linting errors * test the entire output array and replace assertTrue by assertEquals * fixing docs --------- Co-authored-by: Matias Benedetto <[email protected]> * Ensure lightbox toggle is shown if block-level setting exists (#54878) * Block Editor: Update strings in blocks 'RenameModal' component (#54887) * Footnotes: Add aria-label to return links (#54843) * Add aria-label to footnotes front-end links. * Add visual output. Fix PHPCS errors. * Remove visual changes, fix in follow-up. * Font Library: Changed the OTF mime type expected value to be what PHP returns (#54886) * Changed the OTF mime type expected value to be what PHP returns * add unit test for otf file installation --------- Co-authored-by: madhusudhand <[email protected]> * Image: Fix layout shift when lightbox is opened and closed (#53026) * Replace overflow: hidden with JavaScript callback to prevent scrolling * Disable scroll callback on mobile; add comments; fix scrim styles The page jumps around when trying to override the scroll behavior on mobile, so I disabled it altogether. I've also added comments to clarify this and other decisions made around the scroll handling. While testing, I realized that the scrim was completely opaque during the zoom animation, which does not match the design, so I added a new animation specifically for the scrim to fix that. * Add handling for horizontally oriented pages * Move close button so that it's further from scrollbar on desktop * Fix call to handleScroll() and add touch callback to new render method * Improve lightbox experience on mobile To ensure pinch to zoom works as expected when the lightbox is open on mobile, I added logic to disable the scroll override when touch is detected. Without this, the scroll override kicks in whenever one tries to pinch to zoom, and it breaks the experience. I also revised the styles for the scrim to make it opaque, as having content visible outside of the lightbox is distracting when pinching to zoom on a mobile device, and I think having a consistent lightbox across devices will make for the best user experience. * Fix spacing * Add touch directives to block supports * Fix spacing in block supports * Rename attribute for clarity * Update comment * Update comments * Fix spacing --------- Co-authored-by: Ricardo Artemio Morales <[email protected]> * Font Library: move font uploads to a new tab (#54655) * move font uploads to a new tab in the modal * fix invalid success message and revert the dropzone to modal * add success notice for font uploads * make supported file formats message dynamic based on allowed extensions * update hint text and clear successful message with a fresh upload * Block custom CSS: Fix incorrect CSS when multiple root selectors (#53602) * Block custom CSS: Fix incorrect CSS when multiple root selectors * Fix PHP lint error * Use `scope_selector` and `append_to_selector` method and update unit test * Use `scopeSelector` and `appendToSelector` function and update JS unit test * Update packages/block-editor/src/components/global-styles/test/use-global-styles-output.js Co-authored-by: Aaron Robertshaw <[email protected]> * Update packages/block-editor/src/components/global-styles/test/use-global-styles-output.js Co-authored-by: Aaron Robertshaw <[email protected]> * Update packages/block-editor/src/components/global-styles/test/use-global-styles-output.js Co-authored-by: Aaron Robertshaw <[email protected]> * Update packages/block-editor/src/components/global-styles/test/use-global-styles-output.js Co-authored-by: Aaron Robertshaw <[email protected]> * Update packages/block-editor/src/components/global-styles/utils.js Co-authored-by: Aaron Robertshaw <[email protected]> * re-trigger CI --------- Co-authored-by: Aaron Robertshaw <[email protected]> * Add new e2e test for creating a pattern (#54855) * Use list role instead of listbox for patterns (#54884) * Popover: Fix the styles for components that use emotion within popovers (#54912) # Conflicts: # packages/components/CHANGELOG.md * Footnotes: use core’s meta revisioning if available (#52988) # Conflicts: # packages/block-library/src/footnotes/index.php * Remove base url from link control search results (#54553) * Expose baseURL setting on Post and Site Editors via block settings * Strip baseURL from rendered search results * Only fetch baseURL once in top level component * Simplify implementation to utilise URL parse functions * Improve comment wording to avoid referencing undefined var * Remove superfluous conditional * Decode URL prior to operations * Refactor for readability * Fix where url is not defined * Revert change to filter util * Ensure that filterURLForDisplay always receives a string as an arg * Make e2e test locator less strict * Prefer pipe * Force remove trailing slash * [Site Editor]: Update copy of using the default template in a page (#54728) * Remove overflow: hidden from the entity title h1 in the site editor sidebar (#54769) * Site Editor: Avoid same key warnings in template parts area listings (#54863) * TemplateAreas use template part clientId as key * HomeTemplateDetails use template part clientId as key * Cleanup useSelect hook * Fix ToolSelector popover variant (#54840) * Font Library: refactor endpoint permissions (#54829) * break the checking of user permission and file write permissions * return error 500 if the request to the install fonts endpoint needs write permissions and wordpress doens't have write permission on the server * do not ask file write permission on uninstall endpoint * Standardize the output of install and uninstall fonts endpoints Co-authored-by: Jason Crist <[email protected]> Co-authored-by: Jeff Ong <[email protected]> * Handle standardized output from install and uninstall endpoints in the frontend Co-authored-by: Jason Crist <[email protected]> Co-authored-by: Jeff Ong <[email protected]> * Update the install and unintall fonts endpoints unit tests for the new standardized output format Co-authored-by: Jason Crist <[email protected]> Co-authored-by: Jeff Ong <[email protected]> * fix the refersh call for the library Co-authored-by: Jason Crist <[email protected]> Co-authored-by: Jeff Ong <[email protected]> --------- Co-authored-by: Jason Crist <[email protected]> Co-authored-by: Jeff Ong <[email protected]> * Don’t use TypeScript files in scripts package (#54856) * Fix Search Block not updating in Nav block (#54823) * Avoid setState in render * Attempt at test coverage * Improve tests and make them work * Remove word-wrap property (#54866) --------- Co-authored-by: Matias Benedetto <[email protected]> Co-authored-by: Aki Hamano <[email protected]> Co-authored-by: Aaron Robertshaw <[email protected]> Co-authored-by: Bart Kalisz <[email protected]> Co-authored-by: George Mamadashvili <[email protected]> Co-authored-by: Artemio Morales <[email protected]> Co-authored-by: Riad Benguella <[email protected]> Co-authored-by: Marin Atanasov <[email protected]> Co-authored-by: Michal Czaplinski <[email protected]> Co-authored-by: Rich Tabor <[email protected]> Co-authored-by: Glen Davies <[email protected]> Co-authored-by: Jason Crist <[email protected]> Co-authored-by: Alex Stine <[email protected]> Co-authored-by: madhusudhand <[email protected]> Co-authored-by: Carlos Bravo <[email protected]> Co-authored-by: Ricardo Artemio Morales <[email protected]> Co-authored-by: Kai Hao <[email protected]> Co-authored-by: Adam Silverstein <[email protected]> Co-authored-by: Dave Smith <[email protected]> Co-authored-by: Nik Tsekouras <[email protected]> Co-authored-by: Ramon <[email protected]> Co-authored-by: Jason Crist <[email protected]> Co-authored-by: Jeff Ong <[email protected]> Co-authored-by: Pascal Birchler <[email protected]>
✅ I updated the PHP Sync Tracking Issue for WP 6.5 to note this PR does not require a backport for WP 6.5 as it seems to have been cherry picked into WP 6.4. |
What?
Currently, opening and closing the lightbox causes the website layout to shift, which can cause the zoom animation to appear broken, particularly on mobile. It is also possible to scroll the webpage while the lightbox is open by directly manipulating the scrollbar or using a mobile device, which is not in line with web standards.
This PR improves the lightbox implementation by removing the layout shift and updating the scroll handling so that the lightbox behaves reliably whenever a scroll is performed.
Why?
We want to offer a polished lightbox experience, and the current experience appears broken in some cases.
How?
This PR removes
overflow: hidden
from the styles and updates the callback for the scroll handling.Note: Expected behavior is that scrolling will be disabled on both desktop and mobile.
Testing Instructions
up
anddown
arrow keys, thePageUp
andPageDown
keys, and by directly clicking and dragging the scrollbar — verify that the lightbox stays open and doesn't scroll when performing any of those actions.Screenshare
Layout Shift Demonstration
Before the Fix
Lightbox.layout.shift.-.broken.mp4
After the Fix
Lightbox.layout.shift.-.fixed.mp4