Scroll-Jacking and Vestibular Risk

Part of Vestibular-Safe Motion Patterns in Accessible Motion Architecture.

The problem

A product story page takes over scrolling: each wheel gesture advances to the next full-screen section with an animated transition, regardless of how far the user scrolled. Keyboard Page Down jumps two sections. Find-in-page highlights text that is never shown. Users describe it as “fighting the page”, and several report motion sickness.

The same page could tell the same story with native scrolling and scroll-driven animation, and nearly all the discomfort would go with the hijacking.

Root cause analysis: control and prediction

Two properties make ordinary scrolling comfortable. The user controls the amount of movement, and they can predict it — a small gesture moves the page a little.

Scroll-jacking removes both. A wheel handler that calls preventDefault() and animates to a fixed destination means a small gesture produces a large, fixed movement. The eyes see substantial motion the user did not command, which is the same mismatch behind other vestibular triggers described in replacing parallax with accessible alternatives.

It also breaks mechanics users rely on. Momentum scrolling, keyboard paging, Home and End, scrollbar dragging, find-in-page and screen reader “read from here” all assume the document scrolls normally.

Scroll-driven animation is different. An animation whose progress is tied to the scroll position moves because the user moved, in proportion to their gesture, and stops when they stop. The relationship is under their control, which is why scroll-driven animation patterns are broadly acceptable where scroll-jacking is not.

Scroll snapping sits in between. scroll-snap-type: proximity nudges to a nearby point and is usually unobtrusive. mandatory forces every scroll to land on a snap point, which on a long page behaves much like hijacking and can trap users between sections.

Displacement still matters. Even with native scrolling, a scroll-linked effect that moves a full-screen layer a long way as the user scrolls creates large-area motion. Proportional is not automatically safe; it must also be modest.

Scroll techniques by user controlControl and proportionality are what separate acceptable from risky.Scroll techniques by user controlUser controls the amount?Native mechanics intact?VerdictNativescrollingYesYesBaselineScroll-drivenanimationYesYesAcceptable, keep displacementmodestProximitysnappingMostlyYesUsually fineMandatorysnappingPartlyMostlyShort sequences onlyWheel hijackingNoNoAvoid
Control and proportionality are what separate acceptable from risky.

Step-by-step resolution

Replacing hijacking with scroll-driven motionGive scrolling back to the user; keep the storytelling.Replacing hijacking with scroll-driven motion1Remove wheel, touchmove and keydown handlers that prevent default scrolling.Native mechanics restored2Express section effects as scroll-driven animations with view timelines.Motion follows the user's gesture3Keep per-section displacement modest and mostly opacity-based.Less large-area movement4If sections should align, use proximity snapping.Gentle alignment, no trapping5Verify keyboard paging, find-in-page and screen reader reading.Nothing is unreachable6Disable scroll-linked effects and snapping under reduced motion.
Give scrolling back to the user; keep the storytelling.

Production code pattern

/* Native scrolling, with gentle alignment. */
.story {
  scroll-snap-type: y proximity;                 /* not mandatory */
  scroll-padding-block-start: var(--header-h);
}
.story__section {
  scroll-snap-align: start;
  min-block-size: 100svh;
}

/* Section effects follow the user's scroll, with small displacement. */
.story__content {
  animation: section-reveal linear both;
  animation-timeline: view();
  animation-range: entry 10% entry 70%;
}
@keyframes section-reveal {
  from { opacity: 0; translate: 0 24px; }        /* 24px, not a full screen */
}

@media (prefers-reduced-motion: reduce) {
  .story { scroll-snap-type: none; }             /* no snapping */
  .story__content { animation: none; }
}
// What NOT to do, kept as a comment for reviewers:
// window.addEventListener('wheel', (e) => { e.preventDefault(); goToSection(next); }, { passive: false });

// If a "next section" control is wanted, make it an explicit button.
document.querySelectorAll('[data-next-section]').forEach((button) => {
  button.addEventListener('click', () => {
    const target = document.getElementById(button.dataset.nextSection);
    target.scrollIntoView({
      behavior: matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth',
      block: 'start',
    });
    target.focus({ preventScroll: true });
  });
});

Rendering Impact: composite. Scroll-driven animations on opacity and translate are evaluated with scrolling rather than by script, so removing the hijacking also removes a wheel handler from the main thread.

Offering an explicit “next section” button gives users who want section-by-section navigation a way to get it, without imposing it on everyone. It is also reachable by keyboard, unlike a hijacked wheel gesture.

Reported discomfort on the same story pageIllustrative proportions showing the usual ordering; run your own testing with affected users.Reported discomfort on the same story pageWheel hijacking with full-screen transitions38 % of testersMandatory snap, full-screen sections19 % of testersNative scroll, scroll-driven reveals4 % of testers
Illustrative proportions showing the usual ordering; run your own testing with affected users.

Horizontal sections and sticky sequences

The horizontal scroll section pattern — a tall runway with a sticky stage whose content moves sideways — keeps native scrolling, so it avoids the worst problems. It still creates large-area lateral motion driven by vertical input, which is a mismatch of a different kind: the user scrolls down and the world moves sideways.

Keep such sections short, keep the horizontal movement roughly proportional to the vertical scroll, and provide the reduced-motion alternative of a plain horizontal scroller. The same applies to long sticky “scrollytelling” sequences: they are acceptable when each scroll step produces a modest, proportional change, and uncomfortable when a small gesture triggers a large scene change.

Verification checklist

Constraints and trade-offs

  • Native scrolling gives less precise control over pacing than a hijacked sequence.
  • Proximity snapping aligns less reliably than mandatory snapping.
  • Scroll-driven animations are not supported everywhere; the static fallback must be complete.
  • Long sticky sequences still require careful displacement limits.
  • Some design concepts depend on fixed section pacing; they may need rethinking rather than adjusting.

Frequently asked questions

What is scroll-jacking?

Intercepting scroll input and replacing it with scripted movement, so the page moves by an amount the user did not choose.

Are scroll-driven animations the same as scroll-jacking?

No. They tie an animation’s progress to the scroll position the user controls, without changing how much the page scrolls.

Is scroll snapping acceptable?

Proximity snapping usually is. Mandatory snapping on long pages behaves like hijacking and can trap users between sections.

How much movement is safe in a scroll-linked effect?

Keep displacement modest — tens of pixels rather than full screens — and favour opacity changes for large surfaces.