Native CSS Solutions (2025)
1. CSS scroll-timeline & view-timeline NEW
Description: Native browser implementation for scroll-driven animations
Mini Preview: No
Browser Support: Modern browsers (Chrome 115+, Firefox 110+)
Source: MDN Web Docs
1 2 3 4 5 6 7 8 9 10 11 |
.progress-bar { animation: progress-animation auto linear; animation-timeline: scroll(root); } @keyframes progress-animation { 0% { width: 0%; } 100% { width: 100%; } } |
Benefits:
- No JavaScript required
- Excellent performance
- Native browser optimization
- Future-proof solution
Traditional Progress Bars
2. Vanilla JavaScript Progress Bar
Description: Simple, lightweight scroll progress indicator
Mini Preview: No
Size: <1KB
Source: W3Schools
1 2 3 4 5 6 |
window.addEventListener('scroll', () => { const scrolled = (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100; document.getElementById('progress-bar').style.width = scrolled + '%'; }); |
1 2 3 4 5 6 7 8 9 10 11 |
#progress-bar { position: fixed; top: 0; left: 0; height: 4px; background: #4CAF50; z-index: 1000; transition: width 0.1s ease; } |
3. scrollProgress.js
Description: Lightweight library for scroll position tracking
Mini Preview: No
Size: 2KB
Source: CSS Script
1 2 3 4 5 6 7 8 9 |
import { ScrollProgress } from 'scrollprogress.js'; const progress = new ScrollProgress({ target: '.progress-indicator', type: 'horizontal', // or 'circular' color: '#007bff' }); |
Advanced Animation Libraries
4. GSAP ScrollTrigger POPULAR
Description: Professional-grade scroll animations and progress tracking
Mini Preview: No (but can be customized)
Size: 35KB
Source: GSAP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
gsap.registerPlugin(ScrollTrigger); gsap.to(".progress-bar", { scaleX: 1, ease: "none", scrollTrigger: { trigger: "body", start: "top top", end: "bottom bottom", scrub: true } }); |
5. Motion.page ScrollTrigger
Description: Alternative to GSAP with similar functionality
Mini Preview: No
Size: 25KB
Source: Motion.page
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import { Motion } from 'motion.page'; Motion.timeline({ scrollTrigger: { trigger: '.content', start: 'top center', end: 'bottom center', onUpdate: (progress) => { document.querySelector('.indicator').style.width = `${progress * 100}%`; } } }); |
Minimap & Preview Solutions WITH PREVIEW
6. React Simple Minimap MINI PREVIEW
Description: VS Code-style minimap for React applications
Mini Preview: YES – Shows page thumbnail
Size: 15KB
Source: GitHub
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import { Minimap } from 'react-simple-minimap'; function App() { return ( <div> <Minimap className="minimap" selector=".content" width={200} height={400} /> <div className="content"> {/* Your content */} </div> </div> ); } |
7. React Flow MiniMap MINI PREVIEW
Description: Interactive minimap component with viewport indicator
Mini Preview: YES – Shows flow overview
Size: 45KB (with React Flow)
Source: React Flow
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import { ReactFlow, MiniMap } from 'reactflow'; function FlowWithMinimap() { return ( <ReactFlow> <MiniMap nodeColor="#ff6b6b" maskColor="#f0f0f0" style={{ height: 120, width: 200 }} /> </ReactFlow> ); } |
8. Pagemap.js MINI PREVIEW
Description: Sublime Text-style minimap using HTML5 Canvas
Mini Preview: YES – Visual page structure representation
Size: 8KB
GitHub Stars: 1.3k
Source: GitHub
1 2 3 |
<canvas id='map'></canvas> |
1 2 3 4 5 6 7 8 9 10 |
#map { position: fixed; top: 0; right: 0; width: 200px; height: 100%; z-index: 100; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import pagemap from 'pagemap'; pagemap(document.querySelector('#map'), { viewport: null, styles: { 'header,footer,section,article': 'rgba(0,0,0,0.08)', 'h1,a': 'rgba(0,0,0,0.10)', 'h2,h3,h4': 'rgba(0,0,0,0.08)' }, back: 'rgba(0,0,0,0.02)', view: 'rgba(0,0,0,0.05)', drag: 'rgba(0,0,0,0.10)', interval: null }); |
Features:
- Clickable navigation
- Drag viewport indicator
- Custom element styling
- Real-time updates
- Lightweight implementation
9. Custom Minimap with Canvas MINI PREVIEW
Description: Custom implementation using HTML5 Canvas
Mini Preview: YES – Custom thumbnail generation
Size: Variable
Source: Ben Knight Blog
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
class DocumentMinimap { constructor(options) { this.canvas = options.canvas; this.target = options.target; this.init(); } init() { this.generateThumbnail(); this.addScrollListener(); } generateThumbnail() { const ctx = this.canvas.getContext('2d'); // Convert DOM to canvas representation html2canvas(this.target).then(canvas => { ctx.drawImage(canvas, 0, 0, this.canvas.width, this.canvas.height); }); } addScrollListener() { window.addEventListener('scroll', () => { this.updateViewport(); }); } } |
Navigation-Based Indicators
10. Intersection Observer ScrollSpy MODERN
Description: Performance-optimized section highlighting
Mini Preview: No
Size: 3KB
Source: Web Dev Simplified
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { const id = entry.target.getAttribute('id'); const navLink = document.querySelector(`nav a[href="#${id}"]`); if (entry.isIntersecting) { navLink.classList.add('active'); } else { navLink.classList.remove('active'); } }); }, { threshold: 0.5 }); document.querySelectorAll('section').forEach(section => { observer.observe(section); }); |
11. scrollspy.js
Description: Lightweight scrollspy with smooth scrolling
Mini Preview: No
Size: 4KB
Source: CSS Script
1 2 3 4 5 6 7 8 9 |
import { ScrollSpy } from 'scrollspy.js'; const spy = new ScrollSpy({ nav: '.navigation', smooth: true, offset: 100 }); |
Custom Scrollbar Solutions
12. Smooth Scrollbar POPULAR
Description: Fully customizable scrollbar replacement
Mini Preview: Can be extended
Size: 45KB
Source: Smooth Scrollbar
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import Scrollbar from 'smooth-scrollbar'; const scrollbar = Scrollbar.init(document.querySelector('.container'), { plugins: { overscroll: true } }); // Add progress indicator scrollbar.addListener((status) => { const progress = status.offset.y / status.limit.y; updateProgressBar(progress); }); |
13. SimpleBar
Description: Custom scrollbars for web and mobile
Mini Preview: No
Size: 25KB
Source: SimpleBar
1 2 3 4 5 6 7 8 |
import SimpleBar from 'simplebar'; new SimpleBar(document.getElementById('myElement'), { autoHide: false, scrollbarMaxSize: 50 }); |
Reading Progress Indicators
14. Reading Progress Circle VISUAL
Description: Circular progress indicator for articles
Mini Preview: No
Size: 2KB
Source: jQuery Script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function updateReadingProgress() { const article = document.querySelector('article'); const scrolled = window.scrollY; const totalHeight = article.offsetHeight - window.innerHeight; const progress = (scrolled / totalHeight) * 100; const circle = document.querySelector('.progress-circle'); const circumference = 2 * Math.PI * 50; // radius = 50 const offset = circumference - (progress / 100) * circumference; circle.style.strokeDashoffset = offset; } window.addEventListener('scroll', updateReadingProgress); |
1 2 3 4 5 6 7 |
.progress-circle { stroke-dasharray: 314; stroke-dashoffset: 314; transition: stroke-dashoffset 0.1s ease; } |
15. Multi-Section Progress
Description: Progress tracking across multiple content sections
Mini Preview: Shows section previews
Size: 8KB
Source: Custom implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
class MultiSectionProgress { constructor(sections) { this.sections = Array.from(sections); this.init(); } init() { this.createIndicators(); this.bindEvents(); } createIndicators() { this.sections.forEach((section, index) => { const indicator = document.createElement('div'); indicator.className = 'section-indicator'; indicator.dataset.section = index; // Add mini preview const preview = this.generatePreview(section); indicator.appendChild(preview); document.querySelector('.progress-container').appendChild(indicator); }); } generatePreview(section) { const preview = document.createElement('div'); preview.className = 'mini-preview'; preview.innerHTML = section.querySelector('h2, h3')?.textContent || 'Section'; return preview; } } |
Performance Optimized Solutions
16. Throttled Scroll Progress
Description: Performance-optimized vanilla JS solution
Mini Preview: No
Size: 1KB
Source: Custom implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
function throttle(func, delay) { let timeoutId; let lastExecTime = 0; return function (...args) { const currentTime = Date.now(); if (currentTime - lastExecTime > delay) { func.apply(this, args); lastExecTime = currentTime; } else { clearTimeout(timeoutId); timeoutId = setTimeout(() => { func.apply(this, args); lastExecTime = Date.now(); }, delay - (currentTime - lastExecTime)); } }; } const updateProgress = throttle(() => { const scrolled = window.pageYOffset; const maxHeight = document.documentElement.scrollHeight - window.innerHeight; const progress = (scrolled / maxHeight) * 100; document.getElementById('progress').style.width = `${progress}%`; }, 16); // ~60fps window.addEventListener('scroll', updateProgress, { passive: true }); |
Browser Compatibility & Recommendations
For 2025 Projects:
Best Overall: CSS scroll-timeline (for modern browsers) + GSAP ScrollTrigger (fallback)
Best with Preview: Pagemap.js, React Simple Minimap, or custom Canvas solution
Best Performance: Intersection Observer + throttled scroll events
Best Animation: GSAP ScrollTrigger
Most Lightweight: Vanilla JavaScript with throttling
Browser Support Matrix:
Solution | Chrome | Firefox | Safari | Edge | IE11 |
---|---|---|---|---|---|
CSS scroll-timeline | 115+ | 110+ | 17+ | 115+ | No |
Intersection Observer | 51+ | 55+ | 12.1+ | 15+ | No |
GSAP ScrollTrigger | All | All | All | All | 9+ |
Vanilla JS | All | All | All | All | 9+ |
Performance Tips:
- Use
passive: true
for scroll listeners - Implement throttling or debouncing for scroll events
- Use
transform
instead of changingwidth/height
for better performance - Consider using
will-change: transform
for animated elements - Use Intersection Observer for section-based indicators
Implementation Examples
Complete Vanilla JS Solution:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
<!DOCTYPE html> <html> <head> <style> .progress-container { position: fixed; top: 0; left: 0; width: 100%; height: 4px; background: rgba(0,0,0,0.1); z-index: 1000; } .progress-bar { height: 100%; background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); width: 0%; transition: width 0.1s ease; } </style> </head> <body> <div class="progress-container"> <div class="progress-bar" id="progress"></div> </div> <script> function updateScrollProgress() { const scrolled = document.documentElement.scrollTop; const maxHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight; const scrollPercent = (scrolled / maxHeight) * 100; document.getElementById('progress').style.width = scrollPercent + '%'; } // Throttled scroll listener let ticking = false; function requestTick() { if (!ticking) { requestAnimationFrame(updateScrollProgress); ticking = true; setTimeout(() => { ticking = false; }, 16); } } window.addEventListener('scroll', requestTick, { passive: true }); </script> </body> </html> |
FAQ
What is a scroll mini map?
How do I implement a scroll mini map on my website?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
export class Minimap extends HTMLElement { constructor() { super(); this.canvas = document.createElement("canvas"); this.styleElement = document.createElement("style"); } set root(root) { this._root = root; const onScroll = () => this.redraw(); document.addEventListener("scroll", onScroll); } } |
Why is my scroll mini map not working properly?
How can I optimize scroll mini map performance?
Can I use CSS-only solutions for scroll mini maps?
1 2 3 4 5 6 7 8 9 10 11 |
#minimap { background: -moz-element(#content) no-repeat; background-size: 100% 100%; position: fixed; top: 0; right: 0; width: 200px; height: 100%; } |
What are the best libraries for creating scroll mini maps?
How do I handle responsive design with scroll mini maps?
Why does my mini map jump or behave erratically?
How do I customize the appearance of scroll mini map elements?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
const elementMap = { 'h1': '--minimap-heading', 'h2,h3,h4': '--minimap-subheading', 'p': '--minimap-text', 'img': '--minimap-media' }; const styles = { 'h1,h2,h3': 'rgba(255,0,0,0.8)', 'p': 'rgba(0,0,0,0.3)', 'img': 'rgba(0,255,0,0.6)' }; |