Avoiding Layout Shifts Caused by Disapearing Scrollbars
![](/posts/avoiding-layout-shifts-caused-by-disapearing-scrollbars/banner.jpg)
Here is the situation: You need to prevent scrolling on you main content when a certain element becomes visible. Probably a modal1 or a sliding burger menu.
You easily achieve this by setting overflow:hidden;
on the element which must be temporarily
unscrollable, probably the <body>
.
But now you have an unpleasant visual effect, demonstrated in the CodePen below:
As you can see*, the content behind the modal is shifting.
This happens because when we set overflow:hidden
on the <body>
,
the scrollbar disapears and the content adjusts to a new width.
(A new document.body.clientWidth
, to be exact.)
*If you cannot see it, scrollbars must be automatically hidden by your OS. The setting looks like this on Mac and must be equally to find easy on Linux and Windows.
![Screenshot of "Show scroll bars" setting on MacOS, within System Preferences](/posts/avoiding-layout-shifts-caused-by-disapearing-scrollbars/preferences.png)
Prevent content shifting by replacing the scrollbar with padding
Simply add padding as wide as the scrollbar before it disapears and remove that padding when the modal gets closed. 🪄
There is no such thing as document.getScrollbarWidth
and scrollbars are not all
created equal; 15px wide on Chrome, 12px on Edge, 0px on my phone…
But you can get the scrollbar width easily with this single line of JS:
// For a live demo, copy and paste this in the console
window.innerWidth - document.body.clientWidth
With that precious information, all that’s left to do is set a padding on the body.
// on open
document.body.style.paddingRight = `${
window.innerWidth - document.body.clientWidth
}px`
document.body.style.overflow = 'hidden'
// on close
document.body.style.paddingRight = '0'
document.body.style.overflow = 'unset'
And our modal can now open gracefully, without any nonsense in the background:
Don’t bother looking at the HTML and CSS of these CodePens, the magic happens in the
openModal
and closeModal
functions.
“modal”, “dialog”, “popup”, “popover”… I’ll stick to “modal” here. ↩︎