Pure Natural Movement (snapEagerness: 0.0)

Configuration: { snapEagerness: 0.0 }

This header uses the most natural feel but may occasionally show small visual gaps when scrolling up quickly.

💡 Testing tip: Try scrolling at different speeds. Notice how the movement feels completely natural and connected to your scroll speed.

🔍 What Makes This "Natural"?

With snapEagerness set to 0.0, the header only becomes sticky when it naturally reaches the top edge, with no prediction or early switching. This creates the most organic feel.

Key characteristics:

📋 The Source Code Behind This Behavior

Here's the actual TypeScript implementation that powers this natural movement:

/**
 * Natural sticky behavior with configurable snap eagerness
 */
export function naturalStickyTop(element: HTMLElement, options: { snapEagerness?: number } = {}) {
  const { snapEagerness = 1.0 } = options;
  let lastScrollY = window.scrollY;
  let mode = 'relative';

  const handleScroll = () => {
    const currentScrollY = window.scrollY;
    const scrollingDown = currentScrollY > lastScrollY;
    const scrollingUp = currentScrollY < lastScrollY;
    const elementHeight = element.offsetHeight;
    const elementRect = element.getBoundingClientRect();
    const isElementVisible = elementRect.bottom > 0 && elementRect.top < window.innerHeight;

    // Calculate scroll velocity for prediction
    const scrollVelocity = currentScrollY - lastScrollY;
    const predictedTop = elementRect.top - snapEagerness * scrollVelocity;

    // If we are sticky and scroll down, release the element
    if (mode === 'sticky' && scrollingDown) {
      mode = 'relative';
      element.style.position = 'relative';
      element.style.top = `${currentScrollY}px`;
    }
    // If scrolling up and element not visible, prepare for reveal
    else if (mode === 'relative' && scrollingUp && !isElementVisible) {
      element.style.position = 'relative';
      element.style.top = `${currentScrollY - elementHeight}px`;
    }
    // With snapEagerness 0.0, only snap when naturally reaching the top
    else if (mode === 'relative' && predictedTop >= 0) {
      mode = 'sticky';
      element.style.position = 'sticky';
      element.style.top = '0';
    }

    lastScrollY = currentScrollY > 0 ? currentScrollY : 0;
  };

  handleScroll();
  window.addEventListener('scroll', handleScroll, { passive: true });

  return { destroy: () => window.removeEventListener('scroll', handleScroll) };
}

🎯 When to Choose snapEagerness: 0.0

This setting is perfect when:

⚠️ Potential Trade-offs

Be aware that with pure natural movement:

Continue scrolling to test different scroll speeds and patterns. Pay attention to how the header responds to your exact scroll input without any anticipation.

Try these specific tests:

📊 Comparing with Other Values

This 0.0 value represents one end of the spectrum:

The higher the value, the more the system "anticipates" your scrolling to prevent gaps, but at the cost of natural feel.

This is where the pure natural movement really shines - there's no artificial prediction interfering with the organic flow of your scrolling motion.

Keep scrolling to continue testing!
The more you scroll, the better you'll feel the pure natural movement.