-
-
Notifications
You must be signed in to change notification settings - Fork 179
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix Chromium scrollbar-induced layout shifts
This commit addresses an issue in Chromium on Linux and Windows where the appearance of a vertical scrollbar causes unexpected horizontal layout shifts. This behavior typically occurs when the window is resized, a card is opened or a script is selected, resulting in content being pushed to the left. The solution implemented involves using `scrollbar-gutter: stable` to ensure space is always allocated for the scrollbar, thus preventing any shift in the page layout. This fix primarily affects Chromium-based browsers on Linux and Windows. It has no impact on Firefox on any platform, or any browser on macOS (including Chromium). Because these render the scrollbar as an overlay, and do not suffer from this issue. Steps to reproduce the issue using Chromium browser on Linux/Windows: 1. Open the app with a height large enough where a vertical scrollbar is not visible. 2. Resize the window to a height that triggers a vertical scrollbar. 3. Notice the layout shift as the body content moves to the right. Changes: - Add a CSS mixin to handle scrollbar gutter allocation with a fallback. - Add support for modal dialog background lock to handle `scrollbar-gutter: stable;` in calculations to avoid layout shift when a modal is open. - Add E2E test to avoid regression. - Update DevToolkit to accommodate new scrollbar spacing.
- Loading branch information
1 parent
dd71536
commit bc4879c
Showing
12 changed files
with
377 additions
and
180 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
19 changes: 19 additions & 0 deletions
19
src/presentation/assets/styles/base/_prevent-scrollbar-layout-shift.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// This mixin prevents layout shifts caused by the appearance of a vertical scrollbar | ||
// in Chromium-based browsers on Linux and Windows. | ||
// It creates a reserved space for the scrollbar, ensuring content remains stable and does | ||
// not shift horizontally when the scrollbar appears. | ||
@mixin prevent-scrollbar-layout-shift { | ||
scrollbar-gutter: stable; | ||
|
||
@supports not (scrollbar-gutter: stable) { // https://caniuse.com/mdn-css_properties_scrollbar-gutter | ||
// Safari workaround: Shift content to accommodate non-overlay scrollbar. | ||
// An issue: On small screens, the appearance of the scrollbar can shift content, due to limited space for | ||
// both content and scrollbar. | ||
$full-width-including-scrollbar: 100vw; | ||
$full-width-excluding-scrollbar: 100%; | ||
$scrollbar-width: calc($full-width-including-scrollbar - $full-width-excluding-scrollbar); | ||
padding-inline-start: $scrollbar-width; // Allows both right-to-left (RTL) and left-to-right (LTR) text direction support | ||
} | ||
|
||
// More details: https://web.archive.org/web/20240509122237/https://stackoverflow.com/questions/1417934/how-to-prevent-scrollbar-from-repositioning-web-page | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
src/presentation/components/DevToolkit/UseScrollbarGutterWidth.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { | ||
computed, readonly, ref, watch, | ||
} from 'vue'; | ||
import { throttle } from '@/application/Common/Timing/Throttle'; | ||
import { useAutoUnsubscribedEventListener } from '../Shared/Hooks/UseAutoUnsubscribedEventListener'; | ||
|
||
const RESIZE_EVENT_THROTTLE_MS = 200; | ||
|
||
export function useScrollbarGutterWidth() { | ||
const scrollbarWidthInPx = ref(getScrollbarGutterWidth()); | ||
|
||
const { startListening } = useAutoUnsubscribedEventListener(); | ||
startListening(window, 'resize', throttle(() => { | ||
scrollbarWidthInPx.value = getScrollbarGutterWidth(); | ||
}, RESIZE_EVENT_THROTTLE_MS)); | ||
|
||
const bodyWidth = useBodyWidth(); | ||
watch(() => bodyWidth.value, () => { | ||
scrollbarWidthInPx.value = getScrollbarGutterWidth(); | ||
}, { immediate: false }); | ||
|
||
const scrollbarWidthStyle = computed(() => `${scrollbarWidthInPx.value}px`); | ||
return readonly(scrollbarWidthStyle); | ||
} | ||
|
||
function getScrollbarGutterWidth(): number { | ||
return document.documentElement.clientWidth - document.documentElement.offsetWidth; | ||
} | ||
|
||
function useBodyWidth() { | ||
const width = ref(document.body.offsetWidth); | ||
const observer = new ResizeObserver((entries) => throttle(() => { | ||
for (const entry of entries) { | ||
width.value = entry.borderBoxSize[0].inlineSize; | ||
} | ||
}, RESIZE_EVENT_THROTTLE_MS)); | ||
observer.observe(document.body, { box: 'border-box' }); | ||
return readonly(width); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.