Dialog add-padding-to-html technique for scrollbar is problematic and there's no way to control it
See original GitHub issuePackage: @headlessui/react Version: v1.5.0 (latest at time of writing) Chrome + Windows 11 (this issue affects many others as well; see description + case)
Reproduction URL
Any example of the Dialog component is a valid reproduction: open DevTools and monitor the HTML element while opening a Dialog.
Refer to the implementation in dialog.tsx
where the issue behaviour is implemented in a useEffect()
preceded by the comment line // Scroll lock
:
Issue Description
The current Dialog implementation makes less-than-ideal assumptions re scrollbars + project CSS and when a Dialog/modal is open, it applies a technique that adds an inline overflow: hidden
and padding-right
(with value based on a calculation of scrollbar width) to the document’s <html>
tag.
This results in undesired behaviour / jank in different cases especially on Windows PC’s where scrollbars are rather visually imposing.
The lack of control for this behaviour (especially the padding) leaves devs without the ability to more elegantly handle scrollbars and/or handle corner cases or conflicts caused by other styles in a given project.
Why is this important?
Scrollbars are a notoriously fickle beast between different OS’s and browsers (see: one example among many articles on the subject).
I would argue a good reason to check/switch between Mac + PC when working on front-end projects are the differences in scrollbars between these OS’s. Our traffic doesn’t always have the same taste in computers vs. many of us web devs 😄
Example case:
The following breaks down a single case that is part of a wider issue; refer to linked issue + discussion for related cases.
A straightforward cross-platform-friendly technique to avoid jank during loading + transitions that is particularly gross on Windows is to add overflow-y: scroll
to the body
tag. This causes browsers on PC’s to always display a scrollbar even when the content fits within the viewport (in this case the scrollbar is present but is presented as disabled/inactive).
This technique is in the tailwind-preset.js
on several projects I work on and is compatible with many packages/libraries/frameworks to help avoid “jumps” and overall “jank”.
However in this case Dialog is the cause of the very problem it is attempting to prevent:
Content behind the Dialog noticeably “jumps” to the left by the width of the scrollbar.
In the screenshot we see
padding-right
jumping the content by the width of the scrollbar in both of the following cases: when the content is taller than the viewport (i.e. case for y scrollbar), and when it is shorter (i.e. nothing to scroll).
Confirming:
- when
overflow-y: scroll
is removed from thebody
then Dialog behaves the way that the devs presumably intended (tested on Chrome+PC): no padding or jump is present in the content-fits-window case (no scrollbar visible), and in the content-taller-than-window case (scrollbar visible) the added padding compensates for the scrollbar disappearance resulting from Dialog’s addition ofoverflow: hidden
.
The presumably-intended behaviour isn’t particularly elegant in the content-taller-than-window-case on PC either:
Windows’ imposing scrollbar suddenly disappears and this can result in users’ “wtf just blinked”/“jank detection” neurons firing. While a content “jump” is prevented the sudden replacement of the scrollbar with a solid vertical block that doesn’t match the app layout isn’t much better.
At present, devs have no way to control any of this behaviour if they wish to use Dialog –
Before Dialog is rendered (imposing Windows scrollbar is visible):
With Dialog modal showing (scrollbar suddenly disappears and it ain’t pretty):
Expected/Desired Behaviour
Developers require the ability to control the overflow+padding behaviour of Headless Dialog (e.g. exposed via props)
- Dialog is making an assumption that depends on other CSS in the project being a certain way that may not always hold true
- Devs may disagree with an opinionated technique (e.g. jump avoided but jarring swap of scrollbar may not be desired) and wish to override, but still leverage all the other benefits of Headless + Dialog.
Depending on maintainers’ perspective re the presumably-intended behaviour + based on screenshots of it on PC they may wish to classify it as a bug and revisit.
- Different technique(s) or additional case handling (e.g. scrollbar, PC, etc.) could be implemented to improve Dialog in this area
Related Ideas & Discussions:
Issue Analytics
- State:
- Created a year ago
- Reactions:2
- Comments:10
Same here, just not having a control over Dialog’s addition of
overflow: hidden
. This is what is the culprit. Suggesting to solve this and related issues by giving devs a choice to modify this behavior (leaving default as is).you can fix it by adding to your main stylesheet file the following : html{ overflow-y: auto!important; }