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

@gavinlees/dom-minimap

v0.4.1

Published

A true-render minimap for HTML documents: scales a live DOM clone instead of drawing a canvas approximation, so SVG, math, and highlighted code stay pixel-faithful in miniature.

Readme

dom-minimap

A true-render minimap for HTML documents. Live demo →

Existing HTML minimap libraries (pagemap, minimap.js) draw a canvas approximation of the page: each element becomes a grey rectangle. That works for plain prose, but SVG diagrams, math markup, images, and syntax-highlighted code all degrade to indistinguishable boxes.

dom-minimap takes the other approach: it clones the live DOM and scales the clone down with a CSS transform. The browser's own renderer draws the miniature, so anything the engine can render — Mermaid diagrams, KaTeX equations, Prism-highlighted code, inline images — appears pixel-faithful at minimap scale. No dependencies, one file, works in any modern browser.

Install

npm install @gavinlees/dom-minimap

Or just copy src/dom-minimap.js — it's a single dependency-free UMD file.

<script src="dom-minimap.js"></script>

Usage

var mm = DomMinimap.create({ content: document.body, width: 88 });

mm.refresh();          // call after the content changes
mm.setEnabled(false);  // hide (and stop updating); true to show again
mm.destroy();          // remove from the DOM and detach all listeners
mm.element;            // the minimap strip's root element

The minimap renders as a fixed strip on the right edge of the viewport. Click or drag to scroll; the mouse wheel scrolls the document too.

Options

All options are optional.

| Option | Default | Description | | ------------------ | --------------------------- | ----------- | | content | document.body | Element to miniaturize. | | host | document.documentElement | Element the strip is appended to. The default survives wholesale replacement of body.innerHTML. | | scrollElement | document.scrollingElement | Element that scrolls the content. Set this when content lives inside a fixed-height overflow: auto container instead of scrolling the page itself. | | width | 88 | Strip width in px. | | zIndex | 2147483000 | Strip z-index. | | background | 'transparent' | Strip background. | | borderColor | rgba(128, 128, 128, 0.25) | Left border of the strip. | | sliderColor | rgba(128, 128, 128, 0.20) | Viewport slider fill. | | sliderHoverColor | rgba(128, 128, 128, 0.32) | Slider fill while dragging. |

Responsive width

The strip's width option only sets its initial size — the effective width used for scaling is read live from the rendered element, so a host stylesheet can resize the strip with a media query (e.g. narrower on small screens) and the miniature will scale to match on the next refresh:

.dom-minimap { width: 40px !important; }
@media (min-width: 600px) {
  .dom-minimap { width: 88px !important; }
}

While the strip is enabled, create() and setEnabled(true) add a dom-minimap-active class to host (removed on setEnabled(false) and destroy()). Use it to reserve layout space for the strip so it doesn't overlap document content, e.g.:

html.dom-minimap-active { padding-right: 88px; }

Keeping it fresh

  • Window resize and content resizes (via ResizeObserver, so late image loads and font-size changes are caught) trigger an automatic refresh.
  • Scrolling only moves the slider and slides the miniature — no re-clone.
  • After mutating content in a way that doesn't change its size, call mm.refresh() yourself.

Design notes

Two-layer transform. The miniature is positioned by two nested layers: an inner layer carries a static scale() so its raster can be cached, and an outer layer carries the per-scroll translate3d() so scroll updates stay compositor-only. Combining both into one transform makes WebKit re-rasterize the scaled text on every scroll frame, which shows up as visible shimmer. Offsets are snapped to device pixels for the same reason.

Ids are kept, but prefixed. The clone's elements keep their id attributes — stripping them would break SVG content (e.g. Mermaid output) that depends on id-scoped <style> rules and url(#…) references, such as markers, gradients, and clip paths — but each id is rewritten with a prefix unique to the minimap instance, and every reference to it within the clone (url(#…), href/xlink:href, for, aria-labelledby and similar idref attributes, and id selectors inside <style> text) is rewritten to match. So the clone's SVG output still renders correctly, while getElementById, #anchor navigation, and querySelectorAll-based consumer code all see exactly one match for any id in content — the original — regardless of where host is positioned relative to content. <script> elements and nested minimaps are removed from the clone entirely.

Limitations

  • Each refresh clones the content, roughly doubling the DOM node count. Fine for documents; for enormous pages consider content-visibility: auto on the clone or capping its length.
  • Nested-container scrolling is supported via the scrollElement option, but wheel/drag-to-scroll writes to that element's scrollTop are not throttled the way page-level writes historically needed to be (see the note below) — a container has its own scroll/compositor node, so it doesn't share the touch-delivery pipeline the way window.scrollTo() does on iOS WKWebView.
  • On iOS WKWebView, scrolling document.scrollingElement (the default, page-level mode) shares its native UIScrollView with the touch-delivery pipeline: writing to it while a touch is still active can corrupt the clientY WebKit reports for later samples of that same touch, causing jumpy/blanking drags on long pages. Use scrollElement to point at a CSS overflow: auto container instead to avoid this entirely.

Tests

Browser-driven via Playwright (uses your installed Chrome):

npm install
npm test

License

MIT