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:
# Webflow CDN<script src="https://cdn.jsdelivr.net/npm/alrdy-animate@alpha/dist/alrdy-animate.umd.js"></script>
# Next.js / npm projectsnpm install alrdy-animate@alpha gsap lenis1. CSS-only animations are gone
Section titled “1. CSS-only animations are gone”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-animateto 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 usersnpm install gsap. See the Webflow or Next.js install guides.
aa-load is gone
Section titled “aa-load is gone”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 trigger | Fires 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.
2. aa-load is gone, aa-trigger is new
Section titled “2. aa-load is gone, aa-trigger is new”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:
| Value | Fires when |
|---|---|
scroll (default) | Element enters viewport at aa-scroll-start |
load | On every init() cycle — first load and every SPA re-init |
load-once | First-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 value | v8 value | Notes |
|---|---|---|
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-expand | — | Removed. 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.
4. init() options shape changed
Section titled “4. init() options shape changed”The init API is similar but several keys were renamed or removed.
templates → presets
Section titled “templates → presets”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:
// v7AlrdyAnimate.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' }, }, },})
// v8AlrdyAnimate.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:
themeis 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 withaa-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:
// v7AlrdyAnimate.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.
smoothScroll shape changed
Section titled “smoothScroll shape changed”v7 wrapped Lenis options in a nested options key. v8 passes Lenis options through directly:
// v7AlrdyAnimate.init({ smoothScroll: { enabled: true, options: { lerp: 0.12, wheelMultiplier: 1 }, },})
// v8AlrdyAnimate.init({ smoothScroll: { lerp: 0.12, wheelMultiplier: 1 }, // …or just `smoothScroll: true` for library defaults})AlrdyAnimate.initPageAnimations is gone
Section titled “AlrdyAnimate.initPageAnimations is gone”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 pageAlrdyAnimate.initPageAnimations(() => { /* … */ })
// v8 — three hooks for the three things that hook actually did:await AlrdyAnimate.destroy({ keepGlobals: true }) // tear down per-route stateawait AlrdyAnimate.init({ root: newMain }) // re-scan and re-init scoped to the new containerawait AlrdyAnimate.ready() // wait for first-paintSee the Webflow + Barba or Next.js page transitions recipes for the full wiring.
5. Attribute syntax changes
Section titled “5. Attribute syntax changes”A few small but real syntax changes inside existing attributes.
aa-split flags: & → space
Section titled “aa-split flags: & → space”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-distance → aa-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-alpha | v8 |
|---|---|
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 scrub | aa-intensity="1" (default; ±10vw bake-in moved into the feature) |
aa-distance="20" on marquee scrub | aa-intensity="2" |
aa-distance="30" on tabs scroll-pin | aa-intensity="1" (default; 30vh-per-tab bake-in moved into the feature) |
aa-distance="50" on tabs scroll-pin | aa-intensity="1.67" |
aa-distance="-100" on an [aa-scroll-target] link | aa-scroll-offset="-100" |
init({ distance: 1.4 }) | init({ intensity: 1.4 }) |
Hard cutover — aa-distance is not kept as an alias.
6. Renamed / removed animation values
Section titled “6. Renamed / removed animation values”Kept (no change)
Section titled “Kept (no change)”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).
Renamed
Section titled “Renamed”| v7 | v8 | Notes |
|---|---|---|
reveal-slices, reveal-slices-up-7 | slices, slices-up 7 | The 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/right | reveal-oval-up/down/left/right | Same 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 value | v8 equivalent |
|---|---|
appear, appear-up/down/left/right | fade, fade-up/down/left/right |
float-up/down/left/right | fade-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-in | blur |
pseudo-reveal-up/down/left/right#color | reveal-up/down/left/right + a colored backing element |
swing-fwd, swing-bwd, turn-3d-soft, turn-3d-elliptic, flip-up/down/left/right | No direct replacement — these were CSS-only 3D effects. Author the equivalent in GSAP and dispatch via aa-trigger="event:…". |
Removed entirely (no v8 replacement yet)
Section titled “Removed entirely (no v8 replacement yet)”These v7 features depended on the CSS engine or are still being rebuilt:
| v7 feature | Status |
|---|---|
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. |
accordion → tabs
Section titled “accordion → tabs”v7's accordion feature is now tabs. The attribute names mirror the rename:
| v7 | v8 |
|---|---|
aa-accordion="multi" (parent) | aa-tabs="multi" |
aa-accordion-toggle | aa-tabs-toggle |
aa-accordion-content | aa-tabs-content |
aa-accordion-visual | aa-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.
7. JSX autocomplete (Next.js only)
Section titled “7. JSX autocomplete (Next.js only)”New in v8 — drop this in any TypeScript file and your editor (VS Code, Cursor, JetBrains) autocompletes aa-* attributes on every JSX element:
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.
8. Quick find-and-replace checklist
Section titled “8. Quick find-and-replace checklist”If you have a v7 codebase, sweep these in order:
- Add
gsap+lenispeer deps (script tags ornpm install). v8 will warn at runtime if anything's missing. - Remove the v7
<link rel="stylesheet" href="…AlrdyAnimate.css">— replace withimport '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-loadis removed;aa-triggeris the new attribute. -
aa-hover="bg-block-vertical"→aa-hover="block vertical". Drop thebg-prefix; move direction to a space-separated flag. Same forbg-curve→curve.icon-*values are unchanged. - Hover combinations via
&(bg-curve&text-fade-up&icon-right) → one effect per element. Text-on-hover andbg-circle/bg-expandaren't in v8 — author with custom GSAP if you need them. - Remove
aa-hover-text,aa-hover-bg,aa-hover-content,aa-hover-iconchild 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 intoaa-delay="DESKTOP|MOBILE". Same for any other*-mobileattribute. -
aa-accordion*→aa-tabs*. - In
init(): removegsapFeatures: [...], renametemplates→presets(and flatten the value shape), unwrapsmoothScroll.optionsto be the smoothScroll value directly. - If you call
AlrdyAnimate.initPageAnimations(...), replace withdestroy({ 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.