$ cat docs/sliders-&-range-inputs.md
updated Recently · 14 min read · published
Sliders & Range Inputs Range inputs for selecting values within a continuum — from simple volume sliders to multi-handle price range pickers with custom styling and step increments.
Basic Range Slider
A minimal range input with a live value readout. The native <input type="range"> element provides built-in drag interaction, keyboard support, and screen reader accessibility out of the box. Use min , max , and value attributes to set the range boundaries and initial position.
basic-slider HTML CSS JS Live
Copy 1 <div class="slider-demo"> 2 <div class="slider-group"> 3 <label class="slider-label"> 4 Volume 5 <span class="slider-value" id="vol-val"> 6 50 7 </span> 8 </label> 9 <input type="range" class="slider" id="vol-slider" min="0" max="100" value="50"/> 10 </div> 11 </div>
Copy 1 .slider-demo { 2 display:flex; 3 flex-direction:column; 4 gap:20px; 5 padding:32px; 6 align-items:center; 7 font-family:system-ui, 8 sans-serif 9 } 10 .slider-group { 11 width:100%; 12 max-width:320px 13 } 14 .slider-label { 15 display:flex; 16 align-items:center; 17 justify-content:space-between; 18 font-size:12px; 19 font-weight:600; 20 color:#E0E0E0; 21 margin-bottom:8px 22 } 23 .slider-value { 24 font-size:12px; 25 color:#00FF41; 26 font-variant-numeric:tabular-nums; 27 min-width:28px; 28 text-align:right 29 } 30 .slider { 31 width:100%; 32 height:6px; 33 border-radius:3px; 34 appearance:none; 35 background:#2A2A3E; 36 outline:none; 37 cursor:pointer 38 } 39 .slider::-webkit-slider-thumb { 40 appearance:none; 41 width:18px; 42 height:18px; 43 border-radius:50%; 44 background:#00FF41; 45 cursor:pointer; 46 border:2px solid #0A0A0A; 47 box-shadow:0 0 8px rgba(0, 48 255, 49 65, 50 .3) 51 } 52 .slider::-moz-range-thumb { 53 width:18px; 54 height:18px; 55 border-radius:50%; 56 background:#00FF41; 57 cursor:pointer; 58 border:2px solid #0A0A0A 59 } 60 .slider:hover::-webkit-slider-thumb { 61 box-shadow:0 0 16px rgba(0, 62 255, 63 65, 64 .5); 65 transform:scale(1.1) 66 }
untitled.js wrap JavaScript
Copy 1 const slider=document.getElementById('vol-slider');const val=document.getElementById('vol-val');slider.addEventListener('input',()=>val.textContent=slider.value)
preview
Custom Styled
Custom thumb and track styling using ::-webkit-slider-thumb and ::-webkit-slider-runnable-track pseudo-elements. Includes a gradient-filled track that reflects the current value via a sibling div overlay technique. The range input sits on top with a transparent background.
custom-styled HTML CSS JS Live
Copy 1 <div class="styled-demo"> 2 <div class="field"> 3 <label> 4 Brightness 5 </label> 6 <div class="track-wrap"> 7 <input type="range" class="styled-slider" id="bright" min="0" max="100" value="65"/> 8 <div class="track-fill" id="bright-fill" style="width:65%"> 9 </div> 10 </div> 11 <div class="field-info"> 12 <span class="fi-icon"> 13 ☀ 14 </span> 15 <span class="fi-val" id="bright-val"> 16 65% 17 </span> 18 </div> 19 </div> 20 <div class="field"> 21 <label> 22 Contrast 23 </label> 24 <div class="track-wrap"> 25 <input type="range" class="styled-slider" id="contrast" min="0" max="100" value="40"/> 26 <div class="track-fill" id="contrast-fill" style="width:40%"> 27 </div> 28 </div> 29 <div class="field-info"> 30 <span class="fi-icon"> 31 ◐ 32 </span> 33 <span class="fi-val" id="contrast-val"> 34 40% 35 </span> 36 </div> 37 </div> 38 <div class="field"> 39 <label> 40 Saturation 41 </label> 42 <div class="track-wrap"> 43 <input type="range" class="styled-slider" id="sat" min="0" max="100" value="80"/> 44 <div class="track-fill" id="sat-fill" style="width:80%"> 45 </div> 46 </div> 47 <div class="field-info"> 48 <span class="fi-icon"> 49 ◉ 50 </span> 51 <span class="fi-val" id="sat-val"> 52 80% 53 </span> 54 </div> 55 </div> 56 </div>
Copy 1 .styled-demo { 2 max-width:360px; 3 margin:16px auto; 4 display:flex; 5 flex-direction:column; 6 gap:24px; 7 padding:24px; 8 font-family:system-ui, 9 sans-serif 10 } 11 .field { 12 display:flex; 13 flex-direction:column; 14 gap:8px 15 } 16 .field label { 17 font-size:12px; 18 font-weight:600; 19 color:#E0E0E0 20 } 21 .track-wrap { 22 position:relative; 23 height:6px; 24 border-radius:3px; 25 background:#1A1A2E 26 } 27 .track-fill { 28 position:absolute; 29 top:0; 30 left:0; 31 height:100%; 32 border-radius:3px; 33 background:linear-gradient(90deg, 34 #00FF41, 35 #00CC33); 36 pointer-events:none; 37 transition:width .1s ease 38 } 39 .styled-slider { 40 position:absolute; 41 top:0; 42 left:0; 43 width:100%; 44 height:100%; 45 appearance:none; 46 background:transparent; 47 outline:none; 48 cursor:pointer; 49 z-index:2; 50 margin:0 51 } 52 .styled-slider::-webkit-slider-thumb { 53 appearance:none; 54 width:18px; 55 height:18px; 56 border-radius:50%; 57 background:#00FF41; 58 cursor:pointer; 59 border:2px solid #0A0A0A; 60 box-shadow:0 0 8px rgba(0, 61 255, 62 65, 63 .3); 64 margin-top:-6px 65 } 66 .styled-slider::-moz-range-thumb { 67 width:18px; 68 height:18px; 69 border-radius:50%; 70 background:#00FF41; 71 cursor:pointer; 72 border:2px solid #0A0A0A 73 } 74 .styled-slider::-webkit-slider-runnable-track { 75 height:6px; 76 background:transparent; 77 border-radius:3px 78 } 79 .field-info { 80 display:flex; 81 align-items:center; 82 justify-content:space-between; 83 font-size:11px; 84 color:#808080 85 } 86 .fi-icon { 87 font-size:14px 88 } 89 .fi-val { 90 font-variant-numeric:tabular-nums; 91 color:#00FF41; 92 font-weight:600 93 }
untitled.js wrap JavaScript
Copy 1 document.querySelectorAll('.styled-slider').forEach(s=>{const fill=s.parentElement.querySelector('.track-fill');const val=s.closest('.field').querySelector('.fi-val');s.addEventListener('input',()=>{fill.style.width=s.value+'%';val.textContent=s.value+'%'})})
preview
CSS Pseudo-Elements Reference
The range input exposes several pseudo-elements for styling. WebKit and Mozilla use different naming conventions, so you need to target both to ensure cross-browser compatibility.
slider-pseudo-elements.css wrap CSS
Copy 1 /* WebKit (Chrome, Safari, Edge) */ 2 input[type="range"]::-webkit-slider-runnable-track { 3 height: 6px; 4 background: #2A2A3E; 5 border-radius: 3px; 6 } 7 8 input[type="range"]::-webkit-slider-thumb { 9 appearance: none; 10 width: 18px; 11 height: 18px; 12 border-radius: 50%; 13 background: #00FF41; 14 margin-top: -6px; /* center on track */ 15 } 16 17 /* Mozilla (Firefox) */ 18 input[type="range"]::-moz-range-track { 19 height: 6px; 20 background: #2A2A3E; 21 border-radius: 3px; 22 } 23 24 input[type="range"]::-moz-range-thumb { 25 width: 18px; 26 height: 18px; 27 border-radius: 50%; 28 background: #00FF41; 29 border: none; 30 }
Price Range (Dual Handle)
A dual-handle range slider for selecting a price range. Two overlapping range inputs create the min/max handles, with the track highlight between them. Both inputs share the same track but have pointer-events: none on the input with pointer-events: all only on the thumb.
Copy 1 <div class="range-demo"> 2 <div class="range-header"> 3 <span class="range-title"> 4 Price Range 5 </span> 6 <span class="range-display"> 7 $ 8 <span id="min-disp"> 9 25 10 </span> 11 — $ 12 <span id="max-disp"> 13 75 14 </span> 15 </span> 16 </div> 17 <div class="range-track"> 18 <div class="range-fill" id="range-fill"> 19 </div> 20 <input type="range" class="range-input" id="range-min" min="0" max="100" value="25"/> 21 <input type="range" class="range-input" id="range-max" min="0" max="100" value="75"/> 22 </div> 23 <div class="range-labels"> 24 <span> 25 $0 26 </span> 27 <span> 28 $50 29 </span> 30 <span> 31 $100 32 </span> 33 </div> 34 </div>
Copy 1 .range-demo { 2 max-width:360px; 3 margin:16px auto; 4 padding:24px; 5 font-family:system-ui, 6 sans-serif 7 } 8 .range-header { 9 display:flex; 10 align-items:center; 11 justify-content:space-between; 12 margin-bottom:16px 13 } 14 .range-title { 15 font-size:12px; 16 font-weight:600; 17 color:#E0E0E0 18 } 19 .range-display { 20 font-size:12px; 21 color:#00FF41; 22 font-weight:600; 23 font-variant-numeric:tabular-nums 24 } 25 .range-track { 26 position:relative; 27 height:6px; 28 border-radius:3px; 29 background:#1A1A2E 30 } 31 .range-fill { 32 position:absolute; 33 top:0; 34 height:100%; 35 border-radius:3px; 36 background:linear-gradient(90deg, 37 #00FF41, 38 #00CC33); 39 pointer-events:none; 40 z-index:1 41 } 42 .range-input { 43 position:absolute; 44 top:0; 45 left:0; 46 width:100%; 47 height:100%; 48 appearance:none; 49 background:transparent; 50 outline:none; 51 cursor:pointer; 52 z-index:2; 53 margin:0; 54 pointer-events:none 55 } 56 .range-input::-webkit-slider-thumb { 57 appearance:none; 58 width:18px; 59 height:18px; 60 border-radius:50%; 61 background:#00FF41; 62 cursor:pointer; 63 border:2px solid #0A0A0A; 64 box-shadow:0 0 8px rgba(0, 65 255, 66 65, 67 .3); 68 margin-top:-6px; 69 pointer-events:all 70 } 71 .range-input::-moz-range-thumb { 72 width:18px; 73 height:18px; 74 border-radius:50%; 75 background:#00FF41; 76 cursor:pointer; 77 border:2px solid #0A0A0A; 78 pointer-events:all 79 } 80 .range-labels { 81 display:flex; 82 justify-content:space-between; 83 margin-top:8px; 84 font-size:10px; 85 color:#666 86 }
untitled.js wrap JavaScript
Copy 1 const minI=document.getElementById('range-min');const maxI=document.getElementById('range-max');const fill=document.getElementById('range-fill');const minD=document.getElementById('min-disp');const maxD=document.getElementById('max-disp');function updateRange(){const min=parseInt(minI.value);const max=parseInt(maxI.value);const lo=Math.min(min,max);const hi=Math.max(min,max);fill.style.left=lo+'%';fill.style.width=(hi-lo)+'%';minD.textContent=lo;maxD.textContent=hi}minI.addEventListener('input',updateRange);maxI.addEventListener('input',updateRange);updateRange()
preview
Step Slider
A slider with discrete step increments and labeled tick marks. The step attribute constrains values to specific intervals, and custom tick marks show each possible position. The track fill updates dynamically to show the selected range.
Copy 1 <div class="step-demo"> 2 <div class="step-header"> 3 <span class="step-title"> 4 Zoom Level 5 </span> 6 <span class="step-val" id="step-val"> 7 2x 8 </span> 9 </div> 10 <div class="step-track"> 11 <input type="range" class="step-slider" id="step-slider" min="0" max="4" step="1" value="2"/> 12 <div class="step-ticks"> 13 <span class="tick"> 14 </span> 15 <span class="tick"> 16 </span> 17 <span class="tick active"> 18 </span> 19 <span class="tick"> 20 </span> 21 <span class="tick"> 22 </span> 23 </div> 24 <div class="step-labels"> 25 <span> 26 0.5x 27 </span> 28 <span> 29 1x 30 </span> 31 <span> 32 2x 33 </span> 34 <span> 35 4x 36 </span> 37 <span> 38 8x 39 </span> 40 </div> 41 </div> 42 </div>
Copy 1 .step-demo { 2 max-width:360px; 3 margin:16px auto; 4 padding:24px; 5 font-family:system-ui, 6 sans-serif 7 } 8 .step-header { 9 display:flex; 10 align-items:center; 11 justify-content:space-between; 12 margin-bottom:16px 13 } 14 .step-title { 15 font-size:12px; 16 font-weight:600; 17 color:#E0E0E0 18 } 19 .step-val { 20 font-size:14px; 21 color:#00FF41; 22 font-weight:700; 23 font-variant-numeric:tabular-nums 24 } 25 .step-track { 26 position:relative 27 } 28 .step-slider { 29 width:100%; 30 height:6px; 31 appearance:none; 32 background:linear-gradient(90deg, 33 #00FF41 50%, 34 #2A2A3E 50%); 35 border-radius:3px; 36 outline:none; 37 cursor:pointer; 38 z-index:2; 39 position:relative 40 } 41 .step-slider::-webkit-slider-thumb { 42 appearance:none; 43 width:20px; 44 height:20px; 45 border-radius:50%; 46 background:#00FF41; 47 cursor:pointer; 48 border:3px solid #0A0A0A; 49 box-shadow:0 0 10px rgba(0, 50 255, 51 65, 52 .4) 53 } 54 .step-slider::-moz-range-thumb { 55 width:20px; 56 height:20px; 57 border-radius:50%; 58 background:#00FF41; 59 cursor:pointer; 60 border:3px solid #0A0A0A 61 } 62 .step-ticks { 63 display:flex; 64 justify-content:space-between; 65 padding:0 8px; 66 margin-top:-8px; 67 position:relative; 68 z-index:1 69 } 70 .tick { 71 width:4px; 72 height:4px; 73 border-radius:50%; 74 background:#444; 75 transition:all .2s 76 } 77 .tick.active { 78 background:#00FF41; 79 box-shadow:0 0 6px rgba(0, 80 255, 81 65, 82 .5) 83 } 84 .step-labels { 85 display:flex; 86 justify-content:space-between; 87 margin-top:12px; 88 font-size:10px; 89 color:#666; 90 font-variant-numeric:tabular-nums 91 }
untitled.js wrap JavaScript
Copy 1 const labels=['0.5x','1x','2x','4x','8x'];const slider=document.getElementById('step-slider');const val=document.getElementById('step-val');const ticks=document.querySelectorAll('.tick');function updateStep(){const i=parseInt(slider.value);val.textContent=labels[i];ticks.forEach((t,j)=>{t.classList.toggle('active',j<=i)});const pct=(i/4)*100;slider.style.background='linear-gradient(90deg,#00FF41 '+pct+'%,#2A2A3E '+pct+'%)'}slider.addEventListener('input',updateStep);updateStep()
preview
Vertical Slider
Vertical sliders using CSS writing-mode: vertical-lr and direction: rtl for volume-mixer-style layouts. Each channel displays its value above and label below, creating a familiar audio mixing interface.
Copy 1 <div class="vert-demo"> 2 <div class="vert-group"> 3 <input type="range" class="vert-slider" orient="vertical" min="0" max="100" value="30"/> 4 <span class="vert-val" id="v1"> 5 30 6 </span> 7 <span class="vert-ch"> 8 CH 1 9 </span> 10 </div> 11 <div class="vert-group"> 12 <input type="range" class="vert-slider" orient="vertical" min="0" max="100" value="65"/> 13 <span class="vert-val" id="v2"> 14 65 15 </span> 16 <span class="vert-ch"> 17 CH 2 18 </span> 19 </div> 20 <div class="vert-group"> 21 <input type="range" class="vert-slider" orient="vertical" min="0" max="100" value="80"/> 22 <span class="vert-val" id="v3"> 23 80 24 </span> 25 <span class="vert-ch"> 26 CH 3 27 </span> 28 </div> 29 <div class="vert-group"> 30 <input type="range" class="vert-slider" orient="vertical" min="0" max="100" value="45"/> 31 <span class="vert-val" id="v4"> 32 45 33 </span> 34 <span class="vert-ch"> 35 CH 4 36 </span> 37 </div> 38 <div class="vert-group"> 39 <input type="range" class="vert-slider" orient="vertical" min="0" max="100" value="90"/> 40 <span class="vert-val" id="v5"> 41 90 42 </span> 43 <span class="vert-ch"> 44 CH 5 45 </span> 46 </div> 47 </div>
Copy 1 .vert-demo { 2 display:flex; 3 gap:24px; 4 align-items:flex-end; 5 justify-content:center; 6 padding:32px; 7 font-family:system-ui, 8 sans-serif 9 } 10 .vert-group { 11 display:flex; 12 flex-direction:column; 13 align-items:center; 14 gap:8px 15 } 16 .vert-slider { 17 writing-mode:vertical-lr; 18 direction:rtl; 19 width:6px; 20 height:140px; 21 appearance:none; 22 background:#1A1A2E; 23 border-radius:3px; 24 outline:none; 25 cursor:pointer 26 } 27 .vert-slider::-webkit-slider-thumb { 28 appearance:none; 29 width:16px; 30 height:16px; 31 border-radius:50%; 32 background:#00FF41; 33 cursor:pointer; 34 border:2px solid #0A0A0A; 35 box-shadow:0 0 6px rgba(0, 36 255, 37 65, 38 .3) 39 } 40 .vert-slider::-moz-range-thumb { 41 width:16px; 42 height:16px; 43 border-radius:50%; 44 background:#00FF41; 45 cursor:pointer; 46 border:2px solid #0A0A0A 47 } 48 .vert-val { 49 font-size:11px; 50 font-weight:600; 51 color:#00FF41; 52 font-variant-numeric:tabular-nums 53 } 54 .vert-ch { 55 font-size:9px; 56 color:#666; 57 text-transform:uppercase; 58 letter-spacing:.5px 59 }
untitled.js wrap JavaScript
Copy 1 document.querySelectorAll('.vert-slider').forEach(s=>{const val=s.closest('.vert-group').querySelector('.vert-val');s.addEventListener('input',()=>val.textContent=s.value)})
preview
Labeled Sections
A slider with named zones that highlight as the handle passes through them \u2014 useful for settings that have named tiers or thresholds like quality presets. Each segment fills in sequence as the value increases.
labeled-zones HTML CSS JS Live
Copy 1 <div class="zone-demo"> 2 <div class="zone-header"> 3 <span class="zone-title"> 4 Quality Preset 5 </span> 6 <span class="zone-name" id="zone-name"> 7 Balanced 8 </span> 9 </div> 10 <div class="zone-track"> 11 <div class="zone-fill" id="zone-fill"> 12 </div> 13 <input type="range" class="zone-slider" id="zone-slider" min="0" max="4" step="1" value="2"/> 14 <div class="zone-segments"> 15 <span class="seg" data-zone="Ultra Fast"> 16 </span> 17 <span class="seg" data-zone="Fast"> 18 </span> 19 <span class="seg active" data-zone="Balanced"> 20 </span> 21 <span class="seg" data-zone="Quality"> 22 </span> 23 <span class="seg" data-zone="Ultra"> 24 </span> 25 </div> 26 </div> 27 <div class="zone-labels"> 28 <span> 29 Ultra Fast 30 </span> 31 <span> 32 Fast 33 </span> 34 <span> 35 Balanced 36 </span> 37 <span> 38 Quality 39 </span> 40 <span> 41 Ultra 42 </span> 43 </div> 44 </div>
Copy 1 .zone-demo { 2 max-width:400px; 3 margin:16px auto; 4 padding:24px; 5 font-family:system-ui, 6 sans-serif 7 } 8 .zone-header { 9 display:flex; 10 align-items:center; 11 justify-content:space-between; 12 margin-bottom:12px 13 } 14 .zone-title { 15 font-size:12px; 16 font-weight:600; 17 color:#E0E0E0 18 } 19 .zone-name { 20 font-size:12px; 21 color:#00FF41; 22 font-weight:600 23 } 24 .zone-track { 25 position:relative; 26 padding:8px 0 27 } 28 .zone-slider { 29 width:100%; 30 height:8px; 31 appearance:none; 32 background:transparent; 33 border-radius:4px; 34 outline:none; 35 cursor:pointer; 36 position:relative; 37 z-index:2 38 } 39 .zone-slider::-webkit-slider-thumb { 40 appearance:none; 41 width:20px; 42 height:20px; 43 border-radius:50%; 44 background:#00FF41; 45 cursor:pointer; 46 border:3px solid #0A0A0A; 47 box-shadow:0 0 10px rgba(0, 48 255, 49 65, 50 .4) 51 } 52 .zone-slider::-moz-range-thumb { 53 width:20px; 54 height:20px; 55 border-radius:50%; 56 background:#00FF41; 57 cursor:pointer; 58 border:3px solid #0A0A0A 59 } 60 .zone-segments { 61 position:absolute; 62 top:50%; 63 left:0; 64 right:0; 65 display:flex; 66 transform:translateY(-50%); 67 z-index:1; 68 gap:3px; 69 padding:0 8px 70 } 71 .seg { 72 flex:1; 73 height:8px; 74 border-radius:4px; 75 background:#1A1A2E; 76 transition:all .3s 77 } 78 .seg.active { 79 background:#00FF41; 80 box-shadow:0 0 8px rgba(0, 81 255, 82 65, 83 .3) 84 } 85 .zone-labels { 86 display:flex; 87 justify-content:space-between; 88 margin-top:8px; 89 font-size:9px; 90 color:#555; 91 text-transform:uppercase; 92 letter-spacing:.3px 93 }
untitled.js wrap JavaScript
Copy 1 const names=['Ultra Fast','Fast','Balanced','Quality','Ultra'];const slider=document.getElementById('zone-slider');const nameEl=document.getElementById('zone-name');const segs=document.querySelectorAll('.seg');function updateZone(){const i=parseInt(slider.value);nameEl.textContent=names[i];segs.forEach((s,j)=>s.classList.toggle('active',j<=i));const pct=(i/4)*100;slider.style.background='linear-gradient(90deg,rgba(0,255,65,.2) '+pct+'%,transparent '+pct+'%)'}slider.addEventListener('input',updateZone);updateZone()
preview
Disabled & Readonly
Sliders in disabled and read-only states. The disabled state prevents all interaction and dims the control with reduced opacity, while readonly preserves the value but blocks user changes. Both states use cursor: not-allowed to signal non-interactivity.
disabled-states HTML CSS JS Live
Copy 1 <div class="disabled-demo"> 2 <div class="d-field"> 3 <label> 4 Active Slider 5 </label> 6 <input type="range" class="d-slider" min="0" max="100" value="60"/> 7 <span class="d-val"> 8 60 9 </span> 10 </div> 11 <div class="d-field"> 12 <label> 13 Disabled Slider 14 </label> 15 <input type="range" class="d-slider disabled" min="0" max="100" value="40" disabled/> 16 <span class="d-val dim"> 17 40 18 </span> 19 </div> 20 <div class="d-field"> 21 <label> 22 Readonly Slider 23 </label> 24 <input type="range" class="d-slider readonly" min="0" max="100" value="75" readonly/> 25 <span class="d-val"> 26 75 27 </span> 28 </div> 29 </div>
Copy 1 .disabled-demo { 2 max-width:360px; 3 margin:16px auto; 4 display:flex; 5 flex-direction:column; 6 gap:20px; 7 padding:24px; 8 font-family:system-ui, 9 sans-serif 10 } 11 .d-field { 12 display:flex; 13 flex-direction:column; 14 gap:6px 15 } 16 .d-field label { 17 font-size:11px; 18 font-weight:600; 19 color:#808080; 20 text-transform:uppercase; 21 letter-spacing:.5px 22 } 23 .d-slider { 24 width:100%; 25 height:6px; 26 border-radius:3px; 27 appearance:none; 28 background:#2A2A3E; 29 outline:none; 30 cursor:pointer 31 } 32 .d-slider::-webkit-slider-thumb { 33 appearance:none; 34 width:16px; 35 height:16px; 36 border-radius:50%; 37 background:#00FF41; 38 cursor:pointer; 39 border:2px solid #0A0A0A 40 } 41 .d-slider::-moz-range-thumb { 42 width:16px; 43 height:16px; 44 border-radius:50%; 45 background:#00FF41; 46 cursor:pointer; 47 border:2px solid #0A0A0A 48 } 49 .d-slider.disabled { 50 opacity:.35; 51 cursor:not-allowed 52 } 53 .d-slider.disabled::-webkit-slider-thumb { 54 background:#555; 55 cursor:not-allowed 56 } 57 .d-slider.readonly { 58 cursor:not-allowed 59 } 60 .d-val { 61 font-size:11px; 62 color:#00FF41; 63 font-weight:600; 64 font-variant-numeric:tabular-nums 65 } 66 .d-val.dim { 67 color:#555 68 }
untitled.js wrap JavaScript
Copy 1 const active=document.querySelector('.d-slider:not(.disabled):not(.readonly)');active.addEventListener('input',()=>{active.nextElementSibling.textContent=active.value})
preview
HTML Structure
The range input follows a semantic pattern: a <label> connected via for , the <input type="range"> with min/max/step/value attributes, and an aria live region for the current value. Always include the ARIA attributes for screen reader support.
slider-structure.html wrap HTML
Copy 1 <label for="volume">Volume</label> 2 3 <input type="range" 4 id="volume" 5 min="0" max="100" 6 value="50" step="1" 7 aria-valuenow="50" 8 aria-valuemin="0" 9 aria-valuemax="100" /> 10 11 <span aria-live="polite">50</span>
Best Practices
ℹ info
Always pair a range input with a visible value display. Users need precise feedback \u2014 the slider thumb alone is not enough to communicate the exact value. A tooltip or numeric readout works best.
⚠ warning
Custom-styled sliders require separate pseudo-element rules for -webkit- and -moz- prefixes. Test on both Chrome and Firefox to ensure consistent appearance across browsers.
✓ best practice
Use the step attribute for discrete values (e.g., quality presets, zoom levels). For continuous values like volume or brightness, omit step or set it to 1 for smooth movement.
Always include aria-valuemin , aria-valuemax , and aria-valuenow for screen readers. The browser updates aria-valuenow automatically, but custom displays need manual updates. Use aria-live="polite" on the value display so assistive technologies announce changes without interrupting the current task. Minimum touch target for slider thumbs should be 44\u00d744px on mobile \u2014 increase thumb size or add invisible padding to meet WCAG touch target requirements. For dual-handle range sliders, ensure handles cannot cross each other to maintain logical min/max ordering. Clamp values on the input event. Consider providing a number input alongside the slider for users who need precise values. Sync both inputs bidirectionally. Use font-variant-numeric: tabular-nums on value displays to prevent layout shifts as digits change width.