lazer-slider
v1.10.0
Published
A lightweight, accessible slider with smooth scroll-to-snap animations and drag-to-scroll support
Downloads
92
Maintainers
Readme
Lazer Slider
A lightweight, accessible slider with smooth scroll-to-snap animations, drag-to-scroll support, and full keyboard navigation.
Features
- Smooth Animations - Buttery smooth scroll animations using
requestAnimationFramewith customizable easing - Drag to Scroll - Mouse and touch drag support with momentum physics
- Responsive - Separate settings for mobile and desktop (slidesPerView, slidesPerScroll)
- Vertical & Horizontal - Support for both horizontal and vertical slider directions
- Loop Mode - Infinite loop navigation
- Autoplay - Automatic slide advancement with pause on hover
- Auto-Scroll - Continuous smooth scrolling with drag interaction and seamless infinite loop
- Automatic Bullets - Auto-generate navigation bullets from slides
- Thumbs Gallery - Auto-generate thumbnail navigation from slide images
- Accessible - Full ARIA support, keyboard navigation (arrow keys)
- Optional CSS - Ready-to-use styles with CSS custom properties for easy theming
- Lightweight - Zero dependencies, ~17KB minified
- TypeScript - Full type definitions included
Installation
npm install lazer-sliderQuick Start
HTML Structure
<div class="lazer-slider">
<button class="lazer-nav lazer-prev">‹</button>
<button class="lazer-nav lazer-next">›</button>
<div class="lazer-feed">
<div class="lazer-slide">Slide 1</div>
<div class="lazer-slide">Slide 2</div>
<div class="lazer-slide">Slide 3</div>
</div>
<div class="lazer-bullets"></div>
</div>CSS
Import the optional stylesheet for ready-to-use styles:
import 'lazer-slider/css'Or in HTML:
<link rel="stylesheet" href="node_modules/lazer-slider/dist/lazer-slider.css">Note: The CSS is optional. You can write your own styles or customize the included ones using CSS custom properties. See Styling for details.
JavaScript
import { createSlider } from 'lazer-slider'
import 'lazer-slider/css'
const slider = createSlider({
feed: document.querySelector('.lazer-feed'),
slides: [...document.querySelectorAll('.lazer-slide')],
prevSlideButton: document.querySelector('.lazer-prev'),
nextSlideButton: document.querySelector('.lazer-next'),
bulletsContainer: document.querySelector('.lazer-bullets'),
bulletsClass: 'lazer-bullet',
enableDragToScroll: true
})
// Cleanup when done
slider.unload()Configuration Options
Required
| Option | Type | Description |
|--------|------|-------------|
| feed | HTMLElement | Container element that holds the slides (scrollable) |
| slides | HTMLElement[] | Array of slide elements |
Navigation
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| prevSlideButton | HTMLElement \| null | null | Previous slide button |
| nextSlideButton | HTMLElement \| null | null | Next slide button |
| thumbs | HTMLElement[] | undefined | Thumbnail/dot elements for direct navigation (manual) |
| bulletsContainer | HTMLElement \| null | null | Container element for auto-generated bullets |
| bulletsClass | string | 'slider-bullet' | CSS class for bullet elements |
| bulletsActiveClass | string | 'active' | CSS class for active bullet element |
| thumbsContainer | HTMLElement \| null | null | Container element for auto-generated thumbnails |
| thumbsClass | string | 'slider-thumb' | CSS class for thumbnail elements |
| thumbsActiveClass | string | 'active' | CSS class for active thumbnail element |
| thumbImageSelector | string | 'img' | CSS selector to find image inside each slide |
| thumbSize | { width: number; height: number } | undefined | Fixed dimensions for thumbnail images |
Note: When
bulletsContainerorthumbsContaineris provided, navigation elements are automatically generated. Ifthumbsis also provided, both containers are ignored. Elements are only generated for visible slides.
Responsive
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| mobileSlidesPerView | number \| 'auto' | undefined | Slides visible at once on mobile |
| desktopSlidesPerView | number \| 'auto' | undefined | Slides visible at once on desktop |
| mobileSlidesPerScroll | number | 1 | Slides to scroll per nav click on mobile |
| desktopSlidesPerScroll | number | 1 | Slides to scroll per nav click on desktop |
| slideGap | number | 0 | Gap between slides in pixels |
Note: Desktop breakpoint is
min-width: 64rem(1024px)
Tip: Use
'auto'forslidesPerViewwhen slides have different/natural widths defined in CSS.
Behavior
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| direction | 'horizontal' \| 'vertical' | 'horizontal' | Slider orientation |
| enableDragToScroll | boolean | false | Enable mouse/touch drag to scroll |
| loop | boolean | false | Enable infinite loop navigation |
| autoplay | boolean | false | Enable automatic slide advancement |
| autoplayInterval | number | 3000 | Autoplay interval in milliseconds |
| pauseOnHover | boolean | true | Pause autoplay on hover/touch |
| autoScroll | boolean | false | Enable auto-scroll (continuous scrolling, implicitly enables loop) |
| autoScrollSpeed | number | 50 | Auto-scroll speed in pixels per second |
| autoScrollDirection | 'forward' \| 'backward' | 'forward' | Auto-scroll direction |
| autoScrollStartDelay | number | 0 | Delay in ms before auto-scroll starts or resumes after interaction |
| stopOnInteraction | boolean | false | Permanently stop auto-scroll after user drag (call play() to restart) |
| stopOnMouseEnter | boolean | true | Pause auto-scroll when mouse hovers over the slider |
| stopOnFocusIn | boolean | true | Pause auto-scroll when a slide receives keyboard focus |
| easing | EasingFunction | easeOutExpo | Custom easing function |
Note: When
autoScrollis enabled, loop mode is automatically enabled. Auto-scroll integrates with drag-to-scroll: dragging pauses the scroll, and it resumes after the drag ends (unlessstopOnInteractionistrue).
Scrollbar (Optional)
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| scrollbarThumb | HTMLElement \| null | null | Custom scrollbar thumb element |
| scrollbarTrack | HTMLElement \| null | null | Custom scrollbar track element |
Callbacks
| Option | Type | Description |
|--------|------|-------------|
| onScrollStart | (params) => void | Fired when scroll animation starts |
| onScroll | (params) => void | Fired during scroll |
| onScrollEnd | (params) => void | Fired when scroll animation ends |
Callback Parameters
// onScrollStart
{
currentScroll: number // Current scroll position in pixels
target: HTMLElement // Target slide element
direction: 'prev' | 'next'
}
// onScroll, onScrollEnd
{
currentScroll: number // Current scroll position in pixels
currentSlideIndex: number // Index of the current slide
}API Methods
const slider = createSlider({ /* options */ })
// Navigate to specific slide (0-indexed)
slider.goToIndex(2)
// Navigate prev/next (respects loop setting)
slider.next()
slider.prev()
// Autoplay/Auto-scroll control
slider.play() // Start autoplay or auto-scroll
slider.pause() // Stop autoplay or auto-scroll
// Recalculate dimensions (call after DOM changes)
slider.refresh()
// Cleanup all event listeners
slider.unload()Easing Functions
Built-in easing functions for smooth animations:
import {
createSlider,
easeOutExpo, // Default - smooth deceleration
easeOutCubic, // Gentler deceleration
easeInOutCubic, // Smooth acceleration & deceleration
easeOutQuad, // Gentle deceleration
linear // Constant speed
} from 'lazer-slider'
const slider = createSlider({
// ... options
easing: easeOutCubic
})Custom Easing
// Custom easing function (t goes from 0 to 1)
const customEase = (t: number) => t * t * t
const slider = createSlider({
// ... options
easing: customEase
})Examples
Basic Slider
const slider = createSlider({
feed: document.querySelector('.slider-feed'),
slides: [...document.querySelectorAll('.slide')]
})With Navigation Buttons
const slider = createSlider({
feed: document.querySelector('.slider-feed'),
slides: [...document.querySelectorAll('.slide')],
prevSlideButton: document.querySelector('.prev'),
nextSlideButton: document.querySelector('.next')
})Responsive Multi-Slide
const slider = createSlider({
feed: document.querySelector('.slider-feed'),
slides: [...document.querySelectorAll('.slide')],
mobileSlidesPerView: 1,
desktopSlidesPerView: 3,
slideGap: 16,
mobileSlidesPerScroll: 1,
desktopSlidesPerScroll: 3
})Autoplay Carousel
const slider = createSlider({
feed: document.querySelector('.slider-feed'),
slides: [...document.querySelectorAll('.slide')],
loop: true,
autoplay: true,
autoplayInterval: 5000,
pauseOnHover: true
})Auto-Scroll
Create a continuous scrolling effect with seamless infinite loop. Perfect for ticker-style content, logos, or announcements. Integrates with drag-to-scroll for smooth user interaction.
const ticker = createSlider({
feed: document.querySelector('.ticker-feed'),
slides: [...document.querySelectorAll('.ticker-item')],
autoScroll: true,
autoScrollSpeed: 80, // 80 pixels per second
autoScrollDirection: 'forward', // or 'backward'
enableDragToScroll: true, // Users can drag to interact
autoScrollStartDelay: 1000, // Resume 1s after interaction
stopOnMouseEnter: true // Pause on hover (default)
})
// Control programmatically
ticker.pause() // Stop auto-scroll
ticker.play() // Resume auto-scrollKey Features:
- Continuous smooth scrolling using
requestAnimationFrame(not CSS animations) - Frame-rate independent animation for consistent speed across devices
- Automatic loop clone management for seamless infinite scrolling
- Drag interaction support: dragging pauses auto-scroll, resumes after release
- Pause on hover, touch, and keyboard focus
- Configurable resume delay after user interaction
stopOnInteractionmode for one-time auto-scroll that stops after user engagement
When to use Auto-Scroll vs Autoplay:
- Auto-Scroll: For continuous ticker-style scrolling (news tickers, logo carousels, announcements)
- Autoplay: For slide-by-slide carousel navigation (image galleries, product showcases)
Vertical Slider
Create a vertical scrolling slider for content that flows top-to-bottom.
<div class="vertical-slider">
<button class="slider-prev">↑</button>
<button class="slider-next">↓</button>
<div class="slider-feed">
<div class="slide">Slide 1</div>
<div class="slide">Slide 2</div>
<div class="slide">Slide 3</div>
</div>
</div>.slider-feed {
display: flex;
flex-direction: column;
overflow-y: auto;
height: 400px; /* Set a fixed height for vertical scrolling */
scrollbar-width: none;
}
.slider-feed::-webkit-scrollbar {
display: none;
}
.slide {
scroll-snap-align: start;
flex-shrink: 0;
}const verticalSlider = createSlider({
feed: document.querySelector('.slider-feed'),
slides: [...document.querySelectorAll('.slide')],
prevSlideButton: document.querySelector('.slider-prev'),
nextSlideButton: document.querySelector('.slider-next'),
direction: 'vertical',
mobileSlidesPerView: 1,
desktopSlidesPerView: 1,
enableDragToScroll: true
})Key Features:
- Full vertical scrolling support with smooth animations
- Keyboard navigation with ArrowUp/ArrowDown keys
- Drag-to-scroll works in vertical direction
- Loop mode and autoplay work vertically
- Custom scrollbar can be styled vertically
When to use Vertical Slider:
- Full-page vertical carousels
- Testimonial/review sliders with tall content
- Timeline or step-by-step displays
- Mobile-first vertical content experiences
Bullets/Dots Navigation
You can add clickable dots that navigate to specific slides using either manual or automatic generation.
Automatic Bullets (Recommended)
The slider can automatically generate bullets from your slides. Just provide a container element:
<div class="slider">
<div class="slider-feed">
<div class="slide">Slide 1</div>
<div class="slide">Slide 2</div>
<div class="slide">Slide 3</div>
</div>
<div class="slider-bullets"></div>
</div>.slider-bullets {
display: flex;
gap: 8px;
justify-content: center;
margin-top: 16px;
}
.slider-bullet {
width: 12px;
height: 12px;
border-radius: 50%;
border: none;
background: #ccc;
cursor: pointer;
transition: background 0.3s;
}
.slider-bullet.active {
background: #333;
}const slider = createSlider({
feed: document.querySelector('.slider-feed'),
slides: [...document.querySelectorAll('.slide')],
bulletsContainer: document.querySelector('.slider-bullets'),
bulletsClass: 'slider-bullet', // Optional: default is 'slider-bullet'
bulletsActiveClass: 'active' // Optional: default is 'active'
})Features:
- Automatically generates one bullet per visible slide
- Full ARIA support (
role="tablist",aria-selected, etc.) - Active state automatically managed
- Only generates bullets for visible slides (hidden slides are skipped)
Manual Bullets
Alternatively, you can create bullets manually and pass them via the thumbs option:
<div class="slider">
<div class="slider-feed">
<div class="slide">Slide 1</div>
<div class="slide">Slide 2</div>
<div class="slide">Slide 3</div>
</div>
<div class="slider-dots">
<button class="dot" aria-label="Go to slide 1"></button>
<button class="dot" aria-label="Go to slide 2"></button>
<button class="dot" aria-label="Go to slide 3"></button>
</div>
</div>.slider-dots {
display: flex;
gap: 8px;
justify-content: center;
margin-top: 16px;
}
.dot {
width: 12px;
height: 12px;
border-radius: 50%;
border: none;
background: #ccc;
cursor: pointer;
transition: background 0.3s;
}
.dot.active {
background: #333;
}const slider = createSlider({
feed: document.querySelector('.slider-feed'),
slides: [...document.querySelectorAll('.slide')],
thumbs: [...document.querySelectorAll('.dot')]
})Note: If both
bulletsContainerandthumbsare provided,bulletsContaineris ignored and manualthumbsare used instead.
Thumbs Gallery
Auto-generate clickable thumbnail images from your slides. Perfect for product galleries, image carousels, and any slider where visual previews enhance navigation.
<div class="slider">
<div class="slider-feed">
<div class="slide">
<img src="/images/product-1.jpg" alt="Product 1">
<h3>Product Title 1</h3>
</div>
<div class="slide">
<img src="/images/product-2.jpg" alt="Product 2">
<h3>Product Title 2</h3>
</div>
<div class="slide">
<img src="/images/product-3.jpg" alt="Product 3">
<h3>Product Title 3</h3>
</div>
</div>
<!-- Empty container - thumbnails are auto-generated -->
<div class="thumbs-gallery"></div>
</div>.thumbs-gallery {
display: flex;
gap: 8px;
margin-top: 16px;
justify-content: center;
}
.slider-thumb {
padding: 0;
border: 2px solid transparent;
background: none;
cursor: pointer;
opacity: 0.6;
transition: opacity 0.2s, border-color 0.2s;
}
.slider-thumb:hover {
opacity: 0.8;
}
.slider-thumb.active {
opacity: 1;
border-color: #007bff;
}
.slider-thumb img {
display: block;
border-radius: 4px;
}const slider = createSlider({
feed: document.querySelector('.slider-feed'),
slides: [...document.querySelectorAll('.slide')],
thumbsContainer: document.querySelector('.thumbs-gallery'),
thumbsClass: 'slider-thumb', // Optional: default is 'slider-thumb'
thumbsActiveClass: 'active', // Optional: default is 'active'
thumbImageSelector: 'img', // Optional: CSS selector for slide images
thumbSize: { width: 80, height: 60 } // Optional: fixed thumbnail dimensions
})Features:
- Automatically extracts images from each slide using
thumbImageSelector - Creates clickable thumbnail buttons with proper accessibility attributes
- Full ARIA support (
role="tablist",aria-selected, etc.) - Active state automatically managed on scroll and click
- Only generates thumbnails for visible slides
- Optional fixed dimensions with
thumbSizefor consistent layouts
When to use Thumbs Gallery vs Bullets:
- Thumbs Gallery: For image-heavy sliders where visual previews help navigation (product galleries, portfolios)
- Bullets: For simpler navigation where slide content preview isn't needed (text carousels, testimonials)
Custom Scrollbar
Create a draggable scrollbar that syncs with the slider position.
<div class="slider">
<div class="slider-feed">
<div class="slide">Slide 1</div>
<div class="slide">Slide 2</div>
<div class="slide">Slide 3</div>
</div>
<div class="scrollbar-track">
<div class="scrollbar-thumb"></div>
</div>
</div>.scrollbar-track {
width: 100%;
height: 4px;
background: #eee;
border-radius: 2px;
margin-top: 16px;
position: relative;
}
.scrollbar-thumb {
height: 100%;
background: #333;
border-radius: 2px;
/* Width is set automatically by the slider */
}const slider = createSlider({
feed: document.querySelector('.slider-feed'),
slides: [...document.querySelectorAll('.slide')],
scrollbarThumb: document.querySelector('.scrollbar-thumb'),
scrollbarTrack: document.querySelector('.scrollbar-track')
})The slider automatically:
- Sets the thumb width based on visible content ratio
- Updates thumb position on scroll
- Hides the scrollbar when all content is visible
Auto Width Slides
Use slidesPerView: 'auto' when slides have different natural widths defined in CSS.
<div class="slider-feed">
<div class="slide slide--small">Small</div>
<div class="slide slide--medium">Medium</div>
<div class="slide slide--large">Large content here</div>
</div>.slide--small { width: 150px; }
.slide--medium { width: 250px; }
.slide--large { width: 400px; }const slider = createSlider({
feed: document.querySelector('.slider-feed'),
slides: [...document.querySelectorAll('.slide')],
mobileSlidesPerView: 'auto',
desktopSlidesPerView: 'auto',
slideGap: 16,
enableDragToScroll: true
})Full Featured
const slider = createSlider({
feed: document.querySelector('.slider-feed'),
slides: [...document.querySelectorAll('.slide')],
prevSlideButton: document.querySelector('.prev'),
nextSlideButton: document.querySelector('.next'),
// Navigation (choose one: bullets OR thumbs)
bulletsContainer: document.querySelector('.slider-bullets'), // Auto-generated bullets
// OR for image galleries:
// thumbsContainer: document.querySelector('.thumbs-gallery'),
// thumbSize: { width: 80, height: 60 },
// Responsive
mobileSlidesPerView: 1,
desktopSlidesPerView: 3,
slideGap: 20,
// Behavior
enableDragToScroll: true,
loop: true,
autoplay: true,
autoplayInterval: 4000,
// Callbacks
onScrollEnd: ({ currentSlideIndex }) => {
console.log('Now viewing slide:', currentSlideIndex)
}
})Accessibility
The slider automatically adds ARIA attributes for accessibility:
role="region"andaria-roledescription="carousel"on the feedrole="group"andaria-label="Slide X of Y"on each slidearia-controlslinking buttons to the feedaria-hiddenandtabindexmanagement for visibility- Keyboard navigation with arrow keys when feed is focused
TypeScript
Full TypeScript support with exported types:
import {
createSlider,
generateBullets,
generateThumbs,
type SliderSettings,
type Slider,
type EasingFunction,
type ScrollParams,
type ScrollStartParams,
type SliderDirection,
type AutoScrollDirection
} from 'lazer-slider'Note:
generateBulletsandgenerateThumbsare also exported if you need to manually generate navigation elements outside of the slider initialization.
Styling
Optional CSS
Lazer Slider ships with an optional stylesheet that provides ready-to-use styles:
import 'lazer-slider/css'CSS Classes
The stylesheet provides these class names:
| Class | Description |
|-------|-------------|
| .lazer-slider | Optional wrapper container |
| .lazer-feed | Scrollable container (horizontal) |
| .lazer-feed.lazer-vertical | Vertical slider variant |
| .lazer-feed.lazer-draggable | Adds grab cursor for drag-enabled sliders |
| .lazer-slide | Individual slide |
| .lazer-bullets | Bullets container |
| .lazer-bullet | Individual bullet/dot |
| .lazer-thumbs | Thumbnails container |
| .lazer-thumb | Individual thumbnail |
| .lazer-track | Scrollbar track |
| .lazer-scrollbar | Scrollbar thumb |
| .lazer-nav | Navigation button base |
| .lazer-prev | Previous button |
| .lazer-next | Next button |
CSS Custom Properties
Customize the look by overriding CSS variables:
:root {
/* Bullets */
--lazer-bullet-size: 12px;
--lazer-bullet-gap: 8px;
--lazer-bullet-color: #cbd5e1;
--lazer-bullet-active-color: #1e293b;
--lazer-bullet-hover-scale: 1.2;
/* Thumbnails */
--lazer-thumb-gap: 8px;
--lazer-thumb-opacity: 0.6;
--lazer-thumb-hover-opacity: 0.8;
--lazer-thumb-active-opacity: 1;
--lazer-thumb-border-width: 2px;
--lazer-thumb-border-color: transparent;
--lazer-thumb-active-border-color: #3b82f6;
--lazer-thumb-border-radius: 4px;
/* Scrollbar */
--lazer-track-height: 4px;
--lazer-track-color: #e2e8f0;
--lazer-thumb-color: #1e293b;
--lazer-track-radius: 2px;
/* Navigation */
--lazer-nav-size: 40px;
--lazer-nav-offset: 12px;
--lazer-nav-bg: rgba(255, 255, 255, 0.9);
--lazer-nav-hover-bg: white;
--lazer-nav-radius: 50%;
--lazer-nav-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
/* Transitions */
--lazer-duration: 0.3s;
--lazer-easing: ease;
}Custom Styles (No CSS Import)
If you prefer not to use the included styles, here's the minimal CSS required:
.slider-feed {
display: flex;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
}
.slider-feed::-webkit-scrollbar {
display: none;
}
.slide {
flex-shrink: 0;
}Browser Support
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
Requires scroll-snap-type CSS support for snap behavior.
License
UNLICENSED
