npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

natural-sticky

v1.4.2

Published

A lightweight, framework-agnostic package for a natural hide-on-scroll effect.

Readme

Natural Sticky

Live Demo | CodePen Examples

License npm version NPM Downloads jsDelivr hits (npm)

Minified Size Gzip Size Brotli Size - natural-sticky.top.min.js

Minified Size Gzip Size Brotli Size - natural-sticky.bottom.min.js

A lightweight, framework-agnostic package for natural hide-on-scroll effects.

Why Natural Sticky?

The problem with existing solutions: Most sticky header libraries use CSS animations or JavaScript tweening that feel disconnected from your actual scroll behavior. They slide, fade, or pop in/out with predetermined timing that can feel jarring or distracting.

Our approach: No animations at all. Instead, we smartly switch between positioning modes (sticky/relative for traditional behavior, fixed/absolute for floating elements), letting the browser's native scrolling handle all movement. Headers and footers flow naturally with your scroll speed - hide naturally when scrolling down, reappear naturally when scrolling up. The floating mode enables multiple elements on the same page without document flow conflicts.

Key Benefits:

  • 🎈 Ultra Lightweight: 1.2KB (header) / 1.3KB (footer) - no dependencies
  • 🌊 Natural Movement: Flows with your scroll speed, no artificial animations or distracting effects
  • 🤹 Multiple Elements: Animate multiple headers, footers, and floating elements without conflicts
  • 🎪 Event-Driven: Listen to state changes and create dynamic, responsive interfaces
  • ♟️ Smart Positioning: Predictive gap elimination and flexible document flow control
  • 🎛️ Fine-tunable: Three parameters control natural feel, activation, and positioning mode

Compared to alternatives:

  • Headroom.js: ~4.6KB, slide animations, requires configuration
  • AOS: ~14.2KB, complex animations, heavy setup
  • Natural Sticky: 1.2-1.3KB, zero dependencies, natural movement that doesn't break focus

Quick Start

Installation & CDN

Choose your preferred method:

Option 1: NPM Package

npm install natural-sticky
import { naturalStickyTop, naturalStickyBottom } from 'natural-sticky';

// Headers
const headerInstance = naturalStickyTop(document.querySelector('.header'));

// Footers
const footerInstance = naturalStickyBottom(document.querySelector('.footer'));

// Clean up when needed
headerInstance.destroy();
footerInstance.destroy();

Option 2: CDN (Browser)

<!-- For headers (1.2KB) -->
<script src="https://cdn.jsdelivr.net/npm/natural-sticky/dist/natural-sticky.top.min.js"></script>
<script>
  const header = document.querySelector('.header');
  window.naturalStickyTop(header);
</script>

<!-- For footers (1.3KB) -->
<script src="https://cdn.jsdelivr.net/npm/natural-sticky/dist/natural-sticky.bottom.min.js"></script>
<script>
  const footer = document.querySelector('.footer');
  window.naturalStickyBottom(footer);
</script>

Framework Integration

Works seamlessly with any framework:

// React
useEffect(() => {
  const instance = naturalStickyTop(headerRef.current);
  return () => instance.destroy();
}, []);

// Vue
mounted() {
  this.headerInstance = naturalStickyTop(this.$refs.header);
},
beforeDestroy() {
  this.headerInstance.destroy();
}

// Angular
ngAfterViewInit() {
  this.headerInstance = naturalStickyTop(this.headerElement.nativeElement);
}
ngOnDestroy() {
  this.headerInstance.destroy();
}

Most use cases require no additional configuration. For specific requirements or advanced features, see CSS Requirements, Configuration, or Events below.

How It Works

Natural Sticky uses a three-state finite state machine to create fluid, natural-feeling sticky behavior without animations.

stateDiagram-v2
    [*] --> home : Page loads
    home --> relative : Scroll down
    relative --> sticky : Scroll up to viewport edge
    sticky --> relative : Scroll down
    relative --> home : Scroll off-screen or reach top
    sticky --> home : Scroll to very top (scrollY ≤ 0)

State Overview:

  • home - Element at natural document position (page top or scrolled off-screen)
  • sticky - Element locked to viewport edge (traditional sticky behavior)
  • relative - Element transitioning with scroll (natural movement phase)

The core insight: avoid animations entirely. Instead, we smartly switch between positioning modes at exactly the right moments, letting the browser's native scrolling handle all movement.

Live Demo: How It Works - Deep dive into the three-state system

Events

Natural Sticky dispatches events when elements change state, enabling dynamic interfaces that respond to scroll behavior:

const header = document.querySelector('.header');
naturalStickyTop(header);

// Listen for state changes
header.addEventListener('natural-sticky', event => {
  const currentState = event.detail.state; // 'home', 'sticky', or 'relative'

  // Apply different styles based on state
  header.classList.remove('state-home', 'state-sticky', 'state-relative');
  header.classList.add(`state-${currentState}`);

  console.log(`Header is now: ${currentState}`);
});

Live Demo: Event System Demo - Interactive event system showcase Minimal Demos: Header Events | Footer Events | Style on Scroll - Basic event implementation and common styling patterns

Configuration

For most use cases, the defaults work perfectly. However, you can fine-tune the behavior:

naturalStickyTop(header, {
  reserveSpace: true, // Document flow: true (traditional) or false (floating)
  snapEagerness: 1.0, // Gap prevention: 0.0 (natural) to 3.0+ (magnetic)
  scrollThreshold: 0, // Activation threshold: 0 (always) to 30+ (fast scroll only)
});

reserveSpace - Positioning Mode

Controls whether the element reserves space in document flow:

  • true - Traditional sticky behavior (sticky ↔ relative positioning)
  • false - Floating elements (fixed ↔ absolute positioning)

Key capability: Multiple elements on the same page. Floating mode allows you to animate multiple headers, footers, floating action buttons, notifications, and status indicators simultaneously without document flow conflicts.

Use floating elements for:

  • Multiple headers/footers without layout conflicts
  • Floating action buttons, notifications, or status indicators
  • Overlay elements that shouldn't affect content flow

Live Demo:

Minimal Demos:

snapEagerness - Tuning Natural vs Gap-Free

Controls how aggressively the element anticipates scroll direction changes to prevent visual gaps during fast scrolling.

  • 0.0 - Pure natural movement (occasional gaps during very fast scrolling)
  • 1.0 - Balanced default (recommended for most cases)
  • 2.0+ - Aggressive gap prevention (more predictive, less natural)

Learn more about the "Visual Gap" problem and how to solve it:

scrollThreshold - Controlling Activation

Controls when the scroll-in effect activates based on scroll speed:

  • 0 - Always activate (default, most responsive)
  • 5-15 - Moderate threshold (deliberate scrolling required)
  • 20+ - High threshold (fast scrolling only)

Live Demos:

CSS Requirements

Traditional Sticky Elements (reserveSpace: true)

Elements must align with their respective screen edges:

.header {
  margin-top: 0; /* Required: must align with top edge */
  /* Other margins preserved: margin-bottom, margin-left, margin-right */
}

.footer {
  margin-bottom: 0; /* Required: must align with bottom edge */
  /* Other margins preserved: margin-top, margin-left, margin-right */
}

Floating Elements (reserveSpace: false)

For floating elements that don't affect document flow:

/* Required for bottom script with floating elements */
body {
  position: relative;
}

/* Required for floating bottom elements - proper initialization */
.floating-bottom-container {
  position: absolute; /* Required for proper initialization */
  bottom: 0; /* Required for proper initialization */
}

Why body positioning: Bottom script uses absolute positioning with bottom coordinates. Without body { position: relative; }, coordinates calculate from viewport bottom instead of document bottom.

Why floating bottom element positioning: Without proper CSS positioning, the bottom script cannot determine the element's initial location, causing it to render incorrectly until the user scrolls to the very bottom of the page.

Optional: Adding gaps from viewport edges

By default, floating elements align flush with viewport edges. To add spacing, wrap elements in containers:

/* Example: Adding 20px gap from edges */
.floating-header-container {
  padding-top: 20px; /* Distance from top (for top script) */
}

.floating-footer-container {
  padding-bottom: 20px; /* Distance from bottom (for bottom script) */
}
// Apply Natural Sticky to containers, not inner elements
naturalStickyTop(document.querySelector('.floating-header-container'), {
  reserveSpace: false,
});

Why containers: Containers position at top: 0px or bottom: 0px when sticky, while padding controls the actual element placement inside the container.