| name | locomotive-scroll |
| description | Comprehensive skill for Locomotive Scroll smooth scrolling library with parallax effects, viewport detection, and scroll-driven animations. Use this skill when implementing smooth scrolling experiences, creating parallax effects, building scroll-triggered animations, or developing immersive scrolling websites. Triggers on tasks involving Locomotive Scroll, smooth scrolling, parallax, scroll detection, scroll events, sticky elements, horizontal scrolling, or GSAP ScrollTrigger integration. Integrates with GSAP for advanced scroll-driven animations. |
Locomotive Scroll
Comprehensive guide for implementing smooth scrolling, parallax effects, and scroll-driven animations using Locomotive Scroll.
Overview
Locomotive Scroll is a JavaScript library that provides:
- Smooth scrolling: Hardware-accelerated smooth scroll with customizable easing
- Parallax effects: Element-level speed control for depth
- Viewport detection: Track when elements enter/exit viewport
- Scroll events: Monitor scroll progress for animation synchronization
- Sticky elements: Pin elements within defined boundaries
- Horizontal scrolling: Support for horizontal scroll layouts
When to use Locomotive Scroll:
- Building immersive landing pages with parallax
- Creating smooth, Apple-style scroll experiences
- Implementing scroll-triggered animations
- Developing narrative/storytelling websites
- Adding depth and motion to long-form content
Trade-offs:
- Scroll-hijacking can impact accessibility (provide disable option)
- Performance overhead on low-end devices (detect and disable)
- Mobile touch scrolling feels different (test extensively)
- Fixed positioning requires workarounds
Installation
npm install locomotive-scroll
import LocomotiveScroll from 'locomotive-scroll';
import 'locomotive-scroll/dist/locomotive-scroll.css';
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/locomotive-scroll/dist/locomotive-scroll.min.css">
<script src="https://cdn.jsdelivr.net/npm/locomotive-scroll/dist/locomotive-scroll.min.js"></script>
Core Concepts
1. HTML Structure
Every Locomotive Scroll implementation requires specific data attributes:
<div data-scroll-container>
<div data-scroll-section>
<h1 data-scroll>Basic detection</h1>
<div data-scroll data-scroll-speed="2">
Moves faster than scroll
</div>
<div data-scroll data-scroll-sticky>
Sticks within section
</div>
<div data-scroll data-scroll-id="hero">
Accessible via JavaScript
</div>
<div data-scroll data-scroll-call="fadeIn">
Triggers custom event
</div>
</div>
</div>
2. Initialization
const scroll = new LocomotiveScroll({
el: document.querySelector('[data-scroll-container]'),
smooth: true,
lerp: 0.1,
multiplier: 1,
class: 'is-inview',
repeat: false,
offset: [0, 0]
});
3. Data Attributes
| Attribute | Purpose | Example |
|---|
data-scroll | Enable detection | data-scroll |
data-scroll-speed | Parallax speed | data-scroll-speed="2" |
data-scroll-direction | Parallax axis | data-scroll-direction="horizontal" |
data-scroll-sticky | Sticky positioning | data-scroll-sticky |
data-scroll-target | Sticky boundary | data-scroll-target="#section" |
data-scroll-offset | Trigger offset | data-scroll-offset="20%" |
data-scroll-repeat | Repeat detection | data-scroll-repeat |
data-scroll-call | Event trigger | data-scroll-call="myFunction" |
data-scroll-id | Unique identifier | data-scroll-id="hero" |
data-scroll-class | Custom class | data-scroll-class="is-visible" |
Common Patterns
1. Basic Smooth Scrolling
import LocomotiveScroll from 'locomotive-scroll';
const scroll = new LocomotiveScroll({
el: document.querySelector('[data-scroll-container]'),
smooth: true
});
<div data-scroll-container>
<div data-scroll-section>
<h1>Smooth scrolling enabled</h1>
</div>
</div>
2. Parallax Effects
<div data-scroll data-scroll-speed="0.5">
Moves slower than scroll (background effect)
</div>
<div data-scroll data-scroll-speed="3">
Moves faster than scroll (foreground effect)
</div>
<div data-scroll data-scroll-speed="-2">
Moves in opposite direction
</div>
<div data-scroll data-scroll-speed="2" data-scroll-direction="horizontal">
Moves horizontally
</div>
3. Viewport Detection and Callbacks
scroll.on('scroll', (args) => {
console.log(args.scroll.y);
console.log(args.speed);
console.log(args.direction);
if (args.currentElements['hero']) {
const progress = args.currentElements['hero'].progress;
console.log(`Hero progress: ${progress}`);
}
});
scroll.on('call', (value, way, obj) => {
console.log(`Event triggered: ${value}`);
});
<div data-scroll data-scroll-id="hero">Hero section</div>
<div data-scroll data-scroll-call="playVideo">Video section</div>
4. Sticky Elements
<div data-scroll-section>
<div data-scroll data-scroll-sticky>
I stick while section is in view
</div>
</div>
<div id="sticky-container">
<div data-scroll data-scroll-sticky data-scroll-target="#sticky-container">
I stick within #sticky-container
</div>
</div>
5. Programmatic Scrolling
scroll.scrollTo('#target-section');
scroll.scrollTo('top');
scroll.scrollTo('bottom');
scroll.scrollTo('#target', {
offset: -100,
duration: 1000,
easing: [0.25, 0.0, 0.35, 1.0],
disableLerp: true,
callback: () => console.log('Scrolled!')
});
scroll.scrollTo(500);
6. Horizontal Scrolling
const scroll = new LocomotiveScroll({
el: document.querySelector('[data-scroll-container]'),
smooth: true,
direction: 'horizontal'
});
<div data-scroll-container>
<div data-scroll-section style="display: flex; width: 300vw;">
<div>Section 1</div>
<div>Section 2</div>
<div>Section 3</div>
</div>
</div>
7. Mobile Responsiveness
const scroll = new LocomotiveScroll({
el: document.querySelector('[data-scroll-container]'),
smooth: true,
tablet: {
smooth: true,
breakpoint: 1024
},
smartphone: {
smooth: false,
breakpoint: 768
}
});
Integration with GSAP ScrollTrigger
Locomotive Scroll and GSAP ScrollTrigger work together for advanced animations:
import LocomotiveScroll from 'locomotive-scroll';
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
const locoScroll = new LocomotiveScroll({
el: document.querySelector('[data-scroll-container]'),
smooth: true
});
locoScroll.on('scroll', ScrollTrigger.update);
ScrollTrigger.scrollerProxy('[data-scroll-container]', {
scrollTop(value) {
return arguments.length
? locoScroll.scrollTo(value, 0, 0)
: locoScroll.scroll.instance.scroll.y;
},
getBoundingClientRect() {
return {
top: 0,
left: 0,
width: window.innerWidth,
height: window.innerHeight
};
},
pinType: document.querySelector('[data-scroll-container]').style.transform
? 'transform'
: 'fixed'
});
gsap.to('.fade-in', {
scrollTrigger: {
trigger: '.fade-in',
scroller: '[data-scroll-container]',
start: 'top bottom',
end: 'top center',
scrub: true
},
opacity: 1,
y: 0
});
ScrollTrigger.addEventListener('refresh', () => locoScroll.update());
ScrollTrigger.refresh();
Instance Methods
const scroll = new LocomotiveScroll();
scroll.init();
scroll.update();
scroll.destroy();
scroll.start();
scroll.stop();
scroll.scrollTo(target, options);
scroll.setScroll(x, y);
scroll.on('scroll', callback);
scroll.on('call', callback);
scroll.off('scroll', callback);
Performance Optimization
- Use
data-scroll-section to segment long pages:
<div data-scroll-container>
<div data-scroll-section>Section 1</div>
<div data-scroll-section>Section 2</div>
<div data-scroll-section>Section 3</div>
</div>
-
Limit parallax elements - Too many can impact performance
-
Disable on mobile if performance is poor:
smartphone: { smooth: false }
- Update on resize:
window.addEventListener('resize', () => {
scroll.update();
});
- Destroy when not needed:
scroll.destroy();
Common Pitfalls
1. Fixed Positioning Issues
Problem: position: fixed elements break with smooth scroll
Solution: Use data-scroll-sticky instead or add fixed elements outside container:
<nav style="position: fixed;">Navigation</nav>
<div data-scroll-container>
</div>
2. Images Not Lazy Loading
Problem: All images load at once
Solution: Integrate with lazy loading:
<img data-scroll data-src="image.jpg" class="lazy">
scroll.on('call', (func) => {
if (func === 'lazyLoad') {
}
});
3. Scroll Position Not Updating
Problem: Dynamic content doesn't update scroll positions
Solution: Call update() after DOM changes:
addDynamicContent();
scroll.update();
4. Accessibility Concerns
Problem: Screen readers and keyboard navigation broken
Solution: Provide disable option:
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const scroll = new LocomotiveScroll({
smooth: !prefersReducedMotion
});
5. Memory Leaks
Problem: Scroll instance not cleaned up on route changes (SPAs)
Solution: Always destroy on unmount:
useEffect(() => {
const scroll = new LocomotiveScroll();
return () => scroll.destroy();
}, []);
6. Z-Index Fighting
Problem: Parallax elements overlap incorrectly
Solution: Set explicit z-index on parallax layers:
[data-scroll-speed] {
position: relative;
z-index: var(--layer-depth);
}
Related Skills
- gsap-scrolltrigger: Advanced scroll-driven animations (use together)
- barba-js: Page transitions with Locomotive Scroll integration
- scroll-reveal-libraries: Simpler alternative for basic fade-in effects
- react-three-fiber: Scroll-driven 3D scenes (sync with Locomotive events)
- motion-framer: Alternative scroll animations in React
Resources
- Scripts:
generate_config.py - Configuration generator, integration_helper.py - GSAP integration code
- References:
api_reference.md - Complete API, gsap_integration.md - GSAP ScrollTrigger patterns
- Assets:
starter_locomotive/ - Complete starter template with examples