npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

viewport-units-fix

v1.0.0

Published

Reliable vh/vw on mobile. Injects --vh and --vw CSS variables that match the real visible viewport, updated on resize and orientation change. Zero dependencies.

Readme

📐 viewport-units-fix

Reliable vh/vw on mobile. Injects --vh and --vw CSS variables that match the real visible viewport, updated on resize and orientation change. Zero dependencies.

npm version npm downloads bundle size license TypeScript zero dependencies

Live Demo →


Why

100vh on mobile Safari doesn't mean what you think. The browser's address bar and toolbar are part of the viewport, so a 100vh element extends behind the chrome — clipped and broken. It's one of the most-upvoted CSS issues on StackOverflow. In 2025, dvh exists but support is incomplete and its behaviour during scroll transitions is inconsistent.

Developers resort to CSS hacks, window.innerHeight calculations, or scattered one-off solutions that break in specific orientations, don't update on resize, or fail during SSR. None of them are drop-in.

viewport-units-fix makes this stop being a problem. One import, four CSS variables (--vh, --vw, --dvh, --dvw) that are always correct. Powered by visualViewport with innerHeight fallback, debounced updates, SSR safe. ~0.7kB gzipped.


Install

npm install viewport-units-fix
# or
yarn add viewport-units-fix
# or
pnpm add viewport-units-fix

Quick Start

Set it and forget it

import ViewportUnitsFix from 'viewport-units-fix';

new ViewportUnitsFix();

Now use the CSS variables anywhere:

.hero {
  height: calc(var(--vh) * 100); /* true full viewport */
}

.sidebar {
  height: var(--dvh); /* also full viewport, as a single value */
}

With options

import ViewportUnitsFix from 'viewport-units-fix';

const vf = new ViewportUnitsFix({
  prefix: 'app-',       // sets --app-vh, --app-vw, etc.
  debounce: 50,          // update speed in ms
  variables: ['vh'],     // only inject --vh
  onUpdate: (values) => {
    console.log(values.dvh); // full visible height in px
  },
});

Features

  • Zero dependencies — pure TypeScript, no external packages
  • Four CSS variables--vh, --vw (1% units) and --dvh, --dvw (full pixel values)
  • visualViewport API — uses the modern window.visualViewport for accurate measurements, falls back to innerHeight/innerWidth
  • Debounced updates — configurable delay (default 100ms) so resize floods don't thrash the DOM
  • Orientation change — handles portrait/landscape transitions automatically
  • iOS Safari aware — accounts for the address bar and safe area on real devices
  • Custom prefix — namespace your variables with prefix: 'app-' to avoid conflicts
  • Variable filtering — only inject the ones you need with the variables option
  • Flexible targeting — set variables on documentElement, a specific element, or a CSS selector
  • onUpdate callback — react to viewport changes in JS, not just CSS
  • SSR safe — guards all DOM access behind typeof window
  • ~0.7kB minified + gzipped

API

new ViewportUnitsFix(options?)

| Option | Type | Default | Description | |--------|------|---------|-------------| | variables | ViewportVariable[] | all | Which CSS variables to inject: 'vh', 'vw', 'dvh', 'dvw' | | prefix | string | '' | Prefix for CSS variable names (e.g. 'app-'--app-vh) | | debounce | number | 100 | Debounce delay in milliseconds; set to 0 for immediate updates | | target | HTMLElement \| string | document.documentElement | Element or CSS selector to set CSS variables on | | onUpdate | (values: ViewportValues) => void | — | Called after each measurement update |

ViewportValues

| Property | Type | Description | |----------|------|-------------| | vh | number | 1% of the visible viewport height | | vw | number | 1% of the visible viewport width | | dvh | number | Full visible viewport height in pixels | | dvw | number | Full visible viewport width in pixels |

CSS Variables

| Variable | Value | CSS Usage | |----------|-------|-----------| | --vh | {height/100}px | height: calc(var(--vh) * 100) | | --vw | {width/100}px | width: calc(var(--vw) * 100) | | --dvh | {height}px | height: var(--dvh) | | --dvw | {width}px | width: var(--dvw) |

Instance methods

| Method | Returns | Description | |--------|---------|-------------| | .refresh() | void | Force an immediate recalculation and CSS variable update | | .destroy() | void | Remove all event listeners and clean up | | [Symbol.dispose]() | void | Alias for destroy() — enables using syntax |

Instance properties

| Property | Type | Description | |----------|------|-------------| | .values | ViewportValues | Read-only snapshot of the current measurements |


Examples

React hook

import { useEffect } from 'react';
import ViewportUnitsFix from 'viewport-units-fix';

function useViewportUnits() {
  useEffect(() => {
    const vf = new ViewportUnitsFix();
    return () => vf.destroy();
  }, []);
}

// In your app
function App() {
  useViewportUnits();
  return <div style={{ height: 'calc(var(--vh) * 100)' }}>Full screen</div>;
}

Vue composable

import { onMounted, onUnmounted } from 'vue';
import ViewportUnitsFix from 'viewport-units-fix';

export function useViewportUnits() {
  let vf: ViewportUnitsFix;

  onMounted(() => {
    vf = new ViewportUnitsFix();
  });

  onUnmounted(() => vf?.destroy());
}

Scoped to a container

import ViewportUnitsFix from 'viewport-units-fix';

const vf = new ViewportUnitsFix({
  target: '#app-shell',
  prefix: 'app-',
});
#app-shell .hero {
  height: calc(var(--app-vh) * 100);
}

CDN (no build step)

<script type="module">
  import ViewportUnitsFix from 'https://esm.sh/viewport-units-fix';

  new ViewportUnitsFix();
</script>

<style>
  .hero {
    height: calc(var(--vh) * 100);
  }
</style>

License

MIT