Skip to content

InView

v0.1.0UI ComponentsVanilla JSVueNuxtReact

Framework-agnostic engines for scroll, element visibility, and viewport-relative position — with adapters for Vue 3, Nuxt, and React, sharing one rAF loop and a pooled IntersectionObserver.

InView
Get started →
npm install @macrulez/inview-core
01 — Purpose

When you'd reach for this

Reveal-on-scroll effects, parallax, a reading-progress indicator — a common enough need that usually gets solved either with a heavy animation library or hand-rolled scroll listeners and IntersectionObservers duplicated in every component. Inview handles that mechanics once, at the shared-engine level.

A landing page's background needs to move slower than the page itself

That classic sense-of-depth trick: a background image or illustration drifts slower (or faster) than the actual content while the page scrolls. Normally that means hand-computing an element's scroll position every frame and turning it into a CSS transform from scratch. The ready-made version already clamps the offset at the edges, takes a speed and an axis, and switches itself off for anyone who's asked their system for less motion.

Feed sections need to reveal themselves while scrolling, not just sit there

Fade-in, a slide up from below, a gentle scale-in — every one of those effects comes down to the same thing: knowing the moment an element enters view, and firing the animation once, right then. Cards that share the same tracking options share one IntersectionObserver instead of spawning one each — the effect stays cheap even across a feed of hundreds of them.

A reading-progress bar at the top of a long article

A thin bar that fills edge-to-edge as the page scrolls — a common fixture on blogs and long-form pieces. Normally that means dividing the distance scrolled by the document's height by hand and redoing the math on every scroll frame. The ready-made progress value is already a plain 0-to-1 number, updated on its own on the same shared animation loop as the rest of the scroll state.

A header that hides on scroll down and reappears on scroll up

Navigation stays out of the way while reading, then reappears the moment you scroll back up — a familiar pattern on mobile sites and long landing pages. Normally that means comparing the current scroll position against the last one by hand, on every scroll event. The scroll direction is already computed on the same shared loop as the rest of the scroll state, and resets on its own once scrolling pauses.

02 — Features

At a glance

A scroll engine on one shared animation loop

A scroll engine on one shared animation loop

Position, direction, progress, and velocity for the window or an element — all derived on one shared animation-frame loop instead of a separate loop per subscription. The "currently scrolling" state resets on its own after a pause in scrolling.

Element visibility with a shared pool of observers

Element visibility with a shared pool of observers

Reactively tells you whether an element is visible, which way it entered or left, and can fire just once. Elements that share the same tracking options share one observer instead of spawning a new one for each — real savings on pages with a lot of them.

An element's position relative to the viewport

An element's position relative to the viewport

An element's bounding box, how far it's traveled through the viewport, and its distance from the screen's center — a ready-made data set for reveal effects and progress indicators, no hand-rolled geometry per component.

A parallax layer that respects reduced motion

A parallax layer that respects reduced motion

Turns an element's position in the viewport into a CSS transform with a configurable speed, axis, edge clamping, and easing curve — and switches itself off for anyone who's asked their system for less motion.

A Nuxt module with automatic imports

A Nuxt module with automatic imports

Every function is available with no explicit import anywhere in a Nuxt app. Default options are set once in the project config and picked up automatically — nothing extra to pass at each call site.

React hooks mirroring the Vue version one-to-one

React hooks mirroring the Vue version one-to-one

The same API and the same behavior as the Vue version, just without reactive refs — plain values that update on their own, plus a safe snapshot for server-side rendering.

03 — Quick example

See how it works

A scroll progress bar, no framework required

createScrollEngine() batches scroll events into one shared requestAnimationFrame loop and hands back progress, direction, and velocity — one subscribe() call away.

scroll-progress.ts
import { createScrollEngine } from '@macrulez/inview-core'

const progressBar = document.querySelector('#progress-bar') as HTMLElement
const scroll = createScrollEngine()

scroll.subscribe((state) => {
  progressBar.style.transform = `scaleX(${state.progress})`
})

Reveal an element as it scrolls into view

createElementTracker() tracks how far an element has travelled through the viewport; bindCSSVar() writes that straight onto a CSS custom property, so the actual fade/translate lives in plain CSS.

reveal-on-scroll.ts
import { createElementTracker, bindCSSVar } from '@macrulez/inview-core'

const card = document.querySelector('#card') as HTMLElement
const tracker = createElementTracker(card)

tracker.subscribe((state) => {
  bindCSSVar(card, 'reveal', state.viewportProgress)
})