autumn-plugin-gsap
GSAP animations for Autumn 0.10 apps. You write Rust and Maud. You need no npm, no bundler and no JavaScript.
- The crate includes GSAP 3.15.0, ScrollTrigger and SplitText.
- The plugin serves them through the plugin asset seam of Autumn: hashed URLs, SRI, immutable cache.
- Builders:
Gsap,Timeline,Parallax. Typed values:Ease,ScrollPos,Position,Props,Tag. - htmx: new content animates.
init.jsreverts the animations of removed content. - Elements do not animate when the user prefers reduced motion.
- The plugin works with a strict CSP.
Quickstart
-
Add the plugin.
use GsapPlugin; app.plugin.run.await; -
Put the tags in the page
<head>. The script tags have thedeferattribute and SRI hashes.use ; html! -
Animate.
use Duration; use ; html!
We recommend gsap_stylesheet() on each page. It shows all content in print,
keeps split masks from cutting letters, and styles Gsap::scroll_progress().
Features
Tweens
use ;
zoom_in; // a preset
scale.stagger.stagger_from; // the children, one by one
from_props; // gsap.from
to_props; // gsap.to
from_to; // gsap.fromTo
Presets: fade, fade_up, fade_down, fade_left, fade_right, scale, zoom_in, zoom_out,
slide_up, slide_down, slide_left, slide_right, rotate_in, blur_in, flip_x.
With from_to, put each property of from also in to. GSAP animates only the properties in to.
ScrollTrigger
By default, a tween plays one time when its element top is at 85% of the viewport. The start is never after the last scroll position, so content at the page end also plays.
use ;
fade.play; // no ScrollTrigger
fade.start; // start: "top center"
fade.replay; // forward on enter, backward on leave back
fade.scrub.pin; // link to the scroll, pin
fade.trigger.markers; // another trigger, debug markers
parallax; // drift 30% of its height
scroll_progress; // a page progress bar
Timelines
A Timeline plays its data-gsap children in document order, as one sequence.
Position sets where each step starts. With no position, a step starts at the end of the timeline.
use ;
new.wrap_in
- The timeline options (
play,scrub,pin,repeat, ...) apply to the full sequence. - The
durationandeaseof the timeline are defaults for the steps. - On a step,
init.jsignores the ScrollTrigger options anddata-gsap-reduced. Parallaxand the progress bar cannot be timeline steps.
htmx
init.jsscans eachhtmx:loadelement. Thus server-rendered partials animate with no extra code.- On
htmx:beforeCleanupElement, it stops the tweens, ScrollTriggers and SplitText of removed content. - Before an
outerHTMLordeleteswap, it restores the target, so a pin spacer does not wrap the new content. If a later listener cancels the swap,init.jsstarts the target again. - After an
innerHTMLswap into a stagger, split or timeline container, it starts the container again. For a split timeline step, it removes the old split label. The new text of the step does not animate. - Content that stays on the page (for example with
beforeend) does not play again. - htmx keeps a copy of the page for the Back button.
init.jsremoves the GSAP state from that copy only. The page on screen does not change. After Back, the restored content animates again. - After each
htmx:afterSettle, it refreshes the ScrollTrigger positions.
async
JavaScript API
window.AutumnGsap has these members:
scan(root): starts the animations inroot. It returns the count.revert(root): reverts the animations inroot. It returns the count.parse: the attribute parsers (for tests and debug).presets,version.
You can also use window.gsap, window.ScrollTrigger and window.SplitText in your own code.
Attribute reference
The builders write these attributes. You can also write them by hand.
For a bad value, init.js writes a console warning and uses the default.
| Attribute | Values | Default |
|---|---|---|
data-gsap |
a preset name, custom, parallax, scroll-progress |
fade-up |
data-gsap-from, data-gsap-to |
JSON, for example {"x":-40,"opacity":0} |
— |
data-gsap-on |
scroll, load |
scroll |
data-gsap-delay, data-gsap-duration, data-gsap-repeat-delay |
seconds, for example 0.25 |
0, 0.8, 0 |
data-gsap-ease |
a GSAP ease, for example power3.out, back.out(1.7), steps(5) |
power3.out |
data-gsap-stagger-ease |
a GSAP ease | linear |
data-gsap-repeat |
a whole number, or -1 (no end) |
— |
data-gsap-yoyo |
bare | — |
data-gsap-trigger |
a CSS selector | the element |
data-gsap-start |
top 85%, center center, -40px 50% |
top 85%; top bottom with scrub |
data-gsap-end |
bottom top, +=500 |
ScrollTrigger default; bottom top with scrub |
data-gsap-toggle-actions |
four of play pause resume reverse restart reset complete none |
— |
data-gsap-once |
false |
plays one time (not with a pin or scrub) |
data-gsap-scrub |
true, or seconds |
— |
data-gsap-pin, data-gsap-markers |
bare | — |
data-gsap-stagger |
seconds between children | — |
data-gsap-stagger-from |
start, center, end, edges, random, an index |
start |
data-gsap-split |
chars, words, lines |
— |
data-gsap-split-mask |
bare | — |
data-gsap-parallax |
a factor, for example -0.3 |
0.3 |
data-gsap-position |
<, >, <0.2, +=0.5, -=0.2, 1.5 (only in a timeline) |
the end of the timeline |
data-gsap-reduced |
animate |
no animation for reduced motion |
data-gsap-timeline |
bare, on the timeline element | — |
data-gsap-ignore |
bare, on a region that must not animate | — |
init.js sets data-gsap-init on each element that it starts. A second scan skips these elements.
data-gsap-from and data-gsap-to accept only these names with finite numbers:
x, y, xPercent, yPercent, scale, scaleX, scaleY, rotation, rotationX, rotationY,
skewX, skewY, opacity, autoAlpha.
Rules for Autumn pages
- Do not use inline
<script>. The default CSP isscript-src 'self'. - Do not use inline
<style>. Nonce mode blocks it. - htmx adds an inline indicator style. For a strict CSP, add
meta name="htmx-config" content=r#"{"includeIndicatorStyles":false}"#;. - Put a stagger on the container. Do not put
data-gsapon its children too. - Put SplitText on a heading (
Tag::H1toTag::H6). SplitText puts thearia-labelon that element. Screen readers can ignore anaria-labelon adivorp. - SplitText hides the split parts from screen readers. Thus
init.jsdoes not split text with links or controls. xandxPercentstart states can make the page wider. Putoverflow-x: clipon a parent. Do not putoverflow: hiddenonbody. It stops pins.autoAlpha: 0hides content from screen readers and the Tab key until the tween plays. Preferopacity.
Motion and accessibility
- Use
data-gsap-reduced="animate"(animate_on_reduced_motion()) only for motion that is necessary. - A change of the reduced-motion setting after load reverts or starts the animations.
Repeat::Foreveris motion with no end. WCAG 2.2.2 requires a pause control for it.
Demo
# open http://127.0.0.1:3000
The demo shows a hero timeline with split text, scroll reveals, a pinned scrub timeline, parallax, a progress bar, and htmx append and replace.
Tests
| Command | What it tests |
|---|---|
cargo test |
Builders, assets, plugin routes, conformance, property tests, golden fixture. |
cargo llvm-cov --fail-under-lines 85 |
Rust line coverage. |
node --test tests/js/*.test.mjs |
init.js parsers and scan rules (fake GSAP), golden fixture values. |
cargo build --example gsap_demo && npm --prefix tests/e2e ci && npm --prefix tests/e2e test |
The demo in Chromium (Playwright). It also checks the init.js line coverage (85% or more). |
After a change to an attribute value, run UPDATE_GOLDEN=1 cargo test --test golden.
Limits
- Each plugin release pins one GSAP version. See
assets/manifest.json. - The crate does not include
Flip,Draggable,ScrollSmoother,MorphSVG,DrawSVG,MotionPathor other GSAP plugins. - htmx content that you add into an existing stagger container or timeline does not animate. Send a new container in the partial.
linessplit occurs one time, at scan. A resize does not split the lines again.- Until the deferred scripts run, the browser shows the end state. On a slow network, this can be about one second. Then the content goes to its start state and animates.
- Each element gets its own tween and ScrollTrigger. A scan of 200 elements takes about 80 ms; 1000 elements take about 650 ms. For long lists, animate a container with a stagger.
- htmx Back plays the animations of the restored page again.
- With a swap delay (
hx-swap="outerHTML swap:500ms") on a pinned target, the pin can wrap the new content. Use no delay for pinned targets. - An out-of-band swap (
hx-swap-oob) of a pinned element has the same limit.
Untrusted content
init.js acts on each data-gsap* attribute on the page. User HTML can contain these attributes.
For example, it can move an element over your own UI.
- Remove
data-gsap*attributes from user HTML when you sanitize it. (DOMPurify keepsdata-*by default.) - Or put user HTML in an element with
data-gsap-ignoreorhx-disable.init.jsdoes not animate in these regions. data-gsap-pinpins only the element or an element in it.init.jsdoes not pin other elements.- Do not use SplitText on untrusted rich HTML. A revert parses the saved HTML again.
License
- The crate license expression is
Apache-2.0 AND LicenseRef-GSAP-Standard-License. If you usecargo-deny, addLicenseRef-GSAP-Standard-Licenseto your allow list after you read the terms. - The plugin code: Apache-2.0 (see
LICENSE). - The GSAP files (
assets/gsap.min.js,assets/ScrollTrigger.min.js,assets/SplitText.min.js): GSAP Standard License. GSAP is free, also for commercial use. If a no-code animation tool competes with Webflow, the license does not allow it in that tool. Read the full terms before you use GSAP. SeeLICENSES/LicenseRef-GSAP-Standard-License.txt.