UX: Improve sidebar positioning in Safari (#30888)

- Calculate the overscroll amount and subtract it from viewport-based
measurements

- Only round at the end of calculations, to avoid sub-pixel rounding
discrepancies

- Compensate for < 1px fluctuations which happen during scroll

Before: https://github.com/user-attachments/assets/a1044405-9f4a-46b1-a3b1-bc5fff29bf45

After: https://github.com/user-attachments/assets/212e4a32-aa97-4054-aba0-b7f1d993f007
This commit is contained in:
David Taylor 2025-01-21 09:43:30 +00:00 committed by GitHub
parent 4a7b98ef6d
commit 4933cfd46c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -90,16 +90,28 @@ export default class GlimmerSiteHeader extends Component {
return;
}
// clamping to 0 to prevent negative values (hello, Safari)
const headerWrapBottom = Math.max(
// We expect this to be zero, but when overscrolling in Safari it can have a non-zero value:
const overscrollPx = Math.max(
0,
Math.floor(this._headerWrap.getBoundingClientRect().bottom)
document.documentElement.getBoundingClientRect().top
);
const headerWrapTop =
this._headerWrap.getBoundingClientRect().top - overscrollPx;
let headerWrapBottom =
this._headerWrap.getBoundingClientRect().bottom - overscrollPx;
// While scrolling on iOS, an element fixed to the top of the screen can have a `top` which fluctuates
// between -1 and 1. To avoid that fluctuation affecting the header offset, we subtract that tiny fluctuation from the bottom value.
if (Math.abs(headerWrapTop) < 1) {
headerWrapBottom -= headerWrapTop;
}
let mainOutletOffsetTop = Math.max(
0,
Math.floor(this._mainOutletWrapper.getBoundingClientRect().top) -
headerWrapBottom
this._mainOutletWrapper.getBoundingClientRect().top -
headerWrapBottom -
overscrollPx
);
if (DEBUG && isTesting()) {
@ -111,16 +123,19 @@ export default class GlimmerSiteHeader extends Component {
}
const docStyle = document.documentElement.style;
const currentHeaderOffset =
parseInt(docStyle.getPropertyValue("--header-offset"), 10) || 0;
const newHeaderOffset = headerWrapBottom;
const newHeaderOffset = Math.floor(headerWrapBottom);
if (currentHeaderOffset !== newHeaderOffset) {
docStyle.setProperty("--header-offset", `${newHeaderOffset}px`);
}
const currentMainOutletOffset =
parseInt(docStyle.getPropertyValue("--main-outlet-offset"), 10) || 0;
const newMainOutletOffset = headerWrapBottom + mainOutletOffsetTop;
const newMainOutletOffset = Math.floor(
headerWrapBottom + mainOutletOffsetTop
);
if (currentMainOutletOffset !== newMainOutletOffset) {
docStyle.setProperty("--main-outlet-offset", `${newMainOutletOffset}px`);
}