Progress Drag Slider
Original · freemedia scrubber slider
byWeb InnoventixReact + Tailwind
sldprogressdragsliders
Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.
npx shadcn@latest add https://webinnoventix.com/r/sld-progress-drag.jsonsld-progress-drag.tsx
"use client";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import type {
KeyboardEvent as ReactKeyboardEvent,
PointerEvent as ReactPointerEvent,
} from "react";
import { motion, useReducedMotion } from "motion/react";
type Chapter = { time: number; title: string };
const DURATION = 2732; // 45:32 episode
const CHAPTERS: Chapter[] = [
{ time: 0, title: "Cold open" },
{ time: 214, title: "Who actually pays maintainers" },
{ time: 726, title: "The bus-factor problem" },
{ time: 1320, title: "Funding models that stuck" },
{ time: 1980, title: "Burnout & boundaries" },
{ time: 2450, title: "What we'd fund next" },
];
const RATES = [0.75, 1, 1.25, 1.5, 2] as const;
const WAVE_BARS = 56;
function formatTime(total: number): string {
const s = Math.max(0, Math.floor(total));
const m = Math.floor(s / 60);
const sec = s % 60;
return `${m}:${sec.toString().padStart(2, "0")}`;
}
function clampTime(t: number): number {
return Math.min(DURATION, Math.max(0, t));
}
function fractionFrom(el: HTMLElement | null, clientX: number): number {
if (!el) return 0;
const r = el.getBoundingClientRect();
if (r.width === 0) return 0;
return Math.min(1, Math.max(0, (clientX - r.left) / r.width));
}
export default function SldProgressDrag() {
const reduce = useReducedMotion();
const [isPlaying, setIsPlaying] = useState(false);
const [currentTime, setCurrentTime] = useState(214);
const [buffered, setBuffered] = useState(560);
const [isDragging, setIsDragging] = useState(false);
const [scrubTime, setScrubTime] = useState<number | null>(null);
const [hoverFraction, setHoverFraction] = useState<number | null>(null);
const [volume, setVolume] = useState(0.72);
const [isMuted, setIsMuted] = useState(false);
const [volDragging, setVolDragging] = useState(false);
const [rateIndex, setRateIndex] = useState(1);
const trackRef = useRef<HTMLDivElement | null>(null);
const volTrackRef = useRef<HTMLDivElement | null>(null);
const playbackRate = RATES[rateIndex];
const bars = useMemo(() => {
const out: number[] = [];
for (let i = 0; i < WAVE_BARS; i++) {
const a = Math.sin(i * 0.37) * 0.5 + 0.5;
const b = Math.sin(i * 0.13 + 1.7) * 0.5 + 0.5;
const c = Math.sin(i * 0.91 + 0.4) * 0.5 + 0.5;
const h = a * 0.52 + b * 0.32 + c * 0.16;
out.push(Math.min(1, Math.max(0.16, h)));
}
return out;
}, []);
// Playback simulation
useEffect(() => {
if (!isPlaying || isDragging) return;
const id = window.setInterval(() => {
setCurrentTime((t) => Math.min(DURATION, t + 0.25 * playbackRate));
}, 250);
return () => window.clearInterval(id);
}, [isPlaying, isDragging, playbackRate]);
// Stop at end of track
useEffect(() => {
if (currentTime >= DURATION && isPlaying) setIsPlaying(false);
}, [currentTime, isPlaying]);
// Progressive buffering ahead of the playhead
useEffect(() => {
const id = window.setInterval(() => {
setBuffered((b) => Math.min(DURATION, b + 13));
}, 650);
return () => window.clearInterval(id);
}, []);
const displayTime =
isDragging && scrubTime !== null ? scrubTime : currentTime;
const progressFraction = displayTime / DURATION;
const shownBuffered = Math.max(buffered, displayTime);
const bufferedFraction = Math.min(1, shownBuffered / DURATION);
const effectiveVolume = isMuted ? 0 : volume;
const currentChapter = useMemo(() => {
let idx = 0;
for (let i = 0; i < CHAPTERS.length; i++) {
if (displayTime >= CHAPTERS[i].time) idx = i;
}
return idx;
}, [displayTime]);
const togglePlay = useCallback(() => {
setIsPlaying((p) => {
if (!p && currentTime >= DURATION) setCurrentTime(0);
return !p;
});
}, [currentTime]);
// ---- Seek slider pointer + keyboard ----
const seekPointerDown = useCallback(
(e: ReactPointerEvent<HTMLDivElement>) => {
e.currentTarget.setPointerCapture(e.pointerId);
setIsDragging(true);
setScrubTime(fractionFrom(trackRef.current, e.clientX) * DURATION);
},
[],
);
const seekPointerMove = useCallback(
(e: ReactPointerEvent<HTMLDivElement>) => {
const frac = fractionFrom(trackRef.current, e.clientX);
setHoverFraction(frac);
if (isDragging) setScrubTime(frac * DURATION);
},
[isDragging],
);
const seekPointerUp = useCallback(
(e: ReactPointerEvent<HTMLDivElement>) => {
if (isDragging) {
setCurrentTime(fractionFrom(trackRef.current, e.clientX) * DURATION);
setScrubTime(null);
setIsDragging(false);
}
if (e.currentTarget.hasPointerCapture(e.pointerId)) {
e.currentTarget.releasePointerCapture(e.pointerId);
}
},
[isDragging],
);
const seekKeyDown = useCallback(
(e: ReactKeyboardEvent<HTMLDivElement>) => {
let handled = true;
switch (e.key) {
case "ArrowRight":
case "ArrowUp":
setCurrentTime((t) => clampTime(t + 5));
break;
case "ArrowLeft":
case "ArrowDown":
setCurrentTime((t) => clampTime(t - 5));
break;
case "PageUp":
setCurrentTime((t) => clampTime(t + 30));
break;
case "PageDown":
setCurrentTime((t) => clampTime(t - 30));
break;
case "Home":
setCurrentTime(0);
break;
case "End":
setCurrentTime(DURATION);
break;
case " ":
case "Enter":
togglePlay();
break;
default:
handled = false;
}
if (handled) e.preventDefault();
},
[togglePlay],
);
// ---- Volume slider pointer + keyboard ----
const volPointerDown = useCallback(
(e: ReactPointerEvent<HTMLDivElement>) => {
e.currentTarget.setPointerCapture(e.pointerId);
setVolDragging(true);
setIsMuted(false);
setVolume(fractionFrom(volTrackRef.current, e.clientX));
},
[],
);
const volPointerMove = useCallback(
(e: ReactPointerEvent<HTMLDivElement>) => {
if (!volDragging) return;
setVolume(fractionFrom(volTrackRef.current, e.clientX));
},
[volDragging],
);
const volPointerUp = useCallback(
(e: ReactPointerEvent<HTMLDivElement>) => {
setVolDragging(false);
if (e.currentTarget.hasPointerCapture(e.pointerId)) {
e.currentTarget.releasePointerCapture(e.pointerId);
}
},
[],
);
const volKeyDown = useCallback((e: ReactKeyboardEvent<HTMLDivElement>) => {
let handled = true;
switch (e.key) {
case "ArrowRight":
case "ArrowUp":
setIsMuted(false);
setVolume((v) => Math.min(1, v + 0.05));
break;
case "ArrowLeft":
case "ArrowDown":
setVolume((v) => Math.max(0, v - 0.05));
break;
case "Home":
setVolume(0);
break;
case "End":
setIsMuted(false);
setVolume(1);
break;
default:
handled = false;
}
if (handled) e.preventDefault();
}, []);
const knobAnimated = isPlaying && !isDragging && !reduce;
return (
<section className="relative w-full overflow-hidden bg-gradient-to-b from-slate-50 to-slate-100 px-4 py-16 sm:py-24 dark:from-zinc-950 dark:to-zinc-900">
<style>{`
@keyframes sldpd-eq {
0%, 100% { transform: scaleY(0.32); }
50% { transform: scaleY(1); }
}
@keyframes sldpd-pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.45); }
50% { box-shadow: 0 0 0 6px rgba(99, 102, 241, 0); }
}
@keyframes sldpd-shimmer {
0% { transform: translateX(-120%); }
100% { transform: translateX(320%); }
}
.sldpd-eq-bar { transform-origin: bottom; animation: sldpd-eq 0.9s ease-in-out infinite; }
.sldpd-knob-live { animation: sldpd-pulse 2s ease-in-out infinite; }
.sldpd-shimmer { animation: sldpd-shimmer 2.4s linear infinite; }
@media (prefers-reduced-motion: reduce) {
.sldpd-eq-bar, .sldpd-knob-live, .sldpd-shimmer { animation: none !important; }
}
`}</style>
<div className="mx-auto w-full max-w-2xl">
<div className="overflow-hidden rounded-3xl border border-slate-200 bg-white shadow-xl shadow-slate-900/5 dark:border-zinc-800 dark:bg-zinc-900 dark:shadow-black/40">
<div className="p-6 sm:p-8">
{/* Header */}
<div className="flex items-start justify-between gap-4">
<div className="flex items-center gap-2.5">
<span
className="flex h-3.5 items-end gap-[3px]"
aria-hidden="true"
>
{[0, 1, 2, 3].map((i) => (
<span
key={i}
className={`w-[3px] rounded-full bg-emerald-500 dark:bg-emerald-400 ${
isPlaying && !reduce ? "sldpd-eq-bar" : ""
}`}
style={{
height: "100%",
animationDelay: `${i * 0.16}s`,
transform: isPlaying && !reduce ? undefined : "scaleY(0.5)",
transformOrigin: "bottom",
}}
/>
))}
</span>
<span className="text-xs font-semibold uppercase tracking-[0.18em] text-slate-500 dark:text-zinc-400">
{isPlaying ? "Now playing" : "Paused"}
</span>
</div>
<span className="rounded-full bg-indigo-50 px-2.5 py-1 text-xs font-semibold tabular-nums text-indigo-700 ring-1 ring-inset ring-indigo-200 dark:bg-indigo-500/10 dark:text-indigo-300 dark:ring-indigo-500/30">
EP 142
</span>
</div>
{/* Title block */}
<div className="mt-5 flex items-center gap-4">
<div className="relative hidden h-16 w-16 shrink-0 items-center justify-center overflow-hidden rounded-2xl bg-gradient-to-br from-indigo-500 via-violet-500 to-sky-500 shadow-lg shadow-indigo-500/20 sm:flex">
<svg
viewBox="0 0 40 40"
className="h-8 w-8 text-white/90"
aria-hidden="true"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
>
<path d="M6 20h2M12 12v16M18 6v28M24 10v20M30 14v12M36 20h-2" />
</svg>
</div>
<div className="min-w-0">
<h2 className="truncate text-lg font-bold text-slate-900 sm:text-xl dark:text-zinc-50">
The quiet economics of open-source maintenance
</h2>
<p className="mt-0.5 truncate text-sm text-slate-500 dark:text-zinc-400">
Signal & Noise · Dana Okafor & Ravi Menon
</p>
</div>
</div>
{/* Current chapter */}
<p className="mt-5 text-sm font-medium text-slate-600 dark:text-zinc-300">
<span className="tabular-nums text-indigo-600 dark:text-indigo-400">
Ch. {currentChapter + 1}
</span>
<span className="mx-2 text-slate-300 dark:text-zinc-600">/</span>
{CHAPTERS[currentChapter].title}
</p>
{/* Scrubber */}
<div className="mt-3">
<div
ref={trackRef}
role="slider"
tabIndex={0}
aria-label="Seek through episode"
aria-orientation="horizontal"
aria-valuemin={0}
aria-valuemax={DURATION}
aria-valuenow={Math.round(displayTime)}
aria-valuetext={`${formatTime(displayTime)} of ${formatTime(
DURATION,
)}, chapter ${currentChapter + 1}: ${CHAPTERS[currentChapter].title}`}
onPointerDown={seekPointerDown}
onPointerMove={seekPointerMove}
onPointerUp={seekPointerUp}
onPointerCancel={seekPointerUp}
onPointerLeave={() => setHoverFraction(null)}
onKeyDown={seekKeyDown}
className="group relative flex h-16 cursor-pointer touch-none select-none items-center rounded-xl px-1 outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-zinc-900"
>
{/* Waveform bars */}
<div className="pointer-events-none flex h-full w-full items-center gap-[3px]">
{bars.map((h, i) => {
const centerFrac = (i + 0.5) / WAVE_BARS;
const played = centerFrac <= progressFraction;
const inBuffer = centerFrac <= bufferedFraction;
const color = played
? "bg-indigo-500 dark:bg-indigo-400"
: inBuffer
? "bg-slate-300 dark:bg-zinc-600"
: "bg-slate-200 dark:bg-zinc-700";
return (
<span
key={i}
className={`flex-1 rounded-full transition-colors duration-150 ${color}`}
style={{ height: `${Math.round(h * 100)}%` }}
/>
);
})}
</div>
{/* Chapter tick marks */}
{CHAPTERS.map((c) =>
c.time === 0 ? null : (
<span
key={c.time}
aria-hidden="true"
className="pointer-events-none absolute inset-y-3 w-px -translate-x-1/2 bg-slate-900/15 dark:bg-white/20"
style={{ left: `${(c.time / DURATION) * 100}%` }}
/>
),
)}
{/* Hover preview */}
{hoverFraction !== null && !isDragging && (
<div
aria-hidden="true"
className="pointer-events-none absolute inset-y-2"
style={{ left: `${hoverFraction * 100}%` }}
>
<span className="absolute inset-y-0 w-px -translate-x-1/2 bg-slate-400/70 dark:bg-zinc-400/60" />
<span className="absolute bottom-full mb-2 -translate-x-1/2 whitespace-nowrap rounded-md bg-slate-900 px-2 py-1 text-[11px] font-semibold tabular-nums text-white shadow-lg dark:bg-zinc-100 dark:text-zinc-900">
{formatTime(hoverFraction * DURATION)}
</span>
</div>
)}
{/* Playhead + knob */}
<div
aria-hidden="true"
className="pointer-events-none absolute inset-y-1"
style={{ left: `${progressFraction * 100}%` }}
>
<span className="absolute inset-y-0 w-px -translate-x-1/2 bg-indigo-500/70 dark:bg-indigo-300/70" />
<span
className={`absolute top-1/2 h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-indigo-500 bg-white shadow-md transition-transform duration-150 dark:border-indigo-300 dark:bg-zinc-50 ${
isDragging ? "scale-125" : "scale-100"
} ${knobAnimated ? "sldpd-knob-live" : ""}`}
/>
</div>
</div>
{/* Time row */}
<div className="mt-2 flex items-center justify-between text-xs font-medium tabular-nums text-slate-500 dark:text-zinc-400">
<span>{formatTime(displayTime)}</span>
<span className="flex items-center gap-1.5">
{(isDragging || hoverFraction !== null) && (
<span className="hidden rounded bg-slate-100 px-1.5 py-0.5 text-slate-600 sm:inline dark:bg-zinc-800 dark:text-zinc-300">
drag to seek
</span>
)}
<span>-{formatTime(DURATION - displayTime)}</span>
</span>
</div>
</div>
{/* Transport controls */}
<div className="mt-6 flex items-center justify-between gap-3">
{/* Playback speed */}
<button
type="button"
onClick={() => setRateIndex((i) => (i + 1) % RATES.length)}
aria-label={`Playback speed, currently ${playbackRate} times. Tap to change.`}
className="inline-flex h-9 min-w-[3rem] items-center justify-center rounded-full px-3 text-sm font-bold tabular-nums text-slate-600 ring-1 ring-inset ring-slate-200 transition-colors hover:bg-slate-100 hover:text-slate-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:text-zinc-300 dark:ring-zinc-700 dark:hover:bg-zinc-800 dark:hover:text-zinc-50 dark:focus-visible:ring-offset-zinc-900"
>
{playbackRate}
<span className="ml-0.5 text-xs">×</span>
</button>
{/* Center transport */}
<div className="flex items-center gap-2 sm:gap-4">
<button
type="button"
onClick={() => setCurrentTime((t) => clampTime(t - 15))}
aria-label="Rewind 15 seconds"
className="inline-flex flex-col items-center justify-center rounded-full p-2 text-slate-600 transition-colors hover:text-indigo-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:text-zinc-300 dark:hover:text-indigo-400 dark:focus-visible:ring-offset-zinc-900"
>
<svg
viewBox="0 0 24 24"
className="h-5 w-5"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path d="M11 7l-5 5 5 5M18 7l-5 5 5 5" />
</svg>
<span className="mt-0.5 text-[10px] font-bold leading-none tabular-nums">
15
</span>
</button>
<motion.button
type="button"
onClick={togglePlay}
whileTap={reduce ? undefined : { scale: 0.9 }}
aria-label={isPlaying ? "Pause" : "Play"}
aria-pressed={isPlaying}
className="inline-flex h-14 w-14 items-center justify-center rounded-full bg-indigo-600 text-white shadow-lg shadow-indigo-600/30 transition-colors hover:bg-indigo-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-zinc-900"
>
{isPlaying ? (
<svg
viewBox="0 0 24 24"
className="h-6 w-6"
fill="currentColor"
aria-hidden="true"
>
<rect x="6" y="5" width="4" height="14" rx="1" />
<rect x="14" y="5" width="4" height="14" rx="1" />
</svg>
) : (
<svg
viewBox="0 0 24 24"
className="h-6 w-6 translate-x-0.5"
fill="currentColor"
aria-hidden="true"
>
<path d="M8 5.14v13.72a1 1 0 0 0 1.54.84l10.28-6.86a1 1 0 0 0 0-1.68L9.54 4.3A1 1 0 0 0 8 5.14z" />
</svg>
)}
</motion.button>
<button
type="button"
onClick={() => setCurrentTime((t) => clampTime(t + 30))}
aria-label="Skip forward 30 seconds"
className="inline-flex flex-col items-center justify-center rounded-full p-2 text-slate-600 transition-colors hover:text-indigo-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:text-zinc-300 dark:hover:text-indigo-400 dark:focus-visible:ring-offset-zinc-900"
>
<svg
viewBox="0 0 24 24"
className="h-5 w-5"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path d="M13 7l5 5-5 5M6 7l5 5-5 5" />
</svg>
<span className="mt-0.5 text-[10px] font-bold leading-none tabular-nums">
30
</span>
</button>
</div>
{/* Volume */}
<div className="flex items-center gap-2">
<button
type="button"
onClick={() => setIsMuted((m) => !m)}
aria-label={isMuted ? "Unmute" : "Mute"}
aria-pressed={isMuted}
className="inline-flex h-9 w-9 items-center justify-center rounded-full text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:text-zinc-300 dark:hover:bg-zinc-800 dark:hover:text-zinc-50 dark:focus-visible:ring-offset-zinc-900"
>
<svg
viewBox="0 0 24 24"
className="h-5 w-5"
fill="none"
stroke="currentColor"
strokeWidth={1.8}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path
d="M4 9v6h3.5L13 19V5L7.5 9H4z"
fill="currentColor"
stroke="none"
/>
{isMuted || effectiveVolume === 0 ? (
<path d="M17 9.5l4 5M21 9.5l-4 5" />
) : (
<>
<path d="M16.5 8.8a4.5 4.5 0 0 1 0 6.4" />
{effectiveVolume > 0.5 && (
<path d="M19 6.5a8 8 0 0 1 0 11" />
)}
</>
)}
</svg>
</button>
<div
ref={volTrackRef}
role="slider"
tabIndex={0}
aria-label="Volume"
aria-orientation="horizontal"
aria-valuemin={0}
aria-valuemax={100}
aria-valuenow={Math.round(effectiveVolume * 100)}
aria-valuetext={`Volume ${Math.round(effectiveVolume * 100)} percent`}
onPointerDown={volPointerDown}
onPointerMove={volPointerMove}
onPointerUp={volPointerUp}
onPointerCancel={volPointerUp}
onKeyDown={volKeyDown}
className="group relative hidden h-9 w-20 cursor-pointer touch-none select-none items-center rounded-full outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white sm:flex dark:focus-visible:ring-offset-zinc-900"
>
<span className="pointer-events-none relative h-1.5 w-full rounded-full bg-slate-200 dark:bg-zinc-700">
<span
className="absolute inset-y-0 left-0 rounded-full bg-indigo-500 dark:bg-indigo-400"
style={{ width: `${effectiveVolume * 100}%` }}
/>
<span
className="absolute top-1/2 h-3.5 w-3.5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-indigo-500 bg-white shadow transition-transform duration-150 group-hover:scale-110 dark:border-indigo-300 dark:bg-zinc-50"
style={{ left: `${effectiveVolume * 100}%` }}
/>
</span>
</div>
</div>
</div>
{/* Chapter jump list */}
<div className="mt-6 border-t border-slate-100 pt-5 dark:border-zinc-800">
<p className="mb-3 text-xs font-semibold uppercase tracking-[0.14em] text-slate-400 dark:text-zinc-500">
Chapters
</p>
<div className="flex flex-wrap gap-2">
{CHAPTERS.map((c, i) => {
const active = i === currentChapter;
return (
<button
key={c.time}
type="button"
onClick={() => setCurrentTime(c.time)}
aria-current={active ? "true" : undefined}
className={`inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-zinc-900 ${
active
? "bg-indigo-600 text-white shadow-sm"
: "bg-slate-100 text-slate-600 hover:bg-slate-200 hover:text-slate-900 dark:bg-zinc-800 dark:text-zinc-300 dark:hover:bg-zinc-700 dark:hover:text-zinc-50"
}`}
>
<span
className={`tabular-nums ${
active
? "text-indigo-100"
: "text-slate-400 dark:text-zinc-500"
}`}
>
{formatTime(c.time)}
</span>
{c.title}
</button>
);
})}
</div>
</div>
</div>
</div>
<p className="mt-4 text-center text-xs text-slate-400 dark:text-zinc-600">
Drag the waveform, use arrow keys to seek, or jump by chapter.
</p>
<span className="sr-only" aria-live="polite">
{isPlaying ? "Playing" : "Paused"} at {formatTime(displayTime)}
</span>
</div>
</section>
);
}Dependencies
motion
Licence
Built by Web Innoventix. Free for personal and commercial use, no attribution required.
Built by Web Innoventix
Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.
Get a free quoteSimilar components
Browse all →
Basic Slider
Originalbasic range slider with value bubble

Range Dual Slider
Originaldual-handle min/max range

Steps Slider
Originalstepped slider with tick marks

Vertical Slider
Originalvertical sliders

Tooltip Slider
Originalslider with a floating value tooltip

Volume Slider
Originalvolume slider with icon

Price Range Slider
Originalprice range filter slider

Gradient Track Slider
Originalslider with a gradient track

Rating Slider
Originalsatisfaction rating slider

Brightness Slider
Originalbrightness/contrast control sliders

Marks Slider
Originalslider with labelled marks

Color Hue Slider
Originalhue/saturation color sliders

