Scroll reveal animations in pure CSS with a small vanilla JavaScript helper: headings, step cards, stats and a call-to-action fade, slide, zoom or flip into place as they enter the viewport. It is a lightweight alternative to animation libraries for landing pages, feature sections, timelines and portfolios.
How it works
Every element that should animate gets a data-sr attribute with an effect name: fade-up, fade-down, fade-left, fade-right, zoom or flip. The CSS defines the hidden start state for each effect (an opacity of 0 and a starting transform stored in --sr-from). Adding .sr-visible plays the sr-reveal keyframes from that transform to the resting position, using --sr-duration and the ease-out curve in --sr-easing, so the motion settles gently.
On DOMContentLoaded the script finds every .sr-root, reads data-sr-threshold and data-sr-repeat, and creates one IntersectionObserver per root. It copies each data-sr-delay value (in milliseconds) into the --sr-delay custom property, which the CSS uses as animation-delay to stagger neighbouring items. Only then does it add .sr-ready to the root and immediately reveals items that are already on screen. The hidden states are all scoped under .sr-ready, so if JavaScript fails or IntersectionObserver is missing, the content simply stays visible. Other items receive .sr-visible when they intersect; in the default one-shot mode the observer then stops watching it.
The demo section is a dark navy "workflow" layout: a header, four numbered .sr-step cards that slide in from alternating sides, three .sr-stat counters that zoom in, and a gradient .sr-cta panel that flips up.
Customization
data-sr="fade-up"on any element inside.sr-root: switch tofade-left,fade-right,fade-down,zoomorflipto change the entrance effect.data-sr-delay="200": delays that element's animation by 200 ms; increase the value step by step to stagger a group.data-sr-threshold="0.2"on.sr-root: how much of an element (0 to 1) must be visible before it animates.data-sr-repeat="true"on.sr-root: elements hide again when they leave the viewport and replay every time they return.--sr-distanceinstyle.css: how far items travel (default2.5rem); smaller values give a subtler effect.--sr-durationand--sr-easing: the speed and curve of every reveal (default0.8s).--sr-accentand--sr-accent-2: the amber-to-coral gradient on the step numbers, stat values and the call-to-action panel.
Accessibility
The section is labelled by its heading with aria-labelledby, and the steps are an ordered list. The decorative step numbers are aria-hidden because the list already conveys order. Content is in the DOM from the start, so screen readers read it regardless of animation state. When the user has prefers-reduced-motion: reduce, the script does not hide anything and the CSS also forces every item to be visible with no animation. The call-to-action link has a clear :focus-visible outline.
Usage notes
You can place several .sr-root wrappers on one page; each has its own observer and settings. Avoid putting data-sr on elements that contain focusable controls you expect users to tab to before scrolling, or give them fade-up with a short distance.