Paste either snippet into your page — a Custom HTML block on WordPress, Shopify, or Squarespace works too. Both snippets update live as you customize, so what you copy is exactly what you configured.
<!-- Free Back to Top Button widget by haynes lab — https://hayneslab.dev/tools/widgets/back-to-top -->
<script data-hl-offset="120" data-hl-bg="#f97316">
/*!
* haynes lab — Back to Top Button widget (free)
* Docs & embed code: https://hayneslab.dev/tools/widgets/back-to-top
*/
(function () {
'use strict';
var PREFIX = 'hlw-back-to-top';
var STYLE_ID = 'hlw-back-to-top-styles';
function findScript() {
return document.currentScript ||
document.querySelector('script[src*="widgets/back-to-top.js"]');
}
function injectStyles() {
if (document.getElementById(STYLE_ID)) return;
var css =
'.' + PREFIX + ', .' + PREFIX + ' * { box-sizing: border-box; }' +
'.' + PREFIX + ' { position: fixed; bottom: 20px; right: 20px; z-index: 99999;' +
' display: flex; flex-direction: column; align-items: center; gap: 6px;' +
' font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;' +
' font-size: 16px; line-height: 1.5; }' +
'.' + PREFIX + '--left { right: auto; left: 20px; }' +
'.' + PREFIX + '--in-stage { position: absolute; z-index: 10; }' +
'.' + PREFIX + '__button { width: 44px; height: 44px; padding: 0; border: 0; border-radius: 50%;' +
' background: #1f2937; color: #ffffff; cursor: pointer;' +
' display: flex; align-items: center; justify-content: center;' +
' box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);' +
' opacity: 0; visibility: hidden; transform: translateY(8px);' +
' transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s; }' +
'.' + PREFIX + '__button--visible { opacity: 1; visibility: visible; transform: translateY(0); }' +
'.' + PREFIX + '__button:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.22); }' +
'.' + PREFIX + '__button:focus-visible { outline: 2px solid #1f2937; outline-offset: 3px; }' +
'.' + PREFIX + '__button svg { display: block; }' +
'.' + PREFIX + '__credit { font-size: 11px; display: inline-flex; align-items: center; gap: 4px;' +
' color: #9ca3af; text-decoration: none;' +
' opacity: 0; visibility: hidden; transition: opacity 0.2s ease, visibility 0.2s; }' +
'.' + PREFIX + '__button--visible ~ .' + PREFIX + '__credit { opacity: 1; visibility: visible; }' +
'.' + PREFIX + '__credit svg { display: block; }' +
'.' + PREFIX + '__credit:hover { color: #6b7280; text-decoration: underline; }' +
'@media (prefers-reduced-motion: reduce) {' +
' .' + PREFIX + '__button, .' + PREFIX + '__credit { transition: none; } }' +
'@media (max-width: 560px) {' +
' .' + PREFIX + ' { bottom: 14px; right: 14px; }' +
' .' + PREFIX + '--left { right: auto; left: 14px; } }';
var style = document.createElement('style');
style.id = STYLE_ID;
style.textContent = css;
document.head.appendChild(style);
}
function el(tag, className, text) {
var node = document.createElement(tag);
if (className) node.className = className;
if (text != null) node.textContent = text;
return node;
}
function arrowSvg() {
var svgNS = 'http://www.w3.org/2000/svg';
var svg = document.createElementNS(svgNS, 'svg');
svg.setAttribute('viewBox', '0 0 24 24');
svg.setAttribute('width', '20');
svg.setAttribute('height', '20');
svg.setAttribute('fill', 'none');
svg.setAttribute('stroke', 'currentColor');
svg.setAttribute('stroke-width', '2.5');
svg.setAttribute('stroke-linecap', 'round');
svg.setAttribute('stroke-linejoin', 'round');
svg.setAttribute('aria-hidden', 'true');
svg.setAttribute('focusable', 'false');
var path = document.createElementNS(svgNS, 'path');
path.setAttribute('d', 'M12 19V5M5 12l7-7 7 7');
svg.appendChild(path);
return svg;
}
function logoSvg() {
var svgNS = 'http://www.w3.org/2000/svg';
var svg = document.createElementNS(svgNS, 'svg');
svg.setAttribute('viewBox', '0 0 32 32');
svg.setAttribute('width', '13');
svg.setAttribute('height', '13');
svg.setAttribute('aria-hidden', 'true');
svg.setAttribute('focusable', 'false');
function circle(cx, cy, r, attrs) {
var c = document.createElementNS(svgNS, 'circle');
c.setAttribute('cx', cx);
c.setAttribute('cy', cy);
c.setAttribute('r', r);
for (var key in attrs) c.setAttribute(key, attrs[key]);
svg.appendChild(c);
}
circle(16, 16, 16, { fill: '#0f0a0a' });
circle(16, 16, 12, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.3 });
circle(16, 16, 9, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.5 });
circle(16, 16, 6, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1.2, opacity: 0.9 });
circle(16, 16, 3, { fill: '#06b6d4' });
circle(24.5, 7.5, 2, { fill: '#f97316' });
circle(7, 16, 1.8, { fill: '#f97316' });
circle(20, 20, 1.5, { fill: '#f97316' });
return svg;
}
function build(script) {
var offset = parseInt(script.getAttribute('data-hl-offset'), 10);
if (isNaN(offset) || offset < 0) offset = 300;
var bg = script.getAttribute('data-hl-bg') || '#1f2937';
var left = script.getAttribute('data-hl-position') === 'left';
var stage = script.closest('.widget-demo__stage');
injectStyles();
var root = el('div', PREFIX + (left ? ' ' + PREFIX + '--left' : ''));
var button = el('button', PREFIX + '__button');
button.type = 'button';
button.setAttribute('aria-label', 'Back to top');
button.style.background = bg;
button.appendChild(arrowSvg());
root.appendChild(button);
var credit = el('a', PREFIX + '__credit');
credit.appendChild(logoSvg());
credit.appendChild(document.createTextNode('Powered by haynes lab'));
credit.href = 'https://hayneslab.dev/tools/widgets/back-to-top';
credit.target = '_blank';
credit.rel = 'noopener';
root.appendChild(credit);
// In the landing-page demo the button is scoped to the stage: absolute
// positioning, stage scroll drives visibility, click scrolls the stage.
var scrollEl = stage || window;
function scrollTop() {
return stage ? stage.scrollTop :
(window.pageYOffset || document.documentElement.scrollTop || 0);
}
function update() {
var visible = scrollTop() > offset;
button.classList.toggle(PREFIX + '__button--visible', visible);
button.setAttribute('aria-hidden', visible ? 'false' : 'true');
button.tabIndex = visible ? 0 : -1;
}
var reduced = window.matchMedia &&
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
button.addEventListener('click', function () {
var opts = { top: 0, behavior: reduced ? 'auto' : 'smooth' };
try {
scrollEl.scrollTo(opts);
} catch (e) {
scrollEl.scrollTop = 0; // older browsers
}
});
scrollEl.addEventListener('scroll', update, { passive: true });
window.addEventListener('resize', update);
if (stage) {
root.classList.add(PREFIX + '--in-stage');
stage.appendChild(root);
} else {
document.body.appendChild(root);
}
update();
}
function init() {
var script = findScript();
if (!script) {
console.warn('[haynes lab back-to-top] Could not locate its own script tag. Nothing rendered.');
return;
}
build(script);
}
if (document.body) {
init();
} else {
document.addEventListener('DOMContentLoaded', init);
}
})();
</script>