I’ll make this as a strong, self-contained chunk-006-back-to-top.js file: fast, accessible, mobile-friendly, customizable, and safe for WordPress theme loading.
For this file:
assets/static-js/static-chunks-js/chunk-006-back-to-top.js
Use this advanced Back To Top chunk. It creates the button automatically, supports scroll progress, accessibility, keyboard control, reduced-motion, mobile safe behavior, custom CSS variables, footer collision protection, and WordPress-friendly loading.
/*!
* RX Theme - Back To Top Advanced Chunk
* File: assets/static-js/static-chunks-js/chunk-006-back-to-top.js
* Author: RxHarun
* Purpose: Advanced, accessible, performance-friendly Back To Top button
*/
(function () {
"use strict";
/**
* ============================================================
* RX Back To Top Configuration
* ============================================================
*/
const RX_BACK_TO_TOP_CONFIG = {
buttonId: "rx-back-to-top",
progressId: "rx-back-to-top-progress",
wrapperClass: "rx-back-to-top-wrap",
visibleClass: "is-visible",
activeClass: "is-active",
scrollingClass: "is-scrolling",
footerAwareClass: "is-footer-aware",
showAfter: 320,
hideBelow: 200,
scrollDuration: 550,
scrollOffset: 0,
rightDesktop: 24,
bottomDesktop: 24,
rightMobile: 16,
bottomMobile: 16,
zIndex: 9999,
buttonSizeDesktop: 52,
buttonSizeMobile: 46,
enableProgressRing: true,
enableKeyboardShortcut: true,
enableFooterCollisionProtection: true,
enableAutoCreateStyle: true,
enableReducedMotionRespect: true,
enableScrollSpyClass: true,
keyboardShortcutKey: "Home",
keyboardShortcutAlt: true,
footerSelectors: [
"footer",
".site-footer",
"#colophon",
".rx-footer",
"[data-rx-footer]"
],
ignoreWhenSelectorsExist: [
".wp-admin",
".block-editor-page"
],
ariaLabel: "Back to top",
title: "Back to top",
iconSvg:
'<svg class="rx-back-to-top-icon" width="22" height="22" viewBox="0 0 24 24" aria-hidden="true" focusable="false">' +
'<path d="M12 5.25c.28 0 .55.11.75.31l6 6a1.06 1.06 0 0 1-1.5 1.5L13.06 8.87V18a1.06 1.06 0 0 1-2.12 0V8.87l-4.19 4.19a1.06 1.06 0 0 1-1.5-1.5l6-6c.2-.2.47-.31.75-.31Z" fill="currentColor"></path>' +
'</svg>'
};
/**
* ============================================================
* Safe Helpers
* ============================================================
*/
const doc = document;
const win = window;
const html = doc.documentElement;
const body = doc.body;
if (!body || !html) {
return;
}
function qs(selector, context) {
return (context || doc).querySelector(selector);
}
function qsa(selector, context) {
return Array.prototype.slice.call(
(context || doc).querySelectorAll(selector)
);
}
function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}
function isNumber(value) {
return typeof value === "number" && Number.isFinite(value);
}
function prefersReducedMotion() {
return (
RX_BACK_TO_TOP_CONFIG.enableReducedMotionRespect &&
win.matchMedia &&
win.matchMedia("(prefers-reduced-motion: reduce)").matches
);
}
function isMobileViewport() {
return win.matchMedia && win.matchMedia("(max-width: 767px)").matches;
}
function getScrollTop() {
return win.pageYOffset || html.scrollTop || body.scrollTop || 0;
}
function getDocumentHeight() {
return Math.max(
body.scrollHeight,
body.offsetHeight,
html.clientHeight,
html.scrollHeight,
html.offsetHeight
);
}
function getViewportHeight() {
return win.innerHeight || html.clientHeight || 0;
}
function getMaxScrollTop() {
return Math.max(0, getDocumentHeight() - getViewportHeight());
}
function getScrollProgress() {
const maxScroll = getMaxScrollTop();
if (maxScroll <= 0) {
return 0;
}
return clamp((getScrollTop() / maxScroll) * 100, 0, 100);
}
function shouldIgnorePage() {
return RX_BACK_TO_TOP_CONFIG.ignoreWhenSelectorsExist.some(function (selector) {
return qs(selector);
});
}
function rafThrottle(callback) {
let ticking = false;
return function throttled() {
const args = arguments;
const context = this;
if (!ticking) {
win.requestAnimationFrame(function () {
callback.apply(context, args);
ticking = false;
});
ticking = true;
}
};
}
/**
* ============================================================
* Auto CSS Injection
* ============================================================
*/
function injectBackToTopStyles() {
if (!RX_BACK_TO_TOP_CONFIG.enableAutoCreateStyle) {
return;
}
if (qs("#rx-back-to-top-style")) {
return;
}
const style = doc.createElement("style");
style.id = "rx-back-to-top-style";
style.textContent = `
:root {
--rx-btt-size: ${RX_BACK_TO_TOP_CONFIG.buttonSizeDesktop}px;
--rx-btt-size-mobile: ${RX_BACK_TO_TOP_CONFIG.buttonSizeMobile}px;
--rx-btt-right: ${RX_BACK_TO_TOP_CONFIG.rightDesktop}px;
--rx-btt-bottom: ${RX_BACK_TO_TOP_CONFIG.bottomDesktop}px;
--rx-btt-right-mobile: ${RX_BACK_TO_TOP_CONFIG.rightMobile}px;
--rx-btt-bottom-mobile: ${RX_BACK_TO_TOP_CONFIG.bottomMobile}px;
--rx-btt-z-index: ${RX_BACK_TO_TOP_CONFIG.zIndex};
--rx-btt-bg: #0f172a;
--rx-btt-color: #ffffff;
--rx-btt-bg-hover: #2563eb;
--rx-btt-shadow: 0 12px 30px rgba(15, 23, 42, 0.22);
--rx-btt-shadow-hover: 0 16px 40px rgba(37, 99, 235, 0.32);
--rx-btt-radius: 999px;
--rx-btt-progress: #22c55e;
--rx-btt-ring-bg: rgba(255, 255, 255, 0.22);
}
.rx-back-to-top-wrap {
position: fixed;
right: var(--rx-btt-right);
bottom: var(--rx-btt-bottom);
z-index: var(--rx-btt-z-index);
pointer-events: none;
opacity: 0;
transform: translate3d(0, 14px, 0) scale(0.94);
transition:
opacity 220ms ease,
transform 220ms ease,
bottom 220ms ease;
}
.rx-back-to-top-wrap.is-visible {
opacity: 1;
transform: translate3d(0, 0, 0) scale(1);
pointer-events: auto;
}
.rx-back-to-top-wrap.is-active {
transform: translate3d(0, -2px, 0) scale(0.98);
}
.rx-back-to-top-button {
position: relative;
width: var(--rx-btt-size);
height: var(--rx-btt-size);
display: inline-flex;
align-items: center;
justify-content: center;
border: 0;
border-radius: var(--rx-btt-radius);
background: var(--rx-btt-bg);
color: var(--rx-btt-color);
box-shadow: var(--rx-btt-shadow);
cursor: pointer;
overflow: hidden;
outline: none;
isolation: isolate;
-webkit-tap-highlight-color: transparent;
transition:
background-color 180ms ease,
color 180ms ease,
box-shadow 180ms ease,
transform 180ms ease;
}
.rx-back-to-top-button::before {
content: "";
position: absolute;
inset: 4px;
border-radius: inherit;
background:
radial-gradient(circle at 35% 30%, rgba(255, 255, 255, 0.24), transparent 38%),
linear-gradient(145deg, rgba(255, 255, 255, 0.10), rgba(255, 255, 255, 0));
z-index: -1;
opacity: 0.9;
}
.rx-back-to-top-button:hover {
background: var(--rx-btt-bg-hover);
box-shadow: var(--rx-btt-shadow-hover);
transform: translateY(-2px);
}
.rx-back-to-top-button:active {
transform: translateY(0) scale(0.96);
}
.rx-back-to-top-button:focus-visible {
outline: 3px solid rgba(37, 99, 235, 0.35);
outline-offset: 4px;
}
.rx-back-to-top-icon {
position: relative;
z-index: 2;
width: 22px;
height: 22px;
display: block;
}
.rx-back-to-top-progress {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
transform: rotate(-90deg);
pointer-events: none;
z-index: 1;
}
.rx-back-to-top-progress circle {
fill: none;
stroke-width: 2.5;
}
.rx-back-to-top-progress .rx-btt-progress-bg {
stroke: var(--rx-btt-ring-bg);
}
.rx-back-to-top-progress .rx-btt-progress-value {
stroke: var(--rx-btt-progress);
stroke-linecap: round;
transition: stroke-dashoffset 120ms linear;
}
.rx-back-to-top-text {
position: absolute !important;
width: 1px !important;
height: 1px !important;
padding: 0 !important;
margin: -1px !important;
overflow: hidden !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
border: 0 !important;
}
html.rx-is-scrolling .rx-back-to-top-button {
will-change: transform;
}
@media (max-width: 767px) {
.rx-back-to-top-wrap {
right: var(--rx-btt-right-mobile);
bottom: var(--rx-btt-bottom-mobile);
}
.rx-back-to-top-button {
width: var(--rx-btt-size-mobile);
height: var(--rx-btt-size-mobile);
}
.rx-back-to-top-icon {
width: 20px;
height: 20px;
}
}
@media (prefers-reduced-motion: reduce) {
.rx-back-to-top-wrap,
.rx-back-to-top-button,
.rx-back-to-top-progress .rx-btt-progress-value {
transition: none !important;
animation: none !important;
scroll-behavior: auto !important;
}
}
@media print {
.rx-back-to-top-wrap {
display: none !important;
}
}
`;
doc.head.appendChild(style);
}
/**
* ============================================================
* Button Creation
* ============================================================
*/
function createBackToTopButton() {
let existingButton = qs("#" + RX_BACK_TO_TOP_CONFIG.buttonId);
if (existingButton) {
return existingButton;
}
const wrapper = doc.createElement("div");
wrapper.className = RX_BACK_TO_TOP_CONFIG.wrapperClass;
wrapper.setAttribute("data-rx-back-to-top", "true");
const button = doc.createElement("button");
button.type = "button";
button.id = RX_BACK_TO_TOP_CONFIG.buttonId;
button.className = "rx-back-to-top-button";
button.setAttribute("aria-label", RX_BACK_TO_TOP_CONFIG.ariaLabel);
button.setAttribute("title", RX_BACK_TO_TOP_CONFIG.title);
if (RX_BACK_TO_TOP_CONFIG.enableProgressRing) {
const progressSvg = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
progressSvg.setAttribute("class", "rx-back-to-top-progress");
progressSvg.setAttribute("id", RX_BACK_TO_TOP_CONFIG.progressId);
progressSvg.setAttribute("viewBox", "0 0 52 52");
progressSvg.setAttribute("aria-hidden", "true");
progressSvg.setAttribute("focusable", "false");
const bgCircle = doc.createElementNS("http://www.w3.org/2000/svg", "circle");
bgCircle.setAttribute("class", "rx-btt-progress-bg");
bgCircle.setAttribute("cx", "26");
bgCircle.setAttribute("cy", "26");
bgCircle.setAttribute("r", "23");
const valueCircle = doc.createElementNS("http://www.w3.org/2000/svg", "circle");
valueCircle.setAttribute("class", "rx-btt-progress-value");
valueCircle.setAttribute("cx", "26");
valueCircle.setAttribute("cy", "26");
valueCircle.setAttribute("r", "23");
progressSvg.appendChild(bgCircle);
progressSvg.appendChild(valueCircle);
button.appendChild(progressSvg);
}
const iconWrap = doc.createElement("span");
iconWrap.className = "rx-back-to-top-icon-wrap";
iconWrap.innerHTML = RX_BACK_TO_TOP_CONFIG.iconSvg;
const text = doc.createElement("span");
text.className = "rx-back-to-top-text";
text.textContent = RX_BACK_TO_TOP_CONFIG.ariaLabel;
button.appendChild(iconWrap);
button.appendChild(text);
wrapper.appendChild(button);
body.appendChild(wrapper);
return button;
}
/**
* ============================================================
* Progress Ring Setup
* ============================================================
*/
function setupProgressRing() {
const circle = qs(".rx-btt-progress-value");
if (!circle) {
return null;
}
const radius = Number(circle.getAttribute("r")) || 23;
const circumference = 2 * Math.PI * radius;
circle.style.strokeDasharray = String(circumference);
circle.style.strokeDashoffset = String(circumference);
return {
circle: circle,
circumference: circumference
};
}
function updateProgressRing(progressData) {
if (!progressData || !progressData.circle) {
return;
}
const progress = getScrollProgress();
const offset =
progressData.circumference -
(progress / 100) * progressData.circumference;
progressData.circle.style.strokeDashoffset = String(offset);
}
/**
* ============================================================
* Smooth Scroll To Top
* ============================================================
*/
function easeOutCubic(t) {
return 1 - Math.pow(1 - t, 3);
}
function smoothScrollToTop() {
const startPosition = getScrollTop();
const targetPosition = Math.max(0, RX_BACK_TO_TOP_CONFIG.scrollOffset);
const distance = startPosition - targetPosition;
if (distance <= 0) {
return;
}
if (prefersReducedMotion()) {
win.scrollTo(0, targetPosition);
return;
}
const duration = RX_BACK_TO_TOP_CONFIG.scrollDuration;
const startTime = performance.now();
function animationStep(currentTime) {
const elapsed = currentTime - startTime;
const progress = clamp(elapsed / duration, 0, 1);
const eased = easeOutCubic(progress);
const nextPosition = startPosition - distance * eased;
win.scrollTo(0, nextPosition);
if (progress < 1) {
win.requestAnimationFrame(animationStep);
} else {
win.scrollTo(0, targetPosition);
}
}
win.requestAnimationFrame(animationStep);
}
/**
* ============================================================
* Footer Collision Protection
* ============================================================
*/
function getFooterElement() {
const selectors = RX_BACK_TO_TOP_CONFIG.footerSelectors;
for (let i = 0; i < selectors.length; i++) {
const footer = qs(selectors[i]);
if (footer) {
return footer;
}
}
return null;
}
function updateFooterCollision(wrapper, footer) {
if (!RX_BACK_TO_TOP_CONFIG.enableFooterCollisionProtection) {
return;
}
if (!wrapper || !footer) {
return;
}
const viewportHeight = getViewportHeight();
const footerRect = footer.getBoundingClientRect();
const isMobile = isMobileViewport();
const baseBottom = isMobile
? RX_BACK_TO_TOP_CONFIG.bottomMobile
: RX_BACK_TO_TOP_CONFIG.bottomDesktop;
const overlap = viewportHeight - footerRect.top;
if (overlap > 0) {
const safeBottom = baseBottom + overlap;
wrapper.style.bottom = safeBottom + "px";
wrapper.classList.add(RX_BACK_TO_TOP_CONFIG.footerAwareClass);
} else {
wrapper.style.bottom = "";
wrapper.classList.remove(RX_BACK_TO_TOP_CONFIG.footerAwareClass);
}
}
/**
* ============================================================
* Visibility Logic
* ============================================================
*/
function updateVisibility(wrapper) {
if (!wrapper) {
return;
}
const scrollTop = getScrollTop();
const shouldShow = scrollTop >= RX_BACK_TO_TOP_CONFIG.showAfter;
if (shouldShow) {
wrapper.classList.add(RX_BACK_TO_TOP_CONFIG.visibleClass);
wrapper.setAttribute("aria-hidden", "false");
} else if (scrollTop <= RX_BACK_TO_TOP_CONFIG.hideBelow) {
wrapper.classList.remove(RX_BACK_TO_TOP_CONFIG.visibleClass);
wrapper.setAttribute("aria-hidden", "true");
}
}
function updateHtmlScrollClass() {
if (!RX_BACK_TO_TOP_CONFIG.enableScrollSpyClass) {
return;
}
if (getScrollTop() > 8) {
html.classList.add("rx-page-scrolled");
} else {
html.classList.remove("rx-page-scrolled");
}
}
/**
* ============================================================
* Events
* ============================================================
*/
function bindButtonEvents(button, wrapper) {
if (!button || !wrapper) {
return;
}
button.addEventListener(
"click",
function (event) {
event.preventDefault();
wrapper.classList.add(RX_BACK_TO_TOP_CONFIG.activeClass);
smoothScrollToTop();
win.setTimeout(function () {
wrapper.classList.remove(RX_BACK_TO_TOP_CONFIG.activeClass);
}, 220);
},
{ passive: false }
);
button.addEventListener("keydown", function (event) {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
smoothScrollToTop();
}
});
}
function bindKeyboardShortcut() {
if (!RX_BACK_TO_TOP_CONFIG.enableKeyboardShortcut) {
return;
}
doc.addEventListener("keydown", function (event) {
const activeElement = doc.activeElement;
const tagName = activeElement ? activeElement.tagName : "";
const isTyping =
tagName === "INPUT" ||
tagName === "TEXTAREA" ||
tagName === "SELECT" ||
(activeElement && activeElement.isContentEditable);
if (isTyping) {
return;
}
const keyMatch = event.key === RX_BACK_TO_TOP_CONFIG.keyboardShortcutKey;
const altMatch = RX_BACK_TO_TOP_CONFIG.keyboardShortcutAlt
? event.altKey
: true;
if (keyMatch && altMatch) {
event.preventDefault();
smoothScrollToTop();
}
});
}
function addScrollingState() {
if (!RX_BACK_TO_TOP_CONFIG.enableScrollSpyClass) {
return function () {};
}
let scrollTimer = null;
return function () {
html.classList.add("rx-is-scrolling");
if (scrollTimer) {
win.clearTimeout(scrollTimer);
}
scrollTimer = win.setTimeout(function () {
html.classList.remove("rx-is-scrolling");
}, 180);
};
}
/**
* ============================================================
* Public API
* ============================================================
*/
function exposePublicApi(button, wrapper) {
win.RXBackToTop = {
button: button,
wrapper: wrapper,
show: function () {
wrapper.classList.add(RX_BACK_TO_TOP_CONFIG.visibleClass);
wrapper.setAttribute("aria-hidden", "false");
},
hide: function () {
wrapper.classList.remove(RX_BACK_TO_TOP_CONFIG.visibleClass);
wrapper.setAttribute("aria-hidden", "true");
},
scroll: function () {
smoothScrollToTop();
},
refresh: function () {
const event = new Event("scroll");
win.dispatchEvent(event);
},
config: RX_BACK_TO_TOP_CONFIG
};
}
/**
* ============================================================
* Init
* ============================================================
*/
function initBackToTop() {
if (shouldIgnorePage()) {
return;
}
if (body.classList.contains("rx-back-to-top-disabled")) {
return;
}
injectBackToTopStyles();
const button = createBackToTopButton();
const wrapper = button.closest("." + RX_BACK_TO_TOP_CONFIG.wrapperClass);
if (!button || !wrapper) {
return;
}
const progressData = setupProgressRing();
const footer = getFooterElement();
const setScrollingState = addScrollingState();
const updateAll = rafThrottle(function () {
updateVisibility(wrapper);
updateProgressRing(progressData);
updateFooterCollision(wrapper, footer);
updateHtmlScrollClass();
setScrollingState();
});
bindButtonEvents(button, wrapper);
bindKeyboardShortcut();
exposePublicApi(button, wrapper);
wrapper.setAttribute("aria-hidden", "true");
win.addEventListener("scroll", updateAll, { passive: true });
win.addEventListener("resize", updateAll, { passive: true });
win.addEventListener("orientationchange", updateAll, { passive: true });
updateAll();
doc.dispatchEvent(
new CustomEvent("rxBackToTopReady", {
detail: {
button: button,
wrapper: wrapper,
config: RX_BACK_TO_TOP_CONFIG
}
})
);
}
/**
* ============================================================
* DOM Ready
* ============================================================
*/
if (doc.readyState === "loading") {
doc.addEventListener("DOMContentLoaded", initBackToTop);
} else {
initBackToTop();
}
})();
To load it in your RX Theme, enqueue it like this in your theme enqueue file, for example:
inc/core/enqueue.php
<?php
/**
* RX Theme Enqueue Assets
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
function rx_theme_enqueue_back_to_top_chunk() {
wp_enqueue_script(
'rx-chunk-006-back-to-top',
get_template_directory_uri() . '/assets/static-js/static-chunks-js/chunk-006-back-to-top.js',
array(),
wp_get_theme()->get( 'Version' ),
true
);
}
add_action( 'wp_enqueue_scripts', 'rx_theme_enqueue_back_to_top_chunk' );
Also check your folder slash. In WordPress/PHP always use forward slash:
assets/static-js/static-chunks-js/chunk-006-back-to-top.js
Not this:
assets\static-js\static-chunks-js/chunk-006-back-to-top.js
This file can work alone without writing HTML manually. It will automatically create the Back To Top button on the front end.