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

astro-reveal

v0.3.1

Published

Scroll reveal animations for Astro. Zero-JS by default via native CSS scroll-driven animations, with an opt-in IntersectionObserver engine. UI-framework agnostic.

Readme

astro-reveal

Documentación & Demo · Playground

Scroll reveal animations for Astro — smooth slide-ins, fades, scales — that work with any UI framework or no framework at all.

  • Zero runtime JS by default. Uses native CSS scroll-driven animations. In purist mode your page ships a single stylesheet and nothing else.
  • Opt-in JS engine (mode: "observer") when you want the classic "reveal once and stay" behaviour and universal browser support — about 0.6 KB gzipped.
  • UI-agnostic. Astro outputs HTML in the end, so the same data-reveal attribute animates content whether it came from a React, Vue, Svelte island, or plain HTML.
  • FOUC-proof and accessible by design. No flash of hidden content, and prefers-reduced-motion is respected out of the box.
  • Plays nice with View Transitions and late-hydrating islands.

It's the AOS idea, rebuilt for the Astro era.

Install

npx astro add astro-reveal

Or manually:

npm install astro-reveal
// astro.config.mjs
import { defineConfig } from "astro/config";
import reveal from "astro-reveal";

export default defineConfig({
  integrations: [reveal()],
});

That's it. The integration injects the stylesheet for you — no manual CSS import needed.

Usage

Use the component:

---
import Reveal from "astro-reveal/Reveal.astro";
---
<Reveal animation="up">
  <h1>I rise into place as you scroll.</h1>
</Reveal>

<Reveal animation="scale" distance="3rem" as="section">
  <p>Render as any tag with `as`.</p>
</Reveal>

Or the raw attribute (works on anything, including markup from other frameworks):

<div data-reveal="fade">Hello</div>
<img data-reveal="left" src="/photo.jpg" alt="" />

Animations

| Value | Effect | | ------- | --------------------------------------- | | up | rises into place (starts below) | | down | drops into place (starts above) | | left | slides left (starts to the right) | | right | slides right (starts to the left) | | fade | opacity only | | scale | scales up from slightly smaller | | blur | un-blurs into focus |

The two engines

You pick the engine; the API stays identical.

| mode | Runtime JS | Behaviour | Browser support | | -------------------- | ---------- | ---------------------------------- | ------------------------------------------- | | "scroll" (default) | none | scrubbed — reverses on scroll up | animates where supported, static elsewhere | | "observer" | ~0.6 KB | plays once and stays | everywhere | | "auto" | ~0.6 KB | native where supported, JS fallback | identical everywhere |

reveal({ mode: "observer" }); // the "on steroids" mode

Why this matters: in "scroll" mode the hidden ("from") state only exists inside an @supports (animation-timeline: view()) block. A browser that can't animate it never hides the content — so there is no flash and nothing can get stuck invisible. The trade-off is that scroll-driven animations are scrubbed: scroll back up and they play in reverse. If you want "appear once and stay put", switch to "observer".

Options

reveal({
  mode: "scroll",                  // "scroll" | "observer" | "auto"
  once: true,                      // observer/auto: reveal once and keep it
  threshold: 0.15,                 // observer/auto: IntersectionObserver threshold
  rootMargin: "0px 0px -10% 0px",  // observer/auto: fire slightly before the fold
});

Customisation

Tune per element with CSS custom properties (via the component or inline style):

<div data-reveal="up" style="--reveal-distance: 4rem; --reveal-duration: 1s;"></div>

| Variable | Default | Applies to | | -------------------- | ------------------------------ | ----------------- | | --reveal-distance | 1.5rem | up/down/left/right | | --reveal-scale | 0.94 | scale | | --reveal-blur | 8px | blur | | --reveal-duration | 700ms | observer mode | | --reveal-easing | cubic-bezier(.16,1,.3,1) | observer mode | | --reveal-index | 0 | stagger position | | --reveal-stagger | 90ms | observer mode |

Everything ships inside @layer astro-reveal, so your own styles always win without specificity battles.

Theming

These variables are inherited, so you can set them on any ancestor and every [data-reveal] descendant picks them up automatically:

/* Global defaults for the whole page */
:root {
  --reveal-distance: 2rem;
  --reveal-duration: 900ms;
}

/* Override for a specific section */
.hero {
  --reveal-distance: 4rem;
}

Precedence (highest to lowest):

  1. Inline style on the element — style="--reveal-distance: 6rem"
  2. Attribute preset on the element — data-distance-preset="large"
  3. Ancestor rule — :root, section, .hero, etc.
  4. Built-in default — e.g. 1.5rem

--reveal-index is the one exception: it is declared per element (default 0) and is almost always set inline for stagger. Set it on each individual element, not on an ancestor.

Easing presets

Mode note: data-easing / easing and data-speed / speed only take effect in observer and auto modes. In scroll mode (the default, purist engine) the animation timing is driven by scroll position and uses a fixed linear curve by design — these attributes do nothing there and that is not a bug. data-distance-preset / distancePreset works in both engines.

Pick a named easing curve with data-easing or the component prop:

<div data-reveal="up" data-easing="bounce">…</div>
<Reveal animation="up" easing="bounce">…</Reveal>

| Value | Curve | Feel | | --------- | ----------------------------------------- | ------------------------------ | | smooth | cubic-bezier(0.16, 1, 0.3, 1) | easeOutExpo — default | | bounce | cubic-bezier(0.34, 1.56, 0.64, 1) | slight overshoot / spring | | elastic | cubic-bezier(0.68, -0.55, 0.27, 1.55) | pronounced back-and-forth snap | | sharp | cubic-bezier(0.4, 0, 0.2, 1) | Material Design crisp | | soft | cubic-bezier(0.33, 1, 0.68, 1) | easeOutCubic, gentle |

These are sugar over --reveal-easing. Setting the variable directly always wins.

Speed presets

<div data-reveal="fade" data-speed="fast">…</div>
<Reveal animation="fade" speed="fast">…</Reveal>

| Value | --reveal-duration | | --------- | ------------------- | | instant | 200ms | | fast | 400ms | | normal | 700ms (= default) | | slow | 1100ms |

Sugar over --reveal-duration. The duration prop / inline var always wins.

Distance presets

<div data-reveal="left" data-distance-preset="large">…</div>
<Reveal animation="left" distancePreset="large">…</Reveal>

| Value | --reveal-distance | | -------- | ------------------- | | small | 0.5rem | | medium | 1.5rem (= default) | | large | 3rem |

Sugar over --reveal-distance. The distance prop / inline var always wins.

Stagger

{items.map((item, i) => <Reveal animation="up" index={i}>{item}</Reveal>)}

In observer mode each item gets transition-delay: index * --reveal-stagger for a crisp cascade. In scroll mode stagger is approximated by nudging each item's scroll range (grouped staggers are crisper in observer mode).

Accessibility

Users with prefers-reduced-motion: reduce see content in its final state immediately, in every mode. This is built into the CSS, not bolted on — there's no configuration to forget.

Browser support

Native scroll-driven animations (animation-timeline: view()) ship in Chromium-based browsers. Firefox and Safari support has been moving — check caniuse.com/css-scroll-timeline for the current state before relying on purist mode for a specific audience.

  • If you target only modern Chromium → "scroll" (zero JS).
  • If you need identical behaviour everywhere → "auto" (native where it can, JS where it can't).
  • If you specifically want "reveal once and stay" → "observer".

How it works

A tiny head-inline script (only injected in observer/auto modes) decides which engine runs by toggling a single reveal-js class on <html> before paint. The two CSS rule sets — one scoped to html:not(.reveal-js) (native), one to html.reveal-js (JS) — are therefore mutually exclusive by construction. No double-animating, no race conditions.

License

MIT © Nicolás Picotto