Animation Examples

Live demo and testing ground for the Anime.js v4 `data-animate` system in src/utils/animations/.

On-Load Sequences

The hero pattern: elements fade up in a cascading sequence as soon as the page loads, using data-on="load" with staggered data-delay values.

Hero cascade

Badge, heading, subheading, and CTA each fire on load with an increasing data-delay (0 / 150 / 300 / 450ms).

Badge

Hero Heading

Placeholder hero subheading copy.

Call to Action
<span data-animate="fade-up" data-on="load" data-delay="0">Badge</span>
<h2 data-animate="fade-up" data-on="load" data-delay="150">Heading</h2>
<p data-animate="fade-up" data-on="load" data-delay="300">Subheading</p>
<a data-animate="fade-up" data-on="load" data-delay="450">CTA</a>

Scroll Reveals

Each preset below fires once via a scroll observer. Generous spacing separates them so you can see each one trigger individually while scrolling.

data-animate="fade"

fade
<div data-animate="fade">
  ...
</div>

data-animate="fade-up"

fade-up
<div data-animate="fade-up">
  ...
</div>

data-animate="fade-down"

fade-down
<div data-animate="fade-down">
  ...
</div>

data-animate="fade-left"

fade-left
<div data-animate="fade-left">
  ...
</div>

data-animate="fade-right"

fade-right
<div data-animate="fade-right">
  ...
</div>

Zoom & Blur

Scale and filter-based reveals.

data-animate="zoom-in"

zoom-in
<div data-animate="zoom-in">
  ...
</div>

data-animate="blur-in"

blur-in
<div data-animate="blur-in">
  ...
</div>

Staggered Children

data-animate-children applies a preset to a container's direct children as one staggered group, under a single scroll observer.

Default stagger

Uses the preset's built-in default stagger (80ms for fade-up).

Card 1
Card 2
Card 3
Card 4
Card 5
Card 6
<div data-animate-children="fade-up">
  <div>Card 1</div>
  ...
  <div>Card 6</div>
</div>

Custom stagger (150ms)

data-stagger overrides the default gap between each child's animation.

Card 1
Card 2
Card 3
Card 4
Card 5
Card 6
<div data-animate-children="fade-up" data-stagger="150">
  <div>Card 1</div>
  ...
  <div>Card 6</div>
</div>

Text Splitting

splitText() breaks headline text into chars, words, or lines, then staggers each unit in.

Split Chars

Animate this headline

<div data-animate="split-chars">
  ...
</div>

Split Words

Animate this headline

<div data-animate="split-words">
  ...
</div>

Split Lines

Animate this headline

<div data-animate="split-lines">
  ...
</div>

Counters

Animated number counters. Each element reads its target from data-count-to.

Stat blocks

2500 Happy Customers
18 Years in Business
4300 Projects Completed
42 Team Members
<span data-animate="counter" data-count-to="2500">0</span>
<span data-animate="counter" data-count-to="18">0</span>
<span data-animate="counter" data-count-to="4300">0</span>
<span data-animate="counter" data-count-to="42">0</span>

Modifiers

data-duration, data-distance, data-ease, and data-repeat modify any preset's default behavior. All boxes below use fade-up.

data-duration: 300 vs 1500

300ms
1500ms
<div data-animate="fade-up" data-duration="300">Fast</div>
<div data-animate="fade-up" data-duration="1500">Slow</div>

data-distance: 20 vs 120

20px
120px
<div data-animate="fade-up" data-distance="20">Short</div>
<div data-animate="fade-up" data-distance="120">Long</div>

data-ease: outQuart vs outElastic vs linear

outQuart
outElastic
linear
<div data-animate="fade-up" data-ease="outQuart">outQuart</div>
<div data-animate="fade-up" data-ease="outElastic">outElastic</div>
<div data-animate="fade-up" data-ease="linear">linear</div>

data-repeat: true

Replays on every re-enter — scroll this box out of view and back in to see it fire again.

Replays every time
<div data-animate="fade-up" data-repeat="true">Replays every time</div>

Reduced Motion

How the animation module respects prefers-reduced-motion.

initAnimations() in src/utils/animations/index.ts checks window.matchMedia('(prefers-reduced-motion: no-preference)') before touching a single element. If the user has requested reduced motion, the function returns immediately — no preset ever calls utils.set(), so nothing is ever hidden.

Because the hidden state is JS-owned (never CSS opacity-0), all content on this page — and every page using this system — renders fully visible by default, with or without JavaScript.

To test:

  • macOS: System Settings → Accessibility → Display → enable "Reduce motion", then reload this page.
  • Chrome DevTools: Cmd+Shift+P → "Show Rendering" → set "Emulate CSS media feature prefers-reduced-motion" to "reduce".