@get-set/gs-reveal
v1.0.2
Published
Get-Set Reveal  scroll-reveal animations (IntersectionObserver)
Maintainers
Readme
@get-set/gs-reveal
A dependency-free, accessible scroll-reveal animation plugin powered by
IntersectionObserver, available in two flavours from one codebase:
- Native / vanilla JS — auto-init on
[data-gs-reveal], awindow.GSReveal(selectorOrElement, params)factory (alsonew-able, plus a jQuery plugin and anHTMLElement.prototypemethod), and awindow.GSRevealConfigueinstance registry. - React — a
<GSReveal>forwardRefcomponent that wraps its children and exposes an imperativereveal / reset / refresh / destroyhandle.
Both surfaces drive one shared core (actions/, constants/, helpers/,
types/), so behaviour is identical across the two — every option works in
both targets (except the few tagged React-only below).
Features
- 43-entry animation catalog — fades (
fade,fade-up/-down/-left/-rightand all four diagonals), zooms (zoom-in,zoom-out, and directionalzoom-in-up…zoom-out-down), 3D flips (flip-up/-down/-left/-right,flip-diagonal), opaque slides (slide-up/-down/-left/-right), rotations (rotate,rotate-left,rotate-right,swing), and an expressive set (blur,blur-up,skew,skew-left,roll,unfold,bounce-up,bounce-in,elastic,reveal-clip,reveal-clip-up,glitch), plus anoneno-op. - Tunable timing —
duration,delay,easing(named presets or any raw CSS timing function),distancefor directional animations, and a transformoriginpivot for zoom/flip/rotate/skew. - IntersectionObserver control —
threshold(0..1),rootMargin, and a custom scrollroot(selector / element / React ref). oncevs repeat — reveal a single time (default) or re-hide on leave and re-reveal on re-entry;mirroranimates the element back out when it leaves the viewport.- Staggering —
staggerChildrenreveals a container's children sequentially, with a tunablestaggerstep, optionalstaggerSelector, and astaggerMaxclamp. - Accessibility first — honors
prefers-reduced-motion(reducedMotion: 'auto' | true | false) at both the JS and CSS layers, so reduced-motion users get an instant, static reveal. disabledshows content immediately with no observer (great for SSR fallbacks / feature flags).- Light / dark / auto theming, an accent color token, RTL (mirrors
left/right directional animations), per-instance
className, and a React-only scoped-stylesgsxprop. - Lifecycle callbacks
onReveal/onHidewith{ element, reference, ratio }.
Compatibility
| Target | Requirement |
|---|---|
| React (the <GSReveal> component) | React 16.8+ (Hooks + forwardRef), and 17 / 18 / 19. React is an optional peer dependency — you only need it for the component. |
| Native / vanilla (window.GSReveal) | No framework. Any modern evergreen browser with IntersectionObserver. Optional jQuery ($(...).GSReveal(...)) and HTMLElement.prototype.GSReveal(...) adapters are registered automatically by the bundle. |
| TypeScript | First-class — type declarations (.d.ts) ship in the package. |
| SSR / Next.js | Safe to import server-side (all DOM access is client-gated). Render the component inside a Client Component ('use client'). Without IntersectionObserver (old browsers / SSR) elements reveal immediately, so content is never hidden. |
The peer range is
^16.8.0 || ^17.0 || ^18.0 || ^19.0, withreact/react-dommarked optional so the native build has zero dependencies.
Package entry points
From package.json:
| Field | Value |
|---|---|
| name | @get-set/gs-reveal |
| main | dist/components/GSReveal.js |
| module | dist/components/GSReveal.js |
| types | dist/components/GSReveal.d.ts |
| exports['.'].import | ./dist/components/GSReveal.js (React / ESM) |
| exports['.'].require | ./dist-js/bundle.js (native window bundle) |
| files | dist, dist-js, styles, example.html |
Project layout
GSReveal.ts # native entry (webpack -> dist-js/bundle.js, window global)
components/GSReveal.tsx # React component (tsc -> dist/, npm entry)
actions/ # shared engine (init/reveal/reset/refresh/destroy/autoInit/applyState)
constants/ # animation catalog, easings, default params
helpers/ # reveal (classes/vars/observer opts) + ui helpers
types/ # Params / Ref / Window augmentation
components/styles/ # SCSS + compiled CSS + CSS-as-TS (runtime injection for React)
styles/ # SCSS + compiled CSS (for <link> use by the native build)Install
npm i @get-set/gs-revealBuild & test
npm install
npm run build # builds both targets
npm run build:js # native bundle -> dist-js/bundle.js
npm run build:react # React/ESM -> dist/
npm test # vitest (logic + native registry + React render/handle/gsx)Usage — Vanilla / Native
Include the stylesheet and the bundle. Every element with a data-gs-reveal
attribute is initialized automatically on DOM ready.
<link rel="stylesheet" href="node_modules/@get-set/gs-reveal/styles/GSReveal.css" />
<script src="node_modules/@get-set/gs-reveal/dist-js/bundle.js"></script>
<!-- Auto-init: the attribute value is the animation name -->
<h1 data-gs-reveal="fade-up">Hello</h1>
<img data-gs-reveal="zoom-in" data-gs-reveal-delay="200" src="hero.jpg" />
<!-- Stagger a list's children -->
<ul data-gs-reveal="fade-up" data-gs-reveal-stagger-children data-gs-reveal-stagger="80">
<li>One</li><li>Two</li><li>Three</li>
</ul>Every param has a data-gs-reveal-* attribute (kebab-cased): duration,
delay, easing, distance, origin, threshold, root-margin, root,
once, mirror, disabled, stagger-children, stagger, stagger-max,
stagger-selector, theme, accent, rtl, reference.
Factory — selector or element
The factory accepts a CSS selector string or a DOM element — this is what lets the jQuery and prototype adapters (which pass an element) share the same core:
// by selector
const r1 = window.GSReveal('#hero', { animation: 'fade-up', duration: 800 });
// by element (identical behaviour)
const el = document.querySelector('.card');
const r2 = window.GSReveal(el, { animation: 'zoom-in', threshold: 0.3 });
// constructor form also works
const r3 = new window.GSReveal(el, { animation: 'blur' });Instance registry
window.GSReveal('#gallery', { reference: 'gallery', staggerChildren: true });
const inst = window.GSRevealConfigue.instance('gallery');
inst.reveal(); // force-show now
inst.reset(); // back to hidden
inst.refresh(); // re-scan children + re-wire the observer
inst.destroy(); // tear down + unregister
inst.isRevealed(); // -> boolean
inst.getElements(); // -> HTMLElement[]window.GSReveal.autoInit() re-runs the [data-gs-reveal] scan manually (e.g.
after injecting new markup); already-initialized nodes are skipped.
Usage — jQuery
The bundle registers $.fn.GSReveal when jQuery is present. It initializes each
matched element (passing the element to the core factory):
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="node_modules/@get-set/gs-reveal/dist-js/bundle.js"></script>
<script>
$('.reveal').GSReveal({ animation: 'fade-up', duration: 700, stagger: 60 });
</script>Usage — HTMLElement.prototype
The bundle also adds HTMLElement.prototype.GSReveal, so any element can
initialize itself:
document.querySelector('#card').GSReveal({
animation: 'flip-left',
origin: 'left',
threshold: 0.25
});Usage — React
'use client';
import GSReveal from '@get-set/gs-reveal';
export default function Page() {
return (
<>
<GSReveal animation="fade-up" duration={800}>
<h1>Hello</h1>
</GSReveal>
{/* Stagger children sequentially */}
<GSReveal staggerChildren stagger={100} animation="zoom-in">
<Card /> <Card /> <Card />
</GSReveal>
{/* Repeat on every entry, and mirror out on leave */}
<GSReveal once={false} mirror animation="fade-left">
<section>…</section>
</GSReveal>
</>
);
}The wrapper renders a <div> by default; use as to change it:
<GSReveal as="section" animation="blur">…</GSReveal>Imperative handle (ref)
import { useRef } from 'react';
import GSReveal, { GSRevealHandle } from '@get-set/gs-reveal';
function Demo() {
const ref = useRef<GSRevealHandle>(null);
return (
<>
<GSReveal ref={ref} animation="zoom-in" once={false}>
<Box />
</GSReveal>
<button onClick={() => ref.current?.reveal()}>Reveal</button>
<button onClick={() => ref.current?.reset()}>Reset</button>
<button onClick={() => ref.current?.refresh()}>Refresh</button>
</>
);
}GSRevealHandle methods:
| Method | Description |
|---|---|
| reveal() | Force every target into the revealed state now (skips the observer + delays). |
| reset() | Force every target back to the hidden state (repeat/mirror aware). |
| refresh() | Re-scan children and re-wire the IntersectionObserver from the current props. |
| destroy() | Disconnect the observer and strip all reveal classes / CSS vars. |
| isRevealed() | boolean — whether at least one target is currently revealed. |
| getElements() | HTMLElement[] — the live list of observed targets (wrapper or staggered children). |
React-only: scoped styles via gsx
gsx injects a scoped stylesheet keyed to this instance ([data-key='…']),
removed automatically on unmount:
<GSReveal
animation="fade-up"
gsx={{
padding: '2rem',
borderRadius: '18px',
' h2': { color: 'rebeccapurple' }
}}>
<h2>Scoped</h2>
</GSReveal>Props / Params
Every option works in both targets unless marked. In the native target the
same keys are passed in the params object (and available as data-gs-reveal-*
attributes).
Core animation
| Prop | Type | Default | Description |
|---|---|---|---|
| animation | GSRevealAnimation \| false | 'fade-up' | Entrance animation from the catalog. false disables motion (element just appears). |
| duration | number | 600 | Animation duration in ms. |
| delay | number | 0 | Delay before the reveal starts, in ms. |
| easing | named preset or raw CSS | 'smooth' | ease · ease-in · ease-out · ease-in-out · linear · smooth · snappy · spring · back · expo · gentle, or any cubic-bezier(...). |
| distance | number \| string | '32px' | Translate distance for directional animations. Number = px. |
| origin | GSRevealOrigin | 'center' | Transform-origin pivot for zoom/flip/rotate/skew: auto · center · top · bottom · left · right · top-left · top-right · bottom-left · bottom-right. |
Observer / trigger
| Prop | Type | Default | Description |
|---|---|---|---|
| threshold | number (0..1) | 0.15 | Fraction of the element that must be visible to trigger. Clamped to 0..1. |
| rootMargin | string | '0px' | IntersectionObserver rootMargin (grows/shrinks the trigger box). |
| root | selector · element · ref | viewport | Scroll container the observer watches. |
| once | boolean | true | Reveal a single time. false re-hides on leave and re-reveals on re-entry. |
| mirror | boolean | false | Animate the element back out when it leaves the viewport (implies repeat). |
Stagger / children
| Prop | Type | Default | Description |
|---|---|---|---|
| staggerChildren | boolean | false | Reveal the container's children sequentially instead of the container itself. |
| stagger | number | 100 | Per-child stagger step, in ms. |
| staggerSelector | string | direct children | CSS selector for the children to stagger. |
| staggerMax | number | — | Clamp the cumulative stagger delay (ms). |
Behaviour / a11y
| Prop | Type | Default | Description |
|---|---|---|---|
| disabled | boolean | false | Show content immediately, no observer, no animation. |
| reducedMotion | 'auto' \| boolean | 'auto' | Honor prefers-reduced-motion. 'auto' follows the OS; true always reduces; false never. |
Theming / presentation
| Prop | Type | Default | Description |
|---|---|---|---|
| theme | 'light' \| 'dark' \| 'auto' | 'auto' | Visual theme for the accent token. |
| accentColor | string | — | Accent color token (CSS var --gsr-accent). |
| rtl | boolean | false | Right-to-left layout — mirrors left/right directional animations. |
| className | string | — | Extra class name on the reveal element / wrapper. |
| reference | string | auto GUID | Registry key (window.GSRevealConfigue.instance(reference)). |
React-only
| Prop | Type | Default | Description |
|---|---|---|---|
| as | element type | 'div' | Wrapper element to render. |
| gsx | NestedCSS | — | Scoped inline styles injected for this instance (removed on unmount). |
| children | ReactNode | — | Content to reveal. |
Callbacks
| Prop | Signature | Description |
|---|---|---|
| onReveal | (info) => void | Fired each time the element (or a staggered child) is revealed. |
| onHide | (info) => void | Fired each time the element (or a child) is hidden again (repeat / mirror). |
info is { element: HTMLElement; reference: string; ratio: number }.
Animation catalog
| Group | Names |
|---|---|
| Fades | fade, fade-up, fade-down, fade-left, fade-right, fade-up-left, fade-up-right, fade-down-left, fade-down-right |
| Zooms | zoom-in, zoom-out, zoom-in-up, zoom-in-down, zoom-in-left, zoom-in-right, zoom-out-up, zoom-out-down |
| Flips (3D) | flip-up, flip-down, flip-left, flip-right, flip-diagonal |
| Slides (opaque) | slide-up, slide-down, slide-left, slide-right |
| Rotations | rotate, rotate-left, rotate-right, swing |
| Expressive | blur, blur-up, skew, skew-left, roll, unfold, bounce-up, bounce-in, elastic, reveal-clip, reveal-clip-up, glitch |
| No-op | none (reduced-motion / disabled fallback) |
Directional animations (react to distance): all fade-* translates,
zoom-*-up/down/left/right, all slide-*, blur-up, bounce-up.
Easing presets
ease, ease-in, ease-out, ease-in-out, linear, smooth, snappy,
spring, back, expo, gentle — or pass any raw CSS timing function.
Styling / CSS variables
The engine drives everything through classes + CSS custom properties, so you can theme it freely:
| Variable | Purpose |
|---|---|
| --gsr-duration | Transition duration. |
| --gsr-delay | Transition delay (per-target; includes stagger offset). |
| --gsr-easing | Transition timing function. |
| --gsr-distance | Translate distance for directional animations. |
| --gsr-origin | Transform origin. |
| --gsr-accent | Accent color token. |
| --gsr-blur | Blur radius for blur / blur-up. |
State classes: .gs-reveal (base, always present), .gs-reveal-<animation>
(hidden state), and .gs-reveal-visible (revealed / identity state). RTL adds
.gs-reveal-rtl; themes add .gs-reveal-theme-{light,dark}.
Reduced motion
When prefers-reduced-motion: reduce matches (and reducedMotion is 'auto'
or true), the reveal is instant — the JS sets zero duration/delay and the
CSS @media (prefers-reduced-motion: reduce) block forces
transition: none; transform: none; opacity: 1. Content is always visible;
motion is simply removed.
Runnable example
Open example.html after npm run build for a full native
showcase: auto-init on [data-gs-reveal], the directional / zoom / flip /
expressive catalogs, staggered children, repeat & mirror, and a programmatic
box driven through the registry.
License
ISC © Get-Set
