animate
Scroll-triggered animations using IntersectionObserver.
Usage
<div data-controller="animate"
data-animate-class-value="animate-fade-in">
Content that animates when scrolled into view
</div>
<!-- With delay for staggered animations -->
<div data-controller="animate"
data-animate-class-value="animate-fade-in"
data-animate-delay-value="200">
Delayed animation
</div>
Values
| Value | Type | Default | Description |
|---|---|---|---|
class |
String | "animate-in" |
CSS class to add when visible |
delay |
Number | 0 |
Delay in milliseconds |
threshold |
Number | 0.1 |
Visibility threshold (0-1) |
CSS Animation Classes
/* Initial state - invisible */
[data-controller="animate"] {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.6s ease, transform 0.6s ease;
}
/* Animated state */
.animate-fade-in {
opacity: 1;
transform: translateY(0);
}
Source
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
static values = {
class: { type: String, default: "animate-in" },
delay: { type: Number, default: 0 },
threshold: { type: Number, default: 0.1 },
};
connect() {
this.observer = new IntersectionObserver(
(entries) => this.handleIntersect(entries),
{ threshold: this.thresholdValue }
);
this.observer.observe(this.element);
}
disconnect() {
this.observer.disconnect();
}
handleIntersect(entries) {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setTimeout(() => {
this.element.classList.add(this.classValue);
}, this.delayValue);
this.observer.unobserve(this.element);
}
});
}
}
Staggered Animations
Use increasing delay values for a staggered effect:
<div class="grid grid-cols-3 gap-4">
<div data-controller="animate" data-animate-delay-value="0">Card 1</div>
<div data-controller="animate" data-animate-delay-value="100">Card 2</div>
<div data-controller="animate" data-animate-delay-value="200">Card 3</div>
</div>