or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-scrollama

Lightweight scrollytelling library using IntersectionObserver for scroll-driven interactive narratives

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/scrollama@3.2.x

To install, run

npx @tessl/cli install tessl/npm-scrollama@3.2.0

index.mddocs/

Scrollama

Scrollama is a lightweight JavaScript library for creating scrollytelling (scroll-driven storytelling) experiences using the IntersectionObserver API. It provides a simple interface for detecting when elements enter or exit the viewport during scrolling, with support for step-based interactions, progress tracking, custom offsets, and sticky graphic implementations.

Package Information

  • Package Name: scrollama
  • Package Type: npm
  • Language: JavaScript (with TypeScript definitions)
  • Installation: npm install scrollama

Core Imports

import scrollama from "scrollama";

For CommonJS:

const scrollama = require("scrollama");

Browser (CDN):

<script src="https://unpkg.com/scrollama"></script>

Basic Usage

import scrollama from "scrollama";

// Create scrollama instance
const scroller = scrollama();

// Setup with step elements and callbacks
scroller
  .setup({
    step: ".step", // required: CSS selector for step elements
    offset: 0.5,   // trigger when element is 50% in viewport
    debug: false   // optional visual debugging
  })
  .onStepEnter((response) => {
    // Fired when step enters offset threshold
    const { element, index, direction } = response;
    console.log("Step entered:", index, direction);
  })
  .onStepExit((response) => {
    // Fired when step exits offset threshold  
    const { element, index, direction } = response;
    console.log("Step exited:", index, direction);
  });

Architecture

Scrollama uses the IntersectionObserver API for performance optimization over traditional scroll events. Key components:

  • Factory Function: scrollama() creates instances with chainable method calls
  • Observer Management: Automatically manages IntersectionObserver instances per step
  • Resize Handling: Built-in ResizeObserver automatically handles viewport changes
  • Progress Tracking: Optional fine-grained progress monitoring within steps
  • Debug Mode: Visual overlays for development and testing

Capabilities

Instance Creation

Creates a new scrollama instance for managing scroll-driven interactions.

/**
 * Creates a new scrollama instance
 * @returns ScrollamaInstance - Instance with chainable configuration methods
 */
function scrollama(): ScrollamaInstance;

Setup Configuration

Configures the scrollama instance with step elements and interaction settings.

/**
 * Configure scrollama instance with steps and options
 * @param options - Configuration object
 * @returns ScrollamaInstance - For method chaining
 */
setup(options: ScrollamaOptions): ScrollamaInstance;

interface ScrollamaOptions {
  /** Required: CSS selector, NodeList, HTMLElement[], or single HTMLElement for step elements */
  step: string | NodeList | HTMLElement[] | HTMLElement;
  /** Trigger position in viewport: 0-1 or string with "px" (default: 0.5) */
  offset?: number | string;
  /** Enable incremental progress updates (default: false) */
  progress?: boolean;
  /** Granularity of progress updates in pixels, minimum 1 (default: 4) */
  threshold?: number;
  /** Only trigger steps once, then remove listeners (default: false) */
  once?: boolean;
  /** Show visual debugging overlays (default: false) */
  debug?: boolean;
  /** Parent element for step selector, useful for shadow DOM */
  parent?: HTMLElement;
  /** Parent element for scroll story, for overflow: scroll/auto containers */
  container?: HTMLElement;
  /** Element used as viewport for visibility checking (default: browser viewport) */
  root?: HTMLElement;
}

Usage Example:

// Basic setup
scroller.setup({
  step: ".story-step"
});

// Advanced setup with progress tracking
scroller.setup({
  step: ".story-step",
  offset: 0.8,        // Trigger at 80% viewport height
  progress: true,     // Enable progress callbacks
  threshold: 2,       // Higher granularity
  debug: true,        // Show debug overlays
  container: document.querySelector('.scroll-container')
});

Step Event Callbacks

Register callbacks for step enter and exit events.

/**
 * Callback fired when step elements enter the offset threshold
 * @param callback - Function receiving step event data
 * @returns ScrollamaInstance - For method chaining
 */
onStepEnter(callback: (response: CallbackResponse) => void): ScrollamaInstance;

/**
 * Callback fired when step elements exit the offset threshold
 * @param callback - Function receiving step event data
 * @returns ScrollamaInstance - For method chaining
 */
onStepExit(callback: (response: CallbackResponse) => void): ScrollamaInstance;

interface CallbackResponse {
  /** The DOM element that triggered the event */
  element: HTMLElement;
  /** Zero-based index of the step element */
  index: number;
  /** Scroll direction when event fired */
  direction: "up" | "down";
}

Usage Example:

scroller
  .onStepEnter((response) => {
    const { element, index, direction } = response;
    element.classList.add('is-active');
    
    // Handle different steps
    if (index === 0) {
      // First step logic
    } else if (index === 2) {
      // Third step logic
    }
  })
  .onStepExit((response) => {
    const { element, index, direction } = response;
    element.classList.remove('is-active');
  });

Progress Tracking

Monitor fine-grained progress within step elements (requires progress: true in setup).

/**
 * Callback fired for incremental progress through steps
 * @param callback - Function receiving progress event data
 * @returns ScrollamaInstance - For method chaining
 */
onStepProgress(callback: (response: ProgressCallbackResponse) => void): ScrollamaInstance;

interface ProgressCallbackResponse {
  /** The DOM element being tracked */
  element: HTMLElement;
  /** Zero-based index of the step element */
  index: number;
  /** Progress through the step (0.0 to 1.0) */
  progress: number;
  /** Current scroll direction */
  direction: "up" | "down";
}

Usage Example:

scroller
  .setup({
    step: ".step",
    progress: true,
    threshold: 1  // Very granular updates
  })
  .onStepProgress((response) => {
    const { element, index, progress, direction } = response;
    
    // Update progress bar
    const progressBar = element.querySelector('.progress-bar');
    progressBar.style.width = `${progress * 100}%`;
    
    // Fade in/out based on progress
    element.style.opacity = progress;
  });

Offset Management

Get or set the trigger offset position dynamically.

/**
 * Get or set the offset trigger value (note: method name is 'offset' in implementation)
 * @param value - Optional new offset value (0-1 or string with "px")
 * @returns ScrollamaInstance if setting, current offset value if getting
 */
offset(value?: number | string): ScrollamaInstance | number;

Usage Example:

// Get current offset
const currentOffset = scroller.offset();

// Set new offset
scroller.offset(0.25); // Trigger at 25% viewport height
scroller.offset("100px"); // Trigger at 100px from top

Instance Control

Control scrollama instance state and lifecycle.

/**
 * Resume observing for trigger changes (if previously disabled)
 * @returns ScrollamaInstance - For method chaining
 */
enable(): ScrollamaInstance;

/**
 * Stop observing for trigger changes
 * @returns ScrollamaInstance - For method chaining
 */
disable(): ScrollamaInstance;

/**
 * Manual resize trigger (automatic with built-in ResizeObserver)
 * @returns ScrollamaInstance - For method chaining
 */
resize(): ScrollamaInstance;

/**
 * Remove all observers and callback functions
 * @returns void
 */
destroy(): void;

Usage Example:

// Temporarily disable
scroller.disable();

// Re-enable later
scroller.enable();

// Clean up when done
scroller.destroy();

Custom Offset Data Attributes

Override the global offset for individual step elements using data attributes.

<!-- Use percentage (0-1) -->
<div class="step" data-offset="0.25">Step with 25% offset</div>

<!-- Use pixels -->
<div class="step" data-offset="100px">Step with 100px offset</div>

<!-- Use default global offset -->
<div class="step">Step with global offset</div>

Types

Complete TypeScript type definitions for all interfaces and callbacks.

interface ScrollamaInstance {
  setup(options: ScrollamaOptions): ScrollamaInstance;
  onStepEnter(callback: StepCallback): ScrollamaInstance;
  onStepExit(callback: StepCallback): ScrollamaInstance;
  onStepProgress(callback: StepProgressCallback): ScrollamaInstance;
  offset(value?: number | string): ScrollamaInstance | number;
  resize(): ScrollamaInstance;
  enable(): ScrollamaInstance;
  disable(): ScrollamaInstance;
  destroy(): void;
}

type DecimalType = 0 | 0.1 | 0.2 | 0.3 | 0.4 | 0.5 | 0.6 | 0.7 | 0.8 | 0.9 | 1;

type StepCallback = (response: CallbackResponse) => void;
type StepProgressCallback = (response: ProgressCallbackResponse) => void;

Common Patterns

Sticky Graphic Side-by-Side

<div class="container">
  <div class="graphic">
    <!-- Fixed graphic content -->
  </div>
  <div class="scroller">
    <div class="step" data-step="1">First step content</div>
    <div class="step" data-step="2">Second step content</div>
    <div class="step" data-step="3">Third step content</div>
  </div>
</div>
.container {
  display: flex;
}

.graphic {
  position: sticky;
  top: 0;
  flex: 1;
  height: 100vh;
}

.scroller {
  flex: 1;
}

.step {
  margin-bottom: 80vh;
  padding: 1rem;
}

Mobile-Friendly Pixel Offsets

// Use pixel offsets for consistent mobile behavior
const isMobile = window.innerWidth < 768;
const offset = isMobile ? "150px" : 0.5;

scroller.setup({
  step: ".step",
  offset: offset
});

Dynamic Step Management

// Add new steps dynamically
function addStep(content) {
  const newStep = document.createElement('div');
  newStep.className = 'step';
  newStep.textContent = content;
  document.querySelector('.scroller').appendChild(newStep);
  
  // Reconfigure scrollama
  scroller.setup({
    step: ".step" // Automatically picks up new steps
  });
}