-
-
Notifications
You must be signed in to change notification settings - Fork 46
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
[useScrollLock] Avoid scrollbar layout shift issues #604
Conversation
Netlify deploy preview |
Problem 1: floating-ui/floating-ui#3045 |
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.
However, this raises the concern of interoperability with extensions and third party widgets that use outdated versions of Floating UI, Popper (assuming it's broken there) or custom positioning methods that don't handle this edge case (where html has an offset).
I think this is fine given that Base UI will take some time to get real traction, and a newer Floating UI version will be around during this time. Given that Floating UI is a dependency, I think we should recommend to use a matching Floating UI version in the docs either way.
The forced overflow of the scrollbar appears on top of backdrops, which looks a bit unsightly if it's not a custom scrollbar
I agree this is not ideal. I'd take that over any layout shift though.
ddf3668
to
51fd682
Compare
When the body has an overflow-y: scroll style, layout shift would still occur
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.
Alright, desktop looks fantastic.
Checking iOS now and I can get it to do weird jerky right after zooming in or out... Is there any hope we can nail this?
RPReplay_Final1726222510.mov
Doing another test:
Screen.Recording.2024-09-13.at.15.59.42.mov |
You mean once the scroll lock is activated and using pinch-zoom? I can also reproduce this. I suppose it's a limitation of react-aria's solution then, unfortunately. If you mean scrolling or zooming while it's not activated, I can't reproduce that.
Will have to check that. |
Wow I'm shocked that that's what they have... if you open a dialog zoomed in, your only way to get out of it is basically to close the page. |
I'll try to complete the |
@atomiks To reproduce, you need to empty the page to not have enough content to create a scrollbar: Screen.Recording.2024-09-15.at.01.23.39.movhttps://deploy-preview-604--base-ui.netlify.app/components/react-dialog/ @joshwcomeau Cool hack in https://www.joshwcomeau.com/css/has/#global-detection-6 by the way: but we can't use this here 😄. |
Co-authored-by: Olivier Tassinari <[email protected]> Signed-off-by: atomiks <[email protected]>
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.
Phew, looks great. Added a slight comment on the scrollbar-gutter
edge case, otherwise good to go.
I'm super excited about this, reliable scroll locking without layout shifts was such a pain with Radix and other libs.
For posterity, here's what I was testing across browsers
html
with overflow: scroll
- Scrollable page
- Non-scrollable page
- Reducing viewport size to make the page scrollable
- Expanding viewport size to make the page not scrollable
html
with scrollbar-gutter: stable
- Scrollable page
- Non-scrollable page
- Reducing viewport size to make the page scrollable
- Expanding viewport size to make the page not scrollable
body with overflow: scroll
- Scrollable page
- Non-scrollable page
- Reducing viewport size to make the page scrollable
- Expanding viewport size to make the page not scrollable
Default browser styles on body
and html
- Scrollable page
- Non-scrollable page
- Reducing viewport size to make the page scrollable
- Expanding viewport size to make the page not scrollable
not cleanest feeling UX. I guess the idea is that it's easier for developers not to have to add .mui-fixed like class names? I would feel better like this though:
@oliviertassinari check this out:
Screen.Recording.2024-09-16.at.12.02.34.mov
Screen.Recording.2024-09-16.at.12.05.34.mov
Developers consistently forget about hard scrollbars. Even in the teams of the highest calibre, these often end up being completely unhandled.
I agree that it would be ideal if the backdrop covered the scrollbar area. I am confident though that's not what the end user would be looking at or care about, since their attention is going to be on the part of the page that presents new information. Any risk of having a layout shift is not worth it in my opinion.
With other component libraries, we've been living with the common scrollbar compensation method that requires you to use a CSS variable to set a right
value for position: fixed
elements, and realistically people just forget to do this all the time.
As another example, here is a UI I had built:
Screen.Recording.2024-09-16.at.12.06.29.mov
The header doesn't move because I did handle that, but the input got messed up over time. Maybe it was me, maybe another developer forgot that; it's natural because you don't anticipate any relationship between a random element on the page and an unrelated dialog.
My guiding rule here is that great DX and great UX go hand in hand. Compromises in DX tend to lead to end user issues.
Co-authored-by: Vlad Moroz <[email protected]> Signed-off-by: atomiks <[email protected]>
@vladmoroz My guess it's because everyone on those teams works on macOS, so they don't see it. They forgot that 70% of the end-users are on Windows. And even for developers, it's still a majority https://www.notion.so/mui-org/Product-Analytics-f2e2576203564e6c8dc3429cf305a36f?pvs=4#8a38433d21814a15acfb99ce2eac7d6b.
Yeah, hard dilemma. Your rationale makes sense. Not peak of UX but we avoid the worst-case scenario, and since this is what people tend to remember, it's probably for the best: https://www.notion.so/mui-org/DevEx-DX-044f844fbda74826b2dea32a6e6d8a38?pvs=4#2a825570dd744a5a90b75e014100562e (point made by https://github.com/rsms). I guess in this case, it's critical to have @atomiks |
const html = document.documentElement; | ||
const body = document.body; |
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.
As far as I know, this should only be a fallback. It breaks supports for iframes and popups, we fixed a bunch of those through the years, e.g. mui/material-ui#18701, mui/material-ui#10328. Example of use case: https://www.openfin.co/.
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.
That doesn't apply here since it's locking document level of the current script. There's no element to grab an owner of.
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 no element to grab an owner of.
This is the point I get to, I don't see how this API can work:
useScrollLock(modal && mounted); |
I think we should provide to useScrollLock.ts enough context to know which document it needs to scroll lock. Issue created #629.
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.
iOS looks good too
I think we got to remove the theme_color
from manifest.json
because it's super obnoxious when testing iOS; kind of spoils the impression when it doesn't have to.
Closes #603
Problem 1
This currently breaks the positioning of (anchored) floating elements. Edit: works if using the
fixed
positioning strategy instead ofabsolute
+ fixed in latest Floating UI version.This raises some minor interoperability concerns with extensions and third party widgets that use outdated versions of Floating UI, Popper (assuming it's broken there) or custom positioning methods that don't handle this edge case (where
html
has an offset).Problem 2
The forced overflow of the
<html>
scrollbar appears on top of backdrops, which looks a bit unsightly if it's not a custom scrollbar