Skip to content

Instantly share code, notes, and snippets.

@getify
Last active October 20, 2024 20:10
Show Gist options
  • Save getify/150ea5a3b30b8822dee7798883d120b9 to your computer and use it in GitHub Desktop.
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.
<!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>
: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;
}
"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);
}
})();
@privatenumber
Copy link

I love it. Very elegant work around! Have you considered packaging this on to npm?

@CUBICinfinity
Copy link

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