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

rune-scroller

v4.0.0

Published

Lightweight scroll animations for Svelte 5. Drop-in AOS replacement. 30 animations, zero dependencies.

Readme

⚡ Rune Scroller

Lightweight scroll animations. AOS replacement. Works everywhere.

Built with native IntersectionObserver — zero JS on scroll, GPU-accelerated, ~5.6KB gzipped.

🚀 Open Source by ludoloops at LeLab.dev 📜 Licensed under MIT


🚀 Quick Start

Any framework — Svelte, React, Vue, Angular, Vanilla JS

npm install rune-scroller
import AOS from "rune-scroller/aos";
AOS.init();
<div data-aos="fade-up" data-aos-duration="800">Animated</div>
<div data-aos="zoom-in" data-aos-delay="200">Delayed zoom</div>

That's it. Same API as AOS. Works everywhere.

Svelte (native action)

<script>
	import rs from 'rune-scroller';
</script>

<div use:rs={{ animation: 'fade-up' }}>Animates on scroll</div>

React

import { useEffect } from "react";
### React (not tested — should work)

```jsx
import { useEffect } from 'react';
import AOS from 'rune-scroller/aos';

function App() {
	useEffect(() => { AOS.init(); }, []);
	return (
		<>
			<h1 data-aos="fade-down">Welcome</h1>
			<p data-aos="fade-up" data-aos-delay="200">Subtitle</p>
		</>
	);
}

Vue (not tested — should work)

<script setup>
import { onMounted } from "vue";
import AOS from "rune-scroller/aos";
onMounted(() => AOS.init());
</script>

<template>
  <div data-aos="fade-up">Animated</div>
</template>

Angular (not tested — should work)

// app.component.ts
import { Component, OnInit } from "@angular/core";
import AOS from "rune-scroller/aos";

@Component({ selector: "app-root", templateUrl: "./app.component.html" })
export class AppComponent implements OnInit {
  ngOnInit() {
    AOS.init();
  }
}
<!-- app.component.html -->
<div data-aos="fade-up">Animated</div>

CDN (not tested — should work)

<script type="module">
  import AOS from "https://esm.sh/rune-scroller/aos";
  AOS.init();
</script>

<div data-aos="fade-up">Works without any build step</div>

✨ Features

  • Framework agnostic — Svelte, React, Vue, Angular, Vanilla JS, CDN
  • AOS drop-in — Same data-aos attributes, same init() API
  • Zero dependencies — Pure JS + native IntersectionObserver
  • ~5.6KB gzipped — Smaller than AOS (6.9KB)
  • 37 animations — Fade, Zoom, Flip, Slide, Bounce
  • Zero JS on scroll — Browser handles detection natively
  • TypeScript support — Full type definitions
  • SSR-ready — SvelteKit, Next.js, Nuxt compatible
  • GPU-accelerated — CSS transforms via translate3d
  • Accessible — Respects prefers-reduced-motion
  • No wrapper divs — Your layouts stay intact

AOS vs rune-scroller

| | rune-scroller | AOS | | ------------------------- | ---------------------------------------------- | ------------------------------------------ | | Bundle size (gzipped) | ~5.6KB JS+CSS | ~6.9KB JS+CSS | | Dependencies | 0 | lodash.throttle, lodash.debounce | | Scroll detection | IntersectionObserver (native, C++) | Scroll event + throttle (JS) | | Per-scroll cost | 0 — browser handles it | Iterates ALL elements every 99ms | | Layout reads | 1 per element (init only) | offsetParent loop per element per scroll | | Resize handling | ResizeObserver (native) | debounced scroll recalc | | 100 animated elements | ~0ms per scroll | ~2-5ms per scroll (layout thrashing) | | Animations | 30 | 28 | | Framework | Any (Svelte, React, Vue, Angular, Vanilla) | Vanilla JS only |

The key difference: AOS runs JavaScript on every scroll event for every element. rune-scroller delegates detection to the browser's native IntersectionObserver — zero JS execution until an element actually enters the viewport.


🎨 Available Animations (30)

Fade (10)

  • fade — Simple opacity fade
  • fade-up / fade-down / fade-left / fade-right — Fade + translate
  • fade-up-right / fade-up-left / fade-down-right / fade-down-left — Diagonal fades

Zoom (10)

  • zoom-in / zoom-out — Scale in/out
  • zoom-in-up / zoom-in-down / zoom-in-left / zoom-in-right — Zoom + translate
  • zoom-out-up / zoom-out-down / zoom-out-left / zoom-out-right — Zoom out + translate

Slide (4)

  • slide-up / slide-down / slide-left / slide-right — Slide from off-screen

Flip (4)

  • flip-left / flip-right — 3D flip on Y-axis
  • flip-up / flip-down — 3D flip on X-axis

Special (2)

  • slide-rotate — Slide + rotate
  • bounce-in — Bouncy spring entrance

Customizable distance

All animations use the --rs-distance CSS variable (default: 100px):

<div data-aos="fade-up" style="--rs-distance: 200px">Farther slide</div>

⚙️ Options

AOS Mode (data attributes)

| Attribute | Example | Description | | ------------------- | --------------- | -------------------------- | | data-aos | "fade-up" | Animation name | | data-aos-duration | "800" | Duration in ms | | data-aos-delay | "200" | Delay in ms | | data-aos-easing | "ease-in-out" | CSS timing function | | data-aos-offset | "120" | Trigger offset in px | | data-aos-once | "true" | Animate only once | | data-aos-mirror | "true" | Animate on scroll away too |

AOS init options

AOS.init({
  offset: 120,
  duration: 400,
  delay: 0,
  easing: "ease",
  once: false,
  mirror: false,
  startEvent: "DOMContentLoaded",
});

Svelte Action options

interface RuneScrollerOptions {
  animation?: AnimationType; // default: 'fade-up'
  duration?: number; // default: 400
  delay?: number; // default: 0
  easing?: string; // default: 'ease'
  repeat?: boolean; // default: false
  debug?: boolean;
  offset?: number; // negative = earlier trigger
  onVisible?: (el: HTMLElement) => void;
  sentinelColor?: string;
  sentinelId?: string;
}

🎯 How It Works

  1. Invisible 1px sentinel appended as child of the animated element
  2. When sentinel enters viewport, animation triggers via IntersectionObserver
  3. Pure CSS transitions (GPU-accelerated via translate3d)
  4. ResizeObserver auto-repositions sentinel

No wrapper divs — the element itself becomes the positioning context. Your flex/grid layouts stay intact.


♿ Accessibility

Respects prefers-reduced-motion — animations are disabled automatically.


📚 API Reference

// Framework agnostic (AOS mode)
import AOS from "rune-scroller/aos";
AOS.init();
AOS.refresh();
AOS.refreshHard();

// Svelte action (default)
import rs from "rune-scroller";

// Named exports
import {
  runeScroller,
  useIntersection,
  useIntersectionOnce,
  calculateRootMargin,
  ANIMATION_TYPES,
} from "rune-scroller";

// Types
import type { AnimationType, RuneScrollerOptions } from "rune-scroller";

📖 Examples

Staggered Animations

<script>
	import rs from 'rune-scroller';
	const items = ['Item 1', 'Item 2', 'Item 3'];
</script>

{#each items as item, i}
	<div use:rs={{ animation: 'fade-up', duration: 800, delay: i * 100 }}>
		{item}
	</div>
{/each}

Hero Section

<h1 data-aos="fade-down" data-aos-duration="1000">Welcome</h1>
<p data-aos="fade-up" data-aos-duration="1200">Subtitle</p>
<button data-aos="zoom-in" data-aos-duration="800">Get Started</button>

🔄 Replacing AOS

npm uninstall aos
npm install rune-scroller
- import AOS from 'aos';
- import 'aos/dist/aos.css';
+ import AOS from 'rune-scroller/aos';

Everything else stays the same. Same attributes, same options.


🔗 Links


📄 License

MIT © ludoloops


Made with ❤️ by LeLab.dev