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 🙏

© 2024 – Pkg Stats / Ryan Hefner

root-units

v2.3.0

Published

What you really wanted when you asked for viewport units

Downloads

660

Readme

Usage

This:

require('root-units').install();

...enables this:

.Thing {
  width: var(--rw);
  /* ^ 100% of <html> width */
  height: calc(var(--rh) / 2);
  /* ^ 50% of <html> height */
}

...because this:

Root units update as browser is resized

"Wait. Why do I need this?"

Viewport units were an awesome idea. They let us set CSS properties to lengths that are proportion to the viewport's width, height, or the min/max of the two. There's a small caveat that'll trip you up sooner or later though : they don't play nicely with scroll bars.

You see, we never know when scroll bars are going to appear, or what form they'll take. On many touch/hybrid devices (and some desktop devices, dependant on settings), scroll bars appear as little transparent pills that float up and down the side of the page, above the content. In this case, 100vw is the same as the root element's clientWidth value, so we can be sure that it will fill 100% of the client width. However, some scroll bars appear inside the viewport, reducing the root element's width/height by ~15px (on average), but viewport units don't take the reduced client dimensions into account. If you hadn't noticed that before, jump to the beginning of this README and have a look at those distracting GIFs for a moment. I'll wait.

Did you see that? the first example has the floaty scroll bar, and everything is as cool as. The second example highlights the problem I'm trying to solve. Read on for how I actually did that...

How it works

When you call .install(), the following happens*:

  • The CSS custom properties --rw and --rh are added to the root element (document.documentElement) matching its clientWidth and clientHeight values.
  • Event handlers are attached to window's resize and orientationchange events, which takes another measurement of the root element's width & height.
  • If either value has changed since the last measurement, we update the CSS custom properties.

"…but #perfmatters!", I hear you cry…

Worry not: if the root element's size changes, further events are ignored until the CSS custom properties have been updated. This results in as few DOM reads/writes as possible. Even more importantly, measurements and mutations follow a simple asynchronous scheduling pattern which you can control. There's not much point in you taking the time to optimise DOM reads/writes in your app, then drop in this little utility and cause a bunch of unwanted layout thrashing.

*How it actually works

  • Event handlers are attached to window's resize and orientationchange events, which can schedule a measurement of the root element's dimensions. By default, this is posponed to the next render frame (using requestAnimationFrame).
  • If the dimensions have changed since the last measurement, a mutation is scheduled, which will update the CSS custom properties. By default, this happens in the same frame as the measurement.
  • A measurement is manually scheduled, which allows the initial CSS custom properties to be created.

If you want to take full control of this scheduling, you can provide your own functions as options. They'll be called with a single argument (either the measureTask or mutateTask function), letting you manually run those tasks when it best suits your app's architecture:

require('root-units')
.install({
  measure: window.requestAnimationFrame,
  mutate: mutateTask => mutateTask()
});

The above example shows the default behaviour, but you're free to replace that with whatever strategy suits you. Maybe you want to also run measureTask in the same tick of the event loop:

require('root-units')
.install({
  measure: measureTask => measureTask()
});

...or appoint a purpose-built library such as fastdom:

const fastdom = require('fastdom');

require('root-units')
.install({
  measure: fastdom.measure.bind(fastdom),
  mutate: fastdom.mutate.bind(fastdom)
});

Find what works best for you!

Try it out

  1. Clone this repo, 2) install the development server and 3) fire it up:
git clone [email protected]:colingourlay/root-units.git
cd root-units
npm install && npm start
  1. Open 🔗 localhost:8080 and start poking around your dev tools.

To switch between examples, which demonstrate alternative scheduling strategies, (un)comment the relevant sections in example.js