js-cloudimage-before-after
v1.0.1
Published
Interactive before/after image comparison slider with zoom, labels, and accessibility
Readme
Why js-cloudimage-before-after?
Existing before/after sliders are often rigid, inaccessible, or missing features like zoom and vertical orientation. This library was built to fill the gap:
- Lightweight — under 15 KB gzipped with zero runtime dependencies
- Accessible by default — WCAG 2.1 AA compliant out of the box
- Framework-agnostic — works with vanilla JS, React, or any framework
- Built-in zoom & pan — no need for a separate zoom library
- Multiple interaction modes — drag, hover, or click
- Optional Cloudimage CDN — serve optimally-sized images automatically
Features
- Three interaction modes — Drag, hover, or click to reveal before/after
- Horizontal & vertical — Slider works in both orientations
- Zoom & Pan — CSS transform-based with mouse wheel, pinch-to-zoom, double-click, drag-to-pan
- Handle styles — Arrows, circle, or minimal line
- Labels — Customizable before/after labels with configurable position
- Entrance animation — Smooth slider animation on first view with configurable duration, delay, and easing
- Fullscreen mode — Built-in fullscreen toggle
- WCAG 2.1 AA — Full keyboard navigation, ARIA attributes, focus management, reduced motion
- CSS variable theming — Light and dark themes, fully customizable
- Two init methods — JavaScript API and HTML data-attributes
- React wrapper — Separate entry point with component, hook, and ref API
- TypeScript — Full type definitions
- Cloudimage CDN — Optional responsive image loading
- Lazy loading — IntersectionObserver-based image lazy loading
Installation
npm install js-cloudimage-before-afterCDN
<script src="https://scaleflex.cloudimg.io/v7/plugins/js-cloudimage-before-after/1.0.0/js-cloudimage-before-after.min.js?vh=ad6399&func=proxy"></script>Quick Start
JavaScript API
import CIBeforeAfter from 'js-cloudimage-before-after';
const viewer = new CIBeforeAfter('#slider', {
beforeSrc: 'https://example.com/kitchen-before.jpg',
afterSrc: 'https://example.com/kitchen-after.jpg',
beforeAlt: 'Kitchen before renovation',
afterAlt: 'Kitchen after renovation',
zoom: true,
labels: { before: 'Before', after: 'After' },
theme: 'light',
handleStyle: 'arrows',
animate: { duration: 800 },
onSlide(position) {
console.log('Position:', position);
},
});HTML Data-Attributes
<div
data-ci-before-after-before-src="https://example.com/before.jpg"
data-ci-before-after-after-src="https://example.com/after.jpg"
data-ci-before-after-before-alt="Before renovation"
data-ci-before-after-after-alt="After renovation"
data-ci-before-after-zoom="true"
data-ci-before-after-theme="light"
data-ci-before-after-handle-style="arrows"
data-ci-before-after-label-before="Before"
data-ci-before-after-label-after="After"
></div>
<script>CIBeforeAfter.autoInit();</script>API Reference
Constructor
new CIBeforeAfter(element: HTMLElement | string, config: CIBeforeAfterConfig)Config
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| beforeSrc | string | — | Before image URL (required) |
| afterSrc | string | — | After image URL (required) |
| beforeAlt | string | 'Before' | Before image alt text |
| afterAlt | string | 'After' | After image alt text |
| mode | 'drag' \| 'hover' \| 'click' | 'drag' | Interaction mode |
| orientation | 'horizontal' \| 'vertical' | 'horizontal' | Slider direction |
| initialPosition | number | 50 | Starting position (0–100) |
| handleStyle | 'arrows' \| 'circle' \| 'line' | 'arrows' | Handle visual style |
| labels | boolean \| { before?: string; after?: string } | true | Show labels or custom text |
| labelPosition | 'top' \| 'bottom' | 'top' | Label placement |
| theme | 'light' \| 'dark' | 'light' | Color theme |
| zoom | boolean | false | Enable zoom & pan |
| zoomMax | number | 4 | Maximum zoom level |
| zoomMin | number | 1 | Minimum zoom level |
| zoomControls | boolean | true | Show zoom control buttons |
| zoomControlsPosition | string | 'bottom-right' | Zoom controls position (top-left, top-center, top-right, bottom-left, bottom-center, bottom-right) |
| scrollHint | boolean | true | Show scroll hint when zoomed |
| animate | boolean \| AnimateConfig | false | Entrance animation |
| animateOnce | boolean | true | Animate only on first view |
| fullscreenButton | boolean | true | Show fullscreen button |
| lazyLoad | boolean | true | Lazy load images |
| keyboardStep | number | 1 | Arrow key step (%) |
| keyboardLargeStep | number | 10 | Shift+Arrow step (%) |
| onSlide | (position: number) => void | — | Position change callback |
| onZoom | (level: number) => void | — | Zoom change callback |
| onFullscreenChange | (isFullscreen: boolean) => void | — | Fullscreen callback |
| onReady | () => void | — | Ready callback |
| cloudimage | CloudimageConfig | — | Cloudimage CDN config |
AnimateConfig
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| duration | number | 800 | Duration in ms |
| delay | number | 0 | Delay before start in ms |
| easing | string | 'ease-out' | CSS easing function |
Instance Methods
instance.setPosition(percent: number): void // Set slider position (0–100)
instance.getPosition(): number // Get current position
instance.setZoom(level: number): void // Set zoom level
instance.getZoom(): number // Get current zoom level
instance.resetZoom(): void // Reset zoom to 1×
instance.enterFullscreen(): void // Enter fullscreen
instance.exitFullscreen(): void // Exit fullscreen
instance.isFullscreen(): boolean // Check fullscreen state
instance.update(config: Partial<Config>): void // Update config
instance.destroy(): void // Destroy instance
instance.getElements(): Elements // Get DOM elementsStatic Methods
CIBeforeAfter.autoInit(root?: HTMLElement): CIBeforeAfterInstance[]React Usage
import { CIBeforeAfterViewer, useCIBeforeAfter } from 'js-cloudimage-before-after/react';
// Component
function ImageComparison() {
return (
<CIBeforeAfterViewer
beforeSrc="/kitchen-before.jpg"
afterSrc="/kitchen-after.jpg"
beforeAlt="Kitchen before renovation"
afterAlt="Kitchen after renovation"
zoom
labels={{ before: 'Before', after: 'After' }}
handleStyle="arrows"
animate={{ duration: 800 }}
onSlide={(pos) => console.log('Position:', pos)}
/>
);
}
// Hook
function ImageComparison() {
const { containerRef, instance } = useCIBeforeAfter({
beforeSrc: '/kitchen-before.jpg',
afterSrc: '/kitchen-after.jpg',
zoom: true,
});
return (
<>
<div ref={containerRef} />
<button onClick={() => instance.current?.setZoom(2)}>Zoom 2×</button>
</>
);
}
// Ref API
function ImageComparison() {
const ref = useRef<CIBeforeAfterViewerRef>(null);
return (
<>
<CIBeforeAfterViewer
ref={ref}
beforeSrc="/kitchen-before.jpg"
afterSrc="/kitchen-after.jpg"
zoom
/>
<button onClick={() => ref.current?.setPosition(75)}>Show 75%</button>
</>
);
}Theming
All visuals are customizable via CSS variables:
.my-viewer {
--ci-before-after-border-radius: 12px;
--ci-before-after-handle-color: #0058a3;
--ci-before-after-handle-size: 44px;
--ci-before-after-handle-border: 2px solid white;
--ci-before-after-label-bg: rgba(0, 0, 0, 0.6);
--ci-before-after-label-color: #fff;
--ci-before-after-label-radius: 4px;
}Set theme: 'dark' for the built-in dark theme.
Accessibility
- Slider handle is a focusable element with
role="slider"andaria-valuenow Arrow keysmove the slider positionShift + Arrowmoves in larger stepsHome/Endjump to 0% / 100%+/-/0control zoomEscapeexits fullscreenprefers-reduced-motiondisables animations- Before/after images have configurable alt text
Cloudimage Integration
new CIBeforeAfter('#el', {
beforeSrc: 'https://example.com/before.jpg',
afterSrc: 'https://example.com/after.jpg',
cloudimage: {
token: 'demo',
limitFactor: 100,
params: 'q=80',
},
});Browser Support
| Browser | Version | |---------|---------| | Chrome | 80+ | | Firefox | 80+ | | Safari | 14+ | | Edge | 80+ |
License
Support
If this library helped your project, consider buying me a coffee!
