PageSourceSearch

https://gapfy.io/js/scroll-reveal.tmcmthwde9.js

js gapfy.io collected 2026-09-24 15:04:12 UTC 4,093 bytes, 94 lines download raw bytes

1/*
2 * Gapfy scroll-reveal — fade-up sections, cards and headings as they
3 * enter the viewport. Stripe / Linear style: short, subtle, calm.
4 *
5 * Usage in Razor:
6 *   <div class="reveal">...</div>              -> single element
7 *   <div class="reveal-stagger">child*</div>   -> children animate one
8 *                                                 after the other (80ms
9 *                                                 between each)
10 *
11 * Graceful degradation:
12 *   - Without JS, the `.has-js` class never gets added so the CSS
13 *     "hidden" state never applies. Everything is visible by default.
14 *   - With prefers-reduced-motion: reduce, all elements snap to visible
15 *     instantly; no animations run.
16 *   - Without IntersectionObserver (very old browsers), same fallback.
17 *
18 * Blazor Server enhanced navigation swaps the DOM in place when the user
19 * navigates between pages. We re-scan on the `enhancedload` event so new
20 * reveal targets get picked up.
21 */
22(function () {
23    if (typeof window === 'undefined' || typeof document === 'undefined') return;
24
25    // Mark the document so CSS can apply the initial "hidden" state only
26    // when JS is actually running. Without this class, .reveal stays
27    // visible (graceful no-JS fallback).
28    document.documentElement.classList.add('has-js');
29
30    var reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
31    var hasIO = 'IntersectionObserver' in window;
32
33    // If we cannot observe or the user prefers no motion, snap everything
34    // to visible the moment it lands in the DOM.
35    if (!hasIO || reducedMotion) {
36        var snapAll = function () {
37            var nodes = document.querySelectorAll('.reveal, .reveal-stagger > *');
38            for (var i = 0; i < nodes.length; i++) {
39                nodes[i].classList.add('is-visible');
40            }
41        };
42        document.addEventListener('DOMContentLoaded', snapAll);
43        document.addEventListener('enhancedload', snapAll);
44        // Run now in case DOMContentLoaded already fired.
45        snapAll();
46        return;
47    }
48
49    var io = new IntersectionObserver(function (entries) {
50        for (var i = 0; i < entries.length; i++) {
51            var entry = entries[i];
52            if (!entry.isIntersecting) continue;
53            entry.target.classList.add('is-visible');
54            io.unobserve(entry.target);
55        }
56    }, {
57        // Trigger slightly before the element fully reaches the viewport
58        // so the animation finishes around the time the user notices it.
59        rootMargin: '0px 0px -10% 0px',
60        threshold: 0.05
61    });
62
63    function observeAll() {
64        // Plain reveal — single element fades up when seen.
65        var singles = document.querySelectorAll('.reveal:not(.is-visible)');
66        for (var i = 0; i < singles.length; i++) {
67            io.observe(singles[i]);
68        }
69
70        // Stagger — children fade up one after the other. We mark the
71        // wrapper visible immediately to avoid running observeAll again
72        // on the same parent (the .reveal class on children carries the
73        // actual animation; the wrapper is just a marker).
74        var staggers = document.querySelectorAll('.reveal-stagger:not(.has-stagger)');
75        for (var j = 0; j < staggers.length; j++) {
76            var parent = staggers[j];
77            parent.classList.add('has-stagger');
78            var children = parent.children;
79            for (var k = 0; k < children.length; k++) {
80                var child = children[k];
81                if (child.classList.contains('reveal')) continue;
82                child.classList.add('reveal');
83                child.style.setProperty('--reveal-delay', (k * 80) + 'ms');
84                io.observe(child);
85            }
86        }
87    }
88
89    document.addEventListener('DOMContentLoaded', observeAll);
90    document.addEventListener('enhancedload', observeAll);
91    // Run now in case DOMContentLoaded already fired (script ran late
92    // because it's at the bottom of the body or was injected after).
93    observeAll();
94})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.