Entrance animations are one attribute: data-animate="fade-up" plays the preset once when the element scrolls into view.
Built-in presets
fade-in, fade-up, fade-down, fade-left, fade-right, scale-up, blur-in, slide-up, flip-in, spring, bounce-in, thud-in, tile-flip
Controls
data-animate-trigger="load"plays immediately instead of on scroll.data-animate-stagger="80ms"on the PARENT delays eachdata-animatechild by one step more (a row of cards).data-animate-exit="fade-out"plays that exit preset when the element scrolls back out, and the entrance again on return. Exit presets:fade-out,fade-out-up,fade-out-down.- Reduced motion is automatic — visitors with prefers-reduced-motion get no animation. Don't add your own media query.
When a preset doesn't cover it
Add the @keyframes and a class to theme.css with edit_site_file (a motion specific to this site: a line drawing in, dashes marching, a bar filling), then put the class on the element. Animate transform, opacity, clip-path or background-position, never layout properties (width, height, top, left, margin, padding): they run on the main thread and shift the page. A fill is transform: scaleX() or clip-path: inset(). Never @keyframes in a page file or animate-* Tailwind classes for entrances.