Skip to content

Instantly share code, notes, and snippets.

@AshKyd
Last active August 22, 2025 06:47
Show Gist options
  • Save AshKyd/d3428f6c4e6cf2e3467f8ce432382ede to your computer and use it in GitHub Desktop.
Save AshKyd/d3428f6c4e6cf2e3467f8ce432382ede to your computer and use it in GitHub Desktop.
Returns a value that's safe enough to use in iPadOS webview browsers
/**
* Due to assorted bugs in iPadOS webview browsers (Firefox, Brave), `vh` and `lvh`
* units change unpredictably and can cause content to shift.
*
* This utility provides an approximation of `100vh` in the browser. In most
* browsers it will be exactly `100vh`, but in Safari iPad browsers it will
* be clamped at either `100dvh` or `100vh` depending on the state of the browser
* address bar at the time.
*
* Use this to size elements in the page flow in a way that won't trigger
* content to shift when the address bar appears.
*
* @param {function(number, { type: "orientation" | "resize" }): void} onChange -
* A callback function that receives the newly measured height
* (in pixels) whenever the viewport height changes. This will be triggered on
* orientation change and, on non-iPad devices, also on resize events.
*
* @returns {number} The initial measured height (in pixels) representing 100vh.
*
* @see https://github.com/mozilla-mobile/firefox-ios/issues/22607
*/
function getViewportMaxHeightStore(onChange) {
const isClientSide = typeof window !== "undefined";
let previousValue = 0;
if (!isClientSide) {
return 0;
}
const div = document.createElement("div");
div.style = "height:10vh;position:absolute;left:0;top:0;width:1px;pointer-events:none;";
document.body.appendChild(div);
function measure() {
if (!div) {
return 0;
}
const { height } = div.getBoundingClientRect();
return height * 10;
}
screen.orientation.addEventListener("change", () => onChange(measure(), { type: "orientation" }));
const isIpad =
!!navigator.userAgent.match(/Macintosh.*AppleWebKit.*Safari/) && navigator.maxTouchPoints > 1;
const resizeObserver = new ResizeObserver(() => {
// BUG: Safari webviews on iPadOS are the cause of all our headaches. For
// those browsers, never recalculate height except on orientation change.
if (!previousValue || !isIpad) {
onChange(measure(), { type: "resize" });
}
});
resizeObserver.observe(div);
return measure();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment