├── LICENSE.md ├── README.md ├── demos ├── .gitignore ├── .prettierrc ├── README.md ├── netlify.toml ├── package-lock.json ├── package.json └── src │ ├── base.css │ ├── combined │ ├── icb-fixed-visual │ │ └── index.html │ └── viewport-units │ │ └── index.html │ ├── core.js │ ├── index.html │ ├── individual │ ├── icb │ │ ├── fixedmeta.html │ │ ├── index.html │ │ ├── nometa.html │ │ └── viewport-fit.html │ ├── layout-viewport │ │ └── index.html │ ├── pwa │ │ ├── contain.html │ │ ├── cover.html │ │ ├── index.html │ │ ├── manifest-contain.json │ │ └── manifest-cover.json │ ├── safe-inset-areas │ │ ├── index.html │ │ ├── viewport-fit-auto.html │ │ ├── viewport-fit-contain.html │ │ └── viewport-fit-cover.html │ ├── virtual-keyboard │ │ └── index.html │ └── visual-viewport │ │ └── index.html │ └── scrollbar-gutter │ ├── long-both-edges │ └── index.html │ ├── long-stable │ └── index.html │ ├── scrollbar-gutter-bars.css │ ├── scrollbar-gutter-bars.js │ ├── short-both-edges │ └── index.html │ └── short-stable │ └── index.html └── explainers ├── README.md ├── icb.md ├── illustrations ├── icb+layout-viewport.png ├── icb-desktop-content-long--classic-scrollbar.png ├── icb-desktop-content-long.png ├── icb-desktop-content-short.png ├── icb-mobile--uaui-expanded.png ├── icb-mobile--uaui-retracted.png ├── layout-viewport-desktop-content-long--classic-scrollbar.png ├── layout-viewport-desktop-content-long--scrolled.png ├── layout-viewport-desktop-content-long.png ├── layout-viewport-desktop-content-short.png ├── layout-viewport-desktop.png ├── layout-viewport-mobile--uaui-expanded.png ├── layout-viewport-mobile--uaui-retracted.png ├── uaui-expanded.png ├── uaui-retracted.png ├── viewport-units-desktop-long-content--classic-scrollbar.png ├── viewport-units-desktop-long-content.png ├── viewport-units-desktop.png ├── viewport-units-mobile-dvh--mobilesafari.png ├── viewport-units-mobile-naming-things.png ├── viewport-units-mobile-svh+lvh--with-icb--mobilesafari.png ├── viewport-units-mobile-svh+lvh--with-icb--uaui-expanded.png ├── viewport-units-mobile-svh+lvh--with-icb--uaui-retracted.png ├── viewport-units-mobile-vh--uaui-expanded.png ├── viewport-units-mobile-vh--uaui-retracted.png ├── virtual-keyboard-api-overlayscontent.png ├── virtual-keyboard-api-three-behaviors.png ├── virtual-keyboard-input-focus--with-everything---offset-layout-viewport.png ├── virtual-keyboard-input-focus--with-everything.png ├── virtual-keyboard-input-focus--with-icb--with-viewport-units.png ├── virtual-keyboard-input-focus--with-icb.png ├── virtual-keyboard-input-focus--with-layout-viewport-1.png ├── virtual-keyboard-input-focus--with-layout-viewport-2.png ├── virtual-keyboard-input-focus.png ├── virtual-keyboard-input-nofocus--with-icb.png ├── virtual-keyboard-input-nofocus--with-layout-viewport.png ├── virtual-keyboard-input-nofocus.png ├── visual-viewport-desktop--pinch-zoomed-in-1.png ├── visual-viewport-desktop--pinch-zoomed-in-2.png ├── visual-viewport-desktop--pinch-zoomed-in-alt-1.png ├── visual-viewport-desktop--pinch-zoomed-in-alt-2.png ├── visual-viewport-desktop.png ├── visual-viewport-mobile--pinch-zoomed-in.png ├── visual-viewport-mobile--uaui-expanded.png └── visual-viewport-mobile--uaui-retracted.png ├── layout-viewport.md ├── screenshots ├── visual-viewport-mobile-safari-over-pinch-zoom-out-1.jpeg └── visual-viewport-mobile-safari-over-pinch-zoom-out-2.jpeg ├── scrolling.md ├── sizing.md ├── videos ├── visual-viewport-desktop-overscroll--chrome-with-flag.mp4 ├── visual-viewport-desktop-overscroll--chrome.mp4 ├── visual-viewport-desktop-overscroll--firefox.mp4 ├── visual-viewport-desktop-overscroll--safari--different-behaviors.mp4 ├── visual-viewport-desktop-overscroll--safari.mp4 ├── visual-viewport-mobile-over-pinch-zoom-out--safari--different-behaviors.mp4 ├── visual-viewport-mobile-over-pinch-zoom-out--safari.mp4 ├── visual-viewport-mobile-overscroll--safari--different-behaviors.mp4 └── visual-viewport-mobile-overscroll--safari.mp4 ├── viewport-units.md ├── virtual-keyboard-api.md ├── virtual-keyboard.md └── visual-viewport.md /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The 3-Clause BSD License 2 | 3 | Copyright © web-platform-tests contributors 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Interop 2022: Viewport 2 | 3 | Work on the [viewport investigation project](https://github.com/web-platform-tests/interop-2022/issues/41) for Interop 2022. 4 | 5 | > To sort out any confusion about what “viewport” means, and what browsers _(especially on mobile devices)_ should be doing with various viewport measurements & sizing. To make recommendations for what should change to improve interoperability. 6 | > 7 | > - Open any issues at CSSWG, WHATWG, wherever if we find underspecified standards on what these measurements should be in relationship to the viewport — so that the appropriate standards group can take it from there & define any missing details. 8 | > - Open any issues/bugs with the appropriate browsers, noting places where they are not aligned with the standard. Or if the standard is unclear, where they are not aligned with each other. 9 | 10 | ## Definitions and Findings 11 | 12 | In this effort we are looking at various viewport-related aspects. Their definitions and initial findings are collected in the [./explainers](./explainers) folder: 13 | 14 | - [Layout Viewport](./explainers/layout-viewport.md) 15 | - [Initial Containing Block (ICB)](./explainers/icb.md) 16 | - [Visual Viewport](./explainers/visual-viewport.md) 17 | - [Virtual Keyboard](./explainers/virtual-keyboard.md) 18 | - [Virtual Keyboard API](./explainers/virtual-keyboard-api.md) 19 | - [Viewport Units](./explainers/viewport-units.md) 20 | 21 | ## Demos 22 | 23 | There are a bunch of demos available to visualize, debug, and test the behavior of certain viewport-related aspects in browsers. 24 | 25 | These demos are published at [https://interop-2022-viewport.netlify.app/](https://interop-2022-viewport.netlify.app/) 26 | The results of some tests ran against these demos can be reviewed at [https://goo.gle/interop-2022-viewport-testresults](https://goo.gle/interop-2022-viewport-testresults) 27 | 28 | Included tests: 29 | 30 | - [Initial Containing Block (ICB)](https://interop-2022-viewport.netlify.app/individual/icb/) 31 | - [Layout Viewport](https://interop-2022-viewport.netlify.app/individual/layout-viewport/) 32 | - [Visual Viewport](https://interop-2022-viewport.netlify.app/individual/visual-viewport/) 33 | - [Virtual Keyboard API](https://interop-2022-viewport.netlify.app/individual/virtual-keyboard/) 34 | - [Viewport Units + window.innerHeight](https://interop-2022-viewport.netlify.app/combined/viewport-units/) 35 | - [Viewport Units + -webkit-fill-available](https://devinrousso.com/demo/WebKit/css/viewport-units.html) _(external)_ 36 | - [ICB + Fixed Viewport + Visual Viewport + Event Debugger](https://interop-2022-viewport.netlify.app/combined/icb-fixed-visual/) 37 | 38 | The source of the non-external demos is [included in this repository’s `demos` folder](./demos). 39 | 40 | ## Recommendations 41 | 42 | - TODO -------------------------------------------------------------------------------- /demos/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .netlify -------------------------------------------------------------------------------- /demos/.prettierrc: -------------------------------------------------------------------------------- 1 | semi: true 2 | singleQuote: true 3 | trailingComma: "es5" 4 | useTabs: true 5 | printWidth: 9999 6 | overrides: 7 | - files: "*.json" 8 | options: 9 | useTabs: false 10 | - files: "*.css" 11 | options: 12 | singleQuote: false 13 | -------------------------------------------------------------------------------- /demos/README.md: -------------------------------------------------------------------------------- 1 | # Interop 2022: Viewport: Demos 2 | 3 | A collection of demos that allow you to debug and test the behavior of certain viewport and viewport-related aspects, part of the Interop 2022 Viewport Investigation Effort 4 | 5 | - View online: https://interop-2022-viewport.netlify.app/ 6 | - Tested Scenarios + Results: https://goo.gle/interop-2022-viewport-testresults 7 | 8 | ## Installation 9 | 10 | ``` 11 | npm i 12 | ``` 13 | 14 | ## Running the project 15 | 16 | ``` 17 | npm start 18 | ``` 19 | 20 | A local dev server will be spun up, serving the content of `.src/` at [http://localhost:3000](http://localhost:3000) 21 | -------------------------------------------------------------------------------- /demos/netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "src/" -------------------------------------------------------------------------------- /demos/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "interop-2022-viewport-demos", 3 | "version": "1.0.0", 4 | "description": "A collection of demos that allow you to debug and test the behavior of certain viewport and viewport-related aspects", 5 | "scripts": { 6 | "start": "serve src", 7 | "deploy": "netlify deploy --prod", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [ 11 | "viewports", 12 | "interop2022" 13 | ], 14 | "author": "", 15 | "license": "ISC", 16 | "devDependencies": { 17 | "serve": "^14.0.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /demos/src/base.css: -------------------------------------------------------------------------------- 1 | *, 2 | *::after, 3 | *::before { 4 | box-sizing: border-box; 5 | } 6 | 7 | html, 8 | body { 9 | margin: 0; 10 | padding: 0; 11 | height: 100%; 12 | width: 100%; 13 | } 14 | 15 | /* This code effectively outlines the ICB */ 16 | html { 17 | outline: 10px dashed red; 18 | outline-offset: -10px; 19 | } 20 | 21 | body { 22 | background-color: aliceblue; 23 | background-image: linear-gradient(to right, rgb(0 0 0 / 0.05) 1px, transparent 1px), linear-gradient(to bottom, rgb(0 0 0 / 0.05) 1px, transparent 1px); 24 | background-size: 50px 50px; 25 | height: 300%; /* Add some scroll estate */ 26 | position: relative; 27 | } 28 | /* Element that goes vertically and indicates the start and end of each 100% block */ 29 | .heightdebugger { 30 | position: absolute; 31 | top: 0; 32 | left: calc(50% - 1em); 33 | bottom: 0; 34 | width: 2em; 35 | background: cyan; 36 | background-image: linear-gradient(to bottom, darkmagenta 10px, transparent 1px), linear-gradient(to top, slateblue 10px, transparent 1px); 37 | background-size: 100% calc(100% / 3); 38 | z-index: -1; 39 | } 40 | 41 | /* To prevent auto-zoom on iOS */ 42 | input { 43 | font-size: 16px; 44 | } 45 | 46 | #debug, 47 | #visualviewport, 48 | #layoutviewport { 49 | padding: 1em; 50 | overflow-y: hidden; 51 | pointer-events: none; 52 | } 53 | 54 | [id$="-values"] { 55 | margin-bottom: 1em; 56 | white-space: pre-wrap; 57 | font-family: monospace; 58 | } 59 | 60 | #layoutviewport { 61 | position: fixed; 62 | inset: 0; 63 | border: 5px dotted blue; 64 | z-index: 999; 65 | } 66 | 67 | #debug, 68 | [data-vv-positioning="absolute"] #visualviewport { 69 | position: absolute; 70 | top: var(--vvpt, 0px); 71 | left: var(--vvpl, 0px); 72 | width: var(--vvw, 100%); 73 | height: var(--vvh, 100vh); 74 | font-size: calc(1em / var(--vvz, 1)); 75 | } 76 | 77 | [data-vv-positioning="fixed"] #visualviewport { 78 | position: fixed; 79 | top: var(--vvot, 0px); 80 | left: var(--vvol, 0px); 81 | width: var(--vvw, 100%); 82 | height: var(--vvh, 100v); 83 | right: var(--vvol, 0px) + var(--vvw, 0px); 84 | bottom: var(--vvol, 0px) + var(--vvh, 0px); 85 | } 86 | 87 | #visualviewport { 88 | border: 8px solid; 89 | border-image: repeating-linear-gradient(45deg, orange, orange 10px, transparent 10px, transparent 20px) 10; 90 | } 91 | 92 | #inputwrapper { 93 | position: absolute; 94 | right: 10vw; 95 | top: 20vh; 96 | z-index: 999; 97 | opacity: 0.5; 98 | } 99 | 100 | /* Tweak Lists */ 101 | :not(li) > ul, 102 | :not(li) > ol { 103 | margin-block-start: 1em; 104 | margin-block-end: 1em; 105 | padding-inline-start: 2em; 106 | } 107 | 108 | li { 109 | margin-block-end: 0.2em; 110 | } 111 | 112 | /* The Options Box */ 113 | #options[open] { 114 | padding: 1em; 115 | background-color: white; 116 | border-radius: 1em; 117 | display: flex; 118 | flex-direction: column; 119 | gap: 2rem; 120 | max-width: 80ch; 121 | } 122 | 123 | #options[open]::backdrop { 124 | backdrop-filter: blur(0.5em); 125 | } 126 | 127 | #options header { 128 | display: flex; 129 | flex-direction: row-reverse; 130 | justify-content: space-between; 131 | align-items: center; 132 | } 133 | 134 | #options header > * { 135 | margin: 0; 136 | } 137 | 138 | #options section { 139 | display: flex; 140 | flex-direction: column; 141 | width: 100%; 142 | justify-content: flex-start; 143 | overflow-y: scroll; 144 | } 145 | 146 | #options :is(h1, h2, h3, h4, h5, h6) { 147 | margin: 0; 148 | } 149 | 150 | #options section > p { 151 | margin: 1em 0 0.25em 0; 152 | } 153 | 154 | .option { 155 | display: flex; 156 | align-items: center; 157 | gap: 0.5em; 158 | } 159 | 160 | .option :is(input[type="checkbox"], input[type="radio"]) { 161 | width: 2em; 162 | height: 2em; 163 | } 164 | 165 | :disabled + label { 166 | text-decoration: line-through; 167 | cursor: not-allowed; 168 | user-select: none; 169 | } 170 | 171 | #btnOptions { 172 | position: fixed; 173 | right: 1em; 174 | top: 1em; 175 | } 176 | 177 | .hidden { 178 | display: none !important; 179 | } 180 | -------------------------------------------------------------------------------- /demos/src/combined/icb-fixed-visual/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Visual Viewport Debugger 7 | 8 | 9 | 192 | 193 | 478 | 479 | 480 | 481 |
482 | 483 | 486 |
487 | 488 |

I am root

489 | 490 |
491 |
492 | 517 |
    518 |
    519 |
    520 | 521 |
    .fixed--top
    522 |
    .fixed--bottom
    523 | 524 |
    .fixed--vvtop
    525 |
    .fixed--vvbottom
    526 | 527 |
    .fixed--vvtop.unzoomed
    528 |
    .fixed--vvbottom.unzoomed
    529 | 530 |
    531 |
    532 | 533 | 534 | -------------------------------------------------------------------------------- /demos/src/combined/viewport-units/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Viewport Units Debugger – Interop 2022 Viewport Investigation Effort Demo Pages 7 | 8 | 9 | 190 | 191 | 368 | 369 | 370 | 371 |
    372 | 373 | 374 |
    375 |
    376 |
    377 | OnResize handler calls will be displayed below with window.innerWidth 378 | and documentElement.clientHeight

    379 |
    380 |
    381 |
    382 |
    unit-text position:
    383 |
    384 |
    385 |
    386 | 441 | 442 | 443 | -------------------------------------------------------------------------------- /demos/src/core.js: -------------------------------------------------------------------------------- 1 | const clamp = (val, min, max) => Math.min(Math.max(val, min), max); 2 | export { clamp }; 3 | 4 | const write = (vals, $el, prefix = '') => { 5 | if (!$el) return; 6 | $el.innerText = `${prefix} ${vals ? JSON.stringify(vals, null, 4) : 'N/A'}`.trim(); 7 | }; 8 | export { write }; 9 | 10 | const browserUpdatesScrollPositionsOnOverscroll = () => { 11 | // WebKit based browsers do this. 12 | // @TODO: Move away from UA Sniffing 13 | return CSS.supports('selector(:nth-child(1 of x))'); 14 | }; 15 | 16 | const getVisualViewPortValues = ({ clampOffsets = false, resizeDimensions = false }) => { 17 | let { width, height, widthOrig = null, heightOrig = null, scale, offsetTop, offsetLeft, offsetTopOrig = null, offsetLeftOrig = null, pageLeft, pageTop, pageLeftOrig = null, pageTopOrig = null } = window.visualViewport; 18 | 19 | // Adjust values by clamping 20 | if (clampOffsets) { 21 | // Cache original values 22 | pageTopOrig = pageTop; 23 | pageLeftOrig = pageLeft; 24 | offsetTopOrig = offsetTop; 25 | offsetLeftOrig = offsetLeft; 26 | 27 | // Offsets measured against the page cannot go negative, nor exceed the max scroll offset 28 | // @note: Values below only used for absolute positioning 29 | pageTop = clamp(pageTop, 0, document.body.offsetHeight - height); 30 | pageLeft = clamp(pageLeft, 0, document.body.offsetWidth - width); 31 | 32 | // Offsets measured against the Layout Viewport cannot go negative, nor exceed the max offset within the Layout Viewport 33 | // @note: Values below only used for fixed positioning 34 | const layoutViewportHeight = window.innerHeight * scale; // @TODO: This only is true when window.innerHeight resizes. Which is not always the case when pinch-zooming (or which happens too late as UA UI shows/hides) 35 | const layoutViewportWidth = window.innerWidth * scale; // @TODO: This only is true when window.innerHeight resizes. Which is not always the case when pinch-zooming (or which happens too late as UA UI shows/hides) 36 | offsetTop = clamp(offsetTop, 0, layoutViewportHeight - height); 37 | offsetLeft = clamp(offsetLeft, 0, layoutViewportWidth - width); 38 | } 39 | 40 | // Adjust values by resizing height/width 41 | // Only needed for browsers that expose overscrolling through scrollX/scrollY 42 | if (resizeDimensions) { 43 | widthOrig = width; 44 | heightOrig = height; 45 | pageTopOrig = pageTop; 46 | pageLeftOrig = pageLeft; 47 | offsetTopOrig = offsetTop; 48 | offsetLeftOrig = offsetLeft; 49 | 50 | // Mobile Safari: Fixed Viewport does overscroll. Max value can overshoot 0 (inverse of overScrollY) and min value can exceed inverted max scroll distance 51 | // Desktop Safari: Fixed Viewport does not overscroll. Max value is 0, Min value is inverted max scroll distance. 52 | // We need a way to distinguish between both, because they need a different fix 53 | // @TODO: No longer rely on UA sniffing here, and find a way to detect wether the Fixed Viewport can overscroll or not … 54 | const isMobileSafari = !!window.navigator.userAgent.match(/iPad/i) || !!window.navigator.userAgent.match(/iPhone/i); 55 | const isDesktopSafari = !isMobileSafari; 56 | 57 | // Overscrolling at the right edge 58 | if (width + pageLeft > document.body.offsetWidth) { 59 | width = document.body.offsetWidth - pageLeft; 60 | 61 | // @note: Value below only used for fixed positioning 62 | if (isMobileSafari) { 63 | if (scale == 1) offsetLeft -= widthOrig - width; 64 | if (offsetLeft < 0) offsetLeft = -offsetLeft; 65 | } else { 66 | offsetLeft += widthOrig - width; // Add overscroll value to the offset 67 | } 68 | } 69 | 70 | // Overscrolling at the bottom edge 71 | if (height + pageTop > document.body.offsetHeight) { 72 | height = document.body.offsetHeight - pageTop; 73 | 74 | // @note: Value below only used for fixed positioning 75 | if (isMobileSafari) { 76 | if (scale == 1) offsetTop -= heightOrig - height; 77 | if (offsetTop < 0) offsetTop = -offsetTop; 78 | } else { 79 | offsetTop += heightOrig - height; // Add overscroll value to the offset 80 | } 81 | } 82 | 83 | // Overscrolling at the left edge 84 | // @note: Might be tricky to achieve as you might trigger back navigation 85 | if (pageLeft < 0) { 86 | width += pageLeft; 87 | 88 | // @note: Value below only used for abs position 89 | pageLeft = 0; 90 | 91 | // @note: Value below only used for fixed positioning 92 | offsetLeft = Math.abs(offsetLeft); 93 | } 94 | 95 | // Overscrolling at the top edge 96 | // @note: Might be tricky to achieve as you might trigger pull-to-refresh 97 | if (pageTop < 0) { 98 | height += pageTop; 99 | 100 | // @note: Value below only used for abs positioning 101 | pageTop = 0; 102 | 103 | // @note: Value below only used for fixed positioning 104 | offsetTop = Math.abs(offsetTop); 105 | } 106 | } 107 | 108 | return Object.fromEntries( 109 | Object.entries({ 110 | width, 111 | widthOrig, 112 | height, 113 | heightOrig, 114 | scale, 115 | offsetTop, 116 | offsetTopOrig, 117 | offsetLeft, 118 | offsetLeftOrig, 119 | pageLeft, 120 | pageLeftOrig, 121 | pageTop, 122 | pageTopOrig, 123 | widthXscale: Math.round(width * scale), 124 | heightXscale: Math.round(height * scale), 125 | }).filter(([k, v]) => v !== null) 126 | ); 127 | }; 128 | export { getVisualViewPortValues }; 129 | 130 | const syncVisualViewportValuesToCustomProperties = (vvv, $el = document.documentElement) => { 131 | $el.style.setProperty('--vvw', `${vvv.width}px`); 132 | $el.style.setProperty('--vvh', `${vvv.height}px`); 133 | $el.style.setProperty('--vvpt', `${vvv.pageTop}px`); 134 | $el.style.setProperty('--vvpl', `${vvv.pageLeft}px`); 135 | $el.style.setProperty('--vvot', `${vvv.offsetTop}px`); 136 | $el.style.setProperty('--vvol', `${vvv.offsetLeft}px`); 137 | $el.style.setProperty('--vvz', vvv.scale); 138 | }; 139 | export { syncVisualViewportValuesToCustomProperties }; 140 | 141 | const getScrollValues = () => { 142 | // @note: document.documentElement.scrollTop/Left = window.scrollY/scrollX = window.pageYOffset/pageXOffset 143 | let { scrollX, scrollY } = window; 144 | 145 | // Calc overscroll 146 | const overScrollX = scrollX < 0 ? scrollX : Math.max(0, scrollX + window.innerWidth - document.body.offsetWidth); 147 | const overScrollY = scrollY < 0 ? scrollY : Math.max(0, scrollY + window.innerHeight - document.body.offsetHeight); 148 | 149 | return { 150 | scrollX, 151 | scrollY, 152 | overScrollX, 153 | overScrollY, 154 | }; 155 | }; 156 | export { getScrollValues }; 157 | 158 | const getBodyValues = () => { 159 | let { offsetHeight, offsetWidth } = document.body; 160 | 161 | return { 162 | offsetHeight, 163 | offsetWidth, 164 | }; 165 | }; 166 | export { getBodyValues }; 167 | 168 | const getLayoutViewportValues = () => { 169 | // @NOTE: You might think we could use window.innerHeight/innerWidth for this 170 | // but this is not true: when overscrolling Safari into a pull-to-refresh, 171 | // the window.innerHeight shrinks, but the layout viewport remains the same. 172 | const layoutViewport = document.querySelector('#layoutviewport'); 173 | 174 | if (!layoutViewport) return; 175 | 176 | const { width, height } = layoutViewport.getBoundingClientRect(); 177 | 178 | return { 179 | width, 180 | height, 181 | }; 182 | }; 183 | export { getLayoutViewportValues }; 184 | 185 | const getICBValues = () => { 186 | // @NOTE: Instead of getting the values through by measuring the root element 187 | // (i.e `document.documentElement.getBoundingClientRect()`), we can use 188 | // `documentElement.clientHeight/clientWidth` which yield the correct values. 189 | // These values do not require the ICB to be sized as 100% x 100%. 190 | return { 191 | width: document.documentElement.clientWidth, 192 | height: document.documentElement.clientHeight, 193 | }; 194 | }; 195 | export { getICBValues }; 196 | 197 | const getICBValuesByMeasuringTheRootElement = () => { 198 | const rect = document.documentElement.getBoundingClientRect(); 199 | return { 200 | width: rect.width, 201 | height: rect.height, 202 | }; 203 | }; 204 | export { getICBValuesByMeasuringTheRootElement }; 205 | 206 | const getWindowValues = () => { 207 | let { innerWidth, innerHeight, outerWidth, outerHeight } = window; 208 | 209 | return { 210 | innerWidth, 211 | innerHeight, 212 | outerWidth, 213 | outerHeight, 214 | }; 215 | }; 216 | export { getWindowValues }; 217 | 218 | const getScreenValues = () => { 219 | let { width, height } = screen; 220 | 221 | return { 222 | width, 223 | height, 224 | }; 225 | }; 226 | export { getScreenValues }; 227 | 228 | const initOptionsModal = (update) => { 229 | if (!document.querySelector('#btnOptions')) return; 230 | 231 | // Show/Hide Modal 232 | document.querySelector('#btnOptions').addEventListener('click', (e) => { 233 | e.preventDefault(); 234 | document.querySelector('#options').showModal(); 235 | }); 236 | document.querySelector('#btnOptionsClose').addEventListener('click', (e) => { 237 | e.preventDefault(); 238 | document.querySelector('#options').close(); 239 | }); 240 | 241 | // Update after closing the options 242 | document.querySelector('#options').addEventListener('close', update); 243 | }; 244 | 245 | const initFullscreenToggle = () => { 246 | if (!document.querySelector('#optGoFullScreen')) return; 247 | 248 | // Need to put spec names first, as Chrome doesn’t fire events when webkit-prefixed 249 | // @see http://crbug.com/1511367 250 | const exitFullscreen = document.exitFullscreen || document.webkitExitFullscreen; 251 | const requestFullscreen = document.requestFullscreen || document.webkitRequestFullscreen; 252 | 253 | let fullscreen = false; 254 | document.querySelector('#optGoFullScreen').addEventListener("click", function () { 255 | if (fullscreen) document.exitFullscreen(); 256 | else document.documentElement.requestFullscreen(); 257 | }); 258 | 259 | document.addEventListener("fullscreenchange", (e) => { 260 | console.log(document.fullscreenElement); 261 | fullscreen = document.fullscreenElement ? true : false 262 | document.querySelector('#optGoFullScreen').checked = fullscreen; 263 | document.querySelector('#options').close(); // Workaround for fullscreen element getting drawn on top of open dialog 264 | document.querySelector('#options').showModal(); // Workaround for fullscreen element getting drawn on top of open dialog 265 | }); 266 | } 267 | 268 | const initVirtualKeyboardIntegration = () => { 269 | if (!document.querySelector('#optVirtualKeyboardOverlaysContent')) return; 270 | 271 | if ('virtualKeyboard' in navigator) { 272 | navigator.virtualKeyboard.overlaysContent = document.querySelectorAll('#optVirtualKeyboardOverlaysContent:checked').length > 0; 273 | 274 | navigator.virtualKeyboard.addEventListener('geometrychange', () => { 275 | // Values a reported by Virtual Keyboard API 276 | write(navigator.virtualKeyboard.boundingRect, document.querySelector('#vk-values'), 'Virtual Keyboard'); 277 | 278 | // Read values from the #vkdebugger 279 | const rect = document.querySelector('#vkdebugger').getBoundingClientRect(); 280 | write({ width: rect.width, height: rect.height }, document.querySelector('#vkdebugger-values'), 'Virtual Keyboard Spacetaker'); 281 | }); 282 | 283 | // Hook up toggle 284 | document.querySelector('#optVirtualKeyboardOverlaysContent').addEventListener('change', (e) => { 285 | navigator.virtualKeyboard.overlaysContent = e.target.checked; 286 | }); 287 | 288 | // Values on load 289 | const vkRect = navigator.virtualKeyboard.boundingRect; 290 | write({ width: vkRect.width, height: vkRect.height }, document.querySelector('#vk-values'), 'Virtual Keyboard'); 291 | 292 | if (document.querySelector('#vkdebugger')) { 293 | const rect = document.querySelector('#vkdebugger').getBoundingClientRect(); 294 | write({ width: rect.width, height: rect.height }, document.querySelector('#vkdebugger-values'), 'Virtual Keyboard Spacetaker'); 295 | } 296 | } else { 297 | document.querySelector('#optVirtualKeyboardOverlaysContent').disabled = 'disabled'; 298 | 299 | if (document.querySelector('#vk-values')) { 300 | write(null, document.querySelector('#vk-values'), 'Virtual Keyboard'); 301 | } 302 | 303 | if (document.querySelector('#vkdebugger')) { 304 | const rect = document.querySelector('#vkdebugger').getBoundingClientRect(); 305 | write({ width: rect.width, height: rect.height }, document.querySelector('#vkdebugger-values'), 'Virtual Keyboard Spacetaker'); 306 | } 307 | } 308 | }; 309 | const initVisualViewportIntegration = () => { 310 | // Browsers that do no update scroll position on overscroll do not need this 311 | if (browserUpdatesScrollPositionsOnOverscroll()) return; 312 | 313 | // No options included? Bail out 314 | if (!document.querySelectorAll('[name="vvAdjustValues"]').length) return; 315 | 316 | document.querySelectorAll('[name="vvAdjustValues"]').forEach((checkbox) => { 317 | checkbox.disabled = 'disabled'; 318 | }); 319 | }; 320 | 321 | const init = (update, autoTick = 0) => { 322 | initOptionsModal(update); 323 | initVirtualKeyboardIntegration(); 324 | initVisualViewportIntegration(); 325 | initFullscreenToggle(); 326 | 327 | // Update on scroll/resize 328 | window.addEventListener('scroll', update, { passive: true }); 329 | window.visualViewport.addEventListener('scroll', update, { passive: true }); 330 | window.visualViewport.addEventListener('resize', update, { passive: true }); 331 | 332 | // Make sure we have values on load 333 | setTimeout(update, 100); 334 | 335 | if (autoTick) { 336 | setInterval(update, autoTick); 337 | } 338 | }; 339 | export { init }; 340 | -------------------------------------------------------------------------------- /demos/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Interop 2022 Viewport Investigation Effort Demo Pages 7 | 8 | 9 | 24 | 38 | 39 | 40 | 41 |
    42 |

    Interop 2022 Viewport Investigation Effort Demo Pages

    43 | 44 |

    A collection of demos that allow you to debug and test the behavior of certain viewport and viewport-related aspects.

    45 | 46 |

    These demos are part of the Viewport Investigation Effort

    47 | 48 |

    Individual Viewports and related aspects

    49 |
      50 |
    1. Initial Containing Block (ICB) 51 | 57 |
    2. 58 |
    3. Layout Viewport
    4. 59 |
    5. Visual Viewport
    6. 60 |
    7. Virtual Keyboard API
    8. 61 |
    62 | 63 |

    Viewport Units

    64 |
      65 |
    1. 66 | Viewport Units + window.innerHeight
      67 | (Adapted from https://bokand.github.io/demo/urlbarsize.html) 68 |
    2. 69 |
    3. 70 | Viewport Units + -webkit-fill-available 71 |
    4. 72 |
    73 | 74 |

    Events / All-in-one

    75 | 76 | 79 | 80 |

    Scrollbar Gutter

    81 | 82 | 98 | 99 |

    Safe Inset Areas

    100 |
      101 |
    1. With viewport meta tag (width=device-width) but no viewport-fit
    2. 102 |
    3. With viewport-fit=auto in the viewport meta tag
    4. 103 |
    5. With viewport-fit=cover in the viewport meta tag
    6. 104 |
    7. With viewport-fit=contain in the viewport meta tag
    8. 105 |
    106 | 107 |

    Legend

    108 | 113 |
    114 | 115 |
    116 |
    117 | 118 |
    119 | -------------------------------------------------------------------------------- /demos/src/individual/icb/fixedmeta.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ICB Debugger (fixed meta tag) – Interop 2022 Viewport Investigation Effort Demo Pages 7 | 8 | 9 | 10 | 30 | 31 | 32 | 33 |
    34 | 35 |
    36 |

    ICB Debugger (fixed meta)

    37 |
    38 |
    39 |
    40 | 41 |
    42 | 43 | 44 | 45 |
    46 | 47 |

    Options

    48 |
    49 |
    50 |

    Virtual Keyboard

    51 |
    52 |
    53 |
    54 |

    Fullscreen

    55 |
    56 |
    57 |
    58 | 59 | 60 | -------------------------------------------------------------------------------- /demos/src/individual/icb/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ICB Debugger – Interop 2022 Viewport Investigation Effort Demo Pages 7 | 8 | 9 | 10 | 30 | 31 | 32 | 33 |
    34 | 35 |
    36 |

    ICB Debugger

    37 |
    38 |
    39 |
    40 | 41 |
    42 | 43 | 44 | 45 |
    46 | 47 |

    Options

    48 |
    49 |
    50 |

    Virtual Keyboard

    51 |
    52 |
    53 |
    54 |

    Fullscreen

    55 |
    56 |
    57 |
    58 | 59 | 60 | -------------------------------------------------------------------------------- /demos/src/individual/icb/nometa.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ICB Debugger (no meta tag) – Interop 2022 Viewport Investigation Effort Demo Pages 7 | 8 | 9 | 29 | 30 | 31 | 32 |
    33 | 34 |
    35 |

    ICB Debugger (no meta)

    36 |
    37 |
    38 |
    39 | 40 |
    41 | 42 | 43 | 44 |
    45 | 46 |

    Options

    47 |
    48 |
    49 |

    Virtual Keyboard

    50 |
    51 |
    52 |
    53 |

    Fullscreen

    54 |
    55 |
    56 |
    57 | 58 | 59 | -------------------------------------------------------------------------------- /demos/src/individual/icb/viewport-fit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ICB Debugger (viewport-fit) – Interop 2022 Viewport Investigation Effort Demo Pages 7 | 8 | 9 | 10 | 30 | 31 | 32 | 33 |
    34 | 35 |
    36 |

    ICB Debugger

    37 |
    38 |
    39 |
    40 | 41 |
    42 | 43 | 44 | 45 |
    46 | 47 |

    Options

    48 |
    49 |
    50 |

    Virtual Keyboard

    51 |
    52 |
    53 |
    54 |

    Fullscreen

    55 |
    56 |
    57 |
    58 | 59 | 60 | -------------------------------------------------------------------------------- /demos/src/individual/layout-viewport/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Layout Viewport Debugger – Interop 2022 Viewport Investigation Effort Demo Pages 7 | 8 | 9 | 10 | 36 | 37 | 38 | 39 |
    40 | 41 |
    42 |

    Layout Viewport Debugger

    43 |
    44 |
    45 |
    46 |
    47 |
    48 | 49 |
    50 |
    51 | 52 | 53 | 54 |
    55 | 56 |

    Options

    57 |
    58 |
    59 |

    Virtual Keyboard

    60 |
    61 |
    62 |
    63 |

    Fullscreen

    64 |
    65 |
    66 |
    67 | 68 | 69 | -------------------------------------------------------------------------------- /demos/src/individual/pwa/contain.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Fullscreen PWA – Interop 2022 Viewport Investigation Effort Demo Pages 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 37 | 87 | 88 | 89 | 90 |
    91 | 92 |
    93 |

    PWA fullscreen + viewport-fit=contain + mobile-web-app-capable

    94 |
    95 |
    96 |
    97 | 98 |
    99 | 100 |
    101 |
    102 | 103 |
    104 |
    105 |
    106 | 107 | 108 | 109 | 110 |
    111 | 112 |

    Options

    113 |
    114 |
    115 |

    Virtual Keyboard

    116 |
    117 |
    118 |
    119 |

    Fullscreen

    120 |
    121 |
    122 |
    123 | 124 | 125 | -------------------------------------------------------------------------------- /demos/src/individual/pwa/cover.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Fullscreen PWA – Interop 2022 Viewport Investigation Effort Demo Pages 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 37 | 87 | 88 | 89 | 90 |
    91 | 92 |
    93 |

    PWA fullscreen + viewport-fit=cover + mobile-web-app-capable

    94 |
    95 |
    96 |
    97 | 98 |
    99 | 100 |
    101 |
    102 | 103 |
    104 |
    105 |
    106 | 107 | 108 | 109 | 110 |
    111 | 112 |

    Options

    113 |
    114 |
    115 |

    Virtual Keyboard

    116 |
    117 |
    118 |
    119 |

    Fullscreen

    120 |
    121 |
    122 |
    123 | 124 | 125 | -------------------------------------------------------------------------------- /demos/src/individual/pwa/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |
  1. PWA fullscreen + viewport-fit=contain + mobile-web-app-capable
  2. 10 |
  3. PWA fullscreen + viewport-fit=cover + mobile-web-app-capable
  4. 11 | 12 | -------------------------------------------------------------------------------- /demos/src/individual/pwa/manifest-contain.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PWA (contain)", 3 | "start_url": "contain.html", 4 | "id": "interop-2022-viewport.netlify.app/individual/pwa/contain", 5 | "icons": [ 6 | { 7 | "src": "https://cdn.glitch.me/d0334cc3-be72-47b5-a25f-d3fbe953f5ff%2Fapple-touch-icon.png?v=1636974061118", 8 | "type": "image/png", 9 | "sizes": "257x257" 10 | } 11 | ], 12 | "display": "fullscreen" 13 | } -------------------------------------------------------------------------------- /demos/src/individual/pwa/manifest-cover.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PWA (cover)", 3 | "start_url": "cover.html", 4 | "id": "interop-2022-viewport.netlify.app/individual/pwa/cover", 5 | "icons": [ 6 | { 7 | "src": "https://cdn.glitch.me/d0334cc3-be72-47b5-a25f-d3fbe953f5ff%2Fapple-touch-icon.png?v=1636974061118", 8 | "type": "image/png", 9 | "sizes": "257x257" 10 | } 11 | ], 12 | "display": "fullscreen" 13 | } -------------------------------------------------------------------------------- /demos/src/individual/safe-inset-areas/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Safe Inset Areas – Interop 2022 Viewport Investigation Effort Demo Pages 7 | 8 | 9 | 10 | 30 | 80 | 81 | 82 | 83 |
    84 | 85 |
    86 |

    No viewport-fit

    87 |
    88 |
    89 |
    90 | 91 |
    92 | 93 |
    94 |
    95 | 96 |
    97 |
    98 |
    99 | 100 | 101 | 102 | 103 |
    104 | 105 |

    Options

    106 |
    107 |
    108 |

    Virtual Keyboard

    109 |
    110 |
    111 |
    112 |

    Fullscreen

    113 |
    114 |
    115 |
    116 | 117 | 118 | -------------------------------------------------------------------------------- /demos/src/individual/safe-inset-areas/viewport-fit-auto.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Safe Inset Areas (viewport-fit=auto) – Interop 2022 Viewport Investigation Effort Demo Pages 7 | 8 | 9 | 10 | 30 | 80 | 81 | 82 | 83 |
    84 | 85 |
    86 |

    viewport-fit=auto

    87 |
    88 |
    89 |
    90 | 91 |
    92 | 93 |
    94 |
    95 | 96 |
    97 |
    98 |
    99 | 100 | 101 | 102 | 103 |
    104 | 105 |

    Options

    106 |
    107 |
    108 |

    Virtual Keyboard

    109 |
    110 |
    111 |
    112 |

    Fullscreen

    113 |
    114 |
    115 |
    116 | 117 | 118 | -------------------------------------------------------------------------------- /demos/src/individual/safe-inset-areas/viewport-fit-contain.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Safe Inset Areas (viewport-fit=contain) – Interop 2022 Viewport Investigation Effort Demo Pages 7 | 8 | 9 | 10 | 30 | 80 | 81 | 82 | 83 |
    84 | 85 |
    86 |

    viewport-fit=contain

    87 |
    88 |
    89 |
    90 | 91 |
    92 | 93 |
    94 |
    95 | 96 |
    97 |
    98 |
    99 | 100 | 101 | 102 | 103 |
    104 | 105 |

    Options

    106 |
    107 |
    108 |

    Virtual Keyboard

    109 |
    110 |
    111 |
    112 |

    Fullscreen

    113 |
    114 |
    115 |
    116 | 117 | 118 | -------------------------------------------------------------------------------- /demos/src/individual/safe-inset-areas/viewport-fit-cover.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Safe Inset Areas (viewport-fit=cover) – Interop 2022 Viewport Investigation Effort Demo Pages 7 | 8 | 9 | 10 | 30 | 80 | 81 | 82 | 83 |
    84 | 85 |
    86 |

    viewport-fit=cover

    87 |
    88 |
    89 |
    90 | 91 |
    92 | 93 |
    94 |
    95 | 96 |
    97 |
    98 |
    99 | 100 | 101 | 102 | 103 |
    104 | 105 |

    Options

    106 |
    107 |
    108 |

    Virtual Keyboard

    109 |
    110 |
    111 |
    112 |

    Fullscreen

    113 |
    114 |
    115 |
    116 | 117 | 118 | -------------------------------------------------------------------------------- /demos/src/individual/virtual-keyboard/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Virtual Keyboard Debugger – Interop 2022 Viewport Investigation Effort Demo Pages 7 | 8 | 9 | 10 | 28 | 29 | 47 | 48 | 49 | 50 |
    51 | 52 |
    53 |

    Virtual Keyboard API Debugger

    54 |
    55 |
    56 |
    57 |
    58 |
    59 |
    60 |
    61 | 62 |
    63 | 64 |
    65 |
    66 |
    67 | 68 | 69 | 70 |
    71 | 72 |

    Options

    73 |
    74 |
    75 |

    Visual Viewport

    76 |

    Some browsers allow negative scrollX/scrollY, and allow the same for the Visual Viewport’s pageTop/pageLeft. This may conflict with positioning the Visual Viewport box, so there’s a few fixes one can apply

    77 |
    78 |
    79 |
    80 |

    Positioning method to use

    81 |
    82 |
    83 |
    84 |
    85 |

    Virtual Keyboard

    86 |
    87 |
    88 |
    89 |

    Fullscreen

    90 |
    91 |
    92 |
    93 | 94 | 95 | -------------------------------------------------------------------------------- /demos/src/individual/visual-viewport/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Visual Viewport Debugger – Interop 2022 Viewport Investigation Effort Demo Pages 7 | 8 | 9 | 10 | 32 | 33 | 34 | 35 |
    36 | 37 |
    38 |

    Visual Viewport Debugger

    39 |
    40 |
    41 |
    42 |
    43 |
    44 |
    45 |
    46 | 47 |
    48 |
    49 |
    50 | 51 | 52 | 53 |
    54 | 55 |

    Options

    56 |
    57 |
    58 |

    Visual Viewport

    59 |

    Some browsers allow negative scrollX/scrollY, and allow the same for the Visual Viewport’s pageTop/pageLeft. This may conflict with positioning the Visual Viewport box, so there’s a few fixes one can apply

    60 |
    61 |
    62 |
    63 |

    Positioning method to use

    64 |
    65 |
    66 |
    67 |
    68 |

    Virtual Keyboard

    69 |
    70 |
    71 |
    72 |

    Fullscreen

    73 |
    74 |
    75 |
    76 | 77 | 78 | -------------------------------------------------------------------------------- /demos/src/scrollbar-gutter/long-both-edges/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | scrollbar-gutter: stable both-edges (long content) – Interop 2022 Viewport Investigation Effort Demo Pages 7 | 8 | 9 | 10 | 11 | 16 | 17 | 39 | 40 | 41 | 42 | 43 |
    44 | 45 |
    46 |

    scrollbar-gutter: stable both-edges; Debugger

    47 |
    48 |

    Warning: Your browser does not support scrollbar-gutter so this demo won’t work.

    49 |
    50 |
    51 |
    52 |
    53 |
    54 |
    55 |
    56 |
    57 |
    58 | 59 |
    60 |
    61 | 62 |
    63 |
    100vw position: absolute (???px)
    64 |
    100lvw position: absolute (???px)
    65 |
    100svw position: absolute (???px)
    66 |
    100dvw position: absolute (???px)
    67 |
    100% position: absolute (???px)
    68 |
    69 | 70 |
    71 |
    100vw position: fixed (???px)
    72 |
    100lvw position: fixed (???px)
    73 |
    100svw position: fixed (???px)
    74 |
    100dvw position: fixed (???px)
    75 |
    100% position: fixed (???px)
    76 |
    77 | 78 | 79 | 80 |
    81 | 82 |

    Options

    83 |
    84 |
    85 |

    Bars

    86 |
    87 |
    88 |
    89 |

    Virtual Keyboard

    90 |
    91 |
    92 |
    93 |

    Fullscreen

    94 |
    95 |
    96 |
    97 | 98 | 99 | -------------------------------------------------------------------------------- /demos/src/scrollbar-gutter/long-stable/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | scrollbar-gutter: stable (long content) – Interop 2022 Viewport Investigation Effort Demo Pages 7 | 8 | 9 | 10 | 11 | 16 | 17 | 39 | 40 | 41 | 42 | 43 |
    44 | 45 |
    46 |

    scrollbar-gutter: stable; Debugger

    47 |
    48 |

    Warning: Your browser does not support scrollbar-gutter so this demo won’t work.

    49 |
    50 |
    51 |
    52 |
    53 |
    54 |
    55 |
    56 |
    57 |
    58 | 59 |
    60 |
    61 | 62 |
    63 |
    100vw position: absolute (???px)
    64 |
    100lvw position: absolute (???px)
    65 |
    100svw position: absolute (???px)
    66 |
    100dvw position: absolute (???px)
    67 |
    100% position: absolute (???px)
    68 |
    69 | 70 |
    71 |
    100vw position: fixed (???px)
    72 |
    100lvw position: fixed (???px)
    73 |
    100svw position: fixed (???px)
    74 |
    100dvw position: fixed (???px)
    75 |
    100% position: fixed (???px)
    76 |
    77 | 78 | 79 | 80 |
    81 | 82 |

    Options

    83 |
    84 |
    85 |

    Bars

    86 |
    87 |
    88 |
    89 |

    Virtual Keyboard

    90 |
    91 |
    92 |
    93 |

    Fullscreen

    94 |
    95 |
    96 |
    97 | 98 | 99 | -------------------------------------------------------------------------------- /demos/src/scrollbar-gutter/scrollbar-gutter-bars.css: -------------------------------------------------------------------------------- 1 | body { 2 | --bar-gap: 0.2em; 3 | --bar-height: 1.6em; 4 | --bar-extra-offset: 0px; 5 | } 6 | 7 | /* We’ve got 10 bars at the top. Shift these elements down */ 8 | #debug, 9 | #inputwrapper { 10 | padding-top: calc((var(--bar-gap) + var(--bar-height)) * 10); 11 | } 12 | 13 | /* We really need sibling-count() and sibling-index(), don’t we? */ 14 | .bar:nth-child(1) { 15 | --i: 1; 16 | } 17 | .bar:nth-child(2) { 18 | --i: 2; 19 | } 20 | .bar:nth-child(3) { 21 | --i: 3; 22 | } 23 | .bar:nth-child(4) { 24 | --i: 4; 25 | } 26 | .bar:nth-child(5) { 27 | --i: 5; 28 | } 29 | 30 | /* Styling of a bar. Each bar gets shifted down a little more, based on its index*/ 31 | .bar { 32 | box-sizing: border-box; 33 | top: calc(((var(--bar-gap) + var(--bar-height)) * (var(--i) - 1)) + var(--bar-gap) + var(--bar-extra-offset)); 34 | height: var(--bar-height); 35 | padding: var(--bar-gap); 36 | line-height: calc(var(--bar-height) - var(--bar-gap) - var(--bar-gap)); 37 | background: rgb(0 0 0 / 0.2); 38 | } 39 | .bar[data-position="absolute"] { 40 | position: absolute; 41 | } 42 | .bar[data-position="fixed"] { 43 | position: fixed; 44 | } 45 | .bar[data-unit="vw"] { 46 | width: 100vw; 47 | } 48 | .bar[data-unit="lvw"] { 49 | width: 100lvw; 50 | } 51 | .bar[data-unit="svw"] { 52 | width: 100svw; 53 | } 54 | .bar[data-unit="dvw"] { 55 | width: 100dvw; 56 | } 57 | .bar[data-unit="%"] { 58 | width: 100%; 59 | } 60 | 61 | /* Push the fixedpos bars below the 5 abspos ones */ 62 | #bars-fixed .bar { 63 | --bar-extra-offset: calc((var(--bar-gap) + var(--bar-height)) * 5); 64 | } 65 | 66 | /* Bar Options. O yeah, we need :has() but firefox don’t play nice yet */ 67 | #optBarsHideBars { 68 | display: none; 69 | } 70 | #optBarsHideBars:checked ~ #bars-fixed, 71 | #optBarsHideBars:checked ~ #bars-absolute { 72 | display: none; 73 | } 74 | #optBarsHideBars:checked ~ #debug, 75 | #optBarsHideBars:checked ~ #inputwrapper { 76 | padding-top: 0; 77 | } 78 | [for="optBarsHideBars"] { 79 | display: flex; 80 | flex-direction: row; 81 | align-items: center; 82 | } 83 | [for="optBarsHideBars"]::before { 84 | font-size: 3rem; 85 | } 86 | #optBarsHideBars ~ #options [for="optBarsHideBars"]::before { 87 | content: "☐"; 88 | } 89 | #optBarsHideBars:checked ~ #options [for="optBarsHideBars"]::before { 90 | content: "☑"; 91 | } 92 | 93 | /* Show warning in browsers with no support */ 94 | .warning { 95 | position: fixed; 96 | bottom: 1rem; 97 | left: 1rem; 98 | right: 1rem; 99 | padding: 1rem; 100 | background-color: yellow; 101 | border: 1px solid #333; 102 | margin: 0.2rem 0; 103 | } 104 | .warning :first-child { 105 | margin-top: 0; 106 | } 107 | .warning :last-child { 108 | margin-bottom: 0; 109 | } 110 | @supports (scrollbar-gutter: stable) { 111 | .warning { 112 | display: none; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /demos/src/scrollbar-gutter/scrollbar-gutter-bars.js: -------------------------------------------------------------------------------- 1 | const updateUI = () => { 2 | const $bars = document.querySelectorAll('.bar'); 3 | $bars.forEach(($bar) => { 4 | const width = Math.round($bar.clientWidth); 5 | $bar.querySelector('.size').innerText = width; 6 | }); 7 | }; 8 | 9 | const init = (autoTick = 0) => { 10 | updateUI(); 11 | window.addEventListener('resize', (e) => { 12 | updateUI(); 13 | }); 14 | 15 | if (autoTick) { 16 | setInterval(updateUI, autoTick); 17 | } 18 | }; 19 | export { init }; 20 | -------------------------------------------------------------------------------- /demos/src/scrollbar-gutter/short-both-edges/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | scrollbar-gutter: stable both-edges (short content) – Interop 2022 Viewport Investigation Effort Demo Pages 7 | 8 | 9 | 10 | 11 | 24 | 25 | 47 | 48 | 49 | 50 | 51 |
    52 | 53 |
    54 |

    scrollbar-gutter: stable both-edges; Debugger

    55 |
    56 |

    Warning: Your browser does not support scrollbar-gutter so this demo won’t work.

    57 |
    58 |
    59 |
    60 |
    61 |
    62 |
    63 |
    64 |
    65 |
    66 | 67 |
    68 |
    69 | 70 |
    71 |
    100vw position: absolute (???px)
    72 |
    100lvw position: absolute (???px)
    73 |
    100svw position: absolute (???px)
    74 |
    100dvw position: absolute (???px)
    75 |
    100% position: absolute (???px)
    76 |
    77 | 78 |
    79 |
    100vw position: fixed (???px)
    80 |
    100lvw position: fixed (???px)
    81 |
    100svw position: fixed (???px)
    82 |
    100dvw position: fixed (???px)
    83 |
    100% position: fixed (???px)
    84 |
    85 | 86 | 87 | 88 |
    89 | 90 |

    Options

    91 |
    92 |
    93 |

    Bars

    94 |
    95 |
    96 |
    97 |

    Virtual Keyboard

    98 |
    99 |
    100 |
    101 |

    Fullscreen

    102 |
    103 |
    104 |
    105 | 106 | 107 | -------------------------------------------------------------------------------- /demos/src/scrollbar-gutter/short-stable/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | scrollbar-gutter: stable (short content) – Interop 2022 Viewport Investigation Effort Demo Pages 7 | 8 | 9 | 10 | 11 | 24 | 25 | 47 | 48 | 49 | 50 | 51 |
    52 | 53 |
    54 |

    scrollbar-gutter: stable; Debugger

    55 |
    56 |

    Warning: Your browser does not support scrollbar-gutter so this demo won’t work.

    57 |
    58 |
    59 |
    60 |
    61 |
    62 |
    63 |
    64 |
    65 |
    66 | 67 |
    68 |
    69 | 70 |
    71 |
    100vw position: absolute (???px)
    72 |
    100lvw position: absolute (???px)
    73 |
    100svw position: absolute (???px)
    74 |
    100dvw position: absolute (???px)
    75 |
    100% position: absolute (???px)
    76 |
    77 | 78 |
    79 |
    100vw position: fixed (???px)
    80 |
    100lvw position: fixed (???px)
    81 |
    100svw position: fixed (???px)
    82 |
    100dvw position: fixed (???px)
    83 |
    100% position: fixed (???px)
    84 |
    85 | 86 | 87 | 88 |
    89 | 90 |

    Options

    91 |
    92 |
    93 |

    Bars

    94 |
    95 |
    96 |
    97 |

    Virtual Keyboard

    98 |
    99 |
    100 |
    101 |

    Fullscreen

    102 |
    103 |
    104 |
    105 | 106 | 107 | -------------------------------------------------------------------------------- /explainers/README.md: -------------------------------------------------------------------------------- 1 | # Interop 2022: Viewport: Explainers 2 | 3 | A collection of explainers covering the key concepts that relate to viewports 4 | 5 | - [Layout Viewport](./layout-viewport.md) 6 | - [Initial Containing Block (ICB)](./icb.md) 7 | - [Visual Viewport](./visual-viewport.md) 8 | - [Virtual Keyboard](./virtual-keyboard.md) 9 | - [Virtual Keyboard API](./virtual-keyboard-api.md) 10 | - [Viewport Units](./viewport-units.md) 11 | - [Scrolling](./scrolling.md) 12 | - [Sizing](./sizing.md) 13 | 14 | These concepts with their visualizations are also covered in [this episode of HTTP 203](https://www.youtube.com/watch?v=xl9R8aTOW_I) _(43:34)_ -------------------------------------------------------------------------------- /explainers/icb.md: -------------------------------------------------------------------------------- 1 | # Initial Containing Block 2 | 3 | ## Definition 4 | 5 | The Initial Containing Block – or ICB for short – is [defined in the CSS2 spec](https://drafts.csswg.org/css2/#containing-block-details): 6 | 7 | > The position and size of an element’s box(es) are sometimes calculated relative to a certain rectangle, called the containing block of the element. The containing block in which the root element[^fn1] lives is a rectangle called the initial containing block. 8 | [^fn1]: Root Element = the `html` element – [DOM Living Standard: The `html` element](https://html.spec.whatwg.org/multipage/semantics.html#the-html-element) 9 | 10 | It takes its size from [the Layout Viewport](./layout-viewport.md) _(for continuous media)_: 11 | 12 | > For continuous media, it has the dimensions of the viewport[^fn2] and is anchored at the canvas origin 13 | 14 | [^fn2]: Back when CSS2 was defined, it had no notion of different viewports. When specs talk about _“the Viewport”_, they mean [the Layout Viewport](./layout-viewport.md). 15 | 16 | ## Visualization 17 | 18 | To visualize it, authors can use this CSS snippet: 19 | 20 | ```css 21 | html { 22 | width: 100%; 23 | height: 100%; 24 | outline: 10px dashed red; 25 | outline-offset: -10px; 26 | } 27 | ``` 28 | 29 | 👉 Try it out: [Initial Containing Block (ICB)](https://interop-2022-viewport.netlify.app/individual/icb/) 30 | 31 | ## Measuring the ICB 32 | 33 | To get the actual size of the ICB, authors do not need to size the root element to be `100%` and measure it that way, but can use this JS snippet: 34 | 35 | ```js 36 | document.documentElement.clientWidth; 37 | document.documentElement.clientHeight; 38 | ``` 39 | 40 | This due to [an exception in the definition of `clientWidth`/`clientWidth`](https://www.w3.org/TR/cssom-view-1/#dom-element-clientwidth) 41 | 42 | > If the element is the root element and the element’s node document is not in quirks mode […] return the viewport width/height excluding the size of a rendered scroll bar (if any). 43 | 44 | 💡 You might think `window.innerWidth` and `window.innerHeight` might also get you these values, but that’s not the case. Some browsers resize those values as you pinch-zoom in. The values for the `document.documentElement.clientWidth`/`.clientHeight` properties remain stable and are not affected by pinch-zooming. 45 | 46 | ## Findings 47 | 48 | 💡 These findings are a textual representation of the [test results table](https://goo.gle/interop-2022-viewport-testresults). 49 | 50 | ### Size 51 | 52 | In desktop browsers, the ICB – as specced – follows the size of the [Layout Viewport](./layout-viewport.md). 53 | 54 | ![Illustration](./illustrations/icb-desktop-content-short.png) 55 | 56 | In mobile browsers the ICB has the same size as the [Layout Viewport](./layout-viewport.md) after initial load which has expanded dynamic UA UI Elements. This is also known as the [Small Viewport](./viewport-units.md). 57 | 58 | ![Illustration](./illustrations/icb-mobile--uaui-expanded.png) 59 | 60 | There, the pixel width of the ICB is determined by the `viewport` meta tag: 61 | 62 | - `` sizes the width of the ICB in relation to the device’s width 63 | - `` sizes the width of the ICB to the given value, e.g. `2000px` 64 | - Lack of a `` tag makes user agents fall back to a default ICB width of `980px`. 65 | 66 | ### Effect of scrolling 67 | 68 | As specced, the ICB is anchored to the canvas origin on which the document is painted. As a result, the ICB slides out of view as you scroll down a page. 69 | 70 | ![Illustration](./illustrations/icb-desktop-content-long.png) 71 | ![Illustration](./illustrations/visual-viewport-desktop.png) 72 | 73 | On Mobile, the ICB remains the same size in most browsers, except for a few outliers: they resize the ICB as the UA UI gets hidden. Note that in that case, these browsers do not resize the ICB to match the size of the [Layout Viewport](./layout-viewport.md) but use an in-between value. 74 | 75 | ![Illustration](./illustrations/icb-mobile--uaui-retracted.png) 76 | 77 | ### Effect of scrollbars 78 | 79 | When operating systems use [Classic Scrollbars](./scrolling.md#classic-scrollbars), the ICB shrinks. This is because those browsers resize the [Layout Viewport](./layout-viewport.md), upon which the ICB size is based. [Overlay Scrollbars](./scrolling.md#overlay-scrollbars) have no effect on the size of the ICB. 80 | 81 | ![Illustration](./illustrations/icb-desktop-content-long--classic-scrollbar.png) 82 | 83 | ### Effect of pinch-zoom 84 | 85 | When pinch-zooming in the ICB does not get resized because the [Layout Viewport](./layout-viewport.md) – upon which the ICB size is based – does not resize either. 86 | 87 | ![Illustration](./illustrations/visual-viewport-desktop--pinch-zoomed-in-1.png) 88 | 89 | ### Effect of the Virtual Keyboard 90 | 91 | See [Virtual Keyboard: Findings](./virtual-keyboard.md#findings). 92 | 93 | ### Effect of Overscrolling / Bouncy Scroll 94 | 95 | When [overscrolling](./scrolling.md#overscrolling-and-rubber-banding) – on platforms that support it - the ICB retains its size and bounces along with the rubber banding as it happens. 96 | 97 | ### Relation to Viewport Units 98 | 99 | Their name might not indicate it, but the [Viewport Units](./viewport-units.md) are sized in relation to [the ICB](./icb.md). See [Viewport Units](./viewport-units.md) for details. 100 | 101 | ## Issues 102 | 103 | We are tracking issues using [the label `ICB`](https://github.com/web-platform-tests/interop-2022-viewport/issues?q=is%3Aissue+label%3AICB) -------------------------------------------------------------------------------- /explainers/illustrations/icb+layout-viewport.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/icb+layout-viewport.png -------------------------------------------------------------------------------- /explainers/illustrations/icb-desktop-content-long--classic-scrollbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/icb-desktop-content-long--classic-scrollbar.png -------------------------------------------------------------------------------- /explainers/illustrations/icb-desktop-content-long.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/icb-desktop-content-long.png -------------------------------------------------------------------------------- /explainers/illustrations/icb-desktop-content-short.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/icb-desktop-content-short.png -------------------------------------------------------------------------------- /explainers/illustrations/icb-mobile--uaui-expanded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/icb-mobile--uaui-expanded.png -------------------------------------------------------------------------------- /explainers/illustrations/icb-mobile--uaui-retracted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/icb-mobile--uaui-retracted.png -------------------------------------------------------------------------------- /explainers/illustrations/layout-viewport-desktop-content-long--classic-scrollbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/layout-viewport-desktop-content-long--classic-scrollbar.png -------------------------------------------------------------------------------- /explainers/illustrations/layout-viewport-desktop-content-long--scrolled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/layout-viewport-desktop-content-long--scrolled.png -------------------------------------------------------------------------------- /explainers/illustrations/layout-viewport-desktop-content-long.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/layout-viewport-desktop-content-long.png -------------------------------------------------------------------------------- /explainers/illustrations/layout-viewport-desktop-content-short.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/layout-viewport-desktop-content-short.png -------------------------------------------------------------------------------- /explainers/illustrations/layout-viewport-desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/layout-viewport-desktop.png -------------------------------------------------------------------------------- /explainers/illustrations/layout-viewport-mobile--uaui-expanded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/layout-viewport-mobile--uaui-expanded.png -------------------------------------------------------------------------------- /explainers/illustrations/layout-viewport-mobile--uaui-retracted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/layout-viewport-mobile--uaui-retracted.png -------------------------------------------------------------------------------- /explainers/illustrations/uaui-expanded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/uaui-expanded.png -------------------------------------------------------------------------------- /explainers/illustrations/uaui-retracted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/uaui-retracted.png -------------------------------------------------------------------------------- /explainers/illustrations/viewport-units-desktop-long-content--classic-scrollbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/viewport-units-desktop-long-content--classic-scrollbar.png -------------------------------------------------------------------------------- /explainers/illustrations/viewport-units-desktop-long-content.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/viewport-units-desktop-long-content.png -------------------------------------------------------------------------------- /explainers/illustrations/viewport-units-desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/viewport-units-desktop.png -------------------------------------------------------------------------------- /explainers/illustrations/viewport-units-mobile-dvh--mobilesafari.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/viewport-units-mobile-dvh--mobilesafari.png -------------------------------------------------------------------------------- /explainers/illustrations/viewport-units-mobile-naming-things.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/viewport-units-mobile-naming-things.png -------------------------------------------------------------------------------- /explainers/illustrations/viewport-units-mobile-svh+lvh--with-icb--mobilesafari.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/viewport-units-mobile-svh+lvh--with-icb--mobilesafari.png -------------------------------------------------------------------------------- /explainers/illustrations/viewport-units-mobile-svh+lvh--with-icb--uaui-expanded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/viewport-units-mobile-svh+lvh--with-icb--uaui-expanded.png -------------------------------------------------------------------------------- /explainers/illustrations/viewport-units-mobile-svh+lvh--with-icb--uaui-retracted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/viewport-units-mobile-svh+lvh--with-icb--uaui-retracted.png -------------------------------------------------------------------------------- /explainers/illustrations/viewport-units-mobile-vh--uaui-expanded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/viewport-units-mobile-vh--uaui-expanded.png -------------------------------------------------------------------------------- /explainers/illustrations/viewport-units-mobile-vh--uaui-retracted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/viewport-units-mobile-vh--uaui-retracted.png -------------------------------------------------------------------------------- /explainers/illustrations/virtual-keyboard-api-overlayscontent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/virtual-keyboard-api-overlayscontent.png -------------------------------------------------------------------------------- /explainers/illustrations/virtual-keyboard-api-three-behaviors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/virtual-keyboard-api-three-behaviors.png -------------------------------------------------------------------------------- /explainers/illustrations/virtual-keyboard-input-focus--with-everything---offset-layout-viewport.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/virtual-keyboard-input-focus--with-everything---offset-layout-viewport.png -------------------------------------------------------------------------------- /explainers/illustrations/virtual-keyboard-input-focus--with-everything.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/virtual-keyboard-input-focus--with-everything.png -------------------------------------------------------------------------------- /explainers/illustrations/virtual-keyboard-input-focus--with-icb--with-viewport-units.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/virtual-keyboard-input-focus--with-icb--with-viewport-units.png -------------------------------------------------------------------------------- /explainers/illustrations/virtual-keyboard-input-focus--with-icb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/virtual-keyboard-input-focus--with-icb.png -------------------------------------------------------------------------------- /explainers/illustrations/virtual-keyboard-input-focus--with-layout-viewport-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/virtual-keyboard-input-focus--with-layout-viewport-1.png -------------------------------------------------------------------------------- /explainers/illustrations/virtual-keyboard-input-focus--with-layout-viewport-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/virtual-keyboard-input-focus--with-layout-viewport-2.png -------------------------------------------------------------------------------- /explainers/illustrations/virtual-keyboard-input-focus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/virtual-keyboard-input-focus.png -------------------------------------------------------------------------------- /explainers/illustrations/virtual-keyboard-input-nofocus--with-icb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/virtual-keyboard-input-nofocus--with-icb.png -------------------------------------------------------------------------------- /explainers/illustrations/virtual-keyboard-input-nofocus--with-layout-viewport.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/virtual-keyboard-input-nofocus--with-layout-viewport.png -------------------------------------------------------------------------------- /explainers/illustrations/virtual-keyboard-input-nofocus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/virtual-keyboard-input-nofocus.png -------------------------------------------------------------------------------- /explainers/illustrations/visual-viewport-desktop--pinch-zoomed-in-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/visual-viewport-desktop--pinch-zoomed-in-1.png -------------------------------------------------------------------------------- /explainers/illustrations/visual-viewport-desktop--pinch-zoomed-in-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/visual-viewport-desktop--pinch-zoomed-in-2.png -------------------------------------------------------------------------------- /explainers/illustrations/visual-viewport-desktop--pinch-zoomed-in-alt-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/visual-viewport-desktop--pinch-zoomed-in-alt-1.png -------------------------------------------------------------------------------- /explainers/illustrations/visual-viewport-desktop--pinch-zoomed-in-alt-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/visual-viewport-desktop--pinch-zoomed-in-alt-2.png -------------------------------------------------------------------------------- /explainers/illustrations/visual-viewport-desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/visual-viewport-desktop.png -------------------------------------------------------------------------------- /explainers/illustrations/visual-viewport-mobile--pinch-zoomed-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/visual-viewport-mobile--pinch-zoomed-in.png -------------------------------------------------------------------------------- /explainers/illustrations/visual-viewport-mobile--uaui-expanded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/visual-viewport-mobile--uaui-expanded.png -------------------------------------------------------------------------------- /explainers/illustrations/visual-viewport-mobile--uaui-retracted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/illustrations/visual-viewport-mobile--uaui-retracted.png -------------------------------------------------------------------------------- /explainers/layout-viewport.md: -------------------------------------------------------------------------------- 1 | # The Layout Viewport 2 | 3 | ## Definition 4 | 5 | The Layout Viewport is [defined in the CSS2 spec](https://drafts.csswg.org/css2/#viewport): 6 | 7 | > User agents for continuous media generally offer users a viewport _(a window or other viewing area on the screen)_ through which users consult a document. User agents may change the document’s layout when the viewport is resized _(see the [initial containing block](./icb.md))_. 8 | > 9 | > When the viewport is smaller than the area of the canvas on which the document is rendered, the user agent should offer a scrolling mechanism. 10 | > 11 | > There is at most one viewport per canvas, but user agents may render to more than one canvas (i.e., provide different views of the same document)." 12 | 13 | The [definition on MDN](https://developer.mozilla.org/en-US/docs/Glossary/Layout_viewport) reads: 14 | 15 | > The layout viewport is the viewport into which the browser draws a web page. Essentially, it represents what is available to be seen, while the visual viewport represents what is currently visible on the user's display device. 16 | > 17 | > This becomes important, for example, on mobile devices, where a pinching gesture can usually be used to zoom in and out on a site's contents. The rendered document doesn't change in any way, so the layout viewport remains the same as the user adjusts the zoom level. Instead, the [visual viewport](./visual-viewport.md) is updated to indicate the area of the page that they can see. 18 | 19 | ## Visualization 20 | 21 | To visualize it, authors can inject an empty HTML element and have it positioned against the edges using `position: fixed`: 22 | 23 | ```css 24 | #layoutviewport { 25 | position: fixed; 26 | inset: 0; 27 | outline: 5px dashed var(--blue); 28 | outline-offset: -5px; 29 | } 30 | ``` 31 | 32 | 👉 Try it out: [Layout Viewport](https://interop-2022-viewport.netlify.app/individual/layout-viewport/) 33 | 34 | ## Measuring the Layout Viewport 35 | 36 | Using `.getBoundingClientRect()` on the injected `#layoutviewport` element, its size in pixels can be determined: 37 | 38 | ```js 39 | const { 40 | width, 41 | height, 42 | } = document.querySelector("#layoutviewport").getBoundingClientRect(); 43 | ``` 44 | 45 | 💡 You might think [`window.innerWidth` and `window.innerHeight`](./sizing.md#windowinnerwidth-and-windowinnerheight) might also get you these values, but that’s not the case: Some browsers resize those values as you pinch-zoom in. 46 | 47 | ## Relation to `position: fixed` 48 | 49 | Elements that use `position: fixed`, are layed out against the Layout Viewport, as specified in the [CSS Positioned Layout Module](https://drafts.csswg.org/css-position/#valdef-position-fixed) 50 | 51 | > Same as `absolute`, except the box is positioned and sized relative to a fixed positioning containing block (usually the viewport in continuous media […]) 52 | > The box’s position is fixed with respect to this reference rectangle: when attached to the viewport it does not move when the document is scrolled […]. 53 | 54 | ## Findings 55 | 56 | 💡 These findings are a textual representation of the [test results table](https://goo.gle/interop-2022-viewport-testresults). 57 | 58 | ### Size 59 | 60 | The Layout Viewport is sized to area through which a user views the document. As you resize the browser, the viewport size is also affected. 61 | 62 | ![Illustration](./illustrations/layout-viewport-desktop.png) 63 | 64 | This is also the case on mobile where the browser can expand/contract dynamic UA UI Elements such as address bars and toolbars. 65 | 66 | ![Illustration](./illustrations/layout-viewport-mobile--uaui-expanded.png) 67 | 68 | ![Illustration](./illustrations/layout-viewport-mobile--uaui-retracted.png) 69 | 70 | ### Effect of scrolling 71 | 72 | The Layout Viewport spans the entire viewport and remains in place as you scroll down a page. 73 | 74 | ![Illustration](./illustrations/layout-viewport-desktop-content-long--scrolled.png) 75 | 76 | On mobile, browsers contract dynamic UA UI elements as you scroll down a page. These elements can be expanded again as you scroll up. The Layout Viewport follows the size. See visualizations above. 77 | 78 | ### Effect of scrollbars 79 | 80 | When operating systems use [Classic Scrollbars](./scrolling.md#classic-scrollbars), the Layout Viewport shrinks. [Overlay Scrollbars](./scrolling.md#overlay-scrollbars) have no effect on the size of the Layout Viewport. 81 | 82 | ![Illustration](./illustrations/layout-viewport-desktop-content-long--classic-scrollbar.png) 83 | 84 | ### Effect of pinch-zoom 85 | 86 | When pinch-zooming in the Layout Viewport does not get resized. This is both the case on Desktop and on Mobile. 87 | 88 | ![Illustration](./illustrations/visual-viewport-desktop--pinch-zoomed-in-1.png) 89 | 90 | ### Effect of the Virtual Keyboard 91 | 92 | See [Virtual Keyboard: Findings](./virtual-keyboard.md#findings). 93 | 94 | ### Effect of Overscrolling / Bouncy Scroll 95 | 96 | When [overscrolling](./scrolling.md#overscrolling-and-rubber-banding), some browsers move the Visual Viewport as it bounces while others do not. A [recent CSSWG Resolution](https://github.com/w3c/csswg-drafts/issues/6299#ref-commit-617c50c) explicitly disallows this behavior: 97 | 98 | > If an element uses [fixed positioning](#relation-to-position-fixed) and is positioned relative to the [initial containing block](./icb.md), or is a sticky positioned element which is currently stuck to the viewport, then when the root scroller experiences "overscroll", that element must not overscroll with the rest of the document’s content; it must instead remain positioned as if the scroller was at its minimum/maximum scroll position, whichever it will return to when the overscroll is finished. 99 | 100 | This behavior newly specced behavior is supported by all WebKit-based browsers on iOS (Safari + Chrome + Edge + Firefox), Firefox on macOS, Chrome on macOS _(with a feature flag)_. 101 | 102 | 👀 See [Recording of Chrome on Desktop](./videos/visual-viewport-desktop-overscroll--chrome.mp4) vs [Recording of Chrome with flag on Desktop](./videos/visual-viewport-desktop-overscroll--chrome-with-flag.mp4) 103 | 104 | This behavior is not supported by Safari on macOS. Android devices do not support overscrolling, so this updated behavior does not affect them. 105 | ### Relation to Viewport Units 106 | 107 | Unlike its name suggest, the [Viewport Units](./viewport-units.md) are not sized in relation to the Layout Viewport. Instead, they are sized in relation to [the ICB](./icb.md). See [Viewport Units](./viewport-units.md) for details. 108 | 109 | ## Issues 110 | 111 | We are tracking issues using [the label `Layout Viewport`](https://github.com/web-platform-tests/interop-2022-viewport/issues?q=is%3Aissue+label%3A%22Layout+Viewport%22) -------------------------------------------------------------------------------- /explainers/screenshots/visual-viewport-mobile-safari-over-pinch-zoom-out-1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/screenshots/visual-viewport-mobile-safari-over-pinch-zoom-out-1.jpeg -------------------------------------------------------------------------------- /explainers/screenshots/visual-viewport-mobile-safari-over-pinch-zoom-out-2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/screenshots/visual-viewport-mobile-safari-over-pinch-zoom-out-2.jpeg -------------------------------------------------------------------------------- /explainers/scrolling.md: -------------------------------------------------------------------------------- 1 | # Scrolling 2 | 3 | ## Scrollbars 4 | 5 | As [per CSS2 spec](https://drafts.csswg.org/css2/#viewport): 6 | 7 | > When [the viewport](./layout-viewport.md) is smaller than the area of the canvas on which the document is rendered, the user agent should offer a scrolling mechanism. 8 | 9 | This scrolling mechanism – most of the time – also comes with scrollbars. There are two types of scrollbars we can distinguish: 10 | 11 | 1. Overlay Scrollbars 12 | 2. Classic Scrollbars 13 | 14 | Controlling the visual styling of these scrollbars is defined in the [CSS Scrollbars Styling Module Level 1](https://w3c.github.io/csswg-drafts/css-scrollbars-1/) spec. 15 | ### Overlay Scrollbars 16 | 17 | Overlay Scrollbars are those iOS/macOS-style scrollbars which are placed over the content. They are not shown by default, but only while the user is scrolling. To keep the content underneath visible they are semi-transparent, but that’s totally up to the user-agent (browser) to determine. While interacting with them, their size may also vary. 18 | 19 | ![Illustration](./illustrations/icb-desktop-content-long.png) 20 | 21 | ### Classic Scrollbars 22 | 23 | Classic Scrollbars are scrollbars that are placed in a dedicated Scrollbar Gutter. The Scrollbar Gutter is the space between the inner Border Edge and the outer Padding Edge. These scrollbars are usually opaque (not transparent) and take away some space from the adjacent content. 24 | 25 | ![Illustration](./illustrations/icb-desktop-content-long--classic-scrollbar.png) 26 | 27 | ## Getting the Scroll Position 28 | 29 | - @TODO: `window.scrollX` / `window.scrollY` 30 | - @TODO: `document.scrollLeft` / `document.scrollTop` 31 | 32 | ## Findings 33 | 34 | 💡 These findings are a textual representation of the [test results table](https://goo.gle/interop-2022-viewport-testresults). 35 | 36 | ### Dynamic User-Agent UI Elements 37 | 38 | @TODO: 39 | ### Overscrolling and Bounce Scroll 40 | 41 | Some browsers support overscrolling the scrollport. When doing this with a swipe gestured, they might bounce back. This behavior is available on macOS, iOS _(and maybe Windows, but that is currently untested)_. Android does not support this. 42 | 43 | Note: When overscrolling slowly at the top edge in Mobile Browsers you might trigger a pull-to-refresh. In this part we are focussing on overscrolling while not triggering pull-to-refresh. 44 | 45 | Overscrolling is [covered in the “CSS Overscroll Behavior Module Level 1” spec](https://drafts.csswg.org/css-overscroll-1/). -------------------------------------------------------------------------------- /explainers/sizing.md: -------------------------------------------------------------------------------- 1 | # Sizing 2 | 3 | There are various ways to try and read the size of the viewport/window. 4 | 5 | ## `window.innerWidth` and `window.innerHeight` 6 | 7 | ### Definition 8 | 9 | `window.innerWidth` and `window.innerHeight` are defined in the [CSSOM View Module](https://drafts.csswg.org/cssom-view/#dom-window-innerwidth): 10 | 11 | > The `innerWidth` attribute must return the viewport[^fn1] width including the size of a rendered scroll bar (if any), or zero if there is no viewport. 12 | > 13 | > The following snippet shows how to obtain the width of the viewport: 14 | > 15 | > ```js 16 | > var viewportWidth = innerWidth 17 | > ``` 18 | > 19 | > The `innerHeight` attribute must return the viewport[^fn1] height including the size of a rendered scroll bar (if any), or zero if there is no viewport. 20 | 21 | [^fn1]: When specs talk about _“the Viewport”_, they mean [the Layout Viewport](./layout-viewport.md). 22 | 23 | ### Findings 24 | 25 | 💡 These findings are a textual representation of the [test results table](https://goo.gle/interop-2022-viewport-testresults). 26 | 27 | In all browsers the `innerHeight` and `innerWidth` behave as defined/expected. 28 | 29 | When pinch-zooming in, WebKit adjusts these values as you pich-zoom in on the page, taking over the `width`/`height` of the [Visual Viewport](./visual-viewport.md) 30 | 31 | ## `window.outerWidth` and `window.outerHeight` 32 | 33 | ### Definition 34 | 35 | `window.outerWidth` and `window.outerHeight` are defined in the [CSSOM View Module](https://drafts.csswg.org/cssom-view/#dom-window-outerwidth): 36 | 37 | > The `outerWidth` attribute must return the width of the client window. If there is no client window this attribute must return zero. 38 | > 39 | > The `outerHeight` attribute must return the height of the client window. If there is no client window this attribute must return zero. 40 | 41 | ### Findings 42 | 43 | 💡 These findings are a textual representation of the [test results table](https://goo.gle/interop-2022-viewport-testresults). 44 | 45 | On Desktop all is pretty straigthforward and the `outerHeight` equals the [`innerHeight`](#windowinnerwidth-and-windowinnerheight) + the size of the browser’s top and bottom bars. 46 | 47 | On Mobile it’s a different story: 48 | 49 | - Only Safari on iOS and Chrome on iOS see the `outerHeight` as the _(unzoomed [^fn1])_ `innerHeight` + size of the browser’s chrome. Essentially this equals the screen’s `height` here, as apps run fullscreen on such devices. 50 | - Most other mobile browsers use the values from `innerHeight` as the value for its `outerHeight`. When the UA UI Toolbars retract, both sizes get adjusted. This seems wrong. 51 | - Firefox on Android does something special where the `outerHeight` is a value somewhere in between the [Small Viewport](./viewport-units.md#the-small-viewport) _(`innerHeight` when UA UI is expanded)_ and [Large Viewport](./viewport-units.md#the-large-viewport) _(`innerHeight` when UA UI is retracted)_. 52 | 53 | [^fn1]: “unzoomed” because `innerHeight` resizes in these browsers. See the section covering [`innerHeight`](#windowinnerwidth-and-windowinnerheight) 54 | 55 | ## `document.documentElement.clientWidth` and `document.documentElement.clientHeight` 56 | 57 | `document.documentElement.clientWidth` and `document.documentElement.clientHeight` can be used to measure the [ICB](./icb.md). This due to [an exception in the definition of `clientWidth`/`clientWidth`](https://www.w3.org/TR/cssom-view-1/#dom-element-clientwidth): 58 | 59 | > If the element is the root element and the element’s node document is not in quirks mode […] return the viewport width/height excluding the size of a rendered scroll bar (if any). 60 | 61 | ## `Element.getBoundingClientRect()` 62 | 63 | If no other way of getting the dimensions of an element is available, [`Element.getBoundingClientRect()`](https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect) can be used. 64 | 65 | > The `getBoundingClientRect()` method, when invoked on an element element, must return the result of getting the bounding box for element. 66 | 67 | Beware that calling this, causes layout _(citation needed)_ -------------------------------------------------------------------------------- /explainers/videos/visual-viewport-desktop-overscroll--chrome-with-flag.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/videos/visual-viewport-desktop-overscroll--chrome-with-flag.mp4 -------------------------------------------------------------------------------- /explainers/videos/visual-viewport-desktop-overscroll--chrome.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/videos/visual-viewport-desktop-overscroll--chrome.mp4 -------------------------------------------------------------------------------- /explainers/videos/visual-viewport-desktop-overscroll--firefox.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/videos/visual-viewport-desktop-overscroll--firefox.mp4 -------------------------------------------------------------------------------- /explainers/videos/visual-viewport-desktop-overscroll--safari--different-behaviors.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/videos/visual-viewport-desktop-overscroll--safari--different-behaviors.mp4 -------------------------------------------------------------------------------- /explainers/videos/visual-viewport-desktop-overscroll--safari.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/videos/visual-viewport-desktop-overscroll--safari.mp4 -------------------------------------------------------------------------------- /explainers/videos/visual-viewport-mobile-over-pinch-zoom-out--safari--different-behaviors.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/videos/visual-viewport-mobile-over-pinch-zoom-out--safari--different-behaviors.mp4 -------------------------------------------------------------------------------- /explainers/videos/visual-viewport-mobile-over-pinch-zoom-out--safari.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/videos/visual-viewport-mobile-over-pinch-zoom-out--safari.mp4 -------------------------------------------------------------------------------- /explainers/videos/visual-viewport-mobile-overscroll--safari--different-behaviors.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/videos/visual-viewport-mobile-overscroll--safari--different-behaviors.mp4 -------------------------------------------------------------------------------- /explainers/videos/visual-viewport-mobile-overscroll--safari.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-platform-tests/interop-2022-viewport/268dbc7a8e6995f5b78bc0e5fbfbf58539449c14/explainers/videos/visual-viewport-mobile-overscroll--safari.mp4 -------------------------------------------------------------------------------- /explainers/viewport-units.md: -------------------------------------------------------------------------------- 1 | # Viewport-percentage Lengths _(aka “Viewport-relative Units”)_ 2 | 3 | ## Definition 4 | 5 | Unlike their name suggest, Viewport Units are **not** based on the size of [the Viewport](./layout-viewport.md) but are based on the size of the [ICB](./icb.md). As per [CSS Values and Units Module Level 4 specification](https://drafts.csswg.org/css-values-4/#viewport-relative-lengths): 6 | 7 | > The viewport-percentage lengths are relative to the size of the initial containing block 8 | 9 | But since [the ICB takes its size from the Layout Viewport](./icb.md#definition) _(for continuous media)_, there is a connection to the Layout Viewport: 10 | 11 | Layout Viewport → Initial Containing Block → Viewport-percentage lengths 12 | 13 | Generally speaking, a Viewport-percentage Length is Equal to 1% of the targeted axis. Its actual pixel value change as the ICB size changes 14 | 15 | > When the height or width of the initial containing block is changed, they are scaled accordingly. 16 | 17 | Taking [the UA-Default Viewport](#ua-default-viewport) as an example, its linked units are: 18 | 19 | - `vw` = 1% of the width of the viewport size 20 | - `vh` = 1% of the height of the viewport size 21 | - `vi` = 1% of the size of the viewport’s inline axis 22 | - `vb` = 1% of the size of the viewport’s block axis 23 | - `vmin` = the smaller of `vw` or `vh` 24 | - `vmax` = the larger of `vw` or `vh` 25 | 26 | ## Visualization 27 | 28 | To visualize the viewport units, we can set an element’s width/height to those units 29 | 30 | ```css 31 | #el { 32 | position: absolute; 33 | top: 0; 34 | left: 0; 35 | width: 100vw; 36 | height: 100vh; 37 | 38 | background: var(--lightblue); 39 | } 40 | ``` 41 | 42 | On desktop _(with no scrollbars present)_, it looks like this: 43 | 44 | ![Illustration](./illustrations/viewport-units-desktop.png) 45 | 46 | On mobile, it looks like this: 47 | 48 | ![Illustration](./illustrations/viewport-units-mobile-vh--uaui-expanded.png) 49 | 50 | To authors this seems weird, but [its behavior is explained below](#size-of-the-ua-default-viewport-on-mobile). 51 | 52 | ## Types of Viewport-percentage Lengths 53 | 54 | Since [the Layout Viewport can change size](./layout-viewport.md#size) as UA UI elements contract or expand, there are different types specific Layout Viewport Sizes that [can be defined](https://drafts.csswg.org/css-values-4/#viewport-relative-lengths). 55 | 56 | ![Illustration](./illustrations/viewport-units-mobile-naming-things.png) 57 | 58 | Each type of specific Layout Viewport has Viewport-percentage Lengths assigned with it. 59 | ### The Large Viewport 60 | 61 | The Large Viewport is the viewport sized assuming any UA UI Elements that are dynamically expanded and retracted to be *retracted*. It has the `l`-prefix, so its linked units are `lvh` / `lvw` / `lvb` / `lvi` / `lvmin` / `lvmax`. 62 | 63 | > The large viewport-percentage units (`lv*`) are defined with respect to the large viewport size: the viewport sized assuming any UA interfaces that are dynamically expanded and retracted to be retracted. This allows authors to size content such that it is guaranteed to fill the viewport, noting that such content might be hidden behind such interfaces when they are expanded. 64 | > 65 | > The sizes of the large viewport-percentage units are fixed (and therefore stable) unless the viewport itself is resized. 66 | 67 | ![Illustration](./illustrations/viewport-units-mobile-svh%2Blvh--with-icb--mobilesafari.png) 68 | 69 | ### The Small Viewport 70 | 71 | The Small Viewport is the viewport sized assuming any UA UI Elements that are dynamically expanded and retracted to be *expanded*. It has the `s`-prefix, so its linked units are `svh` / `svw` / `svb` / `svi` / `svmin` / `svmax`. 72 | 73 | > The small viewport-percentage units (`sv*`) are defined with respect to the small viewport size: the viewport sized assuming any UA interfaces that are dynamically expanded and retracted to be expanded. This allows authors to size content such that it can fit within the viewport even when such interfaces are present, noting that such content might not fill the viewport when such interfaces are retracted. 74 | > 75 | > The sizes of the small viewport-percentage units are fixed (and therefore stable) unless the viewport itself is resized. 76 | 77 | ![Illustration](./illustrations/viewport-units-mobile-svh%2Blvh--with-icb--mobilesafari.png) 78 | 79 | ### The Dynamic Viewport 80 | 81 | The Dynamic Viewport is the viewport sized with dynamic consideration of any UA UI Elements. It will automatically adjust itself in response to UA interface elements being shown or not. It has the `d`-prefix. 82 | 83 | > The dynamic viewport-percentage units (`dv*`) are defined with respect to the dynamic viewport size: the viewport sized with dynamic consideration of any UA interfaces that are dynamically expanded and retracted. This allows authors to size content such that it can exactly fit within the viewport whether or not such interfaces are present. 84 | > 85 | > The sizes of the dynamic viewport-percentage units are not stable even while the viewport itself is unchanged. Using these units can cause content to resize e.g. while the user scrolls the page. Depending on usage, this can be disturbing to the user and/or costly in terms of performance. 86 | > 87 | > The UA is not required to animate the dynamic viewport-percentage units while expanding and retracting any relevant interfaces, and may instead calculate the units as if the relevant interface was fully expanded or retracted during the UI animation. (It is recommended that UAs assume the fully-retracted size for this duration.) 88 | 89 | ![Illustration](./illustrations/viewport-units-mobile-dvh--mobilesafari.png) 90 | 91 | ### UA-default Viewport 92 | 93 | The UA-default Viewport, which for any given document should be equivalent to the large viewport size, small viewport size, or some intermediary size. It has no prefix, so its linked units are `vh` / `vw` / `vb` / `vi` / `vmin` / `vmax`. 94 | 95 | > The UA-default viewport-percentage units (`v*`) are defined with respect to a UA-defined UA-default viewport size, which for any given document should be equivalent to the large viewport size, small viewport size, or some intermediary size. 96 | 97 | ## Findings 98 | 99 | 💡 These findings are a textual representation of the [test results table](https://goo.gle/interop-2022-viewport-testresults). 100 | 101 | ### Effect of scrollbars 102 | 103 | The presence of does not influence the size of the Viewport Units. This per spec: 104 | 105 | > In all cases, scrollbars are assumed not to exist 106 | 107 | When using [Overlay Scrollbars](./scrolling.md#overlay-scrollbars) this is no issue 108 | 109 | ![Illustration](./illustrations/viewport-units-desktop-long-content.png) 110 | 111 | However, when using [Classic Scrollbars](./scrolling.md#classic-scrollbars) this becomes an important note to make, as `100vh` and `100vw` grow “too large”. As a result, the UA also needs to add a horizontal scrollbar. 112 | 113 | ![Illustration](./illustrations/viewport-units-desktop-long-content--classic-scrollbar.png) 114 | 115 | ### Size of non-UA-default Viewport on Desktop 116 | 117 | As all desktop browsers currently do not have dynamic UA UI elements, they – when the units are supported – size the Small and Large Viewport to the size of the UA-default Viewport. 118 | 119 | ### Size of the Small Viewport 120 | 121 | For all mobile browsers, the Small Viewport size follows the size of the ICB. 122 | 123 | ![Illustration](./illustrations/viewport-units-mobile-svh%2Blvh--with-icb--uaui-expanded.png) 124 | 125 | ### Size of the Large Viewport 126 | 127 | For all mobile browsers, the Large Viewport size follows the size of the ICB + size of the dynamic UA UI Elements that have retracted. 128 | 129 | ![Illustration](./illustrations/viewport-units-mobile-svh%2Blvh--with-icb--uaui-retracted.png) 130 | 131 | ### Size of the UA-default Viewport on Mobile 132 | 133 | All mobile browsers seem to agree on sizing the UA-default Viewport to the Large Viewport. This explains that why, when giving an element a height of `100vh`, it gets partially obscured by the dynamic UA UI elements: 134 | 135 | ![Illustration](./illustrations/viewport-units-mobile-vh--uaui-expanded.png) 136 | 137 | ![Illustration](./illustrations/viewport-units-mobile-vh--uaui-retracted.png) 138 | 139 | ### Size of the Dynamic Viewport 140 | 141 | The size of the Dynamic Viewport adapts itself when the UA UI Elements change. 142 | 143 | The value of the `dv*` units are not updated immediately: 144 | 145 | - When slowly scrolling, some browsers only do this after scrolling for a certain distance, while others respond immediately. The value isn’t updated at 60fps, but is throttled, for performance reasons we can assume 146 | - When swiping over the screen and having the document scroll for a large distance, some browsers do not update the value for `dvh` until the scrolling has stopped 147 | 148 | Some browsers allow the Dynamic Viewport grow larger than the Large Viewport. This is the case when users over pinch-zoom out in all browsers on iOS. 149 | 150 | ### Effect of Overscrolling / Bouncy Scroll 151 | 152 | These units follow the size of the [Layout Viewport](./layout-viewport.md). Since the Layout Viewport’s size is not affected by Overscrolling, these units aren’t either. 153 | 154 | ## Issues 155 | 156 | We are tracking issues using [the label `Viewport Units`](https://github.com/web-platform-tests/interop-2022-viewport/issues?q=is%3Aissue+label%3A%22Viewport+Units%22) -------------------------------------------------------------------------------- /explainers/virtual-keyboard-api.md: -------------------------------------------------------------------------------- 1 | # The Virtual Keyboard API 2 | 3 | ## Definition 4 | 5 | With the Virtual Keyboard API, authors can get information about the Virtual Keyboard. This is defined in [the Virtual Keyboard API Specification](https://www.w3.org/TR/virtual-keyboard/) 6 | 7 | > The VirtualKeyboard API provides authors with greater control over the visibility of the Virtual Keyboard, and greater ability to adapt the layout of web pages when VK visibility changes 8 | 9 | ## Using the API 10 | 11 | Authors can listen for a `geometrychange` event to get the Virtual Keyboard size. 12 | 13 | ```js 14 | if ("virtualKeyboard" in navigator) { 15 | navigator.virtualKeyboard.addEventListener('geometrychange', () => { 16 | console.log(vk.boundingRect); 17 | }); 18 | } 19 | ``` 20 | 21 | Additionally they can also trigger it to show or hide. 22 | 23 | ## Overlays Content Mode 24 | 25 | Using the Virtual Keyboard API, authors can change the behavior of the Virtual Keyboard. The only option they have now, is to enable “Overlays Content” mode. This behavior needs to be enabled through JavaScript: 26 | 27 | ```js 28 | if ("virtualKeyboard" in navigator) { 29 | navigator.virtualKeyboard.overlaysContent = true; 30 | } 31 | ``` 32 | 33 | Once enabled, this mode does not resize the [Layout Viewport](./layout-viewport.md), [ICB](./icb.md), nor [Visual Viewport](./visual-viewport.md) when the [Virtual Keyboard](./virtual-keyboard.md) is shown. 34 | 35 | ![Illustration](./illustrations/virtual-keyboard-api-overlayscontent.png) 36 | 37 | ## Browser Support 38 | 39 | The Virtual Keyboard API is only implemented in Chromium-based browsers. Other browser vendors have been asked on their position towards this API: 40 | 41 | - Firefox: [https://github.com/mozilla/standards-positions/issues/531](https://github.com/mozilla/standards-positions/issues/531) 42 | - WebKit: [https://github.com/WebKit/standards-positions/issues/16](https://github.com/WebKit/standards-positions/issues/16) -------------------------------------------------------------------------------- /explainers/virtual-keyboard.md: -------------------------------------------------------------------------------- 1 | # Virtual Keyboard 2 | 3 | ## Definition 4 | 5 | Devices with a touchscreen mostly offer a On-Screen Keyboard also known as the Virtual Keyboard. 6 | 7 | ![Illustration](./illustrations/virtual-keyboard-input-nofocus.png) 8 | 9 | ![Illustration](./illustrations/virtual-keyboard-input-focus.png) 10 | 11 | [The Virtual Keyboard API Specification](https://www.w3.org/TR/virtual-keyboard/) has a section how the Virtual Keyboard affects a browser 12 | 13 | > The Virtual Keyboard (VK) is the on-screen keyboard used for input in scenarios where a hardware keyboard may not be available. 14 | > 15 | > User agents respond to the presence of the VK, without any exposure of this information to web developers in the following way: 16 | > 17 | > 1. Repositioning the user agent above the VK 18 | > 2. Reducing the size of the [layout viewport](./layout-viewport.md) so the VK doesn't occlude it[^fn1] 19 | > 3. Reducing the size of the [visual viewport](./visual-viewport.md) and padding the layout viewport to ensure it can be shifted above the VK[^fn2] 20 | 21 | [^fn1]: This is the current behavior of Chrome on Android 22 | [^fn2]: This is the current behavior of Safari on iOS 23 | 24 | 💡 The [Virtual Keyboard API](./virtual-keyboard-api.md) adds a fourth option to this list of behaviors. 25 | 26 | ## Findings 27 | 28 | 💡 These findings are a textual representation of the [test results table](https://goo.gle/interop-2022-viewport-testresults). 29 | 30 | ### Effect on the Layout Viewport 31 | 32 | The effect of the [Virtual Keyboard](./virtual-keyboard.md) on the [Layout Viewport](./layout-viewport.md) is currently not interoperable, as _some_ browsers resize the Layout Viewport when the Virtual Keyboard gets shown, while others do not resize it. 33 | 34 | ![Illustration](./illustrations/virtual-keyboard-input-focus--with-layout-viewport-1.png) 35 | 36 | Because [`position: fixed` elements are laid out against the Layout Viewport](./layout-viewport.md#relation-to-position-fixed), that might mean that some of those elements get obscured by the keyboard 37 | 38 | ![Illustration](./illustrations/virtual-keyboard-input-focus--with-layout-viewport-2.png) 39 | 40 | This latter behavior might be troublesome if authors want to keep an element positioned against the bottom edge, above the Virtual Keyboard when present. 41 | 42 | 💡 The [Virtual Keyboard API](./virtual-keyboard-api.md) has an option to opt-out of resizing the Layout Viewport in case the Virtual Keyboard is present. 43 | ### Effect on the ICB 44 | 45 | Because the ICB is based on the size of the [Layout Viewport](./layout-viewport.md), browsers that resize the Layout Viewport also resize the ICB. 46 | 47 | ![Illustration](./illustrations/virtual-keyboard-input-focus--with-icb.png) 48 | 49 | This difference in resize-behavior has an effect on [Viewport Units](./viewport-units.md) 50 | 51 | ![Illustration](./illustrations/virtual-keyboard-input-focus--with-icb--with-viewport-units.png) 52 | 53 | ### Effect on the Visual Viewport 54 | 55 | The Visual Viewport resizes the the Virtual Keyboard gets shown. This is consistent across Mobile Browsers. 56 | 57 | ![Illustration](./illustrations/virtual-keyboard-input-focus--with-everything.png) 58 | 59 | ### Auto-scrolling input elements into view 60 | 61 | When an element that might get obscured by the Virtual Keyboard gains focus, some browsers auto-offset the [Layout Viewport](./layout-viewport.md) so that the input remains in view. 62 | 63 | ![Illustration](./illustrations/virtual-keyboard-input-focus--with-everything---offset-layout-viewport.png) 64 | 65 | ## Issues 66 | 67 | We are tracking issues using [the label `Virtual Keyboard`](https://github.com/web-platform-tests/interop-2022-viewport/issues?q=is%3Aissue+label%3A%22Virtual+Keyboard%22) -------------------------------------------------------------------------------- /explainers/visual-viewport.md: -------------------------------------------------------------------------------- 1 | # The Visual Viewport 2 | 3 | ## Definition 4 | 5 | From [the CSSOM Specification](https://drafts.csswg.org/cssom-view/#visual-viewport) 6 | 7 | > The visual viewport is a kind of viewport whose scrolling area is another viewport, called the layout viewport. 8 | > 9 | > In addition to scrolling, the visual viewport may also apply a scale transform to its layout viewport. This transform is applied to the canvas of the layout viewport and does not affect its internal coordinate space. 10 | 11 | From [MDN](https://developer.mozilla.org/en-US/docs/Glossary/Visual_Viewport): 12 | 13 | > The portion of the viewport that is currently visible is called the visual viewport 14 | > 15 | > The Visual Viewport can be smaller than the layout viewport, such as when the user has pinched-zoomed 16 | 17 | ## Visualization 18 | 19 | To visualize it, authors can get sizing and positioning info of the Visual Viewport through the [Visual Viewport API](https://drafts.csswg.org/cssom-view/#the-visualviewport-interface) 20 | 21 | ```js 22 | let { 23 | width, 24 | height, 25 | scale, 26 | offsetTop, 27 | offsetLeft, 28 | pageLeft, 29 | pageTop, 30 | } = window.visualViewport; 31 | ``` 32 | 33 | These values are: 34 | 35 | - `width` / `height` = Width and Height of the Visual Viewport 36 | - `offsetTop` / `offsetLeft` = Distance from edges of the Visual Viewport to edges of the [Layout Viewport](./layout-viewport.md) 37 | - `pageTop` / `pageLeft` = Distance from edges of the Visual Viewport to edges of the [ICB](./icb.md) 38 | - `scale` = The scale factor _(default: `1`)_ 39 | 40 | Using a tab more JS, these values can be synced to Custom Properties which you can use to position an element that outlines this Visual Viewport 41 | 42 | ```js 43 | document.documentElement.style.setProperty('--vvw', `${vvv.width}px`); 44 | document.documentElement.style.setProperty('--vvh', `${vvv.height}px`); 45 | document.documentElement.style.setProperty('--vvpt', `${vvv.pageTop}px`); 46 | document.documentElement.style.setProperty('--vvpl', `${vvv.pageLeft}px`); 47 | document.documentElement.style.setProperty('--vvot', `${vvv.offsetTop}px`); 48 | document.documentElement.style.setProperty('--vvol', `${vvv.offsetLeft}px`); 49 | document.documentElement.style.setProperty('--vvz', vvv.scale); 50 | ``` 51 | 52 | Depending on wether you are using `position: absolute` or `position: fixed`, you need to use these values differently: 53 | 54 | ```css 55 | #visualviewport { 56 | box-sizing: border-box; 57 | border: 8px solid; 58 | border-image: repeating-linear-gradient(45deg, orange, orange 10px, transparent 10px, transparent 20px) 10; 59 | } 60 | 61 | #visualviewport { 62 | position: absolute; 63 | top: var(--vvpt, 0px); 64 | left: var(--vvpl, 0px); 65 | width: var(--vvw, 100%); 66 | height: var(--vvh, 100vh); 67 | } 68 | ``` 69 | 70 | ```css 71 | #visualviewport { 72 | box-sizing: border-box; 73 | border: 8px solid; 74 | border-image: repeating-linear-gradient(45deg, orange, orange 10px, transparent 10px, transparent 20px) 10; 75 | } 76 | 77 | #visualviewport { 78 | position: fixed; 79 | top: var(--vvot, 0px); 80 | left: var(--vvol, 0px); 81 | width: var(--vvw, 100%); 82 | height: var(--vvh, 100vh); 83 | right: var(--vvol, 0px) + var(--vvw, 0px); 84 | bottom: var(--vvol, 0px) + var(--vvh, 0px); 85 | } 86 | ``` 87 | 88 | 👉 Try it out: [Visual Viewport](https://interop-2022-viewport.netlify.app/individual/visual-viewport/) 89 | 90 | ## Findings 91 | 92 | 💡 These findings are a textual representation of the [test results table](https://goo.gle/interop-2022-viewport-testresults). 93 | 94 | ## Size 95 | 96 | As mentioned above, the Visual Viewport API can be used for this. Note that the size of Visual Viewport will be equal to or less than the [Layout Viewport](./layout-viewport.md) 97 | 98 | ![Illustration](./illustrations/visual-viewport-desktop.png) 99 | ![Illustration](./illustrations/visual-viewport-mobile--uaui-expanded.png) 100 | ![Illustration](./illustrations/visual-viewport-mobile--uaui-retracted.png) 101 | 102 | ### Effect of scrolling 103 | 104 | The Visual Viewport updates nicely as you scroll and shifts down with the scroll position. 105 | 106 | ![Illustration](./illustrations/visual-viewport-desktop.png) 107 | 108 | When UA UI Elements expand/contract is also updates accordingly, except in Edge on Android where there’s a few pixels missing. This bug is corrected as soon as you stop the gesture _(i.e. lift up your finger)_, then the correct values are flushed. 109 | 110 | ![Illustration](./illustrations/visual-viewport-mobile--uaui-expanded.png) 111 | ![Illustration](./illustrations/visual-viewport-mobile--uaui-retracted.png) 112 | 113 | ### Effect of scrollbars 114 | 115 | The presence of [Classic Scrollbars](./scrolling.md#classic-scrollbars) take away space from the Visual Viewport 116 | 117 | ### Effect of pinch-zoom 118 | 119 | The Visual Viewport updates nicely as you pinch zoom 120 | 121 | ![Illustration](./illustrations/visual-viewport-desktop--pinch-zoomed-in-2.png) 122 | 123 | In Safari on Desktop the values do not update immediately as you pinch-zoom, but are only updated when the gesture is finished _(i.e. lifting up your fingers)_. 124 | 125 | When you over pinch-zooming out in browsers that allow this _(i.e. all based on WebKit)_ something funky happens with the position and dimensions of the Visual Viewport: 126 | 127 | - Its position gets anchored to `0,0` of the canvas. This origin moves between the top-left edge of the [Large and Small Viewport](./viewport-units.md#the-large-viewport) as you move around. 128 | - The `width`/`height` is measured from that `0,0` origin _(which can move)_ to the right/bottom edge of the [Layout Viewport](./layout-viewport.md) 129 | 130 | 👀 See [Screenshot 1 of Safari on iOS](./screenshots/visual-viewport-mobile-safari-over-pinch-zoom-out-1.jpeg) and [Screenshot 2 of Safari on iOS](./screenshots/visual-viewport-mobile-safari-over-pinch-zoom-out-2.jpeg) 131 | 132 | 👀 See [Recording of Safari on iOS](./videos/visual-viewport-mobile-over-pinch-zoom-out--safari.mp4) 133 | 134 | ### Effect of the Virtual Keyboard 135 | 136 | See [Virtual Keyboard: Findings](./virtual-keyboard.md#findings). 137 | 138 | ### Effect of Overscrolling / Bouncy Scroll 139 | 140 | When [overscrolling](./scrolling.md#overscrolling-and-rubber-banding), WebKit allows negative values for `window.scrollX` and `window.scrollY`. This is the only engine to expose this. While doing so, the values for the Visual Viewport’s `pageTop` and `pageLeft` also become negative, while its `height` and `width` remain the same size. Because of this, the visualization of the Visual Viewport can get clipped by the [ICB](./icb.md) which [does bounce with the rubber banding effect](./icb.md#effect-of-overscrolling--bouncy-scroll) 141 | 142 | 👀 See [Recording of Safari on Desktop](./videos/visual-viewport-desktop-overscroll--safari.mp4) and [Recording of Safari on iOS](./videos/visual-viewport-mobile-overscroll--safari.mp4) 143 | 144 | Other engines that do not allow negative values for `window.scrollX` and `window.scrollY` as it has a rubber banding effect going on – i.e. Chrome on macOS and Firefox on macOS – follow the same behavior as the [ICB](./icb.md): the bounce with the effect. The values for the `height` and `width` remain the same as it rubber bands. 145 | 146 | 👀 See [Recording of Firefox on Desktop](./videos/visual-viewport-desktop-overscroll--firefox.mp4) and [Recording of Chrome on Desktop](./videos/visual-viewport-desktop-overscroll--chrome.mp4) 147 | 148 | In engines that keep the [Layout Viewport](./layout-viewport.md) in place as the browser rubber bands the values for `offsetTop` and `offsetLeft` remain `0`. 149 | 150 | 👀 See [Recording of Firefox on Desktop](./videos/visual-viewport-desktop-overscroll--firefox.mp4) and [Recording of Chrome with flag on Desktop](./videos/visual-viewport-desktop-overscroll--chrome-with-flag.mp4) 151 | 152 | ## Issues 153 | 154 | We are tracking issues using [the label `Visual Viewport`](https://github.com/web-platform-tests/interop-2022-viewport/issues?q=is%3Aissue+label%3A%22Visual+Viewport%22) --------------------------------------------------------------------------------