Skip to content

Instantly share code, notes, and snippets.

@tannerhallman
Created August 22, 2026 15:47
Show Gist options
  • Select an option

  • Save tannerhallman/678dbab918eedab175517901d823031a to your computer and use it in GitHub Desktop.

Select an option

Save tannerhallman/678dbab918eedab175517901d823031a to your computer and use it in GitHub Desktop.
Horizontal Scroll for Colin
/* Horizontal auto-scroll for the first post panel on
* https://www.vortexofinfinitediscovery.com/blogs/detourism
* Paste into the DevTools console. Any user input (wheel/trackpad, touch,
* drag, arrow keys) cancels it. Manual stop: __hScroll.stop()
*/
(() => {
const PX_PER_SECOND = 40; // ← adjust speed here
const LOOP = false; // true = wrap back to the start instead of stopping
if (window.__hScroll) window.__hScroll.stop('restarted');
// The block's classes are hash-suffixed per section, so match by prefix and
// take the first one on the page = the first panel of posts.
const wrap = document.querySelector('[class*="ai-blog-scroll-wrapper-"]');
if (!wrap) return console.warn('[hScroll] post panel not found.');
const track = wrap.querySelector('[class*="ai-blog-scroll-track-"]');
// Most of these blocks are an overflow-x wrapper around a flex track. If the
// wrapper doesn't actually scroll, fall back to translating the track.
const useScrollLeft = wrap.scrollWidth - wrap.clientWidth > 1;
const max = useScrollLeft
? wrap.scrollWidth - wrap.clientWidth
: (track ? track.scrollWidth - wrap.clientWidth : 0);
if (max <= 1) return console.warn('[hScroll] nothing to scroll — panel fits the viewport.', wrap);
// Snap / smooth scrolling fights a per-frame nudge; suspend and restore later.
const prev = { snap: wrap.style.scrollSnapType, behavior: wrap.style.scrollBehavior,
transform: track ? track.style.transform : '',
transition: track ? track.style.transition : '' };
wrap.style.scrollSnapType = 'none';
wrap.style.scrollBehavior = 'auto';
if (!useScrollLeft && track) track.style.transition = 'none';
const KEYS = new Set(['ArrowLeft','ArrowRight','ArrowUp','ArrowDown',
'PageUp','PageDown','Home','End',' ']);
let pos = useScrollLeft ? wrap.scrollLeft : 0;
let last = null;
function stop(reason) {
const s = window.__hScroll;
if (!s) return;
cancelAnimationFrame(s.raf);
s.cleanup();
window.__hScroll = null;
console.log(`[hScroll] stopped (${reason}).`);
}
const onInput = (e) => {
if (e.type === 'keydown' && !KEYS.has(e.key)) return;
stop('user input detected');
};
// The wrapper also has its own drag-to-scroll; bail if the position drifts
// from what we last wrote.
const onScroll = () => {
if (useScrollLeft && Math.abs(wrap.scrollLeft - pos) > 2) stop('user scrolled the panel');
};
function step(now) {
if (last === null) last = now;
const dt = Math.min((now - last) / 1000, 0.1); // clamp tab-switch jumps
last = now;
pos += PX_PER_SECOND * dt;
if (pos >= max - 0.5) {
if (!LOOP) { apply(max); return stop('reached the end'); }
pos = 0;
}
apply(pos);
window.__hScroll.raf = requestAnimationFrame(step);
}
function apply(x) {
if (useScrollLeft) { wrap.scrollLeft = x; pos = wrap.scrollLeft; }
else if (track) { track.style.transform = `translateX(${-x}px)`; }
}
const passive = { passive: true };
wrap.addEventListener('wheel', onInput, passive);
wrap.addEventListener('touchstart', onInput, passive);
wrap.addEventListener('pointerdown', onInput, passive);
wrap.addEventListener('scroll', onScroll, passive);
window.addEventListener('wheel', onInput, passive);
window.addEventListener('touchstart', onInput, passive);
window.addEventListener('keydown', onInput);
window.__hScroll = {
el: wrap,
raf: requestAnimationFrame(step),
stop,
cleanup() {
wrap.removeEventListener('wheel', onInput);
wrap.removeEventListener('touchstart', onInput);
wrap.removeEventListener('pointerdown', onInput);
wrap.removeEventListener('scroll', onScroll);
window.removeEventListener('wheel', onInput);
window.removeEventListener('touchstart', onInput);
window.removeEventListener('keydown', onInput);
wrap.style.scrollSnapType = prev.snap;
wrap.style.scrollBehavior = prev.behavior;
if (track) track.style.transition = prev.transition;
},
};
console.log('[hScroll] running on', wrap, useScrollLeft ? '(scrollLeft)' : '(transform)');
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment