← See my other Open Source projects
A lightweight, customizable scrollbar library for JavaScript applications, now with reactive programming support via the @rvanbaalen/signals package. This package provides horizontal and vertical scrollbar components that you can style and control programmatically, perfect for custom UI implementations where native scrollbars don't meet your design requirements.
- 🔄 Custom horizontal and vertical scrollbars
- 🎨 Fully stylable through CSS
- 🖱️ Handles drag operations and track clicks
- 📏 Automatically sizes thumbs based on content
- 🔗 Reactive state management using signals
- 🚀 Programmatic scrolling control
- 📡 Event-based architecture via signals
- 💨 Smooth animations and interactions
Install the package via npm:
npm install @rvanbaalen/custom-scrollThe @rvanbaalen/signals package will be automatically installed as a dependency.
// Import the scrollbar components
import { HorizontalScrollbar, VerticalScrollbar } from '@rvanbaalen/custom-scroll';
// Import the default styles (optional)
import '@rvanbaalen/custom-scroll/scrollbar.css';<!-- Include the signals library first -->
<script src="path/to/node_modules/@rvanbaalen/signals/dist/signals.min.js"></script>
<!-- Then include the scrollbar library -->
<script src="path/to/node_modules/@rvanbaalen/custom-scroll/dist/custom-scroll.min.js"></script>
<!-- Include the styles -->
<link rel="stylesheet" href="path/to/node_modules/@rvanbaalen/custom-scroll/scrollbar.css">
<script>
// Access via the global namespace
const { HorizontalScrollbar, VerticalScrollbar } = customScroll;
</script>import { HorizontalScrollbar, VerticalScrollbar } from '@rvanbaalen/custom-scroll';
// Create a horizontal scrollbar
const horizontalScrollbar = new HorizontalScrollbar({
container: document.getElementById('my-container'),
contentWidth: 2000, // Total scrollable width in pixels
visibleWidth: 500, // Width of the visible area
onScroll: (scrollX) => {
// Handle horizontal scroll
document.getElementById('content').style.transform = `translateX(-${scrollX}px)`;
}
});
// Create a vertical scrollbar
const verticalScrollbar = new VerticalScrollbar({
container: document.getElementById('my-container'),
contentHeight: 3000, // Total scrollable height
visibleHeight: 600, // Height of the visible area
onScroll: (scrollY) => {
// Handle vertical scroll
document.getElementById('content').style.transform = `translateY(-${scrollY}px)`;
}
});The signal-based implementation enables programmatic control of the scrollbars:
// Scroll to a specific position
horizontalScrollbar.scrollTo(500);
// Scroll to the middle
const middleX = (horizontalScrollbar.store.state.contentSize - horizontalScrollbar.store.state.visibleSize) / 2;
horizontalScrollbar.scrollTo(middleX);
// Get current scroll position
const currentPosition = horizontalScrollbar.scrollValue;
// Update content dimensions
horizontalScrollbar.setContentSize(3000);
verticalScrollbar.setContentSize(5000);
// Update visible dimensions (e.g., on window resize)
window.addEventListener('resize', () => {
horizontalScrollbar.setVisibleSize(container.offsetWidth);
verticalScrollbar.setVisibleSize(container.offsetHeight);
});The signals integration allows for more complex reactive behavior:
// Get direct access to the scroll signal
const scrollSignal = horizontalScrollbar.getScrollSignal();
// Connect to the signal for custom behavior
const cleanup = scrollSignal.connect((scrollValue) => {
console.log('Horizontal scroll changed:', scrollValue);
updateOtherElement(scrollValue);
});
// Clean up when done
cleanup();
// Create synchronized scrollbars
horizontalScrollbar.getScrollSignal().connect((scrollX) => {
// Make vertical scrollbar follow horizontal scrollbar
const ratio = scrollX / (horizontalScrollbar.store.state.contentSize - horizontalScrollbar.store.state.visibleSize);
const targetY = ratio * (verticalScrollbar.store.state.contentSize - verticalScrollbar.store.state.visibleSize);
verticalScrollbar.scrollTo(targetY);
});Create smooth scroll animations by controlling the scrollbar programmatically:
function smoothScrollTo(scrollbar, targetPosition, duration = 500) {
const startPosition = scrollbar.scrollValue;
const distance = targetPosition - startPosition;
const startTime = performance.now();
function step(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
const easeProgress = 1 - Math.pow(1 - progress, 3); // Cubic ease-out
const currentPosition = startPosition + (distance * easeProgress);
scrollbar.scrollTo(currentPosition);
if (progress < 1) {
requestAnimationFrame(step);
}
}
requestAnimationFrame(step);
}
// Usage
smoothScrollTo(horizontalScrollbar, 1000, 800);The package includes a basic stylesheet that you can import directly:
// Import the default styles
import '@rvanbaalen/custom-scroll/scrollbar.css';Or include it via HTML if you're using a bundled version:
<link rel="stylesheet" href="path/to/node_modules/@rvanbaalen/custom-scroll/scrollbar.css">You can also create your own custom styles. Add these to your CSS:
/* Track styles */
.custom-scrollbar-track {
position: absolute;
background: #f1f1f1; /* Track background color */
border-radius: 4px; /* Rounded corners */
z-index: 100; /* Ensure proper stacking */
}
/* Thumb styles */
.custom-scrollbar-thumb {
position: absolute;
background: #888; /* Thumb color */
border-radius: 4px; /* Rounded corners */
cursor: pointer; /* Change cursor on hover */
z-index: 101; /* Stack above the track */
}
.custom-scrollbar-thumb:hover {
background: #555; /* Darker color on hover */
}
/* Container needs to be positioned relatively or absolutely */
#my-container {
position: relative;
overflow: hidden; /* Hide default scrollbars */
}You can create different themes or styles:
/* Dark theme */
.dark-theme .custom-scrollbar-track {
background: #333;
}
.dark-theme .custom-scrollbar-thumb {
background: #666;
}
.dark-theme .custom-scrollbar-thumb:hover {
background: #999;
}
/* Minimal theme */
.minimal-theme .custom-scrollbar-track {
background: transparent;
}
.minimal-theme .custom-scrollbar-thumb {
background: rgba(0, 0, 0, 0.2);
border-radius: 10px;
}
.minimal-theme .custom-scrollbar-thumb:hover {
background: rgba(0, 0, 0, 0.4);
}For more control, you can add additional classes to the scrollbars:
// Add a custom class to the track and thumb elements
horizontalScrollbar.track.classList.add('custom-horizontal-track');
horizontalScrollbar.thumb.classList.add('custom-horizontal-thumb');
verticalScrollbar.track.classList.add('custom-vertical-track');
verticalScrollbar.thumb.classList.add('custom-vertical-thumb');Then style these classes in your CSS:
/* Custom styling for horizontal scrollbar */
.custom-horizontal-track {
height: 6px; /* Thinner track */
bottom: 0; /* Position at bottom */
}
.custom-horizontal-thumb {
background: linear-gradient(to right, #4568dc, #b06ab3); /* Gradient thumb */
}
/* Custom styling for vertical scrollbar */
.custom-vertical-track {
width: 6px; /* Thinner track */
right: 0; /* Position at right */
}
.custom-vertical-thumb {
background: linear-gradient(to bottom, #4568dc, #b06ab3); /* Gradient thumb */
}new HorizontalScrollbar({
container: HTMLElement, // Container element
contentWidth: number, // Total scrollable width
visibleWidth: number, // Width of the visible area
onScroll: Function // Callback function(scrollX)
})new VerticalScrollbar({
container: HTMLElement, // Container element
contentHeight: number, // Total scrollable height
visibleHeight: number, // Height of the visible area
onScroll: Function // Callback function(scrollY)
})Both scrollbar classes provide the following methods:
update(scrollValue): Updates the thumb position and size based on the current scroll valuescrollTo(value): Programmatically scrolls to a specific positionsetContentSize(size): Updates the content size (width/height) and refreshes the scrollbarsetVisibleSize(size): Updates the visible size (width/height) and refreshes the scrollbargetScrollSignal(): Returns the signal that emits when scrolling occurs
store.state.contentSize: The total scrollable size (width or height)store.state.visibleSize: The visible size of the container (width or height)scrollValue: The current scroll position
- Custom data grids with fixed headers/columns
- Design systems requiring consistent scrollbars across browsers
- Interactive dashboards with custom UI components
- Maps and visualization tools
- Virtual scrolling implementations
- Synchronized scrolling between multiple containers
- Smooth scroll animations and effects
Works in all modern browsers that support ES6.
- Clone the repository
- Install dependencies:
npm install - Start the development server:
npm run dev - Build for production:
npm run build
Contributions are welcome! If you have any suggestions, improvements, or bug fixes, please open an issue or submit a pull request.
Distributed under the MIT License. See the LICENSE file for more information.