or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

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

0

# Scrollama

1

2

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.

3

4

## Package Information

5

6

- **Package Name**: scrollama

7

- **Package Type**: npm

8

- **Language**: JavaScript (with TypeScript definitions)

9

- **Installation**: `npm install scrollama`

10

11

## Core Imports

12

13

```javascript

14

import scrollama from "scrollama";

15

```

16

17

For CommonJS:

18

19

```javascript

20

const scrollama = require("scrollama");

21

```

22

23

Browser (CDN):

24

25

```html

26

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

27

```

28

29

## Basic Usage

30

31

```javascript

32

import scrollama from "scrollama";

33

34

// Create scrollama instance

35

const scroller = scrollama();

36

37

// Setup with step elements and callbacks

38

scroller

39

.setup({

40

step: ".step", // required: CSS selector for step elements

41

offset: 0.5, // trigger when element is 50% in viewport

42

debug: false // optional visual debugging

43

})

44

.onStepEnter((response) => {

45

// Fired when step enters offset threshold

46

const { element, index, direction } = response;

47

console.log("Step entered:", index, direction);

48

})

49

.onStepExit((response) => {

50

// Fired when step exits offset threshold

51

const { element, index, direction } = response;

52

console.log("Step exited:", index, direction);

53

});

54

```

55

56

## Architecture

57

58

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

59

60

- **Factory Function**: `scrollama()` creates instances with chainable method calls

61

- **Observer Management**: Automatically manages IntersectionObserver instances per step

62

- **Resize Handling**: Built-in ResizeObserver automatically handles viewport changes

63

- **Progress Tracking**: Optional fine-grained progress monitoring within steps

64

- **Debug Mode**: Visual overlays for development and testing

65

66

## Capabilities

67

68

### Instance Creation

69

70

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

71

72

```javascript { .api }

73

/**

74

* Creates a new scrollama instance

75

* @returns ScrollamaInstance - Instance with chainable configuration methods

76

*/

77

function scrollama(): ScrollamaInstance;

78

```

79

80

### Setup Configuration

81

82

Configures the scrollama instance with step elements and interaction settings.

83

84

```javascript { .api }

85

/**

86

* Configure scrollama instance with steps and options

87

* @param options - Configuration object

88

* @returns ScrollamaInstance - For method chaining

89

*/

90

setup(options: ScrollamaOptions): ScrollamaInstance;

91

92

interface ScrollamaOptions {

93

/** Required: CSS selector, NodeList, HTMLElement[], or single HTMLElement for step elements */

94

step: string | NodeList | HTMLElement[] | HTMLElement;

95

/** Trigger position in viewport: 0-1 or string with "px" (default: 0.5) */

96

offset?: number | string;

97

/** Enable incremental progress updates (default: false) */

98

progress?: boolean;

99

/** Granularity of progress updates in pixels, minimum 1 (default: 4) */

100

threshold?: number;

101

/** Only trigger steps once, then remove listeners (default: false) */

102

once?: boolean;

103

/** Show visual debugging overlays (default: false) */

104

debug?: boolean;

105

/** Parent element for step selector, useful for shadow DOM */

106

parent?: HTMLElement;

107

/** Parent element for scroll story, for overflow: scroll/auto containers */

108

container?: HTMLElement;

109

/** Element used as viewport for visibility checking (default: browser viewport) */

110

root?: HTMLElement;

111

}

112

```

113

114

**Usage Example:**

115

116

```javascript

117

// Basic setup

118

scroller.setup({

119

step: ".story-step"

120

});

121

122

// Advanced setup with progress tracking

123

scroller.setup({

124

step: ".story-step",

125

offset: 0.8, // Trigger at 80% viewport height

126

progress: true, // Enable progress callbacks

127

threshold: 2, // Higher granularity

128

debug: true, // Show debug overlays

129

container: document.querySelector('.scroll-container')

130

});

131

```

132

133

### Step Event Callbacks

134

135

Register callbacks for step enter and exit events.

136

137

```javascript { .api }

138

/**

139

* Callback fired when step elements enter the offset threshold

140

* @param callback - Function receiving step event data

141

* @returns ScrollamaInstance - For method chaining

142

*/

143

onStepEnter(callback: (response: CallbackResponse) => void): ScrollamaInstance;

144

145

/**

146

* Callback fired when step elements exit the offset threshold

147

* @param callback - Function receiving step event data

148

* @returns ScrollamaInstance - For method chaining

149

*/

150

onStepExit(callback: (response: CallbackResponse) => void): ScrollamaInstance;

151

152

interface CallbackResponse {

153

/** The DOM element that triggered the event */

154

element: HTMLElement;

155

/** Zero-based index of the step element */

156

index: number;

157

/** Scroll direction when event fired */

158

direction: "up" | "down";

159

}

160

```

161

162

**Usage Example:**

163

164

```javascript

165

scroller

166

.onStepEnter((response) => {

167

const { element, index, direction } = response;

168

element.classList.add('is-active');

169

170

// Handle different steps

171

if (index === 0) {

172

// First step logic

173

} else if (index === 2) {

174

// Third step logic

175

}

176

})

177

.onStepExit((response) => {

178

const { element, index, direction } = response;

179

element.classList.remove('is-active');

180

});

181

```

182

183

### Progress Tracking

184

185

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

186

187

```javascript { .api }

188

/**

189

* Callback fired for incremental progress through steps

190

* @param callback - Function receiving progress event data

191

* @returns ScrollamaInstance - For method chaining

192

*/

193

onStepProgress(callback: (response: ProgressCallbackResponse) => void): ScrollamaInstance;

194

195

interface ProgressCallbackResponse {

196

/** The DOM element being tracked */

197

element: HTMLElement;

198

/** Zero-based index of the step element */

199

index: number;

200

/** Progress through the step (0.0 to 1.0) */

201

progress: number;

202

/** Current scroll direction */

203

direction: "up" | "down";

204

}

205

```

206

207

**Usage Example:**

208

209

```javascript

210

scroller

211

.setup({

212

step: ".step",

213

progress: true,

214

threshold: 1 // Very granular updates

215

})

216

.onStepProgress((response) => {

217

const { element, index, progress, direction } = response;

218

219

// Update progress bar

220

const progressBar = element.querySelector('.progress-bar');

221

progressBar.style.width = `${progress * 100}%`;

222

223

// Fade in/out based on progress

224

element.style.opacity = progress;

225

});

226

```

227

228

### Offset Management

229

230

Get or set the trigger offset position dynamically.

231

232

```javascript { .api }

233

/**

234

* Get or set the offset trigger value (note: method name is 'offset' in implementation)

235

* @param value - Optional new offset value (0-1 or string with "px")

236

* @returns ScrollamaInstance if setting, current offset value if getting

237

*/

238

offset(value?: number | string): ScrollamaInstance | number;

239

```

240

241

**Usage Example:**

242

243

```javascript

244

// Get current offset

245

const currentOffset = scroller.offset();

246

247

// Set new offset

248

scroller.offset(0.25); // Trigger at 25% viewport height

249

scroller.offset("100px"); // Trigger at 100px from top

250

```

251

252

### Instance Control

253

254

Control scrollama instance state and lifecycle.

255

256

```javascript { .api }

257

/**

258

* Resume observing for trigger changes (if previously disabled)

259

* @returns ScrollamaInstance - For method chaining

260

*/

261

enable(): ScrollamaInstance;

262

263

/**

264

* Stop observing for trigger changes

265

* @returns ScrollamaInstance - For method chaining

266

*/

267

disable(): ScrollamaInstance;

268

269

/**

270

* Manual resize trigger (automatic with built-in ResizeObserver)

271

* @returns ScrollamaInstance - For method chaining

272

*/

273

resize(): ScrollamaInstance;

274

275

/**

276

* Remove all observers and callback functions

277

* @returns void

278

*/

279

destroy(): void;

280

```

281

282

**Usage Example:**

283

284

```javascript

285

// Temporarily disable

286

scroller.disable();

287

288

// Re-enable later

289

scroller.enable();

290

291

// Clean up when done

292

scroller.destroy();

293

```

294

295

## Custom Offset Data Attributes

296

297

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

298

299

```html { .api }

300

<!-- Use percentage (0-1) -->

301

<div class="step" data-offset="0.25">Step with 25% offset</div>

302

303

<!-- Use pixels -->

304

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

305

306

<!-- Use default global offset -->

307

<div class="step">Step with global offset</div>

308

```

309

310

## Types

311

312

Complete TypeScript type definitions for all interfaces and callbacks.

313

314

```typescript { .api }

315

interface ScrollamaInstance {

316

setup(options: ScrollamaOptions): ScrollamaInstance;

317

onStepEnter(callback: StepCallback): ScrollamaInstance;

318

onStepExit(callback: StepCallback): ScrollamaInstance;

319

onStepProgress(callback: StepProgressCallback): ScrollamaInstance;

320

offset(value?: number | string): ScrollamaInstance | number;

321

resize(): ScrollamaInstance;

322

enable(): ScrollamaInstance;

323

disable(): ScrollamaInstance;

324

destroy(): void;

325

}

326

327

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

328

329

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

330

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

331

```

332

333

## Common Patterns

334

335

### Sticky Graphic Side-by-Side

336

337

```html

338

<div class="container">

339

<div class="graphic">

340

<!-- Fixed graphic content -->

341

</div>

342

<div class="scroller">

343

<div class="step" data-step="1">First step content</div>

344

<div class="step" data-step="2">Second step content</div>

345

<div class="step" data-step="3">Third step content</div>

346

</div>

347

</div>

348

```

349

350

```css

351

.container {

352

display: flex;

353

}

354

355

.graphic {

356

position: sticky;

357

top: 0;

358

flex: 1;

359

height: 100vh;

360

}

361

362

.scroller {

363

flex: 1;

364

}

365

366

.step {

367

margin-bottom: 80vh;

368

padding: 1rem;

369

}

370

```

371

372

### Mobile-Friendly Pixel Offsets

373

374

```javascript

375

// Use pixel offsets for consistent mobile behavior

376

const isMobile = window.innerWidth < 768;

377

const offset = isMobile ? "150px" : 0.5;

378

379

scroller.setup({

380

step: ".step",

381

offset: offset

382

});

383

```

384

385

### Dynamic Step Management

386

387

```javascript

388

// Add new steps dynamically

389

function addStep(content) {

390

const newStep = document.createElement('div');

391

newStep.className = 'step';

392

newStep.textContent = content;

393

document.querySelector('.scroller').appendChild(newStep);

394

395

// Reconfigure scrollama

396

scroller.setup({

397

step: ".step" // Automatically picks up new steps

398

});

399

}

400

```