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

overlay-scrollbar

v0.1.6

Published

Dependency-free custom scrollbar (drag, touch, auto-hide) — modern replacement for slim-scroll

Readme

overlay-scrollbar

TypeScript, ~3 KB gzipped, Drag-to-scroll, Touch (nativ), Auto-Hide, programmatisches Scrollen.

Installation

npm install overlay-scrollbar

Verwendung

import { OverlayScrollbar } from "overlay-scrollbar";

const instance = new OverlayScrollbar("#my-list", {
  width: 8,
  color: "#8f8f8f",
  alwaysVisible: false,
  position: "right", // oder "left"
});

instance.scrollTo(0, "smooth");
instance.refresh();  // nach externen DOM-Änderungen, die die Observer nicht mitbekommen
instance.destroy();  // Element in Originalzustand zurückversetzen

Für mehrere Elemente auf einmal (analog zum alten $('.el').slimScroll()):

import { overlayScrollbar } from "overlay-scrollbar";
overlayScrollbar(".scrollable", { alwaysVisible: true });

Optionen

| Option | Default | Beschreibung | | --- | --- | --- | | width | 8 | Breite des Thumbs in px | | color | #8f8f8f | Thumb-Farbe | | opacity | 0.85 | Thumb-Opazität, während sichtbar | | alwaysVisible | false | Nie ausblenden | | distance | 2 | Abstand zum Rand in px | | railVisible | true | Hintergrund-Rail anzeigen | | railColor / railOpacity | #000000 / 0.06 | Rail-Farbe/-Opazität | | minBarHeight | 24 | Mindesthöhe des Thumbs in px | | fadeDelay / fadeDuration | 900 / 200 | Ausblend-Verzögerung/-Dauer in ms | | borderRadius | 6 | Thumb-Eckenradius in px | | position | "right" | "left" oder "right" | | allowPageScroll | false | Scroll-Chaining zur Seite erlauben | | onScroll | – | Callback { scrollTop, scrollHeight, clientHeight } |

Funktionsweise: Das Zielelement (el) behält seine Position und Größe im Layout (Flex/Grid/%/vh — was auch immer deine CSS vorgibt) exakt wie zuvor; nur sein Inhalt wandert in einen inneren, nativ scrollbaren Viewport-Container, dessen native Scrollbar optisch versteckt wird. Ein Thumb/Rail-Overlay darin spiegelt Scroll-Position und -Größe.

React / Vue / Angular / andere Frameworks mit eigenem DOM-Reconciler

new OverlayScrollbar(el) verschiebt die Kind-Elemente von el in einen selbst erzeugten Viewport-Container. Das funktioniert nicht zuverlässig, wenn ein Framework diese Kinder selbst verwaltet (z. B. React bei bedingtem Rendering, Routen-Wechseln, Listen mit wechselnden Keys) — das Framework versucht dann irgendwann, einen Knoten aus dem Elternteil zu entfernen, den es zu kennen glaubt, der aber tatsächlich woanders (im injizierten Viewport) sitzt. Das äußert sich z. B. bei React als:

NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.

Für solche Fälle: viewport/rail/bar selbst als JSX rendern und der Library per Ref übergeben, statt sie automatisch bauen zu lassen — dann fasst die Library keine vom Framework verwalteten Kind-Knoten an.

import { useEffect, useRef } from "react";
import { OverlayScrollbar } from "overlay-scrollbar";

function ScrollableList({ children }: { children: React.ReactNode }) {
  const hostRef = useRef<HTMLDivElement>(null);
  const viewportRef = useRef<HTMLDivElement>(null);
  const railRef = useRef<HTMLDivElement>(null);
  const barRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!hostRef.current || !viewportRef.current || !railRef.current || !barRef.current) return;
    const scrollbar = new OverlayScrollbar({
      host: hostRef.current,
      viewport: viewportRef.current,
      rail: railRef.current,
      bar: barRef.current,
    });
    return () => scrollbar.destroy();
  }, []);

  return (
    <div ref={hostRef} style={{ position: "relative", height: "100%" }}>
      <div ref={viewportRef}>{children}</div>
      <div ref={railRef} />
      <div ref={barRef} />
    </div>
  );
}

host braucht selbst keine overflow/position-Angabe — die Library setzt beides. viewport/rail/bar brauchen keine eigenen Styles, die Library ergänzt ihre CSS-Klassen automatisch.

Lizenz

MIT