Last active
October 20, 2024 20:10
-
-
Save getify/150ea5a3b30b8822dee7798883d120b9 to your computer and use it in GitHub Desktop.
Ever noticed how vw/vh units in CSS seem to be a bit unreliable on various devices (especially mobile)? Here's my solution.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"> | |
<title>Test Page</title> | |
<script> | |
// early compute the vw/vh units more reliably than CSS does itself | |
computeViewportDimensions(); | |
</script> | |
<link rel="stylesheet" href="2.css" /> | |
</head> | |
<body> | |
<h1> Test Page</h1> | |
<p>Some test content.</p> | |
<script src="3.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
:root { | |
/* initial CSS computation values, that are overriden by the JS */ | |
--vw-unit: 1vw; | |
--vh-unit: 1vh; | |
} | |
h1 { | |
/* make the h1 50% of the width of the viewport */ | |
width: calc(50 * var(--vw-unit)); /* instead of '50vw' */ | |
/* make the h1 80% of the height of the viewport */ | |
height: calc(80 * var(--vh-unit)); /* instead of '80vh' */ | |
background-color: blue; | |
color: white; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
function computeViewportDimensions() { | |
if (document.documentElement && document.documentElement.style && document.documentElement.style.setProperty) { | |
document.documentElement.style.setProperty( | |
"--vw-unit", | |
(document.documentElement.clientWidth / 100).toFixed(1) + "px" | |
); | |
document.documentElement.style.setProperty( | |
"--vh-unit", | |
(document.documentElement.clientHeight / 100).toFixed(1) + "px" | |
); | |
} | |
} | |
(function listenForViewportChanges(){ | |
// keep the CSS vw-unit/vh-unit CSS variables updated as the viewport changes size (or orientation!) | |
window.addEventListener("resize",computeViewportDimensions,false); | |
// work-arounds for browsers that don't fire "resize" when the orientation changes | |
// ref: https://developer.mozilla.org/en-US/docs/Web/API/ScreenOrientation/onchange | |
if (typeof window.screen != "undefined" && typeof window.screen.orientation != "undefined") { | |
window.screen.orientation.addEventListener("change",computeViewportDimensions,false); | |
} | |
// ref: https://www.reddit.com/r/javascript/comments/lttxdy/js_workaround_for_fixing_how_css_vwvh_units_arent/gp61ghe/ | |
// ref: https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList/matches | |
else if (typeof window.matchMedia != "undefined") { | |
var query = window.matchMedia("(orientation: landscape)"); | |
// handle variances in the event handling in various older browsers | |
if (typeof query.addEventListener != "undefined") { | |
query.addEventListener("change",computeViewportDimensions,false); | |
} | |
else if (typeof query.addListener != "undefined") { | |
query.addListener(computeViewportDimensions); | |
} | |
else { | |
query.onchange = computeViewportDimensions; | |
} | |
} | |
// make sure nothing during HTML parsing invalidated the early | |
// computation (from the <script> embed) | |
// | |
// has the DOM already loaded? | |
if (document.readyState != "loading") { | |
computeViewportDimensions(); | |
} | |
// otherwise, assume we can listen for the future DOM-ready event | |
else { | |
document.addEventListener("DOMContentLoaded",computeViewportDimensions,false); | |
} | |
})(); |
Find: (?<!-)(\d+)(v(w|h))
Replace: ($1*var(--$2-unit))
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I love it. Very elegant work around! Have you considered packaging this on to npm?