PageSourceSearch

https://undreamedof.ai/app/styles/viewport-fit.js

js undreamedof.ai collected 2026-10-05 13:10:23 UTC 3,825 bytes, 96 lines download raw bytes

1/*
2 * viewport-fit.js — track the actually-visible viewport on iOS Safari.
3 *
4 * iOS Safari's bottom URL bar (iOS 15+ Tab Bar) is rendered as an overlay
5 * on top of page content in some states. CSS units like 100svh and
6 * env(safe-area-inset-bottom) do not reliably subtract its height, so a
7 * naively-sized layout has its bottom clipped by the URL bar.
8 *
9 * The visualViewport API reports the actually-visible region in real time
10 * as the chrome animates. This script copies visualViewport.height into a
11 * CSS custom property (--visible-vh-px) so CSS can use it.
12 *
13 * Falls back gracefully: if visualViewport is unavailable, CSS uses its
14 * native fallback value (100svh / 100vh).
15 */
16(function () {
17    if (!window.visualViewport) {
18        return;
19    }
20
21    const root = document.documentElement;
22    const wantsDebug = (function () {
23        if (typeof URLSearchParams === "undefined") return false;
24        return new URLSearchParams(window.location.search).get("debugvp") === "1";
25    })();
26    let overlay = null;
27
28    function ensureOverlay () {
29        if (!wantsDebug || overlay || !document.body) return;
30        overlay = document.createElement("div");
31        overlay.style.cssText = [
32            "position: fixed",
33            "top: 0",
34            "left: 0",
35            "right: 0",
36            "background: rgba(255,0,0,0.9)",
37            "color: white",
38            "font: 11px monospace",
39            "padding: 6px",
40            "padding-top: calc(env(safe-area-inset-top, 0px) + 6px)",
41            "z-index: 99999",
42            "pointer-events: none",
43            "white-space: pre"
44        ].join(";");
45        document.body.appendChild(overlay);
46    }
47
48    function update () {
49        const h = window.visualViewport.height;
50        const heightPx = h + "px";
51        root.style.setProperty("--visible-vh-px", heightPx);
52        // Inline style is the most reliable: bypass any CSS cascade issues.
53        if (document.body) {
54            document.body.style.setProperty("min-height", heightPx, "important");
55            document.body.style.setProperty("max-height", heightPx, "important");
56        }
57
58        // When an input is focused, iOS pans the page to "reveal" it — but the
59        // body is already sized to the visible area, so the pan just shoves the
60        // layout upward and leaves the input floating above the keyboard.
61        // Undo the pan (skip when pinch-zoomed: offsets are then user intent).
62        if (window.visualViewport.scale === 1 &&
63            (window.scrollY !== 0 || window.visualViewport.offsetTop !== 0)) {
64            window.scrollTo(0, 0);
65        }
66
67        if (!wantsDebug) return;
68        ensureOverlay();
69        if (!overlay || !document.body) return;
70        const styles = getComputedStyle(document.body);
71        const rect = document.body.getBoundingClientRect();
72        overlay.textContent =
73            "innerH: " + window.innerHeight + "\n" +
74            "vvH:    " + h.toFixed(1) + " (offsetTop:" + window.visualViewport.offsetTop.toFixed(1) + ")\n" +
75            "bodyH:  " + rect.height.toFixed(1) + "\n" +
76            "minH:   " + styles.minHeight + "\n" +
77            "maxH:   " + styles.maxHeight + "\n" +
78            "pT/pB:  " + styles.paddingTop + " / " + styles.paddingBottom + "\n" +
79            "--vh:   " + getComputedStyle(root).getPropertyValue("--visible-vh-px");
80    }
81
82    function init () {
83        ensureOverlay();
84        update();
85    }
86
87    if (document.readyState === "loading") {
88        document.addEventListener("DOMContentLoaded", init);
89    } else {
90        init();
91    }
92    window.visualViewport.addEventListener("resize", update);
93    window.visualViewport.addEventListener("scroll", update);
94    window.addEventListener("orientationchange", update);
95    window.addEventListener("resize", update);
96})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.