Description
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.
Features
- 🔄 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
Installation
Install the package via npm:
npm install @rvanbaalen/custom-scroll
The @rvanbaalen/signals
package will be automatically installed as a dependency.
Including the required files
ES Modules (Recommended)
// Import the scrollbar components
import { HorizontalScrollbar, VerticalScrollbar } from '@rvanbaalen/custom-scroll';
// Import the default styles (optional)
import '@rvanbaalen/custom-scroll/scrollbar.css';
Script Tags (Browser)
<!-- 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>
Usage
Basic Example
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)`;
}
});
Programmatic Control
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);
});
Advanced Signal Usage
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);
});
Smooth Animations
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);
Styling
Using the included stylesheet
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">
Creating custom styles
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 */
}
Themed 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);
}
Advanced styling techniques
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 */
}
API Reference
HorizontalScrollbar
new HorizontalScrollbar({
container: HTMLElement, // Container element
contentWidth: number, // Total scrollable width
visibleWidth: number, // Width of the visible area
onScroll: Function // Callback function(scrollX)
})
VerticalScrollbar
new VerticalScrollbar({
container: HTMLElement, // Container element
contentHeight: number, // Total scrollable height
visibleHeight: number, // Height of the visible area
onScroll: Function // Callback function(scrollY)
})
Methods
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
Properties
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
Use Cases
- 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
Browser Support
Works in all modern browsers that support ES6.
Development
- Clone the repository
- Install dependencies:
npm install
- Start the development server:
npm run dev
- Build for production:
npm run build
Contributing
Contributions are welcome! If you have any suggestions, improvements, or bug fixes, please open an issue or submit a pull request.
License
Distributed under the MIT License. See the LICENSE file for more information.