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.