--- title: Liquid Glass Slider description: A realistic 3D liquid glass range slider featuring multi-stage crystal track depth, smooth pointer drag interaction, active liquid color glow channels, 3D specular thumb dome, value tooltips, and keyboard navigation. --- ## Installation CLI Manual ```bash npx lightswind@latest add liquid-glass-slider ``` Copy and paste the following code into your project. ```tsx "use client"; import React, { useState, useEffect, useRef } from "react"; import { cn } from "@/components/lib/utils"; import { motion } from "framer-motion"; export interface LiquidGlassSliderProps { value?: number; defaultValue?: number; min?: number; max?: number; step?: number; onChange?: (value: number) => void; variant?: "emerald" | "primary" | "cyan" | "purple" | "glass"; size?: "sm" | "md" | "lg"; disabled?: boolean; label?: string; showValueTooltip?: boolean; showMarks?: boolean; className?: string; } export const LiquidGlassSlider = React.forwardRef< HTMLDivElement, LiquidGlassSliderProps >( ( { value, defaultValue = 50, min = 0, max = 100, step = 1, onChange, variant = "emerald", size = "md", disabled = false, label, showValueTooltip = true, showMarks = false, className, }, ref ) => { const [val, setVal] = useState(value !== undefined ? value : defaultValue); const [isDragging, setIsDragging] = useState(false); const trackRef = useRef(null); useEffect(() => { if (value !== undefined) { setVal(value); } }, [value]); const percentage = Math.min( 100, Math.max(0, ((val - min) / (max - min)) * 100) ); const updateValueFromPointer = (clientX: number) => { if (!trackRef.current || disabled) return; const rect = trackRef.current.getBoundingClientRect(); const clickX = clientX - rect.left; const rawRatio = Math.min(1, Math.max(0, clickX / rect.width)); const rawVal = min + rawRatio * (max - min); const steppedVal = Math.round(rawVal / step) * step; const finalVal = Math.min(max, Math.max(min, steppedVal)); if (value === undefined) { setVal(finalVal); } onChange?.(finalVal); }; const handlePointerDown = (e: React.PointerEvent) => { if (disabled) return; setIsDragging(true); (e.target as HTMLElement).setPointerCapture(e.pointerId); updateValueFromPointer(e.clientX); }; const handlePointerMove = (e: React.PointerEvent) => { if (isDragging) { updateValueFromPointer(e.clientX); } }; const handlePointerUp = (e: React.PointerEvent) => { if (isDragging) { setIsDragging(false); (e.target as HTMLElement).releasePointerCapture(e.pointerId); } }; const handleKeyDown = (e: React.KeyboardEvent) => { if (disabled) return; let next = val; if (e.key === "ArrowRight" || e.key === "ArrowUp") { next = Math.min(max, val + step); } else if (e.key === "ArrowLeft" || e.key === "ArrowDown") { next = Math.max(min, val - step); } else if (e.key === "Home") { next = min; } else if (e.key === "End") { next = max; } if (next !== val) { if (value === undefined) setVal(next); onChange?.(next); } }; // Sizing presets const trackHeightClasses = { sm: "h-2.5 rounded-full", md: "h-3.5 rounded-full", lg: "h-5 rounded-full", }; const thumbSizeClasses = { sm: "w-5 h-5", md: "w-7 h-7", lg: "w-9 h-9", }; // Active fill liquid glass track variants const activeFillVariants = { emerald: "bg-gradient-to-r from-emerald-400/90 via-teal-500/85 to-emerald-600/90 border-r border-white/80 shadow-[0_4px_18px_rgba(16,185,129,0.5),inset_0_1.5px_1px_rgba(255,255,255,0.95)]", primary: "bg-gradient-to-r from-[#ff6a3d]/90 via-[#ee3e26]/85 to-[#c31e14]/90 border-r border-white/80 shadow-[0_4px_18px_rgba(238,62,38,0.55),inset_0_1.5px_1px_rgba(255,255,255,0.95)]", cyan: "bg-gradient-to-r from-cyan-400/90 via-blue-500/85 to-indigo-600/90 border-r border-white/80 shadow-[0_4px_18px_rgba(0,114,255,0.5),inset_0_1.5px_1px_rgba(255,255,255,0.95)]", purple: "bg-gradient-to-r from-purple-400/90 via-fuchsia-500/85 to-pink-600/90 border-r border-white/80 shadow-[0_4px_18px_rgba(168,85,247,0.5),inset_0_1.5px_1px_rgba(255,255,255,0.95)]", glass: "bg-gradient-to-r from-white/70 via-white/45 to-white/25 dark:from-white/45 dark:to-white/15 border-r border-white shadow-[0_4px_18px_rgba(255,255,255,0.4),inset_0_1.5px_1px_rgba(255,255,255,0.95)]", }; return (
{/* Label & Value Display */} {(label || showValueTooltip) && (
{label && ( {label} )} {showValueTooltip && ( {val} )}
)} {/* Slider Track Container */}
{/* Base Glass Track */}
{/* Top Gloss Reflection Line on Base Track */} {/* Filled Progress Bar with Translucent Liquid Glow */}
{/* Inner Specular Gloss Overlay on Active Fill Bar */}
{/* 3D Liquid Glass Thumb / Handle - Perfectly Centered Vertically */}
{/* Top dome specular glare on Thumb */} {/* Inner Liquid Crystal Center Dot */}
{/* Optional Tick Marks */} {showMarks && (
{min} {Math.round((min + max) / 2)} {max}
)}
); } ); LiquidGlassSlider.displayName = "LiquidGlassSlider"; export default LiquidGlassSlider; ```
## Usage ```tsx import { LiquidGlassSlider } from "@/components/lightswind/liquid-glass-slider" ``` ```tsx import { useState } from "react"; import { LiquidGlassSlider } from "@/components/lightswind/liquid-glass-slider"; export function Demo() { const [val, setVal] = useState(75); return ( ); } ```