Skip to content

Migrating from v7 to v8

v8 is a full TypeScript rewrite of the library. The attribute style stays — you still write aa-animate="fade-up", aa-stagger, aa-anchor, aa-scroll-start — but the engine underneath is completely new, and a few v7 escape hatches no longer exist. This page is the find-and-replace list for upgrading a real project.

If you're starting fresh in v8 you can skip this page. If you have a v7 site you want to bring forward, follow the order below — top changes have the biggest impact.

The latest dist-tag still points to v7.3.5 while v8 is in beta. Install v8 explicitly:

Terminal window
# Webflow CDN
<script src="https://cdn.jsdelivr.net/npm/alrdy-animate@alpha/dist/alrdy-animate.umd.js"></script>
# Next.js / npm projects
npm install alrdy-animate@alpha gsap lenis

Biggest change. v7 shipped two parallel engines: a CSS-keyframes path for lightweight scroll/load animations, and a GSAP path for everything else. v8 is GSAP-only. The shipped alrdy-animate.css no longer carries keyframe animations — it now carries split-text utility classes, structural layout for the hover heads ([aa-hover-bg], [aa-hover-underline], :where([aa-hover~="text"])), the FOUC guard, and a reduced-motion safety net.

What that means concretely:

  • GSAP and ScrollTrigger are required for any aa-animate to do anything. They were optional in v7's CSS path; they're now mandatory.
  • You provide GSAP yourself — it's a peer dependency, never bundled. Webflow users add a <script> tag; npm users npm install gsap. See the Webflow or Next.js install guides.

v7's aa-load attribute triggered a CSS-keyframe animation on page load with no JS required. v8 replaces it with a trigger value on a normal aa-animate element:

<!-- v7 -->
<h1 aa-load="fade-up">Hero</h1>
<!-- v8 -->
<h1 aa-animate="fade-up" aa-trigger="load">Hero</h1>

Two flavours:

v8 triggerFires on
aa-trigger="load"Every init() cycle — first load and every SPA re-init. Use for content that should re-animate on each route change.
aa-trigger="load-once"First-init only. Use for the hero on a marketing page where re-animating on every nav would feel repetitive.

There's an optional CSS fallback recipe for hiding the FOUC window if your bundle is slow — but it's a graceful safety net, not the main path like aa-load was.


v7 used three separate attributes for the three trigger styles: aa-animate (scroll, the default), aa-load (page load), aa-hover (hover). v8 keeps aa-animate and aa-hover as separate attributes — aa-hover is its own interaction system (see §3), and aa-animate is still the scroll/load/event-triggered appear-animation hook. What changed is aa-load: it's removed entirely. Load-triggered animations are now expressed as a value on aa-trigger, on a normal aa-animate element:

<!-- v7 — separate aa-load attribute, ran a CSS-keyframe animation. -->
<h1 aa-load="fade-up">Hero</h1>
<!-- v8 — load is a trigger value on aa-animate. -->
<h1 aa-animate="fade-up" aa-trigger="load">Hero</h1>

aa-trigger is the new attribute that didn't exist in v7. It defaults to scroll (so any existing aa-animate without aa-trigger keeps working exactly as before — that's why most v7 markup doesn't need an aa-trigger added at all). Set it to opt out of scroll triggering or to combine triggers:

ValueFires when
scroll (default)Element enters viewport at aa-scroll-start
loadOn every init() cycle — first load and every SPA re-init
load-onceFirst-init only — for marketing heroes that shouldn't re-fire on subsequent SPA navigations
event:<name>When a aa:trigger event with detail.name === <name> is dispatched
click:<name>When a sibling element with aa-click="<name>" is clicked

You can also combine triggers — v7 couldn't:

<!-- Hero text: animates on first cold load (load-once),
then re-animates on every page transition (event:enter
dispatched from your transition timeline). -->
<h1 aa-animate="text-slide-up" aa-trigger="load-once event:enter"></h1>

See aa-trigger for the full trigger reference and container inference (e.g. children of [aa-tabs-content] infer event:tab-active automatically).


3. aa-hover keeps the name, loses the bg- / text- / icon- prefixes

Section titled “3. aa-hover keeps the name, loses the bg- / text- / icon- prefixes”

v8 keeps the aa-hover attribute, but the value vocabulary is smaller and structural rather than presentational. Direction modifiers are now space-separated flags instead of being baked into the value name; combined hovers (v7's & operator) are gone — one effect per element.

v7 valuev8 valueNotes
aa-hover="bg-block"aa-hover="block"Direction-aware background block.
aa-hover="bg-block-vertical"aa-hover="block vertical"Flag goes as space-separated token. Other flags: horizontal, top, bottom, left, right.
aa-hover="bg-curve"aa-hover="curve"SVG-path curve, same flag system.
aa-hover="icon-up-right"aa-hover="icon-up-right"Unchanged. Optional flags: reverse, triple.
aa-hover="bg-circle", bg-expandRemoved. Closest substitute: block or curve.
aa-hover="text-slide-up", text-fade-up, etc.Removed. v8's hover feature is structural-only; for text-on-hover effects, author with GSAP + mouseenter/mouseleave.
Combined hovers via & (bg-curve&text-fade-up&icon-right)Removed. Use one effect per element.
Required child markers (aa-hover-text, aa-hover-bg, aa-hover-content)Removed. The library injects the panel/clone elements itself.
<!-- v7 -->
<a aa-hover="bg-block-vertical" aa-color="bg:#000 text:#fff">
<span aa-hover-content>Hover me</span>
<div aa-hover-bg></div>
</a>
<!-- v8 — markup is leaner, attribute value drops the bg- prefix and uses
a separate token for the direction. -->
<a aa-hover="block vertical" aa-color="#000">Hover me</a>

The aa-color value shape also simplified: instead of bg:#hex text:#hex, you pass a single colour. The hover feature decides which property to animate based on the effect. See the hover background and hover icon docs.


The init API is similar but several keys were renamed or removed.

The class-to-animation mapping system was renamed from templates to presets and lost the theme shorthand. Custom mappings work the same way; the option key is just different:

// v7
AlrdyAnimate.init({
templates: {
theme: 'blur',
custom: {
'heading-style-h2': { animationType: 'text-blur|text-fade', split: 'lines&words', stagger: 0.05 },
'heading-style-h3': { animationType: 'fade-up' },
},
},
})
// v8
AlrdyAnimate.init({
presets: {
// Class name → bare-keyed map of aa-* attributes (no `aa-` prefix in the keys).
'heading-style-h2': { animate: 'text-blur', split: 'lines', stagger: '0.05' },
// Shorthand: a plain string is treated as `{ animate: <string> }`.
'heading-style-h3': 'fade-up',
},
})

Differences:

  • theme is gone — pre-built theme sets aren't shipped any more. Build the few you need as custom presets.
  • The preset value is either a plain animation string (shorthand for { animate: <string> }) or a bare-keyed map like { animate, split, stagger, delay, duration, trigger, … }. The library prefixes each key with aa- when applying.
  • Presets compose with any explicit aa-* attribute on the element — explicit always wins, so you can override per-instance without removing the class.

gsapFeatures is gone — features auto-load

Section titled “gsapFeatures is gone — features auto-load”

v7 required you to list which GSAP-powered features to enable:

// v7
AlrdyAnimate.init({
gsapFeatures: ['text', 'slider', 'nav', 'accordion', 'parallax'],
})

v8 scans the DOM and lazy-loads only the features it actually needs. Remove the gsapFeatures option entirely — anything you used in v7 will still work, just without the explicit list. Run init({ debug: true }) to see which features the scan loaded and which GSAP plugins they needed.

v7 wrapped Lenis options in a nested options key. v8 passes Lenis options through directly:

// v7
AlrdyAnimate.init({
smoothScroll: {
enabled: true,
options: { lerp: 0.12, wheelMultiplier: 1 },
},
})
// v8
AlrdyAnimate.init({
smoothScroll: { lerp: 0.12, wheelMultiplier: 1 },
// …or just `smoothScroll: true` for library defaults
})

v7 exposed initPageAnimations() as a manual re-scan hook for SPA navigations. v8 replaces it with three explicit lifecycle calls:

// v7 — single hook that re-scanned the whole page
AlrdyAnimate.initPageAnimations(() => { /* … */ })
// v8 — three hooks for the three things that hook actually did:
await AlrdyAnimate.destroy({ keepGlobals: true }) // tear down per-route state
await AlrdyAnimate.init({ root: newMain }) // re-scan and re-init scoped to the new container
await AlrdyAnimate.ready() // wait for first-paint

See the Webflow + Barba or Next.js page transitions recipes for the full wiring.


A few small but real syntax changes inside existing attributes.

v7 joined split flags with &. v8 uses space-separated flags (matching the trigger / animate syntax everywhere else):

<!-- v7 -->
<h1 aa-animate="text-slide-up" aa-split="lines&words"></h1>
<!-- v8 -->
<h1 aa-animate="text-slide-up" aa-split="lines mask"></h1>

The available flags also changed: v8 introduces mask (line/word masking via clip-path) instead of compound lines&words chains. See aa-split.

aa-delay-mobile → responsive | shorthand

Section titled “aa-delay-mobile → responsive | shorthand”

v7 had per-attribute mobile overrides like aa-delay-mobile. v8 replaces all of them with a uniform two-bucket responsive shorthand using |:

<!-- v7 -->
<div aa-animate="fade-up" aa-delay="0.2" aa-delay-mobile="0.1"></div>
<!-- v8 — left of pipe = >= md breakpoint (768px default), right = below -->
<div aa-animate="fade-up" aa-delay="0.2|0.1"></div>

This works on every aa-* attribute (aa-animate, aa-duration, aa-intensity, aa-scroll-start, etc.) and also via Tailwind-style breakpoint suffixes: aa-animate-md="…", aa-animate-lg="…", aa-animate-xl="…". Use none at a breakpoint to opt out entirely:

<div aa-animate="slide-up|none"></div> <!-- desktop only -->
<div aa-animate-md="text-slide-up"></div> <!-- md breakpoint and up -->

aa-anchor, aa-stagger, aa-scroll-start, aa-duration, aa-delay, aa-scrub, aa-color, aa-children, aa-opacity, aa-ease

Section titled “aa-anchor, aa-stagger, aa-scroll-start, aa-duration, aa-delay, aa-scrub, aa-color, aa-children, aa-opacity, aa-ease”

All unchanged in spirit, all still work. See the animations docs for current per-attribute defaults.

Renamed: aa-distanceaa-intensity (with aa-scroll-offset split)

Section titled “Renamed: aa-distance → aa-intensity (with aa-scroll-offset split)”

v7's aa-distance did three semantically different things across features — translate multiplier for fade/slide/rotate/parallax/stack/text/hover, raw vw/vh for marquee/tabs, and raw pixel offset for [aa-scroll-target] links. v8 unifies the multiplier uses under aa-intensity (default 1 everywhere; 0.5 halves the effect, 2 doubles it), rebases the marquee, tabs, and nav defaults so they all behave as multipliers on an internal baseline, and splits the raw-pixel scroll offset onto its own attribute that joins the aa-scroll-* family.

v7 / early-alphav8
aa-distance="2" (fade / slide / rotate / parallax / stack / text-fade / text-blur / hover-icon)aa-intensity="2"
aa-distance="1.5" on nav (clears drop-shadow)aa-intensity="1" (default; -150% bake-in moved into the feature)
aa-distance="10" on marquee scrubaa-intensity="1" (default; ±10vw bake-in moved into the feature)
aa-distance="20" on marquee scrubaa-intensity="2"
aa-distance="30" on tabs scroll-pinaa-intensity="1" (default; 30vh-per-tab bake-in moved into the feature)
aa-distance="50" on tabs scroll-pinaa-intensity="1.67"
aa-distance="-100" on an [aa-scroll-target] linkaa-scroll-offset="-100"
init({ distance: 1.4 })init({ intensity: 1.4 })

Hard cutover — aa-distance is not kept as an alias.


fade, fade-up, fade-down, fade-left, fade-right, slide-up, slide-down, slide-left, slide-right, zoom-in, zoom-out, blur, all text-* variants, all reveal-* variants (clip-path entrances).

v7v8Notes
reveal-slices, reveal-slices-up-7slices, slices-up 7The slices effect is now its own feature with a flag-based syntax. Direction goes inline; the row count is a separate space-separated flag. Add cover for the inverted variant: aa-animate="slices-right cover 12". See slices.
reveal-oval-up/down/left/rightreveal-oval-up/down/left/rightSame names — but the implementation is rewritten under the reveal feature.

Removed (use a fade-* / slide-* / zoom-* substitute)

Section titled “Removed (use a fade-* / slide-* / zoom-* substitute)”
v7 valuev8 equivalent
appear, appear-up/down/left/rightfade, fade-up/down/left/right
float-up/down/left/rightfade-up/down/left/right + aa-ease="back.out" for the bounce
zoom-in-up, zoom-out-down, … (compound directions)zoom-in / zoom-out + a separate fade-up / fade-down element if you need the slide
blur-inblur
pseudo-reveal-up/down/left/right#colorreveal-up/down/left/right + a colored backing element
swing-fwd, swing-bwd, turn-3d-soft, turn-3d-elliptic, flip-up/down/left/rightNo direct replacement — these were CSS-only 3D effects. Author the equivalent in GSAP and dispatch via aa-trigger="event:…".

These v7 features depended on the CSS engine or are still being rebuilt:

v7 featureStatus
aa-animate="pin" (sticky / pin-stack)Deferred. Planned for a v8.x minor. Use GSAP ScrollTrigger.pin directly in your own code in the meantime.
aa-animate="counter", aa-animate="counter-100"Removed. Author via a small GSAP tween — gsap.to({ value: 0 }, { value: 100, onUpdate: ... }) is ~5 lines.
aa-animate="grow-horizontal", aa-animate="grow-vertical"Removed. Use slide-left / slide-up with a clip-path mask, or reveal-*.
aa-animate="section-bg", "section-clip", "section-stack"Removed. These were single-purpose recipes — author the GSAP timeline directly for the section in question.
aa-animate="glide"Removed. Use aa-animate="parallax" for the typical use case.

v7's accordion feature is now tabs. The attribute names mirror the rename:

v7v8
aa-accordion="multi" (parent)aa-tabs="multi"
aa-accordion-toggleaa-tabs-toggle
aa-accordion-contentaa-tabs-content
aa-accordion-visualaa-tabs-visual

The behaviour is the same (one or many open panels), plus a new optional aa-autoplay="…" for time-cycling between panels. See tabs.


New in v8 — drop this in any TypeScript file and your editor (VS Code, Cursor, JetBrains) autocompletes aa-* attributes on every JSX element:

types/global.d.ts
import 'alrdy-animate/jsx'

Hover any aa-* attribute to see the JSDoc with accepted values and defaults. The package also ships an AGENTS.md at its root so AI coding agents (Claude Code, Cursor, Aider) can read the full attribute / trigger / feature reference straight out of node_modules/alrdy-animate/AGENTS.md without web-fetching docs.


If you have a v7 codebase, sweep these in order:

  • Add gsap + lenis peer deps (script tags or npm install). v8 will warn at runtime if anything's missing.
  • Remove the v7 <link rel="stylesheet" href="…AlrdyAnimate.css"> — replace with import 'alrdy-animate/style' (npm) or <link href="…@alpha/dist/alrdy-animate.css"> (CDN).
  • aa-load="X"aa-animate="X" aa-trigger="load" (or "load-once" for once-only). aa-load is removed; aa-trigger is the new attribute.
  • aa-hover="bg-block-vertical"aa-hover="block vertical". Drop the bg- prefix; move direction to a space-separated flag. Same for bg-curvecurve. icon-* values are unchanged.
  • Hover combinations via & (bg-curve&text-fade-up&icon-right) → one effect per element. Text-on-hover and bg-circle / bg-expand aren't in v8 — author with custom GSAP if you need them.
  • Remove aa-hover-text, aa-hover-bg, aa-hover-content, aa-hover-icon child markers. The v8 hover feature injects its own panels and clones.
  • aa-color="bg:#hex text:#hex" (with prefixed pairs) → aa-color="#hex" (single colour). v8's hover feature decides which property to animate based on the chosen effect.
  • aa-split="a&b"aa-split="a b".
  • aa-delay-mobile="X" → fold into aa-delay="DESKTOP|MOBILE". Same for any other *-mobile attribute.
  • aa-accordion*aa-tabs*.
  • In init(): remove gsapFeatures: [...], rename templatespresets (and flatten the value shape), unwrap smoothScroll.options to be the smoothScroll value directly.
  • If you call AlrdyAnimate.initPageAnimations(...), replace with destroy({ keepGlobals: true }) + init({ root: newMainContainer }). See Webflow + Barba or Next.js page transitions.
  • If you use any of the removed values from §6, substitute or rewrite as a custom GSAP timeline.
  • Test in init({ debug: true }) — the boot log lists every loaded feature, required plugin, and any missing GSAP plugins.

If anything breaks that this page doesn't cover, drop it in the GitHub issues and I'll fold it in.