@@ -2,6 +2,12 @@ import type {Rect, Strategy} from '@floating-ui/core';
22import { getWindow , isWebKit } from '@floating-ui/utils/dom' ;
33
44import { getDocumentElement } from '../platform/getDocumentElement' ;
5+ import { getWindowScrollBarX } from './getWindowScrollBarX' ;
6+
7+ // Safety check: ensure the scrollbar space is reasonable in case this
8+ // calculation is affected by unusual styles.
9+ // Most scrollbars leave 15-18px of space.
10+ const SCROLLBAR_MAX = 25 ;
511
612export function getViewportRect ( element : Element , strategy : Strategy ) : Rect {
713 const win = getWindow ( element ) ;
@@ -25,6 +31,32 @@ export function getViewportRect(element: Element, strategy: Strategy): Rect {
2531 }
2632 }
2733
34+ const windowScrollbarX = getWindowScrollBarX ( html ) ;
35+ // <html> `overflow: hidden` + `scrollbar-gutter: stable` reduces the
36+ // visual width of the <html> but this is not considered in the size
37+ // of `html.clientWidth`.
38+ if ( windowScrollbarX <= 0 ) {
39+ const doc = html . ownerDocument ;
40+ const body = doc . body ;
41+ const bodyStyles = getComputedStyle ( body ) ;
42+ const bodyMarginInline =
43+ doc . compatMode === 'CSS1Compat'
44+ ? parseFloat ( bodyStyles . marginLeft ) +
45+ parseFloat ( bodyStyles . marginRight ) || 0
46+ : 0 ;
47+ const clippingStableScrollbarWidth = Math . abs (
48+ html . clientWidth - body . clientWidth - bodyMarginInline ,
49+ ) ;
50+
51+ if ( clippingStableScrollbarWidth <= SCROLLBAR_MAX ) {
52+ width -= clippingStableScrollbarWidth ;
53+ }
54+ } else if ( windowScrollbarX <= SCROLLBAR_MAX ) {
55+ // If the <body> scrollbar is on the left, the width needs to be extended
56+ // by the scrollbar amount so there isn't extra space on the right.
57+ width += windowScrollbarX ;
58+ }
59+
2860 return {
2961 width,
3062 height,
0 commit comments