I’ll build this as a standalone advanced scroll-animation chunk for RX Theme: safe defaults, accessibility support, performance-friendly observers, counters, progress bars, parallax, sticky reveal, and WordPress-friendly auto-init.
Below is a complete advanced code for:
assets/static-js/static-chunks-js/chunk-019-scroll-animation.js
It is written as a safe, performance-focused, WordPress/RX Theme friendly scroll animation chunk.
/*!
* RX Theme - Chunk 019 Scroll Animation
* File: assets/static-js/static-chunks-js/chunk-019-scroll-animation.js
* Version: 1.0.0
*
* Features:
* - Reveal on scroll
* - Stagger animation
* - Counter animation
* - Progress bar animation
* - Circle progress animation
* - Parallax effect
* - Sticky class on scroll
* - Scroll progress indicator
* - Back to top button
* - Header show/hide on scroll
* - Section active class
* - Reduced motion support
* - IntersectionObserver based performance
* - WordPress friendly auto-init
*/
(function () {
'use strict';
/**
* Prevent duplicate loading.
*/
if (window.RXScrollAnimationLoaded) {
return;
}
window.RXScrollAnimationLoaded = true;
/**
* Main namespace.
*/
window.RXTheme = window.RXTheme || {};
const RXScrollAnimation = {
config: {
revealSelector: '[data-rx-reveal], .rx-reveal',
staggerSelector: '[data-rx-stagger]',
counterSelector: '[data-rx-counter]',
progressSelector: '[data-rx-progress]',
circleProgressSelector: '[data-rx-circle-progress]',
parallaxSelector: '[data-rx-parallax]',
stickySelector: '[data-rx-sticky]',
sectionSelector: '[data-rx-section]',
scrollProgressSelector: '[data-rx-scroll-progress]',
backToTopSelector: '[data-rx-back-to-top]',
headerSelector: '[data-rx-scroll-header]',
revealVisibleClass: 'rx-is-visible',
revealHiddenClass: 'rx-is-hidden',
activeClass: 'rx-is-active',
stickyClass: 'rx-is-sticky',
headerHiddenClass: 'rx-header-hidden',
headerVisibleClass: 'rx-header-visible',
rootMargin: '0px 0px -10% 0px',
threshold: 0.12,
counterDuration: 1600,
progressDuration: 1400,
circleDuration: 1400,
parallaxStrength: 0.18,
stickyOffset: 80,
headerHideOffset: 120,
backToTopOffset: 400,
enableReveal: true,
enableStagger: true,
enableCounters: true,
enableProgress: true,
enableCircleProgress: true,
enableParallax: true,
enableSticky: true,
enableScrollProgress: true,
enableBackToTop: true,
enableHeaderScroll: true,
enableSectionSpy: true
},
state: {
lastScrollY: 0,
ticking: false,
reducedMotion: false,
observers: [],
animatedCounters: new WeakSet(),
animatedProgress: new WeakSet(),
animatedCircleProgress: new WeakSet()
},
init() {
this.state.reducedMotion = window.matchMedia &&
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
this.applyBaseClasses();
if (this.config.enableReveal) {
this.initReveal();
}
if (this.config.enableStagger) {
this.initStagger();
}
if (this.config.enableCounters) {
this.initCounters();
}
if (this.config.enableProgress) {
this.initProgressBars();
}
if (this.config.enableCircleProgress) {
this.initCircleProgress();
}
if (this.config.enableScrollProgress) {
this.initScrollProgress();
}
if (this.config.enableBackToTop) {
this.initBackToTop();
}
if (
this.config.enableParallax ||
this.config.enableSticky ||
this.config.enableHeaderScroll ||
this.config.enableSectionSpy
) {
this.bindScrollEvents();
}
this.bindResizeEvents();
document.documentElement.classList.add('rx-scroll-animation-ready');
},
applyBaseClasses() {
const revealItems = document.querySelectorAll(this.config.revealSelector);
revealItems.forEach((el) => {
if (!el.classList.contains(this.config.revealVisibleClass)) {
el.classList.add(this.config.revealHiddenClass);
}
});
},
createObserver(callback, options = {}) {
if (!('IntersectionObserver' in window)) {
return null;
}
const observer = new IntersectionObserver(callback, {
root: null,
rootMargin: options.rootMargin || this.config.rootMargin,
threshold: options.threshold || this.config.threshold
});
this.state.observers.push(observer);
return observer;
},
initReveal() {
const elements = document.querySelectorAll(this.config.revealSelector);
if (!elements.length) {
return;
}
if (this.state.reducedMotion || !('IntersectionObserver' in window)) {
elements.forEach((el) => this.showElement(el));
return;
}
const observer = this.createObserver((entries, obs) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
this.showElement(entry.target);
obs.unobserve(entry.target);
}
});
});
elements.forEach((el) => observer.observe(el));
},
showElement(el) {
const delay = parseInt(el.getAttribute('data-rx-delay') || '0', 10);
const duration = parseInt(el.getAttribute('data-rx-duration') || '650', 10);
const animation = el.getAttribute('data-rx-animation') || '';
if (animation) {
el.setAttribute('data-rx-animation-active', animation);
}
el.style.transitionDuration = `${duration}ms`;
window.setTimeout(() => {
el.classList.remove(this.config.revealHiddenClass);
el.classList.add(this.config.revealVisibleClass);
}, Math.max(delay, 0));
},
initStagger() {
const groups = document.querySelectorAll(this.config.staggerSelector);
if (!groups.length) {
return;
}
groups.forEach((group) => {
const childrenSelector = group.getAttribute('data-rx-stagger-child') || '> *';
const delayStep = parseInt(group.getAttribute('data-rx-stagger-delay') || '90', 10);
const children = group.querySelectorAll(childrenSelector);
children.forEach((child, index) => {
child.classList.add(this.config.revealHiddenClass);
child.setAttribute('data-rx-delay', String(index * delayStep));
});
});
if (this.state.reducedMotion || !('IntersectionObserver' in window)) {
groups.forEach((group) => {
const childrenSelector = group.getAttribute('data-rx-stagger-child') || '> *';
group.querySelectorAll(childrenSelector).forEach((child) => this.showElement(child));
});
return;
}
const observer = this.createObserver((entries, obs) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) {
return;
}
const group = entry.target;
const childrenSelector = group.getAttribute('data-rx-stagger-child') || '> *';
const children = group.querySelectorAll(childrenSelector);
children.forEach((child) => this.showElement(child));
obs.unobserve(group);
});
});
groups.forEach((group) => observer.observe(group));
},
initCounters() {
const counters = document.querySelectorAll(this.config.counterSelector);
if (!counters.length) {
return;
}
if (this.state.reducedMotion || !('IntersectionObserver' in window)) {
counters.forEach((counter) => this.finishCounter(counter));
return;
}
const observer = this.createObserver((entries, obs) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
this.animateCounter(entry.target);
obs.unobserve(entry.target);
}
});
});
counters.forEach((counter) => observer.observe(counter));
},
animateCounter(el) {
if (this.state.animatedCounters.has(el)) {
return;
}
this.state.animatedCounters.add(el);
const start = parseFloat(el.getAttribute('data-rx-counter-start') || '0');
const end = parseFloat(el.getAttribute('data-rx-counter') || el.textContent || '0');
const duration = parseInt(el.getAttribute('data-rx-counter-duration') || this.config.counterDuration, 10);
const decimals = parseInt(el.getAttribute('data-rx-counter-decimals') || '0', 10);
const prefix = el.getAttribute('data-rx-counter-prefix') || '';
const suffix = el.getAttribute('data-rx-counter-suffix') || '';
const separator = el.getAttribute('data-rx-counter-separator') || '';
const startTime = performance.now();
const update = (now) => {
const elapsed = now - startTime;
const progress = Math.min(elapsed / duration, 1);
const eased = this.easeOutCubic(progress);
const value = start + (end - start) * eased;
el.textContent = prefix + this.formatNumber(value, decimals, separator) + suffix;
if (progress < 1) {
requestAnimationFrame(update);
} else {
el.textContent = prefix + this.formatNumber(end, decimals, separator) + suffix;
}
};
requestAnimationFrame(update);
},
finishCounter(el) {
const end = parseFloat(el.getAttribute('data-rx-counter') || el.textContent || '0');
const decimals = parseInt(el.getAttribute('data-rx-counter-decimals') || '0', 10);
const prefix = el.getAttribute('data-rx-counter-prefix') || '';
const suffix = el.getAttribute('data-rx-counter-suffix') || '';
const separator = el.getAttribute('data-rx-counter-separator') || '';
el.textContent = prefix + this.formatNumber(end, decimals, separator) + suffix;
},
initProgressBars() {
const bars = document.querySelectorAll(this.config.progressSelector);
if (!bars.length) {
return;
}
bars.forEach((bar) => {
const value = this.clamp(parseFloat(bar.getAttribute('data-rx-progress') || '0'), 0, 100);
bar.style.setProperty('--rx-progress-value', '0%');
bar.setAttribute('aria-valuemin', '0');
bar.setAttribute('aria-valuemax', '100');
bar.setAttribute('aria-valuenow', '0');
bar.setAttribute('role', 'progressbar');
bar.dataset.rxProgressTarget = String(value);
});
if (this.state.reducedMotion || !('IntersectionObserver' in window)) {
bars.forEach((bar) => this.finishProgressBar(bar));
return;
}
const observer = this.createObserver((entries, obs) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
this.animateProgressBar(entry.target);
obs.unobserve(entry.target);
}
});
});
bars.forEach((bar) => observer.observe(bar));
},
animateProgressBar(bar) {
if (this.state.animatedProgress.has(bar)) {
return;
}
this.state.animatedProgress.add(bar);
const target = this.clamp(parseFloat(bar.dataset.rxProgressTarget || '0'), 0, 100);
const duration = parseInt(bar.getAttribute('data-rx-progress-duration') || this.config.progressDuration, 10);
const startTime = performance.now();
const update = (now) => {
const elapsed = now - startTime;
const progress = Math.min(elapsed / duration, 1);
const eased = this.easeOutCubic(progress);
const value = target * eased;
bar.style.setProperty('--rx-progress-value', `${value}%`);
bar.setAttribute('aria-valuenow', String(Math.round(value)));
if (progress < 1) {
requestAnimationFrame(update);
} else {
bar.style.setProperty('--rx-progress-value', `${target}%`);
bar.setAttribute('aria-valuenow', String(Math.round(target)));
}
};
requestAnimationFrame(update);
},
finishProgressBar(bar) {
const target = this.clamp(parseFloat(bar.dataset.rxProgressTarget || '0'), 0, 100);
bar.style.setProperty('--rx-progress-value', `${target}%`);
bar.setAttribute('aria-valuenow', String(Math.round(target)));
},
initCircleProgress() {
const circles = document.querySelectorAll(this.config.circleProgressSelector);
if (!circles.length) {
return;
}
circles.forEach((circle) => {
const value = this.clamp(parseFloat(circle.getAttribute('data-rx-circle-progress') || '0'), 0, 100);
circle.style.setProperty('--rx-circle-progress-value', '0');
circle.setAttribute('aria-valuemin', '0');
circle.setAttribute('aria-valuemax', '100');
circle.setAttribute('aria-valuenow', '0');
circle.setAttribute('role', 'progressbar');
circle.dataset.rxCircleProgressTarget = String(value);
});
if (this.state.reducedMotion || !('IntersectionObserver' in window)) {
circles.forEach((circle) => this.finishCircleProgress(circle));
return;
}
const observer = this.createObserver((entries, obs) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
this.animateCircleProgress(entry.target);
obs.unobserve(entry.target);
}
});
});
circles.forEach((circle) => observer.observe(circle));
},
animateCircleProgress(circle) {
if (this.state.animatedCircleProgress.has(circle)) {
return;
}
this.state.animatedCircleProgress.add(circle);
const target = this.clamp(parseFloat(circle.dataset.rxCircleProgressTarget || '0'), 0, 100);
const duration = parseInt(circle.getAttribute('data-rx-circle-duration') || this.config.circleDuration, 10);
const label = circle.querySelector('[data-rx-circle-label]');
const startTime = performance.now();
const update = (now) => {
const elapsed = now - startTime;
const progress = Math.min(elapsed / duration, 1);
const eased = this.easeOutCubic(progress);
const value = target * eased;
circle.style.setProperty('--rx-circle-progress-value', String(value));
circle.setAttribute('aria-valuenow', String(Math.round(value)));
if (label) {
label.textContent = `${Math.round(value)}%`;
}
if (progress < 1) {
requestAnimationFrame(update);
} else {
circle.style.setProperty('--rx-circle-progress-value', String(target));
circle.setAttribute('aria-valuenow', String(Math.round(target)));
if (label) {
label.textContent = `${Math.round(target)}%`;
}
}
};
requestAnimationFrame(update);
},
finishCircleProgress(circle) {
const target = this.clamp(parseFloat(circle.dataset.rxCircleProgressTarget || '0'), 0, 100);
const label = circle.querySelector('[data-rx-circle-label]');
circle.style.setProperty('--rx-circle-progress-value', String(target));
circle.setAttribute('aria-valuenow', String(Math.round(target)));
if (label) {
label.textContent = `${Math.round(target)}%`;
}
},
initScrollProgress() {
const progressItems = document.querySelectorAll(this.config.scrollProgressSelector);
if (!progressItems.length) {
return;
}
this.updateScrollProgress();
},
initBackToTop() {
const buttons = document.querySelectorAll(this.config.backToTopSelector);
if (!buttons.length) {
return;
}
buttons.forEach((button) => {
button.setAttribute('type', 'button');
button.addEventListener('click', (event) => {
event.preventDefault();
window.scrollTo({
top: 0,
behavior: this.state.reducedMotion ? 'auto' : 'smooth'
});
});
});
this.updateBackToTop();
},
bindScrollEvents() {
window.addEventListener(
'scroll',
() => {
this.requestTick();
},
{
passive: true
}
);
this.requestTick();
},
bindResizeEvents() {
let resizeTimer = null;
window.addEventListener(
'resize',
() => {
window.clearTimeout(resizeTimer);
resizeTimer = window.setTimeout(() => {
this.requestTick();
}, 120);
},
{
passive: true
}
);
},
requestTick() {
if (!this.state.ticking) {
window.requestAnimationFrame(() => {
this.onScrollFrame();
this.state.ticking = false;
});
this.state.ticking = true;
}
},
onScrollFrame() {
const currentScrollY = window.scrollY || window.pageYOffset || 0;
const direction = currentScrollY > this.state.lastScrollY ? 'down' : 'up';
if (this.config.enableParallax && !this.state.reducedMotion) {
this.updateParallax(currentScrollY);
}
if (this.config.enableSticky) {
this.updateSticky(currentScrollY);
}
if (this.config.enableScrollProgress) {
this.updateScrollProgress();
}
if (this.config.enableBackToTop) {
this.updateBackToTop(currentScrollY);
}
if (this.config.enableHeaderScroll) {
this.updateHeaderScroll(currentScrollY, direction);
}
if (this.config.enableSectionSpy) {
this.updateSectionSpy();
}
this.state.lastScrollY = Math.max(currentScrollY, 0);
},
updateParallax(scrollY) {
const items = document.querySelectorAll(this.config.parallaxSelector);
if (!items.length) {
return;
}
items.forEach((el) => {
const rect = el.getBoundingClientRect();
const speed = parseFloat(el.getAttribute('data-rx-parallax-speed') || this.config.parallaxStrength);
const direction = el.getAttribute('data-rx-parallax-direction') || 'vertical';
const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
if (rect.bottom < 0 || rect.top > viewportHeight) {
return;
}
const centerOffset = rect.top + rect.height / 2 - viewportHeight / 2;
const movement = centerOffset * speed * -1;
if (direction === 'horizontal') {
el.style.transform = `translate3d(${movement}px, 0, 0)`;
} else {
el.style.transform = `translate3d(0, ${movement}px, 0)`;
}
});
},
updateSticky(scrollY) {
const items = document.querySelectorAll(this.config.stickySelector);
if (!items.length) {
return;
}
items.forEach((el) => {
const offset = parseInt(el.getAttribute('data-rx-sticky-offset') || this.config.stickyOffset, 10);
const targetSelector = el.getAttribute('data-rx-sticky-target');
const target = targetSelector ? document.querySelector(targetSelector) : null;
let shouldStick = scrollY > offset;
if (target) {
const targetRect = target.getBoundingClientRect();
shouldStick = targetRect.top <= offset && targetRect.bottom > offset;
}
el.classList.toggle(this.config.stickyClass, shouldStick);
});
},
updateScrollProgress() {
const progressItems = document.querySelectorAll(this.config.scrollProgressSelector);
if (!progressItems.length) {
return;
}
const doc = document.documentElement;
const scrollTop = window.scrollY || doc.scrollTop || 0;
const scrollHeight = doc.scrollHeight - doc.clientHeight;
const percentage = scrollHeight > 0 ? (scrollTop / scrollHeight) * 100 : 0;
const safePercentage = this.clamp(percentage, 0, 100);
progressItems.forEach((el) => {
el.style.setProperty('--rx-scroll-progress', `${safePercentage}%`);
el.setAttribute('aria-valuemin', '0');
el.setAttribute('aria-valuemax', '100');
el.setAttribute('aria-valuenow', String(Math.round(safePercentage)));
el.setAttribute('role', 'progressbar');
});
},
updateBackToTop(scrollY) {
const buttons = document.querySelectorAll(this.config.backToTopSelector);
if (!buttons.length) {
return;
}
const currentScrollY = typeof scrollY === 'number'
? scrollY
: window.scrollY || window.pageYOffset || 0;
buttons.forEach((button) => {
const offset = parseInt(button.getAttribute('data-rx-back-to-top-offset') || this.config.backToTopOffset, 10);
button.classList.toggle(this.config.activeClass, currentScrollY > offset);
button.setAttribute('aria-hidden', currentScrollY > offset ? 'false' : 'true');
});
},
updateHeaderScroll(scrollY, direction) {
const headers = document.querySelectorAll(this.config.headerSelector);
if (!headers.length) {
return;
}
headers.forEach((header) => {
const offset = parseInt(header.getAttribute('data-rx-header-offset') || this.config.headerHideOffset, 10);
const alwaysShowAtTop = header.getAttribute('data-rx-header-show-top') !== 'false';
if (alwaysShowAtTop && scrollY <= offset) {
header.classList.remove(this.config.headerHiddenClass);
header.classList.add(this.config.headerVisibleClass);
return;
}
if (direction === 'down' && scrollY > offset) {
header.classList.add(this.config.headerHiddenClass);
header.classList.remove(this.config.headerVisibleClass);
} else {
header.classList.remove(this.config.headerHiddenClass);
header.classList.add(this.config.headerVisibleClass);
}
});
},
updateSectionSpy() {
const sections = document.querySelectorAll(this.config.sectionSelector);
if (!sections.length) {
return;
}
let activeSection = null;
const viewportMiddle = window.innerHeight * 0.38;
sections.forEach((section) => {
const rect = section.getBoundingClientRect();
if (rect.top <= viewportMiddle && rect.bottom >= viewportMiddle) {
activeSection = section;
}
});
sections.forEach((section) => {
section.classList.toggle(this.config.activeClass, section === activeSection);
});
if (activeSection && activeSection.id) {
this.updateActiveNavLink(activeSection.id);
}
},
updateActiveNavLink(sectionId) {
const links = document.querySelectorAll(`[data-rx-section-link], a[href="#${CSS.escape(sectionId)}"]`);
if (!links.length) {
return;
}
const allSectionLinks = document.querySelectorAll('[data-rx-section-link], a[href^="#"]');
allSectionLinks.forEach((link) => {
const href = link.getAttribute('href') || '';
const target = link.getAttribute('data-rx-section-link') || href.replace('#', '');
const isActive = target === sectionId;
link.classList.toggle(this.config.activeClass, isActive);
if (isActive) {
link.setAttribute('aria-current', 'true');
} else {
link.removeAttribute('aria-current');
}
});
},
easeOutCubic(value) {
return 1 - Math.pow(1 - value, 3);
},
formatNumber(value, decimals, separator) {
const fixed = Number(value).toFixed(decimals);
if (!separator) {
return fixed;
}
const parts = fixed.split('.');
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, separator);
return parts.join('.');
},
clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
},
refresh() {
this.destroy();
this.init();
},
destroy() {
this.state.observers.forEach((observer) => observer.disconnect());
this.state.observers = [];
this.state.ticking = false;
}
};
window.RXTheme.ScrollAnimation = RXScrollAnimation;
/**
* Auto init.
*/
const boot = () => {
RXScrollAnimation.init();
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', boot, {
once: true
});
} else {
boot();
}
/**
* Support WordPress Customizer partial refresh / AJAX content.
*/
document.addEventListener('rxThemeContentUpdated', () => {
RXScrollAnimation.refresh();
});
document.addEventListener('wp-customize-preview-init', () => {
RXScrollAnimation.refresh();
});
})();
Helper CSS needed for this JS
Add this in your main CSS or animation CSS file, for example:
assets/static-css/components/_scroll-animation.css
.rx-scroll-animation-ready .rx-is-hidden {
opacity: 0;
transform: translate3d(0, 28px, 0);
transition-property: opacity, transform;
transition-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
will-change: opacity, transform;
}
.rx-scroll-animation-ready .rx-is-visible {
opacity: 1;
transform: translate3d(0, 0, 0);
}
[data-rx-animation-active="fade-up"] {
transform: translate3d(0, 32px, 0);
}
[data-rx-animation-active="fade-down"] {
transform: translate3d(0, -32px, 0);
}
[data-rx-animation-active="fade-left"] {
transform: translate3d(32px, 0, 0);
}
[data-rx-animation-active="fade-right"] {
transform: translate3d(-32px, 0, 0);
}
.rx-is-visible[data-rx-animation-active="fade-up"],
.rx-is-visible[data-rx-animation-active="fade-down"],
.rx-is-visible[data-rx-animation-active="fade-left"],
.rx-is-visible[data-rx-animation-active="fade-right"] {
transform: translate3d(0, 0, 0);
}
[data-rx-progress] {
position: relative;
overflow: hidden;
}
[data-rx-progress]::before {
content: "";
position: absolute;
inset-block: 0;
inset-inline-start: 0;
width: var(--rx-progress-value, 0%);
transition: width 0.3s ease;
}
[data-rx-scroll-progress] {
--rx-scroll-progress: 0%;
}
[data-rx-scroll-progress]::before {
width: var(--rx-scroll-progress);
}
[data-rx-back-to-top] {
opacity: 0;
visibility: hidden;
pointer-events: none;
transform: translateY(12px);
transition: opacity 0.25s ease, transform 0.25s ease, visibility 0.25s ease;
}
[data-rx-back-to-top].rx-is-active {
opacity: 1;
visibility: visible;
pointer-events: auto;
transform: translateY(0);
}
[data-rx-scroll-header] {
transition: transform 0.25s ease;
will-change: transform;
}
[data-rx-scroll-header].rx-header-hidden {
transform: translateY(-100%);
}
[data-rx-scroll-header].rx-header-visible {
transform: translateY(0);
}
[data-rx-sticky].rx-is-sticky {
position: sticky;
top: var(--rx-sticky-top, 0);
z-index: 50;
}
@media (prefers-reduced-motion: reduce) {
.rx-scroll-animation-ready .rx-is-hidden,
.rx-scroll-animation-ready .rx-is-visible,
[data-rx-back-to-top],
[data-rx-scroll-header] {
transition: none !important;
transform: none !important;
}
}
Example HTML usage
<div class="rx-reveal" data-rx-animation="fade-up">
This content reveals on scroll.
</div>
<div data-rx-stagger data-rx-stagger-delay="120">
<div>First card</div>
<div>Second card</div>
<div>Third card</div>
</div>
<h2>
<span data-rx-counter="2500" data-rx-counter-separator=",">0</span>+
</h2>
<div class="rx-progress" data-rx-progress="75"></div>
<div data-rx-parallax data-rx-parallax-speed="0.12">
Parallax content
</div>
<header data-rx-scroll-header>
Header content
</header>
<button data-rx-back-to-top>
Back to top
</button>
<div data-rx-scroll-progress class="rx-scroll-progress"></div>
<section id="about" data-rx-section>
About section
</section>
<a href="#about" data-rx-section-link="about">About</a>
Best enqueue position in RX Theme
Use this file with defer:
wp_enqueue_script(
'rx-scroll-animation',
get_template_directory_uri() . '/assets/static-js/static-chunks-js/chunk-019-scroll-animation.js',
array(),
RX_THEME_VERSION,
true
);
wp_script_add_data('rx-scroll-animation', 'defer', true);
This file is best as a frontend-only chunk, not admin-side.